MessageBox program in x86 assembly [duplicate] - winapi

This question already has answers here:
What is the meaning and usage of __stdcall?
(9 answers)
How does this asm for a stdcall function clean args from the stack?
(1 answer)
Closed 1 year ago.
Having a simple MessageBox program like that:
NULL EQU 0 ; Constants
MB_DEFBUTTON1 EQU 0
MB_DEFBUTTON2 EQU 100h
IDNO EQU 7
MB_YESNO EQU 4
extern _MessageBoxA#16 ; Import external symbols
extern _ExitProcess#4 ; Windows API functions, decorated
global Start ; Export symbols. The entry point
section .data ; Initialized data segment
MessageBoxText db "Do you want to exit?", 0
MessageBoxCaption db "MessageBox 32", 0
section .text ; Code segment
Start:
push MB_YESNO | MB_DEFBUTTON2 ; 4th parameter. 2 constants ORed together
push MessageBoxCaption ; 3rd parameter
push MessageBoxText ; 2nd parameter
push NULL ; 1st parameter
call _MessageBoxA#16
cmp EAX, IDNO ; Check the return value for "No"
je Start
push NULL
call _ExitProcess#4
My question is:
Shouldn't we add appropriate value to the esp reg after calling the MessageBoxA to restore the stack to it's previous state? If so when calling push MessageBoxCaption how much have to be added to the esp register (4?).

Related

windows assembly x86 weird Function Issue [duplicate]

This question already has answers here:
What if there is no return statement in a CALLed block of code in assembly programs
(2 answers)
Why is no value returned if a function does not explicity use 'ret'
(2 answers)
Closed 10 days ago.
I am creating a simple function that prints a character to the screen
STD_OUTPUT_HANDLE equ -11
NULL equ 0
global Start
extern ExitProcess, GetStdHandle, WriteConsoleA
section .bss
msg resb 1
section .text
Start:
push 'b'
call Print
Print:
; Function Start
push ebp
mov ebp,esp
; accepting the argument
mov ebx , [esp + 8]
mov [msg],ebx
; eax = handle
push STD_OUTPUT_HANDLE
call GetStdHandle
; WriteConsoleA System call
push NULL
push 1
push msg
push eax
call WriteConsoleA
; Funcion end
mov esp, ebp
pop ebp
ret
When I remove the ret instruction the output is as expected is b but when I add it back the output changes to b., what's Happening ????
I am using the Nasm Assembler and the golink link

generating random numbers and writing them to a file in assembler (WriteFile, ReadFile)

in my program, I need to generate 20 random numbers and write them to a file (file.dat) and then also read them from there. so far I do not quite understand how to write many numbers and am trying to write only one. and so, I successfully generate a random number and even write it to a file, but when reading a number from a file, for some reason it is not written to a variable (bufforNumbersRead).
.586P
.MODEL flat, STDCALL
;--- stale z pliku .\include\windows.inc ---
STD_INPUT_HANDLE equ -10
STD_OUTPUT_HANDLE equ -11
GENERIC_READ equ 80000000h
GENERIC_WRITE equ 40000000h
CREATE_NEW equ 1
CREATE_ALWAYS equ 2
OPEN_EXISTING equ 3
OPEN_ALWAYS equ 4
TRUNCATE_EXISTING equ 5
FILE_FLAG_WRITE_THROUGH equ 80000000h
FILE_FLAG_OVERLAPPED equ 40000000h
FILE_FLAG_NO_BUFFERING equ 20000000h
FILE_FLAG_RANDOM_ACCESS equ 10000000h
FILE_FLAG_SEQUENTIAL_SCAN equ 8000000h
FILE_FLAG_DELETE_ON_CLOSE equ 4000000h
FILE_FLAG_BACKUP_SEMANTICS equ 2000000h
FILE_FLAG_POSIX_SEMANTICS equ 1000000h
FILE_ATTRIBUTE_READONLY equ 1h
FILE_ATTRIBUTE_HIDDEN equ 2h
FILE_ATTRIBUTE_SYSTEM equ 4h
FILE_ATTRIBUTE_DIRECTORY equ 10h
FILE_ATTRIBUTE_ARCHIVE equ 20h
FILE_ATTRIBUTE_NORMAL equ 80h
FILE_ATTRIBUTE_TEMPORARY equ 100h
FILE_ATTRIBUTE_COMPRESSED equ 800h
FORMAT_MESSAGE_ALLOCATE_BUFFER equ 100h
FORMAT_MESSAGE_IGNORE_INSERTS equ 200h
FORMAT_MESSAGE_FROM_STRING equ 400h
FORMAT_MESSAGE_FROM_HMODULE equ 800h
FORMAT_MESSAGE_FROM_SYSTEM equ 1000h
FORMAT_MESSAGE_ARGUMENT_ARRAY equ 2000h
FORMAT_MESSAGE_MAX_WIDTH_MASK equ 0FFh
FILE_BEGIN equ 0h ;MoveMethod dla SetFilePointe
FILE_CURRENT equ 1h ;MoveMethod dla SetFilePointe
FILE_END equ 2h ;MoveMethod dla SetFilePointe
;--- funkcje API Win32 z pliku .\include\user32.inc ---
CharToOemA PROTO :DWORD,:DWORD
;--- z pliku .\include\kernel32.inc ---
GetStdHandle PROTO :DWORD
ReadConsoleA PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD
WriteConsoleA PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD
ExitProcess PROTO :DWORD
wsprintfA PROTO C :VARARG ;; int wsprintf(LPTSTR lpOut,// pointer to buffer for output
;; LPCTSTR lpFmt,// pointer to format-control string
;; ... // optional arguments );
lstrlenA PROTO :DWORD
GetCurrentDirectoryA PROTO :DWORD,:DWORD
;;nBufferLength, lpBuffer; zwraca length
CreateDirectoryA PROTO :DWORD,:DWORD
;;lpPathName, lpSecurityAttributes; zwraca 0 jeœli b³ad
lstrcatA PROTO :DWORD,:DWORD
;; lpString1, lpString2; zwraca lpString1
CreateFileA PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
;; LPCTSTR lpszName, DWORD fdwAccess,
;; DWORD fdwShareMode, LPSECURITY_ATTRIBUTES lpsa, DWORD fdwCreate,
;; DWORD fdwAttrsAndFlags, HANDLE hTemplateFile
lstrcpyA PROTO :DWORD,:DWORD
;;LPTSTR lpString1 // address of buffer, LPCTSTR lpString2 // address of string to copy
CloseHandle PROTO :DWORD
;; BOOL CloseHandle(HANDLE hObject)
WriteFile PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD
;; BOOL WriteFile(
;; HANDLE hFile, // handle to file to write to
;; LPCVOID lpBuffer, // pointer to data to write to file
;; DWORD nNumberOfBytesToWrite, // number of bytes to write
;; LPDWORD lpNumberOfBytesWritten, // pointer to number of bytes written
;; LPOVERLAPPED lpOverlapped // pointer to structure needed for overlapped I/O
;;);
ReadFile PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD
;;BOOL ReadFile(
;;HANDLE hFile, // handle of file to read
;;LPVOID lpBuffer, // address of buffer that receives data
;;DWORD nNumberOfBytesToRead, // number of bytes to read
;;LPDWORD lpNumberOfBytesRead, // address of number of bytes read
;;LPOVERLAPPED lpOverlapped // address of structure for data
;;);
CopyFileA PROTO :DWORD,:DWORD,:DWORD
;; BOOL CopyFile(
;;LPCTSTR lpExistingFileName, // pointer to name of an existing file
;;LPCTSTR lpNewFileName, // pointer to filename to copy to
;;BOOL bFailIfExists // flag for operation if file exists
;;);
GetLastError PROTO
GetTickCount PROTO
;--- z pliku ..\include\masm32.inc ---
nseed PROTO :DWORD
nrandom PROTO :DWORD
dwtoa PROTO dwValue:DWORD, lpBuffer:DWORD ;dwtoa convert a DWORD value to an ascii string.
;dwtoa proc dwValue:DWORD, lpBuffer:DWORD
atodw PROTO lpBuffer:DWORD ;atodw converts a decimal string to dword.
;atodw proc String:PTR BYTE
StripLF PROTO :DWORD ;StripLF is designed to remove the CRLF (ascii 13,10) by writing an ascii zero in the place of the first occurrence of ascii 13.
;StripLF proc strng:DWORD
StdIn PROTO :DWORD,:DWORD ;StdIn receives text input from the console and places it in the buffer required as a parameter. The function terminates when Enter is pressed.
;StdIn proc lpszBuffer:DWORD,bLen:DWORD
StdOut PROTO :DWORD ;StdOut will display a zero terminated string at the current position in the console.
;StdOut proc lpszText:DWORD
;--- funkcje
;------------s
;includelib .\lib\user32.lib
;includelib .\lib\kernel32.lib
;includelib .\lib\masm32.lib
;-------------
_DATA SEGMENT
folderName BYTE "/newFolder", 0
fileName BYTE "/file.dat", 0
buffor BYTE 250 dup(0)
randomNumber DD ?
writtenBytes DD ?
writtenBytes2 DD ?
handleFile DD ?
bufforNumbers DD 250 dup(0)
bufforNumbersRead DD 250 dup(0)
_DATA ENDS
;------------
_TEXT SEGMENT
main proc
; -- creating folder "newFolder" --
push OFFSET buffor
push 255
call GetCurrentDirectoryA
push OFFSET folderName
push OFFSET buffor
call lstrcatA
push 0
push OFFSET buffor
call CreateDirectoryA
; -- end --
; -- generating random numbers --
call GetTickCount
push eax
call nseed
mov ecx, 1
generateRandomNumbers:
push ecx
;--- generating random numbers from 0 to 99 ---
push 9
call nrandom
mov bufforNumbers, eax
pop ecx
loop generateRandomNumbers
; -- end --
; -- creating file "file.dat" --
push OFFSET buffor
push 255
call GetCurrentDirectoryA
push OFFSET fileName
push OFFSET buffor
call lstrcatA
push 0
push 0
push CREATE_ALWAYS
push 0
push 0
push GENERIC_WRITE
push OFFSET buffor
call CreateFileA
mov handleFile, eax
; -- end --
; -- write in file numbers --
push 0
push writtenBytes
push 1
push OFFSET bufforNumbers
push eax
call WriteFile
; -- end --
;-- read file --
push 0
push writtenBytes2
push 1
push OFFSET bufforNumbersRead
push handleFile
call ReadFile
; -- end --
push 0
call ExitProcess
main endp
_TEXT ENDS
END
You can't call ReadFile right after WriteFile with the same handle and expect to get back what you just wrote. You must seek back with SetFilePointer before reading.
You should also check the return value after calling a function...

Hello world in NASM with LINK.EXE and WinAPI

I'm trying to get a simple Hello world program in NASM to run.
I want to print to the console without using C-Libraries, interfacing directly with WinAPI.
I am using the Visual Studio provided LINK.EXE for linking.
Here's my code so far:
section .data
message: db 'Hello world!',10 ; 'Hello world!' plus a linefeed character
messageLen: db $-message ; Length of the 'Hello world!' string
global _start
extern GetStdHandle
extern WriteConsoleW
extern ExitProcess
section .text
_start:
; DWORD bytes;
mov rbp, rsp
sub rsp, byte 8
; hStdOut = GetStdHandle(STD_OUTPUT_HANDLE)
mov ecx, -11
call GetStdHandle
; WriteFile(hstdOut, message, length(message), &bytes, 0);
mov rcx, rax
mov rdx, message
mov r8, messageLen
lea r9, [rsp-4]
push 0
call WriteConsoleW
; ExitProcess(0)
mov rcx, 0
call ExitProcess
ret
Which I assemble and link like this:
nasm -f win64 .\ASM.ASM
link /entry:_start /nodefaultlib /subsystem:console .\ASM.obj "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\um\x64\kernel32.lib" "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\um\x64\user32.lib"
However when I run the resulting .exe file, I get nothing.
Some things I tried so far are
Using the decorated names (like _GetStdHandle#4), which resulted in the linker complaining about unresolved references
Not trying to print anything and calling Sleep, which resulted in the process sleeping indefinitely
Exiting with a different return code, which once again did nothing
What am I doing wrong?
EDIT: Fixed calling convention
There are three problems with your revised code. The first is:
message: db 'Hello world!',10 ; 'Hello world!' plus a linefeed character
messageLen: db $-message ; Length of the 'Hello world!' string
You defined messageLen to be a byte containing the length of the message and storing that value at the address of messageLen. You then do this:
mov r8, messageLen
That would move the address of label messageLen to r8. What you really should have done is define messageLen as an assembly time constant like this:
messageLen equ $-message ; Length of the 'Hello world!' string
The second problem is that you define the the string as a sequence of single byte characters:
message: db 'Hello world!',10 ; 'Hello world!' plus a linefeed character
There is nothing wrong with this, but to print them out you need to use the Ansi version of the function WriteConsole which is WriteConsoleA. Using WriteConsoleW printed the string as Unicode (UTF-16 on Windows 2000 and later, UTS-2 on NT4 and earlier versions of Windows).
The third problem is with regards to a mandatory 32 bytes of shadow space before the stack based parameter(s) are placed on the stack before making a function call. You also need to make sure the stack (RSP) is a 16-byte aligned value at the point of making a function call. These requirement can be found in the Microsoft 64-bit calling convention.
Code that would take this into account would look like this:
section .data
message: db 'Hello world!',10 ; 'Hello world!' plus a linefeed character
messageLen equ $-message ; Length of the 'Hello world!' string
global _start
extern GetStdHandle
extern WriteConsoleA
extern ExitProcess
section .text
_start:
; At _start the stack is 8 bytes misaligned because there is a return
; address to the MSVCRT runtime library on the stack.
; 8 bytes of temporary storage for `bytes`.
; allocate 32 bytes of stack for shadow space.
; 8 bytes for the 5th parameter of WriteConsole.
; An additional 8 bytes for padding to make RSP 16 byte aligned.
sub rsp, 8+8+8+32
; At this point RSP is aligned on a 16 byte boundary and all necessary
; space has been allocated.
; hStdOut = GetStdHandle(STD_OUTPUT_HANDLE)
mov ecx, -11
call GetStdHandle
; WriteFile(hstdOut, message, length(message), &bytes, 0);
mov rcx, rax
mov rdx, message
mov r8, messageLen
lea r9, [rsp-16] ; Address for `bytes`
; RSP-17 through RSP-48 are the 32 bytes of shadow space
mov qword [rsp-56], 0 ; First stack parameter of WriteConsoleA function
call WriteConsoleA
; ExitProcess(0)
; mov rcx, 0
; call ExitProcess
; alternatively you can exit by setting RAX to 0
; and doing a ret
add rsp, 8+8+32+8 ; Restore the stack pointer.
xor eax, eax ; RAX = return value = 0
ret

Assembly - Readings strings with scanf in loop only reads one string

I need to read a bunch of strings from command line in assembly, however only one call to scanf seems to take place. I'm pretty sure the problem is related to the scanf format. If I replace what I've currently got with a simple "%s" it works fine, thing is I have to read whole lines including spaces, so "%s" won't do. "%[^c]s" should keep reading characters until it encounters c, but there's 2 problems with this:
I actually have to input the characters \ and n for the scanning to stop.
As I said, it only reads one string. After that, all the prompts for the remaining strings get instantly printed at the program ends.
I've seen that people usually suggest using fread, however I'm not really sure how to access stdin in assembly.
Here's my code:
bits 32
global start
extern exit, scanf, printf
import exit msvcrt.dll
import scanf msvcrt.dll
import printf msvcrt.dll
segment data use32 class=data
msg DB "Enter a value for n", 13, 10, 0
s_msg DB "Enter a string", 13, 10, 0
n_format DB "%d", 0
s_format DB "%[^\n]s", 0
segment bss use32 class=bss
n DD 0
s RESB 1000
segment code use32 class=code
start:
push dword msg
call [printf]
add ESP, 4 * 1
push dword n
push dword n_format
call [scanf]
add ESP, 4 * 2
mov ECX, [n]
read_strings:
pushad
push dword s_msg
call [printf]
add ESP, 4 * 1
push dword s
push dword s_format
call [scanf]
add ESP, 4 * 2
popad
loop read_strings
push dword 0
call [exit]

Assembly : Dealing with user input in windows nasm

I'm a newbie to asm and trying to make a simple hello world which awaits for the user to press a key to end. For now the hello world is all good, but the .exe console program i got from this just close instantly while i want it to stay on screen untill the user press a key.
Now the problem i have is that for some reason, the program keep looping, searching for user input, but when i force close the program (^C) i can see all the keys i pressed are written on the next console line, like if it was using the wrong buffer (?)
I've been searching a fix all over the internet for a few days and finally I'm asking for help cuz this is driving me crazy ^^
Everything i found is mostly based on int system or under linux, while i have to deal with the windows api...
Thank you very much, any help or hint is welcome!
Code :
STD_OUTPUT_HANDLE equ -11
STD_INPUT_HANDLE equ -10
NULL equ 0
global start
extern ExitProcess, GetStdHandle, WriteConsoleA, ReadConsoleInputA
section .data
msg db "Hello World!", 13, 10, 0
msg.len equ $ - msg
consoleInHandle dd 1
section .bss
buffer resd 2
buffer2 resd 2
section .text
start:
push STD_OUTPUT_HANDLE
call GetStdHandle
push NULL
push buffer
push msg.len
push msg
push eax
call WriteConsoleA
read:
push STD_INPUT_HANDLE
call GetStdHandle
mov [consoleInHandle],eax
push consoleInHandle
push dword[buffer2]
push 1
push NULL
call ReadConsoleInputA
cmp eax,1
jge exit
jmp read
exit:
push NULL
call ExitProcess
Moar info about windows functions can be found here:
ReadConsoleInput
WriteConsole
push consoleInHandle pushes the address, not the handle. You want push dword [consoleInHandle]. Conversely, for the buffer you want to pass the address, so you need push buffer2 there. Also, this buffer should be the size of an INPUT_RECORD structure, which I believe is 32 bytes.
Update: As Frank commented, the argument order was also wrong.
This code works for me (note I had to add the #xx stdcall decorations due to how my environment is set up - apparently you don't need those):
STD_OUTPUT_HANDLE equ -11
STD_INPUT_HANDLE equ -10
NULL equ 0
global start
extern ExitProcess#4, GetStdHandle#4, WriteConsoleA#20, ReadConsoleInputA#16
section .data
msg db "Hello World!", 13, 10, 0
msg.len equ $ - msg
consoleInHandle dd 1
section .bss
buffer resd 2
buffer2 resb 32
section .text
start:
push STD_OUTPUT_HANDLE
call GetStdHandle#4
push NULL
push buffer
push msg.len
push msg
push eax
call WriteConsoleA#20
read:
push STD_INPUT_HANDLE
call GetStdHandle#4
mov [consoleInHandle],eax
push NULL
push 1
push buffer2
push dword [consoleInHandle]
call ReadConsoleInputA#16
exit:
push NULL
call ExitProcess#4

Resources