Catalog
Function Call Procedure in C Language
First up with a piece of code
#include<stdio.h>
int Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = Add(a, b);
return 0;
}
This function calculates the value of two numbers. Next, we use disassembly to look at the process after the function is decomposed.
Disassembly
#include <stdio.h>
int main()
{
01151720 push ebp
01151721 mov ebp,esp
01151723 sub esp,0E4h
01151729 push ebx
0115172A push esi
0115172B push edi
0115172C lea edi,[ebp-0E4h]
01151732 mov ecx,39h
01151737 mov eax,0CCCCCCCCh
0115173C rep stos dword ptr es:[edi]
int a = 10;
0115173E mov dword ptr [a],0Ah
int b = 20;
01151745 mov dword ptr [b],14h
int c = Add(a, b);
0115174C mov eax,dword ptr [b]
0115174F push eax
01151750 mov ecx,dword ptr [a]
01151753 push ecx
01151754 call _Add (01151104h)
01151759 add esp,8
0115175C mov dword ptr [c],eax
return 0;
0115175F xor eax,eax
}
01151761 pop edi
01151762 pop esi
01151763 pop ebx
01151764 add esp,0E4h
0115176A cmp ebp,esp
0115176C call __RTC_CheckEsp (01151122h)
01151771 mov esp,ebp
01151773 pop ebp
}
//Add()
#include<stdio.h>
int Add(int x, int y)
{
001016D0 push ebp
001016D1 mov ebp,esp
001016D3 sub esp,0CCh
001016D9 push ebx
001016DA push esi
001016DB push edi
001016DC lea edi,[ebp-0CCh]
001016E2 mov ecx,33h
001016E7 mov eax,0CCCCCCCCh
001016EC rep stos dword ptr es:[edi]
int z = 0;
001016EE mov dword ptr [z],0
z = x + y;
001016F5 mov eax,dword ptr [x]
001016F8 add eax,dword ptr [y]
001016FB mov dword ptr [z],eax
return z;
001016FE mov eax,dword ptr [z]
}
00101701 pop edi
00101702 pop esi
00101703 pop ebx
00101704 mov esp,ebp
00101706 pop ebp
00101707 ret
This is the disassembly code. Now let's read it paragraph by paragraph to see how the whole program is executed.
Creation of main() function
01151720 push ebp
01151721 mov ebp,esp
01151723 sub esp,0E4h
01151729 push ebx
0115172A push esi
0115172B push edi
0115172C lea edi,[ebp-0E4h]
01151732 mov ecx,39h
01151737 mov eax,0CCCCCCCCh
0115173C rep stos dword ptr es:[edi]
//The partition line......................
int a = 10;
0115173E mov dword ptr [a],0Ah
int b = 20;
01151745 mov dword ptr [b],14h
Here I want to give readers a brief introduction to the meaning of variables. ebp and esp are two general registers. They can store immediate numbers and memory addresses. esi is a source-to-address register. edi is a destination address change register.
They can be used to store addresses.
Before the main() function, there is a function CRTStartUp, ebp is the bottom of the function stack, and esp is the top of the function stack.
Next, move through the first two sentences of code mov assignment, and their states become as follows:
In the third sentence, sub is a subtraction instruction, which makes the esp move backwards by 0E4h units.
The next three sentences are to push ebx,esi,edi into the stack.
In the seventh sentence, lea means load effective address, which is to take out the address of [ebp-0E4h] and assign it to edi.
In the eighth sentence, move the 39h assignment to ecx. In the ninth sentence, move the content assignment to eax.
In the tenth sentence, rep stos are duplicate copies of data.
Be careful:
The ecx register is used to save the number of copies and pastes, and ecx is the counter.
Eax registers are used to transmit information, and eax is an accumulator, usually used to store data.
rep repeats the above instructions.
stos moves the value assignment of eax to the storage unit of the address es:[edi].
Now the blank part is filled by cc cc cc cc. Here cc cc is often encountered "hot".
Dividing line
The next operation is well understood, the purpose of which is to create a and b and push them into the stack.
Calling procedure of Add() function
int c = Add(a, b);
0115174C mov eax,dword ptr [b]
0115174F push eax
01151750 mov ecx,dword ptr [a]
01151753 push ecx
01151754 call _Add (01151104h)
01151759 add esp,8
0115175C mov dword ptr [c],eax
In the first four sentences, it finds a register for a and b, and at the same time, it pushes them into the stack in an inverted way with a list of function parameters.
For convenience, the following figure will only intercept the first half.
Next, the call instruction is used, which first moves the instruction to the next sentence, and then manipulates what follows the call. Press F11 at call and we will jump to the Add() function.
Add() function
#include<stdio.h>
int Add(int x, int y)
{
001016D0 push ebp
001016D1 mov ebp,esp
001016D3 sub esp,0CCh
001016D9 push ebx
001016DA push esi
001016DB push edi
001016DC lea edi,[ebp-0CCh]
001016E2 mov ecx,33h
001016E7 mov eax,0CCCCCCCCh
001016EC rep stos dword ptr es:[edi]
This section is very similar to the main function creation process. So it's expressed in two graphs.
int z = 0;
001016EE mov dword ptr [z],0
z = x + y;
001016F5 mov eax,dword ptr [x]
001016F8 add eax,dword ptr [y]
001016FB mov dword ptr [z],eax
return z;
001016FE mov eax,dword ptr [z]
}
00101701 pop edi
00101702 pop esi
00101703 pop ebx
00101704 mov esp,ebp
00101706 pop ebp
00101707 ret
This code is very interesting. Please pay attention to it.
First, we created the variable ptr[z]. Then, the value of ptr[x] is placed in eax, the value of ptr[y] is added to eax and then placed in eax, and the value assignment of eax is moved to ptr[z]. Finally, we need to return z. This is where the compiler again moves the ptr[z] assignment to eax. The reason for this is that ptr[x], ptr[y], ptr[z] will be destroyed when the function is completed, but the register eax will not be destroyed, so the value of eax is very safe.
The fourth sentence outside brackets is very important. It will destroy all data of the function.
After ejecting ebp, the point has only one pointer esp. Next, ret is executed, that is, return returns. Return to the place below the call we just called.
Destruction of main() function
01151759 add esp,8
0115175C mov dword ptr [c],eax
return 0;
0115175F xor eax,eax
}
01151761 pop edi
01151762 pop esi
01151763 pop ebx
01151764 add esp,0E4h
0115176A cmp ebp,esp
0115176C call __RTC_CheckEsp (01151122h)
01151771 mov esp,ebp
01151773 pop ebp
}
Now let's jump back to the first sentence of this paragraph. Now esp + 8 goes to the high address. The purpose is to eliminate the parametric instantiation of a,b. In the second sentence, we move the data assignment stored in eax to c. Finally, xor exclusive or, oneself and oneself exclusive or its value becomes zero.
After that, we stacked edi, esi, ebx. Now esp points to our red arrow ebp - 0E4h at the beginning.
Then, because of the add operation, we continue to move to the high address, where we started.
Now we have the cmp instruction, which is the comparison instruction. Its operation is to subtract the first number from the second, and if the subtraction is ZF=0, it means the same. But cmp does not really decrease. At this point, the main() function is actually executed, and then esp, ebp. Back to the original position. As for the subsequent function calls, there is no need to go into them. So far we have seen how functions are implemented in assembly.
Thank you for your reading. If you have any questions, please give me more advice.