Parameter transfer and stack correction in Win32 assembly

Source: Internet
Author: User

Original article address:

Http://net.pku.edu.cn /~ Course/cs201/2004/ASSEMBLY/Workshop

 

In Win32 compilation, we often deal with APIs. In addition, we often use self-compiled subprograms with parameters similar to APIs, this article describes the concept and Analysis of parameter transfer during the subroutine call process. In a program, the parameter is passed through the stack. That is to say, the caller pushes the parameter to be passed to the subroutine (or called) into the stack, the subroutine is used to retrieve the corresponding values in the stack. For example, if you want to call subrouting (var1, var2, var3), the final code after compilation may be

Push var3
Push var2
Push var1
Call subrouting
Add ESP, 12

That is to say, the caller first pushes the parameter into the stack, and then calls the subroutine. After the completion, the number of values first pushed in the stack is no longer useful, either the caller or the caller must correct the stack pointer to the status before the call. There must be an agreement on whether the parameter is the rightmost first stack or the leftmost stack, and whether the stack is corrected by the caller or by the caller. Otherwise, an incorrect result will be generated, this is why I used the word "possibility" before: the conventions for calling subprograms in different languages are different. Their differences are shown in the following table:

  C Syscall Stdcall Basic FORTRAN Pascal
Parameters from left to right       Yes Yes Yes
Parameters from right to left Yes Yes Yes      
The caller clears the stack. Yes          
Allowed: vararg Yes Yes Yes      

Vararg indicates that the number of parameters can be uncertain. One example is the printf statement in C. In the preceding table, stdcall has a description, that is, if stdcall uses vararg, the stack is cleared by the caller, but the stack is cleared by the caller without vararg. In Win32 compilation, the stdcall method is agreed, so we should use the. Model stdcall statement at the beginning of the program. That is to say, in an API or subroutine, the rightmost parameter first enters the stack, and then the subroutine is responsible for correcting the stack when returning the result. For example, if we want to call the MessageBox API, because it is defined as MessageBox (hwnd, lptext, lpcaption, utype), it should be used in the program as follows:

Push mb_ OK
Push offset szcaption
Push offset sztext
Push hwnd
Call MessageBox
...

We do not need to add the add SP, 4*4 Statement to modify the stack when the API returns, because this is already done by the MessageBox subprogram. In Windows API, the only special API is wsprintf, which is agreed by C. It is defined as wsprintf (lpout, lpformat, var1, var2 ...), therefore, you must:

Push 1111
Push 2222
Push 3333
Push offset szformat
Push offset szout
Call wsprintf
Add ESP, 4*5


The following describes how to access parameters in subprograms. Because the registers for Stack operations have ESP and EBP by default, and ESP is a stack pointer and cannot be used temporarily, EBP is generally used to access the stack, suppose there are two parameters in a call, and the stack pointer ESP before pushing the first parameter is X, then the ESP after pressing the two parameters is the X-8, the program starts to execute the call command, the Call Command pushes the return address into the stack. At this time, ESP is the X-C, and it is already in the subroutine. We can start to use EBP to access the parameters, but in order to restore the EBP value when returning, we still need to push EBP to save the EBP value first, then esp for the X-10, and then execute a sentence mov EBP, ESP, according to can be seen, in fact, [EBP + 8] Is parameter 1, and [EBP + C] Is parameter 2. In addition, local variables are also defined in the stack. Their locations are generally placed behind the EBP values saved by the push EBP, the addresses of local variables 1 and 2 are [ebp-4] and [ebp-8] respectively. Below is a typical subroutine that can complete the first parameter minus the second parameter, which is defined:

Myproc proto var1, var2; has two parameters
Local lvar1, lvar2; there are two local variables

Note that the two local variables are not actually used for demonstration purposes. The specific implementation code is:

Myproc proc

Push EBP
MoV EBP, ESP
Sub ESP, 8
MoV eax, dword ptr [EBP + 8]
Sub eax, dword ptr [EBP + C]
Add ESP, 8
Pop EBP
RET 8

Myproc endp

Now let's analyze this subroutine. Push EBP/mov EBP and ESP are routine code for saving and setting EBP. sub ESP and 8 leave space for two local variables in the stack, the mov/Add statement completes the addition. Add ESP and 8 correct the stacks used by the two local variables. Ret 8 corrects the stacks used by the two parameters, which is equivalent to RET/Add ESP, 8. It can be seen that this is a standard stdcall subprogram. When used, the last parameter first enters the stack, and the return is corrected by the subprogram. Of course, this subroutine uses the method of manually saving EBP and setting local variables to demonstrate the execution process. In fact, the 386 processor has two dedicated commands for this function, that is, enter and leave. The enter statement is used to push EBP/mov EBP, ESP/sub ESP, XXX. This XXX is enter, and leave completes add ESP, xxx/pop EBP function, so the above program can be changed:

Myporc proc

Enter 8, 0

MoV eax, dword ptr [EBP + 8]
Sub eax, dword ptr [EBP + C]

Leave
RET 8

Myproc endp

Well, here, we should also be clear about the principle of parameter passing. Finally, when using masm32 to compile Win32 Assembler programs, we do not need to remember the troublesome addresses such as [EBP + XX], or the stack space to be reserved for local variable calculation, and the value to be added during RET calculation, the macro commands of masm32 have already completed these operations. For example, in masm32, the above programs only need to be written as follows:

Myproc proc var1, var2
Local lvar1, lvar2

MoV eax, var1
Sub eax, var2
RET

Myproc endp

The compiler automatically inserts an enter statement before mov eax and var1. Its parameters are automatically specified based on the number of local variables, A leave is automatically added before ret. Similarly, the compiler will replace RET with ret xxx based on the number of parameters, and change mov eax and var1 to mov eax, dword ptr [EBP + 8] and so on.
The last step is to use the invoke macro command of masm32. We can see that when calling a subroutine with parameters, we need to push the parameters into the stack with push. If we accidentally make a wrong number of parameters, this will cause an imbalance in the stack and cause unexpected consequences for the program to retrieve the wrong return address from the stack. Therefore, it is necessary to have a statement to complete the task of automatic check. Invoke is such a statement, in fact, it is a macro command that automatically pushes all parameters, checks whether the number and type of parameters are correct, and uses call to call. For the above push/call myproc command, you can use one command to complete the following:

Invoke myproc, var1, var2

Of course, after the program is compiled, you can see that the machine code is correctly replaced with the same push/call command. However, before using invoke, You need to declare the function for correct parameter verification, just as in C, the statement is:

Myproc proto: DWORD,: DWORD

In a statement, "Proto" is a keyword that indicates a declaration. "DWORD" indicates that the parameter type is double word. A few of them indicate that there are several parameters. in Win32, the parameters are double word, the declarative statement should be written before invoke, So we generally include it in the include file. Well, in summary, we only need to use a subroutine or API with parameters in masm32:

...
Myproc proto: DWORD,: DWORD
...
. Data
X dd?
Y dd?
Dwresult dd?
...
MoV X, 1
MoV y, 2
Invoke myproc X, Y
MoV dwresult, eax
...

That's all. How is it easy? However, it took me one night to write this article... # % $ ^ & (* & ^ (* & (^ &(*

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.