RCE.SU - реверсинг, кодинг, выделенные сервера, ICQ, proxy

Програмная эмуляция HASP ключей

By exefoliator, 2000. Перевод by Hex
1. Введение
Контора Alladin Knowledge Systems (далее AKS) создала набор HASP (Hardware against software piracy - Железо для защиты от пиратства) ключей которые обычно называют донглами (dongles) для защиты от нелицензионного использования/распостранения программ. AKS во всю раструбила, что HASP ключи делают программы неломаемыми. И все потому, что ихние чипы которые они юзают в ключах не могут быть эмулированы на машине Тьюринга. (т.е. на обычном компе).

2. Линия продуктов HASP и HASP функции (API)
HASPы бывают следующих типов: HASP-3 - базовая модель, за которой последовали MemoHASP-1, MemoHASP-4, TimeHASP и NetHASP. Каждый из этих ключей снабжен памятью от (112 до 512 байт) и 32 битным идентификационным кодом. В эту память можно писать и чиать блоками по 16 бит. В TimeHASP встроен таймер реального времени которым можно управлять. NetHASP юзается для одновременного использования защищенной программы через локальную сеть. Т.е. есть 1 ключ, и те кто подключены в сетку с компом, на котором стоит этот ключ могут использовать эту программу.

HASP-3 поддерживает только 3 основные функции: ISHASP (дает информацию о подключении HASP ключа - т.е. подключени или нет), HASPSTATUS (Возвращает тип ключа HASP) и HASPCODE (возвращает 64 битный код в зависимости от входного (seed) 16 битного кода). Остальные ключи поддерживают эти 3 функции + функции для записи/чтения в память, получение ID кода и доступа к таймеру.

Все эти функции перед вызовом принимают набор данных заносимые в регистры AX, BX ,CX и DX (даже в win32). Функции вызова могут быть разные, но параметры всегда передаются одинаково. Т.е. в программе есть функция обращения к HASP ключу, которая в зависипости от входных параметров вызывает ту или иную функцию ключа и получает/передает данные, а потом возвращает в те же регистры ответ от HASP ключа.

Функции|      Параметры вызова| Возращаемое значение|
ISHASP        BH <- 1, BL <- LPT №.    AX = 0 (нету HASP) или 1 (HASP есть)

HASPCODE      BH <- 2, BL <- LPT №|.    AX = return code 1
              AX <- seed код           |BX = return code 2
              CX <- password1           |CX = return code 3
              DX <- password2           |DX = return code 4

HASPSTATUS    BH <- 5, BL <- LPT №.    |AX = размер памяти
              CX <- password1           |BX = тип HASP
              DX <- password2           |CX = LPT к которому подключено.
Легко понять что они делают и также легко их сэмулировать. Т.е. в bh всегда передается номер функции, а вот входные параметры могут юзать разные регистры.

3. Средства защиты которые может содержать HASP
Перед тем как ломится в глубь ассемблерного кода нужно еще кое-что сказать о том какие методы защиты может включать HASP. HASP ключи могу содержать код для защиты от вирусов/вмешательства в код, шаблонную защиту кода (Pattern Code Security (PCS)) и HASP конверты (envelope).

Код для защиты от вирусов/вмешательства это просто что-то типа проверки контрольной суммы. Шаблонная защита кода означает то что в коде идут специальные структуры типа '$HASP$PCS' встретив которые специальная функция HASP заменяет эти байты на реальный код программы. И, наконец, конверты HASP, которые якобы невозможно сломать. При использовании конверта код программы криптуется, а HASP ключе лежат коды для раскриптовки.

Все криптографические процедуры в конвертах это просто XOR закриптованного блока с полученым из ключа 64 битным значением. Весь прикол в том что это значение берется из ключа. А дальше чтобы не вызвать Exception полученый из ключа код сравнивается с правильным значением ключа раскриптовки. Т.е. идет проверка как в аникдоте про экзамены : "В чем измеряется сила тока? Варианты ответа: - А не в амперах ли? …" :)

Процедура раскриптовки работает так:

(1) Распаковывает в память код конверта.
(2) Раскриптованая процедура вызывает функцию ISHASP для проверки наличия ключа.
(3) Если ключ найден то вызывается функция HASPCODE
(4) HASPCODE возвращает значения, которые сравниваются с константами которые явно видны в коде раскриптовки. Никаких попыток скрыть все это не придпринимается.
(5) Если все сошлось то в конце эта процедура создаст ключ раскриптовки, раскриптует и запустит exe.

Вся эмуляция заключается в том чтобы найти процедуру вызова HASP ключа. Далее анализируются вызовы всех функций, т.е. запоминают все seed и ответы. А дальше заменяют процедуру вызова HASP ключа на следующего вида програмку…

4. Общий вид эмулятора Hasp ключа

  80 FF 01       cmp         bh,1                        ; вызвали ISHASP?
  75 05          jne         @CheckTwo                   ; если нет
  31 C0          xor         eax,eax                     ; иначе
  40             inc         eax                         ; ...возвращаем в eax = 1...
  EB 67          jmp         @EndHProc                   ; и выходим
@CheckTwo:
  80 FF 02       cmp         bh,2                        ; вызвали HASPCODE?
  74 0A          je          @MakeCode                   ; если да
  EB 60          jmp         @EndHProc                   ; иначе ничего не делать
@HASPData:
  db x0 x1 x2 x3 x4 x5 x6 x7                             ; статические данные для HASP 
@MakeCode:
  31 C9          xor         ecx,ecx                     ; 
  31 DB          xor         ebx,ebx                     ; 
  E8 00 00 00 00 call        @NextInst                   ; 
@NextInst:
  5E             pop         esi                         ; 
@NextHBit:
  53             push        ebx                         ; сохранить число бай
  66 BB 89 19    mov         bx,1989h
  F7 E3          mul         eax,ebx
  83 C0 05       add         eax,5                       ; seed <- seed*1989h+5
  0F B7 C0       movzx       eax,ax                      ; seed <- seed and FFFFh
  50             push        eax                         ; сохранить seed
  C1 E8 09       shr         eax,9
  80 E0 3F       and         al,3Fh                      ; al <- bit offset
  89 C3          mov         ebx,eax
  C1 EB 03       shr         ebx,3                       ; ebx <- номер байта.
  80 E0 07       and         al,7                        ; al <- номер бита.
  51             push        ecx                         ; сохранить текущий код
  88 C1          mov         cl,al
  B0 01          mov         al,1
  D2 E0          shl         al,cl                       ; al <- битовая маска
  84 44 33 EF    test        byte ptr [ebx+esi-11h],al   ; бит установлен?
  B0 00          mov         al,0                        ; предположим что нет. 
  74 01          je          @BitClear                   ; jump если ок
  40             inc         eax                         ; иначе выставляем бит
@BitClear:
  59             pop         ecx                         ; ecx <- строка кода
  D0 E5          shl         ch,1                        ; идем на следующий бит
  08 C5          or          ch,al                       ; добавить последний бит
  58             pop         eax                         ; eax <- seed
  5B             pop         ebx                         ; ebx <- число бит
  41             inc         ecx                         ; inc число бит
  80 F9 08       cmp         cl,8                        ; 8 битов?
  75 C7          jne         @NextHBit                   ; jump если нет
  F6 C3 01       test        bl,1                        ; 
  74 03          je          @EvenByte                   ; 
  5A             pop         edx                         ; получаем код возврата 
  88 F1          mov         cl,dh                       ; добавляем верхние 8 бит
@EvenByte:
  51             push        ecx                         ; сохраняем код возврата
  31 C9          xor         ecx,ecx                     ; очищаем строку кода
  43             inc         ebx                         ; inc число байтов
  80 FB 08       cmp         bl,8                        ; 8байтов?
  75 B6          jne         @NextHBit                   ; jump если нет
  5A             pop         edx                         ; edx <- ret code 4
  59             pop         ecx                         ; ecx <- ret code 3
  5B             pop         ebx                         ; ebx <- ret code 2
  58             pop         eax                         ; eax <- ret code 1
@EndHProc:
  C3             ret                                     ; возврат к месту вызова.
P.s. Место вызова HASP функции легко найти через bpx FreeEnvironmentStringsA. Потом поднятся на несколько уровней вверх. Обычно перед таким вызовом идет cmp bh,32 это проверка на номер вызываемой функции. Т.е. у хаспа тока 50 функций :)


<= Вернуться к статьям


Rambler's Top100