Секреты покорения эльфов Крис Касперски ака мыщъх Хакер, номер #083, стр. 083-106-5 А теперь займемся более серьезными вещами, попытавшись внедрить в программу реальный код, который делает что-то полезное. Сразу возникает вопрос: куда мы будем внедряться? Между сегментами свободного места нет, между секциями тоже. Можно (теоретически) расширить последний сегмент и внедрится сюда, но, во-первых, это будет слишком заметно, а во-вторых, довольно сложно. Но все не так плохо, как кажется! По умолчанию gcc выравнивает стартовые адреса функций по границе 10h, а это значит, что даже наш демонстрационный файл содержит просто кучу свободного пространства. В среднем 10h/2h = 8h байт на каждую функцию, включая служебные. Сюда и мамонта упрятать можно, если, конечно, его предварительно расчленить. Цепочка команд NOP, оставленная компилятором в конце функции main для выравнивания ....... ! main: ;xref o80482d7 ....... ! push ebp 8048385 ! mov ebp, esp 8048387 ! sub esp, 8 804838a ! and esp, 0fffffff0h 804838d ! mov eax, 0 8048392 ! sub esp, eax 8048394 ! mov dword ptr [esp], strz_LORDI___the_best_group_in_the_80484e0 804839b ! call wrapper_8049634_80482b0 80483a0 ! leave 80483a1 ! ret 80483a2 nop ....... 80483ac nop А вот еще одна лазейка - буфер ввода/вывода, расположенный в сегменте данных, дамп которого приведен ниже. Это целых 28 байт, которые можно использовать по своему усмотрению! Даже если никаких явных файловых манипуляторов в файле нет (как, например, в нашей демонстрационной программе), такой буфер все равно создается при компиляции программы, что наш случай и подтверждает. STDIN-буфер, расположенный в сегменте данных 80484c2 db 00h ; ' ' 80484c3 db 00h ; ' ' 80484c4 ....... ;******************************************************** ....... ; data object _IO_stdin_used, size 4 (global) ....... ;******************************************************** ....... _IO_stdin_used: ....... db 01h ; ' ' 80484c5 db 00h ; ' ' 80484c6 db 02h ; ' ' ....... 80484de db 00h ; ' ' Остается решить, как передать управление на внедренный код. Это можно сделать различными путями: скорректировать точку входа (HTE это умеет) или внедрить в ее окрестности специальный jmp. Вот так мы и поступим! Запускам редактор, переходим в точку входа и смотрим на нее очень внимательно: Точка входа и ее окрестности ....... ! entrypoint: ....... ! pop esi 80482c1 ! xor ebp, ebp 80482c3 ! mov ecx, esp 80482c5 ! and esp, 0fffffff0h 80482c8 ! push eax 80482c9 ! push esp 80482ca ! push edx 80482cb ! push __libc_csu_fini 80482d0 ! push __libc_csu_init 80482d5 ! push ecx 80482d6 ! push esi 80482d7 ! push main 80482dc ! call wrapper_8049630_80482a0 80482e1 ! hlt 80482e2 ! nop Почему бы нам не заменить pop esi/xor ebp,ebp на «jmp на наш код», откуда мы сможем сделать все, что задумано, выполнить эти команды и вернуться обратно? Но для начала необходимо подготовить код, который мы будем внедрять. Для простоты выведем короткое приветствие на экран. На языке ассемблера это звучит приблизительно так: Исходная программа, выводящая приветствие на экран mov eax, 4 ; системный вызов write mov ebx,1 ; идентификатор стандартного вывода |