Единственный хороший способ предотвратить реинжиниринг («понимание») программы - это пересмотреть ее структуру, чтобы заставить противника понять машины Тьюринга.По сути, вы делаете:
- берете какую-то задачу, которая, как правило, оказывается вычислительно сложной
- синтезирует версию того, чей результат вы знаете;это, как правило, довольно просто по сравнению с решением версией
- , чтобы сделать правильное выполнение программы в зависимости от правильного ответа
- заставить программу вычислять глупости, если ответ не правильный
Теперь оппонент, смотрящий на ваш код, должен решить, что такое «правильное» вычисление, решая сложные алгоритмически задачи.Существует множество непростых проблем, которые никто не решал эффективно в литературе за 40 лет;довольно хорошая ставка, если ваша программа зависит от одного из них, что J. Random Reverse-Engineer внезапно не сможет их решить.
Обычно это делают путем преобразования исходной программы, чтобы скрыть ее контрольпоток и / или его поток данных.Некоторые методы скремблируют поток управления путем преобразования некоторого потока управления по существу в поток данных («косвенный переход через этот массив указателей»), а затем реализуют алгоритмы потока данных, которые требуют точного анализа точек, что является доказуемо сложным и оказалось трудным впрактика.
Вот документ, который описывает различные методы довольно поверхностно, но его легко прочитать: http://www.cs.sjsu.edu/faculty/stamp/students/kundu_deepti.pdf
Вот еще один документ, который фокусируется на том, как обеспечить, чтобы запутывающие преобразования приводили к результатам, которыегарантированно сложны в вычислительном отношении: http://www.springerlink.com/content/41135jkqxv9l3xme/
Вот один из них, в котором рассматривается широкий спектр методов преобразования потоков управления, включая те, которые предоставляют уровни гарантий по безопасности: http://www.springerlink.com/content/g157gxr14m149l13/
Эта статьязапутывает потоки управления в бинарных программах с низкими издержками: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.167.3773&rank=2
Теперь можно избежать многих проблем, чтобы предотвратить декомпиляцию программы.Но если декомпилировать было невозможно понять, вы просто не могли бы беспокоиться;Я бы выбрал такой подход.
Если вы настаиваете на предотвращении декомпиляции, вы можете атаковать ее, подумав, для чего предназначена декомпиляция.Декомпиляция, по сути, предполагает, что вы можете конвертировать каждый байт целевой программы в некоторый фрагмент кода.Один из способов избежать этой ошибки - убедиться, что приложение, очевидно, может использовать каждый байт как в качестве компьютерных инструкций, так и в качестве данных, даже если на самом деле этого не происходит, и что решение сделать это запутано вышеуказаннымиметоды.Одним из вариантов этого является наличие множества условных ветвей в коде, которые на самом деле являются безусловными (с использованием методов обфускации потока управления);другая сторона ветки попадает в бессмысленный код, который выглядит допустимым, но ветвится в безумных местах в существующем коде.Другим вариантом этой идеи является реализация вашей программы в качестве запутанного интерпретатора и реализация фактической функциональности в виде набора интерпретируемых данных.Интересный способ сделать это - генерировать код во время выполнения и выполнять его на лету;большинство обычных языков, таких как C, практически не могут это представить.
Программу, созданную таким образом, будет трудно декомпилировать, не говоря уже о том, чтобы разобраться с фактами.
Инструменты, которые, как утверждают, хорошо справляются с задачей защиты двоичного кода, перечислены по адресу: https://security.stackexchange.com/questions/1069/any-comprehensive-solutions-for-binary-code-protection-and-anti-reverse-engineeri