• Что бы вступить в ряды "Принятый кодер" Вам нужно:
    Написать 10 полезных сообщений или тем и Получить 10 симпатий.
    Для того кто не хочет терять время,может пожертвовать средства для поддержки сервеса, и вступить в ряды VIP на месяц, дополнительная информация в лс.

  • Пользаватели которые будут спамить, уходят в бан без предупреждения. Спам сообщения определяется администрацией и модератором.

  • Гость, Что бы Вы хотели увидеть на нашем Форуме? Изложить свои идеи и пожелания по улучшению форума Вы можете поделиться с нами здесь. ----> Перейдите сюда
  • Все пользователи не прошедшие проверку электронной почты будут заблокированы. Все вопросы с разблокировкой обращайтесь по адресу электронной почте : info@guardianelinks.com . Не пришло сообщение о проверке или о сбросе также сообщите нам.

Автозапутыватель. Обфусцируем код при помощи LLVM

Sascha Оффлайн

Sascha

Заместитель Администратора
Команда форума
Администратор
Регистрация
9 Май 2015
Сообщения
1,483
Баллы
155


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

Что за зверь LLVM


Проект LLVM стартовал в 2000 году и получил распространение в начале десятых. Изначальная расшифровка — low level virtual machine, хотя сейчас она не отражает суть проекта. LLVM — фреймворк с открытым исходным кодом для создания компиляторов. На базе LLVM можно собрать компилятор для собственного языка программирования. Или «улучшить» существующий, чем мы сегодня и займемся.

Архитектурно LLVM разделен на три части: фронтенд, оптимизация и бэкенд. Фронтенд преобразует исходный код в промежуточное представление (intermediate representation) — универсальный промежуточный код, который используется на следующих этапах для оптимизации кода и сборки файла. Оптимизация удаляет неиспользуемый код, сворачивает арифметические вычисления в готовые константы и заменяет неэффективные конструкции более быстрыми. Бэкенд превращает IR в машинный код.

Если ты создаешь новый язык, достаточно написать фронтенд, генерирующий IR, а остальную работу LLVM возьмет на себя. Точно так же можно работать с кодом (изначально написанным на любом поддерживаемом языке) на уровне IR, меняя его под свои нужды на этапе оптимизации. Или написать свой бэкенд, чтобы собирать старый код под неизвестную процессорную архитектуру.

Необязательно писать фронтенд с нуля. Достаточно модифицировать исходный код LLVM. Оптимизация — более гибкий инструмент, она поддерживает загружаемые плагины, добавляющие проходы — специальные функции, обрабатывающие IR на уровне модуля, функции, цикла или базового блока. Для обфускации кода мы будем использовать именно их.

Сборка LLVM под Windows


Для начала установим

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

и поместим утилиту

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

в C:\Program Files\CMake\bin, тем самым сделав ее видимой для CMake при сборке.


mkdir C:\LLVM && cd C:\LLVM

"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"

git clone

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.


cd llvm-project
mkdir build && cd build

cmake -G "Ninja" ^
-DLLVM_ENABLE_PROJECTS="clang" ^
-DLLVM_ENABLE_ASSERTIONS=ON ^
-DCMAKE_BUILD_TYPE=Release ^
-DLLVM_BUILD_LLVM_DYLIB=ON ^
-DLLVM_ENABLE_RTTI=ON ^
-DCMAKE_INSTALL_PREFIX=C:/llvm/custom ^
../llvm

ninja
ninja install


Создаем папку в корне диска и запускаем vcvars64.bat. Он установит необходимые для сборки переменные окружения, такие как путь до компилятора и подключаемых файлов MSVC. Далее клонируем исходный код LLVM и запускаем сборку. Ждем около часа окончания сборки и получаем новые файлы в C:\LLVM\custom. Среди них утилиты, компиляторы и подключаемые файлы .lib. Они пригодятся при создании собственных проходов для оптимизации.

Сборка проходов


Создаем проект в Visual Studio и меняем его настройки:


Configuration Type = Dynamic Library (.dll)

Additional Include Directories = C:\LLVM\include

Additional Library Directories = C:\LLVM\lib

C++ Langauge Standart = ISO C++17 Standard (/std:c++17)

Code Generation → RuntimeLibrary = /MT


Также добавляем список .lib в Additional Dependencies:


LLVMCore.lib

LLVMSupport.lib

LLVMBitReader.lib

LLVMIRReader.lib

LLVMAnalysis.lib

LLVMPasses.lib

LLVMFrontendOpenMP.lib

LLVMTargetParser.lib

LLVMRemarks.lib

LLVMProfileData.lib

LLVMBinaryFormat.lib

LLVMDemangle.lib

LLVMBitstreamReader.lib


Таким образом собираются все наши проходы.

Проход для анализа покрытия


Создадим простой плагин для оптимизатора. Первое, что приходит на ум, — добавить логирование на вход каждой функции. AFL использует похожий подход для определения покрытия — он добавляет свой код в исследуемые исходники, чтобы отслеживать поток управления.


#pragma warning(disable : 4146)

#pragma comment(linker, "/export:llvmGetPassPluginInfo")

#define _CRT_SECURE_NO_WARNINGS

#include "llvm/Passes/PassBuilder.h"

#include "llvm/Passes/PassPlugin.h"

#include "llvm/IR/PassManager.h"

#include "llvm/Pass.h"

#include "llvm/IR/Function.h"

#include "llvm/IR/Module.h"

#include "llvm/IR/IRBuilder.h"

#include "llvm/IR/Type.h"

#include "llvm/IR/Instructions.h"

#include "llvm/IR/Constants.h"

#include "llvm/IR/DerivedTypes.h"

#include "llvm/Support/raw_ostream.h"

using namespace llvm;

struct DebugTracePass : PassInfoMixin<DebugTracePass> {

PreservedAnalyses run(Module& M, ModuleAnalysisManager&) {

LLVMContext& Ctx = M.getContext();

// Получаем i8* тип (char*)

Type* i8Ty = Type::getInt8Ty(Ctx);

PointerType* i8PtrTy = PointerType::get(i8Ty, 0);

// Тип функции: void OutputDebugStringA(char*)

FunctionType* debugFnTy = FunctionType::get(Type::getVoidTy(Ctx), { i8PtrTy }, false);

FunctionCallee debugFn = M.getOrInsertFunction("OutputDebugStringA", debugFnTy);

for (Function& F : M) {

// Пропуск, если это объявление функции

if (F.isDeclaration()) continue;

// Строим IR перед первой инструкцией

Instruction* insertPt = &*F.getEntryBlock().getFirstInsertionPt();

IRBuilder<> builder(insertPt);

// Формируем строку

std::string msg = "Enter: " + F.getName().str();

Value* msgStr = builder.CreateGlobalString(msg);

// Вставляем вызов OutputDebugStringA

builder.CreateCall(debugFn, msgStr);

}

return PreservedAnalyses::none();

}

};

extern "C"

llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() {

return {

LLVM_PLUGIN_API_VERSION, // Версия API плагинов

"InjectFunctionCallPass", // Название плагина

"v0.1", // Версия

[](llvm::PassBuilder& PB) {

PB.registerPipelineParsingCallback(

[](llvm::StringRef Name,

llvm::ModulePassManager& MPM,

llvm::ArrayRef<llvm::PassBuilder::PipelineElement>) {

if (Name == "debug-trace") {

MPM.addPass(DebugTracePass());

return true;

}

return false;

});

} };

}


Здесь я сначала отключаю раздражающее предупреждение о неверной типизации внутри кода LLVM. Затем прошу экспортировать функцию llvmGetPassPluginInfo. Она должна быть в экспорте плагина, чтобы сообщить при загрузке его название и добавить новый проход debug-trace в список доступных проходов. Проход располагается в методе run класса DebugTracePass.

Источник:

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

 
Вверх Снизу