使用 x86_64 sys_call 在 Linux 上读取单键输入(无需等待返回)

AFO*_*OEK 1 linux assembly x86-64 nasm tty

我想让 Linux 只使用 sys_read 从键盘上进行 1 次击键,但 sys_read 只是等到我按下 Enter 键。如何读取 1 个按键?这是我的代码:

Mov EAX,3
Mov EBX,0
Mov ECX,Nada
Mov EDX,1
Int 80h

Cmp ECX,49
Je Do_C
Jmp Error
Run Code Online (Sandbox Code Playgroud)

我已经尝试使用 BIOS 中断但它失败了(分段错误),我想从键盘捕获数字 1 到 8 输入。

fcd*_*cdt 5

64 位 linux 中的系统调用

来自的表格在man syscall这里提供了一个很好的概述:

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    -
Run Code Online (Sandbox Code Playgroud)

我省略了此处不相关的行。在 32 位模式下,参数在等中传递eax ecx系统调用号在 中eax。在 64 位模式下有点不同:所有寄存器现在都是 64 位宽,因此具有不同的名称。系统调用号仍在 中eax,现在变为rax. 但是现在传入了参数rdi rsi等等。另外,syscall这里使用的是指令,而不是int 0x80触发系统调用。

参数的顺序也可以在手册页中阅读,这里man 2 ioctlman 2 read

int ioctl(int fd, unsigned long request, ...);
ssize_t read(int fd, void *buf, size_t count);
Run Code Online (Sandbox Code Playgroud)

所以这里的值int fd是 in rdi,第二个参数 inrsi等。

如何摆脱等待换行

首先termios在内存中创建一个结构(在.bss节中):

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
Run Code Online (Sandbox Code Playgroud)

然后获取当前终端设置并禁用规范模式:

; 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
Run Code Online (Sandbox Code Playgroud)

现在您可以使用sys_read来读入击键:

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
Run Code Online (Sandbox Code Playgroud)

然后检查中的返回值rax:它包含读取的字符数。


参考: