汇编手记之子程序的使用


写在前面

记录结构体、宏定义、解引用指针、子程序以及局部变量的使用。

程序代码

使用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处取单字节赋值给al12h
    mov ax,word ptr bTest1                                                  ;bTest1处取双字节赋值给ax3412h
    mov eax,dword ptr bTest1                                                ;bTest1处取4字节赋值给eax78123412h
    movzx ax,bTest1                                                         ;8位的bTest1扩展到16位赋值给ax0012h
    movzx eax,bTest1                                                        ;8位的bTest1扩展到32位赋值给eax00000012h
    movzx eax,ax                                                            ;16位的ax扩展到32位赋值给eax00000012h
    invoke SUB1,1,2                                                         ;C约定的子程序,参数从右往左入栈,调用完毕需要手动平衡堆栈
    invoke SUB2,1,2                                                         ;stdcall约定的子程序,参数从右往左入栈,无需手动平衡堆栈
    invoke SUB3,1,2                                                         ;PASCAL约定的子程序,参数从左往右入栈,无需手动平衡堆栈

    invoke ExitProcess,NULL

end start

将以上代码编译后,再使用OD反汇编调试,可以看到三种子程序入参和平衡堆栈方式的不同,进一步帮助理解。