Reading a single-key input on Linux (without waiting for return) using x86_64 sys_call - linux

I want to make Linux just take 1 keystroke from keyboard using sys_read, but sys_read just wait until i pressed enter. How to read 1 keystroke ? this is my code:
Mov EAX,3
Mov EBX,0
Mov ECX,Nada
Mov EDX,1
Int 80h
Cmp ECX,49
Je Do_C
Jmp Error
I already tried using BIOS interrupt but it's failed (Segmentation fault), I want capture number 1 to 8 input from keyboard.

Syscalls in 64-bit linux
The tables from man syscall provide a good overview here:
arch/ABI instruction syscall # retval Notes
──────────────────────────────────────────────────────────────────
i386 int $0x80 eax eax
x86_64 syscall rax rax See below
arch/ABI arg1 arg2 arg3 arg4 arg5 arg6 arg7 Notes
──────────────────────────────────────────────────────────────────
i386 ebx ecx edx esi edi ebp -
x86_64 rdi rsi rdx r10 r8 r9 -
I have omitted the lines that are not relevant here. In 32-bit mode, the parameters were transferred in ebx, ecx, etc and the syscall number is in eax. In 64-bit mode it is a little different: All registers are now 64-bit wide and therefore have a different name. The syscall number is still in eax, which now becomes rax. But the parameters are now passed in rdi, rsi, etc. In addition, the instruction syscall is used here instead of int 0x80 to trigger a syscall.
The order of the parameters can also be read in the man pages, here man 2 ioctl and man 2 read:
int ioctl(int fd, unsigned long request, ...);
ssize_t read(int fd, void *buf, size_t count);
So here the value of int fd is in rdi, the second parameter in rsi etc.
How to get rid of waiting for a newline
Firstly create a termios structure in memory (in .bss section):
termios:
c_iflag resd 1 ; input mode flags
c_oflag resd 1 ; output mode flags
c_cflag resd 1 ; control mode flags
c_lflag resd 1 ; local mode flags
c_line resb 1 ; line discipline
c_cc resb 19 ; control characters
Then get the current terminal settings and disable canonical mode:
; Get current settings
mov eax, 16 ; syscall number: SYS_ioctl
mov edi, 0 ; fd: STDIN_FILENO
mov esi, 0x5401 ; request: TCGETS
mov rdx, termios ; request data
syscall
; Modify flags
and byte [c_lflag], 0FDh ; Clear ICANON to disable canonical mode
; Write termios structure back
mov eax, 16 ; syscall number: SYS_ioctl
mov edi, 0 ; fd: STDIN_FILENO
mov esi, 0x5402 ; request: TCSETS
mov rdx, termios ; request data
syscall
Now you can use sys_read to read in the keystroke:
mov eax, 0 ; syscall number: SYS_read
mov edi, 0 ; int fd: STDIN_FILENO
mov rsi, buf ; void* buf
mov rdx, len ; size_t count
syscall
Afterwards check the return value in rax: It contains the number of characters read.
(Or a -errno code on error, e.g. if you closed stdin by running ./a.out <&- in bash. Use strace to print a decoded trace of the system calls your program makes, so you don't need to actually write error handling in toy experiments.)
References:
What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?
Why does the sys_read system call end when it detects a new line?
How do i read single character input from keyboard using nasm (assembly) under ubuntu?
Using the raw keyboard mode under Linux (external site with example in 32-bit assembly)

Related

nasm linux x64 how do i find and cmp EOF to stop printing data from file to screen

nasm linux x64 how do i find and cmp EOF to stop printing data from file to screen
section .data
Nile_2 db '/home/mark/Desktop/mynewfile.txt', 0;
section .bss
fd_out resd 4 ; use this here instead of resb so there is enough room for long file names
fd_in resd 4
info resd 26
section .text
global _start ;must be declared for using gcc
_start:
;open the file for reading
mov rax, 5
mov rbx, Nile_2 ; was file_name
mov rcx, 0 ;for read only access
; mov rdx, 0777 ;read, write and execute by all
int 0x80
mov [fd_in], eax
;mov rdx, 20
READ:
;read from file
mov rax, 3
mov rbx, [fd_in]
mov rcx, info
;mov rdx, 26
int 0x80
; print the info
mov rax, 4
mov rbx, 1
mov rcx, info
;mov rdx, 26
int 0x80
mov r10, info ; dec rdx
cmp r10, 00
jnz READ
;jmp READ ;jnz Read
HERE:
; close the file
mov rax, 6
mov rbx, [fd_in]
int 0x80
mov eax,1 ;system call number (sys_exit)
int 0x80 ;call kernel
how do i find out if sys_read or info is zero
how do i find out EOF ??? i dont know how to use sys_read to check if no bytes have been read... when i try to cmp that info is zero bytes read i get the last few bytes over and over ???
You are using int 0x80 in your 64-bit code. Normally, in 64-bit code, you would use the syscall instruction and use other register arguments. For some light reading, see
What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?
how do i find out if sys_read or info is zero how do i find out EOF ???
If a request to read a certain number of bytes from a file, reports a count that is less than the number that you requested, then the end-of-file has been reached. If the reported count is 0, then the end-of-file was already reached before you made the request, else we say that a partial record was read.
i dont know how to use sys_read to check if no bytes have been read...
sys_read provides you with the number of bytes that were actually read in the eax register. I mentioned this already in my answer to your previous question Nasm linux for some reason i cant use two file variable names at the same time for opening a file and creating a file?
when i try to cmp that info is zero bytes read i get the last few bytes over and over ???
You have managed to create an endless loop!
In NASM, an instruction like mov r10, info loads the address of info in the register. So not the contents that are stored at info which is what you were trying to get.
Since the address is not zero, code like:
mov r10, info
cmp r10, 00
jnz READ
will always jump and thus continue the loop ad infinitum.
fd_out resd 4 ; use this here instead of `resb` so there is **enough room for long file names**
fd_in resd 4
info resd 26
What sys_creat and sys_open return to you in the eax register is a file descriptor (it's in the names fd_in and fd_out), and it's a handle by which the system can identify the open file. It is a single dword and it is meaningless to assign 4 dwords for its storage.
Also, I find it strange that, in response to my previous answer, you changed info resb 26 into info resd 26. This does not make much sense.
With using 64-bit registers this time, you did introduce another mismatch on the fd_in variable! If you stored a dword (mov [fd_in], eax) then don't retrieve a qword (mov rbx, [fd_in])
section .bss
fd_out resd 1
fd_in resd 1
info resb 26
...
READ:
;read from file
mov edx, 26
mov ecx, info
mov ebx, [fd_in]
mov eax, 3
int 0x80 ; -> EAX
test eax, eax
js ERROR ; Some error occured
jz HERE ; End-of-file
; print the info
mov edx, eax ; Could be a partial record
mov ecx, info
mov ebx, 1
mov eax, 4
int 0x80
jmp READ
ERROR:
HERE:
i found out that if i check eax and if its zero thats the end of file
cmp rax, 0
je HERE
'''
so that there does the job and not it will end almost properly when it reaches the end of the file it does spit out an extra bit tho dont know why

Interrupt "console input without echo" in Linux

DOS has int 21h / AH=08H: Console input without echo.
Is there something similar for Linux? If I need to process the entered value before it is displayed in the terminal.
Under Linux, it is the tty that buffers the typed chars before "sending" them to the requesting program.
This is controlled through the terminal mode: raw (no buffering) or cooked (also known respectively as non-canonical and canonical mode).
These modes are actually attributes of the tty, which can be controlled with tcgetattr and tcsetattr.
The code to set the terminal in non-canonical mode without echo can be found, for example, here (more info on the VTIME and VMIN control chars can be found here).
That's C, so we need to translate it into assembly.
From the source of tcgetattr we can see that the tty attributes are retrieved through an IOCTL to stdin with the command TCGETS (value 0x5401) and, similarly, they are set with an IOCTL with the command TCSETS (value 0x5402).
The structure read is not the struct termios but struct __kernel_termios which is basically a shortened version of the former.
The IOCTL must be sent to the stdin file (file descriptor STDIN_FILENO of value 0).
Knowing how to implement tcgetattr and tcsetattr we only need to get the value of the constants (like ICANON and similar).
I advise using a compiler (e.g. here) to find the values of the public constants and to check the structure's offsets.
For non-public constants (not visible outside their translation units) we must resort to reading the source (this is not particularly hard, but care must be taken to find the right source).
Below a 64-bit program that invokes the IOCTLs to get-modify-set the TTY attribute in order to enable the raw mode.
Then the program waits for a single char and displays it incremented (e.g. a -> b).
Note that this program has been tested under Linux (5.x) and, as the various constants change values across different clones of Unix, it is not portable.
I used NASM and defined a structure for struct __kernel_termios, I also used a lot of symbolic constants to make the code more readable. I don't really like using structures in assembly but NASM ones are just a thin macro layer (it's better to get used to them if you aren't already).
Finally, I assume familiarity with 64-bit Linux assembly programming.
BITS 64
GLOBAL _start
;
; System calls numbers
;
%define SYS_READ 0
%define SYS_WRITE 1
%define SYS_IOCTL 16
%define SYS_EXIT 60
;
; __kernel_termios structure
;
%define KERNEL_NCC 19
struc termios
.c_iflag: resd 1 ;input mode flags
.c_oflag: resd 1 ;output mode flags
.c_cflag: resd 1 ;control mode flags
.c_lflag: resd 1 ;local mode flags
.c_line: resb 1 ;line discipline
.c_cc: resb KERNEL_NCC ;control characters
endstruc
;
; IOCTL commands
;
%define TCGETS 0x5401
%define TCSETS 0x5402
;
; TTY local flags
;
%define ECHO 8
%define ICANON 2
;
; TTY control chars
;
%define VMIN 6
%define VTIME 5
;
; Standard file descriptors
;
%define STDIN_FILENO 0
%define STDOUT_FILENO 1
SECTION .bss
;The char read (reserve a DWORD to make termios_data be aligned on DWORDs boundary)
data resd 1
;The TTY attributes
termios_data resb termios_size
SECTION .text
_start:
;
;Get the terminal settings by sending the TCGETS IOCTL to stdin
;
mov edi, STDIN_FILENO ;Send IOCTL to stdin (Less efficient but more readable)
mov esi, TCGETS ;The TCGETS command
lea rdx, [REL termios_data] ;The arg, the buffer where to store the TTY attribs
mov eax, SYS_IOCTL ;Do the syscall
syscall
;
;Set the raw mode by clearing ECHO and ICANON and setting VMIN = 1, VTIME = 0
;
and DWORD [REL termios_data + termios.c_lflag], ~(ICANON | ECHO) ;Clear ECHO and ICANON
mov BYTE [REL termios_data + termios.c_cc + VMIN], 1
mov BYTE [REL termios_data + termios.c_cc + VTIME], 0
;
;Set the terminal settings
;
mov edi, STDIN_FILENO ;Send to stdin (Less efficient but more readable)
mov esi, TCSETS ;Use TCSETS as the command
lea rdx, [REL termios_data] ;Use the same data read (and altered) before
mov eax, SYS_IOCTL ;Do the syscall
syscall
;
;Read a char
;
mov edi, STDIN_FILENO ;Read from stdin (Less efficient but more readable)
lea rsi, [REL data] ;Read into data
mov edx, 1 ;Read only 1 char
mov eax, SYS_READ ;Do the syscall (Less efficient but more readable)
syscall
;
;Increment the char (as an example)
;
inc BYTE [REL data]
;
;Print the char
;
mov edi, STDOUT_FILENO ;Write to stdout
lea rsi, [REL data] ;Write the altered char
mov edx, 1 ;Only 1 char to write
mov eax, SYS_WRITE ;Do the syscall
syscall
;
;Restore the terminal settins (similar to the code above)
;
mov edi, STDIN_FILENO
mov esi, TCGETS
lea rdx, [REL termios_data]
mov eax, SYS_IOCTL
syscall
;Set ECHO and ICANON
or DWORD [REL termios_data + termios.c_lflag], ICANON | ECHO
mov edi, STDIN_FILENO
mov esi, TCSETS
lea rdx, [REL termios_data]
mov eax, SYS_IOCTL
syscall
;
;Exit
;
xor edi, edi
mov eax, SYS_EXIT
syscall

Linux temios non-canonical sys_call getch() doesn't work

I recently make input using Linux assembly (x86_64) i got this code and i try it eventually the code are doesn't work like what i expected, i should wait a keystroke but it auto input from no where, i suspect termios flags... code are in below :
;Get current settings
Mov EAX, 16 ; SYS_ioctl
Mov EDI, 0 ; STDIN_FILENO
Mov ESI, 0x5401 ; TCGETS
Mov RDX, termios
Int 80h
And dword [c_cflag], 0xFD ; Clear ICANON to disable canonical mode
; Write termios structure back
Mov EAX, 16 ; SYS_ioctl
Mov EDI, 0 ; STDIN_FILENO
Mov ESI, 0x5402 ; TCSETS
Mov RDX, termios
Int 80h
Mov EAX,0 ;sys_read kernel call
Mov EBX,0 ;stdin trap (standart input)
Mov ECX,Nada ;Masukkan offset/jumlah byte yang akan di baca
Mov EDX,1 ;Jumlah byte yang dibaca
Int 80h ;Call Kernel
for the termios struct :
SECTION .bss ;deklarasi untuk variable yang belum terdefinisi
Enter: Resb 1 ;Pesan 1 byte untuk Enter
Nada: Resb 1
termios:
c_iflag Resd 1 ; input mode flags
c_oflag Resd 1 ; output mode flags
c_cflag Resd 1 ; control mode flags
c_lflag Resd 1 ; local mode flags
c_line Resb 1 ; line discipline
c_cc Resb 64 ; control characters
for the output :
nasm -f elf64 -g -F stabs key.asm
ld -o KeyPress key.o
./KeyPress
Untuk memulai tekan tombol enter:
Tekan tombol untuk memainkan satu not: (1,2,3,4,5,6,7,8)
//this part are the error occur,i have to check if user inputed right value
if not it will jump to error label and printed below message//
Error note not found please contact the app developer !!
reference : Linux Getch(), My Github Repo
PS: For the newest code i already push on my repository i use ubuntu 20.04 and Intel i7 (64-bit), thanks for the help
... i got this code and i try it eventually the code are doesn't work like what i expected ...
Mov ESI, 0x5401
Mov RDX, termios
Int 80h
This won't work:
Int 80h is the 32-bit system call used in 32-bit programs. The first three arguments are passed in EBX, ECX and EDX, and definitely not in ESI.
And the values of EAX required for Int 80h differ from the method used in 64-bit programs: read() would be EAX=3, not EAX=0.
Int 80h seems to work in 64-bit programs, too, however, passing 64-bit values wont work; so you cannot use Int 80h for system calls that take addresses (in the example: the address of termios) as argument.
Either you assemble and link your code as 32-bit program, use int 80h, pass the arguments in EBX, ECX and EDX and use the values in EAX required for 32-bit programs (for example: EAX=3 for read()):
mov eax, 54 ; sys_ioctl when using "int 80h"
mov ebx, 0 ; stdin
mov ecx, 0x5402 ; TCSETS
mov edx, termios
int 80h
Or you build a 64-bit program and use the syscall instruction to call system calls (see this question):
mov eax, 0 ; sys_read when using "syscall"
; note that this instruction will actually set RAX to 0
mov edi, 0 ; set RDI to stdin (implicitly sets rdi)
mov rsi, Nada ; Address of the buffer (see below)
; we explicitly have to use "rsi" here!
mov edx, 1 ; number of bytes
syscall
mov ecx, Nada
I don't use "nasm" but another assembler; so maybe I am wrong. But as far as I know the instruction above would be interpreted by "nasm" as:
Read the value stored in the RAM at the address Nada and write that value to the ecx register.
However, you want the address of Nada to be written to the ecx register.
As far as I know, this instruction would be written as: mov ecx, offset Nada in "masm".
If this is true, the corresponding line in my example above must be: mov rsi, offset Nada.
And dword [c_cflag], 0xFD ; Clear ICANON to disable canonical mode
This line contains two errors:
ICANON is located in C_LFLAG, not in C_CFLAG.
And this instruction would be identical to the C/C++ instruction: c_cflag &= ~0xFFFFFF02, but you want to do: c_cflag &= ~2.
To clear bit 1 only, you have two possibilities:
And byte [c_lflag], 0xFD
; OR:
And dword [c_lflag], 0xFFFFFFFD

Writing a putchar in Assembly for x86_64 with 64 bit Linux?

I am trying to use the write syscall in order to reproduce the putchar function behavior which prints a single character. My code is as follows,
asm_putchar:
push rbp
mov rbp, rsp
mov r8, rdi
call:
mov rax, 1
mov rdi, 1
mov rsi, r8
mov rdx, 1
syscall
return:
mov rsp, rbp
pop rbp
ret
From man 2 write, you can see the signature of write is,
ssize_t write(int fd, const void *buf, size_t count);
It takes a pointer (const void *buf) to a buffer in memory. You can't pass it a char by value, so you have to store it to memory and pass a pointer.
(Don't print one char at a time unless you only have one to print, that's really inefficient. Construct a buffer in memory and print that. e.g. this x86-64 Linux NASM function: How do I print an integer in Assembly Level Programming without printf from the c library?)
A NASM version of GCC: putchar(char) in inline assembly:
; x86-64 System V calling convention: input = byte in DIL
; clobbers: RDI, RSI, RDX, RCX, R11 (last 2 by syscall itself)
; returns: RAX = write return value: 1 for success, -1..-4095 for error
writechar:
mov byte [rsp-4], dil ; store the char from RDI
mov edi, 1 ; EDI = fd=1 = stdout
lea rsi, [rsp-4] ; RSI = buf
mov edx, edi ; RDX = len = 1
syscall ; rax = write(1, buf, 1)
ret
If you do pass an invalid pointer in RSI, such as '2' (integer 50), the system call will return -EFAULT (-14) in RAX. (The kernel returns error codes on bad pointers to system calls, instead of delivering a SIGSEGV like it would if you deref in user-space).
See also What are the return values of system calls in Assembly?
Instead of writing code to check return values, in toy programs / experiments you should just run them under strace ./a.out, especially if you're writing your own _start without libc there won't be any other system calls during startup that you don't make yourself, so it's very easy to read the output. How should strace be used?

Display contents of register

hi i need help displaying contents of a register.my code is below.i have been able to display values of the data register but i want to display flag states. eg 1 or 0. and it would be helpful if to display also the contents of other registers like esi,ebp.
my code is not printing the states of the flags ..what am i missing
section .text
global _start ;must be declared for using gcc
_start : ;tell linker entry point
mov eax,msg ; moves message "rubi" to eax register
mov [reg],eax ; moves message from eax to reg variable
mov edx, 8 ;message length
mov ecx, [reg];message to write
mov ebx, 1 ;file descriptor (stdout)
mov eax, 4 ;system call number (sys_write)
int 0x80 ;call kernel
mov eax, 100
mov ebx, 100
cmp ebx,eax
pushf
pop dword eax
mov [save_flags],eax
mov edx, 8 ;message length
mov ecx,[save_flags] ;message to write
mov ebx, 1 ;file descriptor (stdout)
mov eax, 4 ;system call number (sys_write)
int 0x80
mov eax, 1 ;system call number (sys_exit)
int 0x80 ;call kernel
section .data
msg db "rubi",10
section .bss
reg resb 100
save_flags resw 100
I'm not going for anything fancy here since this appears to be a homework assignment (two people have asked the same question today). This code should be made as a function, and it can have its performance enhanced. Since I don't get an honorary degree or an A in the class it doesn't make sense to me to offer the best solution, but one you can work from:
BITS_TO_DISPLAY equ 32 ; Number of least significant bits to display (1-32)
section .text
global _start ; must be declared for using gcc
_start : ; tell linker entry point
mov edx, msg_len ; message length
mov ecx, msg ; message to write
mov ebx, 1 ; file descriptor (stdout)
mov eax, 4 ; system call number (sys_write)
int 0x80 ; call kernel
mov eax, 100
mov ebx, 100
cmp ebx,eax
pushf
pop dword eax
; Convert binary to string by shifting the right most bit off EAX into
; the carry flag (CF) and convert the bit into a '0' or '1' and place
; in the save_flags buffer in reverse order. Nul terminate the string
; in the event you ever wish to use printf to print it
mov ecx, BITS_TO_DISPLAY ; Number of bits of EAX register to display
mov byte [save_flags+ecx], 0 ; Nul terminate binary string in case we use printf
bin2ascii:
xor bl, bl ; BL = 0
shr eax, 1 ; Shift right most bit into carry flag
adc bl, '0' ; bl = bl + '0' + Carry Flag
mov [save_flags-1+ecx], bl ; Place '0'/'1' into string buffer in reverse order
dec ecx
jnz bin2ascii ; Loop until all bits processed
mov edx, BITS_TO_DISPLAY ; message length
mov ecx, save_flags ; address of binary string to write
mov ebx, 1 ; file descriptor (stdout)
mov eax, 4 ; system call number (sys_write)
int 0x80
mov eax, 1 ;system call number (sys_exit)
int 0x80 ;call kernel
section .data
msg db "rubi",10
msg_len equ $ - msg
section .bss
save_flags resb BITS_TO_DISPLAY+1 ; Add one byte for nul terminator in case we use printf
The idea behind this code is that we continually shift the bits (using the SHR instruction) in the EAX register to the right one bit at a time. The bit that gets shifted out of the register gets placed in the carry flag (CF). We can use ADC to add the value of the carry flag (0/1) to ASCII '0' to get an ASCII value of '0` and '1'. We place these bytes into destination buffer in reverse order since we are moving from right to left through the bits.
BITS_TO_DISPLAY can be set between 1 and 32 (since this is 32-bit code). If you are interested in the bottom 8 bits of a register set it to 8. If you want to display all the bits of a 32-bit register, specify 32.
Note that you can pop directly into memory.
And if you want to binary dump register and flag data with write(2), your system call needs to pass a pointer to the buffer, not the data itself. Use a mov-immediate to get the address into the register, rather than doing a load. Or lea to use a RIP-relative addressing mode. Or pass a pointer to where it's sitting on the stack, instead of copying it to a global!
mov edx, 8 ;message length
mov ecx,[save_flags] ;message to write ;;;;;;; <<<--- problem
mov ebx, 1 ;file descriptor (stdout)
mov eax, 4 ;system call number (sys_write)
int 0x80
Passing a bad address to write(2) won't cause your program to receive a SIGSEGV, like it would if you used that address in user-space. Instead, write will return EFAULT. And you're not checking the return status from your system calls, so your code doesn't notice.
mov eax,msg ; moves message "rubi" to eax register
mov [reg],eax ; moves message from eax to reg variable
mov ecx, [reg];
This is silly. You should just mov ecx, msg to get the address of msg into ecx, rather than bouncing it through memory.
Are you building for 64bit? I see you're using 8 bytes for a message length. If so, you should be using the 64bit function call ABI (with syscall, not int 0x80). The system-call numbers are different. See the table in one of the links at x86. The 32bit ABI can only accept 32bit pointers. You will have a problem if you try to pass a pointer that has any of the high32 bits set.
You're probably also going to want to format the number into a string, unless you want to pipe your program's output into hexdump.

Resources