© Jo Van Hoey 2019
J. Van HoeyBeginning x64 Assembly Programminghttps://doi.org/10.1007/978-1-4842-5076-1_19

19. Console I/O

Jo Van Hoey1 
(1)
Hamme, Belgium
 

We already know how to do console output using system calls or using printf. In this chapter, we will again use system calls, not only for display on the screen but also for accepting input from the keyboard.

Working with I/O

We could easily borrow functions from the C library, but that would spoil the assembly fun! So, Listing 19-1 shows the example source code.
; console1.asm
section .data
      msg1       db    "Hello, World!",10,0
      msg1len    equ   $-msg1
      msg2       db    "Your turn: ",0
      msg2len    equ   $-msg2
      msg3       db    "You answered: ",0
      msg3len    equ   $-msg3
      inputlen equ     10   ;length of inputbuffer
section .bss
      input resb inputlen+1 ;provide space for ending 0
section .text
      global main
main:
push  rbp
mov   rbp,rsp
      mov   rsi, msg1       ; print first string
      mov   rdx, msg1len
      call  prints
      mov   rsi, msg2       ; print second string, no NL
      mov   rdx, msg2len
      call  prints
      mov   rsi, input      ; address of inputbuffer
      mov   rdx, inputlen   ; length of inputbuffer
      call  reads           ; wait for input
      mov   rsi, msg3       ; print third string
      mov   rdx, msg3len
      call  prints
      mov   rsi, input      ; print the inputbuffer
      mov   rdx, inputlen   ; length of inputbuffer
      call  prints
leave
ret
;----------------------------------------------------
prints:
push  rbp
mov   rbp, rsp
; rsi contains address of string
; rdx contains length of string
      mov   rax, 1          ; 1 = write
      mov   rdi, 1          ; 1 = stdout
      syscall
leave
ret
;----------------------------------------------------
reads:
push  rbp
mov   rbp, rsp
; rsi contains address of the inputbuffer
; rdi contains length of the inputbuffer
      mov   rax, 0         ; 0 = read
      mov   rdi, 1         ; 1 = stdin
      syscall
leave
ret
Listing 19-1

console1.asm

This is not very complicated; we provide an input buffer called input to store the characters from the input. We also specify the length of the buffer in inputlen. After displaying some welcome messages, we call the function reads, which accepts all the characters from the keyboard and returns them to the caller when the Enter key is pressed. The calling program then uses the function prints to display the characters that were entered. Figure 19-1 shows the output.
../images/483996_1_En_19_Chapter/483996_1_En_19_Fig1_HTML.jpg
Figure 19-1

console1.asm output

There are some issues, however! We reserved 10 bytes for the input buffer. What happens if the input is longer than 10 characters? Figure 19-2 shows our result.
../images/483996_1_En_19_Chapter/483996_1_En_19_Fig2_HTML.jpg
Figure 19-2

console1.asm with too many characters

The program accepted only ten characters and doesn’t know what to do with the surplus characters, so it throws them back to the operating system. The operating system tries to figure out and interpret the characters as CLI commands but cannot find corresponding commands. Errors!

That’s not nice, but it’s even worse than at first glance. This way of handling input can cause a security breach, where a hacker can break out of a program and gets access to the operating system!

Dealing with Overflows

Listing 19-2 shows another version, where we count the characters and just ignore surplus characters. As an additional tweak, we only allow lowercase alphabetic characters, a to z.
; console2.asm
section .data
      msg1  db    "Hello, World!",10,0
      msg2  db    "Your turn (only a-z): ",0
      msg3  db    "You answered: ",0
      inputlen    equ   10   ;length of inputbuffer
      NL    db    0xa
section .bss
      input resb inputlen+1     ;provide space for ending 0
section .text
      global main
main:
push  rbp
mov   rbp,rsp
      mov   rdi, msg1   ; print first string
      call  prints
      mov   rdi, msg2   ; print second string, no NL
      call  prints
      mov   rdi, input       ; address of inputbuffer
      mov   rsi, inputlen    ; length of inputbuffer
      call  reads            ; wait for input
      mov   rdi, msg3        ; print third string and add the input string
      call  prints
      mov   rdi, input       ; print the inputbuffer
      call  prints
      mov   rdi,NL           ; print NL
      call  prints
leave
ret
;----------------------------------------------------------
prints:
push  rbp
mov   rbp, rsp
push  r12      ; callee saved
; Count characters
      xor   rdx, rdx    ; length in rdx
      mov   r12, rdi
.lengthloop:
      cmp   byte [r12], 0
      je    .lengthfound
      inc   rdx
      inc   r12
      jmp   .lengthloop
.lengthfound:           ; print the string, length in rdx
      cmp   rdx, 0      ; no string (0 length)
      je    .done
      mov   rsi,rdi     ; rdi contains address of string
      mov   rax, 1      ; 1 = write
      mov   rdi, 1      ; 1 = stdout
      syscall
.done:
pop r12
leave
ret
;----------------------------------------------------------
reads:
section .data
section .bss
      .inputchar       resb       1
section .text
push  rbp
mov   rbp, rsp
      push  r12              ; callee saved
      push  r13              ; callee saved
      push  r14              ; callee saved
      mov   r12, rdi    ; address of inputbuffer
      mov   r13, rsi    ; max length in r13
      xor   r14, r14    ; character counter
.readc:
      mov   rax, 0           ; read
      mov   rdi, 1           ; stdin
      lea   rsi, [.inputchar]     ; address of input
      mov   rdx, 1           ; # of characters to read
      syscall
      mov   al, [.inputchar]      ; char is NL?
      cmp   al, byte[NL]
      je    .done            ; NL end
      cmp   al, 97           ; lower than a?
      jl    .readc           ; ignore it
      cmp   al, 122          ; higher than z?
      jg    .readc           ; ignore it
      inc   r14                    ; inc counter
      cmp   r14, r13
      ja    .readc           ; buffer max reached, ignore
      mov   byte [r12], al   ; safe the char in the buffer
      inc   r12              ; point to next char in buffer
      jmp   .readc
.done:
      inc   r12
      mov   byte [r12],0     ; add end 0 to inputbuffer
      pop   r14              ; callee saved
      pop   r13              ; callee saved
      pop   r12              ; callee saved
leave
ret
Listing 19-2

console2.asm

We modified the prints function so that it first counts the number of characters to display; that is, it counts until it finds a 0 byte. When the length is determined, prints displays the string with a syscall.

The reads function waits for one input character and checks whether it is a new line. If it’s a new line, the character reading from the keyboard stops. Register r14 holds the count of the input characters. The function checks whether the number of characters is larger than inputlen; if not, the character is added to the buffer input. If inputlen is exceeded, the character is ignored, but the reading from the keyboard continues. We require the ASCII code of the character to be 97 or higher and 122 or lower. This will guarantee that only lowercase alphabetic characters are accepted. Note that we saved and restored the callee-saved registers; we used r12 in both functions, prints and reads. In this case, not saving the callee-saved register would not be a problem, but you can imagine that if one function calls another and that one calls yet another, problems could arise.

Figure 19-3 shows the output.
../images/483996_1_En_19_Chapter/483996_1_En_19_Fig3_HTML.jpg
Figure 19-3

console2.asm

Debugging console input with SASM is complicated because we are providing input via a syscall. SASM provides its own functionality for I/O, but we didn’t want to use it because we wanted to show how assembly and machine language work without hiding the details. If you get stuck with debugging in SASM, go back to our good old friend GDB.

Summary

In this chapter, you learned about the following:
  • Keyboard input using syscall

  • Validating keyboard input

  • Debugging with keyboard input, which can be complicated

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.54.168