Основные концепции борьбы с протектерами Сегодня я хочу поговорить про общие положения распаковки протектеров. Речь пойдёт обо всех протекторах и ни о коком конкретно. Я решил выделить основные направления по которым нужно двигаться при встрече с каким-либо протектором. Конечно всю тему охватить не удастся, но это, как вы наверно поняли из заголовка, не является моей целью. Читателю необходимы OllDBG и опыт распаковки простейших пакеров. Итак, что обычно делает протектор? Он ворует первые байты вашей программы и API-функций, шифрует/пакует тело программы. Плюс некоторые антиотладочные приёмы, в основном это IsDebugPresent, SEH. Какой oep? Тут хотяб на EP попасть. Думаю многие, открыв программу в Olly, обнаруживали себя безцеремонно выброшенными куда то далеко от кода программы. Дело в том, что существует такая штука, как TLS Callbacks. Это функции, которые загрузчик вызывает до вызова EP. TLS Callback, как вы уже поняли, часто используется протектерамии вирусами для антиотладки. Например ExeCryptor. Итак, что делать? Для начала нужно сконфигурировать Olly. Для этого идём в опции > Event и устанавливаем System Breakpoints. Грузим программу в PEditor. Directory > TLS. В CallBack address адрес с котогрого начинает исполнятся программа. Соответсвенно грузим прогу в Olly и тормозимся на System Breackpoint. В cmdbar пишем "d CallBack_address". В окне дампа ставим бряк на доступ к этому адресу. F9. Мы на EP. Также в этом нам может помочь плагин OllyAdvanced. Поиск oep Первое что нужно сделать, пройтись по коду. Обычно в протектерах/криптерах много исключений. Нам нужно узнать какое будет последним. И вот почему - оно ближе всех к выходу, тоесть переходу на OEP программы. Ведь после него программа запускается. Последнее исключение мы нашли. Перезапускаем программу (Ctrl-F2). Теперь естественно ставим бряк на первый кадр стека, где протектор сохраняет регистры. Обычно в cmdbar пишем "hr esp-4". И идём до последнего исключения неоглядываясь. Если последнее исключение недалеко от OEP, то, обычно, popad/(pop xxx) уж куда ближе. Короче после этого последнего исключния движемся более аккуратно. А если бряк на esp-4 не сработает? Чтож, это плохо. Но программа то скорее всего в секции кода, а все расшифровывающие операции наверное уже выполненны (мы же подобрались к OEP). Улавливаете мысль? Следующим обращением к секции кода, скорее всего, будет перемещение на oep. Поэтому ставим брейк на доступ к секции. В Memory map выбираем нужную секцию, на правую (или F10), set memory breakpoint on access. Не думайте что мы сразу попадём на oep, возможно да, но это не легко понять, т.к. некоторые протеторы воруют первые байты программы. Каким образом? Ну вот например будет так: Code: $ => popad ; на который мы попали с помощью esp-4 $+1 => push ebp $+2 => jmp $+n ... ; здесь какие-нибудь нехорошие команды $+n => mov ebp,esp $+(n+2) => jmp $+y ... ; ещё что то нехорошее Явно угадывается следующий код. Code: push ebp mov ebp,esp ... И только после этого идёт прыжок в тело программы, естественно этих байт там уже не будет. Иногда если позволяет положение кода программы, спертые байты можно вырезать и вставить перед кодом программы. Соответственно OEP будет новым. Зачем это делают? Это даёт нам визуальное удобство при исследовании самой программы. Для нахождения OEP обычно составляется простой скрипт, его я приводил в своей статье по распаковке PESpin 0.7. Он очень прост! Если исключение, на которое мы нарываемся, последнее, то дальше путь свободен для F9, ставим бряк на OEP, и Shift-F9, снимаем бряк. found_OEP_name_private_protect.txt Code: var real_OEP mov real_OEP,xxxxxx // найденый OEP var ex_OEP mov ex_OEP,yyyyyy // последнее исключение ff: eoe fOEP esto fOEP: cmp eip,ex_OEP jne ff bp real_OEP esto bc real_OEP cmt eip," <------------- OEP" an real_OEP ret Есть ещё один способ найти OEP. Просто запускаем программу, затем в Olly присоединяемся к процессу. Открываем Memory map (Ctrl-M), смотрим и заходим в секцию кода. Тут уже всё расшифрованно, а если ещё протектор байты не ворует, то вам очень везёт. Правда есть недостаток, oep или тело программы может быть смещено от начало секции на ??? байт, а может вообще находится в другой секции. И ещё. Если никак не получается пройти к oep,после последнего исключения, можно поставить брейк на функцию которая должна идти первой (например GetModuleHandleA). После чего мы остановимся на GetModuleHandleA, а там до oep не далеко, правда нам теперь в другую сторону, вверх. Лучше поставить бряк на середину функции т.к. не исключенно, что протектор будет тырить байты. Обобщить выше сказанное можно так. Делаем всё возможное, чтоб как можно ближе оказаться у oep и дальше либо трейсим, если мы должны были оказаться перед oep, либо восстанавливаем цепочку событий, если мы должны были оказаться после oep. Импорт Для начала. Как же проходит импорт? Мы говорим системе о том, что мы хотим вызвать и откуда, а система в нужное место нашей программы предоставляет адрес перехода. Тоесть при загрузке программы, загрузчик в нужное место программы вставляет, найденные им, адреса API-функций. А вызов функции в программе проходит следующим образом. Code: call xxxxxx ... xxxxxx: jmp dword ptr [yyyyyy] ; yyyyyy то самое место, где лежит адрес найденной API Olly анализирут код и в комменте любезно показывает, что за функция вызывается. Казалось, чем можно тут нагадить крякеру? Ну вот допустим мы перенесём первые байты API-функции в какое-нибудь другое место, а после них поставим прыжок уже в тело функции (имеется ввиду, минуя байты которые мы перенсли). В импорте поменяем адреса функций на адреса вырезанных кусков. Тогда Olly не сможет определить где что вызывается, и крякер оказывается как ежик в тумане. Соответсвенно и дамп снять будет трудновато. Воровство байт у функций API происходит примерно так же, как у программы. Вроде маленькая такая пакость, а так может затруднить жизнь. Вот примеры. Пример 1: Code: push 0 jmp xxxxxx ... xxxxxx: jmp yyyyyy db ff jmp oooooo ; ну это для примера db ff jmp pppppp ; ну это для примера ... ... yyyyyy: mov edi,edi push ebp mov ebp,esp jmp zzzzzz ... ... mov edi,edi ; начало функции "OEP" push ebp mov ebp,esp zzzzzz: ; "OEP" функции плюс число украденных байт Пример 2: Code: push 0 call xxxxxx ... xxxxxx: jmp dword ptr [yyyyyy] ; допустим в yyyyyy хранится адрес uuuuuu jmp dword ptr [oooooo] jmp dword ptr [pppppp] ... uuuuuu: mov edi,edi push ebp mov ebp,esp jmp zzzzzz ... ... mov edi,edi ; начало функции "OEP" push ebp mov ebp,esp zzzzzz: ; "OEP" функции плюс число украденных байт И что делать? Дааа, будет трудно, но я в вас верю, дорогие читатели. Итак общий принцип таков. Ставим бряки на все jmp'ы идущие к кускам. И запускаем программу (F9). Когда программа захочет вызвать что-либо, она брякнется на одном из jmp'ов. Идём по переходу(ам), их может быть несколько, короче до вырезанного кода, затем к прыжку (может даже на прыжок) в тело API, поднимаемся на кол-во спёртых байт вверх. И у нас есть адрес API. Есть два выхода из сложившейся ситуации. Первый выход. Найти в списке импортируемых библиотек адрес нужной функции, узнать её имя, и записать куда-нибудь имя функции и адрес jmp, с котрого она вызывалась. Составить такой список из всех вызываемых функций. Затем составить tree файл для ImpRec. Если вам нужен пример этого файла, то просмотрите импорт у не запакованной программы и нажмите конпочку Save tree. Далее, после составления списка, мы снова открываем ImpRec, открываем нужный процесс. Load tree, открывем составленный tree-файл. Ну и, как обычно, fix'ируем. Второй выход. Автоматизировать вышеприведённый способ. Алгоритм нам уже известен. И так нам понадобится адрес первого jmp и последнего. После чего можно дописать, к уже существующему скрипту для прохода к oep, цикл который ставит бряки на jmp'ы. Затем run(F9). После того как программа брякнется на каком-либо jmp'е, сохраняем его адрес. И идём к вырезанному куску, ну это простая комбинация sto - F8. Дальше нам нужно посчитать сколько байт спёр протектор. Мы знаем что джампы он не ворует, следовательно первый встретившийся джамп - переход в тело функции. Ищем адрес опкода джампа, затем вычитаем eip, кол-во спёртых байт есть =). Дальше идём по прыжкам(sto - F8), пока не окажемся в теле какой-нибудь функции. Прибавляем к eip, кол-во спёртых байт, адрес функции есть. Сохраняем его куданить, и в jmp меняем адрес, вот зачем мы сохраняли его адрес, от куда нужно брать адрес функции. Снимаем бряк. И F9. Пример скрипта можно увидеть в моей статье Распаковка PESpin 0.7. Ну а если не все функции используются? Они же не определятся!! - скажет читаель. А зачем вам? - спрошу я. Задача этого способа - помочь сориентироваться внутри программы. Найти место, где программа считывает пароль. А дальше можно написать простой лоадер. А оно того стоит? Если ничего не получается сделать. Просто запустите прогу в отладчике. Будем действовать как с обычной программой, только бряк поставим не в начало, интересующей нас функции (GetWindowText, GetDlgItemText и т.п.), а в конец, скажем на ret. На случай если протектор байты ворует. Когда тормознёмся F8. Дальше делайте что вздумается. Заключение В этой статье я попытался обобщить известные мне способы обхода протектеров, показать направление в котором нужно идти. Конечно для каждого протектера, даже каждой версии одного и того же, потребуется свой подход, поэтому читайте как можно больше информации о распаковке. Ссылки wasm.ru cracklab.ru tuts4you.com Автор: taha
неплохо, хоть имхо название статьи подобрано не достато4но соответствующее ЗЫ: можно как вариант продолжения обсудить самомодификацию, стековый полиморфизм, ВМ етц )
Самомодификация, метаморфизм, полиморфизм, тунелинг - это уже скорее про исследование вирей =). Но я уже думаю об этом. Дальше несколько вариантов событий: 1. Меня не допустят к сесcии и я пополню ряды нашей доблестной армии 2. Я сдам сесcию и пополню ряды в наших доблестных отрезвителях =)) А про стековый полимофизм, цитируя http://vx.netlux.org/lib/vsl05.html.
>>Дальше несколько вариантов событий: 1. Меня не допустят к сесcии и я пополню ряды нашей доблестной армии 2. Я сдам сесcию и пополню ряды в наших доблестных отрезвителях =)) знакомая ситуация ) >>Цитата:Теперь давайте поговорим об актуальности стэкового полиморфизма и его перспективах. С выходом новых процессоров запрещено исполнение кода в стэке. И это используется как SP2 для Windows XP, так и будет использоваться в Longhorn. Так что актуальность данного вида полиморфизма стремится к нулю. ну-ну, ты небось как дядя билл завещал и винду лицензионную юзаешь? была бы эта фи4а 100% "рабо4ей" перестали бы работать в том 4исле многие сценарии атак на приложения, сами много4исленные пользовательские проги >>это уже скорее про исследование вирей =) к с4атья, или к сожалению, vx нын4е тесно связан с протекторами...
>> ну-ну, ты небось как дядя билл завещал и винду лицензионную юзаешь? вообще то да)), просто когда ноут покупал поставленная винда в довесок шла не думаю что она, хотя всё возможно, не лицензионная кста это моя первая лицензионная)) >> была бы эта фи4а 100% "рабо4ей" ... мдя, не задумывался, тут ты прав...