kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

x64_dbg_test.c (5734B)


      1 /* x86_64 ArchDbgOps unit test.
      2  *
      3  * Exercises the small debugger decoder/lifter used by displaced stepping:
      4  * instruction length, RIP-relative displacement rebasing, FS-segment TLS
      5  * addressing, branch rebasing, breakpoint patching, and call detection. */
      6 
      7 #include <kit/core.h>
      8 #include <stdio.h>
      9 #include <string.h>
     10 
     11 #include "arch/arch.h"
     12 #include "core/bytes.h"
     13 #include "lib/kit_unit.h"
     14 
     15 /* Shared test context replaces the per-file counter global; EXPECT aliases
     16  * CU_EXPECT so the call sites are unchanged. This test exercises only the
     17  * arch lookup, so it uses none of the heap/diag/compiler wiring. */
     18 static KitUnit g_u;
     19 #define EXPECT(cond, ...) CU_EXPECT(&g_u, cond, __VA_ARGS__)
     20 
     21 static const ArchDbgOps* x64_dbg(void) {
     22   const ArchImpl* a = arch_lookup(KIT_ARCH_X86_64);
     23   return a ? a->dbg : NULL;
     24 }
     25 
     26 static ArchDbgInsn decode_one(const ArchDbgOps* dbg, const u8* bytes, u32 n,
     27                               u64 pc) {
     28   ArchDbgInsn insn;
     29   memset(&insn, 0, sizeof insn);
     30   EXPECT(dbg->decode_insn(bytes, n, pc, &insn) == KIT_OK, "decode_insn failed");
     31   return insn;
     32 }
     33 
     34 static void check_breakpoint(const ArchDbgOps* dbg) {
     35   u8 patch[ARCH_DBG_MAX_TRAP_BYTES];
     36   u32 n = 0;
     37   EXPECT(dbg->breakpoint_patch(patch, sizeof patch, &n) == KIT_OK,
     38          "breakpoint_patch failed");
     39   EXPECT(n == 1u && patch[0] == 0xccu, "bad breakpoint patch");
     40   EXPECT(dbg->breakpoint_addr_from_fault_pc(0x1235u) == 0x1234u,
     41          "bad x64 breakpoint pc normalization");
     42 }
     43 
     44 static void check_rip_relative(const ArchDbgOps* dbg) {
     45   u8 code[] = {0x48, 0x8b, 0x05, 0x34, 0x12, 0x00, 0x00};
     46   u8 shim[64];
     47   ArchDbgInsn insn = decode_one(dbg, code, sizeof code, 0x1000u);
     48   u32 sentinel = 0;
     49   u64 fallthrough = 0;
     50   EXPECT(insn.len == sizeof code, "rip-rel len got %u", insn.len);
     51   memset(shim, 0, sizeof shim);
     52   EXPECT(dbg->build_displaced_shim(&insn, shim, 0x2000u, sizeof shim, &sentinel,
     53                                    &fallthrough) == KIT_OK,
     54          "rip-rel shim failed");
     55   EXPECT(sentinel == sizeof code, "rip-rel sentinel got %u", sentinel);
     56   EXPECT(fallthrough == 0x1000u + sizeof code, "rip-rel fallthrough bad");
     57   EXPECT(rd_u32_le(shim + 3) == 0x234u, "rip-rel disp got 0x%x",
     58          rd_u32_le(shim + 3));
     59   EXPECT(shim[sizeof code] == 0xccu, "rip-rel missing sentinel");
     60 }
     61 
     62 static void check_fs_tls_copy(const ArchDbgOps* dbg) {
     63   u8 code[] = {0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0};
     64   u8 shim[64];
     65   ArchDbgInsn insn = decode_one(dbg, code, sizeof code, 0x1000u);
     66   u32 sentinel = 0;
     67   u64 fallthrough = 0;
     68   EXPECT(insn.len == sizeof code, "fs tls len got %u", insn.len);
     69   memset(shim, 0xa5, sizeof shim);
     70   EXPECT(dbg->build_displaced_shim(&insn, shim, 0x3000u, sizeof shim, &sentinel,
     71                                    &fallthrough) == KIT_OK,
     72          "fs tls shim failed");
     73   EXPECT(memcmp(shim, code, sizeof code) == 0,
     74          "fs absolute disp32 form should copy unchanged");
     75   EXPECT(sentinel == sizeof code && shim[sentinel] == 0xccu,
     76          "fs tls sentinel bad");
     77   EXPECT(fallthrough == 0x1000u + sizeof code, "fs tls fallthrough bad");
     78 }
     79 
     80 static void check_branches_and_calls(const ArchDbgOps* dbg) {
     81   u8 call[] = {0xe8, 0x78, 0x56, 0x34, 0x12};
     82   u8 jcc[] = {0x0f, 0x85, 0x04, 0x00, 0x00, 0x00};
     83   u8 short_jcc[] = {0x75, 0x7f};
     84   u8 jmp[] = {0xe9, 0x20, 0x00, 0x00, 0x00};
     85   u8 indirect_call[] = {0xff, 0xd0}; /* call *%rax */
     86   u8 shim[64];
     87   ArchDbgInsn insn = decode_one(dbg, call, sizeof call, 0x1000u);
     88   u32 sentinel = 0;
     89   u64 fallthrough = 0;
     90   i64 target = (i64)(0x1000u + sizeof call) + (i64)0x12345678;
     91   i64 nd = target - (i64)(0x2000u + sizeof call);
     92 
     93   EXPECT(insn.len == sizeof call, "call len got %u", insn.len);
     94   EXPECT(dbg->is_call(&insn), "near call not detected");
     95   EXPECT(dbg->build_displaced_shim(&insn, shim, 0x2000u, sizeof shim, &sentinel,
     96                                    &fallthrough) == KIT_OK,
     97          "call shim failed");
     98   EXPECT((i32)rd_u32_le(shim + 1) == (i32)nd, "call disp not rebased");
     99 
    100   insn = decode_one(dbg, jcc, sizeof jcc, 0x4000u);
    101   EXPECT(!dbg->is_call(&insn), "jcc detected as call");
    102   EXPECT(dbg->build_displaced_shim(&insn, shim, 0x5000u, sizeof shim, &sentinel,
    103                                    &fallthrough) == KIT_OK,
    104          "jcc shim failed");
    105   EXPECT((i32)rd_u32_le(shim + 2) == (i32)(0x400au - 0x5006u),
    106          "jcc disp not rebased");
    107 
    108   insn = decode_one(dbg, short_jcc, sizeof short_jcc, 0x1000u);
    109   EXPECT(dbg->build_displaced_shim(&insn, shim, 0x2000u, sizeof shim, &sentinel,
    110                                    &fallthrough) == KIT_OK,
    111          "short jcc promotion failed");
    112   EXPECT(sentinel == 6u && shim[0] == 0x0fu && shim[1] == 0x85u,
    113          "short jcc was not promoted");
    114 
    115   insn = decode_one(dbg, jmp, sizeof jmp, 0x7000u);
    116   EXPECT(dbg->build_displaced_shim(&insn, shim, 0x8000u, sizeof shim, &sentinel,
    117                                    &fallthrough) == KIT_OK,
    118          "jmp shim failed");
    119   EXPECT(sentinel == 0u && shim[0] == 0xccu, "jmp should trap immediately");
    120   EXPECT(fallthrough == 0x7025u, "jmp target got 0x%llx",
    121          (unsigned long long)fallthrough);
    122 
    123   insn = decode_one(dbg, indirect_call, sizeof indirect_call, 0x6000u);
    124   EXPECT(insn.len == sizeof indirect_call, "indirect call len got %u",
    125          insn.len);
    126   EXPECT(dbg->is_call(&insn), "indirect call not detected");
    127 }
    128 
    129 int main(void) {
    130   const ArchDbgOps* dbg;
    131   kit_unit_init(&g_u);
    132   dbg = x64_dbg();
    133   EXPECT(dbg != NULL, "x64 dbg ops missing");
    134   if (!dbg) return 1;
    135   check_breakpoint(dbg);
    136   check_rip_relative(dbg);
    137   check_fs_tls_copy(dbg);
    138   check_branches_and_calls(dbg);
    139   if (g_u.fails) {
    140     fprintf(stderr, "%d FAILED\n", g_u.fails);
    141     return 1;
    142   }
    143   printf("x64 dbg test: OK\n");
    144   return 0;
    145 }