在編寫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鍵可用於更改焦點
- 靜態和編輯控件的字體從系統默認值更改
- 窗口上有一個黑色矩形
- 關閉窗口時顯示退出確認