Nynaeve
April 19th, 2008, 17:01
Continuing on the series about Win32 calling conventions, the next topic of discussion is how the various calling conventions look from an assembler level.
This is useful to know for a variety of reasons; if you are reverse engineering (or debugging) something, one of the first steps is figuring out the calling convention for a function you are working with, so that you know how to find the arguments for it, how it deals with the stack, and soforth.
For this post, I’ll concentrate primarily on __cdecl. Future posts will cover the other major calling conventions.
As I have previously described, __cdecl is an entirely stack based calling convention, in which arguments are cleaned off the stack by the caller. Given this, you can expect to see all of the arguments for a function placed onto the stack before a function call is main. If you are using CL, then this is almost always done by using the “push” instruction to place arguments on the stack.
Consider the following simple example function:
First, we’ll take a look at what calls to a __cdecl function look like. For example, if we look at a call to the function described above like so:
… we’ll see something like this:
There are basically three different things going on here.
1. Setting up arguments for the target function. This is what the three different “push” instructions do. Note that the arguments are pushed in reverse order - you’ll always see them in reverse order if they are placed on the stack via push.
2. Making the actual function call itself. After all the arguments are in place, the “call” instruction is used to transfer execution to the target. Remember that on x86, the call instruction implicitly pushes the return address on the stack. After the function call returns, the return value of the function is stored in the eax (or edx:eax) registers, typically.
3. Cleaning arguments off the stack after the function returns. This is the purpose of the “add esp, 0xc” instruction following the “call” instruction. Since the target function does not adjust the stack to remove arguments after the call, this is up to the calller. Sometimes, you may see multiple __cdecl function calls be made in rapid succession, with the compiler only cleaning arguments from the stack after all of the function calls have been made (turning many different “add esp” instructions into just one “add esp” instruction).
It is also worth looking at the implementation of the function to see what it does with the arguments passed in and how it sets up a return value. The assembler for CdeclFunction1 is as so:
This function is fairly straightforward. Since __cdecl is stack based for argument passing, all of the parameters are on the stack. Recall that the “call” instruction pushes the return address onto the stack, so the stack will begin with the return value at [esp+0] and have the first argument at [esp+4]. A graphical view of the stack layout (relative to “esp”
of this function is thus:
In this case, there is no frame pointer in use, so the function accesses all of the arguments directly relative to “esp”. The steps taken are:
1. The function fills eax with the value of the second argument (b), located at [esp+8] according to our stack layout.
2. Next, the function loads ecx with the value of the first argument (a), which is located at [esp+4].
3. Next, the function adds to eax the value of the first argument (a), now stored in the ecx register.
4. Finally, the function multiplies eax by the value of the third argument (c), located at [esp+c].
5. After finishing with all of the computations needed to implement the function, it simply returns with a “retn” instruction. Since the caller cleans the stack, the “retn” intruction (with a stack adjustment) is not used here; __cdecl functions never use “retn <displacement>“, only “retn”. Additionally, because the result of the “mul” instruction happened to be stored in the eax register here, no extra instructions are needed to set up the return value, as it is already stored in the return value register (eax) at the end of the function.
Most __cdecl function calls are very similar to the one discussed above, although there will typically be much more code to the actual function and the function call (if there are many arguments), and the compiler may play some optimization tricks (such as deferring cleaning the stack across several function calls). The basic things to look for with a __cdecl function are:
* All arguments are on the stack.
* The return instruction is “retn” and not “retn <displacement>“, even when there are a non-zero number of arguments.
* Shortly after the function call returns, the caller cleans the stack of arguments pushed. This may be deferred later, depending on how the compiler assembled the caller.
Note that if you have been paying attention, given the above criteria, you’ve probably noticed that a __cdecl function with zero arguments will look identical to an __stdcall function with zero arguments. If you don’t have symbols or decorated function names, there is no way to tell the two apart when there are no arguments, as the semantics are the same in that special case.
That’s all for a basic overview of __cdecl from an assembler perspective. Next time: more on the other calling conventions at an assembly level.
This is useful to know for a variety of reasons; if you are reverse engineering (or debugging) something, one of the first steps is figuring out the calling convention for a function you are working with, so that you know how to find the arguments for it, how it deals with the stack, and soforth.
For this post, I’ll concentrate primarily on __cdecl. Future posts will cover the other major calling conventions.
As I have previously described, __cdecl is an entirely stack based calling convention, in which arguments are cleaned off the stack by the caller. Given this, you can expect to see all of the arguments for a function placed onto the stack before a function call is main. If you are using CL, then this is almost always done by using the “push” instruction to place arguments on the stack.
Consider the following simple example function:
Code:
__declspec(noinline)
int __cdecl CdeclFunction1(int a, int b, int c)
{
return (a + b) * c;
}
First, we’ll take a look at what calls to a __cdecl function look like. For example, if we look at a call to the function described above like so:
Code:
CdeclFunction1(1, 2, 3);
… we’ll see something like this:
Code:
; 119 : int v = CdeclFunction1(1, 2, 3);
00000 6a 03 push 3
00002 6a 02 push 2
00004 6a 01 push 1
00006 e8 00 00 00 00 call CdeclFunction1
0000b 83 c4 0c add esp, 12
There are basically three different things going on here.
1. Setting up arguments for the target function. This is what the three different “push” instructions do. Note that the arguments are pushed in reverse order - you’ll always see them in reverse order if they are placed on the stack via push.
2. Making the actual function call itself. After all the arguments are in place, the “call” instruction is used to transfer execution to the target. Remember that on x86, the call instruction implicitly pushes the return address on the stack. After the function call returns, the return value of the function is stored in the eax (or edx:eax) registers, typically.
3. Cleaning arguments off the stack after the function returns. This is the purpose of the “add esp, 0xc” instruction following the “call” instruction. Since the target function does not adjust the stack to remove arguments after the call, this is up to the calller. Sometimes, you may see multiple __cdecl function calls be made in rapid succession, with the compiler only cleaning arguments from the stack after all of the function calls have been made (turning many different “add esp” instructions into just one “add esp” instruction).
It is also worth looking at the implementation of the function to see what it does with the arguments passed in and how it sets up a return value. The assembler for CdeclFunction1 is as so:
Code:
CdeclFunction1 proc near
a= dword ptr 4
b= dword ptr 8
c= dword ptr 0Ch
mov eax, [esp+8] ; eax = b
mov ecx, [esp+4] ; ecx = a
add eax, ecx ; eax = eax + ecx
imul eax, [esp+0Ch] ; eax = eax * c
retn ; (return value = eax)
CdeclFunction1 endp
This function is fairly straightforward. Since __cdecl is stack based for argument passing, all of the parameters are on the stack. Recall that the “call” instruction pushes the return address onto the stack, so the stack will begin with the return value at [esp+0] and have the first argument at [esp+4]. A graphical view of the stack layout (relative to “esp”

Code:
+00000000 r db 4 dup(?) ; (Return address)
+00000004 a dd ?
+00000008 b dd ?
+0000000C c dd ?
In this case, there is no frame pointer in use, so the function accesses all of the arguments directly relative to “esp”. The steps taken are:
1. The function fills eax with the value of the second argument (b), located at [esp+8] according to our stack layout.
2. Next, the function loads ecx with the value of the first argument (a), which is located at [esp+4].
3. Next, the function adds to eax the value of the first argument (a), now stored in the ecx register.
4. Finally, the function multiplies eax by the value of the third argument (c), located at [esp+c].
5. After finishing with all of the computations needed to implement the function, it simply returns with a “retn” instruction. Since the caller cleans the stack, the “retn” intruction (with a stack adjustment) is not used here; __cdecl functions never use “retn <displacement>“, only “retn”. Additionally, because the result of the “mul” instruction happened to be stored in the eax register here, no extra instructions are needed to set up the return value, as it is already stored in the return value register (eax) at the end of the function.
Most __cdecl function calls are very similar to the one discussed above, although there will typically be much more code to the actual function and the function call (if there are many arguments), and the compiler may play some optimization tricks (such as deferring cleaning the stack across several function calls). The basic things to look for with a __cdecl function are:
* All arguments are on the stack.
* The return instruction is “retn” and not “retn <displacement>“, even when there are a non-zero number of arguments.
* Shortly after the function call returns, the caller cleans the stack of arguments pushed. This may be deferred later, depending on how the compiler assembled the caller.
Note that if you have been paying attention, given the above criteria, you’ve probably noticed that a __cdecl function with zero arguments will look identical to an __stdcall function with zero arguments. If you don’t have symbols or decorated function names, there is no way to tell the two apart when there are no arguments, as the semantics are the same in that special case.
That’s all for a basic overview of __cdecl from an assembler perspective. Next time: more on the other calling conventions at an assembly level.