Chinaunix首页 | 论坛 | 博客
  • 博客访问: 31811
  • 博文数量: 6
  • 博客积分: 65
  • 博客等级: 民兵
  • 技术积分: 71
  • 用 户 组: 普通用户
  • 注册时间: 2011-10-23 21:40
文章分类

全部博文(6)

文章存档

2014年(2)

2013年(2)

2012年(2)

我的朋友

分类: LINUX

2014-11-17 22:05:34

    无聊的时候学了一下调试,在这里做一下笔记。anti-debug方法是很多的,这里先介绍几种最基本的方法。

方法一:跳到指令的中间    

点击(此处)折叠或打开

  1. //anti-debug1.s
  2. .global _start
  3. _start:
  4.         jmp label+1;
  5. label:
  6.         .byte 0x90
  7.         mov $0x1, %eax
编译方法:gcc anti-debug1.s -c -o anti-debug1.o && ld anti-debug1.o -o anti-debug1。这样定义了一个垃圾指令0x90,它在x86架构的汇编里代表nop,只占一个字节。这段代码正好跳到nop后面,所以这段代码是没有问题的。objdump -d anti-debug1输出如下:

点击(此处)折叠或打开

  1. 08048054 <_start>:
  2.  8048054: eb 01            jmp 8048057 <label+0x1>

  3. 08048056 <label>:
  4.  8048056: 90 nop
  5.  8048057: b8 01 00 00 00    mov $0x1,%eax
现在我们把0x90改成一个多字节的指令0xe9(0xe9是jmp指令的一个字节)代码如下:

点击(此处)折叠或打开

  1. //anti-debug1.s
  2. .global _start
  3. _start:
  4.         jmp label+1;
  5. label:
  6.         .byte 0xe9
  7.         mov $0x1, %eax
  8.         mov $0x2, %eax
然后编译,反汇编代码如下:

点击(此处)折叠或打开

  1. 08048054 <_start>:
  2.  8048054: eb 01               jmp 8048057 <label+0x1>

  3. 08048056 <label>:
  4.  8048056: e9 b8 01 00 00      jmp 8048213 <label+0x1bd>
  5.  804805b: 00 b8 02 00 00 00   add %bh,0x2(%eax)
这次成功的骗过了objdump,看上去是跳到了一条指令的中间位置。但是这种方法对于调试工具(如:gdb)是没作用的。
解决方法:
   解决这个anti-debug的方法比较简单,只要用二进制编辑工具把刚才的垃圾指令(0xe9)改成0x90或者其他的单字节指令然后用objdump反汇编就可以了。但有的程序会实时的检测自己的可执行文件是否被修改,如果被修改了就会做相应的处理,所以比较好的办法是用一些支持交互的反汇编工具,例如ida之类的。

方法二:实时计算跳转指令
   这种方法实时的计算跳转的目的地址,当前指令的地址是存放在eip寄存器里的,只要我们修改这个寄存器的值就能改变指令的执行顺序。直接取eip的值似乎不太可能,所以要使用一对组合call+pop,call会把eip的值压入栈,然后用pop把它取出来就可以了。

点击(此处)折叠或打开

  1. .global _start
  2. _start:
  3.         call earth+1
  4. earth:
  5.         .byte 0xe9         //location_1:
  6.         pop %eax
  7.         nop
  8.         add $9, %eax       //这个9代表从location_1(包含)到location_2(包含)之间代码占的字节数
  9.         push %eax
  10.         ret
  11.         .byte 0xe9         //location_2:
  12. code:
  13.         nop
  14.         nop
  15.         nop
  16.         ret
可以在location_1和location_2加入其他代码,但是要把9改成相应的值。然后反汇编代码如下:

点击(此处)折叠或打开

  1. 08048054 <_start>:
  2.  8048054: e8 01 00 00 00           call 804805a <earth+0x1>

  3. 08048059 <earth>:
  4.  8048059: e9 58 90 83 c0           jmp c88810b6 <_end+0xc083804e>
  5.  804805e: 09 50 c3                 or %edx,-0x3d(%eax)
  6.  8048061: e9 90 90 90 c3           jmp cb9510f6 <_end+0xc390808e>

  7. 08048062 <Code>:
  8.  8048062: 90                       nop
  9.  8048063: 90                       nop
  10.  8048064: 90                       nop
  11.  8048065: c3                       ret
这样从反汇编代码似乎完全看不出来代码的执行顺序了。
解决方法:
   
这个方法也并不完美,只不过是难度稍微加大了一点把jmp指令隐藏起来了。只要把垃圾指令去掉还是很好分析的,不再详解了,自己解决。

方法三:检查int 3指令
   调试工具断点的原理是在相应的地方加int 3指令。int 3对应的二进制是0xcc,可以根据某个地方的指令是不是0xcc来判断程序是否被调试。

点击(此处)折叠或打开

  1. #include <stdio.h>

  2. void foo ()
  3. {
  4.         printf ("Hello\n");
  5. }

  6. int main ()
  7. {
  8.         if ((*(volatile unsigned *) ((unsigned) foo) & 0xff) == 0xcc) {
  9.                 printf ("BREAKPOINT\n");
  10.                 exit (1);
  11.         }
  12.         foo ();
  13.         return 0;
  14. }
如果在foo函数上设置断点则会导致函数进入if语句打印BREAKPOINT,直接执行是不会有问题的。

点击(此处)折叠或打开

  1. (gdb) b foo
  2. Breakpoint 1 at 0x80483ea
  3. (gdb) run
  4. Starting program: /home/weiwenzhao/test/anti-debug3

  5. Breakpoint 1, 0x080483ea in foo ()
  6. Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.80.el6.i686
  7. (gdb) continue
  8. Continuing.
  9. Hello

  10. Program exited normally.
这个gdb在foo上设置断点然后运行的结果??为什么程序正常退出了呢。把foo函数反汇编出来看一下

点击(此处)折叠或打开

  1. (gdb) x /10i 0x80483e4
  2.    0x80483e4 <foo>:       push %ebp
  3.    0x80483e5 <foo+1>:     mov %esp,%ebp
  4.    0x80483e7 <foo+3>:     sub $0x18,%esp
  5.    0x80483ea <foo+6>:     movl $0x8048504,(%esp)
  6.    0x80483f1 <foo+13>:    call 0x8048310 <puts@plt>
  7.    0x80483f6 <foo+18>:    leave
  8.    0x80483f7 <foo+19>:    ret
原来是gdb做了修改并没有把断点设置到函数的开始,而是往下走了几个字节。这种方法对一些其他的调试工具是管用的。
解决方法:
    这个似乎gdb已经给我们例子,只要调试的时候搜一下程序看看是不是有cmp指令比较某个地址的值是不是0xcc,如果有就不要在这个地方加断点了,移动几个字节就可以了,或者调试的时候在它进行比较的地方把0xcc改成其他值。

方法四:用ptrace检查是不是被调试

点击(此处)折叠或打开

  1. #include <stdio.h>

  2. int main ()
  3. {
  4.         if (ptrace (PTRACE_TRACEME, 0, 1, 0) < 0)
  5.         {
  6.                 printf ("DEBUGGING... Bye\n");
  7.                 return 1;
  8.         }
  9.         printf ("Hello\n");
  10.         return 0;
  11. }
一个进程只能同时被一个进程调试,这个程序如果正在被调试那么它调用了ptrace(PTRACE_TRACEME)的时候就会调用失败。
解决方法:
   
调试的只要在if比较的地方修改一下返回值即可。

总结一下:
    以上这几种anti-debug的方法都比较简单,只要稍微动一下手脚就可以解决掉。任何anti-debug方法都不是完美的,只不过是会增加一点debug成本而已。
阅读(1326) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~