I am writing a simple program to display a name supplied by the user. The result is that I should be able to enter the command and get the expected result.
Command
./hello John
Result
Hello, John.
Yet when the program gets around to displaying the name, it doesn't. I believe it has something to do with calculating the length of the argument. May you guys please take a look at my code and tell me what you think?
; hello.asm
;
; Assemble: nasm -f elf hello.asm
; Link: ld -o hello hello.o
; Run: ./hello <name>
section .data
period: db ".", 10
periodLen: equ $-period
helloMsg: db "Hello, "
helloMsgLen: equ $-helloMsg
usageMsg: db "Usage: hello <name>", 10
usageMsgLen: equ $-usageMsg
section .text
global _start
_start:
pop eax ; Get number of arguments
cmp eax, 2 ; If one argument
jne _help ; Not equal, show help + exit
mov eax, 4 ; System call to write
mov ebx, 1 ; Write to console
mov ecx, helloMsg ; Display "Hello, "
mov edx, helloMsgLen ; Length of hello message
int 80h
mov eax, 4 ; System call to write
mov ebx, 1 ; Write to console
pop ecx ; Get program name
pop ecx ; Get name
mov edx, $ ; Beginning of line
sub edx, ecx ; Get length of name
int 80h
mov eax, 4 ; System call to write
mov ebx, 1 ; Write to console
mov ecx, period ; Display a period
mov edx, periodLen ; Length of period
int 80h
mov eax, 1 ; System call to exit
mov ebx, 0 ; No errors
int 80h
_help:
mov eax, 4 ; System call to write
mov ebx, 1 ; Write to console
mov ecx, usageMsg ; Display usage message
mov edx, usageMsgLen ; Length of usage message
int 80h
mov eax, 1 ; System call to exit
mov ebx, 0 ; No errors
int 80h
Ok, since you never used a debugger, I'll show you how. First, compile with nasm -f elf -g hello.asm. The -g switch helps the debugger, this way you can set breakpoints etc. Now start it typing gdb ./hello -q and type break 34. This tells gdb to stop at line 34. Run the program (type run emi (emi is my name :P)). You should see something like this:
blackbear#blackbear-laptop:~$ gdb ./hello -q
Reading symbols from /home/blackbear/hello...done.
(gdb) break 34
Breakpoint 1 at 0x80480a9: file hello.asm, line 34.
(gdb) run emi
Starting program: /home/blackbear/hello emi
Hello,
Breakpoint 1, _start () at hello.asm:34
34 pop ecx ; Get name
(gdb)
Ok, let's see what ecx is, typing display (char *) $ecx:
(gdb) display (char *) $ecx
1: (char *) $ecx = 0xbffff63e "/home/blackbear/hello"
You can use step to continue by one instruction:
(gdb) step
35 mov edx, $ ; Beginning of line
1: (char *) $ecx = 0xbffff654 "emi"
Ok, here we are. ecx points to my name, so the problem isn't here. Now we don't need to watch ecx anymore, so using undisplay gdb won't show it anymore. But we need to check edx:
(gdb) undisplay
Delete all auto-display expressions? (y or n) y
(gdb) display $edx
2: $edx = 7
(gdb) step
36 sub edx, ecx ; Get length of name
2: $edx = 134512810
(gdb) step
37 int 80h
2: $edx = 1208257110
Mmh, guess you didn't expect this, right? :) The problem seems to be here: mov edx, $. I don't get that $ (never used NASM), could you please explain?
EDIT
Ok got it. You misunderstood what the tutorial said. The $ represents the current location of it, in fact:
36 sub edx, ecx ; Get length of name
11: $edx = 134512810
(gdb) display (void *) $edx
12: (void *) $edx = (void *) 0x80480aa
(gdb) display (void *) $eip
13: (void *) $eip = (void *) 0x80480af
now edx contains the address of the instruction mov edx, $. which is 5 bytes long (opcode (1 byte) + address (4 bytes)), that's why eip - edx = 5.
In order to get the length of the argument your only way is to use something like strlen(), but I can't help you here, NASM isn't my assembler. :)
Related
I am trying to teach myself how to write Assembly in X86 on NASM. I'm attempting to write a program that takes a single integer value and prints it back to standard output before exiting.
My code:
section .data
prompt: db "Enter a number: ", 0
str_length: equ $ - prompt
section .bss
the_number: resw 1
section .text
global _start
_start:
mov eax, 4 ; pass sys_write
mov ebx, 1 ; pass stdout
mov edx, str_length ; pass number of bytes for prompt
mov ecx, prompt ; pass prompt string
int 80h
mov eax, 3 ; sys_read
mov ebx, 0 ; stdin
mov edx, 1 ; number of bytes
mov ecx, [the_number] ; pass input of the_number
int 80h
mov eax, 4
mov ebx, 1
mov edx, 1
mov ecx, [the_number]
int 80h
mov eax, 1 ; exit
mov ebx, 0 ; status 0
int 80h
From there I do the assembling nasm -felf -o input.o input.asm and linking ld -m elf_i386 -o input input.o.
I run a test and input an integer and when I press enter, the program exits and Bash tries to execute the number input as a command. I even echo'd the exit status and been returned with 0.
So this is an odd behavior.
The call to read fails and doesn’t read any input. When your program exits, that input is still waiting to be read on the TTY (which was this program's stdin), at which point bash reads it.
You should check the return status of your system calls. If EAX is a negative number when the system call returns, it is the error code. For example, in this case, EAX contains -14, which is EFAULT (“Bad address”).
The reason read fails is that you are passing an invalid pointer as the buffer address. You need to load the address of the_number, not its value. Use mov ecx, the_number.
Any good NASM/Intel Assembly programmers out there? If so, I have a question for you!
Every tutorial I can find online, shows the usage of "printf" for printing the actual value of ARGC to the screen (fd:/dev/stdout). Is it not possible to simply print it with sys_write() for example:
SEGMENT .data ; nothing here
SEGMENT .text ; sauce
global _start
_start:
pop ECX ; get ARGC value
mov EAX, 4 ; sys_write()
mov EBX, 1 ; /dev/stdout
mov EDX, 1 ; a single byte
int 0x80
mov EAX, 1 ; sys_exit()
mov EBX, 0 ; return 0
int 0x80
SEGMENT .bss ; nothing here
When I run this, I get no output at all. I have tried copying ESP into EBP and tried using byte[EBP+4], (i was told the brackets de-reference the memory address).
I can confirm that the value when compared to a constant, works. For instance,
this code works:
pop ebp ; put the first argument on the stack
mov ebp, esp ; make a copy
cmp byte[ebp+4],0x5 ; does it equal 5?
je _good ; goto _good, &good, good()
jne _bad ; goto _bad, &bad, bad()
When we "pop" the stack, we technically should get the full number of arguments, no? Oh, btw, I compile with:
nasm -f elf test.asm -o test.o
ld -o test test.o
not sure if that is relevant. Let me know if i need to provide more information, or format my code for readability.
At least 2 problems.
You need to pass a pointer to the thing you want to print.
You probably want to convert to text.
Something like this should work:
SEGMENT .text ; sauce
global _start
_start:
mov ecx, esp ; pointer to ARGC on stack
add byte [esp], '0' ; convert to text assuming single digit
mov EAX, 4 ; sys_write()
mov EBX, 1 ; /dev/stdout
mov EDX, 1 ; a single byte
int 0x80
mov EAX, 1 ; sys_exit()
mov EBX, 0 ; return 0
int 0x80
Everyone's comments where very helpful! I am honored that you all pitched in and helped! I have used #Jester's code,
SEGMENT .text ; sauce
global _start
_start:
mov ecx, esp ; pointer to ARGC on stack
add byte [esp], '0' ; convert to text assuming single digit
mov EAX, 4 ; sys_write()
mov EBX, 1 ; /dev/stdout
mov EDX, 1 ; a single byte
int 0x80
mov EAX, 1 ; sys_exit()
mov EBX, 0 ; return 0
int 0x80
Which works perfectly when compiled, linked and loaded. The sys_write() function requires a pointer, such like in the common "Hello World" example, the symbol "msg" is a pointer as seen in the code below.
SECTION .data ; initialized data
msg: db "Hello World!",0xa
SECTION .text ; workflow
global _start
_start:
mov EAX, 4
mov EBX, 1
mov ECX, msg ; a pointer!
So first, we move the stack pointer into the counter register, ECX, with the code,
mov ecx, esp ; ecx now contains a pointer!
and then convert it to a string by adding a '0' char to the value pointed to by ESP (which is ARGC), by de-referencing it with square brackets, as [ESP] like so,
add byte[esp], '0' ; update the value stored at "esp"
Again, thank you all for the great help! <3
Recently, I wrote a bit of assembly code that asks for the password and if the user enters the correct password as stored internally, it prints out "Correct!". Else, it prints out "Incorrect!".
Here is the code:
section .text
global _start
_start:
mov edx, len_whatis
mov ecx, whatis
mov ebx, 1
mov eax, 4
int 80h ; outputs: "What is the password?"
mov edx, 5 ; expect 5 bytes of input(so 4 numbers)
mov ecx, pass
mov ebx, 0
mov eax, 3
int 80h ; accepts intput and stores in pass
mov eax, [pass] ; move the pass variable into eax
sub eax, '0' ; change the ascii number in eax to a numerical number
mov ebx, [thepass] ; move the thepass variable into ebx
sub ebx, '0' ; change the ascii number in ebx to a numerical number
cmp eax, ebx ; compare the 2 numbers
je correct ; if they are equal, jump to correct
jmp incorrect ; if not, jump to incorrect
correct:
mov edx, len_corr
mov ecx, corr
mov ebx, 1
mov eax, 4
int 80h ; outputs: "Correct!"
mov ebx, 0
mov eax, 1
int 80h ; exits with status 0
incorrect:
mov edx, len_incor
mov ecx, incor
mov ebx, 1
mov eax, 4
int 80h ; outputs: "Incorrect!"
mov eax, 1
int 80h ; exits with status: 1
section .data
whatis db "What is the password?", 0xA
len_whatis equ $ - whatis
thepass db "12345"
corr db "Correct!", 0xA
len_corr equ $ - corr
incor db "Incorrect!", 0xA
len_incor equ $ - incor
section .bss
pass resb 5
Assemble:nasm -f elf password.s
Link:ld -m elf_i386 -s -o password password.o
(If you did try to assemble link and run this, you may notice that it checks the password incorrectly - ignore this. It is "off topic")
Then, I ran a test:
I ran the code with ./password
When I was prompted for the password, I typed in 123456, one more byte than the code expects
After I hit enter and the code exits, the terminal immediately tries to run a command 6
What is causing this behavior? Is it something to do with the assembler, or how my computer is reading the code?
EDIT:
And, when I run the code with 12345, the terminal prompts for a command twice when the program closes, as if someone just hit the enter button without entering a command.
You're only reading five bytes from standard input, so when you type 123456↵, your application ends up reading 12345 and leaving 6↵ in the buffer. That gets passed on to the shell.
If you want to read the whole line, use a larger buffer.
I'm trying to write 4 bytes to the screen using:
nasm -f elf -g ****.asm
Nothing is happening.
Here is the relevant code segment:
mov eax, 4 ; ow print error mesg
mov ebx, 1
mov ecx, DWORD [para]
mov edx, 4
int 080h
This is my debug run of that portion of my code. Nothing is being printed, I'm showing you the contents of $ecx via gdb.
253 mov eax, 4 ; ow print error mesg
(gdb)
254 mov ebx, 1
(gdb)
255 mov ecx, DWORD [para]
(gdb)
256 mov edx, 4
(gdb)
257 int 080h
(gdb) p /t $ecx
$1 = 1100001010101001000010110000010
(gdb) step
No idea what I'm doing wrong. From previous posts here and on other websites, I can't see a discrepancy with the accepted method.
What is the normal value of para ?
I have an old code and I don't use DWORD
just
mov ecx, [para]
ecx wants the address of the text to print. Unless para is a "pointer" (holds the address of the text), just mov ecx, para is probably correct. Strictly speaking, ebx might want to be 2 (stderr), but I doubt if that's your problem. Show us para!
I started learning how to write programs using the NASM assembly programming language. I wrote this simple program that prompts the user to enter two numbers and then adds the two operands together. I got it to compile with no errors or warnings, but when it prompts the user for the two numbers and it begins to add the two numbers it prints out segmentation fault and program ends. I know a segmentation fault is the equivalent to an access reading / writing violation exception in the Win32 world. But, because I don't know how to debug NASM code; I can't figure out what is wrong. I suspect it has to do with an invalid pointer; but I don't know. Here is the code below:
section .data
msg1: db 'Please Enter A Number: ', 0
length1: equ $ - msg1
msg2: db 'Please Enter A Second Number: ', 0
length2: equ $ - msg2
section .bss
operand1: resb 255
operand2: resb 255
answer: resb 255
section .text
global _start
_start:
; Print first message
mov eax, 4
mov ebx, 1
mov ecx, msg1
mov edx, length1
int 80h
; Now read value
mov eax, 3
mov ebx, 1
mov ecx, operand1
mov edx, 255
int 80h
; Print second message
mov eax, 4
mov ebx, 1
mov ecx, msg2
mov edx, length2
int 80h
; Now read second value
mov eax, 3
mov ebx, 1
mov ecx, operand2
mov edx, 255
int 80h
; Now add operand1 and operand2 and print answer
mov eax, 4
mov ebx, 1
xor ecx, ecx ; Make the ecx register 0
mov ecx, operand1
add ecx, operand2
mov edx, 510
int 80h
(Aside: you should be reading from STDIN_FILENO = 0, not STDOUT_FILENO = 1. Also, you're writing a NUL character and you shouldn't.)
The problem is that operand1 and operand2 are addresses to memory locations holding characters you've read. When you add them, you get a pointer to invalid memory.
You'll have to convert them to integers, add them, and convert back to a string, before you can write it out.
Value in ecx is an address of string that is to be printed when you call int 80h. Last part does not make sense
mov eax, 4
mov ebx, 1
xor ecx, ecx ; Make the ecx register 0
mov ecx, operand1
add ecx, operand2 ; **<<< invalid memory address now in ECX !!!**
mov edx, 510
int 80h
because you are adding address of string operand1 and address of string operand2 and trying to print whatever is located ant resulting address which is most likely points to nowhere.
To debug your program with gdb you can do:
nasm -f elf64 -g -l q1.lst q1.asm
gcc -o q1 q1.o
I replaced the "_start" with "main" so that gcc won't complain, and you can skip the 64 in "-f elf64" if you are building on 32 bit platform.
gdb q1
Here is an example f gdb session:
(gdb) br main
Breakpoint 1 at 0x4004d0: file q1.asm, line 20.
(gdb) r
Starting program: /home/anonymous/Projects/asm/q1
Breakpoint 1, main () at q1.asm:20
20 mov eax, 4
(gdb) n
21 mov ebx, 1
(gdb) n
22 mov ecx, msg1
(gdb) n
23 mov edx, length1
(gdb) p msg1
$1 = 1634036816
(gdb)