一般来说,用IDA反汇编程序时,IDA会自动识别出main函数。如果我们不是用IDA反汇编,那又怎么识别main函数呢?
1、WinMain函数的识别
先来看看WinMain函数的原型:
int WINAPI WinMain(
HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // pointer to command line
int nCmdShow // show state of window
);
1)首先在大多数情况下下,WinMain看起来是启动代码中参数最丰富的函数;
2)其次,放在堆栈中的最后一个变量hInstance是最可能即时能过调用GetModuleHandleA函数进行获取的。例如,在启动代码中,在遇到一个CALL GetMoudleHandleA类型的语句以后,就可以十分自信地断言,接在函数后面的一定是WinMain函数;
3)最后,对WinMain的调用通常放在启动函数代码结尾部分附近,它后面通常跟有诸如exit或者XcptFilter之类的两到三个函数。
我们来看看实例:
00401921 |> \6A 0A push 0A
00401923 |. 58 pop eax
00401924 |> 50 push eax
00401925 |. 56 push esi
00401926 |. 53 push ebx
00401927 |. 53 push ebx ; /pModule
00401928 |. FF15 00204000 call dword ptr [<&KERNEL32.GetModuleH>; \GetModuleHandleA
0040192E |. 50 push eax
0040192F |. E8 5E000000 call 00401992
00401934 |. 8945 98 mov dword ptr [ebp-68], eax
00401937 |. 50 push eax ; /status
00401938 |. FF15 B0214000 call dword ptr [<&MSVCRT.exit>] ; \exit
0040193E |. 8B45 EC mov eax, dword ptr [ebp-14]
00401941 |. 8B08 mov ecx, dword ptr [eax]
00401943 |. 8B09 mov ecx, dword ptr [ecx]
00401945 |. 894D 88 mov dword ptr [ebp-78], ecx
00401948 |. 50 push eax
00401949 |. 51 push ecx
0040194A |. E8 15000000 call <jmp.&MSVCRT._XcptFilter>
0040194F |. 59 pop ecx
00401950 |. 59 pop ecx
00401951 \. C3 retn
再来看看call 00401992处的代码:
00401992 /$ FF7424 10 push dword ptr [esp+10]
00401996 |. FF7424 10 push dword ptr [esp+10]
0040199A |. FF7424 10 push dword ptr [esp+10]
0040199E |. FF7424 10 push dword ptr [esp+10]
004019A2 |. E8 43000000 call <jmp.&MFC42.#1576_AfxWinMain>
004019A7 \. C2 1000 retn 10
2、DllMain函数的识别:
识别DllMain比WinMain困难得多,首先,DllMain的原型相对来说不那么复杂,并且不包含任何特别的内容:
BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // handle to DLL module
DWORD fdwReason, // reason for calling function
LPVOID lpvReserved // reserved
);
其次,对它的调用来自一个令人十分难忘的函数__DllMainCRTStartup的深处,并且没有办法容易地确定这就是我们需要的调用。
不过,调用里面还是存在一些蛛丝马迹。当初始化失败时,DllMain会返回FALSE。__DllMainCRTStartup的代码会对该值班进行检查,并且跳转到函数的发问也是可能的。在启动函数的函数体内包含的这类跳转分支非常少,而且其中仅有一个分支通常会连接到接受三个参数的那个函数。
我们来看看实例:
1003DEDF 57 push edi
1003DEE0 56 push esi
1003DEE1 53 push ebx
1003DEE2 E8 D9C8FCFF call 1000A7C0
1003DEE7 8945 E4 mov dword ptr [ebp-1C], eax
1003DEEA 83FE 01 cmp esi, 1
1003DEED 75 24 jnz short 1003DF13
1003DEEF 85C0 test eax, eax
1003DEF1 75 20 jnz short 1003DF13
3、main函数的识别:
main函数是在mainCRTStartup函数中调用的。main函数仅仅接受两个参数:
int main (int argc, char **argv)
这并不足以将它与别的函数区分开。然而,命令的关键字不仅能够通过参数进行访问,而且能够通过全局变量__argc与__argv进行存取。(全局变量通过直接对内存进行寻址而即刻使自己暴露无遗。换句话说,对全局变量的引用是以诸如MOV EAX,[401066]的形式完成的,其中,0x401066是全局变量的地址)。由此可知,针对main进行的调用通常是如下形式的:
00403BF8 |. A1 B4014100 mov eax, dword ptr [4101B4]
00403BFD |. A3 B8014100 mov dword ptr [4101B8], eax
00403C02 |. 50 push eax
00403C03 |. FF35 AC014100 push dword ptr [4101AC] ;-->全局变量
00403C09 |. FF35 A8014100 push dword ptr [4101A8] ;-->全局变量
00403C0F |. E8 ECD3FFFF call 00401000
00403C14 |. 83C4 0C add esp, 0C
00403C17 |. 8945 E4 mov dword ptr [ebp-1C], eax
00403C1A |. 50 push eax
00403C1B |. E8 39160000 call 00405259
main函数返回的结果被传递给紧接在它之后的那个函数(通常,这就是结束程序的函数)。我们进入到call 00405259内部,一步步步进,可进入到退出的程序:
0040527B /$ 57 push edi
0040527C |. 6A 01 push 1
0040527E |. 5F pop edi
0040527F |. 393D D4014100 cmp dword ptr [4101D4], edi
00405285 |. 75 11 jnz short 00405298
00405287 |. FF7424 08 push dword ptr [esp+8] ; /ExitCode
0040528B |. FF15 20B04000 call dword ptr [<&KERNEL32.GetCurrent>; |[GetCurrentProcess
00405291 |. 50 push eax ; |hProcess
00405292 |. FF15 1CB04000 call dword ptr [<&KERNEL32.TerminateP>; \TerminateProcess
00405298 |> 837C24 0C 00 cmp dword ptr [esp+C], 0
0040529D |. 53 push ebx
0040529E |. 8B5C24 14 mov ebx, dword ptr [esp+14]
004052A2 |. 893D D0014100 mov dword ptr [4101D0], edi
004052A8 |. 881D CC014100 mov byte ptr [4101CC], bl
004052AE |. 75 3C jnz short 004052EC
004052B0 |. A1 F0064100 mov eax, dword ptr [4106F0]
004052B5 |. 85C0 test eax, eax
004052B7 |. 74 22 je short 004052DB
004052B9 |. 8B0D EC064100 mov ecx, dword ptr [4106EC]
004052BF |. 56 push esi
004052C0 |. 8D71 FC lea esi, dword ptr [ecx-4]
004052C3 |. 3BF0 cmp esi, eax
004052C5 |. 72 13 jb short 004052DA
004052C7 |> 8B06 /mov eax, dword ptr [esi]
004052C9 |. 85C0 |test eax, eax
004052CB |. 74 02 |je short 004052CF
004052CD |. FFD0 |call eax
004052CF |> 83EE 04 |sub esi, 4
004052D2 |. 3B35 F0064100 |cmp esi, dword ptr [4106F0]
004052D8 |.^ 73 ED \jnb short 004052C7
004052DA |> 5E pop esi
004052DB |> 68 88D04000 push 0040D088
004052E0 |. 68 80D04000 push 0040D080
004052E5 |. E8 2A000000 call 00405314
004052EA |. 59 pop ecx
004052EB |. 59 pop ecx
004052EC |> 68 94D04000 push 0040D094
004052F1 |. 68 8CD04000 push 0040D08C
004052F6 |. E8 19000000 call 00405314
004052FB |. 59 pop ecx
004052FC |. 59 pop ecx
004052FD |. 85DB test ebx, ebx
004052FF |. 5B pop ebx
00405300 |. 75 10 jnz short 00405312
00405302 |. FF7424 08 push dword ptr [esp+8] ; /ExitCode
00405306 |. 893D D4014100 mov dword ptr [4101D4], edi ; |
0040530C |. FF15 0CB04000 call dword ptr [<&KERNEL32.ExitProces>; \ExitProcess
00405312 |> 5F pop edi
00405313 \. C3 retn