Примеры реальных взломов
Вид материала | Документы |
- Учебник: / Страницы: , 162.62kb.
- В. А. Кулаков московский инженерно-физический институт (государственный университет), 28.6kb.
- И. Закарян И. Филатов, 3215.74kb.
- Задачи : Формирование выборки стран, сбор реальных данных ввп на душу населения 1990-2007, 59.29kb.
- Программа по курсу «Функциональный анализ», 36.73kb.
- 11 готовых сочинений на лингвистическую тему, 59.56kb.
- Экзаменационная программа по стилистике русского языка понятие стиль. Характеристика, 20.43kb.
- Визуализатор алгоритма программа, наглядно демонстрирующая работу алгоритма в пошаговом, 57.48kb.
- Методика по расчету (оценке) общего объема денежных доходов и реальных располагаемых, 252.43kb.
- Планирование аудита и основные его этапы. Тесты средств контроля и примеры их применения, 10.84kb.
таинства "заворота" IsDebuggerPresent
И вот мы снова в той самой процедуре, которая вызывала только что исследованную нами HaronGetProcAddress. Прямо возвращение блудного сына какое-то! Вот они наши родные пенаты, а вот тот самый вызов функции, которая импортирует LoadLibraryA
001B:00446793 LEA EBX,[EAX+00019713]
001B:00446799 CALL 0044658D ; HaronGetProcAddress
001B:0044679E JAE 00446789
Листинг 89 импортирование LoadLibraryA
Постой паровоз! Не стучите колеса! Кондуктор дави на тормоза! Почему здесь импортируется одна лишь LoadLibraryA, ведь (как мы уже разобрали выше) продвинутая функция Харона может импортировать все одним пучком! Может-то она может, но лишь при том условии, что в этом самом "пучке" ей нигде не встретится завершающего нуля. Помните проверку 446637:JNZ 4465C5? Вот это она и есть!
Посмотрим на таблицу расшифрованных имен еще раз (читай: посмотрим на нее очень внимательно!)
.text:004460C8 aLoadlibrarya db 12,'LoadLibraryA', 0
.text:004460D6 aGetprocaddress db 14,'GetProcAddress'
.text:004460E5 aIsdebuggerprese db 17,'IsDebuggerPresent', 0
.text:004460F8 aClosehandle db 11,'CloseHandle'
.text:00446104 aCreatedirectory db 16,'CreateDirectoryA'
.text:00446115 aCreateeventa db 12,'CreateEventA'
… … … … … … … … … … … … … … … … … … … … … … … … … … … … … …
.text:004464F4 aLstrcmpia db 9,'lstrcmpiA', 0
.text:004464FF aInitializesecur db 28,'InitializeSecurityDescriptor'
.text:0044651C aSetsecuritydesc db 25,'SetSecurityDescriptorDacl', 0
.text:00446537 aCharlowera db 10,'CharLowerA'
.text:00446542 aChartooembuffa db 14,'CharToOemBuffA'
.text:00446551 aCharuppera db 10,'CharUpperA', 0
.text:0044655D aDpmichartooembu db 18,'dpmiCharToOemBuffA', 0
.text:00446571 aDpmioemtocharbu db 18,'dpmiOemToCharBuffA', 0
Листинг 90 семь пучков импортируемых функций (раздельные нули взяты в рамку)
Эге! Да тут целых семь "пучков", и в первом из них, как мы можем видеть, действительно находится одна лишь LoadLibraryA и больше ничего! Зато следующий по счету вызов HaronGetProcAddress загружает сразу две функции: GetProcAddress и IsDebuggerPresent и тут же вызывает последнюю из них следующей машинной командой – CALL [EBX – 04] (как мы помним, указатель EBX перемещает сама вызываемая функция и по возвращении из нее он указывает на следующий, еще не обработанный элемент DYN_TABLE, соответственно, – [EBX-4] дает адрес только что загруженной API-функции).
Тоже мне anti-debug trick понимаешь! Только самые примитивнейшие из отладчиков дают себя обнаружить вызовом IsDebuggerPresent, и это именно те отладчики, которые отлаживают программу средствами debug-API. В общем, дрянь это, а не отладчики. Во всяком случае soft-ice таким простым Макаром ни за что не обнаружить. Если бы эту защиту писал не Харон, то мы не стали бы удивляться столь наивному коду (действительно, откуда неотесанным прикладникам знать как работает IsDebuggerPressent и что именно она обнаруживает), но Харон, опытный системщик Харон… нет, здесь действительно что-то не так, а ну-ка присмотримся к защитному коду повнимательнее:
001B:004467D5 CALL 0044658D ; HaronGetProcAddress
001B:004467DA JAE 004467E7 ; ошибка импорта
001B:004467DC CALL [EBX-04] ; CALL IsDebuggerPresent
001B:004467DF OR EAX,EAX ; нас отлаживают?
001B:004467E1 JNZ 00446789 ; нас действительно отлаживают
001B:004467E3 SUB EBX,04 ; "заворачиваем" IsDebuggerPresent
001B:004467E6 DEC ESI ; мусор
001B:004467E7 INC ESI ; мусор
001B:004467E8 CALL 0044658D ; HaronGetProcAddress
001B:004467ED JAE 00446789 ; ошибка импорта
Листинг 91 создание "складки" для скрытого размещения IsDebuggerPresent (команда заворота выделена жирным шрифтом и взята в рамку)
Ага, за пыльной дверью чулана обнаружилась лестница ведущая еще на один уровень вглубь, – вот уж подложил нам Харон гранату! Машинной командой SUB EBX, 04h он "подворачивает" DYN_TABLE, заставляя ячейку с "IsDebuggerPresent" уходить внутрь "складки", затираемой последующим вызовом функции HaronGetProcAddress. Так вот откуда взялось расхождение в один элемент между таблицами импортируемых имен и таблицей динамического импорта!
таинства загрузки USER32.DLL и ADVAAPI32.DLL
В общем, механизм импорта API-функций нам стал более или менее понятен, во всяком случае пустая таблица импорта защитного файла нас перестала удивлять. Тем не менее "белые пятна" еще остались! Более того, самое интересное нас еще ждет впереди! Когда писались заключительные строки предыдущей главы, посвященной этому линкеру я по своей наивности имел неосторожность похвастаться Харону, что дескать знаю, какими путями его защита загружает динамическую библиотеку KERENL32.DLL. Но Харон только улыбнулся в ответ и сказал "…и еще две [динамических библиотеки]". То, что эти библиотеки действительно загружались элементарно обнаруживалось по именам импортируемых функций, содержащихся в расшифрованной таблице импортируемых имен (и как это только я их проглядел!), – см. листинг $-2.
Однако, загружаться тем же самым путем, что и KERNEL32.DLL эти библиотеки со всей своей очевидностью не могли, – хотя бы уже потому, что не были предварительно спроецированы на адресное пространство процесса (как мы помним, KERNEL32.DLL на него спроецирована все-таки была, хотя из нее и не импортировались никакие функции). Впрочем, особой нужды в подобных извращениях на данной стадии уже не было и не нужно быть провидцем, чтобы с вероятностью близкой к единице предположить, что для их загрузки Харон использовал вызов LoadLibraryA уже имеющуюся в его распоряжении. Это давало мне возможность поставив точку останова на LoadLibraryA подсмотреть имена всех загружаемых библиотек и быстро выйти на след того кода, который их загружает. Но я, устояв перед соблазном, все-таки пошел другим путем вручную дизассемблировав код и, признаться, обнаружил в нем много интересного!
Вот, взгляните на следующий фрагмент. Что по-вашему он делает?
001B:004467A3 PUSH 4C4C442E ; ".DLL"
001B:004467A8 PUSH EAX
001B:004467A9 PUSH EAX
… … … … … … … … … … … … …
001B:004467CD MOV DWORD PTR [ESP+08],4C4C442E ; ".DLL"
001B:004467EF MOV DWORD PTR [ESP+04],32335245 ; "ER32"
001B:004467F7 MOV DWORD PTR [ESP],53550000 ; "\0\0US"
001B:004467FE PUSH ESP
001B:004467FF ADD DWORD PTR [ESP],02 ; на "USER32.DLL"
001B:00446803 CALL [EBX-010C] ; LoadLibraryA
Листинг 92 растворенное в коде имя "USER32.DLL"
На первый взгляд какая-то непонятная возня с ничего не говорящими константами. Но присмотритесь к ним повнимательнее: что именно это за константы. Опытные хакеры, знающие практически все ASCII наизусть тут же распознают в последовательности 2Eh 44h 4Ch 4Ch текстовую строку ".DLL" (уж больно часто она встречается в HEX-дампах отладчика). Ага! Уже есть кое-что! Соответственно, 45h 52h 33 32h – это "ER32", а 00h 00h 55h 53h – это "\0\0US". Причем обратите внимание куда помещаются две последних подстроки! Они попадают непосредственно в ту самую область памяти, которая была только что зарезервирована парой машинных команд PUSH EAX/PUSH EAX. Кстати, подстрока ".DLL" засылается на одно и тоже место аж дважды. Интересно узнать: зачем? Чтобы случайно не проскочила мимо глаз взломщика что ли?!
Последний штрих: инструкция "PUSH ESP" передает через стек аргумент функции GetProcAddress – указатель на строку с именем библиотеки и тут же увеличивает его на два "ADD dword ptr [ESP],02", перескакивая тем самым через "ведущие" нули. Зачем понадобился этот изврат? Немного терпения друзья, рассказ об этом нас ждет далее. Пока же давайте разберем с чего это мы взяли, что [EBX – 10Ch] это именно LoadLibraryA не что-то еще. Конечно, в отладчике достаточно просто дать команду "U *(EBX – 10Сh)" и он высветит ее имя в титульной строке окна "CODE", но… а как определить ее именную принадлежность в дизассемблере? Обратим внимание на тот факт, что к настоящему моменту HaronGetProcAddress вызывлась трижды, а в трех "пучках" загруженных ею функций содержится ровно 68 имен. Откинем одно из них на "подвернутый" IsDebuggerPressent и умножим на размер двойного слова для вычисления эффективного смещения в массиве DYN_TABLE. Еще четыре байта уходят на компенсацию авто приращения указателя EBX, возращенного функцией HaronGetProcAddress после ее вызова. Итого, у нас получается… у нас получается, что первый элемент DYN_TABLE и имеет относительное смещение 10Ch, а в нем как мы помним содержится ни что иное как адрес API-функции LoadLibraryA.
Идем дальше. Следующие строки кода затирают символы "\0\0US" и "ER" имени "USER32.DLL" и накладывают поверх них "ADVA" и "PI" соответственно, так что в результате получается "AVDAPI32.DLL", где выделенная жирным шрифтом подстрока "32.DLL" осталась в наследство от старого имени. Естественно, строки "ADVAPI" и "USER" не равны между собой по длине ("ADVAPI" на целых два символа длиннее) и потому в ход идут два тех самых ведущих нуля, на которые мы уже обращали внимание ранее.
001B:0044680D MOV WORD PTR [ESP+04],4950 ; "PI"
001B:00446814 MOV DWORD PTR [ESP],41564441 ; "ADVA"
001B:0044681B JZ 00446789 ; проверка предыдущей загрузки
001B:00446821 PUSH ESP ; указатель на "ADVAPI32.DLL"
001B:00446822 CALL [EBX-010C] ; LoadLibraryA
Листинг 93 растворение в коде строки "ADVAPI32.DLL"
Вот и все! Дальнейшая судьба данного участка кода уже не интересна. Из обоих библиотек загружаются жалкий пяток функций (CharLowerA/CharToOEMbuffA/CharUpperA и InitializeSecurityDescriptor/SetSecurityDescriptor соотвественно). Функции с префиском dmpi, как и следует из названия префикса, экспоритуются DOS-расширителем (наподобие DOS4GW) при запуске линкера из-под MS-DOS (Windows 9x?). Скукота! Лучше давайте посмотрим как вплетены имена загружаемых библиотек в двоичный код:
.text:004467A0 33 C0 50 68 2E 44 4C 4C-50 50 B8 B8 78 01 00 8D "3└Ph.DLLPP╕╕x☺.Н"
.text:004467B0 88 93 17 00 00 3B E9 77-14 8B 08 E3 CC 3B C8 76 "ИУ↨..;щw¶Л◘у╠;╚v"
.text:004467C0 C8 81 E1 00 F0 FF FF 89-0D 70 B2 44 00 C7 44 24 "╚Бс.Ё Й♪p▓D.╟D$"
.text:004467D0 08 2E 44 4C 4C E8 B3 FD-FF FF 73 0B FF 53 FC 0B "◘.DLLш│¤ s♂ S№♂"
.text:004467E0 C0 75 A6 83 EB 04 4E 46-E8 A0 FD FF FF 73 9A C7 "└uжГы♦NFша¤ sЪ╟"
.text:004467F0 44 24 04 45 52 33 32 C7-04 24 00 00 55 53 54 83 "D$♦ER32╟♦$..USTГ"
.text:00446800 04 24 02 FF 93 F4 FE FF-FF 8B E8 0B C0 66 C7 44 "♦$☻ УЇ■ Лш♂└f╟D"
.text:00446810 24 04 50 49 C7 04 24 41-44 56 41 0F 84 68 FF FF "$♦PI╟♦$ADVA☼Дh "
Листинг 94 черные квадратики, похожие на изюминки в сдобой булке на самом деле есть кусочки имен динамических библиотек, экспонирующих необходимые Харону функции
Очевидно, Харон не ставил своей целью действительное сокрытие факта загрузки указанных библиотек, а просто убрал их от греха подальше с глаз "детишек". Мог бы хотя бы ради интереса и зашифровать или потрассировать чуток LoadLibraryA из KERNEL32.DLL чтобы впендюрить имя функции на самой последней стадии, – тогда бы защита так просто не далась.