Preface
In earlier learning, we learned that when a function passes a value to call a parameter, it uses a parameter.
A parameter is a temporary copy of an argument, and changes to the parameter do not affect the values in the argument.
Pass-by and Address Calls 👉 Click on me
Today let's take a look at the argument stack of function calls in assembly language
1. What is a stack area?
A stack is a data structure.
In memory, there are three zones: stack, stack, and static.
Heap areas are mainly used for dynamic memory management and have been described in previous blogs.
Detailed Dynamic Memory Management 👉 Click on me
The stack is the space allocated by the compiler to run the function.
Unlike heap space, which needs to be allocated manually, this part of space is managed automatically by the compiler, and stack frames of functions are created and destroyed automatically.
1.1 small knowledge point of stack area
- Stack areas are used from high to low addresses
- Use of stack areas follows FIFO, LIFO
- Stack area placement is from high address to low address placement: push stack
- Delete from low to high: pop out of stack
2. Knowledge Points
//Code used this time #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 = Add(a, b); printf("%d\n", c); return 0; }
2.1 Register
Common registers are eax, ebx, ecx, edx, among which ebp and esp are more special
The two registers, ebp and esp, store addresses that maintain the function stack frame
- eax/ebx/ecx/edx: Universal register, keep temporary data
- ebp: stack low pointer
- esp: stack top pointer
- eip: Instruction register that holds the address of the next instruction for the current instruction
2.2 Main function call
Each function call creates a space in the stack
We know that the main function is the entry to the program
In fact, the main function is also called by other functions
- mainCRTStartup function call_u tmainCRTStartup
- _u The tmainCRTStartup function calls the main function
The compiler first opens up a portion of space at the high memory address for mainCRTStartup and u tmainCRTStartup functions, which call the main function.
In VS2019, press F10 to debug, after the small yellow arrow appears, right-click to go to disassembly to open the display interface of assembly language in debugging
3. Explain item by item
3.1 Start with main
Let's start with the first part of the code, explaining it sentence by sentence
push ebp//Open up space at the top of the stack to store the corresponding value of the ebp register mov ebp,esp//Pass the value of esp into ebp (that is, move the ebp pointer to where it was originally pointed) sub esp,0E4h//Subtract the contents of the ESP by 0E4h (move the ESP to the location of the original esp-0E4h) push ebx//Place ebx on top of stack push esi//Place esi on top of stack push edi//Place edi on top of stack
- lea:load effecticve address Load valid address
- dword:double word - 4 bytes
lea edi,[ebp-24h]//Put the ebp-24h address in edi mov ecx,9//Put 9 in ecx, corresponding to decimal 36 mov eax,0CCCCCCCCh//Put 0CCCCCCh into eax rep stos dword ptr es:[edi]//Initialize all data for edi down ecx address to 0CCCCCCh
Press F10 down, after rep step, you can see that 36 bytes of data have been initialized to 0CCCCCCh
Continue running to see how the compiler initializes variables a and b
- VS2019 is a small-end storage
int c =Add(a,b); mov eax,dword ptr [b]//Put the contents of b into eax push eax //Place eax on top of stack mov ecx,dword ptr [a]//Put the contents of a into ecx push ecx //Place ecx on top of stack call _Add (01A10B4h) //Place this address at the top of the stack (the address of the next instruction under the call instruction)
This last call is critical and will be used later
3.2 Call Add
Press F11 to enter the Add function
push ebp//Move ebp up mov ebp,esp//Put esp content into EBP (mobile ebp) sub esp,0CCh//esp+0CCh (opens up space for Add) push ebx//Place ebx on top of stack push esi//Place esi on top of stack push edi//Place edi on top of stack
lea edi,[ebp-0Ch]//ebp-0Ch space mov ecx,3//3 Save in ecx mov eax,0CCCCCCCCh//Save in eax rep stos dword ptr es:[edi]//Spatial initialization of esp down 0ch mov ecx,offset _6A27082D_test@c (024C003h) call @__CheckForDebuggerJustMyCode@4 (024131Bh)
Continuing down, the register initializes the data at the Z address to zero
mov eax,dword ptr [x]//Put the value of x into eax add eax,dword ptr [y]//Add the value of y to eax, which is x+y mov dword ptr [z],eax//Put the value of eax into the variable z
return z; mov eax,dword ptr [z]//Put the value of parameter z into eax
pop edi//Delete stack pop esi//pop directive created for edi puts ESI value into ESI (equal to unchanged) pop ebx//every pop, ESP adds ESP once to high displacement, 0CCh//esp address+0CCh, that is, the stack space CMP ebp exiting Add program, esp//compares ESP value with ebp
call __RTC_CheckEsp (0241244h) mov esp,ebp//ebp is assigned to esp, where ESP and ebp are the same
pop ebp//Eject ebp
When the pop-up command is executed here
- Assigns the start address of the main function pointed to by ebp to the ebp pointer
- Move the esp pointer one bit higher
The final result is that esp and ebp restart maintaining the stack space of the main function as shown in the following figure
3.3 Return to main function
ret
Call_mentioned earlier The Add (01A10B4h) command is very important. In fact, when the ret command is executed, the esp pointer points to the address of the next command for the call command stored on the top of the stack, and this address is also pop ped away.
[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-yj4OazoR-1646413319866)(.../.../.../.../AppData/Roaming/Typora/typora-user-images/image-20220304235511090.png)]
Back in the debugging interface, you can see that the small yellow arrow does point to the next call instruction
This command means to add 8 bytes to the esp, that is, to move 8 bytes high.
In fact, this command is destroying our parameters
mov dword ptr [c],eax//Put the value in eax into variable c
Here eax stores the return value of the Add function
Here we can draw a conclusion:
The return value of a custom function is returned to the main function through the register, an intermediate "variable".
- Put the return value in register A first
- The main function takes the return value from register A and places the variable that accepts the return value.
Continuing down, you can see printf ('%d\n', c); After the statement, the compiler again put the value of variable C back to eax
In fact, this is where the printf function runs:
- Put the variable to be printed in eax first
- Push eax on top of stack
- offset string "%d\n" (guess is data type check)
- _ Printf: execute the printf function
add esp,8//Give esp+8 bytes
I was confused about this part and debugged the discovery again
- The push eax directive takes esp four bytes to the low address
- The offset string'%d\n'directive also lowered esp by four bytes
After executing this command, esp returns to the address before printf was executed
3.4 End Procedure
return 0;xor eax,eax//The xor directive is xor--the role here is unclear}pop edi//stack--esp corresponds to moving pop esi//pop ebx//
add esp,0E4h//esp+0E4h (exit space for main function) CMP ebp, esp//compare EBP and ESP
call __RTC_CheckEsp (0241244h) mov esp,ebp//Copy ebp values to esp //The esp and ebp values are still the same at this point
pop ebp//ebp stacking--esp and ebp separation ret//End of main function
4. Summary of assembly language in this blog
-
mov: Data transfer instructions
-
push: the data is stacked while the esp stack top register moves down
-
pop: data pops up to the specified location, while the esp stack top register moves up
-
sub: subtraction
-
add: addition
-
Call: function call. 1. Push the return address; 2. Going to the objective function
-
jump: Modify the eip, go to the target function, and call
-
ret: Reply with the return address, press in the eip, similar to the pop eip directive
-
cmp (comparison): Performs an implicit subtraction operation that subtracts the source operand from the destination operand without modifying any operands
-
Xor: Performs (bitwise) logical exclusive or (XOR) operations between the corresponding bits of two operands and stores the results in the target operand
Partial assembly instructions reference: C Language Chinese Network
5. Conclusion
By the time this blog was completed, it was already Saturday at 00:54 🌛
I've been writing for so long unconsciously since 19:20 on Friday
This part of the function call stack is a bit obscure. Writing this blog is a bit of a clarification.
Come on.
Can't stand it anymore, go to sleep
Ironically, several roommates are still fighting a 300 million Mouse gun battle dream...