Intel Assembly中的键盘缓冲区

时间:2017-06-01 03:06:03

标签: linux assembly keyboard 64-bit intel

我无法管理"键盘溢出"在英特尔组装。 主要问题是在读取读取调用指定的最大大小后,剩余数据将被丢入终端。 我在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

这远不是一个好的解决方案,它会在最大字符时跳转到另一个读取函数,因此它可以吞下剩余的溢出字符。 有一个系统调用吗?有更好的解决方案吗? 感谢您的投入。

1 个答案:

答案 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将隐含地将前者转换为后者。