content
3. Overview of the overall stack frame
4. Overview of the required disassembly code
3. Function stack frame creation and destruction process
1, _tmainCRTStartup function (calling the main function) stack frame creation
2. The creation of the main function stack frame
3. Execute valid code (variables) in the main function
4. The creation of the Add function stack frame
5. Execute valid code in the Add function
6. Destruction of the stack frame of the Add function
7. Destruction of the main function stack frame
1. The purpose of this paper
- 1. How are local variables created?
- 2. Why is the value of the local variable a random value?
- 3. How do functions pass parameters? What is the order in which the parameters are passed?
- 4. What is the relationship between formal parameters and actual parameters?
- 5. How is the function call done?
- 6. How does the function return after the end of the function call?
When we deeply understand the creation and destruction of function stack frames, the answer is naturally clear. The text begins:
2. Basic knowledge
1. Register
register name | Introduction |
eax | "Accumulator" This is the default register for many add and multiply instructions. |
ebx | The "base address" register stores the base address when addressing memory. |
ecx | Counter, which is the default counter for repeat (REP) prefix instructions and LOOP instructions. |
edx | Always used to put the remainder from integer division. |
yes | source index register |
I know | target index register |
ebp | (stack bottom pointer) "base address pointer", which stores the address and is used to maintain the function stack frame |
esp | (stack top pointer) is specially used as a stack pointer, which stores the address and is used to maintain the function stack frame |
2. Code case
- The compiler that this article depends on: VS2013
#include<stdio.h> int Add(int x, int y) { int z = 0; z = x + y; return z; } int main() { int a = 10; int b = 20; int c = 0; c = Add(a, b); printf("%d\n", c); return 0; }
3. Overview of the overall stack frame
Each function call must open up space for it in the stack area. As in the above code, there are the main function and the Add function visible to the naked eye, and corresponding space needs to be opened up for them, but in fact the main function is also called, when we Press F10 for the above code , press it again when it reaches return 0, and the following interface will pop up
It can be seen from the figure that the main function is called by the __tmainCRTStartup function, and __tmainCRTStartup is called by the mainCRTStartup . Let's take a look at the overall function stack frame development:
- Two important knowledge points:
- Push: put an element on the top of the stack
- pop: remove an element from the top of the stack
Next, the development of the function stack frame will be explained in detail:
4. Overview of the required disassembly code
int main() { 00031410 push ebp 00031411 mov ebp,esp 00031413 sub esp,0E4h 00031419 push ebx 0003141A push esi 0003141B push edi 0003141C lea edi,[ebp+FFFFFF1Ch] 00031422 mov ecx,39h 00031427 mov eax,0CCCCCCCCh 0003142C rep stos dword ptr es:[edi] int a = 10; 0003142E mov dword ptr [ebp-8],0Ah int b = 20; 00031435 mov dword ptr [ebp-14h],14h int c = 0; 0003143C mov dword ptr [ebp-20h],0 c=Add(a,b); 00031443 mov eax,dword ptr [ebp-14h] 00031446 push eax 00031447 mov ecx,dword ptr [ebp-8] 0003144A push ecx 0003144B call 00C210E1 00031440 add esp,8 00031443 mov dword ptr [ebp-20h],eax printf("%d", c); 00241456 mov esi,esp 00241458 mov eax,dword ptr [ebp-20h] 0024145B push eax 0024145C push 245858h 00241461 call dword ptr ds:[00249114h] 00241467 add esp,8 0024146A cmp esi,esp 0024146C call 0024113B return 0; 00241471 xor eax,eax } 00031451 pop edi 00031452 pop esi 00031453 pop ebx 00031454 add esp,0E4h 0003145A cmp ebp,esp 0003145C call __RTC_CheckEsp (03113Bh) 00031461 mov esp,ebp 00031463 pop ebp 00031464 ret
int Add(int x, int y) { 000313C0 push ebp 000313C1 mov ebp,esp 000313C3 sub esp,0CCh 000313C9 push ebx 000313CA push esi 000313CB push edi 000313CC lea edi,[ebp-0CCh] 000313D2 mov ecx,33h 000313D7 mov eax,0CCCCCCCCh 000313DC rep stos dword ptr es:[edi] int z = 0; 000313DE mov dword ptr [ebp-8],0 z = x + y; 000313E5 mov eax,dword ptr [ebp+8] 000313E8 add eax,dword ptr [ebp+0ch] 000313EB mov dword ptr [ebp-8],eax return z; 000313EE mov eax,dword ptr [ebp-8] } 000313F1 pop edi 000313F2 pop esi 000313F3 pop ebx 000313F4 mov esp,ebp 000313F6 pop ebp 000313F7 ret
3. Function stack frame creation and destruction process
1, _tmainCRTStartup function (calling the main function) stack frame creation
According to the above, we already know that the main function is called by the _tmainCRTStartup function. Naturally, we need to open up a stack frame for it. This space should be maintained by the ebp and sep registers, provided that the lower address is high and the upper address is low. As shown in the figure:
At this point, you enter the main function. First, you need to push to push the stack:
Push ebp is to push ebp to the top of the stack. At this time, sep moves to the top of the new stack accordingly, which can be verified by monitoring:
The diagram is as follows:
Next perform the mov operation:
This line of code means to assign sep to ebp, so the position pointed to by ebp is the position pointed to by sep, but the source operation address position remains unchanged, which can be verified by monitoring.
Then perform the sub operation:
The operation is to subtract 0E4h from the esp. At this time, the position of the esp will go up. Observe by monitoring:
At this moment, after the sub operation is executed, it has actually entered the development of the main function stack frame below. So far, the development of the _tmainCRTStartup function stack frame has been completed. See diagram below:
2. The creation of the main function stack frame
Continuing from the above, the diagram is as follows:
Next, perform three push operations: Push ebx, sei, and edi into the stack in sequence, and the corresponding esp should also go up.
Take a look at:
The diagram is as follows:
Then perform the following three steps
Operation lea (load effecitve address) loads the effective address. It is equivalent to putting [ebp+FFFFFF1Ch] into edi. After displaying the symbol name, [ebp+FFFFFF1Ch] is [ebp-0E4h]. The -0E4h has been executed before, and it is executed again and put into edi. Then mov puts 39h into ecx, and then mov puts 0CCCCCCCCh in eax.
The purpose of the above operation is to change all dwords (1 word2 bytes, 2dword4 bytes) 39h times down from the edi just now to 0CCCCCCCCch
Take a look at:
The diagram is as follows:
At this point, the development of the main stack frame has been completed, and the next step is to execute the officially valid code, see below:
3. Execute valid code (variables) in the main function
Next do the following:
First mov put 0Ah (10) on the position of ebp-8, similarly put 14h (20) on ebp-14h, and put 0 on ebp-20h, as shown in the figure:
At this moment, the three variables of a, b, and c have been created, and then the Add function is called: first pass the parameters
First, mov puts ebp-14h (b=20) into eax. Next, push, push the stack to put eax (20) on the top of the stack, and move the corresponding esp. Similarly, mov puts ebp-8 (a=10) into ecx, and then push puts ecx on the top of the stack. as the picture shows:
Then execute the call operation, call the Add function, press F10 to execute the call, press F11, then jump to the inside of the Add function and push the address of the next instruction of the call instruction to the top of the stack. The purpose of this is to easily return to the address when jumping to the Add function to come back, as shown in the figure:
Press F11 to officially enter the Add function and create a stack frame for it. See below for details:
4. The creation of the Add function stack frame
int Add(int x, int y) { 000313C0 push ebp 000313C1 mov ebp,esp 000313C3 sub esp,0CCh 000313C9 push ebx 000313CA push esi 000313CB push edi 000313CC lea edi,[ebp-0CCh] 000313D2 mov ecx,33h 000313D7 mov eax,0CCCCCCCCh 000313DC rep stos dword ptr es:[edi]
The previous operations are the same as the internal operations of the previous main function, which are actually preparing our stack frame for the Add function.
First, push ebp pushes ebp to the top of the stack, then mov assigns esp to ebp, then sub, and esp- goes to 0CCh. This step is to open up space for the Add function, and then push three times, the same as the main function. It is still initialized to CCCCCCCC, and the detailed process will not be repeated. It is the same as the main function above, as shown in the figure:
At this point, the development of the Add stack frame has been basically completed, and the next step is to execute the official and valid code, see below:
5. Execute valid code in the Add function
Continued from above:
int z = 0; 000313DE mov dword ptr [ebp-8],0 z = x + y; 000313E5 mov eax,dword ptr [ebp+8] 000313E8 add eax,dword ptr [ebp+0ch] 000313EB mov dword ptr [ebp-8],eax return z; 000313EE mov eax,dword ptr [ebp-8] }
First, put 0 at the position of ebp-8, then mov puts the value of ebp+8 into eax, and eax is 10 at this time. Then add ebp+0ch to eax, that is to add 20, at this time eax is 30, after adding eax (30) into ebp-8, the final result (30) into z .
At this point, the execution of the valid code inside the Add function is completed, as shown in the figure:
The next step is to return, that is, the destruction of the Add function stack frame, see below:
6. Destruction of the stack frame of the Add function
return z; 000313EE mov eax,dword ptr [ebp-8] } 000313F1 pop edi 000313F2 pop esi 000313F3 pop ebx 000313F4 mov esp,ebp 000313F6 pop ebp 000313F7 ret
The above already knows that the value of ebp-8 (30) has been put into eax at this time, and then three pops are executed. Once popped, esp will be added once, as shown in the figure:
Next, assign ebp to esp, and then pop to pop ebp. At this time, esp also moves. At this time, esp and ebp return to the previous maintenance of the main function stack frame. as the picture shows:
At this point, the esp points to the address of the next instruction of the call instruction. Press F10 again, and the disassembly will look like this:
0003144B call 00C210E1 00031440 add esp,8 00031443 mov dword ptr [ebp-20h],eax printf("%d", c); 00241456 mov esi,esp 00241458 mov eax,dword ptr [ebp-20h] 0024145B push eax 0024145C push 245858h 00241461 call dword ptr ds:[00249114h] 00241467 add esp,8 0024146A cmp esi,esp 0024146C call 0024113B return 0; 00241471 xor eax,eax }
At this point, we will understand that the address of the next instruction that previously stored the call instruction is for the convenience of returning, and the position of the esp changes after the previous ret execution:
At this time, the stack frame of the Add function is really destroyed, and then the main function stack frame is destroyed.
7. Destruction of the main function stack frame
0003144B call 00C210E1 00031440 add esp,8 00031443 mov dword ptr [ebp-20h],eax printf("%d", c); 00241456 mov esi,esp 00241458 mov eax,dword ptr [ebp-20h] 0024145B push eax 0024145C push 245858h 00241461 call dword ptr ds:[00249114h] 00241467 add esp,8 0024146A cmp esi,esp 0024146C call 0024113B return 0; 00241471 xor eax,eax }
Through the disassembly code, we know that the add operation adds 8 to the esp at this time, and the two formal parameters x and y are released at this time, pointing to the position as shown in the figure:
Next, mov puts eax on ebp-20h, and eax is the sum calculated when we issue the Add function. At this time, the sum is brought back by us. Next is the destruction of the main function stack frame, which is the same as the above Add function. The destruction of the stack frame is not much different, so I won't go into details here.
The disassembly code is as follows:
00241471 xor eax,eax } 00031451 pop edi 00031452 pop esi 00031453 pop ebx 00031454 add esp,0E4h 0003145A cmp ebp,esp 0003145C call __RTC_CheckEsp (03113Bh) 00031461 mov esp,ebp 00031463 pop ebp 00031464 ret
4. Summary
At this point, the creation and destruction of the function stack frame is officially over, and the questions (goals) at the beginning of this article can also be clearly understood:
as follows:
- 1. How are local variables created?
First, allocate and initialize the stack frame space for the function, and then allocate a little space for the local variables in the stack frame.
- 2. Why is the value of the local variable a random value?
Because the random value is put in when we open up the stack frame, and when we initialize, the random value is overwritten.
- 3. How do functions pass parameters? What is the order in which the parameters are passed?
Before I want to call the function, I have already pushed and pushed these two parameters into the stack from right to left. When we actually enter the formal parameter function, we can find it in the stack frame of the Add function through the offset of the pointer. formal parameters.
- 4. What is the relationship between formal parameters and actual parameters?
The formal parameter is indeed the space opened up when the stack is pushed. The formal parameter and the actual parameter are only the same in value and independent in space. The formal parameter is a temporary copy of the actual parameter, and changing the formal parameter will not affect the actual parameter.
- 5. How does the function return after the end of the function call?
We have already pushed the address of the next instruction of the call instruction into it before the call. When the function returns after the function call, it will jump to the address of the next instruction of the call instruction, and the return value is brought back through the register.