windows10 安装NASM
在编写nasm时数字默认为10进制
x86控制台程序
objexe.asm:
; equ 定义常量
STD_OUTPUT_HANDLE EQU -11
; 导入外部符号, Windows API函数,已修饰
extern _GetStdHandle@4
extern _WriteFile@20
extern _ExitProcess@4
global Start ; 导出符号。 入口点
section .data ; 初始化数据段
Message db "hello.", 0Dh, 0Ah ; 0Dh 0Ah 回车/换行
MessageLength EQU $ - Message ; $ - Message地址 == Message长度
section .bss ; 未初始化的数据段
Written resd 1 ; 一个dword的大小,4字节,32位
section .text ; 代码段
Start:
push STD_OUTPUT_HANDLE
call _GetStdHandle@4 ;检索指定标准设备
push 0 ; 第5个参数
push Written ; 第4个参数
push MessageLength ; 第3个参数
push Message ; 第2个参数
push EAX ; 第1个参数 i/o句柄
call _WriteFile@20 ; 调用函数,将数据写入指定的文件或输入/输出(I/O)设备
push 0
call _ExitProcess@4
>nasm -f win32 objexe.asm
>golink /entry:Start /console kernel32.dll user32.dll objexe.obj
>objexe.exe
hello.
x64控制台程序
STD_OUTPUT_HANDLE EQU -11
extern GetStdHandle
extern WriteFile
extern ExitProcess
global Start
section .data
Message db "hello x64.", 0Dh, 0Ah
MessageLength EQU $-Message
section .bss
; 结构对齐
; 最大成员为8字节
; 该结构应从8整除的内存位置开始,alignb 8确保
; 如果其中有个成员为4字节,那么你要保证接下来的8字节成员自然对齐,那么就需要加padding
; 最后: 总长度要可以被8整除,不能整除就加padding 16/8=2
alignb 8
StandardHandle resq 1
Written resq 1
; test resd 1 ;4字节成员
; padding1 resd 1 ;加padding对齐
section .text
Start:
sub RSP, 8 ; 将堆栈对齐为16个字节的倍数
sub RSP, 32 ; 阴影空间32字节
mov ECX, STD_OUTPUT_HANDLE
call GetStdHandle
mov qword [REL StandardHandle], RAX
add RSP, 32 ; 删除32个字节
; 4个参数每个8字节就是32,外加一个堆栈上的就是8字节
; 函数在内部会把这些参数放在堆栈,寄存器就那么几个
; 最后+8使堆栈对齐16个字节的倍数 48/16=3 40/16=2.5,能整除就是对齐
sub RSP, 32 + 8 + 8 ; 阴影空间+第5个参数+对齐堆栈
; to a multiple of 16 bytes
mov RCX, qword [REL StandardHandle] ; 第1个参数
lea RDX, [REL Message] ; 第2个参数
mov R8, MessageLength ; 第3个参数
lea R9, [REL Written] ; 第4个参数
mov qword [RSP + 32], 0 ; 第5个参数
call WriteFile
add RSP, 48 ; 删除48个字节
xor ECX, ECX ;异或 ECX = 0
call ExitProcess
>nasm -f win64 objexe.asm
>golink /entry:Start /console kernel32.dll user32.dll objexe.obj
>objexe.exe
hello x64.
x86 MessageBox
extern MessageBoxA
extern ExitProcess
global Start
section .data
text db "hello.",0
title db "title",0
section .text
Start:
; int MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
push 0x00000001
push title
push text
push 0
call MessageBoxA
; 如果点击了取消,就继续弹
cmp eax,2
je Start
; 退出
push 0
call ExitProcess
>nasm -f win32 objexe.asm
>golink /entry:Start kernel32.dll user32.dll objexe.obj
>objexe.exe
x64 MessageBox
extern MessageBoxA
extern ExitProcess
global Start
section .data
text db "hello.",0
title db "title",0
section .text
Start:
sub rsp, 8
sub rsp, 32
_msgbox:
; int MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
xor ecx,ecx
lea edx,[text]
lea r8,[title]
mov r9,0x01
call MessageBoxA
; 如果点击了取消,就继续弹
cmp rax,0x2
je _msgbox
add rsp, 32
_exit:
xor ecx,ecx
call ExitProcess
>nasm -f win64 objexe.asm
>golink /entry:Start kernel32.dll user32.dll objexe.obj
>objexe.exe
x86 基本窗口
WindowWidth EQU 640
WindowHeight EQU 480
extern _CreateWindowExA@48
extern _DefWindowProcA@16
extern _DispatchMessageA@4
extern _ExitProcess@4
extern _GetMessageA@16
extern _GetModuleHandleA@4
extern _IsDialogMessageA@8
extern _LoadImageA@24
extern _PostQuitMessage@4
extern _RegisterClassExA@4
extern _ShowWindow@8
extern _TranslateMessage@4
extern _UpdateWindow@4
global Start
section .data
WindowName db "Basic Window 32", 0
ClassName db "Window", 0
section .bss
hInstance resd 1
section .text
Start:
; https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlea
push 0
call _GetModuleHandleA@4
mov dword [hInstance], EAX ; 保存返回的模块句柄
call WinMain
.Exit:
push 0
call _ExitProcess@4
WinMain:
push EBP
mov EBP, ESP
sub ESP, 80 ; 80个字节的局部变量的空间
; define 指令用于分配存储空间。它可以用于保留以及初始化一个或多个字节。
; https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassexa
%define wc EBP - 80 ; WNDCLASSEX结构(包含窗口类信息). 48字节
%define wc.cbSize EBP - 80
%define wc.style EBP - 76
%define wc.lpfnWndProc EBP - 72
%define wc.cbClsExtra EBP - 68
%define wc.cbWndExtra EBP - 64
%define wc.hInstance EBP - 60
%define wc.hIcon EBP - 56
%define wc.hCursor EBP - 52
%define wc.hbrBackground EBP - 48
%define wc.lpszMenuName EBP - 44
%define wc.lpszClassName EBP - 40
%define wc.hIconSm EBP - 36
%define msg EBP - 32 ; MSG结构. 28字节
%define msg.hwnd EBP - 32 ; 不必拆分每个成员
%define msg.message EBP - 28 ; 在这种情况下,但它显示了每个
%define msg.wParam EBP - 24 ; 成员在堆栈上
%define msg.lParam EBP - 20
%define msg.time EBP - 16
%define msg.pt.x EBP - 12
%define msg.pt.y EBP - 8
%define hWnd EBP - 4
mov dword [wc.cbSize], 48 ; [EBP - 80]
mov dword [wc.style], 2 | 1 | 2000h ; [EBP - 76]
mov dword [wc.lpfnWndProc], WndProc ; [EBP - 72]
mov dword [wc.cbClsExtra], 0 ; [EBP - 68]
mov dword [wc.cbWndExtra], 0 ; [EBP - 64]
mov EAX, dword [hInstance] ; Global
mov dword [wc.hInstance], EAX ; [EBP - 60]
; 加载图标,光标,动画光标或位图
push 8000h
push 0
push 0
push 1 ;加载一个图标
push 7F00h
push 0
call _LoadImageA@24 ; Large program icon
mov dword [wc.hIcon], EAX ; [EBP - 56]
push 8000h
push 0
push 0
push 2 ;加载游标
push 7F00h
push 0
call _LoadImageA@24 ; Cursor
mov dword [wc.hCursor], EAX ; [EBP - 52]
mov dword [wc.hbrBackground], 5 + 1 ; [EBP - 48]
mov dword [wc.lpszMenuName], 0 ; [EBP - 44]
mov dword [wc.lpszClassName], ClassName ; [EBP - 40]
push 8000h
push 0
push 0
push 1
push 7F00h
push 0
call _LoadImageA@24 ; Small program icon
mov dword [wc.hIconSm], EAX ; [EBP - 36]
; 注册一个窗口类,供以后在对CreateWindow或CreateWindowEx函数的调用中使用
;https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerclassexa
lea EAX, [wc] ; [EBP - 80]
push EAX
call _RegisterClassExA@4
;创建具有扩展窗口样式的重叠窗口
;https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexa
push 0
push dword [hInstance] ; Global
push 0
push 0
push WindowHeight
push WindowWidth
push 0 ;y
push 0 ;x
push 0CF0000h ;窗口样式 https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles
push WindowName ; Global
push ClassName ; Global
push 2000000h
call _CreateWindowExA@48
mov dword [hWnd], EAX ; [EBP - 4] 新窗口句柄
;设置指定窗口的显示状态, 1 激活并显示一个窗口
push 1
push dword [hWnd] ; [EBP - 4]
call _ShowWindow@8
push dword [hWnd] ; [EBP - 4]
call _UpdateWindow@4
.MessageLoop:
;从调用线程的消息队列中检索消息
lea EAX, [msg] ; [EBP - 32]
push 0
push 0
push 0
push EAX
call _GetMessageA@16
cmp EAX, 0
je .Done
;确定是否将消息用于指定的对话框,如果是,则处理该消息
lea EAX, [msg] ; [EBP - 32]
push EAX
push dword [hWnd] ; [EBP - 4]
call _IsDialogMessageA@8 ; For keyboard strokes
cmp EAX, 0 ; 如果消息已处理,则返回值为非零
jne .MessageLoop ; 跳过TranslateMessage和DispatchMessage
;将虚拟键消息转换为字符消息。字符消息将发布到调用线程的消息队列中
;以在线程下次调用GetMessage或PeekMessage函数时读取。
lea EAX, [msg] ; [EBP - 32]
push EAX
call _TranslateMessage@4
;将消息调度到窗口过程。它通常用于调度由GetMessage函数检索的消息
lea EAX, [msg] ; [EBP - 32]
push EAX
call _DispatchMessageA@4
jmp .MessageLoop
.Done:
mov ESP, EBP ; Remove the stack frame
pop EBP
xor EAX, EAX
ret
; 回调函数
WndProc:
push EBP ; Set up a Stack frame
mov EBP, ESP
; 获取参数
%define hWnd EBP + 8 ; Location of the 4 passed parameters from
%define uMsg EBP + 12 ; the calling function
%define wParam EBP + 16 ; We can now access these parameters by name
%define lParam EBP + 20
cmp dword [uMsg], 2 ; [EBP + 12]
je _WMDESTROY
_DefaultMessage:
push dword [lParam] ; [EBP + 20]
push dword [wParam] ; [EBP + 16]
push dword [uMsg] ; [EBP + 12]
push dword [hWnd] ; [EBP + 8]
call _DefWindowProcA@16
mov ESP, EBP ; Remove the stack frame
pop EBP
ret 16 ; 内平栈,这就涉及到调用约定,被调用的函数需要处理堆栈
_WMDESTROY:
;向系统指示线程已请求终止(退出)
push 0
call _PostQuitMessage@4
xor EAX, EAX ; return 0
mov ESP, EBP ; Remove the stack frame
pop EBP
ret 16 ; 内平栈
>nasm -f win32 objexe.asm
>golink /entry:Start kernel32.dll user32.dll objexe.obj
>objexe.exe
x64 基本窗口
; Basic Window, 64 bit. V1.02
COLOR_WINDOW EQU 5 ; Constants
CS_BYTEALIGNWINDOW EQU 2000h
CS_HREDRAW EQU 2
CS_VREDRAW EQU 1
CW_USEDEFAULT EQU 80000000h
IDC_ARROW EQU 7F00h
IDI_APPLICATION EQU 7F00h
IMAGE_CURSOR EQU 2
IMAGE_ICON EQU 1
LR_SHARED EQU 8000h
NULL EQU 0
SW_SHOWNORMAL EQU 1
WM_DESTROY EQU 2
WS_EX_COMPOSITED EQU 2000000h
WS_OVERLAPPEDWINDOW EQU 0CF0000h
WindowWidth EQU 640
WindowHeight EQU 480
extern CreateWindowExA ; Import external symbols
extern DefWindowProcA ; Windows API functions, not decorated
extern DispatchMessageA
extern ExitProcess
extern GetMessageA
extern GetModuleHandleA
extern IsDialogMessageA
extern LoadImageA
extern PostQuitMessage
extern RegisterClassExA
extern ShowWindow
extern TranslateMessage
extern UpdateWindow
global Start ; Export symbols. The entry point
section .data ; Initialized data segment
WindowName db "Basic Window 64", 0
ClassName db "Window", 0
section .bss ; Uninitialized data segment
alignb 8
hInstance resq 1
section .text ; Code segment
Start:
sub RSP, 8 ; Align stack pointer to 16 bytes
; GetModuleHandleA 只有一个参数为什么还分配32字节的阴影空间
; win64函数约定,假定您已经分配了32个字节的堆栈空间来存储四个参数寄存器
; 自己写的函数可以不用遵守
sub RSP, 32 ; 32 bytes of shadow space
xor ECX, ECX
call GetModuleHandleA
mov qword [REL hInstance], RAX
add RSP, 32 ; Remove the 32 bytes
call WinMain
.Exit:
xor ECX, ECX
call ExitProcess
WinMain:
push RBP ; Set up a stack frame
mov RBP, RSP
sub RSP, 136 + 8 ; 局部变量为136个字节
; a multiple of 16 (for Windows API functions),
; the + 8 takes care of this.
%define wc RBP - 136 ; WNDCLASSEX structure, 80 bytes
%define wc.cbSize RBP - 136 ; 4 bytes. Start on an 8 byte boundary
%define wc.style RBP - 132 ; 4 bytes
%define wc.lpfnWndProc RBP - 128 ; 8 bytes
%define wc.cbClsExtra RBP - 120 ; 4 bytes
%define wc.cbWndExtra RBP - 116 ; 4 bytes
%define wc.hInstance RBP - 112 ; 8 bytes
%define wc.hIcon RBP - 104 ; 8 bytes
%define wc.hCursor RBP - 96 ; 8 bytes
%define wc.hbrBackground RBP - 88 ; 8 bytes
%define wc.lpszMenuName RBP - 80 ; 8 bytes
%define wc.lpszClassName RBP - 72 ; 8 bytes
%define wc.hIconSm RBP - 64 ; 8 bytes. End on an 8 byte boundary
; msg结构最大成员8字节 总字节数44+4字节的结构对齐=48 48/8=6
%define msg RBP - 56 ; MSG structure, 48 bytes
%define msg.hwnd RBP - 56 ; 8 bytes. Start on an 8 byte boundary
%define msg.message RBP - 48 ; 4 bytes
%define msg.Padding1 RBP - 44 ; 4字节自然对齐
%define msg.wParam RBP - 40 ; 8 bytes
%define msg.lParam RBP - 32 ; 8 bytes
%define msg.time RBP - 24 ; 4 bytes
%define msg.py.x RBP - 20 ; 4 bytes
%define msg.pt.y RBP - 16 ; 4 bytes
%define msg.Padding2 RBP - 12 ; 4字节结构对齐
%define hWnd RBP - 8 ; 8 bytes
mov dword [wc.cbSize], 80 ; [RBP - 136]
mov dword [wc.style], CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNWINDOW ; [RBP - 132]
lea RAX, [REL WndProc]
mov qword [wc.lpfnWndProc], RAX ; [RBP - 128]
mov dword [wc.cbClsExtra], NULL ; [RBP - 120]
mov dword [wc.cbWndExtra], NULL ; [RBP - 116]
mov RAX, qword [REL hInstance] ; Global
mov qword [wc.hInstance], RAX ; [RBP - 112]
sub RSP, 32 + 16 ; Shadow space + 2 parameters
xor ECX, ECX
mov EDX, IDI_APPLICATION
mov R8D, IMAGE_ICON
xor R9D, R9D
mov qword [RSP + 4 * 8], NULL
mov qword [RSP + 5 * 8], LR_SHARED
call LoadImageA ; Large program icon
mov qword [wc.hIcon], RAX ; [RBP - 104]
add RSP, 48 ; Remove the 48 bytes
sub RSP, 32 + 16 ; Shadow space + 2 parameters
xor ECX, ECX
mov EDX, IDC_ARROW
mov R8D, IMAGE_CURSOR
xor R9D, R9D
mov qword [RSP + 4 * 8], NULL
mov qword [RSP + 5 * 8], LR_SHARED
call LoadImageA ; Cursor
mov qword [wc.hCursor], RAX ; [RBP - 96]
add RSP, 48 ; Remove the 48 bytes
mov qword [wc.hbrBackground], COLOR_WINDOW + 1 ; [RBP - 88]
mov qword [wc.lpszMenuName], NULL ; [RBP - 80]
lea RAX, [REL ClassName]
mov qword [wc.lpszClassName], RAX ; [RBP - 72]
sub RSP, 32 + 16 ; Shadow space + 2 parameters
xor ECX, ECX
mov EDX, IDI_APPLICATION
mov R8D, IMAGE_ICON
xor R9D, R9D
mov qword [RSP + 4 * 8], NULL
mov qword [RSP + 5 * 8], LR_SHARED
call LoadImageA ; Small program icon
mov qword [wc.hIconSm], RAX ; [RBP - 64]
add RSP, 48 ; Remove the 48 bytes
sub RSP, 32 ; 32 bytes of shadow space
lea RCX, [wc] ; [RBP - 136]
call RegisterClassExA
add RSP, 32 ; Remove the 32 bytes
sub RSP, 32 + 64 ; Shadow space + 8 parameters
mov ECX, WS_EX_COMPOSITED
lea RDX, [REL ClassName] ; Global
lea R8, [REL WindowName] ; Global
mov R9D, WS_OVERLAPPEDWINDOW
mov dword [RSP + 4 * 8], CW_USEDEFAULT
mov dword [RSP + 5 * 8], CW_USEDEFAULT
mov dword [RSP + 6 * 8], WindowWidth
mov dword [RSP + 7 * 8], WindowHeight
mov qword [RSP + 8 * 8], NULL
mov qword [RSP + 9 * 8], NULL
mov RAX, qword [REL hInstance] ; Global
mov qword [RSP + 10 * 8], RAX
mov qword [RSP + 11 * 8], NULL
call CreateWindowExA
mov qword [hWnd], RAX ; [RBP - 8]
add RSP, 96 ; Remove the 96 bytes
sub RSP, 32 ; 32 bytes of shadow space
mov RCX, qword [hWnd] ; [RBP - 8]
mov EDX, SW_SHOWNORMAL
call ShowWindow
add RSP, 32 ; Remove the 32 bytes
sub RSP, 32 ; 32 bytes of shadow space
mov RCX, qword [hWnd] ; [RBP - 8]
call UpdateWindow
add RSP, 32 ; Remove the 32 bytes
.MessageLoop:
sub RSP, 32 ; 32 bytes of shadow space
lea RCX, [msg] ; [RBP - 56]
xor EDX, EDX
xor R8D, R8D
xor R9D, R9D
call GetMessageA
add RSP, 32 ; Remove the 32 bytes
cmp RAX, 0
je .Done
sub RSP, 32 ; 32 bytes of shadow space
mov RCX, qword [hWnd] ; [RBP - 8]
lea RDX, [msg] ; [RBP - 56]
call IsDialogMessageA ; For keyboard strokes
add RSP, 32 ; Remove the 32 bytes
cmp RAX, 0
jne .MessageLoop ; Skip TranslateMessage and DispatchMessageA
sub RSP, 32 ; 32 bytes of shadow space
lea RCX, [msg] ; [RBP - 56]
call TranslateMessage
add RSP, 32 ; Remove the 32 bytes
sub RSP, 32 ; 32 bytes of shadow space
lea RCX, [msg] ; [RBP - 56]
call DispatchMessageA
add RSP, 32 ; Remove the 32 bytes
jmp .MessageLoop
.Done:
mov RSP, RBP ; Remove the stack frame
pop RBP
xor EAX, EAX
ret
; 回调函数
WndProc:
push RBP ; Set up a stack frame
mov RBP, RSP
; 获取阴影空间
; call+上面的push RBP会导致rsp-16,所以第一个要+16
%define hWnd RBP + 16 ; Location of the shadow space setup by
%define uMsg RBP + 24 ; the calling function
%define wParam RBP + 32
%define lParam RBP + 40
; 将函数参数放在阴影空间
mov qword [hWnd], RCX ; Free up RCX RDX R8 R9 by spilling the
mov qword [uMsg], RDX ; 4 passed parameters to the shadow space
mov qword [wParam], R8 ; We can now access these parameters by name
mov qword [lParam], R9
cmp qword [uMsg], WM_DESTROY ; [RBP + 24]
je _WMDESTROY
_DefaultMessage:
; DefWindowProcA需要4个参数,4*8=32 32/16=2 能对齐不用手动对齐
sub RSP, 32 ; 32 bytes of shadow space
mov RCX, qword [hWnd] ; [RBP + 16]
mov RDX, qword [uMsg] ; [RBP + 24]
mov R8, qword [wParam] ; [RBP + 32]
mov R9, qword [lParam] ; [RBP + 40]
call DefWindowProcA
add RSP, 32 ; Remove the 32 bytes
mov RSP, RBP ; Remove the stack frame
pop RBP
ret
_WMDESTROY:
sub RSP, 32 ; 32 bytes of shadow space
xor ECX, ECX
call PostQuitMessage
add RSP, 32 ; Remove the 32 bytes
xor EAX, EAX ; WM_DESTROY has been processed, return 0
mov RSP, RBP ; Remove the stack frame
pop RBP
ret
x86 基本窗口扩展 看原文
对基本窗口示例的改进:
- 窗口将在屏幕上居中
- 客户区现在将是正确的大小(在调整大小之前)
- 窗口具有背景色
- 将创建2个静态控件。单击时文本会更改颜色
- 创建2个编辑控件。Tab键可用于更改焦点
- 静态和编辑控件的字体从系统默认值更改
- 窗口上有一个黑色矩形
- 关闭窗口时显示退出确认
x64 基本窗口扩展 看原文
对基本窗口示例的改进:
- 窗口将在屏幕上居中
- 客户区现在将是正确的大小(在调整大小之前)
- 窗口具有背景色
- 将创建2个静态控件。单击时文本会更改颜色
- 创建2个编辑控件。Tab键可用于更改焦点
- 静态和编辑控件的字体从系统默认值更改
- 窗口上有一个黑色矩形
- 关闭窗口时显示退出确认