Detecting Breakpoints

gdb通过替换目标地址的字节为0xcc来实现断点, 这里给出一个简单的检测int 3断点的示例:

void foo() {
    printf("Hello\n");
}
int main() {
    if ((*(volatile unsigned *)((unsigned)foo) & 0xff) == 0xcc) {
        printf("BREAKPOINT\n");
        exit(1);
    }
    foo();
}

正常运行程序会输出Hello, 但是如果之前有在foo函数这里设置cc断点并运行, gdb则无法断下, 并会输出BREAKPOINT.

# gdb ./x
gdb> bp foo
Breakpoint 1 at 0x804838c
gdb> run
BREAKPOINT
Program exited with code 01.

这个要绕过也很简单, 那就是需要阅读汇编代码并注意设置断点不要在foo函数入口处. 实际情况就要看检测断点的位置是哪里.

这种监视断点的反调试技术, 关键不在于如何绕过它, 而是在于如何检测它. 在这个示例中可以很轻松的发现, 程序也有打印出相应的信息. 在实际情况中, 程序不会输出任何信息, 断点也无法轻易地断下. 我们可以使用perl脚本过滤反汇编代码中有关0xcc的代码出来进行检查.

我们可以使用perl脚本过滤反汇编代码中有关0xcc的代码出来进行检查

#!/usr/bin/perl
while(<>)
{
    if($_ =~ m/([0-9a-f][4]:\s*[0-9a-f \t]*.*0xcc)/ ){ print; }
}

显示结果

# objdump -M intel -d xxx | ./antibp.pl
      80483be: 3d cc 00 00 00 cmp eax,0xcc

检测到后, 既可以将0xcc修改成0x00或0x90, 也可以做任何你想做的操作.

改变0xcc也同样可能带来问题, 就如上篇介绍一样, 程序如果有进行文件校验, 那么我们的改变是会被检测到的. 可能的情况下, 程序也不只是对函数入口点进行检测, 也会在一个循环里对整个函数进行检测.

因此你也可以用十六进制编辑器手动放置一个ICEBP(0xF1)字节到需要断下的位置(而非int 3). 因为ICEBP也一样能让gdb断下来.

Reference: Beginners Guide to Basic Linux Anti Anti Debugging Techniques