Оригинальный DVD-ROM: eXeL@B DVD !
eXeL@B ВИДЕОКУРС !

ВИДЕОКУРС ВЗЛОМ
выпущен 12 ноября!


УЗНАТЬ БОЛЬШЕ >>
Домой | Статьи | RAR-cтатьи | 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 >>>


Комментарии к статье: Распаковка PECompact 2.xx без использования ImpREC.doc

PE_Kill 16.03.2006 11:00:26
Для некоторых версий после распаковки к характеристикам первой секции необходимо добавить Writeble
---
back_analys 13.05.2006 15:22:33
Klassnaya stat'ya, mnogomu nauchilsya. Autor spasibo!
---
Tim 18.05.2006 22:28:36
Написано очень интересно... Вот бы мне такую статью эдак года 2 назад...
---
DrGolova 27.05.2006 21:32:05
BTW, в последних версиях PEC2 при использовании "slim" загрузчика не делает SEH jump на распаковщик, там ставится просто jmp near
---

Материалы находятся на сайте https://exelab.ru



Оригинальный DVD-ROM: eXeL@B DVD !


Вы находитесь на EXELAB.rU
Проект ReactOS