![]() ![]() |
Домой | Статьи | RAR-cтатьи | Форум | Программирование | Скачать | CD & DVD |
Новичку | FAQ | Ссылки | Интервью | Архив | Новости | Связь |
Распаковка PECompact 2.xx без использования ImpREC.docАвтор: PE_Kill <PE_Kill@mail.ru> Распаковка PECompact 2.xx без использования ImpRECИнструменты. OllyDbg 1.10 с плагином OllyScript v0.92 by SHaG или ODbgScript v1.42 by Epsylon3. Любой дампер процессов. Если при проверке программы PEiD говорит вам: PECompact 2.x -> Jeremy Collake, то эта статья для вас. Нахождение OEP. OEP будем искать древнейшим способом. Его придумали еще до моего рождения, но он работает до сих пор. При запуске программы начинает работать загрузчик, который распаковывает программу и эмулирует работу загрузчика Windows. При своей работе загрузчик использует регистры и стек. Так вот после распаковки в стеке находятся уже не нужные загрузчику данные и он должен их от туда убрать и привести стек к тому состоянию, в котором он был до начала работы. При этом всё, что находилось в стеке до начала работы загрузчика должно там и остаться. Давайте загрузим программу и посмотрим, как мы это можем использовать. Вот начало программы: mov eax, 555B30 push eax push dword ptr fs:[0] mov dword ptr fs:[0],esp xor eax,eax mov dword ptr ds:[eax],ecx push eax inc ebp inc ebx Сколько я смотрел программ – эти несколько команд одинаковые. И так, что мы видим? В регистр eax заносится какой то адрес, а затем помещается в стек. Понятно, что при этом указатель на стек (регистр esp) изменится. Перед выполнением распакованного кода распаковщик должен вернуть стек в исходное состояние. Давайте следить, где распаковщик извлекает этот адрес из стека и смотреть, что у нас в стеке, если там то, что было перед запуском – значит мы почти на OEP (или уже на OEP). Запоминаем, что у нас в стеке: 0012FFC4 7C816D4F RETURN to kernel32.7C816D4F 0012FFC8 7C910738 ntdll.7C910738 0012FFCC FFFFFFFF 0012FFD0 7FFDC000 Поставим hardware точку останова (далее бряк) на текущий адрес стека минус 4, т.к. в стек за раз помещается 4 байта. В OllyDbg, в командной строке пишем HR ESP-4 (поставить hardware бряк на чтение на адрес, что находится в esp минус 4). Запускаем программу. Прервались на две команды ниже, это и понятно в стек поместился адрес, про который я говорил. Снова запускаем программу. Прервались, но OllyDbg в строке состояния пишет, что это не остановка на бряке, а ошибка, при попытке записи по адресу 00000000. Хммм, антиотладка? Пока нас это не интересует, поэтому жмем Shift+F9 (передать ошибку для обработки программе). Прервались. Смотрим, что в стеке: 0012FC4C 7C910738 ntdll.7C910738 0012FC50 0012FCD4 0012FC54 FFFFFFFF 0012FC58 00000000 Не то, поэтому жмем F9. Прервались, в стеке: 0012FC48 00555B30 DIVXEK~1.00555B30 0012FC4C 7C910738 ntdll.7C910738 0012FC50 0012FCD4 0012FC54 FFFFFFFF Не то, F9. Прервались, в стеке: 0012FFC0 0012FFF0 0012FFC4 7C816D4F RETURN to kernel32.7C816D4F 0012FFC8 7C910738 ntdll.7C910738 0012FFCC FFFFFFFF Не то, F9. Прервались, в стеке: 0012FFC4 7C816D4F RETURN to kernel32.7C816D4F 0012FFC8 7C910738 ntdll.7C910738 0012FFCC FFFFFFFF 0012FFD0 7FFD5000 О, это же то, что было при загрузке в отладчик! Смотрим в окно кода: jmp eax les esi,fword ptr ds:[ecx+46] add byte ptr ds:[eax],al add byte ptr ds:[eax],al add byte ptr ds:[eax],al jmp eax – это прыжок на OEP. Давайте выполним его (F8). Вот теперь мы на OEP и можно дампить. Есть другой способ прохождения до OEP. Давайте вернемся назад на jmp eax. Посмотрим, что выполнялось до этой команды: push 8000 push 0 push edi call dword ptr ds:[ecx] mov eax,esi pop edx pop esi pop edi pop ecx pop ebx pop ebp Теперь, если вы перезагрузите программу и дойдете до call dword ptr ds:[ecx] (предварительно необходимо убрать hardware бряк: Меню Debug->hardware breakpoints), то увидете, что это всего лишь вызов API функции VirtualFree. Давайте перезагрузим программу и поставим бряк на VirtualFree. Для этого в командной строке OllyDbg пишем bp VirtualAlloc. Жмем F9, прервались, выходим из функции и видим: mov eax,dword ptr ds:[esi+C] add eax,edi pop ebp pop esi pop edi pop ebx retn Выполним первую команду, и смотрим, что у нас в eax. А там у нас адрес OEP, без ImageBase. А следующая команда как раз и добавляет ImageBase. Ставим бряк на полученый адрес, запускаем программу, прерываемся, всё мы на OEP. Далее ImpREC и все как обычно. Но! Можно обойтись и без ImpREC. Восстановление директории импорта. PECompact не шифрует и не перемещает директорию импорта, правда слегка преобразует в другой формат. Есть несколько способов это дело восстановить. Я хочу рассказать, как это делал я. Давайте найдем любой вызов API функции. В моей программе это выглядит так: 0046B1E1 sub esp,58 0046B1E4 push ebx 0046B1E5 push esi 0046B1E6 push edi 0046B1E7 mov dword ptr ss:[ebp-18],esp 0046B1EA call dword ptr ds:[5022AC] ; kernel32.GetVersion 0046B1F0 xor edx,edx Т.е. по адресу 005022AC лежит адрес функции GetVersion. У меня программа написага на C++. Если бы это был Delphi, то выглядело бы так: 00405CEA push 0 00405CEC call 00405C1C // jmp.kernel32.GetModuleHandleA 00405CF1 mov dword ptr ds:[461664],eax По адресу 00405C1C находится jmp dword ptr ds:[463210] // kernel32.GetModuleHandleA А вот по адресу 463210 находится адрес функции GetModuleHandleA. Как видите на Delphi сначала вызывается переходник. И так, мы нашли один из адресов IAT. У меня: 5022AC. Давайте перезагрузим программу, поставим бряк на запись на адрес 5022AC. Для этого пишем в командной строке OllyDbg d 5022AC и жмем Enter. Затем выделяем первые 4 байта, правая кнопка мыши->Breakpoint->Memory, on write. Запускаем программу, прервались, жмем F8 и смотрим, что у нас по адресу 5022AC, если не адрес API, то повторяем F9,F8… И так до тех пор, пока не увидим, что PECompact записал в IAT адрес API. Будет это в таком месте: mov dword ptr ds:[esi],eax mov dword ptr ds:[edx],eax add edx,4 add esi,4 jmp xxxxxxxx В esi находится 5022AC, в edx какой то другой адрес, в eax адрес API функции. Посмотрите, что лежит по адресу из edx. Для этого пишем в командной строке d edx. У меня (у вас нечто подобное): 342F1000 282F1000 122F1000 002F1000 EE2E1000 E22E1000 CE2E1000 BC2E1000 AC2E1000 982E1000 842E1000 782E1000 642E1000 4E2E1000 422E1000 302E1000 Теперь, если к любому из этих адресов прибавить ImageBase то мы увидим строку с названием API функции. Т.е. это не что иное, как указатели на имена функций. Если вы хоть немного знакомы, со структурой директории импорта, то знайте, что это массив из IMAGE_THUNK_DATA на который указывает OriginalFirstThunk, а вот по адресу из esi должен быть такой же массив, но его там нет. Загрузчик берет имя функции из OriginalFirstThunk, вычисляет её адрес и вписывает, как в FirstThunk, так и в OriginalFirstThunk, хотя в OriginalFirstThunk писать нет необходимости, и не понятно чем руководствуются авторы пакера. Ну да ладно, наше дело сей баг устранить. Я решил пропатчить эти две команды таким образом, чтобы в OriginalFirstThunk ничего не писалось, а в FirstThunk вместо адреса API писалась ссылка на строку с именем API, взять которую можно из OriginalFirstThunk. Пропатчим команды: mov dword ptr ds:[esi],eax mov dword ptr ds:[edx],eax add edx,4 add esi,4 jmp xxxxxxxx Вот так: push dword ptr ds:[edx] pop dword ptr ds:[esi] add edx,4 add esi,4 jmp xxxxxxxx Запомните, что в стеке самое первое значение не что иное, как указатель на начало директории импорта. Теперь ставим бряк на VirtualFree, доходим до OEP и дампим программу на диск. Осталось только исправить OEP и Image Import Directory Rva на те, что мы нашли. Всё файл полностью распакован! Зная про вашу природную лень я написал скрипт, распаковывающий PECompact 2.xx. Если в настройках вашего дампера не стоит “Вставлять заголовок из файла”, то после дампа ничего делать не надо, кроме Validate PE и Rebuild PE, он уже полностью рабочий. //////////////////////////////////////////////////////////////////////////////////// // Скрипт полностью распаковывает PECompact 2.xx, и написан специально для статьи // "Распаковка PECompact 2.xx без использования ImpREC" // Автор: PE_Kill //////////////////////////////////////////////////////////////////////////////////// var api_redirect var iat_start var VirtualAlloc var VirtualFree var ImageBase var OEP var counter gpa "VirtualAlloc","kernel32.dll" // После второго срабатывания бряка мы выходим mov VirtualAlloc,$RESULT // чуть выше кода, отвечающего за импорт bp VirtualAlloc gpa "VirtualFree","kernel32.dll" // Нужно для прохождения до OEP mov VirtualFree,$RESULT mov counter,0 gmi eip,MODULEBASE // Надо, чтобы фиксить PEHeader mov ImageBase,$RESULT @Shift_F9: // Выполнится, когда произойдет ошибка esto // и эмулирует нажатие Shift+F9 в Olly jmp @check_unpack @F9: // Просто запускает программу (F9) run @check_unpack: find eip,#8908# // Ищем команду mov dword ptr ds:[eax],ecx cmp $RESULT,eip // если мы стоим на ней, значит произошла je @Shift_F9 // ошибка. Нажимаем Shift+F9 inc counter // Иначе мы на VirtualFree и увеличиваем счетчик cmp counter,2 // Если равен 2, то пора выходить из функции jne @F9 // иначе продолжаем. bc VirtualAlloc // убираем бряк с VirtualAlloc rtr // доходим до RET sti // Выполняем её find eip,#8906890283C20483C604# // и ищем команды запонения IAT по сигнатуре cmp $RESULT,0 // если ничего не нашли, то генерим ошибку je @ERR_SIGN_NOT_FOUND // и выходим mov api_redirect,$RESULT // иначе запоминаем этот адрес bp api_redirect // ставим на него бряк run // запускаем программу bc api_redirect // убираем бряк mov [api_redirect],068F32FF // и патчим это место, чтобы работало, как надо :) mov iat_start,[esp] // запоминаем начало IAT bp VirtualFree // ставим бряк на VirtualFree @trace_to_oep: run // Запускаем программу rtr // Доходим до RET sti // Выполняем её cmp [eip],030C468B // Мы перед переходом на OEP? jne @trace_to_oep // Нет, продолжим трассировку bc VirtualFree // Убираем бряк с VirtualFree sti // Даем сформироваться sti // OEP в eax mov OEP,eax // В eax находится адрес OEP, запомним bp OEP // Ставим бряк на OEP run // Запускаем программу bc OEP // Убираем бряк cmt eip,"<- This is OEP! Good luck crecker!" sub OEP,ImageBase // OEP = OEP - ImageBase sub iat_start,ImageBase // Import Directory RVA = Import Directory RVA - ImageBase mov counter,ImageBase add counter,3C // Смещаемся на DOSStub.e_ifanew mov counter,[counter] // Читаем, смещение на NtHeader add counter,ImageBase // Получаем абсолютный адрес add counter,28 // Смещаемся на NtHeader.OptionalHeader.OEP mov [counter],OEP // Fix OEP add counter,58 // Теперь импорт mov [counter],iat_start // Fix RVA log OEP log iat_start eval "The file is completely unpacked! Dump it on a disk. Do not use ImpREC, import is already restored! OEP: {OEP}, IAT Start: {iat_start}" msg $RESULT @end: log "Who, if not I?" pause ret @ERR_SIGN_NOT_FOUND: msg "Error! Signature not found! Done..." jmp @end Комментарии к статье: Распаковка PECompact 2.xx без использования ImpREC.doc PE_Kill 16.03.2006 00:00:26 Для некоторых версий после распаковки к характеристикам первой секции необходимо добавить Writeble --- back_analys 13.05.2006 04:22:33 Klassnaya stat'ya, mnogomu nauchilsya. Autor spasibo! --- Tim 18.05.2006 11:28:36 Написано очень интересно... Вот бы мне такую статью эдак года 2 назад... --- DrGolova 27.05.2006 10:32:05 BTW, в последних версиях PEC2 при использовании "slim" загрузчика не делает SEH jump на распаковщик, там ставится просто jmp near --- Добавить комментарий Материалы находятся на сайте http://cracklab.ru/ ![]() |
Вы находитесь на CRACKLAB.RU, сегодня 20 октября 2006 года 17:13:03 MSK |
![]() ![]() |