Как стать автором
Обновить

Легальный взлом как разминка для ума

Assembler
IT-шники часто придумывают себе упражнения для ума, пытливый ум постоянно требует разминки. Хочу рассказать об одном из самых жестких и спорных способах – взлом специально защищенных программ-головоломок (Часто их называют crackme).

Одно из мест, где такие головоломки собраны — crackmes.de.

Здесь находятся много интересных программ, на которых можно испробовать свои силы по взлому. Никакого криминала – программы специально написаны для этой цели (так называемые crackme и reverseme);

Часто любят говорить «Все защиты можно взломать». Поковыряв некоторые из crackme вы возможно измените своё мнение.


Итак приступим:


Общая схема работы многих crackme — а давайте какую-то процеду в коде зашифруем, и «верный-неверный пароль» — в зависимости от сделанного хеша из расшифрованных этим паролем данных?

Или как вариент — заюзаем SEH (Structured Exception Handling – механизм обработки аппаратных и программных исключений), в который положим месседжбокс о плохом пароле, перед этим передав управление на наш «расшифрованный» код, при этом если пароль – правильный, то в расшифрованном коде будут «верные» опкоды команд, а если нет, то процессор сгенерит исключение о неверном опкоде и «кошерно» задействует SEH, в котором у нас стоит уведомление об ошибке. Надо сказать, что в общем этот вариант не «святой» поскольку после расшифровки возможны и «полувалидные» опкоды- к примеру jmp-команда за пределами этой нашей процедуры.

Но на первых порах и этого достаточно.

Итак берём крекми отсюда: crackmes.de/users/sharpe/unlockme_crackme_8_by_sharpe/download или отсюда:
crackmes.de/users/sharpe/unlockme_crackme_7_by_sharpe/download (седьмой, кстати, не взломан пока) и загружаем в дебаггер- я использовал олю (OllyDBG)- програмка – очень маленькая- сегмент кода всего 0х2В6=694 байта, очень легко найти часть кода отвечающего за чтение пароля:

0040107F  |. 3D F3030000    CMP EAX,3F3
00401084  |. 75 4E          JNZ SHORT 004010D4                       
00401086  |. 6A 21          PUSH 21                                 	 ; /Count = 21 (33.)
00401088  |. 68 88314000    PUSH 403188                            	 ; |Buffer = eight.00403188
0040108D  |. 68 F1030000    PUSH 3F1                                	 ; |ControlID = 3F1 (1009.)
00401092  |. FF75 08        PUSH DWORD PTR SS:[EBP+8]   ; |hWnd
00401095  |. E8 04020000    CALL 0040129E                           ; \GetDlgItemTextA
0040109A  |. 83F8 07        CMP EAX,7		здесь сравниваем длину пароля
0040109D  |. 76 1F          JBE SHORT 004010BE                       переход на смерть
0040109F  |. 83F8 20        CMP EAX,20		здесь тоже сравниваем длину пародя
004010A2  |. 73 1A          JNB SHORT 004010BE                       
004010A4  |. E8 D9000000    CALL 00401182                            преобразователь пароля
004010A9  |. FF75 08        PUSH DWORD PTR SS:[EBP+8]                
004010AC  |. E8 FD000000    CALL 004011AE                            расшифровщтк кода
004010B1  |. FF75 08        PUSH DWORD PTR SS:[EBP+8]                
004010B4  |. E8 61010000    CALL 0040121A                            не интересно
004010B9  |. E9 85000000    JMP 00401143                             ;  eight.00401143
004010BE  |> 6A 30          PUSH 30                                  а здесь прыгать не надо)))
004010C0  |. 68 34314000    PUSH 403134                              ; |Title = "-=[ Unlock Code Error"
004010C5  |. 68 4A314000    PUSH 40314A                              ; |Text = "The entered Unlock Code is invalid.
004010CA  |. FF75 08        PUSH DWORD PTR SS:[EBP+8]                ; |hOwner
004010CD  |. E8 D8010000    CALL 004012AA                            ; \MessageBoxA
004010D2  |. EB 6F          JMP SHORT 00401143                       ;  eight.00401143


Итак, на основании этого видим что длина пароля долна быть в диапазоне 8-32 знака, и если это так то прыгаем в функцию преобразования изначального пароля:

00401182  /$ 57             PUSH EDI
00401183  |. 33FF           XOR EDI,EDI
00401185  |. BE 88314000    MOV ESI,403188                           ;  здесь в регистр пишем указатель на пароль 
0040118A  |. B9 20000000    MOV ECX,20
0040118F  |. C705 A8314000 >MOV DWORD PTR DS:[4031A8],0 ;здесь мы запишем преобразован «хеш»
00401199  |> AC             /LODS BYTE PTR DS:[ESI]
0040119A  |. 85C0           |TEST EAX,EAX
0040119C  |. 74 08          |JE SHORT 004011A6                       ;  eight.004011A6
0040119E  |. 8BC8           |MOV ECX,EAX	;здесь  мы сохраним «хеш» с пароля
004011A0  |. 03F8           |ADD EDI,EAX	;процедура преобразования пароля
004011A2  |. D3C7           |ROL EDI,CL		; процедура преобразования пароля
004011A4  |.^EB F3          \JMP SHORT 00401199                      ;  eight.00401199
004011A6  |> 893D A8314000  MOV DWORD PTR DS:[4031A8],EDI; сохраняем и выходим 
004011AC  |. 5F             POP EDI
004011AD  \. C3             RETN


А вот здесь живет расшифровщик кода:

004011AE  /$ 55             PUSH EBP
004011AF  |. 8BEC           MOV EBP,ESP
004011B1  |. 83EC 04        SUB ESP,4
004011B4  |. 68 88314000    PUSH 403188                              ; здесь вновь проверяем длину пароля 
004011B9  |. E8 C2000000    CALL 00401280                            ; но это – не обязательно
004011BE  |. 8945 FC        MOV DWORD PTR SS:[EBP-4],EAX
004011C1  |. 837D FC 01     CMP DWORD PTR SS:[EBP-4],1
004011C5  |. 77 16          JA SHORT 004011DD                        ;  eight.004011DD
004011C7  |. 6A 30          PUSH 30                                  
004011C9  |. 68 34314000    PUSH 403134                              ; |Title = "-=[ Unlock Code Error"
004011CE  |. 68 4A314000    PUSH 40314A                              ; |Text = "The entered Unlock Code is invalid.
004011D3  |. FF75 08        PUSH DWORD PTR SS:[EBP+8]                ; |hOwner
004011D6  |. E8 CF000000    CALL 004012AA                            ; \MessageBoxA
004011DB  |. EB 1E          JMP SHORT 004011FB                       ;  eight.004011FB
004011DD  |> B8 49114000    MOV EAX,401149                ;  А вот здесь начинается расшифровка пароля
004011E2  |. B9 7F114000    MOV ECX,40117F
004011E7  |. 2BC8           SUB ECX,EAX				
004011E9  |. 33DB           XOR EBX,EBX
004011EB  |> 8B1418         /MOV EDX,DWORD PTR DS:[EAX+EBX]
004011EE  |. 3315 A8314000  |XOR EDX,DWORD PTR DS:[4031A8]; как видем простая ксор-функция
004011F4  |. 891418         |MOV DWORD PTR DS:[EAX+EBX],EDX
004011F7  |. 43             |INC EBX
004011F8  |. 49             |DEC ECX
004011F9  |.^75 F0          \JNZ SHORT 004011EB                      ;  и выходим
004011FB  |> C9             LEAVE
004011FC  \. C2 0400        RETN 4


Итак, теперь понятно: но как найти правильный хеш-код?
Перебором?

Это очень долго: надо сначала расшифровать, потом посчитать «энтропию» полученного кода и возможно код с минимумом «энтропии»- рабочий, но для этого надо писать или использовать уже написанные дизассемблерные движки;
а можно использовать SEH, в теле которого прописать код брумфорсера, но тогда даже одна «ложно-правильная» инструкция может полностю изменить верный ход исполнения программы.
Но я заметил, что хоть программа написана на чистом ассемблере, но автор всё-же много использовал в своём коде «открытие» стека, поэтому давайте думать, что первые правильные 4 байта в нашем «зашифрованном» коде:

PUSH EBP
MOV EBP,ESP

Значение: 0х55, 0х8В, 0хЕС

А сейчас(до прохождения процедуры шифрования) значения: 0x66, 0x71, 0x77, но мы же помним (можем посмотреть) свойства «ксор» функции и увидеть что тогда конечный хеш станет извесным:
Не забываем о «little-endian» соглашении:

77 71 66 ^
АА ВВ СС ^
ВВ СС    ^
СС
**********
ЕС   8В   55


Итак, посчитав это дело в «Калькуляторе» мы видем, что СС=0х33; ВВ=0хС9; АА=0х61,
Полученная последовательность: ХХ61C93.

Последний байт, к сожалению, пришлось искать перебором с использованием «self-keygening»a (патчинга бинарника), и в конце получаем: E961C933.

А расшифрованная процедура в действительности содержит много мусора(«обфускация»)
Но «открытие стека» нас спасло, и расшифрованая процедура (в теле):
00401149   $ 55             PUSH EBP
0040114A   . 8BEC           MOV EBP,ESP
0040114C   . D3C8           ROR EAX,CL
0040114E   . 58             POP EAX
0040114F   . EB 04          JMP SHORT 00401155                       ;  eight.00401155
00401151     D6             DB D6
00401152     FE             DB FE
00401153   . 32C9           XOR CL,CL
00401155   > BE 9C314000    MOV ESI,40319C                           ;  ASCII "Secret: Marius!"
0040115A   . C706 53656372  MOV DWORD PTR DS:[ESI],72636553
00401160   . C746 04 65743A>MOV DWORD PTR DS:[ESI+4],203A7465
00401167   . C746 08 4D6172>MOV DWORD PTR DS:[ESI+8],6972614D
0040116E   . C746 0C 757321>MOV DWORD PTR DS:[ESI+C],217375
00401175   . EB 00          JMP SHORT 00401177                       ;  eight.00401177
00401177   > 58             POP EAX
00401178   . FFE0           JMP EAX
0040117A     F7             DB F7
0040117B     ED             DB ED
0040117C     12             DB 12
0040117D     DA             DB DA
0040117E     3F             DB 3F                                    ;  CHAR '?'
0040117F     4E             DB 4E                                    ;  CHAR 'N'
00401180     40             DB 40                                    ;  CHAR '@'
00401181     C4             DB C4


Всё что делает ета процедура-записывает слово: Secret: Marius! по соответственому указателю.
Далее осталось узнать исходный пароль – Ну, тут – брутфорс вам в помощь))))

Процедуру преобразования можна «ripp»ать у нашей програмы и оформить это дело как ассемблерную вставку.

	mov  dword ptr [znach], 0	
	xor  edi, edi
	mov  esi, [str1]
    m:
	lodsb
	test al, al
	jz   m1
	mov  ecx, eax
	add  edi, eax
	rol  edi, cl
	jmp  m


Да, исходный пароль-005sj[Vg

Только, если брутфорс будете писать под какую-то экзотику типа cuda или под шейдеры- помните там нет инструкции циклического сдвига- rol eax, 5 и ёё надо заменить на два простых сдвига (один влево и один вправо) и потом «or» над полученным; реализация в виде: #define ROT (n, m) (((n)<<(m))|((n)>>(32-(m))))
В следующих постах вы узнаете о других интересных способах взлома неломаемых защит.

PS. Автор статьи (ash — его пока нет на хабре) попросил меня опубликовать эту статью.
Теги:ассемблерразминка для ума
Хабы: Assembler
Всего голосов 84: ↑79 и ↓5 +74
Просмотры13.4K

Похожие публикации

Лучшие публикации за сутки