写在前面
记录结构体、宏定义、解引用指针、子程序以及局部变量的使用。
程序代码
使用masm32编译。
TestProc.asm
.386
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
include user32.inc
includelib user32.lib
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
;一个宏,可以实现类似cpp中的ifdef _DEBUG
DEBUG equ 0
;定义数据结构
WNDCLASST struct
style DWORD ?
lpfnWndProc DWORD ?
cbClsExtra DWORD ?
cbWndExtra DWORD ?
hInstance DWORD ?
hIcon DWORD ?
hCursor DWORD ?
hbrBackground DWORD ?
lpszMenuName DWORD ?
lpszClassName DWORD ?
WNDCLASST ends
;声明子程序
SUB1 proto C _Var1:dword,_Var2:dword
;未初始化的数据段
if DEBUG
.data?
stWndClass_ WNDCLASST <>
endif
;数据段
.data
stWndClass WNDCLASST <1,1,1,1,1,1,1,1,1,1>
bTest1 db 12h
wtest2 dw 1234h
dwTest3 dd 12345678h
szFormat db "result:0x%08X",0ah,0dh,0
szFormat_addr db "addr:0x%08X",0ah,0dh,0
szPause db "pause",0ah,0dh,0
;代码段
.code
;C约定的子程序
SUB1 PROC C _Var1:dword,_Var2:dword
;定义局部变量,local必须写在所有指令的最前面
local loc1[sizeof szFormat_addr + 1]:byte
pushad
invoke RtlZeroMemory,addr loc1,sizeof loc1 ;局部变量的初始化
invoke crt_memcpy,addr loc1,offset szFormat_addr,sizeof szFormat_addr ;内存拷贝
invoke crt_printf,addr loc1,addr loc1
mov eax,_Var1
mov eax,_Var2
add eax,_Var1
invoke crt_printf,addr szFormat,eax
ret
popad
SUB1 ENDP
;默认使用.model指定的约定,在本例中即stdcall约定,参数类型默认为dword
SUB2 PROC _Var1,_Var2
pushad
mov eax,_Var1
mov eax,_Var2
add eax,_Var2
invoke crt_printf,addr szFormat,eax
popad
ret
SUB2 ENDP
;PASCAL约定的子程序,参数类型默认为dword
SUB3 PROC PASCAL _Var1,_Var2
pushad
mov eax,_Var1
mov eax,_Var2
add eax,_Var1
add eax,_Var2
invoke crt_printf,addr szFormat,eax
popad
ret
SUB3 ENDP
start:
mov eax,stWndClass.style ;为eax赋值结构体成员数值
mov esi,offset stWndClass ;将声明的结构体地址赋值给esi
assume esi:ptr WNDCLASST ;类似于解引用指针
mov eax,[esi].lpfnWndProc ;为eax赋值结构体成员数值
assume esi:nothing ;取消esi的类型定义
mov al,bTest1 ;从bTest1处取单字节赋值给al,12h
mov ax,word ptr bTest1 ;从bTest1处取双字节赋值给ax,3412h
mov eax,dword ptr bTest1 ;从bTest1处取4字节赋值给eax,78123412h
movzx ax,bTest1 ;将8位的bTest1扩展到16位赋值给ax,0012h
movzx eax,bTest1 ;将8位的bTest1扩展到32位赋值给eax,00000012h
movzx eax,ax ;将16位的ax扩展到32位赋值给eax,00000012h
invoke SUB1,1,2 ;C约定的子程序,参数从右往左入栈,调用完毕需要手动平衡堆栈
invoke SUB2,1,2 ;stdcall约定的子程序,参数从右往左入栈,无需手动平衡堆栈
invoke SUB3,1,2 ;PASCAL约定的子程序,参数从左往右入栈,无需手动平衡堆栈
invoke ExitProcess,NULL
end start
将以上代码编译后,再使用OD反汇编调试,可以看到三种子程序入参和平衡堆栈方式的不同,进一步帮助理解。