Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #83, НОЯБРЬ 2005 г.

Секреты покорения эльфов

Крис Касперски ака мыщъх

Хакер, номер #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 ; идентификатор стандартного вывода

Назад на стр. 083-106-4  Содержание  Вперед на стр. 083-106-6