Build your first LLVM Obfuscator

void SampleFiniteStateMachine()
{
using namespace andrivet::ADVobfuscator::Machine1;
OBFUSCATED_CALL0(FunctionToProtect);
auto result = OBFUSCATED_CALL_RET(int, FunctionToProtectWithParameters, OBFUSCATED(“did”), OBFUSCATED(“again”));
cout << “Result: “ << result << endl;
}
extern “C” ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
llvmGetPassPluginInfo() {
return {
LLVM_PLUGIN_API_VERSION, “StringObfuscatorPass”, “v0.1”, [](PassBuilder &PB) {
PB.registerPipelineParsingCallback([](StringRef Name, ModulePassManager &MPM,
ArrayRef<PassBuilder::PipelineElement>) {
if(Name == “string-obfuscator-pass”){
MPM.addPass(StringObfuscatorModPass());
return true;
}
return false;
}
);
}
};
}
struct StringObfuscatorModPass : public PassInfoMixin<StringObfuscatorModPass> {
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) {
// Transform the strings
auto GlobalStrings = encodeGlobalStrings(M);
// Inject functions
Function *DecodeFunc = createDecodeFunc(M);
Function *DecodeStub = createDecodeStubFunc(M, GlobalStrings, DecodeFunc);
// Inject a call to DecodeStub from main
Function *MainFunc = M.getFunction(“main”);
createDecodeStubBlock(MainFunc, DecodeStub);
return PreservedAnalyses::all();
};
};
vector<GlobalString*> encodeGlobalStrings(Module& M){
vector<GlobalString*> GlobalStrings;
for(GlobalVariable &Glob : M.globals()) {
// Ignore external globals & uninitialized globals.
if (!Glob.hasInitializer() || Glob.hasExternalLinkage())
continue;
// Unwrap the global variable to receive its value
Constant *Initializer = Glob.getInitializer();

// Check if its a string
if (isa<ConstantDataArray>(Initializer)) {
auto CDA = cast<ConstantDataArray>(Initializer);
if (!CDA->isString())
continue;

// Extract raw string
StringRef StrVal = CDA->getAsString();
const char *Data = StrVal.begin();
const int Size = StrVal.size();
// Create encoded string variable (ROR1)
char *NewData = EncodeString(Data, Size);
Constant *NewConst = ConstantDataArray::getString(Ctx, StringRef(NewData, Size), false);
// Overwrite the global value
Glob.setInitializer(NewConst);
GlobalStrings.push_back(new GlobalString(&Glob));
Glob.setConstant(false);
}

return GlobalStrings;
}
void Decode(char *String){
while(*String != ‘\x00’){
*String -= 1;
String++;
}
}
$ clang -Os -shared -emit-llvm -c decode.c -o decode.bs && llvm-dis < decode.bs
define dso_local void @Decode(i8* nocapture %String) local_unnamed_addr #0 {
entry:
%0 = load i8, i8* %String, align 1, !tbaa !2
%cmp6 = icmp eq i8 %0, 0
br i1 %cmp6, label %while.end, label %while.body
while.body: ; preds = %entry, %while.body
%1 = phi i8 [ %2, %while.body ], [ %0, %entry ]
%String.addr.07 = phi i8* [ %incdec.ptr, %while.body ], [ %String, %entry ]
%sub = add i8 %1, -1
store i8 %sub, i8* %String.addr.07, align 1, !tbaa !2
%incdec.ptr = getelementptr inbounds i8, i8* %String.addr.07, i64 1
%2 = load i8, i8* %incdec.ptr, align 1, !tbaa !2
%cmp = icmp eq i8 %2, 0
br i1 %cmp, label %while.end, label %while.body
while.end: ; preds = %while.body, %entry
ret void
}
Function *createDecodeFunc(Module &M){
auto &Ctx = M.getContext();
// Add Decode function
FunctionCallee DecodeFuncCallee = M.getOrInsertFunction(“decode”, /*ret*/ Type::getVoidTy(Ctx), /*args*/ Type::getInt8PtrTy(Ctx, 8));
Function *DecodeFunc = cast<Function>(DecodeFuncCallee.getCallee());
DecodeFunc->setCallingConv(CallingConv::C);
// Name DecodeFunc arguments
Function::arg_iterator Args = DecodeFunc->arg_begin();
Value *StrPtr = Args++;
StrPtr->setName(“StrPtr”);
// Create blocks
BasicBlock *BEntry = BasicBlock::Create(Ctx, “entry”, DecodeFunc);
BasicBlock *BWhileBody = BasicBlock::Create(Ctx, “while.body”, DecodeFunc);
BasicBlock *BWhileEnd = BasicBlock::Create(Ctx, “while.end”, DecodeFunc);
// Entry block
IRBuilder<> *Builder = new IRBuilder<>(BEntry);
auto *var0 = Builder->CreateLoad(StrPtr, “var0”);
auto *cmp6 = Builder->CreateICmpEQ(var0, Constant::getNullValue(Type::getInt8Ty(Ctx)), “cmp6”);
Builder->CreateCondBr(cmp6, BWhileEnd, BWhileBody);
// Preheader block
Builder = new IRBuilder<>(BWhileBody);
PHINode *var1 = Builder->CreatePHI(Type::getInt8Ty(Ctx), 2, “var1”);
PHINode *stringaddr07 = Builder->CreatePHI(Type::getInt8PtrTy(Ctx, 8), 2, “stringaddr07”);
auto *sub = Builder->CreateSub(var1, ConstantInt::get(IntegerType::get(Ctx, 8), 1, true));
Builder->CreateStore(sub, stringaddr07);
auto *incdecptr = Builder->CreateGEP(stringaddr07, ConstantInt::get(IntegerType::get(Ctx, 64), 1), “incdecptr”);
auto *var2 = Builder->CreateLoad(incdecptr, “var2”);
auto cmp = Builder->CreateICmpEQ(var2, ConstantInt::get(IntegerType::get(Ctx, 8), 0), “cmp”);
Builder->CreateCondBr(cmp, BWhileEnd, BWhileBody);
Function *createDecodeStubFunc(Module &M, vector<GlobalString*> &GlobalStrings, Function *DecodeFunc){
Context &Ctx = M.getContext();

// Add DecodeStub function
FunctionCallee DecodeStubCallee = M.getOrInsertFunction(“decode_stub”, /*ret*/ Type::getVoidTy(Ctx));
Function *DecodeStubFunc = cast<Function>(DecodeStubCallee.getCallee());
DecodeStubFunc->setCallingConv(CallingConv::C);
// Create entry block
BasicBlock *BB = BasicBlock::Create(Ctx, “entry”, DecodeStubFunc);
IRBuilder<> Builder(BB);
// Add calls to decode every encoded global
for(GlobalString *GlobString : GlobalStrings){
if(GlobString->type == GlobString->SIMPLE_STRING_TYPE){
auto *StrPtr = Builder.CreatePointerCast(GlobString->Glob, Type::getInt8PtrTy(Ctx, 8));
Builder.CreateCall(DecodeFunc, {StrPtr});
}

return DecodeStubFunc;
}
void createDecodeStubBlock(Function *F, Function *DecodeStubFunc){
Context &Ctx = F->getContext();
BasicBlock &EntryBlock = F->getEntryBlock();
// Create new block
BasicBlock *NewBB = BasicBlock::Create(Ctx, “DecodeStub”, EntryBlock.getParent(), &EntryBlock);
IRBuilder<> Builder(NewBB);
// Call stub func instruction
Builder.CreateCall(DecodeStubFunc);
// Jump to original entry block
Builder.CreateBr(&EntryBlock);
}
run(…){

Function *MainFunc = M.getFunction(“main”);
createDecodeStubBlock(MainFunc, DecodeStub);

}
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) {

return PreservedAnalyses::all();
}
# Create LLVM IR from C
$ clang -O3 -emit-llvm hello.c -c -o hello.bc
# Run our plugin through the LLVM optimizer on the LLVM IR
$ opt -load-pass-plugin=/build/lib/LLVMStringObfuscator.so -passes=”string-obfuscator-pass” < hello.bc -o out.bc
# LLVM Static Compiler
$ llc out.bc -o out.s
# Link with clang
$ clang -static out.s -o out
# Profit
$ ./out

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store