Android Armor
Chapters

This chapter will showcase a custom tiny obfuscator that would apply MBA (Mixed Boolean Arithmetic) on a program.

What is LLVM

LLVM is collection of tools and libraries that helps in developing compilers, debuggers, software and more.
llvm have a popular c/++ compiler, clang, which also supports plugins.
In this chapter we will build a tiny, simple llvm plugin.

Setting up the plugin

First, we need to setup the plugin. We will be writing it in c++. The following code will register our plugin in llvm passes:

class MBASub
{
 public:
  static void runOnSub(BasicBlock *BB);
  static void runOnAdd(BasicBlock *BB);

  static void runOnBasicBlock(BasicBlock *);
  static void runOnFunction(Function *);
};

class TinyObfuscator : public PassInfoMixin<TinyObfuscator>
{
 public:
  PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);

  static bool isRequired() { return true; }
};

std::atomic<bool> STATE{false};

PassPluginLibraryInfo getPluginInfo()
{
  return {LLVM_PLUGIN_API_VERSION, "TinyObfuscator", LLVM_VERSION_STRING,
    [](PassBuilder &PB)
    {
      PB.registerPipelineEarlySimplificationEPCallback(
          [&](ModulePassManager &MPM, OptimizationLevel opt)
          {
            if (STATE.load())
              return true;

            STATE.store(true);

            MPM.addPass(TinyObfuscator());

            return true;
      });
  }};
}

extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo()
{
  return getPluginInfo();
}

Implementing TinyObfuscator::run

PreservedAnalyses TinyObfuscator::run(Module &M, ModuleAnalysisManager &MAM)
{
  for (Function &F : M.getFunctionList()) {
    MBASub::runOnFunction(Func);
  }
  return PreservedAnalyses::none();
}

return PreservedAnalyses::none(); means that the module did change.
Now, we need to implement MBASub::runOnFunction, the last peice of this journey.

What is MBA Sub? How does it work?

it is the process of changing a simple mathematical expression, into a more complex one, that give the same result, and can be repeated over and over again to achieve a code harder to be understood by the naked eye.
Some examples of mixed boolean arithmetic expanded expressions:

X - Y == (X ^ -Y) + 2*(X & -Y)

x + y = (~ (x +
  ((- x) +
    ((- x) + (~ y))
  )
))

r = rand(); c = b + r; a = a + c; a = a - r // a and b won't change
                        

Implementing MBASub::run

we will obfuscate only sub expression (a - b) in this example:

void MBASub::runOnBasicBlock(BasicBlock *BB)
{
  runOnSub(BB);
  runOnAdd(BB);
}

void MBASub::runOnFunction(Function *Func)
{
  for (auto &BB : Func->getBasicBlockList())
  {
    runOnBasicBlock(&BB);
  }
}

Value ObfuscateSub(IRBuilder<> &Builder, BinaryOperator *Operation) {
  // X - Y == (X ^ -Y) + 2*(X & -Y)
  Type *type = Operation->getOperand(0)->getType();
  return Builder->CreateAdd(
    Builder->CreateXor(Operation->getOperand(0),
      Builder->CreateNeg(Operation->getOperand(1))),
        Builder->CreateMul(
          ConstantInt::getSigned(type, 2),
          Builder->CreateAnd(
            Operation->getOperand(0),
            Builder->CreateNeg(Operation->getOperand(1)
          )
        )
      )
    );
}

void MBASub::runOnSub(BasicBlock *BB)
{
  std::vector<Instruction *> instructions;

  for (auto &Instr : BB->getInstList())
      if (Instr.getOpcode() == Instruction::Sub)
        instructions.push_back(&Instr);

  for (auto &Instr : instructions)
  {
    BinaryOperator *BinOp = (BinaryOperator *)Instr;
    IRBuilder<> Builder(Instr);
    Instr->replaceAllUsesWith(ObfuscateSub(Builder, BinOp));
  }
}

Compiling the plugin

To compile the code, you can use my CMake configuration below, after installing llvm-devel:

project(tobf DESCRIPTION "obfuscator llvm pass")

set(CMAKE_C_COMPILER clang)
set(CMAKE_CXX_COMPILER clang++)

find_package(LLVM REQUIRED CONFIG)

add_definitions(${LLVM_DEFINITIONS})

include_directories(
    ${LLVM_INCLUDE_DIRS}
    include
)

add_library(tobf SHARED
    src/TinyObfuscator.cpp
    src/passes/MBASub.cpp
)

Using the plugin

To use it, simply run the following command:

clang++ -fpass-plugin=./build/libtobf.so main.cpp -o ./out/main.so

Chapter Contents

Go to next Chapter: