Зняття ASProtect 1.22 - 1.23 Beta 21 і злом Lemonade Tycoon v1.1.6
Зняття ASProtect 1.22 - 1.23 Beta 21 і злом Lemonade Tycoon v1.1.6
Мета: Lemonade Tycoon v1.1.6 (2100кб)
Що ще потрібно:
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:Жертва поки недосяжна для жорстоких підступів дизассемблера;)
Пошук OEP
Користуватися автораспаковщікамі начебто AsprStripperXP ми не будемо і тому запустимо OllyDbg і відкриємо в отладчике виконуваний файл проги.Entry Point
Для успішного знаходження оригінальної точки входу жертви ми повинні включити в Олька обробку винятків (memory access voilation, access violation). Заходимо в опції налагодження (натиснувши Alt + O) і встановлюємо прапорці як на малюнку нижче:
Включення обробки винятків
повертаємося у вікно "CPU" OllyDbg іначінаем проходити виключення (натискаючи Shift + F9). Після 27 раз (якщо ще один раз натиснути Shift + F9, наша прога пройде OEP і запуститься) ми з'явимося за адресою .D405CC
Вікно "CPU" після обробки 27 винятків
Далі необхідно поставити крапку зупинки на команді RETN (.D40609) і наказати Олька "дійти" до неї натисканням Shift + F9 ще раз. Останнім етапом у пошуку OEP буде установка бряка на доступ Аспром до кодової секції захищається ним програми. Для цього натискаємо Alt + M (щоб викликати вікно "Memory Map") і тиснемо F2 на другий за рахунком секції заданого процесу. У нашому випадку це Lemonade codesection за адресою 00401000.
Геп на доступ до кодової секції
Останній раз жмякайте по Shift + F9 і ВСЕ !!! Ми на OEP. Щоб всетаки в це повірити, можна натиснути Ctrl + A і олька проведе аналіз коду і дізассемблерний лістинг замість незрозумілих однобайтном виразів знайде "людський" вигляд =)
А ось і OEP!
робимо дамп
Вибираємо в головному меню Plugins-OllyDump-Dump debugged process і у вікні натискаємо "Dump" (перед цим не забувши зняти прапор "Rebuild import", щоб OllyDump самостійно не намагався відновлювати імпорт дампа).
Робимо дамп за допомогою плагіна OllyDump
Про адреси точки входу і секцій можна не переживати, оскільки 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.
Imprec відновлює імпорт ...
Після проведених маніпуляцій у вікні податків з'явиться такий напис
Імпорт вдало відновлений ...
Це говорить про те, що імпорт відновлений і можна зробити модифікацію нашого дампа (dumped.exe) натисканням на кнопці "Fix Dump"
Дамп вдало модифікований ...
Отже, ми отримали ПОВНІСТЮ працездатний распакований файл (dumped_.exe)!
PEID підтверджує, що аспр вдало знятий
Приступимо до його аналізу ...
Налагодження і злом жертви за допомогою патча
Завантажуємо розпаковану прогу в отладчике. Оскільки для введення реєстраційних даний вона використовує стандартні вікна введення,
Вікно введення реєстраційних даних
і стандартне вікно помилки, то було решінію по-старому встановити точки зупину на викликах функцій MessageBoxA (виводять повідомлення про помилку) або GetDlgItemTextA (зчитувальних вводиться текст).
Установка геп на виклик функцій MessageBoxA
Натискання на кнопку "Enter License Information" привело на такий шматок коду в Олька:
Припинилися на виведенні помилку довжини регкода (вона повинна бути дорівнює 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;)
Ставимо геп на виклики функцій завершення роботи програми
Запускаємо на виконання наш файл, вводимо необхідні рег.данние (я вводив ім'я: ProTeuS і код: 1234567890abcdeABCDE) і без проблем отримуємо шукану точку (.44А841)
Виявлення точки виклику функції завершення роботи програми
поднявщісь на кілька рівнів вгору по структурі дізассемблерного лістингу в IDA бачимо такий код:
seg000: 004247A0 seg000: 004247A0; --------------- SUBROUTINE ---------------------------------- ----- 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; --------------- SUBROUTINE ---------------------------------- ----- 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, то програма буде постійно пропускати етап перевірки На оформлене і відразу ж запускати гру. Для пошуку місця перевірки перезапустити жертву і встановимо точку зупину на запис в зазначену осередки пам'яті:
Геп на запис в комірку пам'яті, хрянящуюю статус зареєстрованих
Тиснемо по 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.
Патчим бінарник гри в шестнадцатиричном редакторі Hiew
Тепер програма повністю працездатна. Запуск гри станься відразу ж, без виведення будь-яких нагов і переривань посеред процесу гри.
Файли до статті:
Мета: Lemonade Tycoon v1.1.6 (2100кб)
Дамп з відновленим імпортом: dumped_.rar
gl hf 2 all
05.01.2006
Коментарі
Коментуючи, пам'ятайте про те, що зміст і тон Вашого повідомлення можуть зачіпати почуття реальних людей, проявляйте повагу та толерантність до своїх співрозмовників навіть у тому випадку, якщо Ви не поділяєте їхню думку, Ваша поведінка за умов свободи висловлювань та анонімності, наданих інтернетом, змінює не тільки віртуальний, але й реальний світ. Всі коменти приховані з індексу, спам контролюється.