Безопасность в Дельфи

       

Блоксхема


Вот блок схема действий, производимых в процедуре/функции, часть кода которой шифруется:

… получить значение маркера; try if <маркер в программе найден> then begin сделать копию N байт после маркера; расшифровать N байт после маркера; if <последние байты совпали с маркером> then заменить длинный переход перед маркером на короткий; end МАРКЕР КОД, КОТОРЫЙ БУДЕТ ЗАШИФРОВАН МАРКЕР finally if <есть непустая копия> then вернуть копию и длинный переход на место; end

  • "Загадочные" число N байт очевидным образом вычисляются из 4 байт предшествующих первому маркеру
  • В получении маркера и в шифровании обязательно должны использоваться аппаратные средства, таким образом программа и "привязывается" к ключу. Но в простейшем случае это может быть и шифрование на основе некоего спрятанного значения.

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

Вот тут на помощь и приходит прекомпилятор, позволяющий автоматизировать весь процесс. Опция {$I FileName.INC} включает в указанное место программы дополнительный код, заданный в файле FileName.INC, а в самом INC-файле можно организовать условную вставку. Создадим include - файл со следующим содержанием:

Code1.Inc {$IFNDEF CODEPASS0} {$DEFINE CODEPASS0} var Объявление рабочих переменных для шифрования {$ELSE} {$IFNDEF CODEPASS1} {$DEFINE CODEPASS1} Все действия по расшифровке; в конце - первый маркер {$ELSE} В начале - второй маркер Дальше - код "зачистки" {$UNDEF CODEPASS1} {$UNDEF CODEPASS0} {$ENDIF} // CODEPASS1 {$ENDIF} // CODEPASS0

Теперь в защищаемой процедуре / функции достаточно трижды вставить опцию $I и прекомпилятор сделает все за нас:

procedure MySecretProcedure; var … {$I Code1.Inc} begin … {$I Code1.Inc} защищаемый код {$I Code1.Inc} … end;

Первый и второй include (если считать с нуля:) должны стоять на "одном" уровне, то есть если первый был до входа в блок begin..end или try..end, то второй нельзя ставить внутрь, и наоборот. Впрочем, компилятор сам отследит. Еще осторожнее следует относиться к безусловным и условным переходам - тут компилятор может и не заметить ошибки.

Более универсальная схема получается с использованием пяти include: объявление переменных; расшифровка; маркер1; маркер2; зачистка. Вставки № 2-3 и 4-5 могут идти подряд. Именно такая схема реализована в прилагаемом примере (файлы testXX.inc). Вот рабочий прототип 5-проходного файла вставки:

//InitialPassword=39D04FB19F47D48F //LabelValue=6A6CEED3A7242FF6 {$IFDEF CODING} {$IFNDEF CODEPASS0} {$DEFINE CODEPASS0} var xxLBuffer,yyLBuffer:array [0..llen-1] of byte; xxLen,xxLLen,xxOldProtect:dword; xxCopy,xxStart:pointer; {$ELSE} {$IFNDEF CODEPASS1} {$DEFINE CODEPASS1} fillchar(xxLBuffer,SizeOf(xxLBuffer),#0); xxLLen:=LLen; xxStart:=nil; xxCopy:=nil; // задаем начальные значения буфера и с помощью аппаратного ключа получим маркер xxLBuffer[0]:=$39; xxLBuffer[1]:=$D0; xxLBuffer[2]:=$4F; xxLBuffer[3]:=$B1; xxLBuffer[4]:=$9F; xxLBuffer[5]:=$47; xxLBuffer[6]:=$D4; xxLBuffer[7]:=$8F; move(xxLBuffer,yyLBuffer,LLen); try // получаем тот самый маркер if GetMarker(xxLBuffer) then begin xxStart:=StrPosLen(Pointer(HInstance), @xxLBuffer, ModuleSize(HInstance), LLen); if xxStart<>nil then begin Move(pointer(dword(xxStart)-4)^,xxLen,4); if virtualprotect(pointer(dword(xxStart)-4),xxLen+4, PAGE_EXECUTE_READWRITE,@xxOldProtect) then begin GetMem(xxCopy,xxLen); Move(xxStart^,xxCopy^,xxLen); // аппаратно расшифровываем; xxLBuffer и yyLBuffer используем в качестве ключа if UnprotectBuffer(xxStart,xxLen,xxLBuffer,yyLBuffer) then begin Move(xxLLen,pointer(dword(xxStart)-4)^,4); virtualprotect(pointer(dword(xxStart)-4),xxLen+4, PAGE_EXECUTE,@xxOldProtect); end; end; end; end; {$ELSE} {$IFNDEF CODEPASS2} {$DEFINE CODEPASS2} asm DB $E9 DD LLen DB $6A,$6C,$EE,$D3,$A7,$24,$2F,$F6 end; {$ELSE} {$IFNDEF CODEPASS3} {$DEFINE CODEPASS3} asm JMP @@1 DB $6A,$6C,$EE,$D3,$A7,$24,$2F,$F6 @@1: end; {$ELSE} finally // зачистка if xxCopy<>nil then begin virtualprotect(pointer(dword(xxStart)-4),xxLen+4, PAGE_EXECUTE_READWRITE,@xxOldProtect); Move(xxLen,pointer(dword(xxStart)-4)^,4); Move(xxCopy^,xxStart^,xxLen); virtualprotect(pointer(dword(xxStart)-4),xxLen+4,PAGE_EXECUTE,@xxOldProtect); FreeMem(xxCopy); xxStart:=nil; end; end; {$UNDEF CODEPASS3} {$UNDEF CODEPASS2} {$UNDEF CODEPASS1} {$UNDEF CODEPASS0} {$ENDIF} // CODEPASS3 {$ENDIF} // CODEPASS2 {$ENDIF} // CODEPASS1 {$ENDIF} // CODEPASS0 {$ENDIF} // CODING

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

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

Остается сделать подобные вставки в самых разнообразных местах программы (эта работа производится только один раз), и в дальнейшем все происходит автоматически, каждая новая версия программы будет содержать уникальную защиту, не забывайте только генерировать исходные тексты. Но и увлекаться не надо. Стоит учесть, что слишком частое обращение к электронному ключу замедлит работу, приемлемой скорости можно добиться только при использовании специализированного контроллера.

Прилагаемый архив содержит исходные тексты программы генерации исходных текстов и последующей обработки скомпилированных модулей, а также программу-пример, иллюстрирующий очевидность процедуры установки защиты. Это - простейшая версия, но каждый может добавить в генерируемые файлы строки с незначащими операторами, борьбу с отладчиками, дизассемблерами и прочее. В данном случае демонстрируется лишь ТЕХНОЛОГИЯ, применение которой будет различаться в конечных реализациях.



Содержание  Назад  Вперед