Как вернуть вывод системного вызова execve в регистр или стек после его выполнения на ассемблере?

Я пишу на ассемблере и пытаюсь понять, как выполнить системный вызов execve, но вместо того, чтобы распечатывать вывод на терминал, я хотел бы знать, где он хранится, чтобы я мог использовать его позже, вроде как конвейерные команды.

Например, вот сборка для выполнения команды 'what' через execve, по сути выполняющая команду '$ which ls':

GLOBAL _start
SECTION .TEXT

_start:
    XOR         EAX,EAX
    PUSH        EAX
    PUSH        0x68636968 
    PUSH        0x772f6e69 
    PUSH        0x622f7273 
    PUSH        0x752f2f2f 
    MOV         EBX, ESP
    PUSH        EAX
    PUSH        0x736c
    MOV         ESI, ESP
    XOR         EDX, EDX
    PUSH        EDX
    PUSH        ESI
    PUSH        EBX
    MOV         ECX, ESP
    MOV         AL, 0x0B; EXECVE SYSCALL NUMBER
    INT         0x80

Строки 7-10 помещают в стек адрес /usr/bin/which, а строка 13 помещает в стек аргумент ls. Затем он помещает массив аргументов в стек и сохраняет его в ECX, где EBX указывает на адрес местоположения /usr/bin/which, а EAX установлен на номер системного вызова 0xb (11) для системного вызова execve. При выполнении он возвращает /bin/ls, местоположение ls, которое мы просили его найти.

Как мне сохранить этот результат /bin/ls где-нибудь для другого использования? Например, если бы я хотел продолжать писать код и использовать возвращаемое здесь как часть следующего фрагмента кода, как мне сохранить возвращаемое значение либо в регистре, либо в стеке?


person Joshua Campbell    schedule 02.06.2017    source источник
comment
Возможный дубликат перенаправления вывода exec в буфер или файл.   -  person Mark Plotnick    schedule 02.06.2017
comment
execve не возвращается до тех пор, пока не произойдет сбой.   -  person stark    schedule 02.06.2017


Ответы (1)


По сути, вам нужно сделать несколько вариантов этого:

  1. Используйте системный вызов pipe() для создания fifo.
  2. fork() вне дочернего процесса. В родительском процессе это возвращает идентификатор дочернего процесса, который вам нужно запомнить. В дочернем элементе это возвращает ноль.
  3. В родительском закройте конец записи fifo, в дочернем закройте конец чтения.
  4. В дочернем элементе используйте dup2(), чтобы переместить конец записи канала в файловый дескриптор 1. Затем закройте исходный файловый дескриптор канала.
  5. В дочернем execve программа, которую вы хотите запустить. Обратите внимание, что execve заменяет текущий процесс новым изображением, поэтому я не уверен, как вы ожидали, что ваша предыдущая программа будет работать.
  6. В родительском элементе читайте из канала, пока не встретите EOF (т. е. read() возвращает ноль). То, что вы читаете, является стандартным выводом ребенка. Сохраните этот вывод в буфере.
  7. Наконец, используйте wait() для получения статуса выхода дочернего элемента. Если вы этого не сделаете, мертвый ребенок задержится в таблице процессов как зомби, забирая ресурсы.

Вместо канала вы также можете open() файл для записи вывода. Это имеет то преимущество, что вы можете использовать fstat(), чтобы узнать, сколько вывода написал процесс. Однако затем вам нужно реализовать логику, аналогичную функции tmpfile, для создания и открытия временного файла.

person fuz    schedule 02.06.2017