| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 2293 人关注过本帖, 1 人收藏
标题:有人知道WIN32 API参数传递的详细过程和步骤吗?
只看楼主 加入收藏
W32Asm
Rank: 1
等 级:新手上路
帖 子:8
专家分:0
注 册:2010-12-10
结帖率:100%
收藏(1)
已结贴  问题点数:20 回复次数:14 
有人知道WIN32 API参数传递的详细过程和步骤吗?
各位高手,有人知道WIN32 API参数传递的详细过程和步骤吗?
我遇到个问题,一直想不明白,请教!
看下面例子(不完整,只有关键部分)
.data
stGlt    SYSTEMTIME    <?>
szTimeout    db    128 dup (?)
szFormat    db    '%x:%x',0

.......

invoke    GetLocalTime,addr stGlt
invoke    wsprintf,addr szTimeOut,addr szFormat,stGlt.wHour,stGlt.wMinute
invoke    MessageBox,NULL,addr szTimeOut,NULL,MB_OK

这个程序是想显示时间,输出的格式用了十六进制输出。
假如现在时间是17:01,则显示结果为:11:10000(这就是让我纳闷的地方),为什么会多出0000这个数值。

我把程序进行了编译成exe后,进行反编译,显示这样:
0040100C  |.  68 04304000   push    00403004                         ; /pSecurity = test.00403004
00401011  |?  E8 42000000   call    <jmp.&kernel32.GetLocalTime>
00401016  |?  6A 00         push    0
00401018  |.  66:FF35 0E304>push    word ptr [40300E]                ; /FailIfExists = FALSE
0040101F  |.  6A 00         push    0                                ; |ExistingFileName = NULL
00401021  |?  66:FF35 0C304>push    word ptr [40300C]
00401028  |?  68 46204000   push    00402046                         ;  ASCII "%x:%x"
0040102D   ?  68 2C304000   push    0040302C
00401032   ?  E8 2D000000   call    <jmp.&user32.wsprintfA>
00401037   ?  83C4 10       add     esp, 10
0040103A   ?  6A 00         push    0
0040103C   .  6A 00         push    0
0040103E   ?  68 2C304000   push    0040302C
00401043   ?  6A 00         push    0
00401045   ?  E8 20000000   call    <jmp.&user32.MessageBoxA>

从反编译看,在push stGlt.wHour和push stGlt.wMinute时,因为stGlt.wHour和stGlt.wMinute是word类型,所以push的时候,又自动加了push 0,但是不知道为什么在pop的时候怎么操作的,给pop stGlt.wHour值是对的,但是pop stGlt.wMinute却多出了0000。

麻烦高手解答,谢谢。顺便,谁能说一下API关于参数使用时,push和pop的详细过程更好。
搜索更多相关主题的帖子: 十六进制 现在时间 
2012-09-06 17:30
zklhp
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:china
等 级:贵宾
威 望:254
帖 子:11485
专家分:33241
注 册:2007-7-10
收藏
得分:20 
汇编中参数的传递和堆栈修正

by 罗云彬


--------------------------------------------------------------------------------

    在 Win32汇编中,我们经常要和 Api 打交道,另外也会常常使用自己编制的类似于 Api 的带参数的子程序,本文要讲述的是在子程序调用的过程中进行参数传递的概念和分析。一般在程序中,参数的传递是通过堆栈进行的,也就是说,调用者把要传递给子程序(或者被调用者)的参数压入堆栈,子程序在堆栈取出相应的值再使用,比如说,如果你要调用 SubRouting(Var1,Var2,Var3),编译后的最终代码可能是

push Var3
push Var2
push Var1
call SubRouting
add esp,12

也就是说,调用者首先把参数压入堆栈,然后调用子程序,在完成后,由于堆栈中先前压入的数不再有用,调用者或者被调用者必须有一方把堆栈指针修正到调用前的状态。参数是最右边的先入堆栈还是最左边的先入堆栈、还有由调用者还是被调用者来修正堆栈都必须有个约定,不然就会产生不正确的结果,这就是我在前面使用“可能”这两个字的原因:各种语言中调用子程序的约定是不同的,它们的不同点见下表:

  C SysCall StdCall Basic Fortran Pascal
参数从左到右       是 是 是
参数从右到左 是 是 是      
调用者清除堆栈 是           
允许使用:VARARG 是 是 是      

VARARG 表示参数的个数可以是不确定的,有一个例子就是 C 中的 printf 语句,在上表中,StdCall 的定义有个要说明的地方,就是如果 StdCall 使用 :VARARG 时,是由调用者清除堆栈的,而在没有:VARARG时是由被调用者清除堆栈的。
在 Win32 汇编中,约定使用 StdCall 方式,所以我们要在程序开始的时候使用 .model stdcall 语句。也就是说,在 API 或子程序中,最右边的参数先入堆栈,然后子程序在返回的时候负责校正堆栈,举例说明,如果我们要调用 MessageBox 这个 API,因为它的定义是 MessageBox(hWnd,lpText,lpCaption,UType) 所以在程序中要这样使用:

push MB_OK
push offset szCaption
push offset szText
push hWnd
call MessageBox
...

我们不必在 API 返回的时候加上一句 add sp,4*4 来修正堆栈,因为这已经由 MessageBox 这个子程序做了。在 Windows API 中,唯一一个特殊的 API 是 wsprintf,这个 API 是 C 约定的,它的定义是 wsprintf(lpOut,lpFormat,Var1,Var2...),所以在使用时就要:

push 1111
push 2222
push 3333
push offset szFormat
push offset szOut
call wsprintf
add esp,4*5

下面要讲的是子程序如何存取参数,因为缺省对堆栈操作的寄存器有 ESP 和 EBP,而 ESP是堆栈指针,无法暂借使用,所以一般使用 EBP 来存取堆栈,假定在一个调用中有两个参数,而且在 push 第一个参数前的堆栈指针 ESP 为 X,那么压入两个参数后的 ESP 为 X-8,程序开始执行 call 指令,call 指令把返回地址压入堆栈,这时候 ESP 为 X-C,这时已经在子程序中了,我们可以开始使用 EBP 来存取参数了,但为了在返回时恢复 EBP 的值,我们还是再需要一句 push ebp 来先保存 EBP 的值,这时 ESP 为 X-10,再执行一句 mov ebp,esp,根据右图可以看出,实际上这时候 [ebp + 8] 就是参数1,[ebp + c]就是参数2。另外,局部变量也是定义在堆栈中的,它们的位置一般放在 push ebp 保存的 EBP 数值的后面,局部变量1、2对应的地址分别是 [ebp-4]、[ebp-8],下面是一个典型的子程序,可以完成第一个参数减去第二个参数,它的定义是:

MyProc proto Var1,Var2 ;有两个参数
local lVar1,lVar2 ;有两个局部变量

注意,这里的两个 local 变量实际上没有被用到,只是为了演示用,具体实现的代码是:

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

现在对这个子程序分析一下,push ebp/mov ebp,esp 是例行的保存和设置 EBP 的代码,sub esp,8 在堆栈中留出两个局部变量的空间,mov /add 语句完成相加,add esp,8 修正两个局部变量使用的堆栈,ret 8 修正两个参数使用的堆栈,相当于 ret / add esp,8 两句代码的效果。可以看出,这是一个标准的 Stdcall 约定的子程序,使用时最后一个参数先入堆栈,返回时由子程序进行堆栈修正。当然,这个子程序为了演示执行过程,使用了手工保存 ebp 并设置局部变量的方法,实际上,386 处理器有两条专用的指令是完成这个功能用的,那就是 Enter 和 Leave,Enter 语句的作用就是 push ebp/mov ebp,esp/sub esp,xxx,这个 xxx 就是 Enter 的,Leave 则完成 add esp,xxx/pop ebp 的功能,所以上面的程序可以改成:

MyPorc proc
enter 8,0

mov eax,dword ptr [ebp + 8]
sub eax,dword ptr [ebp + c]

leave
ret 8
MyProc endp


好了,说到这儿,参数传递的原理也应该将清楚了,还要最后说的是,在使用 Masm32 编 Win32 汇编程序的时候,我们并不需要记住 [ebp + xx] 等麻烦的地址,或自己计算局部变量需要预留的堆栈空间,还有在 ret 时计算要加上的数值,Masm32 的宏指令都已经把这些做好了,如在 Masm32 中,上面的程序只要写成为:

MyProc proc Var1,Var2
local lVar1,lVar2

mov eax,Var1
sub eax,Var2
ret

MyProc endp

编译器会自动的在 mov eax,Var1 前面插上一句 Enter 语句,它的参数会根据 local 定义的局部变量的多少自动指定,在 ret 前会自动加上一句 Leave,同样,编译器会根据参数的多少把 ret 替换成 ret xxx,把 mov eax,Var1 换成 mov eax,dword ptr [ebp + 8] 等等。

最后是使用 Masm32 的 invoke 宏指令,在前面可以看到,调用带参数的子程序时,我们需要用 push 把参数压入堆栈,如果不小心把参数个数搞错了,就会使堆栈不平衡,从而使程序从堆栈中取出错误的返回地址引起不可预料的后果,所以有必要有一条语句来完成自动检验的任务,invoke 就是这样的语句,实际上,它是自动 push 所有参数,检测参数个数、类型是否正确,并使用 call 来调用的一个宏指令,对于上面的 push/push/call MyProc 的指令,可以用一条指令完成就是:

invoke MyProc,Var1,Var2

当然,当程序编译好以后你去看机器码会发现它被正确地换成了同样的 push/push/call 指令。但是,在使用 invoke 之前,为了让它进行正确的参数检验,你需要对函数进行申明,就象在 C 中一样,申明的语句是:

MyProc proto :DWORD,:DWORD

语句中 proto 是关键字,表示申明,:DWORD 表示参数的类型是 double word 类型的,有几个就表示有几个参数,在 Win32 中参数都是 double word 型的,申明语句要写在 invoke 之前,所以我们一般把它包括在 include 文件中,好了,综合一下,在 Masm32 中使用一个带参数的子程序或者 Api ,我们只需用:

...
MyProc proto :dword,:dword
...
.data
x dd ?
y dd ?
dwResult dd ?
...
mov x,1
mov y,2
invoke MyProc x,y
mov dwResult,eax
...

就行了,如何,是不是很简单啊?不过我可苦了,这篇文章整整花了我一个晚上 ... ##%$^&(*&^(*&(^&(* ...


罗云彬专门写过一个东西 如果看了这个你还不懂再问罢
2012-09-06 17:50
zklhp
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:china
等 级:贵宾
威 望:254
帖 子:11485
专家分:33241
注 册:2007-7-10
收藏
得分:0 
stGlt.wHour,stGlt.wMinute

实在不行你这样 movzx eax, stGlt.wHour 然后压栈

win32压栈都是双字罢
2012-09-06 18:01
W32Asm
Rank: 1
等 级:新手上路
帖 子:8
专家分:0
注 册:2010-12-10
收藏
得分:0 
回复 2楼 zklhp
这些,我大概也都明白,但是他们都是针对dword类型,我的问题中,是涉及到word类型,结果就出错了,我想知道错误的具体原因。难道WIN32ASM中就没办法了,只能用dword类型参数吗?

或者像你说的用movzx,这个用法我也知道,用了movzx是可以完美解决问题,但我还是想知道为什么我的问题中的做法错在哪儿了,不会就一句简单的WIN32ASM只能用dword类型参数?
2012-09-06 18:28
zklhp
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:china
等 级:贵宾
威 望:254
帖 子:11485
专家分:33241
注 册:2007-7-10
收藏
得分:0 
貌似win32API压栈就只能压32位的、、、


2012-09-06 18:42
W32Asm
Rank: 1
等 级:新手上路
帖 子:8
专家分:0
注 册:2010-12-10
收藏
得分:0 
回复 5楼 zklhp
貌似是,但是我压入word类型参数,从反编译来看,它自动补齐了。

但是,它最让我莫名其妙的是,显示的小时wHour,是正确的,显示的分钟wMinute错误。我故意把两个参数颠倒顺序,结果是分钟wMinute显示对了,小时wHour却多了0000.我猜测是跟出栈时有关系,后入栈的能够正确出栈,最先入栈的wMinute出栈的时候错误了。
2012-09-06 18:58
pangding
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:北京
等 级:贵宾
威 望:94
帖 子:6784
专家分:16751
注 册:2008-12-20
收藏
得分:0 
还不结题 z版 难道没意见?
2012-09-06 22:52
水哥
Rank: 5Rank: 5
等 级:贵宾
威 望:15
帖 子:65
专家分:111
注 册:2012-8-11
收藏
得分:0 
看下时间结构的定义,定义的类型是字引用还是双字引用 字引用的话会变成word ptr [xxx+偏移]双字引用会变成dword ptr[xxx+偏移],不要被stGlt.wHour这种东西迷惑了,反汇编后没有这种东西
2012-09-10 14:31
hu9jj
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:红土地
等 级:贵宾
威 望:400
帖 子:11857
专家分:43421
注 册:2006-5-13
收藏
得分:0 
Z版确实存有不少的参考资料啊。

活到老,学到老!http://www.(该域名已经被ISP盗卖了)E-mail:hu-jj@
2012-09-10 19:52
zklhp
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:china
等 级:贵宾
威 望:254
帖 子:11485
专家分:33241
注 册:2007-7-10
收藏
得分:0 
以下是引用pangding在2012-9-6 22:52:32的发言:

还不结题 z版 难道没意见?

我有意见有啥用、、
2012-09-10 20:04
快速回复:有人知道WIN32 API参数传递的详细过程和步骤吗?
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.019597 second(s), 7 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved