print out skewed text in x86, but can't see why the 'monitored command dumped core' timeout occurs - string

I am trying to write skewed Text on Terminal via assembly to get my skills going.
Example [] = spaces :
H
[]E
[][]L
[][][] L
[][][][] O
But I seem to run into a timeout, because I am trying to move a character between registers / get the character via memory address - like getting a char at an index. But it won't really work. I tried working with allocating a byte to increment in the .bss section before, but that lead to a different error which I couldn't work around (relocation truncated to fit: R_X86_64_8 against `.bss')
Maybe there is a better approach to handle this, and if you could help me get on the right path I would be so so happy. To get more context, here is the 'problematic' snippet of my latest code.
inc rcx ; increase pointer of rcx
space_loop:
push rdi ;save destination register
cmp byte [rcx], 0 ;end of string found
je eos ; jump to exit subroutine
mov rax, 4; SYS Write
mov rbx, 0 ; STDOUT
cmp rsp,[rbp] ; compare spaceIndex to String Index
je print_char
mov rdi, space ;print space
mov rdx,1 ; length = 1
syscall
inc byte [rsp] ; increase spaceIndex
jnz space_loop
print_char:
;print out char
mov rax, 4
mov rbx, 0
mov rcx,[rcx] ; <- this line breaks it dont know how to access index here
mov rdx, 1
syscall
call new_Line ; make line break
inc rcx ; increase rcx counter
inc byte [rbp] ; increase index counter
mov byte [rsp], 0 ;reset space counter
jmp space_loop; next Line

Related

Why is this register value in x86 assembly from user input different than expected?

Whenever the user inputs s, the expected value in the rax register that the buffer is moved to would be 73, but instead it is a73. Why is this? I need these two values to be equal in order to perform the jumps I need for the user input menu.
On any user input, the information in the register is always preceded by an a, while the register that I use to check for the value is not. This makes it impossible to compare them for a jump.
Any suggestions?
section .data
prompt: db 'Enter a command: '
section .bss
buffer: resb 100; "reserve" 32 bytes
section .text ; code
global _start
_start:
mov rax, 4 ; write
mov rbx, 1 ; stdout
mov rcx, prompt ; where characters start
mov rdx, 0x10 ; 16 characters
int 0x80
mov rax, 3 ; read
mov rbx, 0 ; from stdin
mov rcx, buffer ; start of storage
mov rdx, 0x10; no more than 64 (?) chars
int 0x80
mov rax, [buffer]
mov rbx, "s"
cmp rax, rbx
je _s
; return to Linux
mov rax, 1
mov rbx, 0
int 0x80
_s:
add r8, [buffer]
; dump buffer that was read
mov rdx, rax ; # chars read
mov rax, 4 ; write
mov rbx, 1 ; to stdout
mov rcx, buffer; starting point
int 0x80
jmp _start
If the user types s, followed by <enter>, the memory starting at the address of buffer will contain bytes ['s', '\n', '\0', '\0', ...] (where the newline byte '\n' is from pressing <enter> and the null bytes '\0' are from the .bss section being initialized to 0). As integers, represented in hex, the corresponding values in memory are [0x73, 0x0A, 0x00, 0x00, ...].
The mov rax, [buffer] instruction will copy 8 bytes of memory starting at the address of buffer to the rax register. The byte ordering is little endian on x86, so the 8 bytes will be loaded from memory in reversed order, resulting in rax having 0x0000000000000A73.
Workarounds
This workaround is based on Peter Cordes's comment below. The idea is to compare 1) the first byte starting at the address of buffer with 2) the byte 's'. This would replace the three lines in your question that 1) move [buffer] to rax, 2) move 's' to rbx, and 3) cmp rax, rbx.
cmp byte [buffer], 's'
je _s
This would check that the first character entered is 's', even if followed by other characters. If your intent is to check that only a single character 's' is entered (optionally followed by '\n' in the case that <enter> was pressed to end the input, as opposed to <ctrl-d>), a more thorough approach could utilize the value returned by the read system call, which indicates how many bytes were read.
Without checking how many characters are read, you might want to clear the buffer on each iteration. As is, a user could enter 's' on one iteration, followed by <ctrl-d> on the next iteration, and the buffer would still start with an 's'.
Band-aid Workarounds
(I had originally proposed the following two ideas as workarounds, but they have their own problems that Peter Cordes's identifies in the comments below)
To work around the issue, one option could be to add the newline to your target for comparison.
mov rax, [buffer]
mov rbx, `s\n` ; the second operand was formerly "s"
cmp rax, rbx
je _s
Alternatively, specifying that the read system call only consume 1 byte could be another approach to address the issue.
mov rax, 3 ; read
mov rbx, 0 ; from stdin
mov rcx, buffer ; start of storage
mov rdx, 0x01 ; the second operand was formerly 0x10
int 0x80

x86-64 Bit Assembly Linux Input

I'm trying to input into my program... All it does is run through and print a '0' to the screen. I'm pretty sure that the PRINTDECI function works, I made it a while ago and it works. Do I just have to loop over the input code and only exit when I enter a certain value? I'm not sure how I would do that... Unless it's by ACSII values which might suck.... Anyways, here's my code (Yasm(nasm clone), Intel Syntax):
GLOBAL _start
SECTION .text
PRINTDECI:
LEA R9,[NUMBER + 18] ; last character of buffer
MOV R10,R9 ; copy the last character address
MOV RBX,10 ; base10 divisor
DIV_BY_10:
XOR RDX,RDX ; zero rdx for div
DIV RBX ; rax:rdx = rax / rbx
ADD RDX,0x30 ; convert binary digit to ascii
TEST RAX,RAX ; if rax == 0 exit DIV_BY_10
JZ CHECK_BUFFER
MOV byte [R9],DL ; save remainder
SUB R9,1 ; decrement the buffer address
JMP DIV_BY_10
CHECK_BUFFER:
MOV byte [R9],DL
SUB R9,1
CMP R9,R10 ; if the buffer has data print it
JNE PRINT_BUFFER
MOV byte [R9],'0' ; place the default zero into the empty buffer
SUB R9,1
PRINT_BUFFER:
ADD R9,1 ; address of last digit saved to buffer
SUB R10,R9 ; end address minus start address
ADD R10,1 ; R10 = length of number
MOV RAX,1 ; NR_write
MOV RDI,1 ; stdout
MOV RSI,R9 ; number buffer address
MOV RDX,R10 ; string length
SYSCALL
RET
_start:
MOV RCX, SCORE ;Input into Score
MOV RDX, SCORELEN
MOV RAX, 3
MOV RBX, 0
SYSCALL
MOV RAX, [SCORE]
PUSH RAX ;Print Score
CALL PRINTDECI
POP RAX
MOV RAX,60 ;Kill the Code
MOV RDI,0
SYSCALL
SECTION .bss
SCORE: RESQ 1
SCORELEN EQU $-SCORE
Thanks for any help!
- Kyle
As a side note, the pointer in RCX goes to a insanely large number according to DDD... So I'm thinking I have to get it to pause and wait for me to type, but I have no idea how to do that...
The 'setup' to call syscall 0 (READ) on x86_64 system is:
#xenon:~$ syscalls_lookup read
read:
rax = 0 (0x0)
rdi = unsigned int fd
rsi = char *buf
rdx = size_t count
So your _start code should be something like:
_start:
mov rax, 0 ; READ
mov rdi, 0 ; stdin
mov rsi, SCORE ; buffer
mov rdx, SCORELEN ; length
syscall
The register conventions and syscall numbers for x86_64 are COMPLETELY different than those for i386.
Some conceptual issues you seem to have:
READ does not do ANY interpretation on what you type, you seem to be expecting it to let you type a number (say, 57) and have it return the value 57. Nope. It'll return '5', '7', 'ENTER', 'GARBAGE'... Your SCORELEN is probably 8 (length of resq 1), so you'll read, AT MOST, 8 bytes. or Characters, if you wish to call them that. And unless you type the EOF char (^D), you'll need to type those 8 characters before the READ call will return to your code.
You have to convert the characters you receive into a value... You can do it the easy way and link with ATOI() in the C library, or write your own parser to convert the characters into a value by addition and multiplication (it's not hard, see code below).
Used below, here as a reference:
#xenon:~$ syscalls_lookup write
write:
rax = 1 (0x1)
rdi = unsigned int fd
rsi = const char *buf
rdx = size_t count
Ugh.... So many... I'll just rewrite bits:
global _start
section .text
PRINTDECI:
; input is in RAX
lea r9, [NUMBER + NUMBERLEN - 1 ] ; + space for \n
mov r10, r9 ; save end position for later
mov [r9], '\n' ; store \n at end
dec r9
mov rbx, 10 ; base10 divisor
DIV_BY_10:
xor rdx, rdx ; zero rdx for div
div rbx : rax = rdx:rax / rbx, rdx = remainder
or dl, 0x30 ; make REMAINDER a digit
mov [r9], dl
dec r9
or rax, rax
jnz DIV_BY_10
PRINT_BUFFER:
sub r10, r9 ; get length (r10 - r9)
inc r9 ; make r9 point to initial character
mov rax, 1 ; WRITE (1)
mov rdi, 1 ; stdout
mov rsi, r9 ; first character in buffer
mov rdx, r10 ; length
syscall
ret
MAKEVALUE:
; RAX points to buffer
mov r9, rax ; save pointer
xor rcx, rcx ; zero value storage
MAKELOOP:
mov al, [r9] ; get a character
or al, al ; set flags
jz MAKEDONE ; zero byte? we're done!
and rax, 0x0f ; strip off high nybble and zero rest of RAX (we're lazy!)
add rcx, rcx ; value = value * 2
mov rdx, rcx ; save it
add rcx, rcx ; value = value * 4
add rcx, rcx ; value = value * 8
add rcx, rdx ; value = value * 8 + value * 2 (== value * 10)
add rcx, rax ; add new digit
jmp MAKELOOP ; do it again
MAKEDONE:
mov rax, rcx ; put value in RAX to return
ret
_start:
mov rax, 0 ; READ (0)
mov rdi, 0 ; stdin
mov rsi, SCORE ; buffer
mov rdx, SCORELEN ; length
syscall
; RAX contains HOW MANY CHARS we read!
; -OR-, -1 to indicate error, really
; should check for that, but that's for
; you to do later... right? (if RAX==-1,
; you'll get a segfault, just so you know!)
add rax, SCORE ; get position of last byte
movb [rax], 0 ; force a terminator at end
mov rax, SCORE ; point to beginning of buffer
call MAKEVALUE ; convert from ASCII to a value
; RAX now should have the VALUE of the string of characters
; we input above. (well, hopefully, right?)
mov [VALUE], rax ; store it, because we can!
; it's stored... pretend it's later... we need value of VALUE!
mov rax, [VALUE] ; get the VALUE
call PRINTDECI ; convert and display value
; all done!
mov rax, 60 ; EXIT (60/0x3C)
mov rdi, 0 ; exit code = 0
syscall
section .bss
SCORE: resb 11 ; 10 chars + zero terminator
SCORELEN equ $-SCORE
NUMBER: resb 19 ; 18 chars + CR terminator
NUMBERLEN equ $-NUMBER
I'm going to say that this should work first time, it's off-the-cuff for me, haven't tested it, but it should be good. We read up to 10 chars, terminate it with a zero, convert to a value, then convert to ascii and write it out.
To be more proper, you should save registers to the stack in each subroutine, well, certain ones, and really, only if you're going to interface with libraries... doing things yourself lets you have all the freedom you want to play with the registers, you just have to remember what you put where!
Yes, someone is going to say "why didn't you just multiply by 10 instead of weird adding?" ... uh... because it's easier on the registers and I don't have to set it all up in rdx:rax. Besides, it's just as readable and understandable, especially with the comments. Roll with it! This isn't a competition, it's learning!
Machine code is fun! Gotta juggle all the eggs in your head though... no help from the compiler here!
Technically, you should check return result (RAX) of the syscalls for READ and WRITE, handle errors appropriately, yadda yadda yadda.... learn to use your debugger (gdb or whatever).
Hope this helps.

I'm trying to create a triangle of dots in assembly but it isn't working

I'm trying to create a triangle of dots on the screen by taking a user-entered value (to vary the size of the resulting triangle) and using it to write decrementing lines of dots.
Here is the code:
section .data
global _start
char db ' '
prompt_text db "Enter triangle size (2-99) "
prompt_length equ $-prompt_text
section .bss
tri_size resb 3
tri_size_length equ $-tri_size
section .text
_start:
call prompt
call insert_size
mov rax, [tri_size]
outer_loop:
mov rbx, [tri_size]
inner_loop:
call dot
dec bx
cmp bx, 0
jg inner_loop
call linefeed
call dec_length
dec ax
cmp ax, 0
jne outer_loop
call linefeed
call exit
prompt:
mov rax, 4
mov rbx, 1
mov rcx, prompt_text
mov rdx, prompt_length
int 80h
ret
insert_size:
mov rax, 3
mov rbx, 0
mov rcx, [tri_size]
mov rdx, tri_size_length
int 80h
ret
dot:
mov [char], byte '.'
call print_char
ret
linefeed:
mov [char], byte 10
call print_char
ret
print_char:
push rax
push rbx
push rcx
push rdx
mov rax, 4
mov rbx, 1
mov rcx, char
mov rdx, 1
int 80h
pop rdx
pop rcx
pop rbx
pop rax
ret
dec_length:
push rax
push rbx
push rcx
push rdx
mov rax, [tri_size]
dec ax
mov [tri_size], rax
pop rdx
pop rcx
pop rbx
pop rax
ret
exit:
mov rax, 1
mov rbx, 0
int 80h
Problem:
On running the program, I want the user-inputted number to be used for the number of dots on the first line. However, when I type any number, loads of lines are printed with a single dot on each, then after about a second, a line is printed which contains 32768 dots. This is followed by a line with 32767 dots etc. The number of dots on each line continue to decrease until the line which has 1 dot.
I've noticed that 32768 is hexadecimal for 10000000_00000000, but other than that I'm completely stuck and I'd REALLY appreciate any help!
P.S. I'm using x84-64 linux and assembling with YASM
There are two problems in your code, both when you read the input. First, the fix. Then, an explanation of the current results.
insert_size:
mov rax, 3
mov rbx, 0
mov rcx, [tri_size] ; issue 1
mov rdx, tri_size_length
int 80h
ret ; issue 2 (sort of)
The Fix
First, rcx should contain the address of a buffer, but you are getting the contents of tri_size, not its address. Since tri_size is in the bss section, it is initialized with 0s, so you are telling the OS to read into a NULL buffer. If you were to check the result of the system call, you would see an error code because of this.
Second, when you read input, you are reading a string, not a number. If you want to use it as a number, you need to convert it first. Here is the code with both issues fixed:
insert_size:
mov rax, 3
mov rbx, 0
mov rcx, tri_size ; 1
mov rdx, tri_size_length
int 80h
mov dh, 0 ; 2
mov ah, 0
mov dl, [tri_size] ; 3
mov ah, [tri_size+1]
sub dl, '0' ; 4
cmp al, '0' ; 5
jb done
cmp al, '9' ; 6
ja done
imul dx, 10 ; 7
sub al, '0' ; 8
add dx, ax ; 9
done:
mov [tri_size], dx ; 10
ret
The first issue is an easy fix, just remove the brackets to get the address instead of the contents. The second is more complicated. First, we are going to use 16 bit registers but only read into 8 bits, so step 2 puts 0 in the high byte for each. Then, we read the first two bytes of the string. Next, we convert the first character from a character to a number. Since the digits in ASCII are sequential, we can do this by subtracting the character '0'. Note that this assumes the first character is a valid digit.
We cannot assume the second character is a valid digit, since there might have been only one entered. Therefore, steps 5 and 6 check to see if it is less than '0' and greater than '9', respectively, and jump to the end if either is true. If we get past both, then the second character is a digit. This means the first digit should be in the 10's place, so we multiply it by 10. Then we convert the second character to a number and add it to the first. Now that both characters have been tested, we can store the result back where it is expected and return.
The Explanation
As explained in the fix, you are passing a NULL buffer, so it returns an error. Your tri_size variable is never changed, so it still contains 0. This means both of your counters start at 0. Since you don't check for this, the first dot is printed and bx is decremented, resulting in -1. Since -1 is not greater than 0, the inner loop exits, a newline is printed, your counter and ax are decremented, resulting in -1. This is not zero, so it loops back to the outer loop. This happens 32768 times, until your counter gets to -32768.
When you decrement bx at this point, it becomes -32769, but this number is not representable using 16-bit 2's complement. Instead, it overflows, and the number you get is 32767. This is greater than 0, so you continue in the inner loop until it gets to 0, printing a total of 32768 dots. After the inner loop exits and the newline is printed, the counter and ax are both decremented, resulting in 32767. From this point on, your program works as if the input was 32767, meaning you get a triangle from there to 0 dots.
Interestingly, if your inner loop tested if bx was equal to 0 instead of greater than, you would simply get a triangle from 65536 dots to 0, with no 1-dot lines before it.

Finding null pointer after environment variables

I'm reading a book(Assembly Language Step by Step, Programming with Linux by Jeff Duntemann) and I'm trying to change this program that show's arguments to instead show the environment variables. I'm trying to only use what was taught thus far(no C) and I've gotten the program to print environment variables but only after I counted how many I had and used an immediate, obviously not satisfying. Here's what I have:
global _start ; Linker needs this to find the entry point!
_start:
nop ; This no-op keeps gdb happy...
mov ebp,esp ; Save the initial stack pointer in EBP
; Copy the command line argument count from the stack and validate it:
cmp dword [ebp],MAXARGS ; See if the arg count exceeds MAXARGS
ja Error ; If so, exit with an error message
; Here we calculate argument lengths and store lengths in table ArgLens:
xor eax,eax ; Searching for 0, so clear AL to 0
xor ebx,ebx ; Stack address offset starts at 0
ScanOne:
mov ecx,0000ffffh ; Limit search to 65535 bytes max
mov edi,dword [ebp+16+ebx*4] ; Put address of string to search in EDI
mov edx,edi ; Copy starting address into EDX
cld ; Set search direction to up-memory
repne scasb ; Search for null (0 char) in string at edi
jnz Error ; REPNE SCASB ended without finding AL
mov byte [edi-1],10 ; Store an EOL where the null used to be
sub edi,edx ; Subtract position of 0 from start address
mov dword [ArgLens+ebx*4],edi ; Put length of arg into table
inc ebx ; Add 1 to argument counter
cmp ebx,44; See if arg counter exceeds argument count
jb ScanOne ; If not, loop back and do another one
; Display all arguments to stdout:
xor esi,esi ; Start (for table addressing reasons) at 0
Showem:
mov ecx,[ebp+16+esi*4] ; Pass offset of the message
mov eax,4 ; Specify sys_write call
mov ebx,1 ; Specify File Descriptor 1: Standard Output
mov edx,[ArgLens+esi*4] ; Pass the length of the message
int 80H ; Make kernel call
inc esi ; Increment the argument counter
cmp esi,44 ; See if we've displayed all the arguments
jb Showem ; If not, loop back and do another
jmp Exit ; We're done! Let's pack it in!
I moved the displacement up past the first null pointer to the first environment variable([ebp+4+ebx*4] > [ebp+16+ebx*4]) in both ScanOne and Showem. When I compare to the number of environment variables I have(44) it will print them just fine without a segfault, comparing to 45 only gives me a segfault.
I've tried using the pointers to compare to zero(in search of null pointer): cmp dword [ebp+16+ebx*4],0h but that just returns a segfault. I'm sure that the null pointer comes after the last environment variable in the stack but it's like it won't do anything up to and beyond that.
Where am I going wrong?
What if your program has 2, 3, or 0 args, would your code still work? Each section is separated by a NULL pointer (4 bytes of 0) You could just get the count of parameters and use that as your array index and skip over the args until you get to the NULL bytes. Now you have your Environment Block:
extern printf, exit
section .data
fmtstr db "%s", 10, 0
fmtint db "%d", 10, 0
global main
section .text
main:
push ebp
mov ebp, esp
mov ebx, [ebp + 4]
.SkipArgs:
mov edi, dword [ebp + 4 * ebx]
inc ebx
test edi, edi
jnz .SkipArgs
.ShowEnvBlock:
mov edi, dword [ebp + 4 * ebx]
test edi, edi
jz .NoMore
push edi
push fmtstr
call printf
add esp, 4 * 2
inc ebx
jmp .ShowEnvBlock
.NoMore:
push 0
call exit
Yes I use printf here, but you just swap that with your system call.
Want to go ahead and apologize, this always happens to me(fix it myself after asking question on stackoverflow). I think when I tried comparing pointer to 0h I typed something wrong. Here's what I did:
inc ebx
cmp dword [ebp+16+ebx*4],0h
jnz ScanOne
and
inc esi
cmp dword [ebp+16+esi*4],0h
jnz Showem
This worked.

Clearing out a string variable

I have writen this little experiement bootstrap that has a getline and print_string "functions". The boot stuff is taken from MikeOS tutorial but the rest I have writen myself. I compile this with NASM and run it in QEMU.
So the actual question: I've declared this variable curInpLn on line 6. What ever the user types is saved on that variable and then after enter is hit it is displayed to the user with some additional messages. What I'd like to do is to clear the contents of curInpLn each time the getline function is called but for some reason I can't manage to do that. I'm quite the beginner with Assmebly at the moment.
You can compile the code to bin format and then create a floppy image of it with: "dd status=noxfer conv=notrunc if=FILENAME.bin of=FILENAME.flp" and run it in qemu with: "qemu -fda FILENAME.flp"
BITS 16
jmp start
welcomeSTR: db 'Welcome!',0
promptSTR: db 'Please prompt something: ',0
responseSTR: db 'You prompted: ',0
curInpLn: times 80 db 0 ;this is a variable to hold the input 'command'
curCharCnt: dw 0
curLnNum: dw 1
start:
mov ax, 07C0h ; Set up 4K stack space after this bootloader
add ax, 288 ; (4096 + 512) / 16 bytes per paragraph
mov ss, ax
mov sp, 4096
mov ax, 07C0h ; Set data segment to where we're loaded
mov ds, ax
call clear_screen
lea bx, [welcomeSTR] ; Put string position into SI
call print_string
call new_line
.waitCMD:
lea bx, [promptSTR]
call print_string
call getLine ; Call our string-printing routine
jmp .waitCMD
getLine:
cld
mov cx, 80 ;number of loops for loopne
mov di, 0 ;offset to bx
lea bx, [curInpLn] ;the address of our string
.gtlLoop:
mov ah, 00h ;This is an bios interrupt to
int 16h ;wait for a keypress and save it to al
cmp al, 08h ;see if backspace was pressed
je .gtlRemChar ;if so, jump
mov [bx+di], al ;effective address of our curInpLn string
inc di ;is saved in bx, di is an offset where we will
;insert our char in al
cmp al, 0Dh ;see if character typed is car-return (enter)
je .gtlDone ;if so, jump
mov ah, 0Eh ;bios interrupt to show the char in al
int 10h
.gtlCont:
loopne .gtlLoop ;loopne loops until cx is zero
jmp .gtlDone
.gtlRemChar:
;mov [bx][di-1], 0 ;this needs to be solved. NASM gives error on this.
dec di
jmp .gtlCont
.gtlDone:
call new_line
lea bx, [responseSTR]
call print_string
mov [curCharCnt], di ;save the amount of chars entered to a var
lea bx, [curInpLn]
call print_string
call new_line
ret
print_string: ; Routine: output string in SI to screen
mov si, bx
mov ah, 0Eh ; int 10h 'print char' function
.repeat:
lodsb ; Get character from string
cmp al, 0
je .done ; If char is zero, end of string
int 10h ; Otherwise, print it
jmp .repeat
.done:
ret
new_line:
mov ax, [curLnNum]
inc ax
mov [curLnNum], ax
mov ah, 02h
mov dl, 0
mov dh, [curLnNum]
int 10h
ret
clear_screen:
push ax
mov ax, 3
int 10h
pop ax
ret
times 510-($-$$) db 0 ; Pad remainder of boot sector with 0s
dw 0xAA55 ; The standard PC boot signature
I haven't written code in Assembly for 20 years (!), but it looks like you need to use the 'stosw' instruction (or 'stosb'). STOSB loads the value held in AL to the byte pointed to by ES:DI, whereas STOSSW loads the value held in AX to the word pointed to by ES:DI. The instruction automatically advances the pointer. As your variable curInpLn is 80 bytes long, you can clear it with 40 iterations of STOSW. Something like
xor ax, ax ; ax = 0
mov es, ds ; point es to our data segment
mov di, offset curInpLn ; point di at the variable
mov cx, 40 ; how many repetitions
rep stosw ; zap the variable
This method is probably the quickest method of clearing the variable as it doesn't require the CPU to retrieve any instructions from the pre-fetch queue. In fact, it allows the pre-fetch queue to fill up, thus allowing any following instructions to execute as quickly as possible.

Resources