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 }