Создать массив arg для execve в стеке - PullRequest
2 голосов
/ 20 января 2020

Я хочу написать программу сборки, которая выполняет через EXECVE (системный вызов # 0x3 C) программу / bin / ls с ключами -al.

Страница man (man 2 execve) утверждает, что вызов требует три значения:

int execve(const char *filename, char *const argv[], char *const envp[]);

Я не совсем понимаю, как построить три аргумента. Насколько я знаю, первый аргумент входит в RDI, второй в RSI, а третий в RDX. Я считаю, что для настройки первого достаточно сделать

    push 0x736c2f2f         ;sl//
    push 0x6e69622f         ;nib/
    mov rdi, rsp

Для третьего достаточно просто:

    xor r11, r11
    mov rdx, r11

Моя проблема в том, что я не знать, как создать второй аргумент, который должен быть массивом, содержащим ['/bin//ls', '-aal']

Мне нужно написать его для x86-64, поэтому, пожалуйста, int 0x80 предложений.

Ответы [ 3 ]

4 голосов
/ 20 января 2020

Вы можете поместить массив argv в стек и загрузить его адрес в rsi. Первый член argv является указателем на имя программы, поэтому мы можем использовать тот же адрес, который мы загружаем в rdi.

xor edx, edx        ; Load NULL to be used both as the third
                    ; parameter to execve as well as
                    ; to push 0 onto the stack later.
push "-aal"         ; Put second argument string onto the stack.
mov rax, rsp        ; Load the address of the second argument.
mov rcx, "/bin//ls" ; Load the file name string
push rdx            ; and place a null character
push rcx            ; and the string onto the stack.
mov rdi, rsp        ; Load the address of "/bin//ls". This is
                    ; used as both the first member of argv
                    ; and as the first parameter to execve.

; Now create argv.
push rdx            ; argv must be terminated by a NULL pointer.
push rax            ; Second arg is a pointer to "-aal".
push rdi            ; First arg is a pointer to "/bin//ls"
mov rsi, rsp        ; Load the address of argv into the second
                    ; parameter to execve.

Это также исправляет несколько других проблем с кодом в вопросе. В качестве имени файла используется 8-байтовая переменная pu sh, поскольку x86-64 не поддерживает 4-байтовую переменную pu sh и гарантирует, что имя файла имеет нулевой терминатор.

Этот код использует 64-битную переменную pu sh с 4-байтным значением, ближайшим к pu sh "-aal", поскольку строка помещается в 4 байта. Это также делает его нулевым завершением без необходимости использовать нулевой байт в коде.

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

mov ecx, "X-al"     ; Load second argument string,
shr ecx, 8          ; shift out the dummy character,
push rcx            ; and write the string to the stack.
mov rax, rsp        ; Load the address of the second argument.
mov rcx, "X/bin/ls" ; Load file name string,
shr rcx, 8          ; shift out the dummy character,
push rcx            ; and write the string onto the stack.

Обратите внимание, что строка имени файла получает нулевой терминатор через сдвиг, избегая лишних pu sh. Этот шаблон работает со строками, где двойной символ не будет работать, и его можно использовать и с более короткими строками.

1 голос
/ 20 января 2020

Вы можете написать push '/bin' в NASM, чтобы получить байты в памяти в указанном порядке. (Заполняется 4 байтами нулей, для общей ширины qword; в 64-битном режиме выталкивание dword невозможно.) Нет необходимости возиться с ручным кодированием символов ASCII; в отличие от некоторых ассемблеров, NASM не сосет многосимвольные литералы и может облегчить вашу жизнь.

Используйте mov dword [rsp+4], '//ls' для хранения старшей половины. (Или сделайте хранилище слов, чтобы записать еще 4 байта нулей после этого, с mov r/m64, sign_extended_imm32. Или просто обнулить его с помощью другого pu sh.

или mov eax, '//ls'; shl eax, 8 чтобы получить EAX = "/ls\0" в регистре, готовом к сохранению, для создания 8-байтовой строки с нулем в конце.

Или использовать тот же трюк для перемещения байта после mov r64, imm64 (как в ответе @ prl ) вместо отдельных pu sh / mov. Или НЕ ваших литеральных данных, поэтому вы делаете mov rax, imm64 / not rax / push rax, производя нули в вашем регистре без нулей в машинном коде.

0 голосов
/ 20 января 2020

Загрузите следующий пример C (при необходимости измените) в проводник компилятора Godbolt, и вы увидите, как различные компиляторы обычно генерируют сборку для вызова execve в архитектуре AMD64 (или другой).

#include <stdio.h>
#include <unistd.h>

int 
main(void) {
   char* argv[] = { "/bin/ls", "-al", NULL };
   // char* argv[] = { "-al", NULL };
   // char* argv[] = { "/bin/lsxxx", "-al", NULL };
   // char* argv[] = { "", "-al", NULL };
   char* envp[] = { "PATH=/bin", NULL };

   if (execve("/bin/ls", argv, envp) == -1) {
      perror("Could not execve");
      return 1;
   }  
}
...