Parameters in assembly and stack correction

zhaozj2021-02-16  60

Parameters in assembly and stack correction

Luo Yunbin

In WIN32 assembly, we often have to deal with the API, and it will often use the subroutines that have been prepared similar to the API with the API. This article is to be described in the process of parameter delivery during subroutine calls. Generally in the program, the passage of the parameters is performed by the stack, that is, the caller presses the parameters to be passed to the subroutine (or the caller) into the stack, and the subroutine reuses the corresponding value in the stack, such as Say, if you want to call subruting (var), compiled final code may be Push Var3push Var2push Var1call SubroutingAdd ESP, 12. That is, the caller first presses the parameters into the stack, then call the subroutine, completed After that, since the number of previously pressed in the stack is no longer useful, the caller or the caller must have one to correct the stack pointer to the status before the call. The parameter is the rightmost first in the stack or the leftmost first stack, and the caller is still a set of stacks, otherwise it will have an incorrect result, this is what I use "possibly" in front. " The reason for these two words: The agreement of the recurring subroutine in various languages ​​is different, and the following table is found in the following table:

C

Syscall

Stdcall

Basic

Fortran

Pascal

Parameter from left to right

Yes

Yes

Yes

Parameter from right to left

Yes

Yes

Yes

Call clearance stack

Yes

Allow use: VARARG

Yes

Yes

Yes

VARARG indicates that the number of parameters can be uncertain, there is an example is the PrintF statement in C. In the above table, the definition of stdcall has a place to be explained, that is, if STDCALL uses: VARARG, it is cleared by the caller Stack of stack, without: VARARG is made of stack by the caller. In Win32 assembly, we agree to use the stdcall mode, so we must use the .Model stdcall statement when the program starts. That is, in the API or subroutine, the rightmost parameter is first in the stack, then the child is responsible for calibrating the stack when returning, for example, if we want to call the MessageBox API, because it is MessageBox (hwnd, lptext , LPCAPTION, UTYPE, so use it in the program:

Push MB_okpush Offset Szcaptionpush Offset Sztextpush HWNDCALL MESSAGEBOX ...

We don't have to add ADD SP, 4 * 4 to the API to correct the stack because this has been made by the MessageBox subroutine. In the Windows API, the only special API is WSPrintf. This API is C. The definition is WSPrintf (LPout, LPFORMAT, VAR1, VAR2 ...), so it is:

Push 1111push 2222push 3333push offset szformatpush offset szoutcall WSPrintfadd ESP, 4 * 5

The following is to talk about how the subroutine accesses the parameters, because the default register of the stack operation has ESP and EBP, and the ESP is a stack pointer, so it is not possible to use EBP to access the stack, assume it in a call There are two parameters, and the stack pointer ESP before the first parameter is X, then the ESP after pressing the two parameters is X-8, the program starts executing the CALL instruction, and the CALL instruction presses the return address into the stack, this Time ESP is XC, then in the subroutine, we can start using EBP to access parameters, but in order to restore EBP values ​​when returning, we still need a Push EBP to save EBP value, this When the ESP is X-10, then executes a MOV EBP, ESP, as can be seen according to the right, actually [EBP 8] is parameter 1, [EBP C] is parameter 2. In addition, local variables are also defined in the stack, and their locations are generally placed behind the EBP value saved by the PUSH EBP, and the address corresponding to the local variables 1, 2 is [EBP-4], [EBP-8], below A typical subroutine, can complete the first parameter minus the second parameters, its definition is: MyProc Proto var1, var2; two parameters Local Lvar1, LVAR2; there are two local variables, here Local variables are actually not used, just to demonstrate, the specific implementation code is: MyProc Procpush Ebpmov EBP, ESP

SUB ESP, 8

MOV EAX, DWORD PTR [EBP 8] SUB EAX, DWORD PTR [EBP C]

Add ESP, 8

POP EBPRET 8

MyProc ENDP is now analyzing this subroutine, and the PUSH EBP / MOV EBP, ESP is a routine saving and setting EBP code, SUB ESP, 8 leaves two local variables in the stack, the MOV / Add statement completed Plus, Add ESP, 8 Modified two local variables, RET 8 corrects the stack used by the two parameters, equivalent to RET / ADD ESP, and 8 code effects. It can be seen that this is a standard stdcall agreed subroutine. When the last parameter is used, the stack correction is made by the subroutine when returning. Of course, this subroutine uses a method of manually saving EBP and sets local variables in order to demonstrate the execution process. In fact, the 386 processor has two dedicated instructions to complete this function, which is Enter and Leave, Enter statement The role is that PUSH EBP / MOV EBP, ESP / SUB ESP, XXX, this XXX is Enter, Leave completed the function of add ESP, XXX / POP EBP, so the above program can be modified:

MyPORC ProCenter 8,0mov Eax, DWORD PTR [EBP 8] Sub Eax, DWORD PTR [EBP C] Leaveret 8MyProc ENDP

Ok, I said this, the principle of parameter transmission should also be clear, but I still have to say that when using the Masm32 compiler, we don't need to remember the trouble of [EBP XX] and other troublesome addresses. Or calculate the local variables to reserve the stack space, and the value to be added when RET is calculated, and the MAASM32 macro has been done, as in Masm32, the above program is written as: MyProc PROC Var1, Var2local Lvar1, Lvar2

Mov Eax, Var1Sub Eax, Var2ret

MyProc ENDP

The compiler will automatically in front of the MOV EAX, VAR1, and its parameters are automatically specified according to the local variable defined by the local, which will automatically add a Leave before RET. Similarly, the compiler will depend on the parameters. How many replacements replace RET XXX, replace MOV EAX, VAR1 to MOV Eax, DWORD PTR [EBP 8], and more.

Finally, use the MASM32 INVOKE macro, when you can see, when you call the subroutine with parameters, we need to put the parameters into the stack with PUSH. If you accidentally make the number of parameters, you will make the stack unbalanced However, the program causes the returning return address of the program from the stack to cause unpredictable consequences, so there is a statement to complete the task of the automatic test, INVOKE is such a statement, in fact, it is all parameters of automatic PUSH, detecting parameters Number, whether the type is correct, and a macro to call us, for the above PUSH / PUSH / CALL MYPROC instruction, you can use a directive to complete:

Invoke myProc, Var1, Var2

Of course, when the program is compiled, you will find that it will find it correctly to the same PUSH / PUSH / CALL instruction. However, before using Invoke, in order to make it correct parameter test, you need to declare the function, just like in C, the statement declared is:

MyProc Proto: DWORD,: DWORD

ProtO in the statement is a keyword, indicating that the DWORD indicates that the type of parameters is the Double Word type. There are several parameters that represent the parameters, and the parameters in Win32 are Double Word type, and the statement should be written before invoke. So we generally include it in the include file, well, synthesize, use a subroutine or API with parameters in Masm32, we only need:

... MyProc Proto: DWORD,: DWORD ....DATAX DD? Y DD? DWRESULT DD? ... MOV X, 1MOV Y, 2INVOKE MyProc X, YMOV DWRESULT, EAX ...

转载请注明原文地址:https://www.9cbs.com/read-17472.html

New Post(0)