P-код
Термин p-код восходит к интерпретатору Visual Basic'а, однако, в хакерских кругах означает нечто большее и подразумевает любой интерпретируемый код произвольной виртуальной машины (не путать с VMWare). Непосредственное дизассемблирование в этом случае становится невозможно и приходится прибегать к декомпиляции, а для этого необходимо проанализировать алгоритм работы интерпретатора, написать (и отладить!) декомпилятор, что требует пива и времени. Правда, разработка (и отладка!) интерпретатора тоже даром не обходится и нам ним приходится попыхтеть (Джа в помощь!). К тому же разработка языка тянет за собой кучу вспомогательного инструментария (в первую нам понадобиться отладчик), иначе как на нем программировать?!
Все это выливается в солидный проект, который может быть использован всего один раз, в одной-единственной программе, да и то после выхода нескольких версий ядро интерпретатора желательно слегка изменять, чтобы написанный хакером декомпилятор перестал работать. Что поделаешь! Защита требует жертв и больших вложений. Ладно бы только вложений! Производительность интерпретируемого кода плетется в самом хвосте, отставая от динамической расшифровки и обфускации, но... может быть, есть возможность реализовать на p-коде только защитные модули? Нет! Тогда их отломают не глядя! На p-коде должна быть реализована вся программа целиком, в том числе и защитный механизм, тогда без декомпилятора его будет не хакнуть. Но это — в теории. На практике же, полностью загнать программу в p-код не удается по причине производительности и критичные к быстродействую функции пишутся на языке высокого уровня или даже ассемблере. А вот вызываются они уже из p-кода, в котором сосредоточена основная логика по типу "если нельзя, то все-таки".
Кстати говоря, большинство игровых миров управляются своим собственным скрпитовым языком, описывающим движение облаков, поведение мячика и характер разных монстров. На чистом Си никакую игру сложнее "тетриса" не запрограммировать! А раз мы уже имеем скриптовый язык, то почему бы не включить в него несколько защитных функций? Тогда чтобы взломать программу, хакеру придется разобраться в работе скрипиового движка.
Чаще всего разработчики используют Паскаль- или Бейсик-подобные языки, что не самый лучший выбор с точки зрения защищенности. Программа на p-коде при этом представляет собой последовательность ключевых слов (операторов языка) с аргументами и очень просто декомпилируется. На другом конце шкалы сложности находится Машина Тьюринга, Сети Петри, Стрелка Пирса и прочие примитивные виртуальные машины, реализующие фундаментальные логические операции, в результате чего даже такая конструкция как "IF THEN ELSE" распадается на сотни микрокоманд! Солнце погаснет прежде, чем хакер их проанализирует или… все-таки не погаснет? Существует множество продвинутых способов наглядной визуализации таких алгоритмов и к тому же… мы совсем забыли о производительности! Реализовать crackme на базе Машины Тьюринга еще можно, а вот коммерческое приложение — едва ли.
Хорошая идея — приложить к этому делу Форт. Вот его преимущества: простота реализации Форт-машины, компактность p-кода, довольно высокая производительность, сложность декомпиляции и ни на что не похожесть. Форт стоит особняком от всех языков, совсем не стремясь соответствовать человеческим привычкам, "здравому смыслу" и "логике". Разобраться в его идеологии с нуля — будет непросто. Хакеру придется нарыть кучу литературы и запастись терпением. Даже если ему не наскучит, разработчик защиты получит хорошую фору по времени, ну а там… мыщъх что-нибудь придумает!
Ниже приведен код Форт-машины, выдранный из программы see.exe, поставляемой вместе с Interrupt List'ом Ральфа Брауна.
seg000:1F29 loc_1F29: ; CODE XREF: start+39B9vj
seg000:1F29 call sub_1F04
seg000:1F2C mov word_A5C, bx
seg000:1F30 mov si, dx
seg000:1F32 mov bp, [bx+38h]
seg000:1F35 mov sp, [bx+36h]
seg000:1F38 lodsw
seg000:1F39 mov bx, ax
seg000:1F3B jmp word ptr [bx]