使用 x86_64 sys_call 读取 Linux 上的单键输入(无需等待 return)
Reading a single-key input on Linux (without waiting for return) using x86_64 sys_call
我想让 Linux 使用 sys_read 从键盘敲击 1 次,但 sys_read 只是等到我按下回车键。如何阅读 1 击键?这是我的代码:
Mov EAX,3
Mov EBX,0
Mov ECX,Nada
Mov EDX,1
Int 80h
Cmp ECX,49
Je Do_C
Jmp Error
我已经尝试使用 BIOS 中断但它失败了(分段错误),我想从键盘捕获数字 1 到 8 输入。
64 位系统调用 linux
man syscall
中的表格在这里提供了很好的概述:
arch/ABI instruction syscall # retval Notes
──────────────────────────────────────────────────────────────────
i386 int [=10=]x80 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 -
我省略了这里不相关的行。在 32 位模式下,参数在 ebx
、ecx
等中传输,系统调用号在 eax
中。在 64 位模式下略有不同:所有寄存器现在都是 64 位宽,因此具有不同的名称。系统调用编号仍在 eax
中,现在变为 rax
。但是参数现在是在rdi
, rsi
,等中传递的。另外,这里使用指令syscall
代替int 0x80
来触发系统调用。
参数的顺序也可以在手册页中阅读,这里是 man 2 ioctl
和 man 2 read
:
int ioctl(int fd, unsigned long request, ...);
ssize_t read(int fd, void *buf, size_t count);
所以这里int fd
的值在rdi
,第二个参数在rsi
等等
如何摆脱等待换行
首先在内存中创建一个termios
结构(在.bss
段):
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
然后获取当前终端设置并禁用规范模式:
; 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
现在可以使用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
然后检查rax
中的return值:它包含读取的字符数。
(,例如,如果您在 bash 中通过 运行 ./a.out <&-
关闭标准输入。使用 strace
打印系统调用的解码跟踪你的程序,所以你不需要在玩具实验中实际编写错误处理。)
参考文献:
- How do i read single character input from keyboard using nasm (assembly) under ubuntu?
- Using the raw keyboard mode under Linux(带有 32 位汇编示例的外部站点)
我想让 Linux 使用 sys_read 从键盘敲击 1 次,但 sys_read 只是等到我按下回车键。如何阅读 1 击键?这是我的代码:
Mov EAX,3
Mov EBX,0
Mov ECX,Nada
Mov EDX,1
Int 80h
Cmp ECX,49
Je Do_C
Jmp Error
我已经尝试使用 BIOS 中断但它失败了(分段错误),我想从键盘捕获数字 1 到 8 输入。
64 位系统调用 linux
man syscall
中的表格在这里提供了很好的概述:
arch/ABI instruction syscall # retval Notes
──────────────────────────────────────────────────────────────────
i386 int [=10=]x80 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 -
我省略了这里不相关的行。在 32 位模式下,参数在 ebx
、ecx
等中传输,系统调用号在 eax
中。在 64 位模式下略有不同:所有寄存器现在都是 64 位宽,因此具有不同的名称。系统调用编号仍在 eax
中,现在变为 rax
。但是参数现在是在rdi
, rsi
,等中传递的。另外,这里使用指令syscall
代替int 0x80
来触发系统调用。
参数的顺序也可以在手册页中阅读,这里是 man 2 ioctl
和 man 2 read
:
int ioctl(int fd, unsigned long request, ...);
ssize_t read(int fd, void *buf, size_t count);
所以这里int fd
的值在rdi
,第二个参数在rsi
等等
如何摆脱等待换行
首先在内存中创建一个termios
结构(在.bss
段):
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
然后获取当前终端设置并禁用规范模式:
; 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
现在可以使用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
然后检查rax
中的return值:它包含读取的字符数。
(./a.out <&-
关闭标准输入。使用 strace
打印系统调用的解码跟踪你的程序,所以你不需要在玩具实验中实际编写错误处理。)
参考文献:
- How do i read single character input from keyboard using nasm (assembly) under ubuntu?
- Using the raw keyboard mode under Linux(带有 32 位汇编示例的外部站点)