Примеры реальных взломов

Вид материалаДокументы

Содержание


таинства stealth импорта API-функцийили как устроена HaronLoadLibrary
Листинг 76 содержимое памяти по адресу 44B004h
001B:00446764 movzx ebp,dx
Подобный материал:
1   ...   5   6   7   8   9   10   11   12   13

таинства stealth импорта API-функций
или как устроена HaronLoadLibrary


И вот наконец-то мы и добрались до того самого острова, ради которого и затеяли все "мореплавание". Главная вкусность исследуемой программы заключается в том, что она самостоятельно находит необходимые ей API  функции в памяти, обходясь без таблицы импорта. Хотите узнать: как именно все происходит и декомпилировать защитный алгоритм в удобочитаемый код, который можно использовать и своих собственных разработках? (или на худой конец хвататься своей крутизной перед приятелями - программистам). Если так, то мы начинаем, и начинам мы с того, что содержимое регистра EAX, используемое в качестве базового указателя инструкцией "LEA ESI, [EAX + 9371h]", равно содержимому двойного слова, расположенного по адресу EBX (см. листинг $-2), а сам EBX в свою очередь равен 4460C0h (см. листинг $-6), короче в ESI грузится указатель на 44B004h. Смотрим, что у нас расположено по этому адресу?


.data:0044B004 01 00 00 00 dd 1

.data:0044B008 FF FF FF FF dd 0FFFFFFFFh

.data:0044B00C F6 02 00 00 dd 2F6h

.data:0044B010 14 B0 04 00 dd 4B014h

Листинг 76 содержимое памяти по адресу 44B004h

Теперь становится понятно, что очередная машинная команда – команда LODSD помещает в регистр EAX единицу и сдвигает ESI на следующее двойное слово в списке. Сам EAX незамедлительно пересылается на долговременное хранение в регистр EBP, а в EAX загружается FFFFFFFFh или "минус единица" в знаковом представлении. Впрочем, в EAX она долго не задерживается и содержимое регистров EAX и EBP вскоре меняются местами, а затем и вовсе суммируются в единое целое, равное как и следовало ожидать нулю. Далее следует безумная проверка флага нуля и прыжок куда-то вглубь кода если Zero Flag не установлен. Смысл происходящего не совсем ясен (во всяком случае если в нем как следует не разбираться), однако, поскольку никакого влияния на последующий код эти махинации с регистрами не оказывают, пропустим их на фиг.

А вот следующая пара машинных команд заслуживает самого пристального рассмотрения: MOV EBP, DX/BSWAP EBP. На самом деле это лишь кусочек мозаики, а остальные рассеяны по предыдущим листингам и полная картина выглядит так: MOV EAX, FS:[0]/MOV EDX, [EAX.handle]/BSWAP EDX/MOV EBP, DX/BSWAP EBP В переводе на человеческий язык это звучит приблизительно так: запихнуть в EBP старшее слово адреса оригинального обработчика структурных исключений, установленного операционной системой. Зачем он понадобился Харону? А вот зачем – это ключ к базовому адресу загрузки KERNEL32.DLL. Опытные хакеры наверняка знают, что: а) оригинальный обработчик всегда принадлежит KERNEL32.DLL (правда, автору известны некоторые антивирусные пакеты, которые для каких-то своих целей подменяют оригинальный загрузчик на свой еще на стадии загрузки программы); б) базовые адреса загрузки динамических библиотек кратных 1000h.


001B:00446753 JZ 00446774 ; \

001B:00446755 LEA ESI,[EAX+00009371] ; |

001B:0044675B LODSD ; |

001B:0044675C MOV EBP,EAX ; |

001B:0044675E LODSD ; +- мусор

001B:0044675F XCHG EAX,EBP ; |

001B:00446760 ADD EAX,EBP ; |

001B:00446762 JNZ 00446790 ; /

001B:00446764 MOVZX EBP,DX

001B:00446767 BSWAP EBP

Листинг 77 EBP := HIWORD(FS:[0].handle) получение старшего слова адреса оригинального обработчика структурных исключений, принадлежащего KERNEL32.DLL; мусорные команды залиты темно серым цветом, а значимые – инверсным.

Таким образом, коль скоро по крайней мере один байт, принадлежащий KERNEL32.DLL нам известен, мы можем найти и все остальные. Признаться, в первый момент я подумал, что необходимые для работы линкера API-функции Харон ищет в памяти по их сигнатурам. Способ простой, но в плане процессорных ресурсов весьма недешевый. Однако, Юрий заверил меня, что это не так, – тут-то любопытно и сыграло (почему вы думаете я его защиту вообще ломать стал?!).

Алгоритм поиска API – функций в памяти становится предельно ясен с первого же взгляда на следующую машинную команду "CMP word prt [EBP + 0], 5A4D". Даже начинающие ломатели наверняка распознали в константе 5A4Dh знаменитую сигнатуру "MZ", красующуюся в начале каждого EXE-файла (Mark Zbinovsky то бишь). Если же текущее слово не есть "MZ", то Харон уменьшает значение регистра EBP на одну машинную страницу (SUB EBP, 1000h) и выполняет проверку опять. Так продолжается до тех пор, пока искомая последовательность не будет найдена.

Затем в регистр EAX загружается значение ячейки, расположенной по адресу [EBP + 3C] (указатель на начало PE-заголовка исполняемого файла) и выполняется дополнительная проверка на PE-сигнатуру – "CMP dword ptr [EAX+EBP], 4550h" (4550h в строковом представлении "PE" и есть). Если и эта проверка прошла успешно, базовый адрес загрузки динамической библиотеки KERNEL32.DLL считается найденным.

Для определения адреса API – функции теперь остается лишь вызвать функцию HaronGetProcAddress, которой, функция sub_44658D судя по всему и является. ОК, предположим, что так, но тогда функция HaronGetProcAddress должна принимать в качестве аргумента указатель на имя (оридинал) этой функции ну, или на крайний случай, ее сигнатуру (хотя, если верть Харону, функции импортируются не по сигнатурам).

Перед вызовом sub_44658D явным образом инициализируются всего три регистра: 1) регистр ESI, принимающий прежнее значение регистра EBX (4460C4h), и после считывания двойного слова по LOADSD смещающийся на четыре байта вперед; 2) регистр EAX, аккумулирующий значение этого самого двойного слова (4334F9h); 3) регистр EBX, равный сумме обновленного регистра EAX и константы 19713h, что в результате дает 44СС0Сh и приходится на неинициализированную область памяти.

Итого, в явном виде имен API – функций нет, ничего хотя бы отдаленно похожего на их оридналы тоже нет, а вот содержимое области памяти на которую указывает регистр ESI вполне может быть расценено как сигнатура. Как узнать что есть что наверняка? Естественно, проанализировать алгоритм функции sub_44658Dh и тогда все станет более или менее ясно.


001B:00446769 XOR CL,09

001B:0044676C CMP WORD PTR [EBP+00],5A4D <*******

001B:00446772 JNZ 00446781 -->---[это не MZ]-~ *

001B:00446774 MOV EAX,[EBP+3C] | *

001B:00446777 CMP DWORD PTR [EAX+EBP+0],4550 | *

001B:0044677F JZ 00446790 ########## | *

001B:00446781 SUB EBP,00010000 <----#--------~ *

001B:00446787 LOOP 0044676C *********#************

001B:00446789 XOR ECX,ECX #

001B:0044678B CALL 004466B1 #

001B:00446790 XCHG ESI,EBX <<<########

001B:00446792 LODSD

001B:00446793 LEA EBX,[EAX+00019713]

001B:00446799 CALL 0044658D ; HaronGetProcAddress

001B:0044679E JAE 00446789

Листинг 78 ядро функции stealth загрузки библиотеки Kernel32.dll, определяющее базовый адрес ее загрузки путем поиска сигнатур "MZ" и "PE" в оперативной памяти (заливкой выделена логическая структура кода)


:d esi

0023:004460C4 F9 34 43 00 D5 89 ED 2C-8C 89 2D 4C 4E 2C 4E 2F .4C....,..-LN,N/

0023:004460D4 28 C9 D7 E8 AC 8E 0A 4E-ED 6C 28 8C 8C 4E AC 6E (......N.l(..N.n

0023:004460E4 6E DA 29 6E 88 AC 4C AE-EC EC AC 4E 0A 4E AC 6E n.)n..L....N.N.n

0023:004460F4 AC CD 8E C9 D4 68 8D ED-6E AC 09 2C CD 8C 8D AC .....h..n..,....

Листинг 79 cодержимое области памяти передаваемое функции sub_44658Dh