I try to create the simplest WinAPI window using assembly NASM.
I have problem with Window Proc. Look at commented lines:
%macro API 2
import %1 %2
extern %1
%endmacro
API GetModuleHandleA, kernel32.dll
API LoadIconA,user32.dll
API LoadCursorA,user32.dll
API RegisterClassExA, user32.dll
API CreateWindowExA, user32.dll
API MessageBoxA, user32.dll
API SendMessageA, user32.dll
API DefWindowProcA, user32.dll
API ExitProcess, kernel32.dll
API GetMessageA, user32.dll
API DispatchMessageA, user32.dll
API TranslateMessage,user32.dll
API ShowWindow,user32.dll
API UpdateWindow,user32.dll
API GetCommandLineA,kernel32.dll
API PostQuitMessage,user32.dll
segment .data USE32
windowName db "Hello world!", 0
cmdLine dd 0
hWnd dd 0
hInst dd 0
hCursor dd 0
className db "moje_okno",0
blad db "Blad!!!",0
segment .bss
struc WNDCLASSEX
.sSize resb 4
.style resb 4
.wndProc resb 4
.clsExtra resb 4
.wndExtra resb 4
.hInstance resb 4
.hIcon resb 4
.hCursor resb 4
.background resb 4
.sMenuName resb 4
.sClassName resb 4
.hIconSm resb 4
endstruc
wndClass istruc WNDCLASSEX
iend
global ..start
segment .text USE32
..start:
push 0
call [GetModuleHandleA]
mov dword [hInst], eax ; application handle
push dword 0x00007f00 ; MAKEINTRESOURCE(32512)
push dword 0
call [LoadCursorA]
mov dword [hCursor], eax ; cursor handle
mov dword [wndClass + WNDCLASSEX.sSize], dword 48 ; struct size
mov dword [wndClass + WNDCLASSEX.style], dword 0 ; style
mov dword [wndClass + WNDCLASSEX.wndProc], wndproc ; window proc
mov dword [wndClass + WNDCLASSEX.clsExtra], dword 0
mov dword [wndClass + WNDCLASSEX.wndExtra], dword 0
mov eax, dword [hInst]
mov dword [wndClass + WNDCLASSEX.hInstance], eax ; handle
mov dword [wndClass + WNDCLASSEX.hIcon], dword 0
mov eax, dword [hCursor]
mov dword [wndClass + WNDCLASSEX.hCursor], eax
mov dword [wndClass + WNDCLASSEX.background], dword 0
mov dword [wndClass + WNDCLASSEX.sMenuName], dword 0
mov dword [wndClass + WNDCLASSEX.sClassName], className ; class name
mov dword [wndClass + WNDCLASSEX.hIconSm], dword 0
push wndClass
call [RegisterClassExA]
call near sprawdz_blad ; check return value of RegisterClassExA
push 0 ; param
push dword [hInst] ; handle
push 0 ;hMenu
push 0 ;parent
push 200 ;height
push 200 ;width
push 200 ;y
push 200 ;x
push 0 ;style
push className ;window name
push className ;window class
push 0 ;extended style
call [CreateWindowExA]
push eax
call near sprawdz_blad ;check return value of CreateWindowExA. RETURNS 0
push 0
call [ExitProcess]
wndproc:
; HERE I NEED ACCESS TO WINDOW PROC PARAMETERS: HWND, MSG, WPARAM, LPARAM
; I TRIED:
pop eax
pop ebx
pop ecx
pop edx
; BUT IT DOESN'T WORK
; THERE ARE NOT RIGHT VALUES IN THESE REGISTRIES
ret
box:
push 0
push blad
push blad
push 0
call [MessageBoxA]
ret
sprawdz_blad:
pop eax
cmp eax, 0
jne ok ; if function returns 0 everything is allright
push 0
push blad
push blad
push 0
call [MessageBoxA]
push 1
call [ExitProcess]
ok:
ret
I try to make it work for hours but I'm out of ideas.
Please help.
Greetings, Michal.
A called subroutine, wheether you call it yourself or it's called by Windows (like wndproc) has its return address as the first thing on the stack. You do NOT want to pop this! To access the parameters, you need to look farther up the stack. Try something like...
wndproc:
mov eax, [esp + 4]
mov ebx, [esp + 8]
; etc...
See if that helps...
Best,
Frank
Related
I am studying NASM on MacOS.
I'm trying to implement a single line spacing using a function, but I have a problem.
SYS_EXIT equ 0x1
SYS_READ equ 0x3
SYS_WRITE equ 0x4
STDIN equ 0x1
STDOUT equ 0x2
segment .data
Msg_Line db " ", 0xA, 0x0
Len_Line equ $ - Msg_Line
Msg_FirstPrompt db "Enter a Digit : "
Len_FirstPrompt equ $ - Msg_FirstPrompt
Msg_SecondPrompt db "Please Enter a Second Digit : "
Len_SecondPrompt equ $ - Msg_SecondPrompt
Msg_ThirdPrompt db "The Sum is : "
Len_ThirdPrompt equ $ - Msg_ThirdPrompt
segment .bss
Bss_FirstNumber resb 2
Bss_SecondNumber resb 2
Bss_Result resb 1
section .text
global EntryPoint
Endl:
push dword eax
push dword Len_Line
push dword Msg_Line
push dword STDOUT
sub esp, 0x4
mov eax, SYS_WRITE
int 0x80
pop eax
ret 0x4
EntryPoint:
; Print First Prompt
push dword Len_FirstPrompt
push dword Msg_FirstPrompt
push dword STDOUT
sub esp, 0x4
mov eax, SYS_WRITE
int 0x80
; Get Digit
push dword 2
push dword Bss_FirstNumber
push dword STDIN
sub esp, 0x4
mov eax, SYS_READ
int 0x80
; Print Second Prompt
push dword Len_SecondPrompt
push dword Msg_SecondPrompt
push dword STDOUT
sub esp, 0x4
mov eax, SYS_WRITE
int 0x80
; Get Second Digit
push dword 2
push dword Bss_SecondNumber
push dword STDIN
sub esp, 0x4
mov eax, SYS_READ
int 0x80
; +operator use
mov eax, [Bss_FirstNumber]
sub eax, '0'
mov ebx, [Bss_SecondNumber]
sub ebx, '0'
add eax, ebx
add eax, '0'
mov [Bss_Result], eax
; Print Result Prompt
push dword Len_ThirdPrompt
push dword Msg_ThirdPrompt
push dword STDOUT
sub esp, 0x4
mov eax, SYS_WRITE
int 0x80
; Print Result
push dword 1
push dword Bss_Result
push dword STDOUT
sub esp, 0x4
mov eax, SYS_WRITE
int 0x80
sub esp, 0x4
call Endl
;push dword Len_Line
;push dword Msg_Line
;push dword STDOUT
;sub esp, 0x4
;mov eax, SYS_WRITE
;int 0x80
; Exit Program
sub esp, 0x4
mov eax, SYS_EXIT
int 0x80
Result :
Enter a Digit : 3
Please Enter a Second Digit : 2
The Sum is : 5
Segmentation fault: 11
It works fine if you just write it without calling it as a function.
Why do I get an error if I implement it as a function?
When using assembly language with MASM (x86 architecture), one can make use of the standard C functions by including libraries. For example: printf and getchar.
When compiling with Asembly With Source Code/FAs in Visual Studio and inspecting the resulting assembly file I stumbled upon the following:
PUBLIC _printf
EXTRN __imp__getchar : PROC
_printf is declared PUBLIC and defined locally (inline within the same file, thus not defined externally in the library file), while _imp_getchar is defined externally
This is the resulting _printf definition the compiler generated while compiling in debug:
_TEXT SEGMENT
__ArgList$ = -20 ; size = 4
__Result$ = -8 ; size = 4
__Format$ = 8 ; size = 4
_printf PROC ; COMDAT
; 950 : {
push ebp
mov ebp, esp
sub esp, 216 ; 000000d8H
push ebx
push esi
push edi
lea edi, DWORD PTR [ebp-216]
mov ecx, 54 ; 00000036H
mov eax, -858993460 ; ccccccccH
rep stosd
; 951 : int _Result;
; 952 : va_list _ArgList;
; 953 : __crt_va_start(_ArgList, _Format);
call ??$__vcrt_va_start_verify_argument_type#QBD##YAXXZ ; __vcrt_va_start_verify_argument_type<char const * const>
lea eax, DWORD PTR __Format$[ebp+4]
mov DWORD PTR __ArgList$[ebp], eax
; 954 : _Result = _vfprintf_l(stdout, _Format, NULL, _ArgList);
mov eax, DWORD PTR __ArgList$[ebp]
push eax
push 0
mov ecx, DWORD PTR __Format$[ebp]
push ecx
mov esi, esp
push 1
call DWORD PTR __imp____acrt_iob_func
add esp, 4
cmp esi, esp
call __RTC_CheckEsp
push eax
call __vfprintf_l
add esp, 16 ; 00000010H
mov DWORD PTR __Result$[ebp], eax
; 955 : __crt_va_end(_ArgList);
mov DWORD PTR __ArgList$[ebp], 0
; 956 : return _Result;
mov eax, DWORD PTR __Result$[ebp]
; 957 : }
pop edi
pop esi
pop ebx
add esp, 216 ; 000000d8H
cmp ebp, esp
call __RTC_CheckEsp
mov esp, ebp
pop ebp
ret 0
_printf ENDP
_TEXT ENDS
My question
Why is _printf defined locally as opposed to getchar, which is defined externally?
The code for printf is right there in your listing. If you remove the assembly, you get:
; 950 : {
; 951 : int _Result;
; 952 : va_list _ArgList;
; 953 : __crt_va_start(_ArgList, _Format);
; 954 : _Result = _vfprintf_l(stdout, _Format, NULL, _ArgList);
; 955 : __crt_va_end(_ArgList);
; 956 : return _Result;
; 957 : }
So, printf is an (inline?) function that calls _vfprintf_l, which does all the heavy work (and is probably used to implement other C library functions as well).
Complete source:
SECTION .data ; initialized data
SEEK_SET dd 0;
SEEK_CUR dd 1;
SEEK_END dd 2;
fname: db "d:\asmplus\tsources\s1.txt", 0
fread: db "r", 0
mopf: db "open", 0
mskf: db "seek", 0
mcld: db "[FILE] call [%s]: %d", 10, 0
mcls: db "[FILE] call [%s]: %s", 10, 0
merr: db "[FILE] error opening %s", 10, 0
mret: db "[FILE] ret [%s]: %d", 10, 0
SECTION .text ; code
extern _fopen
extern _fseek
extern _printf
global _main
_main:
; stash base stack pointer
push ebp
mov ebp, esp
push DWORD fname
push DWORD mopf
push DWORD mcls
call _printf
add esp, 12
; open file
push DWORD fread
push DWORD fname
call _fopen
add esp, 8
mov [fh], eax
; output result
push DWORD [fh]
push DWORD mopf
push DWORD mret
call _printf
add esp, 12
push DWORD [fh]
push DWORD mskf
push DWORD mcld
call _printf
add esp, 12
; C:
; fseek(fp, 0L, SEEK_END); ; set up constants: SEEK_END, SEEK_SET, etc.
push DWORD SEEK_END
push DWORD 0
push DWORD [fh] ; file handle
call _fseek ; ret [eax]: 0 okay; otherwise nz
add esp, 12 ; reset stack pointer
; output result
push DWORD eax
push DWORD mskf
push DWORD mret
call _printf
add esp, 12
;; NEXT: sz = ftell(fp); ; result to eax
.done:
; restore base stack pointer
mov esp, ebp
pop ebp
ret
SECTION .bss ; uninitialized data
fh: resd 1
Output when provided a valid file name:
[FILE] call [open]: d:\asmplus\tsources\s1.txt
[FILE] ret [open]: 2002397536
[FILE] call [seek]: 2002397536
[FILE] ret [seek]: -1
The 2nd & 3rd lines should display a file handle. What I see does not look right and would be why the seek return is -1. What am I doing wrong to open the file?
I'm having trouble with the call to BeginPaint(hWnd, lpPaint) in the WindowPaint event. It seems that the function BeginPaint overwrite the stack.
A little C program tell me that the size of PAINTSTRUCT (lpPaint) should be 34.
If i put the adresse to EBP-34 or a little more EBP-56 for the return value of lpPaint, it crash. If I allow alot of stack and put the return adresse to EBP-170 it works.
Simple code:
;Note : All Win32 API functions preserve the EBP, EBX, ESI, and EDI registers
extern GetModuleHandleA
extern ExitProcess
extern GetCommandLineA
extern RegisterClassExA
extern LoadIconA
extern LoadCursorA
extern CreateWindowExA
extern ShowWindow
extern UpdateWindow
extern MessageBoxA
extern GetMessageA
extern TranslateMessage
extern DispatchMessageA
extern PostQuitMessage
extern DefWindowProcA
extern DrawTextA
extern BeginPaint
extern EndPaint
import GetModuleHandleA kernel32.dll
import ExitProcess kernel32.dll
import GetCommandLineA kernel32.dll
import RegisterClassExA user32.dll
import LoadIconA user32.dll
import LoadCursorA user32.dll
import CreateWindowExA user32.dll
import ShowWindow user32.dll
import UpdateWindow user32.dll
import MessageBoxA user32.dll
import GetMessageA user32.dll
import TranslateMessage user32.dll
import DispatchMessageA user32.dll
import PostQuitMessage user32.dll
import DefWindowProcA user32.dll
import DrawTextA user32.dll
import BeginPaint user32.dll
import EndPaint user32.dll
section .text use32
..start:
;Handle from calling process
push dword 0
call [GetModuleHandleA]
mov dword [hInstance], eax
;Get command line
call [GetCommandLineA]
mov dword [commandLine], eax
;Main window
Call WindowMain
;Exit
Push eax
call [ExitProcess]
;========================================================================================
; Create a main windows
;========================================================================================
WindowMain:
push ebp
mov ebp, esp
;Local variable
;==============
;EBP-48 = WNDCLASSEX Structure 48 bytes
;EBP-72 = MSG 24 bytes
;EBP-76 = HWND, handle of our window 4 bytes
sub esp, 76
lea ebx, [ebp-48] ;EBX is now address of WNDCLASSEX
;WNDCLASSEX Structure : http://msdn.microsoft.com/en-us/library/ms633577(v=vs.85).aspx
mov dword [ebx+00], 48 ;Size of the structure
mov dword [ebx+04], 3 ;Style of the window
mov dword [ebx+08], WindowProcedure ;Callback function for events
mov dword [ebx+12], 0
mov dword [ebx+16], 0
mov dword [ebx+20], hInstance ;Handle to the window
;[ebx+24] = HICON
;[ebx+28] = HCURSOR
mov dword [ebx+32], 6 ;Background brush
mov dword [ebx+36], 0 ;Menu name NULL
mov dword [ebx+40], ClassName ;Class name for the window
;[ebx+44] = HICON
;LoadIconA(0, IDI_APPLICATION);
push 32512
push 0
call [LoadIconA]
mov dword [ebx+24], eax ;Icon for our window
mov dword [ebx+44], eax ;Small icon for our window
;LoadCursorA(0, IDC_ARROW);
push dword 32512
push 0
call [LoadCursorA]
mov dword [ebx+28], eax
;RegisterClassExA(WNDCLASSEX address);
push ebx
call [RegisterClassExA]
;CreateWindowEx(0, ClassName, window title, WS_OVERLAPPEDWINDOW, x, y, width, height, handle to parent window, handle to menu, hInstance, NULL);
push 0
push hInstance
push 0
push 0
push 400 ;High
push 500 ;Wide
push dword 0x80000000 ;CW_USEDEFAULT
push dword 0x80000000 ;CW_USEDEFAULT
;WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
push dword 0x00 | 0xC00000 | 0x80000 | 0x40000 | 0x20000 | 0x10000 ;WS_OVERLAPPEDWINDOW
push dword ApplicationName
push dword ClassName
push dword 0
call [CreateWindowExA]
mov dword [ebp-76], eax ;Handle of our window
cmp eax, 0
jz .newWindowsFailed
;ShowWindow(hWind, SW_SHOWDEFAULT);
push dword 10
push dword [ebp-76]
call [ShowWindow]
;UpdateWindow(hWind);
push dword [ebp-76]
call [UpdateWindow]
.MessageLoop:
;GetMessageA(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax);
push dword 0
push dword 0
push dword 0
lea ebx, [ebp-72]
push ebx
call [GetMessageA]
cmp eax, 0 ;WM_QUIT
jz .MessageLoopExit
;TranslateMessage(lpMsg);
lea ebx, [ebp-72]
push ebx
call [TranslateMessage]
;DispatchMessageA(lpMsg);
lea ebx, [ebp-72]
push ebx
call [DispatchMessageA]
jmp .MessageLoop
.MessageLoopExit:
jmp .finish
.newWindowsFailed:
push dword 0
push dword 0
push errMsg
push 0
call [MessageBoxA]
mov eax, 1 ;Return error
mov esp, ebp
pop ebp
ret
.finish:
mov esp, ebp
pop ebp
ret
;========================================================================================
; Handle the events that our window sends us.
;========================================================================================
;LRESULT CALLBACK WindowProc(
; _In_ HWND hwnd,
; _In_ UINT uMsg,
; _In_ WPARAM wParam,
; _In_ LPARAM lParam
;);
;========================================================================================
WindowProcedure:
push ebp
mov ebp, esp
;Local variable
;==============
;EBP-56 = PAINTSTRUCT <- RECALCULER LA TAILLE DE LA STUCTURE!!!
;EBP-60 = HDC
;EBP-76 = RECT
sub esp, 170
mov eax, dword [ebp+12] ;uMsg
cmp eax, 2 ;WM_DESTROY
jz .WindowDestroy
cmp eax, 0X0F ;WM_PAINT
jz .WindowPaint
.WindowDefault:
push dword [ebp+20]
push dword [ebp+16]
push dword [ebp+12]
push dword [ebp+08]
call [DefWindowProcA]
;The return value is from DefWindowProcA so we can't change EAX
mov esp, ebp
pop ebp
ret 16
.WindowDestroy:
;We pass 0 as an argument to the PostQuitMessage() function, to tell it
;to pass 0 as the value of wParam for the next message. At that point,
;GetMessage() will return 0, and the message loop will terminate.
;PostQuitMessage(nExitCode);
push dword 0
call [PostQuitMessage]
jmp .WindowProcedureFinish
.WindowPaint:
;BeginPaint(hWnd, lpPaint);
lea ebx, [ebp-34]
push ebx
push dword [ebp+8]
call [BeginPaint]
mov dword [ebp-60], eax ;Save device context
jmp .WindowProcedureFinish
.WindowProcedureFinish:
xor eax, eax
mov esp, ebp
pop ebp
ret 16
msgBox:
; MessageBoxA(0, msg1, title, 0);
push dword 0
push dword ClassName
push dword TextLabel
push dword 0
call [MessageBoxA]
ret
section .data
ClassName db "Main Window", 0
ApplicationName db "Win32 Assembler", 0
errMsg db "An error occured while making the new window.", 0
TextLabel db "Welcom to the main event!", 13, 10, 13, 10, 0
section .bss
hInstance resd 1
commandLine resd 1
Compile with :
nasm -fobj main.asm
alink -oPE main.obj -o main.exe
A little C program tell me that the size of PAINTSTRUCT (lpPaint)
should be 34
Really? What C compiler is that??? If you look up PAINTSTRUCT on MSDN:
typedef struct tagPAINTSTRUCT {
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
} PAINTSTRUCT, *PPAINTSTRUCT;
hdc = 4 bytes
fErase = 4 bytes
rcPaint = RECT` = 16 bytes
fRestore = 4 bytes
fIncUpdate = 4 bytes
rgbReserved = 32 bytes
and RECT:
typedef struct _RECT {
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT, *PRECT;
All of that added together gives you 64 bytes!!!
That is why [EBP-56] won't work, but [EBP-170] will work, plenty of room for the structure.
extern GetModuleHandleA
extern LoadCursorA
extern RegisterClassA
extern CreateWindowExA
extern GetMessageA
extern DispatchMessageA
extern TranslateMessage
extern ExitProcess
extern PostQuitMessage
extern DefWindowProcA
section .data
MSG dq 0 ;+0 hWnd
dd 0 ;+8 message
dd 0 ;padding for next
wParam dq 0 ;+10 wParam
dq 0 ;+18 lParam
dd 0 ;+20 time
dd 0 ;+24 1st part of point structure
dd 0 ;+28 2nd part of point structure
dd 0 ;padding to bring total size to 48 bytes
WNDCLASS dd 1h+2h+40h ;+0 window class style (CS_VREDRAW+CS_HREDRAW+CS_CLASSDC)
dd 0 ;padding for next
dq WndProcTable ;+8 pointer to Window Procedure
dd 0 ;+10 no. of extra bytes to allocate after structure
dd 0 ;+14 no. of extra bytes to allocate after window instance
hInst dq 0 ;+18 handle to instance containing window procedure
dq 0 ;+20 handle to the class icon
hCursor dq 0 ;+28 handle to the class cursor
dq 6 ;+30 identifies the class background brush (6=COLOR_WINDOW+1)
dq 0 ;+38 pointer to resource name for class menu
dq win_class_name ;+40 pointer to string for window class name
win_class_name db 'simplewindow',0 ;string holding name of window class
win_id dq 0
but_id dq 0
kopf db '64 bit program', 0
class_button db 'button', 0
button_kopf db 'hjh', 0
mbt db 'this is only a test', 0
mbc db 'achtung', 0
section .text
global start
start:
sub rsp, 0x8
xor rcx, rcx
call GetModuleHandleA
mov [hInst], rax
mov rcx, 0
mov rdx, 32512
call LoadCursorA
mov [hCursor], rax
mov rcx, WNDCLASS
sub rsp, 0x20
call RegisterClassA
add rsp, 0x20
;creating main window
mov rcx, 0
mov rdx, win_class_name
mov r8, kopf
mov r9, 0x10000000+0x00080000+0x00020000
push 0
push qword[hInst]
push 0
push 0
push 512
push 512
push 256
push 256
sub rsp, 0x20
call CreateWindowExA
add rsp, 0x20
add rsp, 0x40
mov [win_id], rax
zyklus:
mov rcx, MSG
mov rdx, 0
mov r8, 0
mov r9, 0
sub rsp, 20h
call GetMessageA
add rsp, 20h
or rax, rax
jz fertig
mov rcx, MSG
call TranslateMessage
mov rcx, MSG
call DispatchMessageA
jmp zyklus
fertig:
mov rcx, [wParam]
call ExitProcess
WndProcTable:
sub rsp, 0x8
cmp edx, 0x01 ; see if it is wm_create message
jne quit
; creating button
mov rcx, 0
mov rdx, class_button
mov r8, button_kopf
mov r9, 0x40000000+0x10000000 ; child +visible
push 0
push qword[hInst]
push 0
push qword[win_id]
push 20
push 50
push 30
push 30
sub rsp, 0x20
call CreateWindowExA
add rsp, 0x20
add rsp, 0x40
mov [but_id], rax
jmp alles
quit:
cmp edx, 0x02
jne weiter
xor rcx, rcx
call PostQuitMessage
weiter:
sub rsp,20h
call DefWindowProcA
add rsp,20h
alles:
add rsp, 0x8
ret
however if you place the creation button code after creation main window code
everything works fine but it fails while processing wm_create mrssage
nasm -f win64 first.nasm -o first,obj
golink first,obj user32.dll kernel32.dll gdi32.dll
the button doesn't appear or may be not created at all
what's wrong?
I want to know what is going wrong with this piece of code
is there anybody who notices any mistake in this code
I don't know where to find mistakes
now it is solved. the thing is that main window handle is not valid in creating button after wm_create the valid handle is in rcx register (it is passed in wndproctable as the first parameter) so the right line is push rcx
now it is solved. the thing is that main window handle is not valid in creating button after wm_create the valid handle is in rcx register (it is passed in wndproctable as the first parameter) so the right line is push rcx