Авторские статьи Распаковка PESpin 0.7

Discussion in 'Статьи' started by taha, 10 Dec 2006.

  1. taha

    taha Elder - Старейшина

    Joined:
    20 Aug 2006
    Messages:
    399
    Likes Received:
    330
    Reputations:
    251
    Распаковка PESpin 0.7

    Содержание
    Введени
    Инструменты
    Поиск OEP
    Восстановление импорта
    Умный в гору не пойдёт
    Заключение

    Введение

    Сегодня я покажу как вручную распаковать протектор PESpin. Итак, подопытную программу вы можете скачать тут: http://www.tuts4you.com/blogs/download.php?view.338 Что такое? Там же тутор есть. - скажет читатель. Да есть и кое-где мы будем на него опираться. Просто я расскажу больше чем в туторе. Вы в этом убедитесь. Эта статья посвящена скорее тому, как обойти злобный протектор.

    Инструменты
    Ollydbg + plugins(OllyScript,OllyDump,CmdBar)
    Imp Rec
    И всё!!!

    Поиск OEP
    Грузим прогу в Olly. Давайте просто запустим программу, F9. Она тормозится на каком то исключении, нам первые исключения не интересны. Идём дальше.

    Code:
    004001C9   FFFF             ???                                      ; Unknown command
    004001CB   FFFF             ???                                      ; Unknown command
    004001CD   FFFF             ???                                      ; Unknown command
    004001CF   FFFF             ???                                      ; Unknown command
    004001D1   FFFF             ???                                      ; Unknown command
    004001D3   FFFF             ???                                      ; Unknown command
    004001D5   FFFF             ???                                      ; Unknown command
    004001D7   FFFF             ???                                      ; Unknown command
    Code:
    00407F17   2BDB             SUB EBX,EBX
    00407F19   64:8F03          POP DWORD PTR FS:[EBX]
    00407F1C   58               POP EAX
    00407F1D   5D               POP EBP
    И программа запущена.
    Скорее всего OEP будет идти после последнего или предпоследнего исключения (чаще после последнего).
    Ставим бряк на esp-4.Через CmdBar, набрав hr esp-4. Или можно, пройдя pushad, выбрать в окне регистров esp, нажать на правую и выбрать Follow in dump.
    В окне дампа, выделяем первые четыре байта, на правую Breakpoint > Hardware, on access > Dword.
    F9, и идём до последнего исключения, ещё Shift-F9.
    Ctrl-A - и Олька анализирует код.

    Code:
    00407087   61               POPAD
    00407088   6A 00            PUSH 0 ; <----- Мы тут
    0040708A   EB 01            JMP SHORT packed.0040708D
    0040708C   E3 68            JECXZ SHORT packed.004070F6
    0040708E   97               XCHG EAX,EDI
    0040708F   70 40            JO SHORT packed.004070D1
    00407091   00E9             ADD CL,CH
    Дальше трейсим по F8. После прыжка попадаем на пару байт ниже.

    Code:
    0040708D   68 97704000      PUSH packed.00407097
    00407092  -E9 7DA0FFFF      JMP packed.00401114
    Мда странно. Зачем такие переходы? Лан идём дальше.

    Code:
    00401114   .-E9 04EF4600    JMP 0087001D
    00401119     FF             DB FF
    0040111A   $-E9 6BEF4600    JMP 0087008A
    0040111F     FF             DB FF
    Хм ничего не напоминает? Мне да!! Переходы на импортируемые функции. Но нефакт.
    Дальше.

    0087001D EB 01 JMP SHORT 00870020

    Code:
    00870020   8BFF             MOV EDI,EDI                              ; packed.00407B1D
    00870022   55               PUSH EBP
    00870023   8BEC             MOV EBP,ESP
    00870025   837D 08 00       CMP DWORD PTR SS:[EBP+8],0
    00870029   EB 07            JMP SHORT 00870032
    Идём до прыжка, и f8.

    00870032 ^EB F8 JMP SHORT 0087002C
    Дальше.

    0087002C -E9 01B5F97B JMP kernel32.7C80B532

    Ещё =).

    7C80B532 74 18 JE SHORT kernel32.7C80B54C

    Мы в kernel32, а если взглянуть ниже иожно увидеть вызов GetModuleHandleW.

    Посмотрим на пару байт выше.

    Code:
    7C80B529 > 8BFF             MOV EDI,EDI
    7C80B52B   55               PUSH EBP
    7C80B52C   8BEC             MOV EBP,ESP
    7C80B52E   837D 08 00       CMP DWORD PTR SS:[EBP+8],0
    Помоему я где то это уже видел. Смотри 00870020. Хех он что, у API байты ворует? Откуда всё началось?

    Code:
    00407088   6A 00            PUSH 0 ;
    0040708A   EB 01            JMP SHORT packed.0040708D
    Протектор возвращяет на место значения раегостров (popad). А дальше судя по всему идёт API. OEP? Надо проверить нашу догадку. Трейсим дальше.

    Скажем, не далеко от вызова GetModuleHendleA, мы попадаем в USER32, посмотрев ниже, мы видим:

    Code:
    77D4891B   6A 02            PUSH 2
    77D4891D   FF75 18          PUSH DWORD PTR SS:[EBP+18]
    77D48920   FF75 14          PUSH DWORD PTR SS:[EBP+14]
    77D48923   FF75 10          PUSH DWORD PTR SS:[EBP+10]
    77D48926   50               PUSH EAX
    77D48927   56               PUSH ESI
    77D48928   E8 69DFFFFF      CALL USER32.DialogBoxIndirectParamAorW
    Это скорее всего DialogBoxParamA. Из чего можно сделать вывод, что 00407088 - это искомый OEP. Удаляем бряк с esp.

    Ну и снимаем дамп естественно, через OllyDump.

    Т.к. до OEP, в процессе исследования, придётся доходить часто, напишем скрипт:

    go_to_OEP.txt
    Code:
    	var real_OEP
    	mov real_OEP,00407088	// найденый OEP
    	var ex_OEP
    	mov ex_OEP,00407F17	// последнее исключение
    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
    ex_OEP - это то самое исключение, после которого идёт OEP. Сначала мы идём до последнего исключения. Это делается для того, что бы нам ничего не мешало в процессе прохода до OEP. Вобщем после того как доходим до последнего исключения, ставим бряк на OEP и esto (Shift-F9). Мы на OEP. Согласитесь, использовать скрипт очень удобно, он экономит время.

    Восстановление импорта
    Да, будет нелегко, но мы справимся.
    Итак, перезапускаем программу, вызываем наш скрипт go_to_oep.
    Т.к. протектор прыгает на пару байт дальше адреса функции, ImpRec нам не поможет. Придётся делать список функций, что бы составиь файл для ImpRec. Есть несколько способов решения данной проблемы.
    Первый:
    Нужно поставтить бряки на все вызовы функций (я имею ввиду на jump'ы начиная с 0040110E). И запустить программу (F9), дальше, когда сработают бряки, идти до функций и подниматься на пару байт вверх. F10 > Search for >Name in all modules.
    Опять F10 > Sort by > Address (Это упростит поиск). Ищем полученный адрес.
    Например:

    Первый раз бряк сработает тут ( GetModuleHandleA, но мы этого пока не знаем =) )
    00401114 .-E9 04EF4600 JMP 0087001D

    F8 пока не попадём сюда:
    7C80B532 74 18 JE SHORT kernel32.7C80B54C

    Вверх на пару байт. Как мы знаем функция начинается c
    7C80B529 > 8BFF MOV EDI,EDI

    Следовательно в окне All names ищем 7C80B529.
    7C80B529 .text Export GetModuleHandleA

    И так с каждой функцией.

    Втрой:
    Мы всё ещё на OEP.
    Откроем окно memory map (Alt-M), и посмотрим как в памяти рапсоложенна наша прога.

    Code:
    00400000   00001000   packed                PE header     Imag   RWE       RWE
    00401000   00001000   packed     .petite    code          Imag   RWE       RWE
    00402000   00001000   packed     .petite    data          Imag   RWE       RWE
    00403000   00001000   packed     .petite                  Imag   RWE       RWE
    00404000   00002000   packed     .rsrc      resources     Imag   RWE       RWE
    00406000   00003000   packed     .petite    SFX,imports   Imag   RWE       RWE
    00410000   00103000                                       Map    R         R
    00520000   00129000                                       Map    R E       R E
    00820000   00003000                                       Map    R         R
    00830000   00008000                                       Priv   RW        RW
    00840000   00001000                                       Priv   RW        RW
    00850000   00001000                                       Priv   RW        RW
    00860000   00004000                                       Priv   RW        RW
    00870000   00001000                                       Priv   RWE       RWE
    5D5B0000   00001000   COMCTL32              PE header     Imag   R         RWE
    5D5B1000   00070000   COMCTL32   .text      code,imports  Imag   R         RWE
    Мы знаем, что в процессе работы она гуляет по всем байтам до 5D5B0000.
    Вернёмся назад.
    Мы знаем, что код программы и протетора выполняется от 00400000 до 5DB0000. Давайте задаим параметры трассировки Debug > Set condition (Ctrl-T).
    В первом и во втором полях EIP is outside the range пишем 00400000 и 5DB0000 соответственно. Ставим галочку напротив EIP is outside the range, OK.
    Ctrl-F11 и мы тут:
    7C80B532 74 18 JE SHORT kernel32.7C80B54C
    Ну дальше опять поднимаемся на пару байт вверх. Ищем адрес функции в Name in all Modules.

    И когда мы закончим у нас будет вот такой список:
    Code:
    KERNEL32----------------
    
    40110E
    All names, item 17785
     Address=7C81CAA2 kernel32
     Section=.text
     Type=Export  (Known)
     Name=ExitProcess
    
    401114
    All names, item 5136
     Address=7C80B529 kernel32
     Section=.text
     Type=Export  (Known)
     Name=GetModuleHandleA
    
    USER32----------------
    
    
    40111A
    Names in USER32, item 15
     Address=77D3B4B1
     Section=.text
     Type=Export  (Known)
     Name=BeginPaint
    
    401120
    All names, item 1707
     Address=77D488E1 USER32
     Section=.text
     Type=Export  (Known)
     Name=DialogBoxParamA
    
    401126
    All names, item 9904
     Address=77D46CC9 USER32
     Section=.text
     Type=Export  (Known)
     Name=EndDialog
    
    40112C
    All names, item 9704
     Address=77D3B4C5 USER32
     Section=.text
     Type=Export  (Known)
     Name=EndPaint
    
    401132
    All names, item 9901
     Address=77D467A8 USER32
     Section=.text
     Type=Export  (Known)
     Name=LoadBitmapA
    
    401138
    Names in USER32, item 825
     Address=77D3E2AE
     Section=.text
     Type=Export  (Known)
     Name=SendMessageA
    
    
    GDI32----------------
    
    40113E
    All names, item 12302
     Address=77F16DC0 GDI32
     Section=.text
     Type=Export  (Known)
     Name=BitBlt
    
    401144
    All names, item 12283
     Address=77F15E10 GDI32
     Section=.text
     Type=Export  (Known)
     Name=CreateCompatibleDC
    
    40114A
    All names, item 12301
     Address=77F16CA6 GDI32
     Section=.text
     Type=Export  (Known)
     Name=DeleteDC
    	
    401150
    All names, item 12300
     Address=77F16A3B GDI32
     Section=.text
     Type=Export  (Known)
     Name=DeleteObject
    
    401156
    All names, item 12279
     Address=77F159A0 GDI32
     Section=.text
     Type=Export  (Known)
     Name=SelectObject
    Дальше нам нужно составить файл импорта для ImpRec.

    paced.txt
    Code:
    Target: C:\packed.exe
    OEP: 00007088	IATRVA: 0000306C	IATSize: 00000100
    
    FThunk: 00003070	NbFunc: 00000006
    1	00003070	user32.dll	000E	BeginPaint
    1	00003074	user32.dll	009F	DialogBoxParamA
    1	00003078	user32.dll	00C7	EndDialog
    1	0000307C	user32.dll	00C9	EndPaint
    1	00003080	user32.dll	01B6	LoadBitmapA
    1	00003084	user32.dll	023C	SendMessageA
    
    FThunk: 0000308C	NbFunc: 00000002
    1	0000308C	kernel32.dll	00B0	ExitProcess
    1	00003090	kernel32.dll	016F	GetModuleHandleA
    
    FThunk: 00003098	NbFunc: 00000005
    1	00003098	gdi32.dll	0013	BitBlt
    1	0000309C	gdi32.dll	002E	CreateCompatibleDC
    1	000030A0	gdi32.dll	008D	DeleteDC
    1	000030A4	gdi32.dll	0090	DeleteObject
    1	000030A8	gdi32.dll	020F	SelectObject
    Открывеам ImpRec вибираем процесс и жмём на Load tree, выбираем packed.txt. Fix'ируем на dumped.exe.

    Запускаем наш dumped_.exe. Что такое? он не работает =(. Ну естественно надо ещё jump'ы подправить.

    Открываем dumped_.exe. Ctrl-G > 0040110E. Мда гора мусора.
    Code:
    0040110E     E9             DB E9
    0040110F     ED             DB ED
    00401110     EE             DB EE
    00401111     46             DB 46                                    ;  CHAR 'F'
    00401112     00             DB 00
    Ctrl-B,галочка с keep size убрана, пишем FF25, Ok. Появился jump.
    Code:
    0040110E     FF25 EE4600FF  JMP DWORD PTR DS:[FF0046EE]
    Теперь откопируем его столько раз, сколько у нас функций.
    Теперь исправляем адреса. Адреса функций храняться в .mackt. У меня это адрес 00409000. Там лежат адреса наших функций. Так вот исправляем наши джампы.
    Вместо FF0046EE будет адрес адреса функции. Вот:
    Code:
    0040110E    -FF25 A2CA817C  JMP DWORD PTR DS:[kernel32.ExitProcess]
    Все jump должны остаться на своиз местах, нльзя менять местами функции.
    Как мне разобраться что есть что в окне дампа .mackt? - спросит читатель. Действительно трудно, но вспомнить адреса апи вам поможет плагин APIFinder.

    Умный в гору не пойдёт
    Как всё геморойно! Скажет чиатель и будет прав. Я тоже не люблю возиться с импортом. Главное найти OEP, а там.... Но этот протектор ворует байты у API, что очень затрудняет взлом. И тут вступаю в дело я =)).
    А что если написать скрипт который сам восстановит IAT. Приступим.
    Итак код прохода до OEP есть.
    Вернёмся в packed.exe.
    Что делать дальше? Идея очень проста. Поставим бряки на все джампы.
    Code:
    	var y // первый jump
    	mov y,0040110E
    f_IAT:
    	bp y
    	add y,6
    	cmp y,0040115C
    	jne f_IAT
    	eob f_bpx
    	run
    Дальше по мере срабатывания бряков, мы будим их удалять. Когда сработает бряк, мы будем к примеру тут:
    00401114 .-E9 04EF4600 JMP 0087001D
    Введём переменную, в которую будем сохранять адрес jmp.
    var otcuda
    Проходим jmp'ы:
    sto
    sto
    И мы тут:
    00870020 8BFF MOV EDI,EDI ; packed.00407B1D
    Нужно посчитать растояние до перехода к переходу на переход к функции =). Тобишь до сюда:
    00870029 EB 07 JMP SHORT 00870032
    Мы же должны знать сколько байт спёр протектор.
    Code:
    	findop eip,#EB07#
    	mov count,$RESULT
    	sub count,eip
    Конечно перед этим вводим переменную count. Дальше идём до этого jump'а:
    Code:
    sol_byte:
    	sti
    	cmp eip,$RESULT
    	jne sol_byte
    Проходим джампы:
    Code:
    	sto
    	sto
    	sto
    И мы в kernel32, ну или где там будем. Вычисляем адрес начала функции:
    Code:
    	mov x,eip
    	sub x,count
    и сохраняем его в какю-нибудь не нужную программе область. Я выбрал 0040115F.
    Пишем туда адрес функции и снимаем бряк с джампа.
    Code:
    	mov [cuda],x	
    	bc otcuda
    Теперь на место старого пишем новый.
    Code:
    	mov [otcuda],25FF
    	add otcuda,2
    	mov [otcuda],cuda
    Увеличиваем cuda на 4. И:
    Code:
    	run
    	jmp f_bpx
    Вот скрипт:
    Code:
    	var real_OEP
    	mov real_OEP,00407088	// найденый OEP
    
    	var ex_OEP
    	mov ex_OEP,00407F17	// последнее исключение
    ff:
    	eoe fOEP
    	esto
    fOEP:
    	cmp eip,ex_OEP
    	jne ff
    	bp real_OEP
    	esto
    	bc real_OEP
    	cmt eip," <------------- OEP"
    	an real_OEP
    
    	var count
    	var x
    	var y
    	mov y,0040110E
    	var otcuda
    	var cuda
    	mov cuda,0040115F
    
    f_IAT:
    	bp y
    	add y,6
    	cmp y,0040115C
    	jne f_IAT
    	eob f_bpx
    	run
    f_bpx:	
    	mov otcuda,eip
    	sto
    	sto
    	findop eip,#EB07#
    	mov count,$RESULT
    	sub count,eip
    
    sol_byte:
    	sti
    	cmp eip,$RESULT
    	jne sol_byte
    
    	sto
    	sto
    	sto
    	mov x,eip
    	sub x,count
    	mov [cuda],x	
    	bc otcuda
    	mov [otcuda],25FF
    	add otcuda,2
    	mov [otcuda],cuda
    	add cuda,4
    	run
    	jmp f_bpx
    Запускаем его. После того как окно запустится и перестанет бегать строчка: Ctrl-G > 0040110E. И вот что там:
    Code:
     
    0040110E   .-E9 EDEE4600    JMP 00870000
    00401113     FF             DB FF
    00401114    -FF25 5F114000  JMP DWORD PTR DS:[40115F]                ;  kernel32.GetModuleHandleA
    0040111A    -FF25 6B114000  JMP DWORD PTR DS:[40116B]                ;  USER32.BeginPaint
    00401120    -FF25 63114000  JMP DWORD PTR DS:[401163]                ;  USER32.DialogBoxParamA
    00401126   $-E9 21EF4600    JMP 0087004C
    0040112B     FF             DB FF
    0040112C    -FF25 7B114000  JMP DWORD PTR DS:[40117B]                ;  USER32.EndPaint
    00401132    -FF25 67114000  JMP DWORD PTR DS:[401167]                ;  USER32.LoadBitmapA
    00401138   $-E9 81EF4600    JMP 008700BE
    0040113D     FF             DB FF
    0040113E    -FF25 77114000  JMP DWORD PTR DS:[401177]                ;  GDI32.BitBlt
    00401144    -FF25 6F114000  JMP DWORD PTR DS:[40116F]                ;  GDI32.CreateCompatibleDC
    0040114A    -FF25 7F114000  JMP DWORD PTR DS:[40117F]                ;  GDI32.DeleteDC
    00401150   $-E9 AAEF4600    JMP 008700FF
    00401155     FF             DB FF
    00401156    -FF25 73114000  JMP DWORD PTR DS:[401173]                ;  GDI32.SelectObject
    Да определились не все функции, но ведь не все бряки сработали. Закрываем программу. И строчка опять побежала, после остановки: Ctrl-G > 0040110E.
    Code:
    0040110E   .-FF25 8F114000  JMP DWORD PTR DS:[40118F]                ;  kernel32.ExitProcess
    00401114   .-FF25 5F114000  JMP DWORD PTR DS:[40115F]                ;  kernel32.GetModuleHandleA
    0040111A   $-FF25 6B114000  JMP DWORD PTR DS:[40116B]                ;  USER32.BeginPaint
    00401120   .-FF25 63114000  JMP DWORD PTR DS:[401163]                ;  USER32.DialogBoxParamA
    00401126   $-FF25 8B114000  JMP DWORD PTR DS:[40118B]                ;  USER32.EndDialog
    0040112C   $-FF25 7B114000  JMP DWORD PTR DS:[40117B]                ;  USER32.EndPaint
    00401132   $-FF25 67114000  JMP DWORD PTR DS:[401167]                ;  USER32.LoadBitmapA
    00401138   $-FF25 83114000  JMP DWORD PTR DS:[401183]                ;  USER32.SendMessageA
    0040113E   $-FF25 77114000  JMP DWORD PTR DS:[401177]                ;  GDI32.BitBlt
    00401144   $-FF25 6F114000  JMP DWORD PTR DS:[40116F]                ;  GDI32.CreateCompatibleDC
    0040114A   $-FF25 7F114000  JMP DWORD PTR DS:[40117F]                ;  GDI32.DeleteDC
    00401150   $-FF25 87114000  JMP DWORD PTR DS:[401187]                ;  GDI32.DeleteObject
    00401156   $-FF25 73114000  JMP DWORD PTR DS:[401173]                ;  GDI32.SelectObject
    0040115C     00             DB 00
    0040115D     00             DB 00
    0040115E     00             DB 00
    0040115F   . 29B5807C       DD kernel32.GetModuleHandleA
    00401163   . E188D477       DD USER32.DialogBoxParamA
    00401167   . A867D477       DD USER32.LoadBitmapA
    0040116B   . B1B4D377       DD USER32.BeginPaint
    0040116F   . 105EF177       DD GDI32.CreateCompatibleDC
    00401173   . A059F177       DD GDI32.SelectObject
    00401177   . C06DF177       DD GDI32.BitBlt
    0040117B   . C5B4D377       DD USER32.EndPaint
    0040117F   . A66CF177       DD GDI32.DeleteDC
    00401183   . AEE2D377       DD USER32.SendMessageA
    00401187   . 3B6AF177       DD GDI32.DeleteObject
    0040118B   . C96CD477       DD USER32.EndDialog
    0040118F   . A2CA817C       DD kernel32.ExitProcess
    Копируем всё это великолепие через binary copy.
    Перезапускаем программу, идём до OEP, Ctrl-A, Ctrl-G > 0040110E, вставляем всё что накопровали.
    Ctrl-A, поднимитесь выше!!
    Code:
    004010A5  |. 50             PUSH EAX                                 ; /pPaintstruct
    004010A6  |. FF75 08        PUSH DWORD PTR SS:[EBP+8]                ; |hWnd
    004010A9  |. E8 6C000000    CALL packed.0040111A                     ; \BeginPaint
    004010AE  |. 8945 BC        MOV DWORD PTR SS:[EBP-44],EAX
    004010B1  |. FF75 BC        PUSH DWORD PTR SS:[EBP-44]               ; /hDC
    004010B4  |. E8 8B000000    CALL packed.00401144                     ; \CreateCompatibleDC
    Без комментариев!!

    Заключение
    Не парься %)!
     
    3 people like this.
  2. taha

    taha Elder - Старейшина

    Joined:
    20 Aug 2006
    Messages:
    399
    Likes Received:
    330
    Reputations:
    251
    Если немного изменить мой скрипт, может прокатить и с PESpin 1.304.