This article is for me in Great God Forum Learning reverse cracking shelling is one of the learning notes. It is my review and summary of what I have learned in the past. There may be fallacies. You are welcome to point out.
Notes will be released one after another, hoping to help Mengxin who wants to get started and make progress together
C language inline assembly and call protocol
Previously, we analyzed C language by analyzing disassembly. Now let's explore how to write assembly functions directly in C language
Here we will introduce the concept of naked function in C language
Bare function
Declare bare functions
The difference between a bare function and an ordinary function is that it is declared before the function
__declspec (naked)
This shows that the function is a bare function
Naked function action
To talk about the role of naked functions, we have to mention the difference between naked functions and ordinary functions
Difference between naked function and ordinary function
In the previous disassembly C language notes, we can know that there are not many disassembly codes for an ordinary empty function, such as protecting the site, restoring the site, etc. so how do these disassembly codes come into being?
The answer is: compiler
The compiler will generate this disassembly code for us
In contrast, as long as the ordinary function plus the bare function prefix is converted into a bare function, the compiler will know that this function does not need to generate the disassembly code such as protecting the site and restoring the site, and the disassembly code required for function execution is implemented by ourselves
Therefore, the role of naked functions is imminent:
When we don't want the compiler to generate the assembly code in the function for us, but want to implement the assembly code inside the function ourselves, we can use the bare function to tell the compiler not to generate additional assembly code
The simplest naked function
void __declspec (naked) function(){ __asm{ ret } }
The above is the simplest bare function. The disassembly code has only one line ret
Compared with ordinary empty functions, they also do nothing, but add ret. Why?
We can take a look at the execution flow of the simplest naked function and pay attention to the comparison with ordinary empty functions
Analyze the simplest bare functions
Complete code
Give the complete code first
#include "stdafx.h" void __declspec (naked) function(){ __asm{ ret } } int main(int argc, char* argv[]) { function(); return 0; }
Then enter the world of disassembly
Function external
13: function(); 00401078 call @ILT+10(function) (0040100f) 14: return 0; 0040107D xor eax,eax 15: } 0040107F pop edi 00401080 pop esi 00401081 pop ebx 00401082 add esp,40h 00401085 cmp ebp,esp 00401087 call __chkesp (004010e0) 0040108C mov esp,ebp 0040108E pop ebp 0040108F ret
Function inside
6: void __declspec (naked) function(){ 00401030 ret
Start analysis
00401078 call @ILT+10(function) (0040100f)
We can find that there is no difference between the call of naked function and the call of ordinary function. They are all the call function address
But then go inside the function and you can see
00401030 ret
The function has and is only used in our code__ The ret statement written by asm has no other code
After the ret statement is executed, the function returns normally. Next, it is no different from the ordinary empty function
come to conclusion
Therefore, we can know that the internal assembly code of the bare function is completely implemented by ourselves. The reason why we need to write a ret is to enable the function to return normally
Let's look at the disassembly code without adding ret statements
We'll look at the inside of the naked function__ asm delete
void __declspec (naked) function(){ }
Then look at the assembly code inside the function
We can see that the function is internally a pile of int 3, that is, CC, that is, the contents of the initialization stack
When the program is executed here, it will be interrupted and cannot be executed normally. Therefore, we need to add an assembly ret statement to enable the function to execute normally and return
After we have a general understanding of the bare functions, let's use inline assembly to implement our own addition functions
Implementation of addition function by inline assembly
Self writing addition function
#include "stdafx.h" int __declspec (naked) Plus(int x,int y){ __asm{ //Preserve pre call stack push ebp //Lift stack mov ebp,esp sub esp,0x40 //Protect the site push ebx push esi push edi //Initialize the raised stack and fill the buffer mov eax,0xCCCCCCCC mov ecx,0x10 lea edi,dword ptr ds:[ebp-0x40] rep stosd //Function core function //Take out parameters mov eax,dword ptr ds:[ebp+8] //Parameter addition add eax,dword ptr ds:[ebp+0xC] //Restore site pop edi pop esi pop ebx //Lower stack mov esp,ebp pop ebp //return ret } } int main(int argc, char* argv[]) { Plus(1,2); return 0; }
It's not difficult to find that the addition function we implemented ourselves simulates what the compiler does for us. At this time, you will also see it inside the function
Function inside
6: int __declspec (naked) Plus(int x,int y){ 00401030 push ebp 7: __asm{ 8: //Preserve pre call stack 9: push ebp 10: //Lift stack 11: mov ebp,esp 00401031 mov ebp,esp 12: sub esp,0x40 00401033 sub esp,40h 13: //Protect the site 14: push ebx 00401036 push ebx 15: push esi 00401037 push esi 16: push edi 00401038 push edi 17: //Initialize the raised stack and fill the buffer 18: mov eax,0xCCCCCCCC 00401039 mov eax,0CCCCCCCCh 19: mov ecx,0x10 0040103E mov ecx,10h 20: lea edi,dword ptr ds:[ebp-0x40] 00401043 lea edi,ds:[ebp-40h] 21: rep stosd 00401047 rep stos dword ptr [edi] 22: //Function core function 23: 24: //Take out parameters 25: mov eax,dword ptr ds:[ebp+8] 00401049 mov eax,dword ptr ds:[ebp+8] 26: //Parameter addition 27: add eax,dword ptr ds:[ebp+0xC] 0040104D add eax,dword ptr ds:[ebp+0Ch] 28: 29: 30: //Restore site 31: pop edi 00401051 pop edi 32: pop esi 00401052 pop esi 33: pop esi 00401053 pop esi 34: 35: //Lower stack 36: mov esp,ebp 00401054 mov esp,ebp 37: pop ebp 00401056 pop ebp 38: 39: //return 40: ret
It executes the code written by ourselves, not generated by the compiler, and can also realize the function of addition function
After the function returns
We can find that the returned function is no different from the ordinary function
00401081 add esp,8
All have this line of statements to balance the stack, that is, balance outside the stack. However, if we want to balance the stack inside the function, that is, to achieve balance inside the stack, that is, we want the function to return without this external stack balance statement, so that the stack balance can be handled by ourselves. How can we do this?
Here we will introduce the concept of C language call agreement
Call agreement
Several common calling protocols:
Among them__ cdecl is the default calling protocol of C language
Next, let's compare the three invocation protocols
int __cdecl Plus1(int x,int y){ return x+y; } int __stdcall Plus2(int x,int y){ return x+y; } int __fastcall Plus3(int x,int y){ return x+y; }
It is also a simple addition function, which adopts three different call protocols. Let's use assembly to observe their differences
__cdecl
The first is what we are most familiar with__ cdecl protocol is different from the simple addition function analyzed in our last note
Observe disassembly:
Function external
Function inside
Here we mainly focus on three areas: parameter stack pressing, function return value, and execution statement after return
Parameter stack pressing: push 2 first and then push 1
Function return value: ret
Execute the statement after returning: add esp,8
__stdcall
Function external
Function inside
Then focus on three places: parameter stack pressing, function return value, and execution statement after return
Parameter stack pressing: push 2 first and then push 1
Function return value: ret 8
Execute statements after return: XOR, eax, eax
__fastcall
Function external
Function inside
Still pay attention to three places: parameter stack pressing, function return value, and execute statement after return
Parameter stack pressing: move EDX first, 2 then mov ecx,1
Function return value: ret
Execute statements after return: XOR, eax, eax
Compare the three protocols
We can conclude that:
__ cdecl is to push parameters into the stack, and then balance the stack after function execution returns, that is, balance out of the stack
__ stdcall also pushes parameters into the stack, but it balances the stack through ret xxx inside the function, that is, balance in the stack
__ fastcall directly uses edx and ecx as the carrier of parameter transfer when the number of parameters is less than or equal to 2. It is not necessary to use the stack, so there is no need to balance the stack. However, when the number of parameters is greater than 2, the extra parameters are handled in the way of stdcall, which also adopts intra stack balance
Let's talk about it later__ The problem of return value in stdcall
We can see that we push two immediate numbers 2 and 1 in the above addition function, and the return value is 8
Does this mean that xxxx = number of parameters * 4 in ret xxxx?
Not at all!!! Here, xxxx in ret xxxx is related to the data width of the pressing parameter
The data width of the two immediate numbers we press here is 4 bytes = 32bit, so here we are ret 4+4=8
If it is changed to push ax, that is, when pressing 2 bytes = 16bit, it should ret 2
Here you can refer to the push instruction of stack related assembly instructions I published earlier
After understanding the above call protocol, we can modify the previous simple addition naked function and change it to in stack balance
Stack balanced addition function
__declspec (naked) __stdcall int Plus(int x,int y){ __asm{ //Preserve pre call stack push ebp //Lift stack mov ebp,esp sub esp,0x40 //Protect the site push ebx push esi push edi //Initialize the raised stack and fill the buffer mov eax,0xCCCCCCCC mov ecx,0x10 lea edi,dword ptr ds:[ebp-0x40] rep stosd //Function core function //Take out parameters mov eax,dword ptr ds:[ebp+8] //Parameter addition add eax,dword ptr ds:[ebp+0xC] //Restore site pop edi pop esi pop ebx //Lower stack mov esp,ebp pop ebp //return ret 8 } } int main(int argc, char* argv[]) { Plus(1,2); return 0; }
Compared with the above, RET is modified to ret 8 to achieve stack balance in the function
The basic learning of this series of reverse shelling is in the link below. Welcome to download and communicate
Copyright notice: This article was originally created by lyl610abc. Welcome to share this article. Please keep the source for reprint