Проходя через http://hackoftheday.securitytube.net/2013/04/demystifying-execve-shellcode-stack.html
Я понял программу Nasm, которая вызывает execve
и пытался переписать это.
Некоторая справочная информация:
int execve(const char *filename, char *const argv[], char *const envp[]);
Так, eax = 11
(номер вызова функции для execve
), ebx
следует указать на char* filename
, ecx
следует указать на argv[]
(который будет таким же, как ebx
так как первый аргумент является *filename
сам, например «/ bin / sh» в этом случае) и edx
будет указывать на envp[]
(null
в этом случае).
Оригинальный код носа:
global _start
section .text
_start:
xor eax, eax
push eax
; PUSH //bin/sh in reverse i.e. hs/nib//
push 0x68732f6e
push 0x69622f2f
mov ebx, esp
push eax
mov edx, esp
push ebx
mov ecx, esp
mov al, 11
int 0x80
Стек выглядит следующим образом:
Теперь я попытался оптимизировать это, уменьшив несколько инструкций. Я согласен, что до mov ebx, esp
код останется прежним. Тем не менее, так как ecx
нужно будет указать на ebx
Я могу переписать код следующим образом:
global _start
section .text
_start:
xor eax, eax
push eax
; PUSH //bin/sh in reverse i.e. hs/nib//
push 0x68732f6e
push 0x69622f2f
mov ebx, esp
mov ecx,ebx
push eax
mov edx, esp
mov al, 11
int 0x80
Тем не менее, я получаю ошибку сегментации, когда я запускаю свой переписанный код.
Мой стек выглядит следующим образом:
Есть идеи, почему переписанный код не работает? Я также запустил GDB, и значения адресов соответствуют моим представлениям, но он просто не запускается.
В обоих случаях ebx указывает на строку «// bin / sh». Эквивалент кода C, подобный этому:
char *EBX = "//bin/sh";
Но в вашем первом примере ecx установлен на адрес указателя на эту строку. Эквивалент кода C, подобный этому:
char *temp = "//bin/sh"; // push ebx
char **ECX = &temp; // mov ecx, esp
В то время как во втором примере для ecx установлено то же значение, что и для ebx.
char *ECX = "//bin/sh";
Таким образом, два примера принципиально различны, поскольку ecx имеет два совершенно разных типа и значения.
Обновить:
Я должен добавить, что технически ecx — это массив указателей на символы ( ARGV аргумент), а не просто указатель на указатель на символ. Вы фактически строите массив из двух элементов в стеке.
char *argv[2];
argv[1] = NULL; // push eax, eax being zero
argv[0] = "//bin/sh"; // push ebx
ECX = argv; // mov ecx,esp
Просто половина этого массива удваивается как envp аргумент тоже. поскольку envp является массивом из одного элемента, для которого этот элемент имеет значение NULL, вы можете думать о envp аргументы устанавливаются с помощью кода C следующим образом:
EDX = envp = &argv[1];
Это достигается установкой edx в esp, в то время как массив argv создается только наполовину. Комбинируя код для двух назначений вместе, вы получаете это:
char *argv[2];
argv[1] = NULL; // push eax, eax being zero
EDX = &argv[1]; // mov edx,esp
argv[0] = "//bin/sh"; // push ebx
ECX = argv; // mov ecx,esp
Это немного запутанно, но я надеюсь, что это имеет смысл для вас.
Обновление 2
Все аргументы execve
передаются как регистры, но эти регистры являются указателями на память, которую необходимо где-то разместить — в данном случае в стеке. Поскольку стек строится вниз в памяти, куски памяти должны быть построены в обратном порядке.
Память для трех аргументов выглядит следующим образом:
char *filename: 2f 2f 62 69 | 6e 2f 73 68 | 00 00 00 00
char *argv[]: filename | 00 00 00 00
char *envp[]: 00 00 00 00
имя файла построен так:
push eax // '\0' terminator plus some extra
push 0x68732f6e // 'h','s','/','n'
push 0x69622f2f // 'i','b','/','/'
ARGV аргумент как это:
push eax // NULL pointer
push ebx // filename
И envp аргумент как это:
push eax // NULL pointer
Но, как я уже сказал, оригинальный пример решил разделить память между argv и evp, так что в этом нет необходимости push eax
,
Я также должен отметить, что обратный порядок символов в двух словах, используемых при построении строки, связан с порядком байтов машины, а не с направлением стека.
Других решений пока нет …