X86, Ubuntu Здравствуйте!) Ребят, недавно столкнулся с проблемой, она связана с исполнением шеллкода в программе на Си. Я написал(если честно, то скопировал) простенькую программу на асме, получил шестнадцатеричный код objdump'ом(нуль-байтов нет) и вставил в программу на Си. По сути программа должна выводить на экран строку. Программа, написанная на асме полностью выполняет то, что предполагалось. Но Си-программа, которая должна выполнить шестнадцатеричный код asm-программы завершается с ошибкой "segmentation fault". ASM: ________________ [SECTION .text] global _start _start: jmp short ender starter: xor eax, eax xor ebx, ebx xor edx, edx xor ecx, ecx mov al, 4 mov bl, 1 pop ecx mov dl, 18 int 0x80 xor eax, eax mov al, 1 xor ebx, ebx int 0x80 ender: call starter db 'This is string' _______________ Cи: #include <stdio.h> char code[] = "\xeb\x19\x31\xc0\x31\xdb\x31\xd2\x31\xc9" "\xb0\x04\xb3\x01\x31\xc9\xb0\x04\xb3\x01" "\x59\xb2\x12\xcd\x80\x31\xc0\xb0\x01\x31\xdb" "\xcd\x80"; int main(int argc, char *argv[]) { int (*shell)(); shell = (int (*) () ) code; (int)(*shell)(); } _________________________- Помогите пожалуйста разобраться. Большое спасибо.
С первого взгляда могу предположить, что ты вызываешь шелл-код в Си как функцию, но на самом деле по адресу code находится не функция, а последовательность ассемблерных инструкций без пролога и эпилога. Во-вторых, ассемблерный код не должен быть базозависимым, а он такой и есть у тебя, потому что он осуществляет переходы по меткам (starter, ender), которые, скорее всего, компилируются в переходы по абсолютным адресам, а те, в свою очередь, в сишной программе уже недействительны.
Code: int main(int argc, char *argv[]) { unsigned char code[] = "\xeb\x19\x31\xc0\x31\xdb\x31\xd2\x31\xc9" "\xb0\x04\xb3\x01\x31\xc9\xb0\x04\xb3\x01" "\x59\xb2\x12\xcd\x80\x31\xc0\xb0\x01\x31\xdb" "\xcd\x80"; __asm { lea eax, code call eax } }
> Синтаксис инлайн-асма Intel для Visual Studio или MASM под Windows > Автор просит под Ubuntu (gcc поддерживает только синтаксис AT&T), и его шелл-код с линуксовыми прерываниями > Ответ, видимо, даже не проверен и даже не скомпилирован под *nix
VY_CMa ты когда начал на ассемблере кодить? По теме: Компилируй NASM: nasm -f bin -o data.bin myfile.asm Только после [section .text] добавь строчку use32 А строку call starter замени call near starter Потом его хекс редактором открываешь и копируешь в стиле С и вставляешь. Вызов в С правильный, но только в этом случае, т.к. ассемблекный код вызывает в конце exit, и обработка стека особо не важна. А по хорошему надо соблюдать stdcall А вообще поищи ссылочку на shellstorm
https://en.wikipedia.org/wiki/Executable_space_protection Помимо прочего секция данных скорее всего не исполняема. Нужно выделять кусок виртуальной памяти, ставить ей права на исполнение и передавать управление туда.
Угу... то есть Си-программа пытается перейти по адресу, которого вообще нет? На shellstorm'e я посмотрел исходники - ты прав, ни сегментов, ни меток там действительно нет и код выполняется прекрасно. Все строки передаются через стек, а затем в регистры. В общих чертах я понял, спасибо.
третий параметр отвечает за права участка памяти. Обьединяешь PROT_READ|PROT_WRITE|PROT_EXEC и можешь читать/писать/исполнять данные в этом участке памяти. p.s. в man 2 .... внизу есть пример p.p.s. первый параметр это не адрес твоей функции которую ты вызываешь, это начала участка памяти выделенного под данные, он выравнен на границу 4кб вроде, но точно не помню.
Спасибо. Только теперь возникла другая проблема: не могу разместить код в выделенном участке памяти. Если размещаю до вызова mprotect, то сама функция mprotect не выполняется и выкидывает ошибку "invalid argument". Если размещаю после вызова mprotect, то данные выполняться не хотят. Проблема с "неисполняемостью" решилась заменой шеллкода(брал на shellstorm'е), но хочется понять, почему не работает mprotect. Почему в первом случае он сообщает о неверном аргументе, а во втором шеллкод просто не перводится в машинные команды? Флаги PROT_READ, PROT_WRITE и PROT_EXEC включены.
Моя оплошность, каюсь. Собственно код: #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> #include <string.h> #include <errno.h> int main(int argc, char *argv[]) { permissions(); return 0; } int permissions(char *addr) { int page_size = getpagesize(); addr -= (unsigned long)addr % page_size; addr = "\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3" "\x50\x53\x89\xe1\xb0\x0b\xcd\x80"; if(mprotect(addr, page_size, PROT_READ|PROT_WRITE|PROT_EXEC) == -1) { perror("Can't to run mprotect\n"); return -1; } int (*exeshell)(); exeshell = (int (*) () )addr; (int)(*exeshell)(); } Догадываюсь, что проблема может быть в неверном выравнивании.
первое, что бросается в глаза: 1) вызов без параметров permissions() - в функции addr = (char *)0x00 2) присвоение указателя: addr = "\x32\xc0..." тут ты присвоил указателю addr новый адрес который указывает на статическую строку находящуюся по другому адресу, тогда предыдущее выравнивание было бесполезно. Вот так должно завестись: Code: #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> #include <string.h> #include <errno.h> char *shellcode = "\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"; int main(int argc, char *argv[]) { permissions(shellcode); return 0; } int permissions(char *addr){ int page_size = getpagesize(); char *addr_align = (char *)addr - ( (u_long)addr % page_size ); if( -1 == mprotect( addr_align, page_size, PROT_WRITE|PROT_READ|PROT_EXEC ) ){ perror("mprotect:"); exit(0); } int (*shell)(); shell = int (*)()addr; shell(); } p.s.
Спасибо большое дружище, завтра проверю на свежую голову и зароюсь в документации. А еще вопрос: если по умолчанию стек является неисполняемым, то данные в специально выделенной области памяти с флагом PROT_EXEC могут выполняться?