/\                                                       /\
        /  \_                                                   _/  \
   ____/    /__      T E A M  .  F I F T Y  .  T H R E E      __\    \____
   \__     ____\\-                                 ____    -//____     __/
     /    /_____                             ______\   \__________\    \
   _/    /_\    \_________________________-//     //    ____/    /_\    \_
 __\            _\  ____ /___  _    \_    \/     //    ___\/_            /__
))______________\_    _______\_|__   /____\/_____\    /gf  _/______________((
                  \___\\-       /___//-         /____((-
              TEAM FiFTY THREE  TUTORiALS PACK # SEVENTEEN

HASP SL – Копаем Глубже
Автор..: potassium//ARTeam
Перевод: NightCat//TEAM-53


ВСТУПЛЕНИЕ
В моём предыдущем туторе о HASP SL (6.41.95) я продемонстрировал, как легко и быстро найти OEP, сдампить и перестроить цель. Цель давнного туториала - углубиться в анализ того, как вручную обойти anti-debug, выяснить с какой версией мы имеем дело и т.д. Обход anti-debug, нахождение OEP, сдампливание и перестройка, - всё это заняло не больше 2.5 минут! Готовы?

Пиво? Есть!
Орешки? Есть!
ImportRec? Есть!
OllyDbg? ЕСТЬ!
Ладненько... Поехали!

Цель можно загрузить тут - http://fs1.minitab.com/Store/QC2_Setup.exe (61 mg)



ОБХОДИМ Anti-Debug НА РУКАХ :)

Окей, чтобы сделать всё это больше похожим на "спорт" я не буду использовать никаких анти-дебаговых плагинов, но я всё буду убирать руками используя кое-какие приёмы.

Итак. если мы загрузим наш исполняемый файл в Olly и запустим, то получим сообщение:


"The Allladin HASP SL Runtime has detected a debugger attached to a protected program. The program has been terminated."


Итак, нашу попытку заглянуть внутрь кода обломали злые и плохие дядьки-разработчики протектора. Интересно, а если поставить breakpoint на IsDebuggerPresent, это сработает или нет? Давайте проверим!

Окей, мы прервались, EAX = 1 (debugger present). Теперь у нас есть несколько путей: руками изменить EAX -> 0, что конечно же легче всего, но в данном случае я знаю, что эта процедура будет вызываться много раз. Так что всё это изменение будет пустой тратой времени. Давайте ка проследуем за RETN и посмотрим куда нас это приведёт.

00DE0974 8BD8 MOV EBX,EAX
00DE0976 7D 05 JGE SHORT qc.00DE097D
00DE0978 7C 08 JL SHORT qc.00DE0982
00DE097A C786 90669090 66>MOV DWORD PTR DS:[ESI+90906690],57D9066
00DE0984 7C 08 JL SHORT qc.00DE098E
00DE0986 C786 90669090 66>MOV DWORD PTR DS:[ESI+90906690],DB859066
00DE0990 7A 06 JPE SHORT qc.00DE0998
00DE0992 7B 03 JPO SHORT qc.00DE0997
00DE0994 C783 90900F85 DF>MOV DWORD PTR DS:[EBX+850F9090],0DF
00DE099E 74 04 JE SHORT qc.00DE09A4
00DE09A0 75 02 JNZ SHORT qc.00DE09A4
00DE09A2 C786 90576A24 87>MOV DWORD PTR DS:[ESI+246A5790],7059C987
00DE09AC 06 PUSH ES
00DE09AD 71 04 JNO SHORT qc.00DE09B3

Окей, что мы получили какой-то ужасно-кошмарно-дьявольский код... чистое зло. Вы видите, что EAX кладётся в EBX? После. если вы протрейситесь чуть дальше, то вы увидите:

00DE098B 90 NOP
00DE098C 66:90 NOP
00DE098E 85DB TEST EBX,EBX <--- EBX = 1, ведь мы дебажим.
00DE0990 7A 06 JPE SHORT qc.00DE0998 "jump is not taken"
00DE0992 7B 03 JPO SHORT qc.00DE0997

Итак, мы меняем MOV EBX,EAX на MOV EBX,0 или прост отупо меняем JPE на константный. Но если мы всё это сделаем и запустим, то получим то же сообщение:


"The Allladin HASP SL Runtime has detected a debugger attached to a protected program. The program has been terminated."


О, чёрт, только не снова! А вот почему:

00DE0A1E FF15 20BEDE00 CALL DWORD PTR DS:[<&KERNEL32.GetCurrentProcessId
00DE0A24 50 PUSH EAX <--- PID нашего исполняемого файла
00DE0A25 53 PUSH EBX
00DE0A26 68 00040000 PUSH 400
00DE0A2B FF15 1CBEDE00 CALL DWORD PTR DS:[<&KERNEL32.OpenProcess>]
00DE0A31 8BF8 MOV EDI,EAX
00DE0A33 85FF TEST EDI,EDI
00DE0A35 74 45 JE SHORT qc.00DE0A7C
00DE0A37 68 68C4DE00 PUSH qc.00DEC468
00DE0A3C FF15 24BEDE00 CALL DWORD PTR DS:[<&KERNEL32.GetModuleHandleA>]
00DE0A42 85C0 TEST EAX,EAX
00DE0A44 74 2F JE SHORT qc.00DE0A75
00DE0A46 68 4CC4DE00 PUSH qc.00DEC44C ; ASCII "ZwQueryInformationProcess"
00DE0A4B 50 PUSH EAX
00DE0A4C FF15 14BEDE00 CALL DWORD PTR DS:[<&KERNEL32.GetProcAddress>]
00DE0A52 85C0 TEST EAX,EAX
00DE0A54 74 1F JE SHORT qc.00DE0A75
00DE0A56 215D F8 AND DWORD PTR SS:[EBP-8],EBX
00DE0A59 215D FC AND DWORD PTR SS:[EBP-4],EBX
00DE0A5C 8D4D F8 LEA ECX,DWORD PTR SS:[EBP-8]
00DE0A5F 51 PUSH ECX
00DE0A60 6A 04 PUSH 4
00DE0A62 8D4D FC LEA ECX,DWORD PTR SS:[EBP-4]
00DE0A65 51 PUSH ECX
00DE0A66 6A 07 PUSH 7
00DE0A68 57 PUSH EDI
00DE0A69 FFD0 CALL EAX <--- Вызывается ZwQueryInformationProcess
00DE0A6B 85C0 TEST EAX,EAX
00DE0A6D 75 06 JNZ SHORT qc.00DE0A75
00DE0A6F 3945 FC CMP DWORD PTR SS:[EBP-4],EAX
00DE0A72 74 01 JE SHORT qc.00DE0A75
00DE0A74 43 INC EBX
00DE0A75 57 PUSH EDI
00DE0A76 FF15 C4BDDE00 CALL DWORD PTR DS:[<&KERNEL32.CloseHandle>]
00DE0A7C 5F POP EDI
00DE0A7D 8BC3 MOV EAX,EBX
00DE0A7F 7A 06 JPE SHORT qc.00DE0A87
00DE0A81 7B 03 JPO SHORT qc.00DE0A86
00DE0A83 C783 90907A04 7B>MOV DWORD PTR DS:[EBX+47A9090],1265027B
00DE0A8D 74 03 JE SHORT qc.00DE0A92
00DE0A8F 75 01 JNZ SHORT qc.00DE0A92

Т.е. открывается процесс нашего исполняемого файла (EAX@Openprocess = exe pid) и затем выбирается адрес функции “ZwQueryInformationProcess”. Затем этот адрес хранится в EAX и вызывается по адресу 00DE0A69. Если "Being Debugged", то в ЕАХ возвращается 0. Окей, где это мы были? Изменяя возвращаемое в ЕАХ значение на 0 для IsDebuggerPresent и изменить EAX на 1 для ZwQueryInformationProcess, то всё у нас прокатит. 

Ладно, чтобы сохранить вам немного времени, я скажу вам, что случится если вы просто измените значение регистра EAX сразу после возвращения от API. Снова и снова (несколько раз) будет вызванна та же процедура, так что не нужно этого делать. Альтернатива - сделать прямой старый добрый патч. :) Для примера, запомните вот этот кусок кода:

00DE0974 8BD8 MOV EBX,EAX
00DE0976 7D 05 JGE SHORT qc.00DE097D
00DE0978 7C 08 JL SHORT qc.00DE0982

В данном случаем, при запуске вы увидите, что EDX = 0, так что нам нужно изменить MOV EBX,EAX на MOV EBX,EDX. Далее займёмся ZwQueryInformationProcess. У нас было вот что:

00DE0A69 FFD0 CALL EAX
00DE0A6B 85C0 TEST EAX,EAX
00DE0A6D 75 06 JNZ SHORT qc.00DE0A75

Изменение TEST EAX,EAX на INC EAX сработает. Сделано!

Теперь должно запуститься... нет?


"The Alladin HASP SL Runtime that the memory of component PRVSL (qc.exe) has been tampered with. The program has ben terminated."


От бля! Что теперь делать?!??! Всё снова!
Загружаем исполняемый файл в Olly и я вам всё сражу.

7C812E03 > 64:A1 18000000 MOV EAX,DWORD PTR FS:[18]
7C812E09 8B40 30 MOV EAX,DWORD PTR DS:[EAX+30]
7C812E0C 0FB640 02 MOVZX EAX,BYTE PTR DS:[EAX+2]
7C812E10 C3 RETN
7C812E11 90 NOP
7C812E12 90 NOP
7C812E13 90 NOP
7C812E14 90 NOP
7C812E15 90 NOP

Помните старый добрый kernel32.dll? ТАм есть дочерта места, вы так не думаете? Как насчёт того, чтобы сделать этому кернелу кое-какие временные апгрейды? :)

7C812E03 > 64:A1 18000000 MOV EAX,DWORD PTR FS:[18]
7C812E09 8B40 30 MOV EAX,DWORD PTR DS:[EAX+30]
7C812E0C 0FB640 02 MOVZX EAX,BYTE PTR DS:[EAX+2]
7C812E10 33C0 XOR EAX,EAX
7C812E12 C3 RETN
7C812E13 90 NOP

От так лучше!

А вот теперь время побрить :) ntdll.dll (@ZwQueryInformationProcess):

7C90E01B > B8 9A000000 MOV EAX,9A
7C90E020 BA 0003FE7F MOV EDX,7FFE0300
7C90E025 FF12 CALL DWORD PTR DS:[EDX]
7C90E027 C2 1400 RETN 14
7C90E02A 90 NOP
7C90E02B 90 NOP
7C90E02C 90 NOP
7C90E02D 90 NOP

Побрили:
7C90E01B > B8 9A000000 MOV EAX,9A
7C90E020 BA 0003FE7F MOV EDX,7FFE0300
7C90E025 FF12 CALL DWORD PTR DS:[EDX]
7C90E027 40 INC EAX
7C90E028 C2 1400 RETN 14
7C90E02B 90 NOP
7C90E02C 90 NOP

Конечно нужно будет поставить нуные брейкпоинты, походить по функциям, сделать изменения и затем снять брейкпоинты. Что теперь? Получится у нас или нет? Да! Программа в конце концов запустилась. Мы видим триальное окошко.

Ну а теперь (по идее) делам так: жмём alt+m, подсвечиваем секцию .text нашего исполняемого файла, жмём F2 (ставим break-on-access), выбираем “I would like to try...” и жмём “next” на окошке триала и... Бах! Вы на OEP.

Но... тут такое не сработает. Хотя тоже не сильно сложно. Посто поставьте F2 (break-on-access) на секцию данных (.data) вместо .text. Olly остановится тут:

00DDE080 8A1C3E MOV BL,BYTE PTR DS:[ESI+EDI]
00DDE083 3A1F CMP BL,BYTE PTR DS:[EDI]
00DDE085 7A 04 JPE SHORT qc.00DDE08B
00DDE087 7B 05 JPO SHORT qc.00DDE08E
00DDE089 C786 90669087 D2>MOV DWORD PTR DS:[ESI+87906690],710770D2
00DDE093 02C7 ADD AL,BH
00DDE095 8690 66909066 XCHG BYTE PTR DS:[EAX+66909066],DL
00DDE09B 90 NOP
00DDE09C 72 03 JB SHORT qc.00DDE0A1
00DDE09E 73 01 JNB SHORT qc.00DDE0A1
00DDE0A0 C2 7D05 RETN 57D
00DDE0A3 7C 08 JL SHORT qc.00DDE0AD

Теперь снова жмём alt+m снова и ставим breakpoint (F2) на секцию текста (.text). Запускаем и бум-с, приземляемся на OEP!! Ну адалее стандартная процедура: дампим, перестраиваем и перепаковываем, как хотите.

Согласно HASP SL SDK2 (страница 19 в мануале) существуют функции в этом протекторе для включения разных уровней и шифрования и обфускации. Ну и конечно есть фукция обнаружения дебаггера. Вот почему такое разное поведение в разных программах. Например Minitab 14.20 и  Quality Companion 2.1.0.0. Сравните эти две секции кода:


Minitab 14.20 (HASP SL 6.41)
00BAA4BC 8BD8 MOV EBX,EAX
00BAA4BE 85DB TEST EBX,EBX
00BAA4C0 0F85 AC000000 JNZ Mtb14org.00BAA572
00BAA4C6 57 PUSH EDI
00BAA4C7 90 NOP
00BAA4C8 90 NOP
00BAA4C9 6A 24 PUSH 24
00BAA4CB 90 NOP
00BAA4CC 59 POP ECX
00BAA4CD 8DBD 68FFFFFF LEA EDI,DWORD PTR SS:[EBP-98]
00BAA4D3 F3:AB REP STOS DWORD PTR ES:[EDI]
00BAA4D5 90 NOP
00BAA4D6 8D85 64FFFFFF LEA EAX,DWORD PTR SS:[EBP-9C]
00BAA4DC 50 PUSH EAX
00BAA4DD 90 NOP
00BAA4DE C785 64FFFFFF 94>MOV DWORD PTR SS:[EBP-9C],94
00BAA4E8 FF15 4CF8BA00 CALL DWORD PTR DS:[<&KERNEL32.GetVersion>
00BAA4EE 85C0 TEST EAX,EAX
00BAA4F0 74 7F JE SHORT Mtb14org.00BAA571
00BAA4F2 83BD 74FFFFFF 02 CMP DWORD PTR SS:[EBP-8C],2
00BAA4F9 75 76 JNZ SHORT Mtb14org.00BAA571
00BAA4FB FF15 48F8BA00 CALL DWORD PTR DS:[<&KERNEL32.GetCurrentProcessId>
00BAA501 50 PUSH EAX
00BAA502 53 PUSH EBX
00BAA503 68 00040000 PUSH 400
00BAA508 90 NOP
00BAA509 90 NOP
00BAA50A 90 NOP
00BAA50B FF15 44F8BA00 CALL DWORD PTR DS:[<&KERNEL32.OpenProcess>
00BAA511 8BF8 MOV EDI,EAX
00BAA513 90 NOP
00BAA514 90 NOP
00BAA515 90 NOP
00BAA516 90 NOP
00BAA517 85FF TEST EDI,EDI
00BAA519 74 56 JE SHORT Mtb14org.00BAA571
00BAA51B 68 88FEBA00 PUSH Mtb14org.00BAFE88 ; ASCII "ntdll.dll"
00BAA520 90 NOP
00BAA521 FF15 50F8BA00 CALL DWORD PTR DS:[<&KERNEL32.GetModuleH>
00BAA527 85C0 TEST EAX,EAX
00BAA529 74 3F JE SHORT Mtb14org.00BAA56A
00BAA52B 90 NOP
00BAA52C 90 NOP
00BAA52D 68 6CFEBA00 PUSH Mtb14org.00BAFE6C ; ASCII "ZwQueryInformationProcess"
00BAA532 90 NOP
00BAA533 90 NOP
00BAA534 50 PUSH EAX
00BAA535 90 NOP
00BAA536 90 NOP
00BAA537 90 NOP
00BAA538 FF15 3CF8BA00 CALL DWORD PTR DS:[<&KERNEL32.GetProcAdd>
00BAA53E 85C0 TEST EAX,EAX
00BAA540 74 28 JE SHORT Mtb14org.00BAA56A
00BAA542 215D F8 AND DWORD PTR SS:[EBP-8],EBX
00BAA545 90 NOP
00BAA546 215D FC AND DWORD PTR SS:[EBP-4],EBX
00BAA549 8D4D F8 LEA ECX,DWORD PTR SS:[EBP-8]
00BAA54C 90 NOP
00BAA54D 90 NOP
00BAA54E 90 NOP
00BAA54F 51 PUSH ECX
00BAA550 6A 04 PUSH 4
00BAA552 8D4D FC LEA ECX,DWORD PTR SS:[EBP-4]
00BAA555 51 PUSH ECX
00BAA556 6A 07 PUSH 7
00BAA558 90 NOP
00BAA559 90 NOP
00BAA55A 57 PUSH EDI
00BAA55B FFD0 CALL EAX
00BAA55D 85C0 TEST EAX,EAX
00BAA55F 75 09 JNZ SHORT Mtb14org.00BAA56A
00BAA561 90 NOP
00BAA562 3945 FC CMP DWORD PTR SS:[EBP-4],EAX
00BAA565 74 03 JE SHORT Mtb14org.00BAA56A
00BAA567 90 NOP
00BAA568 90 NOP
00BAA569 43 INC EBX
00BAA56A 57 PUSH EDI
00BAA56B FF15 F4F7BA00 CALL DWORD PTR DS:[<&KERNEL32.CloseHandle>
00BAA571 5F POP EDI
00BAA572 8BC3 MOV EAX,EBX
00BAA574 5B POP EBX
00BAA575 90 NOP
00BAA576 C9 LEAVE
00BAA577 C3 RETN

..и из Quality Companion 2.1.0.0 (так же HASP SL 6.41)

00DE0974 8BD8 MOV EBX,EAX
00DE0976 7D 05 JGE SHORT qc.00DE097D
00DE0978 7C 08 JL SHORT qc.00DE0982
00DE097A C786 90669090 66>MOV DWORD PTR DS:[ESI+90906690],57D9066
00DE0984 7C 08 JL SHORT qc.00DE098E
00DE0986 C786 90669090 66>MOV DWORD PTR DS:[ESI+90906690],DB859066
00DE0990 7A 06 JPE SHORT qc.00DE0998
00DE0992 7B 03 JPO SHORT qc.00DE0997
00DE0994 C783 90900F85 DF>MOV DWORD PTR DS:[EBX+850F9090],0DF
00DE099E 74 04 JE SHORT qc.00DE09A4
00DE09A0 75 02 JNZ SHORT qc.00DE09A4
00DE09A2 C786 90576A24 87>MOV DWORD PTR DS:[ESI+246A5790],7059C987
00DE09AC 06 PUSH ES
00DE09AD 71 04 JNO SHORT qc.00DE09B3
00DE09AF 81BE 66908DBD 68>CMP DWORD PTR DS:[ESI+BD8D9066],-98
00DE09B9 F3:AB REP STOS DWORD PTR ES:[EDI]
00DE09BB 70 04 JO SHORT qc.00DE09C1
00DE09BD 71 02 JNO SHORT qc.00DE09C1
00DE09BF 8DBD 7A047B02 LEA EDI,DWORD PTR SS:[EBP+27B047A]
00DE09C5 83C4 8D ADD ESP,-73
00DE09C8 8564FF FF TEST DWORD PTR DS:[EDI+EDI*8-1],ESP
00DE09CC FF7D ???
00DE09CE 05 7C05C786 ADD EAX,86C7057C
00DE09D3 0087 D2507D06 ADD BYTE PTR DS:[EDI+67D50D2],AL
00DE09D9 7C 03 JL SHORT qc.00DE09DE
00DE09DB C745 FC 669090C7 MOV DWORD PTR SS:[EBP-4],C7909066
00DE09E2 8564FF FF TEST DWORD PTR DS:[EDI+EDI*8-1],ESP
00DE09E6 FF9400 0000FF15 CALL DWORD PTR DS:[EAX+EAX+15FF0000]
00DE09ED ^E0 BD LOOPDNE SHORT qc.00DE09AC
00DE09EF DE00 FIADD WORD PTR DS:[EAX]
00DE09F1 72 03 JB SHORT qc.00DE09F6
00DE09F3 73 01 JNB SHORT qc.00DE09F6
00DE09F5 C2 85C0 RETN 0C085
00DE09F8 7A 06 JPE SHORT qc.00DE0A00
00DE09FA 7B 02 JPO SHORT qc.00DE09FE
00DE09FC 8DBD 9090747A LEA EDI,DWORD PTR SS:[EBP+7A749090]
00DE0A02 83BD 74FFFFFF 02 CMP DWORD PTR SS:[EBP-8C],2
00DE0A09 70 05 JO SHORT qc.00DE0A10
00DE0A0B 71 02 JNO SHORT qc.00DE0A0F
00DE0A0D 65:1290 756A7007 ADC DL,BYTE PTR GS:[EAX+7706A75]
00DE0A14 71 02 JNO SHORT qc.00DE0A18
00DE0A16 C786 90669090 66>MOV DWORD PTR DS:[ESI+90906690],15FF9066
00DE0A20 20BE DE005053 AND BYTE PTR DS:[ESI+535000DE],BH
00DE0A26 68 00040000 PUSH 400
00DE0A2B FF15 1CBEDE00 CALL DWORD PTR DS:[<&KERNEL32.OpenProcess>
00DE0A31 8BF8 MOV EDI,EAX
00DE0A33 85FF TEST EDI,EDI
00DE0A35 74 45 JE SHORT qc.00DE0A7C
00DE0A37 68 68C4DE00 PUSH qc.00DEC468 ; ASCII "ntdll.dll"
00DE0A3C FF15 24BEDE00 CALL DWORD PTR DS:[<&KERNEL32.GetModuleHandleA>
00DE0A42 85C0 TEST EAX,EAX
00DE0A44 74 2F JE SHORT qc.00DE0A75
00DE0A46 68 4CC4DE00 PUSH qc.00DEC44C ; ASCII "ZwQueryInformationProcess"
00DE0A4B 50 PUSH EAX
00DE0A4C FF15 14BEDE00 CALL DWORD PTR DS:[<&KERNEL32.GetProcAdd>; kernel32.GetProcAddress
00DE0A52 85C0 TEST EAX,EAX
00DE0A54 74 1F JE SHORT qc.00DE0A75
00DE0A56 215D F8 AND DWORD PTR SS:[EBP-8],EBX
00DE0A59 215D FC AND DWORD PTR SS:[EBP-4],EBX
00DE0A5C 8D4D F8 LEA ECX,DWORD PTR SS:[EBP-8]
00DE0A5F 51 PUSH ECX
00DE0A60 6A 04 PUSH 4
00DE0A62 8D4D FC LEA ECX,DWORD PTR SS:[EBP-4]
00DE0A65 51 PUSH ECX
00DE0A66 6A 07 PUSH 7
00DE0A68 57 PUSH EDI
00DE0A69 FFD0 CALL EAX
00DE0A6B 85C0 TEST EAX,EAX
00DE0A6D 75 06 JNZ SHORT qc.00DE0A75
00DE0A6F 3945 FC CMP DWORD PTR SS:[EBP-4],EAX
00DE0A72 74 01 JE SHORT qc.00DE0A75
00DE0A74 43 INC EBX
00DE0A75 57 PUSH EDI
00DE0A76 FF15 C4BDDE00 CALL DWORD PTR DS:[<&KERNEL32.CloseHandl>; kernel32.CloseHandle
00DE0A7C 5F POP EDI
00DE0A7D 8BC3 MOV EAX,EBX

Ка видно, во втром примере функция обфускации была включена, так что чуть сложнее проследить ход выполнения.



С КАКОЙ ВЕРСИЕЙ МЫ ИМЕЕМ ДЕЛО?

Всё просто, отличаются наг-окошки. Если это старая "Privilege" версия, то там иконка с буквой "Р". А если это HASP SL, то иконка там с буквой "Н".

А вот версия HASP с которой вы работаете может посмотреть в prv_ee.dll, котоаря можеть быть просмотрена практически сразе же после запуска с помощью alt+e. Последняя версия HASP SL  похоже 6.41.



ЗАКЛЮЧЕНИЕ
Не смотря на то, что HASP SL экипирован несколькими сильным фукциями, например anti-tampering, antidebugging, product activation, шифрование главного исполняемого файла и обфускация кода, это всё равно, как мне кажется, слишком слабая в некоторых моментах защита. Не поймите меня неправильно, некоторые функции очень даже сложно, анализировать из-за anti-tampering'a и anti-debugging'a. Но в этом протекторе нужно слишком много доделать,Что назвать его "прокачанным".

Что же дальше? Какая-нибудь защита импорта? Более экстремальная обфускация кода? Более сильные anti-debugging приёмны? Только будущее покажет.