Статья состоит из двух независимых друг от друга частей.
1. Распаковка ASProtect v.1.2
2. Исследование защиты самой программы.
Для скачивания доступны, как распакованная так и
запакованная версия подопытной программы.
1. Распаковка.
Итак, мы имеем программу защищенную неизвестным нам протектором.
Прежде всего, узнаем что это за протектор, для этого запустим программу
PEiD (PE Identifier) и укажем путь к нашей программе, получим
"ASProtect 1.2 -> Alexey Solodovnikov", т.о. мы знаем чем защищена
программа, это АСПротект версии 1.2, великолепный российский продукт от
Алексея Солодовникова.
Начнем распаковку. Для этого нам потребуются следующие программы:
- PEEDITOR v1.7 by yoda & M.o.D
- SuperBMP(для Win 9x)
- и отладчик SICE
Запустим PEEDITOR, нажмем "browse" и выберем нашу программу.
Посмотрим интересующие нас данные: Entry Point (точка входа в программу) и
Image Base.
File info.
Entry Point: 00001000
Image Base: 00400000
Теперь сложим Entry Point+Image Base=00001000+00400000=400100
Запустим SuperBMP и поставим галочку на "Erase". Чтобы ASProtect не сбрасывал
наши отладочные регистры, в NT для этого придется патчить ntdll. Теперь
запустим программу, вызовем SICE (ctrl+D) и введем в него "addr crkprg2",
затем "bmp 401000 x" выйдем из SICE (f5 или ctrl+D) и закроем программу.
Теперь снова запустим программу, автоматически сработает SICE в точке входа,
нажмем f12 и окажемся здесь.
017F:00423000 90 NOP
017F:00423001 60 PUSHAD ;<- мы здесь
017F:00423002 E81B000000 CALL 00423022
017F:00423007 E9FC8DB50F JMP 0FF7BE08
017F:0042300C 06 PUSH ES
017F:0042300D 0000 ADD [EAX],AL
017F:0042300F 8BFE MOV EDI,ESI
017F:00423011 B997000000 MOV ECX,00000097
017F:00423016 AD LODSD
017F:00423017 3578563412 XOR EAX,12345678
017F:0042301C AB STOSD
017F:0042301D 49 DEC ECX
017F:0042301E 75F6 JNZ 00423016
017F:00423020 EB04 JMP 00423026
017F:00423022 5D POP EBP
017F:00423023 45 INC EBP
017F:00423024 55 PUSH EBP
017F:00423025 C3 RET
Нажмем f8 и затем введем "bpm esp", теперь отпускаем SICE(жмем f5),
SICE тут же срабатывает и мы оказываемся на jmp, который ведет
к OEP (настоящей точке входа).
017F:00BC30C3 8901 MOV [ECX],EAX
017F:00BC30C5 03C3 ADD EAX,EBX
017F:00BC30C7 8944241C MOV [ESP+1C],EAX
017F:00BC30CB 61 POPAD
017F:00BC30CC FFE0 JMP EAX ;<- мы здесь
017F:00BC30CE 00FC ADD AH,BH
017F:00BC30D0 60 PUSHAD
Нажмем f8 и мы в распакованной программе, точно на настоящей точке входа.
017F:00419770 55 PUSH EBP ;<- мы здесь (OEP - настоящая точка входа в программу)
017F:00419771 8BEC MOV EBP,ESP
017F:00419773 83C4F0 ADD ESP,-10
017F:00419776 B8E8964100 MOV EAX,004196E8
017F:0041977B E870CBFEFF CALL 004062F0
017F:00419780 A110B74100 MOV EAX,[0041B710]
017F:00419785 A318BA4100 MOV [0041BA18],EAX
017F:0041978A 6A00 PUSH 00
017F:0041978C 68FC954100 PUSH 004195FC
017F:00419791 6A00 PUSH 00
017F:00419793 6A64 PUSH 64
017F:00419795 FF3518BA4100 PUSH DWORD PTR [0041BA18]
017F:0041979B E86CCDFEFF CALL 0040650C
017F:004197A0 E84FA8FEFF CALL 00403FF4
017F:004197A5 8D4000 LEA EAX,[EAX+00]
Запомним OEP=00419770. Теперь зациклим программу, для этого введем "e eip" и в окне дата,
куда переместится курсор, введем ebfe. Программа
зациклена. Выходим из SICE (f5). Запускаем PEEDITOR, жмем кнопку "tasks", выбираем наш процесс и делаем ему full dump.
Теперь открываем ImpRec, в окне "Attach to an Active Process" выбираем наш процесс,
в поле OEP вводим 00019770 (OEP-image base=00419770-00400000=00019770) и жмем кнопку
"IAT AutoSearch", затем "GetImports" и "Auto Trace". Импорт получен. Теперь нажмем
"Fix Dump" и выберем наш дамп (который мы сдампили в PEEDITOR'е). Теперь осталось
поправить первые два байта на точке входа (00419770) в файле, с ebfe на 558b.
Делаем это в любом hex-редакторе. Запускаем распакованную программу, работает. На этом часть с распаковкой завершена.
2. Исследование, взлом.
Навесная защита с программы снята, теперь перейдем к непосредственно
защите самой программы. Запускаем программу, видим на форме 3 кнопки:
"Reg.Check", "Hw.Check", "Check" и 3 поля ввода при них, при этом одно из
полей(при кнопке "Reg.Check") имеет атрибут readonly, т.е. писать мы
в него не можем, скорее всего используется только для информировании нас об
успешной или неуспешной регистрации, следовательно данные для регистрации он
получает из файла или реестра. Значит программы имеет три различных видов проверки. Пойдем по
порядку и начнем с первой, а именно "Reg.Check".
На этот раз исследовать будем с помощью дизассемблера W32Dasm, я использую
версию 8.93. Запускаем win32dasm в верхнем меню "disassembler" выбираем
"open file to disassemble" в открывшемся окне выбираем наш файл "crkprg2_unpacked.exe",
немного ждем и получаем ассемблерный листинг. Наша цель
- найти место в коде куда программа переходит после нажатия на кнопку "Reg.Check". Смотрим в Dialog
Information.
Number of Dialogs = 1 (decimal)
Name: DialogID_0064, # of Controls=010, Caption:"", ClassName:""
001 - ControlID:0066, Control Class:"EDIT" Control Text:""
002 - ControlID:0067, Control Class:"EDIT" Control Text:""
003 - ControlID:03E9, Control Class:"BUTTON" Control Text:"Reg.Check" ; искомая кнопка
004 - ControlID:0065, Control Class:"EDIT" Control Text:""
005 - ControlID:03EA, Control Class:"BUTTON" Control Text:"Hw.Check"
006 - ControlID:03EB, Control Class:"BUTTON" Control Text:"Check"
007 - ControlID:0000, Control Class:"BUTTON" Control Text:""
008 - ControlID:0000, Control Class:"BUTTON" Control Text:""
009 - ControlID:0000, Control Class:"BUTTON" Control Text:""
010 - ControlID:FFFF, Control Class:"" Control Text:"Z"
Видим искомую кнопку, теперь запомним ее "ControlID" - 03e9. В верхнем меню
win32dasm выбираем "Search", затем "FindText" и в строке поиска введем
запомненное нами число "03e9" и жмем поиск. В смещениях подобных строк попадается
много, их мы пропускаем, нам же нужна конструкция вида cmp ..., 03e9, находим
ее по адресу :00419629.
:00419622 48 dec eax
:00419623 0F8580000000 jne 004196A9
:00419629 817D10E9030000 cmp dword ptr [ebp+10], 000003E9 ; проверка, нажата ли кнопка "Reg.Check"
:00419630 7505 jne 00419637 ; если нет
- прыгает, да - идет дальше
:00419632 E8BDFAFFFF call 004190F4 ; и заходит в этот call, то что мы искали
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
Вот мы и нашли место, куда программа прыгает после нажатия кнопки "Reg.Check" - 004190f4.
Смотрим код по этому адресу, для этого нажмем shift+f12 в win32dasm'е, введем в поле
"code offset" этот адрес (004190f4) и нажмем OK.
:004190F4 55 push ebp
:004190F5 8BEC mov ebp, esp
:004190F7 6A00 push 00000000
:004190F9 6A00 push 00000000
:004190FB 53 push ebx
:004190FC 56 push esi
:004190FD 33C0 xor eax, eax
:004190FF 55 push ebp
:00419100 68D3914100 push 004191D3
:00419105 64FF30 push dword ptr fs:[eax]
:00419108 648920 mov dword ptr fs:[eax], esp
:0041910B B201 mov dl, 01
:0041910D A1648A4100 mov eax, dword ptr [00418A64]
:00419112 E81DFAFFFF call 00418B34 ; инициализация tregistry, для доступа к реестру
:00419117 8BD8 mov ebx, eax
* Possible StringData Ref from Code Obj ->"software\crprog2\regchk"
|
:00419119 BAE8914100 mov edx, 004191E8
:0041911E 8BC3 mov eax, ebx
:00419120 E833FEFFFF call 00418F58 ; проверяет наличия ключа в реестре "hkey_current_user\software\crprog2\regchk"
:00419125 84C0 test al, al ; результат проверки в al
:00419127 747D je 004191A6 ; если ключ не существует, при al=0, прыгает на 004191A6 и пишет "Incorrect",
; иначе, если ключ существует, al=1, идет дальше.
:00419129 B101 mov cl, 01
* Possible StringData Ref from Code Obj ->"software\crprog2\regchk"
|
:0041912B BAE8914100 mov edx, 004191E8
:00419130 8BC3 mov eax, ebx
:00419132 E801FBFFFF call 00418C38
:00419137 84C0 test al, al
:00419139 746B je 004191A6
:0041913B 8D4DFC lea ecx, dword ptr [ebp-04]
* Possible StringData Ref from Code Obj ->"key1" ;
|
:0041913E BA08924100 mov edx, 00419208
:00419143 8BC3 mov eax, ebx
:00419145 E88AFCFFFF call 00418DD4 ; здесь программа получает строку из параметра "key1"
; ключа "hkey_current_user\software\crprog2\regchk"
; и возвращает ее в [ebp-04], т.о. в [ebp-04] будет строка из параметра "key1"
:0041914A 8D4DF8 lea ecx, dword ptr [ebp-08]
* Possible StringData Ref from Code Obj ->"key2"
|
:0041914D BA18924100 mov edx, 00419218
:00419152 8BC3 mov eax, ebx
:00419154 E87BFCFFFF call 00418DD4 ; здесь программа получает строку из параметра "key2"
; ключа "hkey_current_user\software\crprog2\regchk"
; и возвращает ее в [ebp-08], т.о. в [ebp-08] будет строка из параметра "key2"
:00419159 8B45FC mov eax, dword ptr [ebp-04] ; в [ebp-04] строка из параметра "key1"
:0041915C E83FFFFFFF call 004190A0 ; эта функция переводит строку в число, т.е. значение из "key1" в число
; (возвращает в регистр eax)
:00419161 8BF0 mov esi, eax ; и записывает его в esi
:00419163 8B45F8 mov eax, dword ptr [ebp-08]
:00419166 E835FFFFFF call 004190A0 ; тоже самое со строкой key2, после выполнения ф-ии результат будет в eax!
:0041916B 6BD67B imul edx, esi, 0000007B ; здесь программа перемножает esi(значение из "key1" переведенное в число)
; на 7b hex(шестнадцатиричное)=123 dec(десятичное)
:0041916E 6BC861 imul ecx, eax, 00000061 ; здесь программа перемножает eax(значение из "key2" переведенное в число)
; на 61 hex=97 dec
:00419171 03D1 add edx, ecx ; складывает полученные выше результаты
:00419173 81FAEE0CA300 cmp edx, 00A30CEE ; если равно 00A30CEE hex = 10685678 dec идет дальше
:00419179 7522 jne 0041919D ; иначе прыгает на 0041919D и регистрации нам не видать
:0041917B 69C094000000 imul eax, 00000094 ; здесь программа перемножает eax
(значение из "key2" переведенное в число)
; на 94 hex=148 dec
:00419181 03F0 add esi, eax ; складывает esi
(значение из "key1" переведенное в число) с eax (полученным выше числом)
:00419183 81FE25ABD800 cmp esi, 00D8AB25 ; если равно 00D8AB25 hex = 14199589 dec идет дальше к победному концу, где пишет
; "REGISTRY CHECK ... OK!"
:00419189 7512 jne 0041919D ; иначе прыгает на 0041919D и регистрации нам не видать
* Possible StringData Ref from Code Obj ->"REGISTRY CHECK ... OK!"
|
:0041918B 6820924100 push 00419220
* Possible Reference to Dialog: DialogID_0064, CONTROL_ID:0065, ""
|
:00419190 6A65 push 00000065
:00419192 A11CBA4100 mov eax, dword ptr [0041BA1C]
:00419197 50 push eax
* Reference To: user32.SetDlgItemTextA, Ord:0000h
|
:00419198 E8AFD3FEFF Call 0040654C ; выводится строка "REGISTRY CHECK ... OK!" в то самое поле с
атрибутом readonly
Итак, подведем итоги первой проверки. Программа проверяет наличие ключа "hkey_current_user\software\
crprog2\regchk" в реестре. Затем в этом ключе читает строковые параметры "key1" и "key2". Переводит полученные строки в числа, из чего следует, что
строковые параметры в реестре ("key1" и "key2") должны состоять из цифр и проверяет их по следующим условиям:
(123*key2)+(97*key1)=10685678
(148*key1)+key2=14199589
Теперь мы легко можем заметить, что это обычная система уравнений с двумя неизвестными key1 и key2.
Решаем ее.
(123*key1)+(97*key2)=10685678
key1+148*key2=14199589
Из 2-го уравнения получаем key1=14199589-148*key2 и подставляем в первое
(123*(14199589-148*key2))+(97*key2)=10685678
1746549447-18204*key2+97*key2=10685678
-18204*key2+97*key2=10685678-1746549447
18204*key2-97*key2=-10685678+1746549447
18107*key2=1735863769
key2=1735863769/18107
key2=95867
key2 мы нашли, теперь вычислим key1
key1=14199589-148*key2
key1=14199589-148*95867
key1=11273
Все, обе неизвестных теперь нам известны. Нам осталось лишь вписать эти данные в реестр.
Для удобства создадим файл реестра CrkPrg2.reg и запишем в него.
REGEDIT4
[HKEY_CURRENT_USER\SOFTWARE\CRPROG2]
[HKEY_CURRENT_USER\SOFTWARE\CRPROG2\REGCHK]
"key1"="11273"
"key2"="95867"
Запустим CrkPrg2.reg файл, тем самым внесем все эти данные в реестр.
Теперь проверим нашу программу, нажмем кнопку "Reg.Check", пишет - "REGISTRY CHECK ... OK!",
значит с этим мы справились.