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

。_饼干妹妹 提交于 2020-08-15 12:53:55

问题


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. Thanks for the advance Sorry for bad english


回答1:


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 eax, 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 rd 1   ; input mode flags
  c_oflag rd 1   ; output mode flags
  c_cflag rd 1   ; control mode flags
  c_lflag rd 1   ; local mode flags
  c_line  rb 1   ; line discipline
  c_cc    rb 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_cflag], $FD  ; 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.


References:

  • 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)


来源:https://stackoverflow.com/questions/62937150/reading-a-single-key-input-on-linux-without-waiting-for-return-using-x86-64-sy

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!