Compilation Analysis of Function Call in VC Environment [Original]
Frontier: 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 from New, which places are stored in the end, and so on. 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, please keep your full document.
1. Environment: The development environment I use is VC7.1, and its Release single-step debugging requires the following modifications to the project properties:
"C " - "" General "" debug information format "is changed to:" For "editing and continuing" program database (/ zi) "
"C " - "Optimization -" Optimization "is changed: disabled (/ OD)
If you are a VC6 environment, you can modify the Release version attribute as follows:
Select Win32 Release and then Project- "Setting-" C / C - "Category-" General- "Optimization-" disable (debug) - "Debug Info-" Program Database- "Link ---" Generate Debug Info hook hook
2. C language code, as follows:
/***Start********************************************* ******** /
#include "stdafx.h"
INT __CDECL Add (int A, INT B)
{
INT C;
C = a b;
Return C;
}
INT _Tmain (int Argc, _tchar * argv [])
{
INT 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 frames
00401021 MOV EBP, ESP is stored in the stack base, after running EBP = 0012FEE4
(Indicates that the default stack size is about 1 trillion)
00401023 SUB ESP, 44H
Empty out of a stack area, I don't know what to do, can have a high person?
×××××××××××××××××××××
In a container debug article of a VC6, I saw that the variable in Main is stored in this stack, but I found that the IRESULT address is not within the stack in VC7 debugging. I don't know what to explain.
××××××××××××××××××× 00401026 Push EBX protection site 00401027 PUSH ESI
00401028 Push EDI
INT IRESULT = Add (123, 456);
00401029 PUSH 1C8H parameter
0040102E Push 7bh
00401030 Call Add (401000H)
Perform a function call, press F11 to jump to the blue of this article
×××××××××××××××××××××
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 instructions cannot operate on the EIP, there is often a statement in many virus programs:
Call @@ get_eip @@ get_eip: POP EBP; get EIP
×××××××××××××××××××××
00401035 Add ESP, 8 release 123 and 456 variables
00401038 MOV DWORD PTR [IRESULT], EAX Removes results from EAX
Printf ("/ n *********** / N"); below is a function test
0040103B Push Offset String "/ n *********** / n" (4060fch) Variable Address Add Store
00401040 Call Printf (401051H) Performs a Call call function
00401045 Add ESP, 4 variable address outlet
Return 0;
00401048 XOR Eax, Eax makes EAX 0, EAX is the value returned to the operating system
}
0040104A POP EDI
0040104B POP ESI
0040104C POP EBX recovery site
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 Create Stack Frame
00401001 MOV EBP, ESP deposit into the base address
00401003 SUB ESP, 44H opens up the stacker area used by the variable, for internal variables for functions
Before execution, ESP = 0012FE84, after execution, ESP = 0012FE40
×××××××××××××××××××××
Here you can open the memory 0x0012FE8C, see 7b 00 00 00 C8 01 00 00, this is our incoming 123 (0x0012FE8C) and 456 (0x0012FE90) variables
×××××××××××××××××××××
00401006 PUSH EBX Protection Site
00401007 Push ESI00401008 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] The 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 are deposited in EAX
}
00401015 POP EDI Reply site
00401016 POP ESI
00401017 POP EBX
00401018 MOV ESP, EBP Balance Stack, Recycling Variable Stack Area
0040101A POP EBP Release Stack Frame
0040101b Ret returns to the call address, the reader turned from here to the pink
/***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, and 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, and the reason for this sentence: __ stdcall call mode The stack parameter has been released inside the function, so this sentence is not required. 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
004018 MOV ESP, EBP
0040101A POP EBP
0040101B RET 8
This sentence is the __stdcall call mode in the stack parameters
The statement released inside the function!
/***end********************************************* ******** /
Description: For the passage of the function or the address, everyone has analyzed here, I believe that beginners can see a lot of details.
5. Initialization of global variables:
#include "stdafx.h"
Const char szname [] = "
http://blog.9cbs.neet/waterpub ";
Class CTestClass
{
PUBLIC:
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 you perform the corresponding anti-excitement?
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 in English:
/***Start********************************************* ******** /
INT WinMainCrtStartup (Void)
{
Int initret;
Int mainret;
OsversionInfoa * POSVI; int ManagedApp;
POSVI = (OSVersionInfoa *) _ Alloca (Sizeof (osversioninfoa)); // uses this function to avoid the assignment runtime detection of global storage buffers
// Operating system version related code
POSVI-> dwosversionInfosize = sizeof (OsversionInfoa);
(void) getversionExa (POSVI);
_SPLATFORM = POSVI-> dwplatformID;
_winmajor = POSVI-> DWMAJORVERSION;
_winminor = POSVI-> dwminorversion;
_SVER = (POSVI-> dwbuildnumber) & 0x07FFF;
IF (_OSPLATFORM! = VER_PLATFORM_WIN32_NT)
_SVER | = 0x08000;
_winver = (_winmajor << 8) _winminor;
/ / Whether it is a managed program?
ManagedApp = Check_managed_app ();
#ifdef _mt
IF (! _heap_init (1)) / * Multi-threaded in this way is initialized * /
#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 (); // Initialization 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 to run the parameters
/ * Get Wide Environ Info * /
_wenvptr = (wchar_t *) __ crtGetenvironmentStringsw (); / / String of the environment variable
IF (_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 *) __ crtGetenvironmentStrings ();
IF (_setargv () <0)
_AMSG_EXIT (_RT_SPACEARG);
IF (_setenvp () <0)
_AMSG_EXIT (_RT_SPACEENV);
#ENDIF / * WPRFLAG * /
Initret = _CINIT (TRUE); / * Global variable is initialized, it is 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);
// The main call is called here because the runtime code is in the exe file, so you can call the main function (there is no difference with the ordinary function. If you read Win32 compilation, you know that the main or Winmain name is not fixed. Dead)!
#ENDIF / * WPRFLAG * /
#ENDIF / * _WINMAIN_ * /
IF (! managementApp)
exit (mainret);
_cexit ();
}
__except (_xcptfilter (getExceptionCode (), getExceptioninformation ()) // Abnormally here is here, such as lost DLL files
{
/ *
* SHOULD Never Reach Here
* /
MainRet = getExceptioncode ();
IF (! managementApp)
_exit (mainret);
_c_exit ();
} / * end of try - Except * /
Return mainRet;
}
/***end********************************************* ******** /
6.WIN32 startup process
Since the console program we analyze, what is the difference between Win32? The difference is still there (the core code of the launchler is in the CRT0.C file), I have explained the specific analysis method, and Win32 is left to everyone, :) 7. Error may have, Or can be written better, but this dish is only this level, laughing and generous, please ask the master to do not finger. Articles may be modified at any time if you have any questions or good ideas, to my blog (
Http://blog.9cbs.net/waterpub) message, don't stay here, I have less, :)
8. Shenzhen Nanshan Science and Technology Park Science and Technology Industry Building 2005-02-22 17:00:00