nasm - BasicWindowExt_32
文章目录
- nasm - BasicWindowExt_32
- 概述
- 笔记
- nasm_main.asm
- my_build.bat
- END
nasm - BasicWindowExt_32
概述
学习找到的demo
笔记
nasm_main.asm
; @file nasm_main.asm
; @brief 用NASM实现一个32bits的扩展窗口
; nasm - BasicWindowExt_32
; 用NASM做的程序,EXE体积真小啊.
; 同样的功能用VS2019 c++来做,即使在release版下调整编译设置, 也做不到这么小的EXE体积。
; 就是用NASM写程序会很麻烦, 不是有刚需,还是用VS2019来写。不过可以考虑用NASM写一部分代码, 编译成obj后,作为外部函数, link到VS2019的工程中.
; ----------------------------------------
; 宏定义
; ----------------------------------------
; Basic Window Extended, 32 bit. V1.04
ANSI_CHARSET EQU 0 ; Constants
BLACKNESS EQU 42h
CLIP_DEFAULT_PRECIS EQU 0
CS_BYTEALIGNWINDOW EQU 2000h
CS_HREDRAW EQU 2
CS_VREDRAW EQU 1
DEFAULT_PITCH EQU 0
ES_AUTOHSCROLL EQU 80h
ES_CENTER EQU 1
FALSE EQU 0
GRAY_BRUSH EQU 2
IDC_ARROW EQU 7F00h
IDI_APPLICATION EQU 7F00h
IDNO EQU 7
IMAGE_CURSOR EQU 2
IMAGE_ICON EQU 1
LR_SHARED EQU 8000h
MB_DEFBUTTON2 EQU 100h
MB_YESNO EQU 4
NULL EQU 0
NULL_BRUSH EQU 5
OPAQUE EQU 2
PROOF_QUALITY EQU 2
SM_CXFULLSCREEN EQU 10h
SM_CYFULLSCREEN EQU 11h
SS_CENTER EQU 1
SS_NOTIFY EQU 100h
SW_SHOWNORMAL EQU 1
TRUE EQU 1
WM_CLOSE EQU 10h
WM_COMMAND EQU 111h
WM_CREATE EQU 1
WM_CTLCOLOREDIT EQU 133h
WM_CTLCOLORSTATIC EQU 138h
WM_DESTROY EQU 2
WM_PAINT EQU 0Fh
WM_SETFONT EQU 30h
OUT_DEFAULT_PRECIS EQU 0
WS_CHILD EQU 40000000h
WS_EX_COMPOSITED EQU 2000000h
WS_OVERLAPPEDWINDOW EQU 0CF0000h
WS_TABSTOP EQU 10000h
WS_VISIBLE EQU 10000000h
WindowWidth EQU 640
WindowHeight EQU 170
Static1ID EQU 100
Static2ID EQU 101
Edit1ID EQU 102
Edit2ID EQU 103
; ----------------------------------------
; 外部函数声明
; ----------------------------------------
extern AdjustWindowRectEx ; Import external symbols
extern BeginPaint ; Windows API functions, decorated
extern BitBlt
extern CreateFontA
extern CreateSolidBrush
extern CreateWindowExA
extern DefWindowProcA
extern DeleteObject
extern DestroyWindow
extern DispatchMessageA
extern EndPaint
extern ExitProcess
extern GetDlgCtrlID
extern GetMessageA
extern GetModuleHandleA
extern GetStockObject
extern GetSystemMetrics
extern InvalidateRect
extern IsDialogMessageA
extern LoadImageA
extern MessageBoxA
extern PostQuitMessage
extern RegisterClassExA
extern SendMessageA
extern SetBkColor
extern SetBkMode
extern SetTextColor
extern ShowWindow
extern TranslateMessage
extern UpdateWindow
global Start ; Export symbols. The entry point
; ----------------------------------------
; 区段定义
; ----------------------------------------
section .data ; Initialized data segment
Static1Colour dd 0F0F0F0h ; Colour (0BBGGRRh)
Static1ColourA dd 020A0F0h
Static2Colour dd 000FFFFh
Static2ColourA dd 08000FFh
Edit1TextColour dd 0F590F5h
Edit1BackColour dd 0A00000h
Edit2TextColour dd 0A56E3Bh
Edit2BackColour dd 0FEFE8Eh
BackgroundColour dd 0A56E3Bh
WindowName db "Basic Window Extended 32", 0
ClassName db "Window", 0
SegoeUI db "Segoe UI", 0
StaticClass db "STATIC", 0
EditClass db "EDIT", 0
Text1 db "ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789", 0
Text2 db "abcdefghijklmnopqrstuvwxyz_0123456789", 0
ExitText db "Do you want to exit?", 0
section .bss ; Uninitialized data segment
hInstance resd 1
BackgroundBrush resd 1
Font resd 1
Static1 resd 1
Static2 resd 1
Edit1 resd 1
Edit2 resd 1
section .text ; Code segment
; ----------------------------------------
; 函数定义
; ----------------------------------------
; fn_x 是顶层函数, 有栈平衡的操作, 可以 call
; proc.x 是顶层函数调用的功能实现(逻辑实现, 功能块), 没有栈平衡的操作, 不能直接call, 只能 je, jmp
fn_Start:
push NULL
call GetModuleHandleA
mov dword [hInstance], EAX
call fn_WinMain
; .Exit:
push NULL
call ExitProcess
; ----------------------------------------
fn_WinMain:
push EBP ; Set up a stack frame
mov EBP, ESP
sub ESP, 104 ; 104 bytes for local variables
%define Screen.Width EBP - 104
%define Screen.Height EBP - 100
%define ClientArea EBP - 96 ; RECT structure. 16 bytes
%define ClientArea.left EBP - 96
%define ClientArea.top EBP - 92
%define ClientArea.right EBP - 88
%define ClientArea.bottom EBP - 84
%define wc EBP - 80 ; WNDCLASSEX structure. 48 bytes
%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 structure. 28 bytes
%define msg.hwnd EBP - 32 ; Breaking out each member is not necessary
%define msg.message EBP - 28 ; in this case, but it shows where each
%define msg.wParam EBP - 24 ; member is on the stack
%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
push dword [BackgroundColour]
call CreateSolidBrush ; Create a brush for the window backgound
mov dword [BackgroundBrush], EAX
mov dword [wc.cbSize], 48 ; [EBP - 80]
mov dword [wc.style], CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNWINDOW ; [EBP - 76]
mov dword [wc.lpfnWndProc], fn_WndProc ; [EBP - 72]
mov dword [wc.cbClsExtra], NULL ; [EBP - 68]
mov dword [wc.cbWndExtra], NULL ; [EBP - 64]
mov EAX, dword [hInstance]
mov dword [wc.hInstance], EAX ; [EBP - 60]
push LR_SHARED
push NULL
push NULL
push IMAGE_ICON
push IDI_APPLICATION
push NULL
call LoadImageA ; Large program icon
mov dword [wc.hIcon], EAX ; [EBP - 56]
; // x86程序调用WinApi后,不用自己进行栈平衡。被调用者自己做了栈平衡
push LR_SHARED
push NULL
push NULL
push IMAGE_CURSOR
push IDC_ARROW
push NULL
call LoadImageA ; Cursor
mov dword [wc.hCursor], EAX ; [EBP - 52]
mov EAX, dword [BackgroundBrush]
mov dword [wc.hbrBackground], EAX ; [EBP - 48]
mov dword [wc.lpszMenuName], NULL ; [EBP - 44]
mov dword [wc.lpszClassName], ClassName ; [EBP - 40]
push LR_SHARED
push NULL
push NULL
push IMAGE_ICON
push IDI_APPLICATION
push NULL
call LoadImageA ; Small program icon
mov dword [wc.hIconSm], EAX ; [EBP - 36]
lea EAX, [wc] ; [EBP - 80]
push EAX
call RegisterClassExA
push SM_CXFULLSCREEN
call GetSystemMetrics ; Get the current screen width
mov dword [Screen.Width], EAX ; [EBP - 104]
push SM_CYFULLSCREEN
call GetSystemMetrics ; Get the current screen height
mov dword [Screen.Height], EAX ; [EBP - 100]
mov dword [ClientArea.left], 0 ; [EBP - 96]
mov dword [ClientArea.top], 0 ; [EBP - 92]
mov dword [ClientArea.right], WindowWidth ; [EBP - 88]
mov dword [ClientArea.bottom], WindowHeight ; [EBP - 84]
push WS_EX_COMPOSITED ; Extended style
push NULL
push WS_OVERLAPPEDWINDOW ; Style
lea EAX, [ClientArea] ; [EBP - 96]
push EAX
call AdjustWindowRectEx ; Get window size for the desired client size
; Size is returned in ClientArea
mov EAX, dword [ClientArea.top] ; [EBP - 92]
sub dword [ClientArea.bottom], EAX ; New Height = ClientArea.bottom - ClientArea.top
mov EAX, dword [ClientArea.left] ; [EBP - 96]
sub dword [ClientArea.right], EAX ; New Width = ClientArea.right - ClientArea.left
push NULL
push dword [hInstance]
push NULL
push NULL
push dword [ClientArea.bottom] ; Height. [EBP - 84]
push dword [ClientArea.right] ; Width. [EBP - 88]
xor ECX, ECX
mov EAX, dword [Screen.Height] ; [EBP - 100]
sub EAX, dword [ClientArea.bottom] ; Corrected window height. [EBP - 84]
cmovs EAX, ECX ; Clamp to 0 (top) if negative
shr EAX, 1 ; EAX = (Screen.Height - window height) / 2
push EAX ; Y position, now centred
mov EAX, dword [Screen.Width] ; [EBP - 104]
sub EAX, dword [ClientArea.right] ; Corrected window width. [EBP - 88]
cmovs EAX, ECX ; Clamp to 0 (left) if negative
shr EAX, 1 ; EAX = (Screen.Width - window width) / 2
push EAX ; X position, now centred
push WS_OVERLAPPEDWINDOW
push WindowName
push ClassName
push WS_EX_COMPOSITED
call CreateWindowExA
mov dword [hWnd], EAX ; [EBP - 4]
push SW_SHOWNORMAL
push dword [hWnd] ; [EBP - 4]
call ShowWindow
push dword [hWnd] ; [EBP - 4]
call UpdateWindow
.MessageLoop:
push NULL
push NULL
push NULL
lea EAX, [msg] ; [EBP - 32]
push EAX
call GetMessageA
cmp EAX, 0
je .Done
lea EAX, [msg] ; [EBP - 32]
push EAX
push dword [hWnd] ; [EBP - 4]
call IsDialogMessageA ; For keyboard strokes
cmp EAX, 0
jne .MessageLoop ; Skip TranslateMessage and DispatchMessage
lea EAX, [msg] ; [EBP - 32]
push EAX
call TranslateMessage
lea EAX, [msg] ; [EBP - 32]
push EAX
call DispatchMessageA
jmp .MessageLoop
.Done:
xor EAX, EAX
mov ESP, EBP ; Remove the stack frame
pop EBP
ret
; ----------------------------------------
fn_WndProc:
push EBP ; Set up a stack frame
mov EBP, ESP
sub ESP, 68 ; 68 bytes for local variables
%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
%define ps EBP - 68 ; PAINTSTRUCT structure. 64 bytes
%define ps.hdc EBP - 68
%define ps.fErase EBP - 64
%define ps.rcPaint.left EBP - 60
%define ps.rcPaint.top EBP - 56
%define ps.rcPaint.right EBP - 52
%define ps.rcPaint.bottom EBP - 48
%define ps.Restore EBP - 44
%define ps.fIncUpdate EBP - 40
%define ps.rgbReserved EBP - 36
%define hdc EBP - 4
cmp dword [uMsg], WM_CLOSE ; [EBP + 12]
je .LB_WMCLOSE
cmp dword [uMsg], WM_COMMAND ; [EBP + 12]
je proc.LB_WMCOMMAND
cmp dword [uMsg], WM_CREATE ; [EBP + 12]
je proc.LB_WMCREATE
cmp dword [uMsg], WM_CTLCOLOREDIT ; [EBP + 12]
je proc.LB_WMCTLCOLOREDIT
cmp dword [uMsg], WM_CTLCOLORSTATIC ; [EBP + 12]
je proc.LB_WMCTLCOLORSTATIC
cmp dword [uMsg], WM_DESTROY ; [EBP + 12]
je proc.LB_WMDESTROY
cmp dword [uMsg], WM_PAINT ; [EBP + 12]
je proc.LB_WMPAINT
.LB_DefaultMessage:
push dword [lParam] ; [EBP + 20]
push dword [wParam] ; [EBP + 16]
push dword [uMsg] ; [EBP + 12]
push dword [hWnd] ; [EBP + 8]
call DefWindowProcA
jmp LB_Return
.LB_WMCLOSE:
push MB_YESNO | MB_DEFBUTTON2
push WindowName
push ExitText
push dword [hWnd] ; [EBP + 8]
call MessageBoxA
cmp EAX, IDNO
; // 如果要跳到的目标标签已经越过了最近的一个外部标签, 那么这个目标标签就应该是外部标签 x
; // 如果要跳到的目标标签没到达最近的一个外部标签, 那么这个目标标签就是内部标签 .x
je LB_Return.WM_Processed
push dword [hWnd] ; [EBP + 8]
call DestroyWindow ; Send a WM_DESTROY message
jmp LB_Return.WM_Processed
proc.LB_WMCOMMAND:
mov EAX, dword [wParam] ; EAX = ID. [EBP + 16]
cmp AX, Static1ID
je .Static1
cmp AX, Static2ID
je .Static2
jmp LB_Return.WM_Processed
.Static1:
mov EAX, dword [Static1Colour]
mov EBX, dword [Static1ColourA]
mov dword [Static1Colour], EBX
mov dword [Static1ColourA], EAX ; Swap colours
push TRUE
push NULL
push dword [lParam] ; Static1 handle. [EBP + 20]
call InvalidateRect ; Redraw control
jmp LB_Return.WM_Processed
.Static2:
mov EAX, dword [Static2Colour]
mov EBX, dword [Static2ColourA]
mov dword [Static2Colour], EBX
mov dword [Static2ColourA], EAX ; Swap colours
push TRUE
push NULL
push dword [lParam] ; Static2 handle. [EBP + 20]
call InvalidateRect ; Redraw control
jmp LB_Return.WM_Processed
proc.LB_WMCREATE:
push NULL
push dword [hInstance]
push Static1ID
push dword [hWnd] ; [EBP + 8]
push 20 ; Height
push 400 ; Width
push 10 ; Y
push 120 ; X
push WS_CHILD | WS_VISIBLE | SS_NOTIFY | SS_CENTER
push Text1 ; Default text
push StaticClass
push NULL
call CreateWindowExA
mov dword [Static1], EAX
push NULL
push dword [hInstance]
push Static2ID
push dword [hWnd] ; [EBP + 8]
push 20 ; Height
push 400 ; Width
push 40 ; Y
push 120 ; X
push WS_CHILD | WS_VISIBLE | SS_NOTIFY | SS_CENTER
push Text2 ; Default text
push StaticClass
push NULL
call CreateWindowExA
mov dword [Static2], EAX
push NULL
push dword [hInstance]
push Edit1ID
push dword [hWnd] ; [EBP + 8]
push 20 ; Height
push 400 ; Width
push 70 ; Y
push 120 ; X
push WS_CHILD | WS_VISIBLE | ES_CENTER | WS_TABSTOP | ES_AUTOHSCROLL
push Text1 ; Default text
push EditClass
push NULL
call CreateWindowExA
mov dword [Edit1], EAX
push NULL
push dword [hInstance]
push Edit2ID
push dword [hWnd] ; [EBP + 8]
push 20 ; Height
push 400 ; Width
push 100 ; Y
push 120 ; X
push WS_CHILD | WS_VISIBLE | ES_CENTER | WS_TABSTOP | ES_AUTOHSCROLL
push Text2 ; Default text
push EditClass
push NULL
call CreateWindowExA
mov dword [Edit2], EAX
lea EAX, [SegoeUI]
push EAX
push DEFAULT_PITCH
push PROOF_QUALITY
push CLIP_DEFAULT_PRECIS
push OUT_DEFAULT_PRECIS
push ANSI_CHARSET
push NULL
push NULL
push NULL
push 400 ; Weight
push NULL
push NULL
push NULL
push 20 ; Size
call CreateFontA
mov dword [Font], EAX
push FALSE
push dword [Font]
push WM_SETFONT
push dword [Static1]
call SendMessageA ; Set Static1 font
push FALSE
push dword [Font]
push WM_SETFONT
push dword [Static2]
call SendMessageA ; Set Static2 font
push FALSE
push dword [Font]
push WM_SETFONT
push dword [Edit1]
call SendMessageA ; Set Edit1 font
push FALSE
push dword [Font]
push WM_SETFONT
push dword [Edit2]
call SendMessageA ; Set Edit2 font
jmp LB_Return.WM_Processed
proc.LB_WMCTLCOLOREDIT: ; For colouring edit controls
push dword [lParam] ; [EBP + 20]
call GetDlgCtrlID ; EAX = ID
cmp EAX, Edit1ID
je .Edit1
cmp EAX, Edit2ID
je .Edit2
; .Default:
push NULL_BRUSH
call GetStockObject ; Return a brush
jmp LB_Return
.Edit1:
push dword [Edit1TextColour]
push dword [wParam] ; [EBP + 16]
call SetTextColor
push OPAQUE
push dword [wParam] ; [EBP + 16]
call SetBkMode
push dword [Edit1BackColour]
push dword [wParam] ; [EBP + 16]
call SetBkColor
push NULL_BRUSH
call GetStockObject ; Return a brush
jmp LB_Return
.Edit2:
push dword [Edit2TextColour]
push dword [wParam] ; [EBP + 16]
call SetTextColor
push OPAQUE
push dword [wParam] ; [EBP + 16]
call SetBkMode
push dword [Edit2BackColour]
push dword [wParam] ; [EBP + 16]
call SetBkColor
push NULL_BRUSH
call GetStockObject ; Return a brush
jmp LB_Return
proc.LB_WMCTLCOLORSTATIC: ; For colouring static controls
push dword [lParam] ; [EBP + 20]
call GetDlgCtrlID ; EAX = ID
cmp EAX, Static1ID
je .Static1
cmp EAX, Static2ID
je .Static2
; .x 作为子标签 隶属于上面的一个标签或函数, 其他标签和函数也可以有同样名字的.x 不冲突
; .Default:
push NULL_BRUSH
call GetStockObject ; Return a brush
jmp LB_Return
.Static1:
push dword [Static1Colour]
push dword [wParam] ; [EBP + 16]
call SetTextColor
push OPAQUE
push dword [wParam] ; [EBP + 16]
call SetBkMode
push 0604060h
push dword [wParam] ; [EBP + 16]
call SetBkColor
push NULL_BRUSH
call GetStockObject ; Return a brush
jmp LB_Return
.Static2:
push dword [Static2Colour]
push dword [wParam] ; [EBP + 16]
call SetTextColor
push OPAQUE
push dword [wParam] ; [EBP + 16]
call SetBkMode
push 0005000h
push dword [wParam] ; [EBP + 16]
call SetBkColor
push GRAY_BRUSH
call GetStockObject ; Return a brush
jmp LB_Return
proc.LB_WMDESTROY:
push dword [BackgroundBrush]
call DeleteObject
push dword [Font]
call DeleteObject
push NULL
call PostQuitMessage
jmp LB_Return.WM_Processed
proc.LB_WMPAINT:
lea EAX, [ps] ; Starting address of PAINTSTRUCT. [EBP - 68]
push EAX
push dword [hWnd] ; [EBP + 8]
call BeginPaint
mov dword [hdc], EAX
push BLACKNESS ; Operation
push 0 ; Source Y
push 0 ; Source X
push dword [hdc] ; Source device context
push 20 ; Height
push 400 ; Width
push 130 ; Destination Y
push 120 ; Destination X
push dword [hdc] ; Destination device context
call BitBlt ; Blit a black rectangle
lea EAX, [ps] ; [EBP - 68]
push EAX
push dword [hWnd] ; [EBP + 8]
call EndPaint
LB_Return.WM_Processed:
xor EAX, EAX ; WM_ has been processed, return 0
LB_Return:
mov ESP, EBP ; Remove the stack frame
pop EBP
ret 16 ; Pop 4 parameters off the stack and return
; ----------------------------------------
my_build.bat
@echo off
cls
rem ----------------------------------------
rem my_build.bat
rem env
rem NASM version 2.16.03 compiled on Apr 17 2024
rem GoLink.Exe Version 1.0.4.6 Copyright Jeremy Gordon 2002-2025
set path=C:\Program Files\NASM;C:\soft\Golink;%path%
rem . bat默认是不支持中文的 .
rem echo full path name - %~f0
rem echo full path - %~dp0
rem echo file name - %~nx0
rem echo work path - %cd%
rem all param - %*
rem . rem注释的第1个字符和最后1个字符,不能是中文,否则有概率会当作命令来执行 .
rem . 调试.bat的方法 .
rem . 如果.bat写的不对,又不容易看出来,只能在每行后面加pause, 然后执行.bat, 然后按一下空格执行一行 .
set prj_src_name_1=nasm_main.asm
set prj_obj_name_1=nasm_main.obj
set prj_exe_name=BasicWindowExt_32.exe
rem win32 or win64
set prj_build_type=win32
rem /console or /SUBSYSTEM:WINDOWS
set prj_sub_sys=/SUBSYSTEM:WINDOWS
echo [%~nx0 %*]
if "%1" == "build" (
call :fn_build
) else if "%1" == "clear" (
call :fn_clear
) else (
call :fn_usage
)
goto end
rem ----------------------------------------
rem function
rem ----------------------------------------
:fn_usage
echo usage my_build.bat [option]
echo build - build asm to EXE
echo clear - clear trush on the project
exit /b
rem ----------------------------------------
:fn_build
echo build ...
rem find file on work path
call :fn_del_file "%prj_obj_name_1%"
nasm -f %prj_build_type% %prj_src_name_1% -o %prj_obj_name_1%
rem 用IDA打开.obj 已经可以看到实现逻辑了
call :fn_del_file "%prj_exe_name%"
rem 如果不指定要连接的dll, 会报错
golink /entry:fn_Start '%prj_sub_sys%' kernel32.dll user32.dll gdi32.dll %prj_obj_name_1% /fo %prj_exe_name%
call :fn_exec "%prj_exe_name%"
exit /b
rem ----------------------------------------
:fn_clear
echo clear ...
call :fn_del_file "%prj_obj_name_1%"
call :fn_del_file "%prj_exe_name%"
exit /b
rem ----------------------------------------
:fn_del_file
if exist "%~1" (
echo del "%~1"
del "%~1"
)
exit /b
:fn_exec
if exist "%~1" (
echo exec "%~1"
%~1
)
exit /b
rem ----------------------------------------
:end
echo END
rem pause
call cmd