看了上一篇文章[32位汇编系列]002 - 创建标准的windows窗口(3) 之后, 你是否对反汇编代码中有很多ebp+xxx 或者ebp-xxx之类的指令感觉很疑惑, 今天, 我还以 上一篇文章的例子 中的窗口过程_WinProc的反汇编代码作为例子, 来详细的说明一下在32位汇编中, 参数以及局部变量是如何被程序使用的。
首先你必须明白一个事实是, Windows的绝大部分api都是以__stdcall方式调用的, 之所以说绝大部分, 是因为有个别的api因为要用到不定参数, 所以, 采用了c/c++的__cdecl 调用约定。 如果你对什么是函数的调用约定不甚了解,那么我建议你先看看这篇文章:调用方式__cdecl和__stdcall的异同点 。
接着, 我们要详细的讨论一下这段代码【_WinProc的反汇编代码】:
00401000 /. 55 push ebp
00401001 |. 8BEC mov ebp, esp
00401003 |. 83C4 AC add esp, -54
上面这段代码, 是一个汇编的过程的经典代码,在子程序进行任何操作之前, 先保存2个重要的寄存器ebp和esp
通过push ebp 将ebp的值先压入栈中, 接着把当前esp的值赋值给ebp, 以后就可以通过操作ebp来访问各个
参数以及局部变量了, add esp, -54 , 这个是为局部变量保存空间, 这里申请了有3个局部变量:
local @stPS:PAINTSTRUCT
local @stRC:RECT
local @hDC
前面2个是结构PAINTSTRUCT和结构RECT, 后面一个是DWORD类型的变量
你不妨计算一下, 这三个局部变量所占用空间的大小
sizeof(PAINTSTRUCT) + sizeof(RECT) + sizeof(DWORD) = 64 + 16 + 4 = 84
这里是10进制, 换成16进制就是0x54 ,这就是为什么要用add esp, -54
需要注意的是, 这里所有的反汇编代码中出现的数字都是16进制的。
经过上面的分析, 我们来看看具体的栈空间示意图:
|..................| 低地址
----------------
| ebp-54 | hDC的地址 <--当前esp值
----------------
| ebp-50 | @stRC:RECT首地址
|-------------- |
| ebp-4C |
----------------
| ebp-48 |
----------------
| ebp-44 |
----------------
| ebp-40 | @stPS:PAINTSTRUCT 首地址
----------------
| .......... |
-----------------
| ebp | 进入子程序, ebp被压入栈中
-----------------
| ebp+4 | 子程序返回地址
-----------------
| ebp+8 | 参数hWnd
-----------------
| ebp+C | 参数uMsg
-----------------
| ebp+10 | 参数wParam
-----------------
| ebp+14 | 参数lParam
------------------
| ........... | 高地址
从栈空间示意图可以清晰的看出, 进入子程序之前, 由调用程序把4个参数从右到做依次压入栈中
接着通过call指令把子程序_WinProc的返回地址也压入栈中
进入子程序后, 首先ebp被压入栈中, 接着为3个局部变量分配0x54个字节的局部空间