Форум АНТИЧАТ

Форум АНТИЧАТ (https://forum.antichat.xyz/index.php)
-   Авторские статьи (https://forum.antichat.xyz/forumdisplay.php?f=31)
-   -   Unpacking PESpin (https://forum.antichat.xyz/showthread.php?t=38873)

taha 26.04.2007 00:29

Unpacking PESpin
 
Unpacking PESpin 1.3

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Author: taha
Level: для продвинутых новичков
Date: 25.o4.2oo7
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Довольно приличный протектор. На мой взгляд один из самых интересных.
Tutorial’ов на русском не встечал, а те что были на английском даже читать не стал. Что можно почерпнуть для себя из статьи состоящей из фраз “листните на пару экранов вверх”, “патичть здесь”? Для меня в первую очередь важна логика действий,а не “scroll waaaay up”.

Жертву нашёл тут

Content
- Tools
- Anti-deb
- Finding OEP
* The ESP trick
* Stolen bytes
- Fixing IAT
* Length disassembler
* Scripting
- Dump & Rebuild Import
- links
- PS

Tools
- Shadow (модификация OllyDbg)
* plugin AnalyzeThis
* plugin ODBGScript
* plugin OllyDump
* plugin OllyPad
* plugin CommandLine
- OllyScript Editor (необязательно)
- ImpRec

Anti-deb
Первое, что необходимо сделать проверить содержит ли жертва антиотладочные приёмы. Для этого необходимо просто запустить файл в Olly. Не забудьте отключить остановку на исключениях! Запускаем (F9). Где то всеже останавливаемся, но это не важно, идём дальше Shift-F9, файл запустился. Хм.. Меня, если често, это смутило. Потому что редкий протектор не использует хотябы IsDebuggerPresent. Ну раз нет так нет...

Finding OEP
Начнём с EP. Точка входа выглядит вот так

Код:

004170D4 > /EB 01          JMP SHORT UnPackMe.004170D7
004170D6  |68 60E80000    PUSH 0E860
004170DB    0000            ADD BYTE PTR DS:[EAX],AL
004170DD    8B1C24          MOV EBX,DWORD PTR SS:[ESP]

Сразу виден древний фокус “прыжок в середину команды”. Суть в том, что когда дизассемблер Olly проанализирует JMP SHORT UnPackMe.004170D7, то естественно примется за следущий байт - следущую комманду, а следущий байт 68h - оппкод команды push xxx. Врезультате чего, дизасемблерный листинг будет неверным. Этого бы не произошло, еслиб команды эмулировались (как в IDA) и байт 68h был бы проанализирован как db 68h. Тут на помощь Olly придёт плагин AnalyzeThis.

Код:

004170D4 > $ /EB 01        JMP SHORT UnPackMe.004170D7
004170D6    |68            DB 68                                                  ;  CHAR 'h'
004170D7  > \60            PUSHAD
004170D8  .  E8 00000000  CALL UnPackMe.004170DD
004170DD  $  8B1C24        MOV EBX,DWORD PTR SS:[ESP]

Вот так... Вдальнейшем этот плагин будет нераз выручать нас от такого рода приёмов.
Сразу же в глаза бросается команда pushad. И естественно сразу вспоминаем “The ESP trick”.

The ESP trick
Суть в том, что протектор не должен наследить, тоесть ему необходимо вернуть значения регистров и выравнить стек. Здесь мне нравится следущее сравнение: “Вы работаете с некоторым текстовым документом, Вы выделяете и копируете первый параграф. Переписываете его, немного меняете как вдруг приходите к заключению, что старый был лучше. Вы вновь выделяете первый параграф и жмёте “paste”.” Естественно pushad – будет играть роль “copy”, а popad – “paste”.
В CommandLine пишем команду “hr esp-4”, он поместит hardware breakpoint на кадр стека, в котором будет лежать первый сохранившийся регистр (это будет eax). Проходим pushad по F8 и запускаем программу (F9). Опять возникает исключение, но мы смело жмём Shift-F9. В результате чего окажемся здесь:

Код:

00418AF5  .  61            POPAD
00418AF6  .  87D2          XCHG EDX,EDX                                          ;  ntdll.KiFastSystemCallRet
00418AF8  .  4A            DEC EDX
00418AF9  .  C7C1 67639244 MOV ECX,44926367
00418AFF  .  85C1          TEST ECX,EAX

Сразу видно, что после popad идёт ненужный мусор, так что смело шагаем по F8.

Stolen bytes
Однако здесь стоит останоиться. Типичная точка входа.. Кроме переходов и мусорных байтов естественно.

00418B43 PUSH EBP
00418B44 JMP SHORT UnPackMe.00418B47
00418B46 DB 34
00418B47 MOV EBP,ESP
00418B49 JMP SHORT UnPackMe.00418B4C
00418B4B DB 0C
00418B4C PUSH UnPackMe.00418B56
00418B51 JMP UnPackMe.0040A485

Скорее всего – это украденые байты. Для полной уверенности я прошёлся по jump’ам до первого call’а.

Fixing IAT
Как Вы наверно уже заметили, протектор ворует байты не только у точки входа, но и у API. Нужно его отучить это делать. Для этого нам понадобиться вызов какой-нибудь функции. Возьмём к примеру следущую

Код:

00418B62  .  FF15 F4114000 CALL NEAR DWORD PTR DS:[4011F4]
Зайдём внутрь...

008900E9 JMP SHORT 008900EC

008900EC MOV EDI,EDI
008900EE PUSH EBP
008900EF MOV EBP,ESP
008900F1 CMP DWORD PTR SS:[EBP+8],0
008900F5 JMP SHORT 008900FE

008900FE JMP SHORT 008900F8

008900F8 JMP kernel32.7C80B6AA

7C80B6AA JE SHORT kernel32.7C80B6C4
7C80B6AC PUSH DWORD PTR SS:[EBP+8]
7C80B6AF CALL kernel32.7C80E074
7C80B6B4 TEST EAX,EAX
7C80B6B6 JE SHORT kernel32.7C80B6C0
7C80B6B8 PUSH DWORD PTR DS:[EAX+4]
7C80B6BB CALL kernel32.GetModuleHandleW
7C80B6C0 POP EBP
7C80B6C1 RETN 4
7C80B6C4 MOV EAX,DWORD PTR FS:[18]
7C80B6CA MOV EAX,DWORD PTR DS:[EAX+30]
7C80B6CD MOV EAX,DWORD PTR DS:[EAX+8]
7C80B6D0 JMP SHORT kernel32.7C80B6C0


Это вызов GetModuleHandleA! Только вот первые 9 байт перенесены по адресу 008900EC. Итак, запоминаем адеса 4011F4,8900E9, 7C80B6A1(GetModuleHandleA).
Поставим hardware breakpoint на GetModuleHandleA. В результате мы будем знать когда и зачем PESpin обращается к GetModuleHandleA. Запускаем программу (F9). И останавливаемся вот здесь:

Код:

004194B4    3808            CMP BYTE PTR DS:[EAX],CL
004194B6    75 03          JNZ SHORT UnPackMe.004194BB

EAX 7C80B6A1 kernel32.GetModuleHandleA
ECX FFFF2BCC

Этот кусок кода проверяет первый байт API на присутсвие breakpoint’а, установленного помещением int3. Вот зачем нужно использовать hardware breakpoint’ы. Идём дальше...

Код:

00417BE7    8B18            MOV EBX,DWORD PTR DS:[EAX]
...
00417BF5    80FB EB        CMP BL,0EB

Протектор проверяет наличие переходов вначале API. Кстати если в начало поставить jmp 401000, то байты украдены не будут. Идём дальше...

Мы окажемся где то здесь

Код:

004181B9  .  C0EC 04      SHR AH,4
004181BC  .  2AC4          SUB AL,AH
004181BE  .^ 73 F6        JNB SHORT UnPackMe.004181B6
004181C0  .  8A47 FF      MOV AL,BYTE PTR DS:[EDI-1]
004181C3  .  24 0F        AND AL,0F
004181C5  .  3C 0C        CMP AL,0C
004181C7  .  75 03        JNZ SHORT UnPackMe.004181CC
004181C9  .  5A            POP EDX
004181CA  .  F7D2          NOT EDX
004181CC  >  42            INC EDX
004181CD  .  3C 00        CMP AL,0
004181CF  .  74 42        JE SHORT UnPackMe.00418213
004181D1  .  3C 01        CMP AL,1
004181D3  .^ 74 DB        JE SHORT UnPackMe.004181B0
004181D5  .  83C7 51      ADD EDI,51
004181D8  .  3C 0A        CMP AL,0A

Мы явно в какойто функции... Нужно обследовать её снчала. А что бы узнать где начало нужно проанализировать стек.

0012FF64 004186BB UnPackMe.004186BB
0012FF68 7C80B6A1 kernel32.GetModuleHandleA
0012FF6C 00417C5A RETURN to UnPackMe.00417C5A from UnPackMe.004180F3
0012FF70 7C80B6A1 kernel32.GetModuleHandleA
0012FF74 004010E7 UnPackMe.004010E7

Функция получает на вход адрес API! Уже интересно! Посмотрим начало функции...

Length disassembler
Код:

004180F3  $  60            PUSHAD
004180F4  .  FC            CLD
004180F5  .  33D2          XOR EDX,EDX
004180F7  .  8B7424 24    MOV ESI,DWORD PTR SS:[ESP+24]
004180FB  .  8BEC          MOV EBP,ESP
004180FD  .  68 1CF79710  PUSH 1097F71C
00418102  .  68 80671CF7  PUSH F71C6780
00418107  .  68 18973817  PUSH 17389718
0041810C  .  68 18B71C10  PUSH 101CB718

Как только я это увидел, у меня сразу же возникло чувство, что где-то я это уже встречал. Точнее я знал где. Что то подобное было в сорце VirXasm32 (это дизассемблер длин такой). Смотрите таблица заносится в стек, затем идут проверки на наличие префикса, затем разбор, поиск в таблице. В итоге функция вернет длину опкода.
Вообще если протектор ворует байты, то в него 100% должен быть встроен дизассемблер длин. Его задача найти длину комманды. Потому что длина команд нефиксированна, а протектеру нужно знать сколько байт копировать. Если скопировать меньше то программа упадёт, что крайне нежелательно. Ну вот с этой функцией разобрались, пройдём её по F8.

Код:

00417C54  > \50            PUSH EAX
00417C55  .  E8 99040000  CALL UnPackMe.004180F3
00417C5A  .  83C4 04      ADD ESP,4
00417C5D  .  91            XCHG EAX,ECX

Заметьте в eax лежит размер “mov edi,edi” – два байта. Идём дальше...

Код:

00417CA3  > \F3:A4        REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
00417CA5  .  8BC6          MOV EAX,ESI
00417CA7  .  8BF7          MOV ESI,EDI

Вот копируется первый оппкод в 008900EС. Дальше покругу, длина команды <–> копипаст. Следовательно где-то впереди должен заноситься адрес 008900E9 в 4011F4. Поставим breakpoint на доступ к 4011F4. И вот оно:

Код:

00417F77  .  8902          MOV DWORD PTR DS:[EDX],EAX
Но вот встаёт проблема! Между

Код:

00417C54  > \50            PUSH EAX
и
Код:

00417F77  .  8902          MOV DWORD PTR DS:[EDX],EAX
протектор так активно работает со стеком и нарезает такие круги, что патчить будет себе дороже. Но ведь можно написать скрипт! Мы знаем где взять настоящий адрес и куда кладётся левый.

Scripting
Собственно всё как я сказал: ставим breakpoint на 00417c54, чтобы получить адрес API, убираем бряк, чтобы не тормазиться при работе дизассемблера длин, ставим breakpoint на 00417F77, чтобы поместить в eax настоящий адрес. Сравнение с 7C834D41 сделано для того чтоб не пропустить конец таблицы и выгрузить скрипт. 7C834D41 – это последняя используемая функция.

Код:

      var func
      var IAT
      var addr

      mov func,00417c54
      mov IAT,00417F77

      bphws func,"x"

next:
      run
      mov addr,eax
      bphwc func
      bphws IAT,"x"
      run
      mov eax,addr
      bphwc IAT
      bphws func,"x"
      cmp eax,7C834D41
      jne next
      bphwc func
      ret

Не забудьте удалить все ненужные breakpoint’ы. Оставить нужно только один – на oep. Запускаем скрипт, тормазимся на oep.

Ну вот...

Код:

00418B43  > \55            PUSH EBP
00418B44  .  EB 01        JMP SHORT UnPackMe.00418B47
00418B46      34            DB 34                                                  ;  CHAR '4'
00418B47  >  89E5          MOV EBP,ESP
00418B49  .  EB 01        JMP SHORT UnPackMe.00418B4C
00418B4B      0C            DB 0C
00418B4C  >  68 568B4100  PUSH UnPackMe.00418B56
00418B51  .- E9 2F19FFFF  JMP UnPackMe.0040A485
00418B56  .  68 220340D1  PUSH D1400322                                          ; /pModule = D1400322 ???
00418B5B  .  810424 DEFCBF>ADD DWORD PTR SS:[ESP],2EBFFCDE                        ; |
00418B62  .  FF15 F4114000 CALL NEAR DWORD PTR DS:[4011F4]                        ; \GetModuleHandleA
00418B68  .  EB 01        JMP SHORT UnPackMe.00418B6B

У меня всё определилось. =)

Dump & Rebuild Import
Снимаем дамп с помощью плагина OllyDump (галочку с Rebuild Import убрать).
Чтож, теперь ImpRec’ом восстановим импорт. Выбираем процесс..

Код:

Target: D:\Cracking\lab\PESpin\UnPackMe_PeSpin1.3.f.exe
OEP: 00018B43        IATRVA: 0000119C        IATSize: 00000034

FThunk: 0000119C        NbFunc: 00000007
1        0000119C        user32.dll        001B        CallNextHookEx
1        000011A0        user32.dll        010F        GetDesktopWindow
1        000011A4        user32.dll        0175        GetWindowRect
1        000011A8        user32.dll        01DD        MessageBoxA
1        000011AC        user32.dll        028B        SetWindowsHookExA
1        000011B0        user32.dll        029A        SystemParametersInfoA
1        000011B4        user32.dll        02AF        UnhookWindowsHookEx

FThunk: 000011EC        NbFunc: 0000000B
1        000011EC        kernel32.dll        00B7        ExitProcess
1        000011F0        kernel32.dll        013F        GetCurrentThreadId
1        000011F4        kernel32.dll        0176        GetModuleHandleA
1        000011F8        kernel32.dll        01EB        GlobalAlloc
1        000011FC        kernel32.dll        01F2        GlobalFree
1        00001200        kernel32.dll        0203        HeapAlloc
1        00001204        kernel32.dll        0204        HeapCompact
1        00001208        kernel32.dll        0205        HeapCreate
1        0000120C        kernel32.dll        0207        HeapDestroy
1        00001210        kernel32.dll        0209        HeapFree
1        00001214        kernel32.dll        03A4        lstrcat

Теперь Fix Dump. Готово! У меня работает!

Links
www.tuts4you.com
www.reversing.be
arteam.accessroot.com

PS
С Вами был taha. О ошибках и неточностях писать в ПМ.


Время: 15:33