我正在学习Linux上的一些反调试技术,发现了一段代码,用于检查内存中的0xcc字节以检测gdb中的断点。这是代码:
if ((*(volatile unsigned *)((unsigned)foo + 3) & 0xff) == 0xcc)
{
printf("BREAKPOINT\n");
exit(1);
}
foo();
但它不起作用。我什至尝试在 foo() 函数上设置断点并观察内存中的内容,但没有看到任何为断点写入的 0xcc 字节。这是我所做的:
(gdb) b foo
Breakpoint 1 at 0x804846a: file p4.c, line 8.
(gdb) x/x 0x804846a
0x804846a <foo+6>: 0xe02404c7
(gdb) x/16x 0x8048460
0x8048460 <frame_dummy+32>: 0x90c3c9d0 0x83e58955 0x04c718ec 0x0485e024
0x8048470 <foo+12>: 0xfefae808 0xc3c9ffff .....
如您所见,foo() 函数的入口点上似乎没有写入 0xcc 字节。有谁知道发生了什么或我可能哪里错了?谢谢。
最佳答案
第二部分很容易解释(正如 Flortify 正确指出的那样): GDB 显示原始内存内容,而不是断点“字节”。在默认模式下,它实际上什至会在调试器暂停时删除断点并在继续之前重新插入断点。用户通常希望看到他们的代码,而不是用于断点的奇怪的修改指令。
对于您的 C 代码,您错过了几个字节的断点。 GDB 在 之后设置断点function prologue ,因为函数序言通常不是 gdb 用户希望看到的。所以,如果你把 break 放在 foo 上,实际的断点通常会位于它之后的几个字节处(取决于依赖于函数的序言代码本身,因为它可能需要也可能不需要保存堆栈指针、帧指针等)。但是很容易检查。我使用了这段代码:
#include <stdio.h>
int main()
{
int i,j;
unsigned char *p = (unsigned char*)main;
for (j=0; j<4; j++) {
printf("%p: ",p);
for (i=0; i<16; i++)
printf("%.2x ", *p++);
printf("\n");
}
return 0;
}
如果我们单独运行这个程序,它会打印:
0x40057d: 55 48 89 e5 48 83 ec 10 48 c7 45 f8 7d 05 40 00 0x40058d: c7 45 f4 00 00 00 00 eb 5a 48 8b 45 f8 48 89 c6 0x40059d: bf 84 06 40 00 b8 00 00 00 00 e8 b4 fe ff ff c7 0x4005ad: 45 f0 00 00 00 00 eb 27 48 8b 45 f8 48 8d 50 01
现在我们在 gdb 中运行它(为 SO 重新格式化输出)。
(gdb) break main Breakpoint 1 at 0x400585: file ../bp.c, line 6. (gdb) info break Num Type Disp Enb Address What 1 breakpoint keep y 0x0000000000400585 in main at ../bp.c:6 (gdb) disas/r main,+32 Dump of assembler code from 0x40057d to 0x40059d: 0x000000000040057d (main+0): 55 push %rbp 0x000000000040057e (main+1): 48 89 e5 mov %rsp,%rbp 0x0000000000400581 (main+4): 48 83 ec 10 sub $0x10,%rsp 0x0000000000400585 (main+8): 48 c7 45 f8 7d 05 40 00 movq $0x40057d,-0x8(%rbp) 0x000000000040058d (main+16): c7 45 f4 00 00 00 00 movl $0x0,-0xc(%rbp) 0x0000000000400594 (main+23): eb 5a jmp 0x4005f0 0x0000000000400596 (main+25): 48 8b 45 f8 mov -0x8(%rbp),%rax 0x000000000040059a (main+29): 48 89 c6 mov %rax,%rsi End of assembler dump.
据此我们验证了该程序正在打印正确的字节。但这也说明断点已经插入到0x400585处(即函数序言之后),而不是函数的第一条指令处。 如果我们现在在 gdb 下运行程序(使用 run),然后在命中断点后“继续”,我们会得到这个输出:
(gdb) cont Continuing. 0x40057d: 55 48 89 e5 48 83 ec 10 cc c7 45 f8 7d 05 40 00 0x40058d: c7 45 f4 00 00 00 00 eb 5a 48 8b 45 f8 48 89 c6 0x40059d: bf 84 06 40 00 b8 00 00 00 00 e8 b4 fe ff ff c7 0x4005ad: 45 f0 00 00 00 00 eb 27 48 8b 45 f8 48 8d 50 01
现在显示 0xcc 被打印到 main 中的地址 9 字节。
关于c - 反调试 : gdb does not write 0xcc byte for breakpoints. 知道为什么吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23622414/