Compilation Analysis of Function Call in the VC Environment [Original] Frontier: For some details in our usual programming, such as __stdcall and __cdecl compiler how to deal with us, the variables in the function and the variables come out in the end, which places Wait a few columns. This article will analyze the assembly language executed by everyone with everyone, and master your own code by mastering this process, making yourself knowing and optimizing your code. Author: tink joints, contact e-mail: waterpub @ mail.9cbs.net, MSN: waterpub_cn@hotmail.com, my QQ group 3226292, reproduced Please keep the full document.
1. Environment: The development environment I use is VC7.1. Its Release Single-step debugging requires the following modifications to the project properties: "C " - "" General "-" "debug information format" is changed: "for use "Edit and continue" program database (/ zi) "" C "" Optimization "-" Optimization "change to: Disable (/ OD) If you are a VC6 environment, you can modify the Release version attribute: check Win32 Release then Project- "setting-" C / C - "category-" general- "optimization-" disable (debug) - "Debug info-" Program Database- "Link ---" generate debug info hook 2.c Language code, as follows: / *** Start ********************************************** ************* / # include "stdafx.h" int __cdecl add (int A, int b) {INT C; c = a b; returnif;} int _tmain (int Argc) , _Tchar * argv []) {IRESULT = Add (123, 456); Printf ("/ n *********** / n"); return 0;} / *** End * *********************************************************** ** / 3. Program analysis process, F10 single-step startup program: / *** Start *************************************** *********************** / INT _TMAIN (int Argc, _tchar * argv []) {00401020 Push EBP creates stack frame 00401021 MOV EBP, ESP deposit Stack base address, EBP = 0012FEE4 (indicating that the default stack size is about 1 megabytes) 00401023 Sub ESP, 44h vacant a stack area, I don't know what to do, can you have a high-person guidance? ××××××××××××××××××× In an assembly debug article of a VC6, it is stored in this stack in a compile of VC6, but I am debugging at VC7 and discovers the IRESULT address. Not known as the stack range, ××××××××××××××××××××× 00401026 Push EBX protection site 00401027 Push ESI 00401028 Push EDI IRESULT = Add (123, 456 00401029 PUSH 1C8H Parameter Fix No. 0040102E PUSH 7BH 00401030 CALL ADD (401000H) Execute function call, press F11 Jump to this color ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× Call instruction details:
The CALL instruction is a combination of PUSH and JMP, first execute the PUSH EIP to put the current address into the stack (the function call is required to return it), then call the JMP instruction. Because the normal instruction cannot operate on the EIP, there is often the following statement in many virus programs: call @@ get_eip @@ get_eip: POP EBP; obtained Eip ×××××××××××××××××××× × × 00401035 Add ESP, 8 release 123 and 456 variables Piles 00401038 MOV DWORD PTR [IRESULT], EAX Removes the calculation result from EAX ("/ n *********** / n") The following is a function test 0040103b push offset string "/ n *********** / n" (4060fch) Variable Address Add Store 00401040 Call Printf (401051H) Perform a CALL call function 00401045 Add ESP, 4 Variable Address Factory Return 0; 00401048 XOR EAX, EAX makes EAX 0, EAX is the value of returning to the operating system} 0040104A POP EDI 0040104B POP ESI 0040104c POP EBX Recovery Scenario 0040104D MOV ESP, EBP Balance Stack 0040104F Pop EBP Release Stack Frame 00401050 RET Returns the operating system call function definition: int __cdecl add (int A, int b) {00401000 PUSH EBP establishs stack frame 00401001 MOV EBP, ESP deposit into the stack base address 00401003 SUB ESP, 44H open variables used in the stacker area, For function internal variables use before execution ESP = 0012FE84, after execution ESP = 0012F E40 ××××××××××××××××××××× can open the memory 0x0012FE8C, see 7B 00 00 00 00 00 00 00, this is the 123 we are incorporated (0x0012FE8C ) And 456 (0x0012FE90) variable ××××××××××××××××××× 00401006 Push EBX protection site 00401007 Push ESI 00401008 Push EDI INT C; c = a b; 00401009 MOV EAX, DWORD PTR [A] The first parameter, that is, [EBP 8] 0040100C ADD EAX, DWORD PTR [B] second parameter, that is, [EBP C]
0040100F MOV DWORD PTR [C], EAX C variable In the stack, the address is 0x0012FE80, which is the top of the variable stack area Return C; 00401012 MOV EAX, DWORD PTR [C] calculation results Deposit Eax} 00401015 POP EDI Reply Site 00401016 POP ESI 00401017 POP EBX 00401018 MOV ESP, EBP balance stack, recycled variable stack area 0040101A POP EBP release stack frame 0040101b Ret back to call address, readers Go to pink and then watch / *** End ****** **************************************************************** / 4. About __cdecl and __stdcall: (VC project default call mode is __cdecl) We will change the above ADD function to __stdcall form, the execution process is as follows, I will make a mark with the above __cdecl calls: / *** Start************************************************* **** / int _tmain (int argc, _TCHAR * argv []) {00401020 push ebp 00401021 mov ebp, esp 00401023 sub esp, 44h 00401026 push ebx 00401027 push esi 00401028 push edi int iResult = add (123, 456); 00401029 PUSH 1C8H 0040102E PUSH 7BH 00401030 CALL ADD (401000H) Note that there is no ADD ESP, 8, why: __ stdcall call mode is released inside the function, so this sentence does not need .
00401035 MOV DWORD PTR [IRESULT], EAX Printf ("/ n *********** / n"); 00401038 Push Offset String "/ N *********** / n "(4060FCh) 0040103D call printf (40104Eh) 00401042 add esp, 4 return 0; 00401045 xor eax, eax} 00401047 pop edi 00401048 pop esi 00401049 pop ebx 0040104A mov esp, ebp 0040104C pop ebp 0040104D ret function part: int __stdcall add (int a, int b) {00401000 push ebp 00401001 mov ebp, esp 00401003 sub esp, 44h 00401006 push ebx 00401007 push esi 00401008 push edi int c; c = a b; 00401009 mov eax, dword ptr [a] 0040100C add eax, dword ptr [b] 0040100F mov dword ptr [c], eax return c; 00401012 mov eax, dword ptr [c]} 00401015 pop edi 00401016 pop esi 00401017 pop ebx 00401018 mov esp, ebp 0040101A pop ebp 0040101B ret 8 This sentence is the statement released in the __stdcall call mode to release the statement inside the function! /***end********************************************* ******** / Description: For the value of the function or the address, everyone has analyzed after this, I believe that beginners can see a lot of detail 5. Initialization of global variables: #include "stdafx .h "const char szname" [] = "http://blog.9cbs.neet/waterpub"; CTestClass () {printf (" ctestclass :: ctestclass () / n ");} ~ ctestclass ) {Printf ("ctestclass :: ~ ctestclass () / n");}}; const ctestclass tobject; int _tmain (int argc, _tchar * argv []) {printf ("/ n ********* **** / n "); return 0;} How to initialize the szname in this program, why didn't the corresponding anti-excitement statement? Szname has been initialized because the main function starts, and all we can't run this place.
When the user starts this EXE program, enter the C / C runtime library code (CRTSTARTUP), which initializes static variables and global variables, and then transfer to the main function. We now set a breakpoint on the green section, then run, the program is disconnected here.
Press (Ctrl Alt C: VC7.1 shortcuts, VC6 has the corresponding menu item), point to the bottom-up call function, can be seen from this: The program is executed by the operating system when executing the "MainCrtStartup" function, simplifies the code I posted below, some very intuitive English did not translate: / *** Start ***************************************************** ********************** / INT WINMAINCRTSTARTUP (void) {int initret; int mainret; osversioninfoa * POSVI; int ManagedApp; POSVI = (OsversionInfoa *) _ Alloca ( SIZEOF (OSVersionInfoA); // Use this function to avoid allocation runtime detection // operating system version related to the allocation of the global storage buffer POSVI-> dwosversionInfosize = sizeof (OsversionInfoA); (void) getversionExa (POSVI); _osplatform = posvi-> dwPlatformId; _winmajor = posvi-> dwMajorVersion; _winminor = posvi-> dwMinorVersion; _osver = (posvi-> dwBuildNumber) & 0x07fff; if (! _osplatform = VER_PLATFORM_WIN32_NT) _osver | = 0x08000; _winver = (_winmajor << 8 ) _winminor; // Is it managed managedApp = check_managed_app (); #ifdef _mt if (! _heap_init (1)) / * Multi-threaded in this way initializes Head * / # else / * _mt * / if (! _heap_init) 0) / * Single-threaded use this mode to initialize the pile * / # endif / * _mt * / fast_error_exit (_rt_heapinit); / * Write message and die * / #ifdef _mt if (! _Mtinit ()) / * Initialize multi-thread * / fast_error_exit (_RT_THREAD); / * write message and die * / # endif / * _MT * / / * * Initialize the Runtime Checks stuff * / # ifdef _RTC _RTC_Initialize (); // initialize runtime # Endif / * _RTC * / / / * * The following is the remaining initialization code (including the initialization of the global variable of my program is here, huh, huh) * Initialization, call main or winmain (executed in TRY) * / __Try { IF (_IOINIT () <0) / * IO initialization, it should be input and output, not clear * / _AMSG_EXIT (_RT_LOWIOINIT); #ifdef wprflag / * get wide cmd line info * / _wcmdln = (wchar_t *) __ crtGetcommandLinew () ;
// String / * get wide environ info * / _wenvptr = (wchar_t *) __ crtGetenvironmentStringsw (); // String IF to get environment variable (_wsetargv () <0) _AMSG_EXIT (_RT_Spacearg); if (_wsetenvp () <0) _AMSG_EXIT (_RT_SPACEENV); # else / * wprflag * / / * get cmd line info * / / _ACMDLN = (char *) getcommandlinea (); / * get environ info * / _AENVPTR = (char *) __ CrtGetenvironmentStringsTRings ); If (_Setargv () <0) _AMSG_EXIT (_RT_Spacearg); if (_SETENVP () <0) _AMSG_EXIT (_RT_SPACEENV); # Endif / * wprflag * / initret = _CINIT (TRUE); / * Global variable initialization, find right here! ! ! * / If (initret = 0!) _Amsg_exit (initret); #ifdef _WINMAIN_ StartupInfo.dwFlags = 0; GetStartupInfo (& StartupInfo); #ifdef WPRFLAG lpszCommandLine = _wwincmdln (); mainret = wWinMain (#else / * WPRFLAG * / lpszCommandLine = _wincmdln (); mainret = WinMain (#endif / * WPRFLAG * / GetModuleHandleA (NULL), NULL, lpszCommandLine, StartupInfo.dwFlags & STARTF_USESHOWWINDOW StartupInfo.wShowWindow:? SW_SHOWDEFAULT); # else / * _WINMAIN_ * / #ifdef WPRFLAG __winitenv = _wenviron MainRet = WMAIN (__ argc, __wargv, _wenviron); # else / * wprflag * / __initenv = _ENVIRON; mainret = main (__ argc, __ARGV, _ENVIRON); // is here to call Main, because the runtime code is in the EXE file In this, it can be called to call the main function (there is no difference with the ordinary function. If you read Win32 assembly, you know that the main or Winmain name is not dead)! # Endif / * WPRFLAG * / #ENDIF / * _Winmain_ * / if (! Managedapp) EXIT (MAINRET); _CEXIT ();} __except (_XCPTFILTER (), getExceptionInformation ()) // Exception is here, such as lost DLL file {/ * * * * Should Never reach here * / mainret = getExceptioncode ();