Как избежать отправки stdin-ввода, не помещающегося в буфер, в оболочку в 64-битной сборке Intel (x86-64) Linux

Изменить: заголовок изменен, поскольку @Gunner указал, что это не переполнение буфера.

При чтении пользовательского ввода из stdin с NR_read в 64-битной сборке Intel Linux, мне интересно, как я могу избежать того, чтобы ввод, который не помещается во входной буфер, отправлялся, например, в оболочку Linux. баш? Например, в этом примере программы я определил входной буфер размером 255 байтов (размер буфера может быть любым> = 1). Остальная часть ввода длиной более 255 байт отправляется в bash (если выполняется из bash), и это, очевидно, серьезная уязвимость. Как следует читать ввод в 64-битной сборке Linux, чтобы избежать этой уязвимости?

Вот мой код:

[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

person nrz    schedule 17.09.2012    source источник
comment
Хм? вы передаете input_buffer_length, убедитесь, что он не превышает размер вашего буфера.   -  person J-16 SDiZ    schedule 17.09.2012
comment
@ J-16SDiZ input_buffer_length ограничивает только количество байтов, считываемых в мою программу. Остальная часть ввода поступает в оболочку Linux, например. bash и запускается, и это то, чего я пытаюсь избежать. В DOS большинство вопросов, связанных с клавиатурой, можно решить с помощью перехвата прерывания клавиатуры и использования пользовательского прерывания клавиатуры, но мне кажется, что это неправильный способ делать это в Linux (или это вообще возможно для некорневых программ?) .   -  person nrz    schedule 17.09.2012
comment
О, вы спрашиваете, как очистить остаток пара перед возвращением. Не как ограничить чтение.   -  person J-16 SDiZ    schedule 18.09.2012


Ответы (3)


Это не переполнение буфера, как заявляли другие. Я написал руководство по чтению с терминала в Linux, в котором также показано, как справиться с этой проблемой. Он использует 32-битный Int 0x80, но вы можете легко изменить его под свои нужды.

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

person Gunner    schedule 17.09.2012
comment
Большое спасибо, это решило мою проблему. Я реализовал код очистки входного буфера в моей собственной процедуре чтения и ввода, и он отлично работает. - person nrz; 18.09.2012
comment
Ну, по крайней мере, это работает, но отправлять системный вызов для каждого лишнего символа - неразумно. Увеличенный буфер tmp был бы улучшением, но все же ... вы никогда не знаете, какие данные входят, если используются, например, в конвейере. - person IdiotFromOutOfNowhere; 29.09.2012

Системный вызов чтения уже имеет встроенную защиту. Еще одна вещь: вы не должны явно использовать syscall. Что делать, если ваш код переносится на машину x86-64 (которая использует sysenter)? Вы должны использовать Linux VDSO (виртуальный динамический общий объект), который содержит код для выполнения системных вызовов на всех архитектурах, независимо от того, поддерживают ли они syscall, sysenter или только int.

person Linuxios    schedule 17.09.2012
comment
Это lea rsi,[input_buffer] (Загрузить эффективный адрес, и он точно сохраняет эффективный адрес в регистре), что эквивалентно mov rsi,input_buffer. Я предпочитаю lea, потому что он отмечает, что значение, хранящееся в регистре, является адресом памяти. Кроме того, lea можно использовать в более продвинутых формах адресации, вы можете вычислить rax = rbx+8*rcx+7238 только с одной lea инструкцией: lea rax,[rbx+8*rcx+7238], и, несмотря на синтаксис адресации памяти с [ ], в MMU вычисляется только адрес памяти, но никакая память не адресуется. В x86 [ ] имеют значение только регистры, а не адреса памяти. - person nrz; 17.09.2012
comment
@nrz: Извини! Совершенно забыл про lea. Я избавлюсь от этого. - person Linuxios; 17.09.2012
comment
Если я заменю syscall (который у меня работает нормально, по крайней мере, NR_read, NR_write и NR_exit работают - но с этой проблемой в NR_read) на sysenter, я сразу же получу Segmentation fault, уже до того, как я смогу ввести какие-либо данные в свою программу. Итак, почему-то sysenter вообще не работает. Моя собственная версия ядра - 3.5.3, а мой процессор - Intel Core i7-2760QM. И что вы имеете в виду, говоря, что если ваш код переносится на машину x86-64 (которая использует sysenter)? ? Это машина x86-64. - person nrz; 17.09.2012
comment
@nrz: Правда? Я думал, что только 64-битные процессоры AMD имеют системный вызов, я думал, что только у процессоров Intel есть sysenter. Это странно. А может, это просто x86, у которого есть только sysenter. - person Linuxios; 17.09.2012
comment
Действительно. Поскольку процессоры Intel x86-64 основаны на архитектуре AMD amd64, они также должны иметь syscall, чтобы быть совместимыми. Руководство разработчика программного обеспечения для архитектур Intel 64 и IA-32, объединенные тома: 1, 2A, 2B , 2C, 3A, 3B и 3C в справочнике по набору команд MZ перечисляет как syscall, так и sysenter. Текст на sysenter говорит, что ... программное обеспечение должно указывать сегмент кода уровня привилегий 0 и точку входа в код, а также сегмент стека привилегированного уровня 0 и указатель стека ... На syscall таких требований нет. - person nrz; 17.09.2012
comment
Таким образом, доступны как syscall, так и sysenter, но они не подлежат прямому обмену. Я новичок в 64-разрядной сборке Linux и сборке в защищенном режиме в целом (хотя я знаком с сборкой в ​​реальном режиме DOS x86), поэтому я считаю, что причина sysenter не работает в моем коде в том, что для этого требуется настройка вверх MSR адреса 0x174, 0x175 и 0x175 соответствующим образом (согласно Intel 325462.pdf, ссылка выше), что я не сделал в моем коде. С другой стороны, syscall, похоже, требует только правильных параметров, поэтому его проще использовать. - person nrz; 17.09.2012
comment
@nrz: ядро ​​должно настраивать эти msrs. Я действительно думал, что Linux умеет. Но вы все равно должны использовать vdso для своих системных вызовов. - person Linuxios; 17.09.2012

Вы можете читать ввод, пока не будет найден символ новой строки.

person Juho    schedule 17.09.2012