что такое обфускация кода
Техники обфускации кода при помощи LLVM
Введение
Статья несет в себе больше теории, чем практической составляющей и предполагает наличия определенных знаний у читателя, а так же желание решать интересные задачи самому, не получая готовых решений. Большинство инструкций в LLVM IR базируется на трехадресном коде, это значит, что они принимают два аргумента и возвращают одно значение и что количество доступных нам инструкций ограничено.
Что возможно реализовать при помощи LLVM?
1) Cлучайный CFG
Данный метод модифицирует граф исполнения программы дополняя ее базовыми блоками, оригинальный стартовый блок может быть перемещен, разбавлен мусором.
Оригинальный граф
Обфускация 1 запуск, функция main
Обфускация 2 запуск, функция main
Обфускация 3 запуск, функция main
Первый раз.
Второй раз.
8) Создание из кода псевдоциклов.
Применимо относительно функций, берется базовый блок конкретной функции, к нему добавляется еще несколько блоков для организации цикла, цикл исполняется только один раз.
9) Создается случайная виртуальная машина, весь существующий код трансформируется под нее, для меня этот пункт возможен пока что только в теории.
С чего начать изучение?
FIRST_TERM_INST ( 1)
HANDLE_TERM_INST ( 1, Ret, ReturnInst)
HANDLE_TERM_INST ( 2, Br, BranchInst)
HANDLE_TERM_INST ( 3, Switch, SwitchInst)
HANDLE_TERM_INST ( 4, IndirectBr, IndirectBrInst)
HANDLE_TERM_INST ( 5, Invoke, InvokeInst)
HANDLE_TERM_INST ( 6, Resume, ResumeInst)
HANDLE_TERM_INST ( 7, Unreachable, UnreachableInst)
LAST_TERM_INST ( 7)
// Standard binary operators…
FIRST_BINARY_INST( 8)
HANDLE_BINARY_INST( 8, Add, BinaryOperator)
HANDLE_BINARY_INST( 9, FAdd, BinaryOperator)
HANDLE_BINARY_INST(10, Sub, BinaryOperator)
HANDLE_BINARY_INST(11, FSub, BinaryOperator)
HANDLE_BINARY_INST(12, Mul, BinaryOperator)
HANDLE_BINARY_INST(13, FMul, BinaryOperator)
HANDLE_BINARY_INST(14, UDiv, BinaryOperator)
HANDLE_BINARY_INST(15, SDiv, BinaryOperator)
HANDLE_BINARY_INST(16, FDiv, BinaryOperator)
HANDLE_BINARY_INST(17, URem, BinaryOperator)
HANDLE_BINARY_INST(18, SRem, BinaryOperator)
HANDLE_BINARY_INST(19, FRem, BinaryOperator)
// Logical operators (integer operands)
HANDLE_BINARY_INST(20, Shl, BinaryOperator) // Shift left (logical)
HANDLE_BINARY_INST(21, LShr, BinaryOperator) // Shift right (logical)
HANDLE_BINARY_INST(22, AShr, BinaryOperator) // Shift right (arithmetic)
HANDLE_BINARY_INST(23, And, BinaryOperator)
HANDLE_BINARY_INST(24, Or, BinaryOperator)
HANDLE_BINARY_INST(25, Xor, BinaryOperator)
LAST_BINARY_INST(25)
// Memory operators…
FIRST_MEMORY_INST(26)
HANDLE_MEMORY_INST(26, Alloca, AllocaInst) // Stack management
HANDLE_MEMORY_INST(27, Load, LoadInst ) // Memory manipulation instrs
HANDLE_MEMORY_INST(28, Store, StoreInst )
HANDLE_MEMORY_INST(29, GetElementPtr, GetElementPtrInst)
HANDLE_MEMORY_INST(30, Fence, FenceInst )
HANDLE_MEMORY_INST(31, AtomicCmpXchg, AtomicCmpXchgInst )
HANDLE_MEMORY_INST(32, AtomicRMW, AtomicRMWInst )
LAST_MEMORY_INST(32)
// Other operators…
FIRST_OTHER_INST(45)
HANDLE_OTHER_INST(45, ICmp, ICmpInst ) // Integer comparison instruction
HANDLE_OTHER_INST(46, FCmp, FCmpInst ) // Floating point comparison instr.
HANDLE_OTHER_INST(47, PHI, PHINode ) // PHI node instruction
HANDLE_OTHER_INST(48, Call, CallInst ) // Call a function
HANDLE_OTHER_INST(49, Select, SelectInst ) // select instruction
HANDLE_OTHER_INST(50, UserOp1, Instruction) // May be used internally in a pass
HANDLE_OTHER_INST(51, UserOp2, Instruction) // Internal to passes only
HANDLE_OTHER_INST(52, VAArg, VAArgInst ) // vaarg instruction
HANDLE_OTHER_INST(53, ExtractElement, ExtractElementInst)// extract from vector
HANDLE_OTHER_INST(54, InsertElement, InsertElementInst) // insert into vector
HANDLE_OTHER_INST(55, ShuffleVector, ShuffleVectorInst) // shuffle two vectors.
HANDLE_OTHER_INST(56, ExtractValue, ExtractValueInst)// extract from aggregate
HANDLE_OTHER_INST(57, InsertValue, InsertValueInst) // insert into aggregate
HANDLE_OTHER_INST(58, LandingPad, LandingPadInst) // Landing pad instruction.
LAST_OTHER_INST(58)
Cтоит посмотреть публичные реализации обфускации кода для ознакомления.
1) Obfuscator-llvm
Реализована замена инструкций, уплотнение графа исполнения.
2) Kryptonite
Реализована замена инструкций аналогами / разложение инструкций.
Сниппеты
Для того чтобы вставить асм инструкции можно использовать llvm::InlineAsm или MachinePass, через машинные проходы можно изменять, добавлять инструкции. Неплохой пример есть тут.
Как прочесть байткод файл?
Как сделать итерацию функций в модуле?
Как проверить на принадлежность к какой-то инструкции?
Как заменить терминатор другой инструкцией?
Как сделать приведение одной инструкции к другой?
Как получить первую не phi инструкцию в базовом блоке?
Как итерировать инструкции в функции?
Как узнать используется ли инструкция где то еще?
Как получить/изменить базовые блоки на которые ссылается InvokeInst и другие?
(Это продолжение обзора обфускаторов. Начало тут)
1. Обфускаторы.
1.1. Методики
Объединение сборок и пространств имён (Assembly Merge, Namespace Flatten)
Данная методика сама по себе не задерживает злоумышленника ни на минуту, но очень полезна для дальнейшего его запутывания. Т.к. чем больше классов будет содержать результирующая сборка, тем сложнее без детального анализа будет в ней найти то, что надо.
Опять же, при попытке украсть ваш код, злоумышленник получит вместо нескольких проектов-библиотек и одной программы только один проект, в котором все классы будут лежать в одной папке (и в одном неймспейсе).
Для объединения сборок можно использовать утилиту ilmerge, либо встроенную в обфускатор функциональность. Пространства имён обычно объединяются во время обфускации имён классов (чтобы не было коллизий с одинаково названными классами из разных неймспейсов).
Изменение содержимого классов
Некоторые обфускаторы могут объединять несколько классов в один, или делать из обычного класса вложенный. Но такая обфускация часто приводит к ошибкам в результирующей программе, и используется очень редко.
Обфускация control flow
На этом этапе меняется порядок инструкций в коде и даже меняются сами инструкции. Пожалуй, самый интересный и самый спорный этап.
Данная методика позволяет ввести в заблуждение (а иногда и в полный ступор) большинство декомпиляторов языков высокого уровня. Что очень хорошо противодействует «воровству» кода. Также «запутывает» кракеров и авторов кейгенов.
Обратная сторона медали — иногда сниженная производительность. Логично что чем больше мы запутываем ход выполнения программы, тем дольше она выполняется. Особенно это относится к использованию исключений.
В большинстве случаев код метода бьётся на блоки, эти блоки перемешиваются в случайном порядке и «склеиваются» с помощью безусловных переходов (инструкции br и br.s). В качестве примера:
Бывают и случаи, когда метод очень короткий, и «перемешать» его хорошо не получается, в этом случае некоторые обфускаторы выдают переход на следующую инструкцию:
Между инструкцией перехода, и её целью очень часто вставляется всякое «фуфло», типа выпадения в дебаггер, или просто невалидных инструкций:
Некоторые обфускаторы заменяют инструкции перехода (как оригинальные, так и вставленные) на загрузку константы и переход на switch:
L_0000: br.s L_0023
L_0002: ldloc num3
L_0006: switch (L_005b, L_0068, L_00ce, L_00af, L_0047, L_007b)
.
.
.
L_003c: ldc.i4 4
L_0041: stloc num3
L_0045: br.s L_0002
очевидно, что в данном примере инструкция со смещением L_0045 «в девичестве» была br L_0047, а если учесть предыдущие методики, то это вообще nop 😉
Иногда можно встретить «переход на переход»:
в одной из программ я видел цепочку из 6 (шести) таких переходов 😉
Интересный подход — использование условных переходов для выражений, которые всегда верны (или неверны).
Самый простой пример:
То же самое, но слегка более запутанное:
L_0014: ldc.i4.1
L_0015: stloc.0
L_0016: br.s L_001c
L_0018: nop
L_0019: ldarg.1
L_001a: br.s L_002e
L_001c: ldloc.0
L_001d: brtrue.s L_0018
L_0000: ldc.i4.5
L_0001: dup
L_0002: dup
L_0003: ldc.i4.6
L_0004: sub
L_0005: blt L_0001
Простое перемешивание некоторых инструкций, например:
L_0000: ldc.i4 4
L_0005: stloc num
L_0009: ldstr «\u5f03»
L_000e: ldloc num
компилятор иногда может выдавать код вида stloc X, ldloc X, когда требуется записать значение в локальную переменную, но не убирать его со стэка. В случае обфускаторов, эта переменная (num) добавлена искусственно, и нигде кроме данных двух инструкций больше не используется.
Один из самых «жёстких» методов — всегда выбрасываемое внутри блока try—catch исключение. Данный подход используется очень редко, т.к. резко снижает производительность и может нарушить логику приложения при неверном использовании. Скриншот я не привожу т. к. он занимает много места.
Кажется, что самые популярные методики я перечислил, если вы знаете что-то ещё, сообщите пожалуйста в комментах.
Invalid IL
Тут всё очень просто. В участки кода, которые никогда не будут исполнены, вставляются не описанные в стандарте опкоды (т.е. невалидные инструкции).
В рефлекторе вы увидите примерно такое:
или, если переключится на IL:
Данная методика обескураживает начинающих «хаксоров». Но не является чем-то сложным для обхода (данные опкоды просто заменяются на nop).
Сокрытие строк
Деобфускаторами это называется «шифрование строк», но назвать это шифрованием у меня не поворачивается язык.
Обычно это делается каким-нибудь «детским» алгоритмом шифрования типа XOR на константу:
public static string Decode( string str, int num)
<
int length = str.Length;
char [] chArray = str.ToCharArray();
while (—length >= 0)
chArray[length] = ( char )(chArray[length] ^ num);
return new string (chArray);
>
Иногда строки объединяются в одну, и потом происходит вызов метода Substring; иногда строки прячут в ресурсы.
В любом случае «шифрование» представлено в виде статического метода с несколькими аргументами, обычно это строка и/или число. Никаких криптографических алгоритмов не применяется, что вполне логично: если применить здесь настоящее шифрование, то программа будет безбожно тормозить.
Данный метод спасает от начинающих кракеров, которые будут искать по коду строки типа “Invalid serial number” или другие тексты сообщений.
Специфичные атрибуты и баги декомпиляторов
Самый часто встречаемый атрибут — [SuppressIldasm], который «вежливо просит» не работать на данной сборке официальный декомпилятор Microsoft — ildasm. Существуют также специфичные атрибуты для рефлектора и для коммерческих декомпиляторов.
В качестве багов можно встретить как чисто технические недоработки декомпиляторов (например, рефлектор выпадает на инструкции ldfld string 儽.凍::儽, а большинство деобфускаторов на базе Mono.Cecil — на неправильных RVA), также и алгоритмичиские допущения: многие декомпиляторы высокого уровня отслеживают состояние стэка, но идут по методу не как по графу, а линейно, и радостно валятся на методах в которых после последней инструкции ret вставлен бесконечный цикл. Против плагина Reflexil хорошо «помогает» инструкция, переходящая сама на себя.
Спасибо пользователю Exaktus за комментарии и дополнения
Далее будет часть 1.2. Обзор обфускаторов
продолжение следует.
* All source code was highlighted with MSVS and wordpad 😉
HackWare.ru
Этичный хакинг и тестирование на проникновение, информационная безопасность
Как обфусцировать JavaScript код
Нужно ли обфусцировать JavaScript
JavaScript код работает в веб браузерах пользователей и доступен им для изучения и других действий. Если вы задались вопросом «Зачем мне обфусцировать мой код JavaScript?», то существует множество причин, по которым рекомендуется защищать код, например:
Обфусцированный JavaScript медленнее!
В статье, ссылка на которую приведена выше, объясняется различие между минимизацией и обфускацией исходного кода. Кроме способа их выполнения и простоты восстановления в исходный вид, полученный результат также различается производительностью.
Минимизированный код: скачивается с сервера быстрее, так как имеет меньший размер, время выполнения (производительность) такая же, как и у кода в исходном варианте.
Обфусцированный код: обычно имеет больший размер и практически всегда является более медленным (на десятки процентов), поскольку кроме основной функции, выполняются сопутствующие действия, необходимые для запуска кода.
Итак, минимизированный код: легко восстановить в исходную форму, производительность не падает.
Обфусцированный код: (очень) трудно восстановить в исходную форму, производительность кода падает. В обфусцированный код можно добавить самозащиту и защиту от отладки, а также бессмысленные фрагменты кода, которые сильно усложнят его анализ.
Вывод: обфусцируйте только тот код, который вы хотите защитить. То есть имеет смысл обфусцировать свой код, но нет смысла обфусцировать код популярных JavaScript библиотек, которые и так общедоступны в исходном виде.
Программы для минимизации и обфускации JavaScript
В этой статье будут рассмотрены различные инструменты и способы защиты и оптимизации JavaScript скода.
Функции btoa и atob
В простых случаев для защиты от парсеров не нужны громоздкие инструменты обфускации и достаточно просто «спрятать» некоторые строки.
Чтобы было понятно, приведу практически пример. Мой сайт kali.tools стали проксировать на другом домене, т. е. выводится содержимое сайта и в нём заменены все ссылки на чужой домен, чтобы при переходе по ссылкам пользователь оставался на этом постороннем сайте.
Поскольку сайт копировался полностью, вместе со всеми скриптами, то достаточно добавить подобный код:
Этот код проверяет, на каком домене открыта страница, и если эта страница не на домене kali.tools, то делается переход на https://kali.tools.
Проблема в том, что при проксировании все упоминания kali.tools заменяются на ЧУЖОЙ_ДОМЕН.ru, и в результате код превращался в:
Код можно обфусцировать (но недостаточно просто минимизировать!). Но ещё проще спрятать строку «kali.tools» с помощью функций btoa и atob.
Функции btoa и atob являются встроенными функциями JavaScript и всегда доступны.
Функция btoa переводит указанную строку в набор символов (работает наподобие Base64), а функция atob выполняет обратную операцию.
Принцип действия в двух строках:
Итак, посмотрим, во что превратится строка «https://kali.tools»:
Теперь в моём простом коде делаем с помощью функции atob обратное преобразование этой строки, получаем:
Данные код делает именно то, что нужно — проверяет на каком домене сайт был открыт и в случае если это не kali.tools, то делает редирект на kali.tools. При этом при проксировании сайта, парсеры не видят строку kali.tools и не делают никаких изменений в этом фрагменте кода.
JavaScript Obfuscator
JavaScript Obfuscator — это мощнейший обфускатор с множеством опций. С помощью JavaScript Obfuscator будет получен код, в котором действительно трудно разобраться. Дополнительно JavaScript Obfuscator может встроить защиту от отладки (при открытии панели «Отладка» в Инструментах веб-мастера в браузере, браузер будет зависать), самозащиту кода (вставка бессмысленных фрагментов и пр.). У JavaScript Obfuscator множество тонкий опций подстройки процесса обфускации.
Данный инструмент имеет как графический интерфейс, так и интерфейс командной строки.
JavaScript Obfuscator онлайн
Вместо установки на компьютер, вы можете использовать онлайн сервис от авторов, он расположен по адресу: https://obfuscator.io/
Установка JavaScript Obfuscator
Установка в Kali Linux
Установка в BlackArch
Как установить графический интерфейс JavaScript Obfuscator
Установка веб интерфейса JavaScript Obfuscator в Kali Linux
После этого веб-интерфейс будет доступен по адресу http://localhost:3000/
Установка веб интерфейса JavaScript Obfuscator в BlackArch
После этого веб-интерфейс будет доступен по адресу http://localhost:3000/
Подробное описание опций и примеры запуска JavaScript Obfuscator вы найдёте на странице https://kali.tools/?p=5621
Чтобы в командной строке обфусцировать JavaScript код в файле script.js:
Например, с опциями по умолчанию код:
JSFuck
JSFuck это очень необычный обфускатор, любой JavaScript код он может представить записью всего из шести символов, а именно []()!+
Такая запись может поставить тупик сильнее, чем вывод рассмотренного выше JavaScript Obfuscator. Но, на самом деле, обфусцированный код может быть приведён к прежнему виду, например, инструментом JStillery.
Установка JSFuck
Установка в Kali Linux
После этого JSFuck будет доступен по адресу http://localhost:8181/
Установка в BlackArch
Установка в Windows
Откройте в веб-браузере файл index.html их архива.
Независимо от способа установки, в файле index.html рекомендуется заменить строку
В результате вы сможете вводить для обфускации не только отдельные строки, но и большие фрагменты кода.
Согласитесь, это очень необычно! Но это не всё! Автор JSFuck подготовил совершенно нереальные концепты, которые взрывают мозг:
Эти крайне занимательные примеры будут рассмотрены в конце этой статьи, поскольку имеют больше образовательное (чем практическое) значение.
Онлайн сервис обфускации кода
Получаемый код по стойкости к деобфускации является довольно слабым и может быть деобфусцирован инструментами, рассмотренными в статье «Деобфускация JavaScript кода».
Пример кода для обфускации:
Все онлайн сервисы обфускации JavaScript
UglifyJS
Инструмент UglifyJS отлично справляется с минимизацией кода JavaScript.
Установка UglifyJS
Установка в Kali Linux
Установка в BlackArch
Пример запуска (для сжатия кода в файле redir.js):
Пример исходного кода:
Пример полученного результата:
У данного инструмента много опций настройки, полный список вы найдёте на странице https://kali.tools/?p=5594
slimit и python-jsmin
Программы slimit и python-jsmin представляют собой модули Python для сжатия исходного кода JavaScript.
Установка slimit
В Kali Linux:
В BlackArch:
Установка python-jsmin в BlackArch:
Самая сложная обфускация JavaScript
Я уже упоминал, что автор JSFuck подготовил самые необычные формы и примеры JavaScript, их список вы найдёте на сайте http://aem1k.com/. Вот некоторые из них:
К примеру, код, написанный тайскими символами:
Код, написанный с помощью только исков:
Это рабочий код, который вы можете поместить в теги и открыть в веб-браузере.
В последнем примере вы не сможете украсть JavaScript код копипастой!