Архив номеров > N20 > Один раз - не Alkatraz

 

 

Один раз - не Alkatraz


Территория острова Алькатрас использовалась как защитный форт, позже как военная тюрьма, а затем как сверхзащищённая тюрьма для особо опасных преступников и тех, кто совершал попытки побега из предыдущих мест заключения.

В настоящее время тюрьма расформирована, остров превращён в музей, куда ходит паром из Сан-Франциско от пирса номер 33.

Теперь иногда в голливудских фильмах появляется эта тюрьма, и, скорее всего, является древностью. Для нас, спектрумистов, название Alkatraz ассоциируется с защитой от копирования. Чем защита характеризуется? Нестандартный формат сигнала (http://www.worldofspectrum.org/tapsamp.html) и зашифрованная процедура загрузки. Благодаря World of Spectrum, защита существует в .tzx-форматах, например в Fairlight II:

http://www.worldofspectrum.org/infoseekid.cgi?id=0001715

Увидеть, как работает процедура, легко – поставьте точку прерывания отладчика эмулятора по адресу $5D5D.

После загрузки отладчик остановится на указанном адресе на команде RET. Я выполняю один шаг и вижу интересную процедуру:

После процедуры $9EF4-$9f09 оказывается нечитаемый мусор из команд. Это и есть одна из защит, если посмотреть на процедуру, которая называется ксорка (термин от битовой операции XOR – исключающее ИЛИ), то становится ясно, что опкоды команд дешифруются, после выполнения «расксоривания» процедуры появится следующее:

Часть блока дешифрована, и следующий участок явно выполняет те же действия. И такая защита идёт дальше и дальше, понадобится терпение, чтобы расшифровать весь кодовый блок. Впоследствии появились утилиты, которые помогают расшифровать блоки: XOR'EM ALL (https://zxpress.ru/article.php?id=6320) или утилита, применяющая похожий способ защиты – называлась, если не ошибаюсь LooksLikeShit.

Теперь, в наше время, такие защиты тоже можно отнести к древности, как и тюрьму на острове. Но речь пойдёт не об истории, а о другом.

Я собирал коллекцию crackintro от релизов TR-DOS, и часто наталкивался на различные способы защиты, которые применялись командами Jurassic Park и Golden Disk Corp.

Что самое интересное – это массовые процедуры, которые используют нестандартные приёмы. Я невольно задумался: как это реализовать? Ведь процедуры расшифровки повторяются несколько раз? Ничего я не придумал, и задумал свой вариант.

Я набрал свой код дешифровки, повторяющий варианты (void.asm):

 


;compile with sjasmplus
     device zxspectrum128
 ORG #6000
 begin
     di
     ld hl,next1+1
     xor a
     ld b,a
     ld c,b
     ld r,a
 lp1:
     call $52;+2
     dec sp;+1
     dec sp;+1
     pop de;+1
     ld a,r;+2 ED operation
     xor(hl)
     xor d
     xor e
     xor c
     xor b
     ld (hl),a
     inc bc
 ;    ld r,a
     inc l ;+1
 next1:
     jr nz,lp1;+1
     inc h;+1
     jr nz,lp1;+1

 ;вторая ксорка

     ld hl,next2+1;+1
 lp2:
     call $52;+2
     dec sp;+1
     dec sp;+1
     pop de;+1

     ex de,hl
     or a
     sbc hl,de;+2 ED operation
     ex de,hl

     ld a,r;+2 ED operation
     xor(hl)
     xor d
     xor e
     xor c
     xor b
     ld (hl),a
     dec bc
     inc l ;+1
 next2:
     jr nz,lp2;+1
     inc h;+1
     jr nz,lp2;+1

; код расшифрован, демонстрация этого – распаковка картинки на экран
     ld hl,pica,de,$4000:call dzx7_standard
;завершение исполнения
     jr $

;сжатая картинка и распаковщик zx7
pica:incbin "Faded TNT Megademo2.scr.zx7"
zx7:include "zx7.a80"

 end

 display /d,end-begin
 display next1+1
 savebin "xorka.code",begin,end-begin

 

Один из хитроумных способов шифрования: применяются различные ключи, в примере Fairlight II используются данные в регистрах D и E. Как они работают? Операция XOR с числом дважды изменит значение: первый раз на другое, второй раз – на начальное значение. Получается, что в Fairlight II кодовый блок был зашифрован один раз, теперь процедуры расшифровывают коды повторно. Чтобы обойти ручную расшифровку, введены переменные значения – регистр BC, который изменяется во время работы от нуля до неизвестного значения.

Заодно применен регистр Z80 R – регистр регенерации памяти, 8 бит. Увеличивается на 1 после каждой выборки команды, но инкремент затрагивает только младшие 7 бит, старший бит не меняется и может быть использован в программах.

Увеличение на 1 зависит от операции. Например, при выполнении команд с префиксами #CB, #ED, #DD, #FD регистр R увеличится на 2.

Зная, как работает регистр R, я могу применить задуманное. Но для начала не помешает расписать принцип работы.

Почему использован ld hl,next+1 ? Взгляните на мою уже зашифрованную процедуру:

Что-то здесь не так. Ясно, что мусор ниже первой ксорки уже зашифрован, но переход JR NZ,$5FA0 ведёт в никуда. Так вот, этот трюк сделан, чтобы запутать юного хакера: по адресу $6019 хранится условный переход JR NZ, NNNN, который представляет собой два байта – $20 – это код, и второй байт – это относительное смещение. После первого прохода процедура дешифрует второй байт и занесёт его по нужному адресу ($601A), после чего процедура будет работать дальше.

Второй нюанс:


          call $52;+2
          dec sp;+1
          dec sp;+1
          pop de;+1

Это тоже малопонятно. Реализуется так: по адресу ПЗУ $0052 находится RET, на стеке сохранится адрес возврата $600C, следующие три команды извлекают этот адрес, DE=$600C, так осуществлена привязка декодирующей процедуры, в некоторых случаях будет трудно обойти первый участок кода.

Теперь, после прохода отладчиком несколько раз, видна картина:

Условие завершения работы ксорки понятно: расшифровка будет выполняться, пока HL<>0. Здесь можно поставить точку прерывания после завершения цикла – на $601E, и не прогонять ксорку постоянно. Хотя есть способ обмануть хакера, о нём я пока умолчу.

Итак, void.asm уже скомпилирован, имеется кодовый блок xorka.code, теперь нужно фрагменты зашифровать, пока непонятно, как? Первая процедура зашифрует код до неузнаваемости, вторая – тоже.

Наверное, нужно выполнить те же действия, написав ксорки на другом языке. Я выбрал PureBasic, потому что он поддерживает нужные побитовые операции. Согласно задуманным процедурам пишется код (genxor.pb):

 


Global r.a ; в переменной r будут храниться значения регистра R
 *m=AllocateMemory(65536); общая память для Спектрума

Procedure incr(dr); процедура увеличения регистра R на dr – бит 7 сохраняется неизменным
   
   r=(r&128)|( (r+dr)&127)
 EndProcedure

 ;чтение готового файла ксорок
 ReadFile(0,"xorka.code")
 adr=$6000

 While Not Eof(0)
   PokeA(*m+adr,ReadAsciiCharacter(0))
   adr+1
 Wend
 CloseFile(0)

 ;первая процедура
 r=0

 hl.u=$601A
 bc.u=0

 lp1:
 ; в комментариях написаны команды и приращение для регистра R
 ; это поможет при написании шифровки.
 ; lp1:
 ;     call $52;+2
 incr(2)
 ;     dec sp;+1
 ;     dec sp;+1
 ;     pop de;+1
 incr(3)
 de.u=$600C

 ;     ld a,r;+2 ED operation
 incr(2)
 a.a=r

 ;     XOr(hl);+1
 incr(1)
 a=a!PeekA(*m+hl)

 d.a=de/256
 e.a=de&255

 c.a=bc&255
 b.a=bc/256

 ;     XOr d;+1
 a=a!d
 incr(1)

 ;     XOr e;+1
 a=a!e
 incr(1)

 ;     XOr c;+1
 a=a!c
 incr(1)

 ;     XOr b;+1
 a=a!b
 incr(1)

 ;     ld (hl),a;+1
 PokeA(*m+hl,a)
 ;     inc bc;+1
 bc+1
 incr(2);?

 ;     ld r,a
 ;r=a
 ;     inc l ;+1
 ; next1:
 ;     jr nz,lp1;+1
 l.a=hl&255
 h.a=hl/256
 l+1
 hl=h*256+l
 incr(2)
 If l<>0
   Goto lp1
 EndIf
 ;     inc h;+1
 ;     jr nz,lp1;+1
 h+1
 hl=h*256+l
 incr(2)
 If h<>0
   Goto lp1
 EndIf

 ; второй проход

 ;     ld hl,next1+1;+1
 hl=$6037
 incr(1)

 lp2:
 ;     call $52;+2
 ;     dec sp;+1
 ;     dec sp;+1
 ;     pop de;+1
 incr(5)
 ;-de=$6024

 ;     ex de,hl
 ;     Or a
 ;     sbc hl,de #ED
 ;     ex de,hl
 incr(5)
 de=$6024-hl

 ;     ld a,r;+2 ED operation
 incr(2)
 a=r

 ;     XOr(hl)
 incr(1)
 a=a!PeekA(*m+hl)

 d.a=de/256
 e.a=de&255
 c.a=bc&255
 b.a=bc/256

 ;     XOr d
 a=a!d
 incr(1)
 ;     XOr e
 a=a!e
 incr(1)
 ;     XOr c
 a=a!c
 incr(1)
 ;     XOr b
 a=a!b
 incr(1)

 ;     ld (hl),a
 PokeA(*m+hl,a)
 ;     dec bc
 bc-1
 incr(2);?

 ;     inc l ;+1
 ; next2:
 ;     jr nz,lp2;+1
 l.a=hl&255
 h.a=hl/256
 l+1
 hl=h*256+l
 incr(2)
 If l<>0
   Goto lp2
 EndIf
 ;     inc h;+1
 ;     jr nz,lp1;+1
 h+1
 hl=h*256+l
 incr(2)
 If h<>0
   Goto lp2
 EndIf

 

Теперь зашифрованная процедура уже готова, добавлю код (check.asm):

 


            device zxspectrum128
             ORG #6000
           begin
             incbin "xorka2.code"
      
           end
             display /d,end-begin
             savesna "!check.sna",begin

Да, защита от любопытных глаз работает медленно, но постепенно выполняет свою работу.

Я не предлагаю готовое решение, я предлагаю идею. Возможно, когда-то и пригодится, хотя во время эмуляторов со всеми функциями подобная защита станет неактуальной.

Или эмуляторы останутся в прошлом, вслед за островом и защитой для копирования кассет, и «Спектрум» заменит PC.

Удачи!

 

Примечание редакции: исходники и прочие упомянутые в статье файлы можно скачать здесь:

http://dgmag.in/N20/alcatraz/filez.zip

 


g0blinish



Архив номеров > N20 > Один раз - не Alkatraz