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
Related
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
I have a file that contains one word on every line (the number of words and their length is unknown) and I need to rewrite those words into another file, starting with the last word and getting to the first one. When I print the last word in the file, I try to set the cursor (seek_cur) to seek for the next word, but it sets it in an unknown place. Tried printing the current cursor to see what happens and it gives characters like "#A".
The second jmp get_out stops the program after writing the last word, if it's deleted it gets to the jmp search tag and then it's printing the same last word infinitely.
.386
.model flat, stdcall
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;includem biblioteci, si declaram ce functii vrem sa importam
includelib msvcrt.lib
extern exit: proc
extern fopen: proc
extern fclose: proc
extern fscanf: proc
extern fprintf: proc
extern fseek: proc
extern fgets:proc
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;declaram simbolul start ca public - de acolo incepe executia
public start
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;sectiunile programului, date, respectiv cod
.data
file_read db "r",0
file_write db "w",0
file_name_read db "read.txt",0
file_name_write db "write.txt",0
seek_end dd 2
seek_cur dd 1
seek_set dd 0
caracter_format db "%c",0
string_format db "%s",0
decimal_format db "%d",0
string db 0
caracter db 0
back dd 0
first_word db 0
.code
start:
push offset file_read
push offset file_name_read
call fopen
add esp,8
mov esi,eax
push offset file_write
push offset file_name_write
call fopen
add esp,8
mov edi,eax
;in first_word se pune cuvantul de pe prima linie ca sa fie posibila comparatia mai incolo si sa se iese din bucla cand se ajunge la primul cuvant
repeat_search_first_word:
push offset caracter
push offset caracter_format
push esi
call fscanf
add esp,12
inc back
cmp caracter,0Ah
je out_of_search_first_word
jmp repeat_search_first_word
out_of_search_first_word:
inc back
neg back
push seek_cur
push back
push esi
call fseek
add esp,12
neg back
push esi
push back
push offset first_word
call fgets
add esp,12
mov back,0
;incepe cautarea cuvintelor de la capat
push seek_end
push -1
push esi
call fseek
add esp,12
search:
push offset caracter
push offset caracter_format
push esi
call fscanf
add esp,12
inc back
cmp caracter,0Ah
jne is_caracter
push esi
push back
push offset string
call fgets
add esp,12
push offset string
push offset string_format
push edi
call fprintf
add esp,12
;testam daca cuvantul coincide cu primul (first_word)
mov ebx,0
mov bl,string
cmp bl,first_word
je get_out
add back,2
neg back
;!!!!!!!!!!!!!!
;problema pentru rularea infinita ii aici fiindca seek_cur muta cursorul intr-o zona necunoscuta din fisier
push seek_cur
push back
push esi
call fseek
add esp,12
mov back,0
jmp get_out ;linia 152 lasata fara comentariu permite afisare ultimului cuvant fara sa intre in rularea infinita a buclei
jmp search
is_caracter:
push seek_cur
push -2
push esi
call fseek
add esp,12
jmp search
get_out:
push edi
call fclose
add esp,4
push esi
call fclose
add esp,4
push 0
call exit
end start
read.txt contains:
abc
defg
hijklm
write.txt should be:
hijklm
defg
abc
string db 0 reserves space for 1 byte (initialized to zero).
You then call fgets(fp, string, back), which will overwrite later things in your data section if it reads more than 1 byte (including the terminating 0).
Use a bigger buffer in the BSS, like a couple MB or something.
Use a debugger to trace function calls / system calls. On Linux you could use ltrace to trace the libc stdio functions, or strace to trace the system calls they use. On Windows IDK. You can always step through and look at the args you've pushed on the stack before each call to make sure they're sane, but it's often easier to see a log-file style of listing when looking for one with the wrong args.
[SOLVED]
I'm trying to do my own assembly code to do what similar C code will do:
main()
{
scanf("%d",&integer_var); // here must be the address of the integer_var
printf("Your Value is:%d",integer_var);
}
Well this is in C, so I'm doing with NASM under linux with extern functions. scanf and printf and compile first with nasm and then with gcc.
Here's my code (is not right :D)
SECTION .text
argstr: db "%d",10,0
str: db "Your value is:%d",10,0
extern printf
extern scanf
SECTION .data
global main
main:
push ebp
mov esp,ebp
sub esp, 0x10 ;ok integer right?
mov [ebp-0x4],0x0 ;just put 0 number on our integer variable
mov eax,(ebp-0x4) ;here i don't know how to push the address of ebp-0x4
push ecx ;first push is last argument so here's our address to scanf
push argstr ;just the string format
call scanf ;call that to input something
;I have no idea how to do this
;but if i don't do this i get an error
;because the scanf won't clear the arguments on stack
;and what scanf can't return
pop edx ;maybe help here? but it works fine
pop edx
push [-0x4(ebp)] ;i want the value of our var :D
push str
call printf
pop edx ;clear the stack to avoid "segment fault" or something similar
pop edx
mov esp,ebp
pop ebp
ret ;the end :(
Compiler error:
a.asm:18: error: invalid operand type
a.asm:28: error: parser: expecting ]
Another thing: Do I need to align the stack on this case, by the way?
thanks guys ! :)
EDIT solved whole program!
well at least, I can print the variable with printf. scanf i will do later and then I will share here the last result:
SECTION .text
str: db "Value is:%d",10,0
extern printf
SECTION .data
global main
main:
push ebp ;the main function starts here.
mov ebp,esp
;
sub esp,4 ;we need 4bytes of space for the integer
and esp,0xfffffff0 ;align the stack
mov [esp-4], dword 0xff ;move the value 0xff to our var
mov eax,[esp-4] ;move our variable value to the eax
push eax ;second argument of printf
push str ;first argument of printf
call printf ;printf
;
add esp,16 ;this add to the stack pointer what we pushed basicly
mov ebp,esp ;if we don't do add 16 to esp it shows us
pop ebp ;a segment fault cuz ret doesnt pop saved ebp
ret ;of who whatever called this program :)
To load the address EBP-4 into EAX, use lea eax, [ebp-4]. (this is NOT the same as pushing the address.)
In order to push the value at memory location EBP-4, push dword [ebp-4] should work.
Then you need to specify operand size for one of your movs, too: mov [ebp-4], dword 0x0.
These will fix your current assembler errors, and make your program compile, but there are a few other errors in there that will probably prevent it from running.
Here's a working attempt that is close to yours:
;note the sections, the string literals are better in .rodata
;all code goes in .text
SECTION .rodata
;no newline after scanf string
argstr: db "%d",0
str: db "Your value is: %d",10,0
SECTION .text
extern printf
extern scanf
global main
main:
push ebp
mov ebp,esp ;move esp to ebp, NOT other way round!
sub esp, 4 ;4 bytes are enough for the local variable
;there are NO alignment requirements for this program
lea eax,[ebp-4]
push eax
push dword argstr
call scanf
add esp, 8 ;since we don't actually need the popped values
;we can increment esp instead of two pop edx
push dword [ebp-4]
push dword str
call printf
add esp, 8
mov esp,ebp
pop ebp
ret
In response to my question about Windows API's, I have successfully gotten it to work. My question is in regards to this code:
push STD_OUTPUT_HANDLE
call GetStdHandle
push NULL
push offset other
push mlen
push offset msg
push eax
call WriteConsole
push 0
call ExitProcess
This code is supposed to print the value of msg. Why does one need to do:
a)
push STD_OUTPUT_HANDLE
call GetStdHandle
push NULL
And:
b)
push offset other
push mlen
push offset msg
push eax
I am just wondering what the need is for getting a StdHandle and pushing offsets.
Thanks in advance,
Progrmr
Look at the definition of WriteConsole. The NULL is the last argument of the function, the lpReserved argument. Arguments are pushed in right-to-left order. The first function argument is the console handle, the one you got from GetStdHandle and you pass by pushing eax.
So properly commenting the assembly code:
push STD_OUTPUT_HANDLE ; GetStdHandle nStdHandle argument
call GetStdHandle ; eax = Console handle
push NULL ; lpReserved = null
push offset other ; lpNumberOfCharsWritten = pointer to "other"
push mlen ; nNumberOfCharsToWrite = length of "msg"
push offset msg ; lpBuffer = pointer to "msg"
push eax ; hConsoleOutput = console handle from GetStdHandle
call WriteConsole ; Write string
push 0 ; exit code = 0
call ExitProcess ; terminate program
I am trying just to print out the value of argc using the CommandLineToArgvW Window's API function in NASM. The following is what I have:
extern _ExitProcess#4
extern _GetCommandLineA#0
extern _CommandLineToArgvW#8
extern printf
global _start
section .code
Format:
db "%d",10,0
FormatS:
db "%s",10,0
_start:
push ebp
mov ebp, esp
sub esp, 4 ; Create empty space for ArgC
call _GetCommandLineA#0
push eax; Push value beneath ArgC
mov ebx, ebp ; Set ebx to ebp
sub ebx, 4
push dword ebx ; pushes ArgC address onto stack
push dword [ebp - 8] ; pushes pointer to Command Line String
call _CommandLineToArgvW#8
push dword [ebp - 4]
push Format
call printf
push dword 0
call _ExitProcess#4
No matter what I do, the value for argc is 1. What am I doing wrong?
I assemble and link with these commands:
nasm -fwin32 FunctionTests.asm
golink FunctionTests.obj kernel32.dll msvcrt.dll shell32.dll /console /entry _start
FunctionTests.exe hi asdf asdf asdf asdf
As you can see, from the last line, argc should be 6.
Change _GetCommandLineA to _GetCommandLine*W* CommandLineToArgv expects a pointer to a UNICODE string.
MASM but just about the same:
start:
push ebp
mov ebp, esp
sub esp, 4
call GetCommandLineW
lea ecx, dword ptr[ebp - 4] ; need the address of local
push ecx ; address of local
push eax ; pointer to unicode string
call CommandLineToArgvW
push dword ptr [ebp - 4] ; value of local
push offset Format
call crt_printf
add esp, 8
; this is all? Then we don't have to restore stack.
push 0
call ExitProcess
end start
and this is the output:
D:\Projects>ConTest.exe boo boo I see you
6
D:\Projects>
Set a breakpoint just before you call CommandLineToArgvW and inspect the parameters you're about to pass. Observe that the first parameter you are passing is not a pointer to a string. It is a pointer to a pointer to a string.
You need to use GetCommandLineW rather than GetCommandLineA.
You are using CommandLineToArgvW with an ANSI string, use GetCommandLineW to get the command line in unicode.
You are also not dereferencing the pointer to the command line:
push ebx ; pushes ArgC address onto stack