VC下防止反汇编的办法(1)

最近在看IDA的书,讲汇编语言的部分提到了一种防止递归向下汇编器逆向程序的方法

这里esp指向栈顶,也就是调用方最后入栈的返回地址。然而实际在VC2017里用内联汇编这么做是不行的,原因可以看看VC生成的汇编 代码:

 1 int __stdcall func1(int param)
 2 {
 3 00AC10A0  push        ebp  
 4 00AC10A1  mov         ebp,esp  
 5 00AC10A3  sub         esp,8  
 6     int local = param;
 7 00AC10A6  mov         eax,dword ptr [param]  
 8 00AC10A9  mov         dword ptr [local],eax  
 9     int local2 = 1 + param;
10 00AC10AC  mov         ecx,dword ptr [param]  
11 00AC10AF  add         ecx,1  
12 00AC10B2  mov         dword ptr [local2],ecx  
13     _asm{
14         add dword ptr[ebp+4],13
15 00AC10B5  add         dword ptr [ebp+4],0Dh  
16     }
17     return param*2;
18 00AC10B9  mov         eax,dword ptr [param]  
19 00AC10BC  shl         eax,1  
20 }
21 00AC10BE  mov         esp,ebp  
22 00AC10C0  pop         ebp  
23 }

可以看到VC生成的汇编代码中添加了一些前缀后缀:

前缀用来保存调用前堆栈顶ebp,还有设置新的堆栈顶位置到ebp。如果有局部变量,还要减少esp位置(相当于入栈几个未知数据)以留出局部变量的位置。注意函数堆栈是从内存大编号向小编号堆叠的,越大的地址编号越靠下,就像一个金字塔,下大上小。

后缀用来清理堆栈(mov esp,ebp),并且从堆栈中恢复此次调用之前的ebp(pop ebp)。不难发现在被调用的函数体内修改函数返回地址的话,就需略过ebp的位置。因此内嵌汇编的那一句需要用ebp+4来得到返回地址指针。后面地址+13是略过的调用方的一个printf方法调用,要跳过多少代码可以在反汇编窗口自行查看地址计算一下。

下面是调用方的代码:

int main()
{
00AC1002  in          al,dx  
00AC1003  sub         esp,1Ch  
00AC1006  mov         eax,dword ptr [__security_cookie (0AC3000h)]  
00AC100B  xor         eax,ebp  
00AC100D  mov         dword ptr [ebp-4],eax  
    int d = 10;
00AC1010  mov         dword ptr [d],0Ah  
    func1(10);
00AC1017  push        0Ah  
00AC1019  call        func1 (0AC10A0h)  
    printf("loc1
");
00AC101E  push        0AC20F8h  
00AC1023  call        printf (0AC1140h)  
00AC1028  add         esp,4  
    printf("loc2
");
00AC102B  push        0AC2100h  
00AC1030  call        printf (0AC1140h)  
00AC1035  add         esp,4  
    int a[] = {0,1,2,3,4};
00AC1038  mov         dword ptr [a],0  
00AC103F  mov         dword ptr [ebp-14h],1  
00AC1046  mov         dword ptr [ebp-10h],2  
00AC104D  mov         dword ptr [ebp-0Ch],3  
00AC1054  mov         dword ptr [ebp-8],4  
    printf("%d",func2(a));
00AC105B  lea         eax,[a]  
00AC105E  push        eax  
00AC105F  call        func2 (0AC10D0h)  
00AC1064  add         esp,4  
00AC1067  push        eax  
00AC1068  push        0AC2108h  
00AC106D  call        printf (0AC1140h)  
00AC1072  add         esp,8  
    printf("writing code...
");
00AC1075  push        0AC210Ch  
00AC107A  call        printf (0AC1140h)  
00AC107F  add         esp,4  
    func3();
00AC1082  call        __vcrt_va_start_verify_argument_type<char const * const> (0AC10F0h)  
    getchar();
00AC1087  call        dword ptr [__imp__getchar (0AC20A8h)]  
    return 0;
00AC108D  xor         eax,eax  
}

还有要注意的是这里为了防止代码优化,要关闭vc的编译优化选项。用以上这种方法可以配合一些跳转让反汇编的工具不能正确预测哪部分是代码区,从而达到隐藏一部分代码的目的。

以上是STDCALL调用约定的例子,cdecl和其他约定的以后再尝试整理。

原文地址:https://www.cnblogs.com/fancybit/p/antiDecompile1.html