您的位置:首页 > 其它

[32位汇编系列]003 汇编中__stdcall 调用约定以及参数传递

2009-07-14 22:25 218 查看
看了上一篇文章[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个字节的局部空间

00401006 |. 8B45 0C
mov eax, dword ptr [ebp+C]

从栈示意图, 可以很容易看出,ebp+C就是uMsg的地址, 这里就是把uMsg的值送入eax寄存器

00401009 |. 83F8
0F
cmp eax, 0F

这里将uMsg的值和WM_PAINT=0F进行比较

0040100C |. 75
55
jnz short 00401063

如果uMsg不等于WM_PAINT, 那么就要跳转到下一个cmp比较了

0040100E |. 8D45 C0
lea eax, dword ptr [ebp-40]

从栈示意图, 不难看出, 这里是把局部变量@stPS的地址送入eax

00401011 |.
50
push
eax
; /pPaintstruct

将@stPS地址压栈

00401012 |. FF75
08
push dword ptr
[ebp+8]
; |hWnd

从栈示意图, 不难看出, ebp+8是窗口句柄hWnd的地址, 这里2个push指令

将BeginPaint函数用到的2个参数从右到左依次压入栈中

00401015 |. E8 B0010000 call
<jmp.&user32.BeginPaint>
; /BeginPaint

调用BeginPaint, BeginPaint函数返回之后, 会自动将上面2个push指令的压栈操作进行平衡

0040101A |. 8945
AC
mov dword ptr [ebp-54], eax

从栈示意图上,很容易看出, ebp-54正是局部变量@hDC的地址, 这里将BeginPaint的返回值即

设备句柄送入@hDC中保存

0040101D |. 68 00FF0000 push
0FF00
; /Color = <LIGHTGREEN>

将颜色值压入栈中

00401022 |. FF75
AC
push dword ptr
[ebp-54]
; |hDC

将@hDC值压入栈中, 前面2个压栈, 为调用SetTextColor准备参数

00401025 |. E8 24020000 call
<jmp.&gdi32.SetTextColor> ;
/SetTextColor

调用SetTextColor设置文字颜色, 同样, 前面2个压栈操作由SetTextColor再返回前进行平衡

0040102A |. 6A
00
push
0
; /Color = <BLACK>

将背景颜色值压入栈中

0040102C |. FF75
AC
push dword ptr
[ebp-54]
; |hDC

将@hDC值压入栈中, 和SetTextColor一样, 这里2个压栈操作为SetBkColor准备参数

0040102F |. E8 14020000 call
<jmp.&gdi32.SetBkColor>
; /SetBkColor

调用SetBkColor设置背景色

00401034 |. 8D45
B0
lea eax, dword ptr [ebp-50]

从栈示意图不难看出, ebp-50正是局部变量@stRC的地址

00401037 |.
50
push
eax
; /pRect

将@stRC的地址压入栈中

00401038 |. FF75 08
push dword ptr
[ebp+8]
; |hWnd

将参数hWnd压入栈中, 前面2个压栈操作为GetClientRect准备参数

0040103B |. E8 B4010000 call
<jmp.&user32.GetClientRect> ;
/GetClientRect

调用GetClientRect得到客户区大小, 前面2个压栈由GetClientRect在内部进行平衡

00401040 |. 6A
25
push
25
; /Flags =

DT_CENTER|DT_VCENTER|DT_SINGLELINE

将 DT_SINGLELINE or DT_VCENTER or DT_CENTER三者的组合值25压栈

00401042 |. 8D45 B0
lea eax, dword ptr
[ebp-50] ; |

将局部变量@stRC的地址送入eax

00401045 |.
50
push
eax
; |pRect

将@stRC地址压栈

00401046 |. 6A
FF
push
-1
; |Count = FFFFFFFF (-1.)

将-1压栈, 就是字符个数, 这里表示以0结尾的字符串

00401048 |. 68 75204000 push
00402075
; |Text = "things have changed from now"

将要显示的字符串"things have changed from now"

的地址压栈

0040104D |. FF75
AC
push dword ptr
[ebp-54]
; |hDC

将@hDC压栈, 上面5个压栈操作, 为DrawTextA准备参数

00401050 |. E8 93010000 call
<jmp.&user32.DrawTextA>
; /DrawTextA

调用DrawTextA显示字符串, 上面5个压栈操作由DrawTextA返回之前在内部进行平衡

00401055 |. 8D45
C0
lea eax, dword ptr [ebp-40]

将@stPS地址送入eax

00401058 |.
50
push
eax
; /pPaintstruct

将@stPS地址压栈

00401059 |. FF75
08
push dword ptr
[ebp+8]
; |hWnd

将参数hWnd压栈, 上面2个压栈操作为EndPaint准备参数

0040105C |. E8 8D010000 call
<jmp.&user32.EndPaint>
; /EndPaint

调用EndPaint, 释放BeginPaint申请的资源, 上面2个压栈操作由EndPaint内部平衡

00401061 |. EB
2E
jmp short 00401091

WM_PAINT消息处理完毕, 跳转到子程序出口处

00401063 |> 83F8 10
cmp eax, 10

比较uMsg是不是等于WM_CLOSE

00401066 |. 75
14
jnz short 0040107C

不是, 则跳转到DefWindowProc处理处

00401068 |. FF35 04304000 push dword ptr
[403004]
; /hWnd = NULL

将全局变量hWinMain的值压入栈中, 为DestroyWindow准备参数

0040106E |. E8 69010000 call
<jmp.&user32.DestroyWindow> ;
/DestroyWindow

调用DestroyWindow销毁窗口

00401073 |. 6A
00
push
0
; /ExitCode = 0

将程序退出代码0压栈, 为PostQuitMessage准备参数

00401075 |. E8 98010000 call
<jmp.&user32.PostQuitMessage> ; /PostQuitMessage

调用PostQuitMessage向窗口过程发送一个WM_QUIT消息, 让消息循环可以退出

0040107A |. EB
15
jmp short 00401091

WM_CLOSE消息处理完毕,跳转到子程序出口处

0040107C |> FF75
14 push dword
ptr
[ebp+14]
; /lParam

0040107F |. FF75 10
push dword ptr
[ebp+10]
; |wParam

00401082 |. FF75 0C
push dword ptr
[ebp+C]
; |Message

00401085 |. FF75
08
push dword ptr
[ebp+8]
; |hWnd

上面4个压栈操作, 从由到左, 依次将DefWindowProcA用到的4个参数压栈

00401088 |. E8 49010000 call
<jmp.&user32.DefWindowProcA> ;
/DefWindowProcA

调用默认窗口处理过程,上面的4个压栈操作由DefWindowProcA内部进行平衡

0040108D |.
C9
leave

此句代码相当于下面2句代码:

mov esp, ebp ; 恢复子程序开始处ebp被压入栈的时候的esp值

pop ebp ; 恢复ebp进入子程序前的值,同时由于pop操作, esp此时也恢复成了进入子程序之前的值

0040108E |. C2 1000
retn 10

此句代码相当于

pop hWnd

pop uMsg

pop wParam

pop lParam

pop eip ; 此句代码并不正确, 只是形象的表示ret的作用, 将子程序的返回地址送入eip, 让程序可以接着执行

retn 后面的10 表示弹出的字节数0x10, 总共有16个字节(10进制表示)

由此可见, 通过

push ebp

mov ebp, esp

add esp, XXXX

以及

leave

retn YYYY

就可以让栈保持平衡, 子程序之前是什么样, 子程序返回之后还是什么样, 无论你子程序之间进行什么样的操作

有多少次压栈, 有多少次出栈, 栈是否平衡, 但是子程序退出后, 通过这种机制, 能够确保子程序能够正确的

返回, 需要特别注意的是, 在子程序内部前面不要随意更改ebp的值, 它的值一旦改动, 程序估计就要崩掉了

00401091 |> 33C0
xor eax, eax

将eax清0, 表示子程序返回的值为0, 即正常返回, 任何子程序的返回值都要放在eax中

00401093 |.
C9
leave

00401094 /. C2
1000
retn 10

这2句跟上面一样, 在另一个分支退出之前, 保持栈的平衡

好了, 到目前为止, 通过对每句代码的仔细讲解, 以及配上栈示意图, 你是否明白了程序中参数的传递方式

以及参数的使用方式?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: