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

Форум АНТИЧАТ (https://forum.antichat.xyz/index.php)
-   Авторские статьи (https://forum.antichat.xyz/forumdisplay.php?f=31)
-   -   Снятие ASProtect 1.22 - 1.23 Beta 21 и патчинг Lemonade Tycoon v1.1.6 (https://forum.antichat.xyz/showthread.php?t=22617)

ProTeuS 14.08.2006 17:08

Снятие ASProtect 1.22 - 1.23 Beta 21 и патчинг Lemonade Tycoon v1.1.6
 
Мишень: Lemonade Tycoon v1.1.6(2100кб, ftp://ftp.gamehouse.com/pub/LemonadeTycoonInstall.exe)
Что еще нужно:
1. OllyDbg + плагин OllyDump
2. ImpRec 1.6 Final
3. IDA
4. Hiew или любой другой шестнадцятиричный редактор
4. Пиво "Beck's", 0.5 ;)

Жертва
Нашей мишенью является одна незамысловатая играшка. Но самое важное, что она запротекчена "ASProtect 1.22 - 1.23 Beta 21", о чем нам любезно поведал PEID:

http://securityprobe.net/other/ProTe..._protected.jpg
Жертва пока недосягаема для жестоких козней дизассемблера ;)

Поиск OEP
Пользоваться автораспаковщиками вроде AsprStripperXP мы не будем и поэтому запустим OllyDbg и откроем в отладчике исполняемый файл проги.

Для успешного нахождения оригинальной точки входа жертвы мы должны включить в ольке обработку исключений (memory access voilation, access violation). Заходим в опции отладки (нажав Alt+O) и устанавливаем флажки как на рисунке ниже:
http://securityprobe.net/other/ProTe...exceptions.jpg
Включение обработки исключений при работе с памятью

возвращаемся в окно "CPU" OllyDbg иначинаем проходить исключения(нажимая Shift+F9). После 27 раз (если еще один раз нажать Shift+F9, наша прога пройдет OEP и запустится) мы появимся по адресу .D405CC
http://securityprobe.net/other/ProTe...exceptions.jpg
Окно "CPU" после обработки 27 исключений

Далее необходимо поставить точку останова на команде RETN (.D40609) и приказать ольке "дойти" до нее нажатием Shift+F9 еще раз. Последним этапом в поиске OEP будет установка бряка на доступ аспром к кодовой секции защищаемого им приложения. Для этого нажимаем Alt+M (чтобы вызвать окно "Memory Map") и жмем F2 на второй по счету секции заданного процесса. В нашем случае это Lemonade codesection по адресу 00401000.

http://securityprobe.net/other/ProTe...odesection.jpg
Бряк на доступ к кодовой секции

Последний раз жмякаем по Shift+F9 и ВСЕ!!! Мы на OEP. Чтобы всетаки в это поверить, можно нажать Ctrl+A и олька проведет анализ кода и дизассемблерный листинг вместо непонятных однобайтным выражений обретет "человеческий" вид =)


Делаем дамп

Выбираем в главном меню Plugins-OllyDump-Dump debugged process и в появившемся окне нажимаем "Dump" (перед этим не забыв снять флаг "Rebuild import", чтобы OllyDump самостоятельно не пытался восстанавливать импорт дампа).

http://securityprobe.net/other/ProTe...on/dumping.jpg
Делаем дамп с помощью плагина OllyDump

Восстановление импорта

Просто так дамп (я назвал файл dumped.exe) запускаться не будет - ему нужно восстановить таблицу импортированных функций. Для этого запустим ImpRec и выберем в ней наш исследуемый процесс Lemonade.exe (он будет все еще "стоять" на OEP, ведь Olly вместе с ним мы еще не закрывали). В поле "OEP" вводим адрес нашей OEP(который равен 4FB6B) и кликаем по "IAT AutoSearch"-"Get Imports"-"Show Invalid". Получаем пару сотен неопределенных функций. Для их восстановления попытаемся провести трассировку в 2 этапа. В первом щелкнем правой кнопкой мыши на одной из выделенных неопределенных функций и выбираем в меня "Trace Level1 (Disasm)". Во втором этапе заново щелкаем по "Show invalid", а далее восстанавливаем функции с помощью плагина Plugin Tracers - Asprotect 1.22.

http://securityprobe.net/other/ProTe...nstruction.jpg
Imprec восстанавливает импорт...

После проведенных манипуляций в окне логов появится такая надпись

http://securityprobe.net/other/ProTe...no_invalid.jpg
Импорт удачно восстановлен...

Это говорит о том, что импорт восстановлен и можно произвести модификацию нашего дампа (dumped.exe) нажатием на кнопке "Fix Dump"

http://securityprobe.net/other/ProTe...ixing_dump.jpg
Дамп удачно модифицирован...

Итак, мы получили ПОЛНОСТЬЮ работоспособный распакованый файл (dumped_.exe)!

http://securityprobe.net/other/ProTe...nprotected.jpg
PEID подтверждает, что аспр удачно снят

Приступим к его анализу...

ProTeuS 14.08.2006 17:14

Отладка и взлом жертвы с помощью патча

Загружаем распакованную прогу в отладчике. Поскольку для ввода регистрационных данный она использует стандартные окна ввода,


http://securityprobe.net/other/ProTe...ng_reginfo.jpg
Окно ввода регистрационных данных

и стандартное окно ошибки, то было решино по старинке установить точки останова на вызовах функций MessageBoxA (выводящих сообщения об ошибке) или GetDlgItemTextA (считывающих вводимый текст).


http://securityprobe.net/other/ProTe...ting_Break.jpg
Установка бряков на вызове функций MessageBoxA

Нажатие на кнопку "Enter License Information" привело на такой кусок кода в ольке:


http://securityprobe.net/other/ProTe...MessageBox.jpg
Прервались на выводе ошибке длинны регкода (она должна быть равна 20)

Каким же было мое удивление, когда протрассируя участки с проверкой длин введенных регистрационных данных (.0042563A), кучей строковых и арифметических преобразований с ними (.00425692) и записью сомнительных ключей в реестр я не обнаружил их последующего чтения ни установкой бряков на соответствующих апи, ни с помощью RegMon'а (разумеется, все тестировалось после повторной загрузки программы)! Оказалось, что причиной тому был нижеприведенный участок кода:

Код:

seg000:00425D99                call    esi ; DialogBoxParamA ; Create a modal dialog box from a
seg000:00425D99                                        ; dialog box template resource
seg000:00425D9B                mov    edi, eax
seg000:00425D9D
seg000:00425D9D loc_425D9D:                            ; CODE XREF: sub_425C59+12Dj
seg000:00425D9D                cmp    edi, 3EBh
seg000:00425DA3                jnz    short loc_425DAA ; jmp если не вводились рег.данные
seg000:00425DA5                call    sub_425228      ; CreateProcessA
seg000:00425DAA
seg000:00425DAA loc_425DAA:                            ; CODE XREF: sub_425C59+14Aj
seg000:00425DAA                mov    eax, edi
seg000:00425DAC
seg000:00425DAC loc_425DAC:                            ; CODE XREF: sub_425C59+178j
seg000:00425DAC                pop    edi
seg000:00425DAD                pop    esi
seg000:00425DAE                pop    ebx
seg000:00425DAF                leave
seg000:00425DB0                retn

Т.е., после ввода рег.данных, главное окно приложения просто дезактивировалось и приложение закрывалось. Но самое интересное, что до этого (.00425DA5) шел вызов функции, которая создавала новый процесс-копию только что запущенного нами процесса-жертвы(аля CopeMemII армы, хотя процессу взлома этот финт, по-моему, никакой сложности не прибавил, кроме как необычности). Вот кусок кода процедуры sub_425228, котоый это подтверждает.

Код:

seg000:00425278                lea    eax, [ebp+ProcessInformation]
seg000:0042527B                mov    [ebp+StartupInfo.cb], 44h
seg000:00425282                mov    [ebp+StartupInfo.dwFlags], 40h
seg000:00425289                push    eax            ; lpProcessInformation
seg000:0042528A                lea    eax, [ebp+StartupInfo]
seg000:0042528D                push    eax            ; lpStartupInfo
seg000:0042528E                push    esi            ; lpCurrentDirectory
seg000:0042528F                push    esi            ; lpEnvironment
seg000:00425290                push    esi            ; dwCreationFlags
seg000:00425291                push    esi            ; bInheritHandles
seg000:00425292                push    esi            ; lpThreadAttributes
seg000:00425293                push    esi            ; lpProcessAttributes
seg000:00425294                lea    eax, [ebp+ApplicationName]
seg000:0042529A                push    esi            ; lpCommandLine
seg000:0042529B                push    eax            ; lpApplicationName
seg000:0042529C                call    ds:CreateProcessA
seg000:004252A2                pop    esi
seg000:004252A3                leave
seg000:004252A4                retn
seg000:004252A4 sub_425228      endp
seg000:004252A4

Пролистав код немного выше, я понял почему записаные в реестре рег.данные не считывались при запуске. Оказалось, что при создани процесса создавался уникальный мютекс, который "давал знать" родительскому процессу о том, запущен ли дочерний процесс в качестве "запустить игрушку", или в качестве "проверить корректность рег.данных". Поскольку проверки после ввода имени-кода проходили уже в доцернем процессе, а в OllyDbg их отловить не удавалось (за момент подключения к толькочто созданному процессу все проверки уже были пройденными, а возможности использования достойного ring0-mode отладчика не было), то я решил пойти на некоторую хитрость: модифицировать бинарник исследуемого файла таким образом, чтобы первой из строк его исполняемой функций (например, WinMain) было зацикливания (оппкоды EBFE). Таким образом созданный процесс зациклился и я бы смог подключится к нему вовремя и восстановив оригинальные 2 байта кода спокойно поисследовать нужные участки. Но, к сожалению, пробуя реализовать описанный метод было потрачено полчаса, а дочерний процесс никак не зацикливался! Не желая больше тратить время на нахождение причины (которая, видимо, состояла в дублировании функций Main в жертве) я решил пойти более "традиционным" методом и поставить бряки в родительском процессе на вызов функий TerminateProcess и ExitProcess ;)


http://securityprobe.net/other/ProTe...ateProcess.jpg
Ставим бряки на вызовы функций завершения работы приложения

Запускаем на выполнение наш файл, вводим необходимые рег.данные (я вводил имя: ProTeuS и код: 1234567890abcdeABCDE) и без проблем получаем искомую точку (.44А841)


http://securityprobe.net/other/ProTe...coon/break.jpg
Обнаружение точки вызова функции завершения работы приложения

поднявщись на несколько уровней вверх по структуре дизассемблерного листинга в IDA видим такой код:

ProTeuS 14.08.2006 17:20

Код:

seg000:004247A0
seg000:004247A0 ; --------------- S U B R O U T I N E ---------------------------------------
seg000:004247A0
seg000:004247A0 ; Attributes: bp-based frame
seg000:004247A0
seg000:004247A0 sub_4247A0      proc near              ; CODE XREF: sub_437940+26p
seg000:004247A0                                        ; sub_437940+3Bp
seg000:004247A0
seg000:004247A0 var_200        = dword ptr -200h
seg000:004247A0
seg000:004247A0                push    ebp
seg000:004247A1                mov    ebp, esp
seg000:004247A3                sub    esp, 200h
seg000:004247A9                push    esi
seg000:004247AA                push    edi
seg000:004247AB                call    sub_437940
seg000:004247B0                mov    dword ptr [eax+20h], offset aLemonade ; "Lemonade"
seg000:004247B7                call    sub_437940
seg000:004247BC                mov    dword ptr [eax+24h], offset aLemonadeTycoon ; "Lemonade Tycoon"
seg000:004247C3                call    sub_437940
seg000:004247C8                mov    dword ptr [eax+28h], offset a1_1_5 ; "1.1.5"
seg000:004247CF                call    sub_437940
seg000:004247D4                mov    dword ptr [eax+28h], offset a1_1_6 ; "1.1.6"
seg000:004247DB                call    sub_437940
seg000:004247E0                mov    dword ptr [eax+1Ch], offset aB6081ca706b415 ; "{B6081CA-706B-415E-AE52-910C4FB06016}"
seg000:004247E7                call    sub_437940
seg000:004247EC                mov    dword ptr [eax+10h], offset a1_0 ; "1.0"
seg000:004247F3                call    sub_437940
seg000:004247F8                mov    dword ptr [eax+14h], offset a72733b3Ac0f4d4 ; "{72733B3-AC0F-4D43-BED1-25EE1194A7BA}"
seg000:004247FF                call    sub_437940
seg000:00424804                mov    dword ptr [eax+18h], offset a48033dc6A54144 ; "{48033DC6-A541-4454-A9CE-3186C3365B75}"
seg000:0042480B                call    sub_437940
seg000:00424810                mov    dword ptr [eax+88h], 96h
seg000:0042481A                call    sub_437940
seg000:0042481F                xor    edi, edi
seg000:00424821                push    68h
seg000:00424823                mov    [eax+34h], edi
seg000:00424826                pop    esi
seg000:00424827
seg000:00424827 loc_424827:                            ; CODE XREF: sub_4247A0+98j
seg000:00424827                call    sub_437940
seg000:0042482C                mov    [eax+esi], edi
seg000:0042482F                add    esi, 4
seg000:00424832                cmp    esi, 84h
seg000:00424838                jl      short loc_424827
seg000:0042483A                call    sub_4263DD      ; функЦия проверки
seg000:0042483F                test    eax, eax
seg000:00424841                jz      short loc_42484A
seg000:00424843                push    1              ; int
seg000:00424845                call    _exit
seg000:0042484A ; ---------------------------------------------------------------------------
seg000:0042484A
seg000:0042484A loc_42484A:                            ; CODE XREF: sub_4247A0+A1j
seg000:0042484A                call    sub_4264BC      ; eax = ds:dword_57F99C
seg000:0042484F                test    eax, eax
seg000:00424851                jz      short loc_42489B ; JMP if eax=1 (в до4ернем процессе)
seg000:00424853                mov    esi, ds:GetModuleHandleA
seg000:00424859                lea    eax, [ebp+var_200]
seg000:0042485F                push    eax            ; char *
seg000:00424860                push    edi            ; lpModuleName
seg000:00424861                call    esi ; GetModuleHandleA
seg000:00424863                push    eax            ; hInstance
seg000:00424864                call    sub_426404
seg000:00424869                pop    ecx
seg000:0042486A                lea    eax, [ebp+var_200]
seg000:00424870                pop    ecx
seg000:00424871                mov    ds:dword_57F988, edi
seg000:00424877                push    eax            ; int
seg000:00424878                push    edi            ; lpModuleName
seg000:00424879                call    esi ; GetModuleHandleA
seg000:0042487B                push    eax            ; hInstance
seg000:0042487C                call    sub_425C59      ; ReadParametrs
seg000:00424881                pop    ecx
seg000:00424882                cmp    eax, 3EBh
seg000:00424887                pop    ecx
seg000:00424888                jnz    short loc_424891
seg000:0042488A                push    1              ; int
seg000:0042488C                call    _exit          ; закрываем приложением после ввода рег.данных
seg000:00424891 ; ---------------------------------------------------------------------------
seg000:00424891
seg000:00424891 loc_424891:                            ; CODE XREF: sub_4247A0+E8j
seg000:00424891                mov    ds:dword_57F988, 1
seg000:0042489B
seg000:0042489B loc_42489B:                            ; CODE XREF: sub_4247A0+B1j
seg000:0042489B                pop    edi
seg000:0042489C                pop    esi
seg000:0042489D                leave                  ; прыгаем на .4365C9
seg000:0042489E                retn
seg000:0042489E sub_4247A0      endp
seg000:0042489E

Несложно догадаться, что 0042483A call sub_4263DD есть ни что иное, как вызов функЦия проверки, влияющей на ход проверки по адресу .0042484F. Именно от ее результатов проверки зависит, будет ли вызвана функция ExitProcess, или будет ли восстановлен обычный порядок работы в случае ввода зарегистрированности пользователя и запуск игрушки.
Заглянув в вызываемую функцию 0042484A call sub_4264BC видим, что в регистр eax заносится 1, содержание ячейки памяти по адресу 57F99C.

Код:

seg000:004264BC
seg000:004264BC ; --------------- S U B R O U T I N E ---------------------------------------
seg000:004264BC
seg000:004264BC
seg000:004264BC sub_4264BC      proc near              ; CODE XREF: sub_413B78+5Cp
seg000:004264BC                                        ; sub_4157E8+531p ...
seg000:004264BC                mov    eax, ds:dword_57F99C
seg000:004264C1                retn
seg000:004264C1 sub_4264BC      endp
seg000:004264C1

Логично предположить, что до этого, во время проверки в эту самую ячейку производится запись в случае ввода неправильных данных. И если ячейка содержит 0, то программа будет постоянно пропускать этап проверки на зарегистрированность и сразу же запускать игру. Для поиска места проверки перезапустим жертву и установим точку останова на запись в указанную ячейки памяти:


http://securityprobe.net/other/ProTe...m_on_write.jpg
Бряк на запись в ячейку памяти, хрянящую статус зарегистрированности

Жмем по F9 и перед нами появляется такой кусок кода:

Код:

seg000:00426296
seg000:00426296                push    ebp
seg000:00426297                mov    ebp, esp
seg000:00426299                sub    esp, 530h
seg000:0042629F                push    esi
seg000:004262A0                xor    esi, esi
seg000:004262A2                cmp    ds:dword_57F9A0, esi
seg000:004262A8                jnz    loc_4263DA
seg000:004262AE                lea    eax, [ebp+KeyName]
seg000:004262B4                push    offset a1831    ; "183-1"
seg000:004262B9                push    eax
seg000:004262BA                mov    ds:dword_57F99C, 1 ; записывает 1 в я4ейку памяти в слу4ае ввода неправильных рег.данных
seg000:004262C4                call    ds:lstrcpy

По адресу .004262A2 видим интересную команду по сравнению ячейки 57F9A0 с 0. Это место проверки на отсчет 60 секунд с момента запуска игры(щелкаем по .4262A2, нажимаем правую кнопку мыши, выбираем "Find References to - Address constant" и видим по адресу .00426046 соответствующий код MOV DWORD PTR DS:[57F9A0],1. Он выполняется только в случае зарегистрированности юзера и "дает знать" игре, что не нужно прерываться после минуты исполнения). Если содержание ячейки 57F9A0 будет 0, то игра вылетит даже после пропатчивания переменной статуса зарегистрированности! Итак, для полного взлома защиты нужно поменять результат проверки по адресу 004262A2, чтобы хоть один из операндов равнялся 1 и вместо сравнения содержимого ячейки 57F9A0 на 0 (адрес .4261D6) записать туда 1. Чтобы не использовать более массивные операции с модификацией ячеек памяти можно просто занести 1 в регистр esi. Для этого можно заменить команду xor esi, esi по адресу 004262A0 на inc esi и nop (оппкоды 46 90). Вместо CMP DWORD PTR DS:[57F9A0],0 нужно записать INC DWORD PTR DS:[57F9A0], ведь по умолчанию незарегистрированная прога будет хранить там 0 и мы таким образом оптимизируя код внесем в содержимое ячейки памяти 1. По желанию следующий условный переход JNE cracked.00426292 можно заменить на безусловный JMP cracked.00426292. Это и сделаем в любом шестнадцатиричном редакторе. Я выбрал Hiew. Для пропатчивание открываем в нем файл dumped_.exe, 2 раза жмем Enter, затем F5, вводим .4262A0, жмем F3, вводим 46 90. То же делаем и для остальных команд, оппкоды которых можно увидить на скрине ниже. Сохраняем изменения F9.


http://securityprobe.net/other/ProTe...ycoon/pat4.jpg
Патчим бинарник игры в шестнадцатиричном редакторе Hiew

Теперь програма полностью работоспособна. Запуск игры происходи сразу же, без вывода любых нагов и прерываний посреди процесса игры.

gl hf 2 all


Время: 10:56