前言:功能键F7和F8在DTDEBUG工具中,都是用来执行指令的,那他们有什么区别呢?下面将为大家讲解在调试阶段F7和F8的区别,以及如何做到简单的反调试。
1、F7/F8的区别
F7:单步步入
F8:单步步过
单步步入与单步步过的区别:F7/F8在普通的指令上是没有任何区别的,都是一步一步往下执行,区别就在于CALL这个指令上。
F7:F7执行call指令的时候会先跳转到call指令里存放的EIP(下一步执行指令的地址),然后再往下一步一步执行。
F8:F8执行call指令的时候会从call指令中的EIP开始一下子执行到ret。
2、调试器实现原理
断点: 0xCC
单步步入:设置EFLAGS的TF位
单步步过:在下一行设置断点
3、断点
1、如何设置断点?
快捷键F2
2、断点的功能
程序执行到断点就会停下来,其实当我们把exe拖进来之后,程序停止时的EIP中的地址就是一个断点。
3、断点的本质是什么?
断点的本质其实是指令INT 3,当CPU执行的时候,见到INT 3 的时候就会停下来,停在调试器里。
当我们F2设置断点的时候,那么这一行汇编对应的机器码的第一个字节就已经被改写为 0xCC ,尽管调试器不会显示,但是实际上已经进行了更改,调试器在执行到这里检测到了 0xCC 之后就会断下。(0xCC是int 3 的机器码。)
4、CALL和RET指令
CALL指令:将call指令后的值存入EIP(下一次执行的地址),然后当前call语句下一行的地址存入esp栈指针寄存器中,最后栈指针-4。其原理可以通过两个指令实现。
假设:CALL 0x401141
F7运行,不能使用F8,因为我们还没做RET。
结果:
分析:一个CALL 0x401141本质上就是:
JMP 401141H
MOV dword ptr ds:[esp-4],401131H -- 这一行应该在401141的地方执行
SUB esp,4
RET指令:返回。将当前esp内存放的值取出,放入eip变为下一次执行的地址,然后esp+4。
F8执行两次。
RET指令就是返回到当前堆栈中存放的地址,原理如下:
JMP dword ptr ds:[esp]
ADD esp,4 -- 这一行应该在上一步esp中的地址执行
5、简单的反调试Fake F8
Fake F8就和他的名字一样Fake:欺骗,也就是骗别人使用F8进行调试,因为F7是一步一步的执行,但是F8是一下子执行到RET。
演示:F7
执行一次:
执行两次:
执行三次:
执行四次:
演示:F8
执行一次:
我们就是可以通过F8一下子会执行完,但F7是一步一步的执行这一特性,如果说F8执行CALL开始到RET返回的中间,堆栈中的值做了修改,那么RET的时候,返回的就是堆栈中此时的地址,这样的话你就做到了反调试。
比如:CALL 0x233244,然后这条指令所在的地方假如是:0x233000,假设它的下一个指令地址是:0x233001。
那么它会跳到233244这个地址,执行233244地址中的指令 ,然后将233001存入堆栈中,以便之后的返回。假设此时233244地址中的指令是:mov dword ptr ds:[esp],11223344H
那么他就会修改堆栈中的值为11223344,这个时候如果执行到RET指令,就会跳转到11223344地址去执行指令,这个时候就到了人生地不熟的地方了,调试员也会懵逼,这就做到了最简单的反调试。
那么,是否会有疑问说:那这样的话一直摁F7不就行了,大不了不用F8。
不行,因为实际做反调试的时候,并不仅是这么简简单单的几条指令就返回。我们可以在从单个CALL指令跳转到的地址到RET之前,再次调用CALL跳转到其他地址。也就是一个CALL之后到RET返回之前,可以在多弄几个CALL,然后第二波的CALL中再多弄几个CALL,直到最后可能有几个或者几十个CALL指令,然后每条CALL指令之后到RET指令之前都设置几百个或者几万个没用的指令,比如mov来mov去的那种。最终只在一个CALL和RET之间是可以正常返回的,也就是说没有修改堆栈的地址,其他CALL到RET中,都设置有修改堆栈值的命令。
这样一来,面对成千上万的指令,而且基本都是没用的,如果调试员还是一直F7,那么执行了几万条指令都没用,估计会气的受不了,但是如果他认为这个call语句中不会有错,摁下了F8,那么其中一条语句就会把他带到人生地不熟的地方,这样他就被骗了。
这是一个特别基础的反调试,本人不是从事该行业,只是看了一些教程,有些懵,所以记录了下来,如果有错,还望大佬指正。