我无法管理"键盘溢出"在英特尔组装。 主要问题是在读取读取调用指定的最大大小后,剩余数据将被丢入终端。 我在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
这远不是一个好的解决方案,它会在最大字符时跳转到另一个读取函数,因此它可以吞下剩余的溢出字符。 有一个系统调用吗?有更好的解决方案吗? 感谢您的投入。
答案 0 :(得分:1)
你的解决方案,一旦推广,就完全没问题了。
首先,考虑一下这个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
系统调用
read(0, "hello world\n", 1024) = 12
因此,在这种情况下,scanf
只能读取1024字节的块
我不知道libc用来设置读取长度的逻辑,因为我不认为它在这里有用,我不会深入研究它。
如果我们输入超过1024个字符怎么办? 如果我输入 1 2 3 4 ... 1024 (即所有数字最多1024个用空格分隔)并按结果
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 并按下输入操作系统将缓冲整个“hello world \ n”字符串,但sys_read
将只读取空间,留下“世界\ n”为下一个读者(壳)。
您可以选择修复或缓解此问题。
读取更大的尺寸可以缓解问题 - 比如C示例。 由于您应该始终检查函数或系统调用的返回值,这不应该对程序布局产生很大影响。
或者,您可以按照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
小心被破坏的寄存器。
我不知道有任何系统调用这样做,我不指望任何 - 标准输入对内核没有特殊意义。
作为旁注,将寄存器归零的好方法是xoring it with itself
此外,在64位寄存器的低32位部分上移动或执行操作会将高32位归零 - 因此mov rdi, 1
可以写为mov edi, 1
。
无论如何,NASM将隐含地将前者转换为后者。