欺骗单步步过F8导致程序跑飞
前言:欺骗单步步过F8导致程序跑飞的笔记
单步步过跑飞的原理
我们这里说的跑飞就是在调试器中调试的时候为什么有时候在CALL F8的时候就会导致程序跑飞的现象
首先需要知道的一点就是F8单步步过CALL的简单实现,实际上是调试器会在CALL+5个字节,也就是调试器会帮你在CALL XXXX的地址的下一行地址上打一个断点,那么此时如果CALL中执行的代码最后返回的不是下一行地址,那么程序就会断在下一行地址了,而最后CALL跳转的地址都没有打过断点,这就会导致CPU一直在执行机器码,这就是跑飞现象的原因
简单的代码实现
我这里通过代码简单的模拟一个F8单步步过跑飞的现象
#include<stdio.h>
int iEax;
int iRet;
void _declspec(naked) test01()
{
printf("cheat f8 test...\n");
__asm
{
mov iEax, eax; // save eax
mov eax, [esp];
add eax, 7;
mov iRet, eax;
pop eax;
mov eax, iEax;
push iRet;
ret;
}
}
void test02()
{
printf("test02...\n");
}
int main()
{
int a = 0;
test01();
a = 1;
printf("test...");
return 0;
}
载入到调试器中进行调试,调用处如下位置
此时如果F8的话,可以看到程序直接终止了,如下图所示
如果F7步入的话,那么还能继续执行,首先F7进到CALL中
这里继续单步,然后就要执行返回了RET了
可以看到继续执行了
所以这里对于F8跑飞的解决方式就是对CALL进行单步步入即可
但是一般在程序中为了F8跑飞的实现,程序可能会多个嵌套的跑飞,所以你也需要多个F7单步步入来进行调试
如何识别是否是跑飞CALL
可以先看下CALL的跳转,如果就在附近的话,那么就F7单步步入慢慢跟就好了
壳中的应用
在壳用跑飞其实很常见的,自己这里拿一个来Yoda's Crypter(1.2)来演示跑飞效果
将壳程序载入到调试器中,可以看到pushad,那么这里就直接用ESP定律即可,接着F9你就会来到如下的地方
此时这个CALL就会导致跑飞,因为该CALL中就直接让你跳到别的地方,如果F8步过CALL的话,那么就直接跑飞了,可以看到F8步过CALL之后就直接来到了其他的地方
如果是F7跟入的话,此时会执行这两条
0040541B 58 pop eax ; UnPackMe.004053FE
0040541C C3 retn
而pop eax之后,此时的栈顶就是OEP的地址,那么单步走的话就直接来到OEP了,CALL下面的语句就不会执行