DFCGVC编译器优化分析-逆向工程和破解适用。[color]
EVE[color=#008000]跃迁。。5个跳跃,,趁这个时间我写点东西。
希望能够对编程不是很熟悉的逆向工程者一些帮助
一样以VC8编译器为例,有可能穿插一点 Inter编译器。
Release为主。 Debug版本基本都看得懂而且,一般也不会用Debug版本发布软件。
1. 先从函数框架说起走
代码这样子:
#include "stdafx.h"
#include <stdlib.h>
void study1()
{
__asm int 3
char str[10];
gets(str);
printf("你输入的内容是\n%s",str);
__asm int 3 }
int _tmain(int argc, _TCHAR* argv[])
{
study1();
system("PAUSE");
return 0;
}
默认情况Release编译吧。以两个__ASM int 3之间为代码
汇编代码:
#include "stdafx.h"
#include <stdlib.h>
void study1()
{
__asm int 3
char str[10];
gets(str);
printf("你输入的内容是\n%s",str);
__asm int 3
}
int _tmain(int argc, _TCHAR* argv[])
{
00401000 sub esp,10h
00401003 mov eax,dword ptr [___security_cookie (403000h)]
00401008 xor eax,esp
0040100A mov dword ptr [esp+0Ch],eax
study1();
0040100E int 3
0040100F lea eax,[esp]
00401012 push eax
00401013 call dword ptr [__imp__gets (4020A4h)]
00401019 lea ecx,[esp+4]
0040101D push ecx
0040101E push offset string "\xc4\xe3\xca\xe4\xc8\xebbbbbbbbbbbb" (4020F4h)
00401023 call dword ptr [__imp__printf (40209Ch)]
00401029 add esp,0Ch
0040102C int 3
system("PAUSE");
0040102D push offset string "PAUSE" (402108h)
00401032 call dword ptr [__imp__system (402098h)]
return 0;
}
00401038 mov ecx,dword ptr [esp+10h]
0040103C add esp,4
0040103F xor ecx,esp
00401041 xor eax,eax
00401043 call __security_check_cookie (40104Ch)
00401048 add esp,10h
0040104B ret
我们用默认选项都可以看得出函数已经被内联了。
00401000 sub esp,10h
分配16字节。 这里注意一下,一般的其他语言可能也多半是会用函数框架也就是push esp
Mov ebp,esp
然后靠ebp来引用局部变量,这一点在VC中是没有的。
你一定会觉得很奇怪明明分配的10字节怎么会变成了16字节。
答案是这样的。
00401003 mov eax,dword ptr [___security_cookie (403000h)]
00401008 xor eax,esp
0040100A mov dword ptr [esp+0Ch],eax
保存esp函数指针防止堆溢出。主要是在VC中靠esp引用局部变量所以千万错不得。。
但是还有两字节呢? 是这样的。VC中要进行指针对其 也就是说一般情况下会使4的倍数。
试验一下:
改为:
char str[12];
gets(str);
代码依然是
00401000 sub esp,10h
00401003 mov eax,dword ptr [___security_cookie (403000h)]
00401008 xor eax,esp
0040100A mov dword ptr [esp+0Ch],eax
下面再来说说:
2. 关于ecx指针。在VC中虽然可以指定链接方式也就是C样式还是标准API样式或者是 Fastcall
不过一般而言是C样式 Fastcall相对用的较少。另外还有thiscall方式。也就是类指针。我们来看看MSDN
The __thiscall calling convention is used on member functions and is the default calling convention used by C++ member functions that do not use variable arguments. Under __thiscall, the callee cleans the stack, which is impossible for vararg functions. Arguments are pushed on the stack from right to left, with the this pointer being passed via register ECX, and not on the stack, on the x86 architecture.
ThisCall的定义如上所示
我们可以看到考ecx传递指针,参数有调用者恢复,以至于可以传递参数不定的函数。
还是来看段代码
void study2()
{
__asm int 3
CString MyStr;
MyStr="PeDiy By Fox";
puts(MyStr);
__asm int 3
}
为了方便研究我们去掉了esp检查和C++异常
int _tmain(int argc, _TCHAR* argv[])
{
00401040 push ecx
00401041 push esi
00401042 push edi 保护寄存器就不说了
//study1();
study2();
00401043 int 3
00401044 mov eax,dword ptr [ATL::g_strmgr (403380h)]
00401049 mov edx,dword ptr [eax+0Ch]
0040104C mov ecx,offset ATL::g_strmgr (403380h)
00401051 call edx
00401053 add eax,10h
00401056 mov dword ptr [esp+8],eax
0040105A mov edi,0Ch
0040105F lea eax,[esp+8]
00401063 call ATL::CSimpleStringT<char,0>::SetString (4010B0h)
00401068 mov esi,dword ptr [esp+8]
0040106C push esi
0040106D call dword ptr [__imp__puts (4020C8h)]
00401073 add esp,4
00401076 int 3
00401077 lea eax,[esi-10h]
0040107A lea ecx,[eax+0Ch]
0040107D or edx,0FFFFFFFFh
00401080 lock xadd dword ptr [ecx],edx
00401084 dec edx
00401085 test edx,edx
00401087 pop edi
00401088 pop esi
00401089 jg main+55h (401095h)
0040108B mov ecx,dword ptr [eax]
0040108D mov edx,dword ptr [ecx]
0040108F push eax
00401090 mov eax,dword ptr [edx+4]
00401093 call eax
system("PAUSE");
00401095 push offset string "PAUSE" (402160h)
0040109A call dword ptr [__imp__system (4020C4h)]
return 0;
004010A0 xor eax,eax
}
004010A2 add esp,8
004010A5 ret
注意,这里不是什么类型的CALL而是为了减小体积而来的代码内联,不存在保护寄存器之类的问题。
00401044 mov eax,dword ptr [ATL::g_strmgr (403380h)]
00401049 mov edx,dword ptr [eax+0Ch]
0040104C mov ecx,offset ATL::g_strmgr (403380h)
00401051 call edx
后面的就不详细分析。我要说的是。最后一句004010A2 add esp,8
并不代表局部变量为2个DWORD而是只有一个。
原因是这样的,由于才用ESP传递指针,所以任何push pop操作都会影响指针。
而最前面就有三个PUSH 最后面对应的寄存器却只有两个。POP
那么实际上就只有一个变量而本身call dword ptr [__imp__system (4020C4h)]就要恢复 add esp,4 再加上一个指针变量 就刚好是 add esp,8,可见编译器的高度优化。把保护寄存器和分配局部变量合并了、。令人佩服。
不怎么好读得懂。。。:(
再说一下
3:结构和数组。
struct test
{
int a;
char b;
};
void study3()
{
__asm int 3
DWORD MyStr[]={'PeDi','Y\0'};
test MyTest;
MyTest.a='LOVE';
MyTest.b='\0';
puts((char *)&MyTest);
puts((char *)MyStr);
__asm int 3
}00401020 /$ 83EC 10 sub esp,10
00401023 |. 56 push esi
00401024 |. CC int3
00401025 |. 8B35 C4204000 mov esi,dword ptr ds:[<&MSVCR80.puts>] ; MSVCR80.puts
0040102B |. 8D4424 04 lea eax,dword ptr ss:[esp+4]
0040102F |. 50 push eax ; /s
00401030 |. C74424 10 69446550 mov dword ptr ss:[esp+10],50654469 ; |
00401038 |. C74424 14 00590000 mov dword ptr ss:[esp+14],5900 ; |
00401040 |. C74424 08 45564F4C mov dword ptr ss:[esp+8],4C4F5645 ; |
00401048 |. C64424 0C 00 mov byte ptr ss:[esp+C],0 ; |
0040104D |. FFD6 call esi ; \puts
0040104F |. 8D4C24 10 lea ecx,dword ptr ss:[esp+10]
00401053 |. 51 push ecx ; /s
00401054 |. FFD6 call esi ; \puts
00401056 |. 83C4 08 add esp,8
00401059 |. CC int3
0040105A |. 68 50214000 push 编译器学.00402150 ; /command = "PAUSE"
0040105F |. FF15 C0204000 call dword ptr ds:[<&MSVCR80.system>] ; \system
00401065 |. 83C4 04 add esp,4
00401068 |. 33C0 xor eax,eax
0040106A |. 5E pop esi
0040106B |. 83C4 10 add esp,10
0040106E \. C3 retn
分配10个字节, 后面就是对堆操作. 但值得注意的是
0040102B |. 8D4424 04 lea eax,dword ptr ss:[esp+4]
0040102F |. 50 push eax ; /s
00401030 |. C74424 10 50654469 mov dword ptr ss:[esp+10],69446550 ; |
00401038 |. C74424 14 59000000 mov dword ptr ss:[esp+14],59 ; |
00401040 |. C74424 08 4C4F5645 mov dword ptr ss:[esp+8],45564F4C ; |
00401048 |. C64424 0C 00 mov byte ptr ss:[esp+C],0 ; |
第一句没有错,但为什么后面没有对[esp+4]操作了。。push eax。。这个。 因此相应的就应该是
从esp+8开始
以及对其。即使是CHAR也是一个INT所以在WINDOWS编程中应当尽量就行4字节操作(64位为8字节)
今天就讲到这么里
以后我会介绍 算数运算的编译器优化,以及条件语句。