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

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

Содержание


UniLink v1.03 от Юрия Харона IIили переходим от штурма к осаде
Entry Point и ее окружение
001B:0044667d jb 00446682 ; never jump
PUSH DWORD PTR [EBX] ; дублируем 406701h на стеке
Листинг 64 содержимое стека на момент выполнения инструкции RETF
Подобный материал:
1   2   3   4   5   6   7   8   9   10   ...   13

UniLink v1.03 от Юрия Харона II
или переходим от штурма к осаде


"Не снабжайте детей готовыми формулами, формулы – пустота. Обогатите их образами и картинами, на которых видны связующие нити. Не отягощайте детей мертвым грузом фактов, обучите их приемам и способам, которые помогут им постигать. Не судите о способностях по легкости усвоения. Успешнее и дальше идет тот, кто мучительно преодолевает себя и препятствия. Любовь к познанию – вот главное мерило. Не учите их, что польза – главное. Главное – возрастание в человеке человеческого. Честный и верный человек гладко выстругает и доску. Научите их почтению, потому что насмехаться любят бездельники, для них не существует целостной картины"

Антуан де Сент-Экзюпери. Цитадель

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

Кроме того, процесс "потрошения" линкера позволяет наглядно продемонстрировать преимущества связки отладчик + дизассемблер над каждым из этих инструментов по отдельности. Такие программы вообще не ломаются в дизассемблере! Даже возможностей IDA Pro окажется более чем недостаточно! Харон активно использует многоуровневые математические преобразования критических к раскрытию текстовых строк и указателей, "благодаря" чему они полностью растворяются в дизассемблерном коде, однако без труда обнаруживаются просмотром дампа памяти под отладчиком. С другой стороны, отладчик в силу другой профессиональной направленности очень плохо приспособлен для изучения взаимосвязи различных частей кода друг с другом и без помощи дизассемблера мы будем видеть не лес, но деверья…

Entry Point и ее окружение


Точка входа в программу начинается с традиционного сохранения регистра EBP (см. листинг $), которое вставлено сюда Хароном исключительно ради этики и приличия, а на самом деле совершенно необязательно, поскольку программа, завершающая свое выполнение по RETN (а UniLink свое выполнение именно так и завершает), передает управление функции ExitProcess, которой как в том анекдоте совершенно по фигу как надета тюбетейка, простите, какое значение содержит регистр EBP.

Следующая за ней команда – PUSH 42CFAEh открывает трилогию "математических манипуляций с указателем" и скрывает адреса передачи управления от дизассемблеров и "детишек". Эвристический анализатор IDA Pro всех версий ошибается, во-первых, принимая 42CFAEh за смещение, и во-вторых, генерируя совершенно "левую" перекрестную ссылку по соответствующему ему адресу. Чтобы махинации с указателем не так бросались в глаза, Харон сдабривает их небольшим количеством мусорного кода, используя для этой цели малораспространенные, а потому и незнакомые начинающим взломщикам, машинные команды XLAT и DAA, однако, результат их выполнения никак не используется в программе, что сразу же демаскирует "мусор" в глазах мало-мальски толковых хакеров.

Метаморфозы указателя очень хорошо наблюдать с помощью отладчика. Отдав команду "DD; D ESP" мы сможем увидеть следующую цепочку превращений: 42CEAEh  44CFAEh  406701h. Последнее значение и будет тем самым адресом на который защитный код спустя несколько машинных команд передаст управление. Чтобы "засечь" тот же самый факт в дизассемблере, все математические вычисления нам придется выполнить вручную. Ну, не то, чтобы совсем вручную (встроенный калькулятор в IDA еще никто не отменял), но такой путь чреват ошибками и вообще трудоемок. Достоинства отладчика в том, что можно вообще не вычислять целевой адрес, а просто сидеть и смотреть куда в следующее мгновение переметнется ветка управления.


001B:00446673 PUSH EBP ; // сохраняем EBP

001B:00446674 PUSH 0042CFAE ; // 1] кусочек указателя

001B:00446679 MOV EBX,ESP ; // EBX := ESP

001B:0044667B AND AL,03 ; м у с о р

001B:0044667D JB 00446682 ; NEVER JUMP

001B:0044667F INC BYTE PTR [EBX+02] ; // 2] 42CEAEh  43CFAEh

001B:00446682 XLAT ; м у с о р

001B:00446683 DAA ; м у с о р

001B:00446684 ADD DWORD PTR [ESP],00009753 ; // 3] 43CFAEh  446701h

001B:0044668B SBB CL,CL ; м у с о р

001B:0044668D LAHF ; м у с о р

001B:0044668E PUSH DWORD PTR [EBX] ; дублируем 406701h на стеке

001B:00446690 CLD ; для подстраховки ;-)

001B:00446691 RET ;  JUMP TO 446701h

Листинг 63 код окрестностей точки входа. передает управление на 406701h (значимые команды выделены жирным шрифтом)

Маленькое замечание для начинающих. Вы думаете, что машинная команда RET в строке 446691h представляет собой инструкцию возврата из под программы? Так-то оно так, да не совсем. Если следовать этой логике, то данный RET должен был вышвырнуть программу обратно в Windows (точнее, в породившую ее материнскую функцию CreateProcessA). Но ведь этого не происходит, верно? На самом деле, инструкция RET ничего не знает о породивших ее функциях. Она просто снимает двойное слово с верхушки стека (где при нормальном развитии событий находится адрес возврата) и передает туда управление. Таким образом, конструкция "PUSH p/RETN" полностью эквивалента "JMP p" за тем исключением, что прямой jump слишком нагляден, а вот состояние стека на момент выполнения инструкции RETN в дизассемблере не видать и все, что нам остается: либо наивно надеяться, что данный RET "легален" и действительно возвратит нас туда, откуда мы были вызваны, либо же утомительно анализировать весь код функции в попытке обнаружить "теневые" манипуляции с указателем стека или лежащем на его вершине значением. Если ни то, ни друге вас не прельщает, – запустите свой любимый отладчик и загляните на стек в "живую".

В данном случае он должен выглядеть так:


RETN:...

23:0012FFB8  00446701h – дублированный указатель BOND 007

00446701h – указатель BOND 007, полученный путем хитрых махинаций

0012FFF0h – сохраненное значение EBP (кадр стека CreateProcess)

77E87903h – адрес возврата в CreateProcess

........

Листинг 64 содержимое стека на момент выполнения инструкции RETF

Выделенная жирным цветом строка и есть тот адрес, на который RENT передает управление.