通过LLVM传递来混淆整数常量

时间:2019-02-02 18:12:09

标签: llvm obfuscation

在阅读this nice article from Quarkslab on obfuscating zeroes之后,我想我将对其进行一些微调以混淆任意整数常量。

但是,看来我的密码被忽略了,或者对生成的LLVM位代码(甚至二进制可执行文件)没有任何影响。

简单的混淆处理如下:生成一个随机的int,然后使用该键对要隐藏的常量进行XOR运算。将二进制补码应用于结果。

这将产生一个整数,然后通过发出所需的LLVM位代码将其计算为原始值。

这是我的PoC(改编自1):

#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <sstream>

using namespace llvm;

namespace {
class MyPass : public BasicBlockPass {
public:
  static char ID;

  MyPass() : BasicBlockPass(ID) {}

  bool runOnBasicBlock(BasicBlock &BB) override {

    bool modified = false;
    for (typename BasicBlock::iterator I = BB.getFirstInsertionPt(),
                                       end = BB.end();
         I != end; ++I) {

      Instruction &Inst = *I;

      if (!isValidCandidateInstruction(Inst))
        continue;

      for (size_t i = 0; i < Inst.getNumOperands(); ++i) {

        if (Constant *C = isValidCandidateOperand(Inst.getOperand(i))) {
          std::stringstream stream;
          stream << std::hex << C->getUniqueInteger().getLimitedValue();
          std::string result(stream.str());
          errs() << "Found an integer: 0x" << result << "\n";

          if (C->getUniqueInteger().getLimitedValue() == 1337) {
            errs() << "Obfuscating constant 1337\n";
            if (Value *New_val = obfuscateInt(Inst, C)) {
              Inst.setOperand(i, New_val);
              modified = true;
              errs() << "Replaced with " << New_val << "\n";
            } else {
              errs() << "ObfuscateZero: could not rand pick a variable for "
                        "replacement\n";
            }
          }
        }
      }
    }
    return modified;
  }

  // replValue = ~(originalInt ^ key) -1
  Value *obfuscateInt(Instruction &Inst, Constant *C) {

    srand(time(NULL));
    int key = std::rand();
    int64_t replacedValue = ~(C->getUniqueInteger().getLimitedValue() ^ key);

    Constant *replValue = ConstantInt::get(C->getType(), replacedValue),
             *keyValue = ConstantInt::get(C->getType(), key);

    IRBuilder<> Builder(&Inst);
    Value *repl = Builder.CreateXor(replValue, keyValue);
    Value *finValue = Builder.CreateNeg(repl);

    return Builder.CreateSub(finValue, ConstantInt::get(C->getType(), 1));
  }

  // only interested in integer values  
  Constant *isValidCandidateOperand(Value *V) {
    Constant *C;
    if (!(C = dyn_cast<Constant>(V)))
      return nullptr;

    if (!C->getType()->isIntegerTy()) {
      return nullptr;
    }

    return C;
  }

  bool isValidCandidateInstruction(Instruction &Inst) {
    if (isa<GetElementPtrInst>(&Inst)) {
      errs() << "Ignoring GEP\n";
      return false;
    } else if (isa<SwitchInst>(&Inst)) {
      errs() << "Ignoring Switch\n";
      return false;
    } else if (isa<CallInst>(&Inst)) {
      errs() << "Ignoring Calls\n";
      return false;
    } else {
      return true;
    }
  }
};

} // namespace

char MyPass::ID = 0;
static RegisterPass<MyPass> X("MyPass", "Obfuscates 1337", true, false);

// register pass for clang use
static void registerMyPassPass(const PassManagerBuilder &,
                               llvm::legacy::PassManagerBase &PM) {
  PM.add(new MyPass());
}

static RegisterStandardPasses
    RegisterMBAPass(PassManagerBuilder::EP_OptimizerLast, registerMyPassPass);

以及简单的测试程序:

int main(void)
{
    volatile int a = 3;
    a += 1337;
    return a;
}

我按如下所示编译LLVM传递:

clang -g3 -shared -fPIC MyPass.cpp -o pass/MyPass.so

然后我对上述简单测试的LLVM位代码运行通过:

opt -S -load pass/MyPass.so -MyPass bin/simple_test.ll -o bin/out.ll

bin / out.ll的内容与bin / simple_test.ll相同,这显然与我想要的相反:

; ModuleID = 'bin/simple_test.ll'
source_filename = "tests/simple_test.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

; Function Attrs: noinline nounwind optnone sspstrong uwtable
define dso_local i32 @main() #0 {
  %1 = alloca i32, align 4
  %2 = alloca i32, align 4
  store i32 0, i32* %1, align 4
  store volatile i32 3, i32* %2, align 4
  %3 = load volatile i32, i32* %2, align 4
  %4 = add nsw i32 %3, 1337
  store volatile i32 %4, i32* %2, align 4
  %5 = load volatile i32, i32* %2, align 4
  ret i32 %5
}

attributes #0 = { noinline nounwind optnone sspstrong uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0, !1, !2}
!llvm.ident = !{!3}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"PIC Level", i32 2}
!2 = !{i32 7, !"PIE Level", i32 2}
!3 = !{!"clang version 7.0.1 (tags/RELEASE_701/final)"}

当然,我认为编译器可以优化我的小型混淆尝试,但是在将小型转换手动应用于测试程序之后,我可以在结果反汇编中看到其他XOR,NEG和SUB操作,这使我认为这里的优化器没有错。

我对概念证明很感兴趣,在这个概念证明中,常数1337被(略)“隐藏”,仅出于此目的。对评论说混淆是徒劳的,或者指出代码中您不喜欢的与问题无关的东西,不太感兴趣。

2 个答案:

答案 0 :(得分:0)

这里的问题是IRBuilder,它在创建新的IR指令时默认执行Constant Folding

要解决此问题,我必须在IR中创建一个新的volatile(volatile不是必须的,但我可以这样做)变量,对其执行“混淆”算术运算,并替换该指令的操作数使用“ 1337”作为结果值。

除了函数obfuscateInt(...)如下所示,代码与问题中的代码相同:

  // replValue = ~(originalInt ^ key) -1
  Value *obfuscateInt(BasicBlock &BB, Instruction &Inst, Constant *C) {

    srand(time(NULL));
    int key = std::rand();
    int32_t replacedValue = ~(C->getUniqueInteger().getLimitedValue() ^ key);

    Constant *replValue = ConstantInt::get(C->getType(), replacedValue),
             *keyValue = ConstantInt::get(C->getType(), key);

    IRBuilder<> Builder(&Inst);

    // allocate enough space on the stack to store a 32-bit value. Var name = "AA"
    AllocaInst *varAlloc = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, "AA");

    // Store the key in AA, set "volatile" to true
    Builder.CreateStore(keyValue, varAlloc, true);

    // read the variable "AA"
    LoadInst *loadVar = Builder.CreateLoad(varAlloc, true, "AA");

    // use it
    Value *repl = Builder.CreateXor(replValue, loadVar);
    Value *finValue = Builder.CreateNeg(repl);

    return Builder.CreateSub(finValue, Builder.getInt32(1));
  }

生成的IR现在看起来像:

  %1 = alloca i32, align 4
  %2 = alloca i32, align 4
  store i32 0, i32* %1, align 4
  store volatile i32 3, i32* %2, align 4
  %3 = load volatile i32, i32* %2, align 4
  %4 = alloca i32
  store volatile i32 525933950, i32* %4
  %5 = load volatile i32, i32* %4
  %6 = xor i32 -525932616, %5
  %7 = sub i32 0, %6
  %8 = sub i32 %7, 1
  %9 = add nsw i32 %3, %8
  store volatile i32 %9, i32* %2, align 4
  %10 = load volatile i32, i32* %2, align 4
  %11 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([41 x i8], [41 x i8]* @.str, i32 0, i32 0), i32 %10)
  %12 = load volatile i32, i32* %2, align 4
  ret i32 %12

反汇编显示1337没有出现在任何地方,但是程序的行为得以保留:

0000000000001140 <main>:
    1140:       55                      push   rbp
    1141:       48 89 e5                mov    rbp,rsp
    1144:       48 83 ec 10             sub    rsp,0x10
    1148:       31 c0                   xor    eax,eax
    114a:       c7 45 fc 00 00 00 00    mov    DWORD PTR [rbp-0x4],0x0
    1151:       c7 45 f8 03 00 00 00    mov    DWORD PTR [rbp-0x8],0x3
    1158:       8b 4d f8                mov    ecx,DWORD PTR [rbp-0x8]
    115b:       c7 45 f4 02 77 c4 31    mov    DWORD PTR [rbp-0xc],0x31c47702
    1162:       8b 55 f4                mov    edx,DWORD PTR [rbp-0xc]
    1165:       81 f2 c4 8d 3b ce       xor    edx,0xce3b8dc4
    116b:       29 d0                   sub    eax,edx
    116d:       83 e8 01                sub    eax,0x1
    1170:       01 c1                   add    ecx,eax
    1172:       89 4d f8                mov    DWORD PTR [rbp-0x8],ecx
    ....

答案 1 :(得分:0)

IRBuilder performs basic constant folding by default(由问题作者在其answer中报告)。

要在IRBuilder中禁用常量折叠,请按以下步骤创建。

IRBuilder<NoFolder> Builder;