Intel Assembly 中的键盘缓冲区
Keyboard buffer in Intel Assembly
我在 Intel Assembly 上管理 "keyboard overflows" 时遇到问题。
主要问题是在读取了 read 调用指定的最大大小之后,剩余的数据被扔进了终端。
我在 x64 架构上使用 Linux。
这其实就是功课。
我的主要想法如下:
%define maxChars 10
%define maxChars_2 100
section .bss
strLida : resb maxChars
strLidaL : resd 1
read:
mov dword [strLidaL], maxChars
mov rax, 0
mov rdi, 1
mov rsi, strLida
mov rdx, [strLidaL]
syscall
mov [strLidaL], rax
size_compare:
cmp [strLidaL], maxChars
jge overflow
overflow:
mov dword [strLidaL_2], maxChars_2
mov rax, 0
mov rdi, 1
mov rsi, strLida_2
mov rdx, [strLidaL_2]
syscall
这远不是一个好的解决方案,它会在达到最大字符数时跳转到另一个读取函数,以便它可以吞下剩余的溢出字符。
有一个系统调用吗?有更好的解决方案吗?
感谢您的输入。
您的解决方案一旦推广就非常好。
首先考虑这个C程序
cook.c
#include <stdio.h>
int main()
{
char buffer[200];
scanf("%s", buffer);
return 0;
}
它很脆弱,return
是多余的,但请耐心等待。
该程序只是从输入中读取一个字符串,与您的输入非常相似。
如果你输入像 hello world 这样的短字符串,scanf
会将 hello 读入 buffer
但是 world 不会出现在终端中(与您的程序不同)。
那么 scanf
是如何做到这一点的呢?
无需逆向工程(或获取源代码)即可分析程序的简便方法是 strace。
如果我在我的系统中 运行 strace ./cook
我可以看到 cook
执行 sys_read
系统调用 as
read(0, "hello world\n", 1024) = 12
因此 scanf
简单地以 1024 字节的块读取,在这种情况下。
我不知道 libc 用来设置读取长度的逻辑,因为我认为它在这里不相关,所以我不会深入研究它。
如果我们输入超过 1024 个字符怎么办?
如果我键入 1 2 3 4 ... 1024(即所有 1024 以内的数字由 space 分隔)并按下结果是
manager@debian64-jboss:~$ ./cook
1 2 3 4 5 [... omitted]
manager@debian64-jboss:~$ 284 285 286 287 288 289 290 [... omitted]
显示部分输入进入终端提示。
如果我们进行数学计算,我们将得到预期的 9*2 + 90*3 + 184 * 4 = 1024。
长话短说:您并没有真正遇到问题 - 这是 Linux 下的预期行为。
在你的情况下,它更烦人,因为你读取的字节数很少。
说来话长the input processing mode: canonical or non-canonical.
默认的是 canonical,其中 OS 缓冲文本行以提供输入编辑功能。
如果您的程序要求 5 个字节,并且用户键入 hello world 并按下回车键,OS 将缓冲整个 "hello world\n" 字符串,但 sys_read
将只读到 space,为下一个 reader(shell)留下“world\n”。
您可以选择修复或缓解此问题。
读取更大的尺寸可以缓解问题 - 就像 C 示例一样。
由于您应该始终检查函数或系统调用的 return 值,因此这不会对您的程序布局产生重大影响。
或者,您可以按照 comp.lang.c 的建议阅读所有输入。
在汇编中,您可以使用
以一般方式执行此操作
;edi = file descriptor
emptyfd:
lea rsi, [rsp-80h] ;We use the redzone for the read buffer
mov edx, 80h ;Chunk length
.read_chunk:
xor eax, eax ;sys_read
syscall
;We read all the buffer? (Note: this also check for errors as long as rdx != -1)
cmp rdx, rax
je .read_chunk
ret
当心损坏的寄存器。
我不知道有任何系统调用这样做,但我不希望有任何系统调用 - 标准输入对内核没有特殊意义。
附带说明一下,将寄存器清零的好方法是 。
此外,移动或对 64 位寄存器的低 32 位部分执行操作会将高 32 位归零 - 因此 mov rdi, 1
可以写为 mov edi, 1
.
NASM 无论如何都会将前者隐式转换为后者。
我在 Intel Assembly 上管理 "keyboard overflows" 时遇到问题。 主要问题是在读取了 read 调用指定的最大大小之后,剩余的数据被扔进了终端。 我在 x64 架构上使用 Linux。 这其实就是功课。 我的主要想法如下:
%define maxChars 10
%define maxChars_2 100
section .bss
strLida : resb maxChars
strLidaL : resd 1
read:
mov dword [strLidaL], maxChars
mov rax, 0
mov rdi, 1
mov rsi, strLida
mov rdx, [strLidaL]
syscall
mov [strLidaL], rax
size_compare:
cmp [strLidaL], maxChars
jge overflow
overflow:
mov dword [strLidaL_2], maxChars_2
mov rax, 0
mov rdi, 1
mov rsi, strLida_2
mov rdx, [strLidaL_2]
syscall
这远不是一个好的解决方案,它会在达到最大字符数时跳转到另一个读取函数,以便它可以吞下剩余的溢出字符。 有一个系统调用吗?有更好的解决方案吗? 感谢您的输入。
您的解决方案一旦推广就非常好。
首先考虑这个C程序
cook.c
#include <stdio.h>
int main()
{
char buffer[200];
scanf("%s", buffer);
return 0;
}
它很脆弱,return
是多余的,但请耐心等待。
该程序只是从输入中读取一个字符串,与您的输入非常相似。
如果你输入像 hello world 这样的短字符串,scanf
会将 hello 读入 buffer
但是 world 不会出现在终端中(与您的程序不同)。
那么 scanf
是如何做到这一点的呢?
无需逆向工程(或获取源代码)即可分析程序的简便方法是 strace。
如果我在我的系统中 运行 strace ./cook
我可以看到 cook
执行 sys_read
系统调用 as
read(0, "hello world\n", 1024) = 12
因此 scanf
简单地以 1024 字节的块读取,在这种情况下。
我不知道 libc 用来设置读取长度的逻辑,因为我认为它在这里不相关,所以我不会深入研究它。
如果我们输入超过 1024 个字符怎么办?
如果我键入 1 2 3 4 ... 1024(即所有 1024 以内的数字由 space 分隔)并按下结果是
manager@debian64-jboss:~$ ./cook
1 2 3 4 5 [... omitted]
manager@debian64-jboss:~$ 284 285 286 287 288 289 290 [... omitted]
显示部分输入进入终端提示。
如果我们进行数学计算,我们将得到预期的 9*2 + 90*3 + 184 * 4 = 1024。
长话短说:您并没有真正遇到问题 - 这是 Linux 下的预期行为。
在你的情况下,它更烦人,因为你读取的字节数很少。
说来话长the input processing mode: canonical or non-canonical.
默认的是 canonical,其中 OS 缓冲文本行以提供输入编辑功能。
如果您的程序要求 5 个字节,并且用户键入 hello world 并按下回车键,OS 将缓冲整个 "hello world\n" 字符串,但 sys_read
将只读到 space,为下一个 reader(shell)留下“world\n”。
您可以选择修复或缓解此问题。
读取更大的尺寸可以缓解问题 - 就像 C 示例一样。 由于您应该始终检查函数或系统调用的 return 值,因此这不会对您的程序布局产生重大影响。
或者,您可以按照 comp.lang.c 的建议阅读所有输入。
在汇编中,您可以使用
;edi = file descriptor
emptyfd:
lea rsi, [rsp-80h] ;We use the redzone for the read buffer
mov edx, 80h ;Chunk length
.read_chunk:
xor eax, eax ;sys_read
syscall
;We read all the buffer? (Note: this also check for errors as long as rdx != -1)
cmp rdx, rax
je .read_chunk
ret
当心损坏的寄存器。
我不知道有任何系统调用这样做,但我不希望有任何系统调用 - 标准输入对内核没有特殊意义。
附带说明一下,将寄存器清零的好方法是
此外,移动或对 64 位寄存器的低 32 位部分执行操作会将高 32 位归零 - 因此 mov rdi, 1
可以写为 mov edi, 1
.
NASM 无论如何都会将前者隐式转换为后者。