Некоторые широко распространенные языки программирования в процессе компиляции преобразуют исходный текст в так называемый псевдокод — некоторое промежуточное представление текста программы, не являющееся машинным кодом. К таким языкам можно отнести Clipper, C#, FoxPro, инсталляционные сценарии InstallShield, Java, Maplnfo Map Basic, MicroStation MDL, Python, Visual Basic и многие другие. При выполнении программы виртуальная машина интерпретирует псевдокод и выполняет его на виртуальном процессоре. Теоретически использование виртуальной машины может являться эффективным способом противодействия исследованию программы, т. к. до начала анализа алгоритма необходимо разобраться с устройством виртуальной машины. Но это справедливо только для ситуации, когда система команд, применяемая машиной, нигде и никем не была описана, т. е. является уникальной.
Очевидно, что для популярных языков программирования это совершенно не так. Для некоторых языков (например С#, Java и Python) в Интернете нетрудно найти подробное описание того, как кодируется та или иная операция, поддерживаемая виртуальной машиной. А интерпретатор языка Python вообще распространяется в исходных текстах, что не позволяет сохранять устройство виртуальной машины в тайне. Если для какого-то языка нет описания кодов операций, но этим языком пользуется довольно много программистов, рано или поздно кто-то задастся целью разобраться в деталях псевдокода и разработает весь необходимый инструментарий. Существует несколько причин, почему разобраться с системой команд виртуальной машины для транслируемого языка программирования обычно бывает не очень сложно. Прежде всего, исследователь может компилировать любые примеры и смотреть, в какой псевдокод будут превращаться команды. Это очень важно, т. к. внося незначительные изменения в исходный текст и анализирую разницу в оттранслированном псевдокоде, гораздо легче устанавливать закономерности, чем внося изменения в псевдокод и контролируя изменения в поведении виртуальной машины. Кроме того, виртуальная машина обычно проектируется, исходя из требований максимизации производительности. Следовательно, знание базовых принципов построения эффективных виртуальных машин часто позволяет быстро разобраться с особенностями конкретной реализации. Вдобавок система команд виртуальной машины редко бывает очень сложной.
Это на реальном процессоре обнулить регистр еах можно несколькими способами, например: sub еах, еах; хог еах, еах; and еах, еах; mov еах, 0. А в виртуальной машине такая избыточность не имеет смысла. И, наконец, виртуальная машина, являющаяся частью языка программирования, не разрабатывается как запутанное логическое устройство, работа которого никогда не должна быть проанализирована противником. Напротив, чем проще будет организована виртуальная машина, тем легче будет ее отлаживать и оптимизировать. В любом случае, для многих популярных транслируемых языков программирования были разработаны декомпиляторы, позволяющие получить если не точную копию оригинального исходного текста, то часто эквивалентный код, который может быть скомпилирован и будет работать так же, как оригинальная программа. В разных языках программирования количество информации, сохраняемой в оттранслированном тексте, может отличаться. В некоторых случаях сохраняются имена всех функций и переменных.
Иногда могут быть извлечены даже номера строк исходного текста, где располагался тот или иной оператор, и имя исходного файла. Но может быть и так, что сохраняется только последовательность вызовов функций и употребления операторов, в то время как вся символическая информация об именах оказывается утраченной. Но, даже имея всего лишь эквивалентный текст, в котором имена переменных и функций заменены на произвольные, анализировать защитные механизмы гораздо проще, чем если бы они были написаны на языке, компилируемом в команды реального процессора. Для предотвращения применения декомпиляторов иногда разработчики программ идут на модификацию виртуальной машины. Модификация виртуальной машины FoxPro FoxPro является одной из популярных коммерческих систем управления базами данных (СУБД) и позволяет создавать законченные приложения, функционирующие независимо от среды разработки. Но существует несколько очень хороших декомпиляторов, способных полностью восстановить исходный текст программ, созданных в FoxPro, например ReFox или UnFoxAII. И некоторые разработчики, использующие FoxPro (например авторы программы Hardware Inspector), пытаются найти способ защитить свои программы от декомпиляции.
Делается это примерно следующим образом. Программа разрабатывается в среде FoxPro и компилируется самым обычным образом. Чтобы ReFox невозможно было использовать для получения исходного текста программы, необходимо изменить способ кодирования псевдокода. Но тогда и виртуальная машина не сможет работать с перекодированным псевдокодом. Следовательно, необходимо исправить и виртуальную машину. После этого скомпилированную программу можно распространять вместе с модифицированной виртуальной машиной, и ReFox окажется бессилен. Однако в данной схеме есть одно слабое звено. Дело в том, что исполняющая часть виртуальной машины обычно оформляется в виде динамической библиотеки (vfp50O.dll для FoxPro 5 или vfp6r.dll для FoxPro 6) и разрешается свободное распространение этой библиотеки (как redistributable component). Следовательно, оригинальная (неизмененная) версия виртуальной машины может быть легко найдена в Интернете. Далее достаточно выяснить, чем отличается модифицированная версия виртуальной машины, и либо перекодировать программу, приведя ее к виду, доступному для понимания ReFox, либо модифици- ровать ReFox таким же образом, каким была модифицирована виртуальная машина.
Так что модификация виртуальной машины является весьма сомнительным средством для защиты от декомпиляции псевдокода. К тому же, распространение модифицированной виртуальной машины почти всегда является нарушением условий лицензии, в согласии с которыми эта машина должна использоваться. В случае с виртуальной машиной FoxPro, являющейся собственностью корпорации Microsoft, ущемленными оказываются права последней, а это может вызвать серьезные последствия. Одним словом, использование транслируемых языков программирования, допускающих частичную или полную декомпиляцию, — это далеко не лучший выбор для реализации защитных механизмов. Противник имеет возможность в самые короткие сроки получить доступ к исходным текстам всех исходных алгоритмов, чтобы попытаться найти уязвимость. А иногда противнику и вовсе удается убрать из декомпилированного текста все обращения к защитным функциям и заново скомпилировать программу.