在NASMX中使用局部变量。
理论:
在我的这个贴里http://bbs.pediy.com/showthread.php?t=96753 是使用全局变量实现的,特别是对结构体都是在数据段内先声明再使用。全局变量的使用使得程序会占用更多的内存,而且会影响程序的结构化和数据隐藏。所以局部变量是结构化程序设计数据隐藏的唯一方法。下面我将使用局部变量重新实现一个标准的窗口。
NASMX本身为我们提供了对一般数据类型实现局部变量的方法:
locals
local param, 数据类型
endlocals
但是在这个框架下只是实现了一些基本的数据类型的局部变量声明和使用,如byte,word,dword,qword。所以如果想要实现支持像结构体、数组等数据类型的话,就要修改一下nasmx.inc这个宏文件了。它里面都是用宏定义的高级语法特性,对这些特性都了解了也就能基本理解NASMX的语法了。我们需要将下面这段内容修改一下:
%imacro LOCAL 1-2
%ifidni %2, qword
%assign %$locnt 8+%$locnt
%elifidni %2, dword
%assign %$locnt 4+%$locnt
%elifidni %2, word
%assign %$locnt 2+%$locnt
%elifidni %2, byte
%assign %$locnt 1+%$locnt
%else
%assign %$locnt %2+%$locnt
%endif
%1 EQU %$locnt
%endmacro
%else
%assign %$locnt %2+%$locnt
这两行就是实现支持任意长度局部变量的宏定义。
locals/endlocals块内所声明局部变量的使用:分两种情况,一、如果是取局部变量值的话,需要使用var()宏来实现。二、如果是取局部变量的实际地址,则需要使用lea命令来实现,这一点一定要注意分清!
下面看一下重新编写的标准窗口程序:
%include '..\inc\nasmx.inc'
%include '..\inc\win32\windows.inc'
%include '..\inc\win32\kernel32.inc'
%include '..\inc\win32\user32.inc'
entry start
[section .bss]
hInstance: resd 1
lpCommandLine: resd 1
[section .data]
szTitle: db "My First Window", 0x0
szClass: db "FirstWindow", 0x0
[section .code]
proc start
invoke GetModuleHandleA, dword NULL
mov [hInstance], eax
invoke GetCommandLineA
mov [lpCommandLine], eax
invoke WinMain, dword [hInstance], dword NULL, dword lpCommandLine, dword SW_SHOWNORMAL
invoke ExitProcess, dword NULL
ret
endproc
proc WinMain
hinst argd ; Current instance handle
hpinst argd ; Previous instance handle
cmdln argd ; Command line arguments
dwshow argd ; Display style
locals
local wc, WNDCLASSEX_size ;以数据结构长度声明一个结构体,自己把nasm.inc中的local宏改了一下,
local message, MSG_size ;让它可以支持任意长度的数据
local hWnd, dword
endlocals
mov var(wc + WNDCLASSEX.cbSize), dword WNDCLASSEX_size ;WNDCLASSEX.cbSize
mov var(wc + WNDCLASSEX.style), dword CS_HREDRAW | CS_VREDRAW ;WNDCLASSEX.style
mov var(wc + WNDCLASSEX.lpfnWndProc), dword WndProc ;WNDCLASSEX.lpfnWndProc
mov var(wc + WNDCLASSEX.cbClsExtra), dword NULL ;WNDCLASSEX.cbClsExtra
mov var(wc + WNDCLASSEX.cbWndExtra), dword NULL ;WNDCLASSEX.cbWndExtra
push dword [hInstance]
pop dword var(wc + WNDCLASSEX.hInstance) ;WNDCLASSEX.hInstance
mov var(wc + WNDCLASSEX.hbrBackground), dword COLOR_WINDOW + 1 ;WNDCLASSEX.hbrBackground
mov var(wc + WNDCLASSEX.lpszMenuName), dword NULL ;WNDCLASSEX.lpszMenuName
mov var(wc + WNDCLASSEX.lpszClassName), dword szClass ;WNDCLASSEX.lpszClassName
invoke LoadIconA, dword NULL, IDI_APPLICATION
mov var(wc + WNDCLASSEX.hIcon), eax ;WNDCLASSEX.hIcon
mov var(wc + WNDCLASSEX.hIconSm), eax ;WNDCLASSEX.hIconSm
invoke LoadCursorA, dword NULL, IDC_ARROW
mov var(wc + WNDCLASSEX.hCursor), eax ;WNDCLASSEX.hCursor
lea eax, var(wc) ;获取结构体wc的实际地址传入RegisterClassExA函数
invoke RegisterClassExA, eax
invoke CreateWindowExA, dword NULL, dword szClass, dword szTitle, dword WS_OVERLAPPEDWINDOW + WS_VISIBLE, dword CW_USEDEFAULT, dword CW_USEDEFAULT, dword CW_USEDEFAULT, dword CW_USEDEFAULT, dword NULL, dword NULL, dword argv(hinst), dword NULL
mov var(hWnd), eax
invoke ShowWindow, dword var(hWnd), dword argv(dwshow)
invoke UpdateWindow, dword var(hWnd)
.WHILE:
lea ebx, var(message) ;获取结构体message的实际地址
invoke GetMessageA, ebx, dword NULL, dword NULL, dword NULL
cmp eax, dword 0
je .ENDW
invoke TranslateMessage, ebx
invoke DispatchMessageA, ebx
jmp .WHILE
.ENDW:
mov eax, dword var(message + MSG.wParam) ;MSG.wParam
ret
endproc
proc WndProc
hwnd argd ; Window handle
umsg argd ; Window message
wparam argd ; wParam
lparam argd ; lParam
if argv(umsg), ==, dword WM_DESTROY
invoke PostQuitMessage, dword NULL
else
invoke DefWindowProcA, dword argv(hwnd), dword argv(umsg), dword argv(wparam), dword argv(lparam)
endif
ret
endproc
分析:
locals
local wc, WNDCLASSEX_size ;以数据结构长度声明一个结构体,自己把nasm.inc中的local宏改了一下,
local message, MSG_size ;让它可以支持任意长度的数据
local hWnd, dword
endlocals
上面这一段是变量声明,实际上就是分配内存空间。local的使用方法和上面说明的是一样的,但是一定要注意在“数据类型”中必须是数值否则的话无法编译通过,这一点宏里没有加以限制,所以只能靠自己来遵守。WNDCLASSEX_size是WNDCLASSEX数据结构的长度。
local wc, WNDCLASSEX_size
这句的意思就是声明一个叫wc的WNDCLASSEX数据结构实例。
mov var(wc + WNDCLASSEX.cbSize), dword WNDCLASSEX_size ;WNDCLASSEX.cbSize
mov var(wc + WNDCLASSEX.style), dword CS_HREDRAW | CS_VREDRAW ;WNDCLASSEX.style
mov var(wc + WNDCLASSEX.lpfnWndProc), dword WndProc ;WNDCLASSEX.lpfnWndProc
mov var(wc + WNDCLASSEX.cbClsExtra), dword NULL ;WNDCLASSEX.cbClsExtra
mov var(wc + WNDCLASSEX.cbWndExtra), dword NULL ;WNDCLASSEX.cbWndExtra
push dword [hInstance]
pop dword var(wc + WNDCLASSEX.hInstance) ;WNDCLASSEX.hInstance
mov var(wc + WNDCLASSEX.hbrBackground), dword COLOR_WINDOW + 1 ;WNDCLASSEX.hbrBackground
mov var(wc + WNDCLASSEX.lpszMenuName), dword NULL ;WNDCLASSEX.lpszMenuName
mov var(wc + WNDCLASSEX.lpszClassName), dword szClass ;WNDCLASSEX.lpszClassName
invoke LoadIconA, dword NULL, IDI_APPLICATION
mov var(wc + WNDCLASSEX.hIcon), eax ;WNDCLASSEX.hIcon
mov var(wc + WNDCLASSEX.hIconSm), eax ;WNDCLASSEX.hIconSm
invoke LoadCursorA, dword NULL, IDC_ARROW
mov var(wc + WNDCLASSEX.hCursor), eax
上面是对wc进行初始化。注意var()的使用。
lea eax, var(wc) ;获取结构体wc的实际地址传入RegisterClassExA函数
invoke RegisterClassExA, eax
因为RegisterClassExA函数的参数要求是传入一个WNDCLASSEX结构的指针,所以我们要先取得wc的实际地址,这就需要使用lea eax, var(wc)这一句了。
lea ebx, var(message) ;获取结构体message的实际地址
invoke GetMessageA, ebx, dword NULL, dword NULL, dword NULL
cmp eax, dword 0
je .ENDW
invoke TranslateMessage, ebx
invoke DispatchMessageA, ebx
上面几句也是同理。
invoke ShowWindow, dword var(hWnd), dword argv(dwshow)
invoke UpdateWindow, dword var(hWnd)
这两句则不同,因为ShowWindow和UpdateWindow这两个函数都是要传入其本身的值,所以直接使用var()宏就可以了。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课