2012-09-17 14 views
4

编辑:标题已更改,因为@Gunner指出,这不是缓冲区溢出。如何避免不适合缓冲区的stdin输入被发送到Linux 64位Intel(x86-64)程序集的外壳程序

在从stdin读取用户输入与在Linux 64位Intel汇编NR_read,我不知道如何能够避免不适合在输入缓冲器中的输入被发送到Linux外壳例如。庆典?例如,在这个示例程序中,我定义了一个255字节的输入缓冲区(缓冲区的大小可以是> = 1)。长度超过255字节的输入的其余部分被发送到bash(如果从bash运行)并且这显然是一个严重的漏洞。如何在Linux 64位程序集中读取输入以避免此漏洞?

这里是我的代码:

[bits 64] 

section .text 
global _start 

; can be compiled eg. with nasm or yasm. 
; nasm: 
; nasm -f elf64 read_stdin_64.asm; ld read_stdin_64.o -o read_stdin_64 
; yasm: 
; yasm -f elf64 -m amd64 read_stdin_64.asm -o read_stdin_64.o; ld read_stdin_64.o -o read_stdin_64 

NR_read  equ 0 
NR_exit  equ 60 

STDIN  equ 1 

; input: 
; rax number of syscall 
; rdi parameter 1 
; rsi parameter 2 
; rdx parameter 3 
; r10 parameter 4 
; r8 parameter 5 
; r9 parameter 6 
; 
; output: 
; rax syscall's output 
@do_syscall: 
    push rcx 
    push r11 
    syscall  ; 64-bit syscall, overwrites rcx and r11 
    pop  r11 ; syscall's return value in rax 
    pop  rcx 
    ret 

@read_stdin: 
    push rdi 
    push rsi 
    push rdx 
    mov  rdi,STDIN    ; file handle to read. STDIN = 1. 
    lea  rsi,[input_buffer] 
    mov  rdx,input_buffer_length ; length of string 
    mov  rax,NR_read    ; number of syscall (0) 
    call @do_syscall 
    sub  rax,1     ; get the number of writable characters. 
    pop  rdx 
    pop  rsi 
    pop  rdi 
    ret 

_start:  ; linker entry point 
    call @read_stdin 

@end_program: 
    xor  rdi,rdi 
    mov  rax,NR_exit ; number of syscall (60) 
    syscall 

section .data 

input_buffer   times 255 db 0 
input_buffer_length equ $-input_buffer 
+0

呵呵?你传递'input_buffer_length',你确定它不会超过你的缓冲区大小。 –

+0

@ J-16SDiZ'input_buffer_length'只限制读入我的程序的字节数。其余的输入到Linux shell例如。 bash并得到执行,这就是我想要避免的。在DOS中,键盘上的大多数事情都可以通过钩住键盘中断和使用自定义键盘中断来处理,但在我看来,这不是在Linux中正确执行的方法(或者甚至可能用于非root程序?) 。 – nrz

+0

哦,你问在返回之前如何清除剩余的蒸汽。不是如何限制阅读。 –

回答

3

这不是像其他人所说的缓冲区溢出。我在Linux的终端上写了一篇关于阅读的教程,其中还展示了如何处理这个问题。它使用Int 80,但您可以轻松更改以符合您的需求。

http://www.dreamincode.net/forums/topic/286248-nasm-linux-terminal-inputoutput-wint-80h/

+0

非常感谢,这解决了我的问题。我在自己的读取输入例程中实现了输入缓冲区刷新代码,并且它工作得很好。 – nrz

+0

那么,至少它是有效的,但是为每个多余的角色调度系统调用 - 这听起来不合理。一个更大的tmp缓冲区可能是一个增强功能,但仍然......如果使用了某个数据,例如在管道中,则永远不会知道哪些数据会进入。 – IdiotFromOutOfNowhere

+0

一直在寻找这一整天,谢谢。 – divinci

0

直到换行符发现,你可以读取输入。

3

读取系统调用已经具有内置的保护功能。但有一件事是:您不应该明确使用syscall。如果您的代码被带到x86-64机器(使用sysenter)会怎么样?您应该使用Linux的VDSO(虚拟动态共享对象),它包含在所有体系结构上执行系统调用的代码,无论它们是支持syscallsysenter还是仅支持int

+0

它是''''''''''''输入缓冲区''(负载有效地址,它精确地将有效地址存储在一个寄存器中),相当于'mov rsi,input_buffer'。我更喜欢'lea',因为它标志着存储在寄存器中的值是一个内存地址。此外,'lea'可以用于更高级的寻址形式,你可以只用一个'lea'指令计算'rax = rbx + 8 * rcx + 7238':'lea rax,[rbx + 8 * rcx + 7238] ,尽管使用'[]'的内存寻址语法,只有内存地址在MMU中计算,但没有寻址内存。在x86'[]中'只与寄存器有关,而不与内存地址有关。 – nrz

+0

@nrz:对不起!完全忘了'lea'。我会摆脱这一点。 – Linuxios

+0

如果我用'sysenter'替换'syscall'(对我来说工作正常,至少NR_read,NR_write和NR_exit工作 - 但在NR_read中有这个问题),我会立即得到'Segmentation fault',我可以输入任何输入我的程序内。所以,由于某种原因'sysenter'根本不起作用。我的定制内核版本是3.5.3,我的处理器是Intel Core i7-2760QM。 “你的意思是”如果你的代码被带到一台x86-64机器(它使用'sysenter')?“? ?这是一台x86-64机器。 – nrz

相关问题