lichao 发表于 2024-9-21 11:56:16

OLLVM学习之五 —— 字符串加密模块分析

本帖最后由 lichao 于 2024-9-30 13:24 编辑



  由于这阵子出去玩了, 所以IR的学习又推迟了1周, 现在继续学习

## 前置知识

* 一个项目由多个Module构成, 一个Module约等于一个存在函数实体的文件, 头文件或者被包含的文件不算
* 一个Module由多个Function/GlobalVariable/GlobalAlias/GlobalIFunc/NamedMDNode构成, 前两个最常用
* 一个Function由多个BasicBlock构成, 继承过程: Function - GlobalObject - GlobalValue - Constant - User - Value
* 一个BasicBlock由多个Instruction构成, 每个BasicBlock都有一个结束指令, 继承过程: BasicBlock - Value
* 一个Instruction即为一条IR指令, 最终会编译为一条或多条汇编指令, 继承过程: Instruction - User - Value
* GlobalVariable继承过程: GlobalVariable - GlobalObject - GlobalValue - Constant - User - Value

## Hikari相关源码

```cpp
struct StringEncryption : public ModulePass {
static char ID;
map<Function * /*Function*/, GlobalVariable * /*Decryption Status*/>
      encstatus;
bool flag;
StringEncryption() : ModulePass(ID) { this->flag = true; }
StringEncryption(bool flag) : ModulePass(ID) { this->flag = flag; }
StringRef getPassName() const override {
    return StringRef("StringEncryption");
}
bool runOnModule(Module &M) override {
    // in runOnModule. We simple iterate function list and dispatch functions
    // to handlers
    for (Module::iterator iter = M.begin(); iter != M.end(); iter++) {
      Function *F = &(*iter);

      if (toObfuscate(flag, F, "strenc")) {
      errs() << "Running StringEncryption On " << F->getName() << "\n";
      Constant *S = ConstantInt::get(Type::getInt32Ty(M.getContext()), 0);
      GlobalVariable *GV = new GlobalVariable(
            M, S->getType(), false, GlobalValue::LinkageTypes::PrivateLinkage,
            S, "");
      encstatus = GV;
      HandleFunction(F);
      }
    }
    return true;
} // End runOnModule
void HandleFunction(Function *Func) {
    FixFunctionConstantExpr(Func);
    set<GlobalVariable *> Globals;
    set<User *> Users;
    for (BasicBlock &BB : *Func) {
      for (Instruction &I : BB) {
      for (Value *Op : I.operands()) {
          if (GlobalVariable *G = dyn_cast<GlobalVariable>(Op->stripPointerCasts())) {
            if(User* U=dyn_cast<User>(Op)){
            Users.insert(U);
            }
            Users.insert(&I);
            Globals.insert(G);
          }
      }
      }
    }
    set<GlobalVariable *> rawStrings;
    set<GlobalVariable *> objCStrings;
    map<GlobalVariable *, pair<Constant *, GlobalVariable *>> GV2Keys;
    map<GlobalVariable * /*old*/, pair<GlobalVariable * /*encrypted*/, GlobalVariable * /*decrypt space*/>> old2new;
    for (GlobalVariable *GV : Globals) {
      if (GV->hasInitializer() &&
          GV->getSection() != StringRef("llvm.metadata") &&
          GV->getSection().find(StringRef("__objc")) == string::npos &&
          GV->getName().find("OBJC") == string::npos) {
      if (GV->getInitializer()->getType() ==
            Func->getParent()->getTypeByName("struct.__NSConstantString_tag")) {
          objCStrings.insert(GV);
          rawStrings.insert(
            cast<GlobalVariable>(cast<ConstantStruct>(GV->getInitializer())
                                       ->getOperand(2)
                                       ->stripPointerCasts()));

      } else if (isa<ConstantDataSequential>(GV->getInitializer())) {
          rawStrings.insert(GV);
      }else if(isa<ConstantArray>(GV->getInitializer())){
         ConstantArray* CA=cast<ConstantArray>(GV->getInitializer());
         for(unsigned i=0;i<CA->getNumOperands();i++){
             Value* op=CA->getOperand(i)->stripPointerCasts();
             if(GlobalVariable* GV=dyn_cast<GlobalVariable>(op)){
               Globals.insert(GV);
             }
         }

      }
      }
    }
    for (GlobalVariable *GV : rawStrings) {
      if (GV->getInitializer()->isZeroValue() ||
          GV->getInitializer()->isNullValue()) {
      continue;
      }
      ConstantDataSequential *CDS =
          cast<ConstantDataSequential>(GV->getInitializer());
      Type *memberType = CDS->getElementType();
      // Ignore non-IntegerType
      if (!isa<IntegerType>(memberType)) {
      continue;
      }
      IntegerType *intType = cast<IntegerType>(memberType);
      Constant *KeyConst = NULL;
      Constant *EncryptedConst = NULL;
      Constant *DummyConst = NULL;
      if (intType == Type::getInt8Ty(GV->getParent()->getContext())) {
      vector<uint8_t> keys;
      vector<uint8_t> encry;
      vector<uint8_t> dummy;
      for (unsigned i = 0; i < CDS->getNumElements(); i++) {
          uint8_t K = cryptoutils->get_uint8_t();
          uint64_t V = CDS->getElementAsInteger(i);
          keys.push_back(K);
          encry.push_back(K ^ V);
          dummy.push_back(rand());
      }
      KeyConst = ConstantDataArray::get(GV->getParent()->getContext(),
                                          ArrayRef<uint8_t>(keys));
      EncryptedConst = ConstantDataArray::get(GV->getParent()->getContext(),
                                                ArrayRef<uint8_t>(encry));
      DummyConst = ConstantDataArray::get(GV->getParent()->getContext(),
                                                ArrayRef<uint8_t>(dummy));

      } else if (intType == Type::getInt16Ty(GV->getParent()->getContext())) {
      vector<uint16_t> keys;
      vector<uint16_t> encry;
      vector<uint16_t> dummy;
      for (unsigned i = 0; i < CDS->getNumElements(); i++) {
          uint16_t K = cryptoutils->get_uint16_t();
          uint64_t V = CDS->getElementAsInteger(i);
          keys.push_back(K);
          encry.push_back(K ^ V);
          dummy.push_back(rand());
      }
      KeyConst = ConstantDataArray::get(GV->getParent()->getContext(),
                                          ArrayRef<uint16_t>(keys));
      EncryptedConst = ConstantDataArray::get(GV->getParent()->getContext(),
                                                ArrayRef<uint16_t>(encry));
      DummyConst = ConstantDataArray::get(GV->getParent()->getContext(),
                                                ArrayRef<uint16_t>(dummy));
      } else if (intType == Type::getInt32Ty(GV->getParent()->getContext())) {
      vector<uint32_t> keys;
      vector<uint32_t> encry;
      vector<uint32_t> dummy;
      for (unsigned i = 0; i < CDS->getNumElements(); i++) {
          uint32_t K = cryptoutils->get_uint32_t();
          uint64_t V = CDS->getElementAsInteger(i);
          keys.push_back(K);
          encry.push_back(K ^ V);
          dummy.push_back(rand());
      }
      KeyConst = ConstantDataArray::get(GV->getParent()->getContext(),
                                          ArrayRef<uint32_t>(keys));
      EncryptedConst = ConstantDataArray::get(GV->getParent()->getContext(),
                                                ArrayRef<uint32_t>(encry));
      DummyConst = ConstantDataArray::get(GV->getParent()->getContext(),
                                                ArrayRef<uint32_t>(dummy));
      } else if (intType == Type::getInt64Ty(GV->getParent()->getContext())) {
      vector<uint64_t> keys;
      vector<uint64_t> encry;
      vector<uint64_t> dummy;
      for (unsigned i = 0; i < CDS->getNumElements(); i++) {
          uint64_t K = cryptoutils->get_uint64_t();
          uint64_t V = CDS->getElementAsInteger(i);
          keys.push_back(K);
          encry.push_back(K ^ V);
          dummy.push_back(rand());
      }
      KeyConst = ConstantDataArray::get(GV->getParent()->getContext(),
                                          ArrayRef<uint64_t>(keys));
      EncryptedConst = ConstantDataArray::get(GV->getParent()->getContext(),
                                                ArrayRef<uint64_t>(encry));
      DummyConst = ConstantDataArray::get(GV->getParent()->getContext(),
                                                ArrayRef<uint64_t>(dummy));
      } else {
      errs() << "Unsupported CDS Type\n";
      abort();
      }
      // Prepare new rawGV
      GlobalVariable *EncryptedRawGV = new GlobalVariable(
          *(GV->getParent()), EncryptedConst->getType(), false,
          GV->getLinkage(), EncryptedConst, "EncryptedString", nullptr,
          GV->getThreadLocalMode(), GV->getType()->getAddressSpace());
      GlobalVariable *DecryptSpaceGV = new GlobalVariable(
          *(GV->getParent()), DummyConst->getType(), false,
          GV->getLinkage(), DummyConst, "DecryptSpace", nullptr,
          GV->getThreadLocalMode(), GV->getType()->getAddressSpace());
      old2new = make_pair(EncryptedRawGV, DecryptSpaceGV);
      GV2Keys = make_pair(KeyConst, EncryptedRawGV);
    }
    // Now prepare ObjC new GV
    for (GlobalVariable *GV : objCStrings) {
      ConstantStruct *CS = cast<ConstantStruct>(GV->getInitializer());
      GlobalVariable *oldrawString = cast<GlobalVariable>(CS->getOperand(2)->stripPointerCasts());
      if (old2new.find(oldrawString) == old2new.end()) { // Filter out zero initializers
      continue;
      }
      GlobalVariable *EncryptedOCGV = ObjectivCString(GV, "EncryptedStringObjC", oldrawString, old2new.first, CS);
      GlobalVariable *DecryptSpaceOCGV = ObjectivCString(GV, "DecryptSpaceObjC", oldrawString, old2new.second, CS);
      old2new = make_pair(EncryptedOCGV, DecryptSpaceOCGV);
    } // End prepare ObjC new GV
    if(old2new.empty() || GV2Keys.empty())
      return;
    // Replace Uses
    for (User *U : Users) {
      for (map<GlobalVariable *, pair<GlobalVariable *, GlobalVariable *>>::iterator iter =
               old2new.begin();
         iter != old2new.end(); ++iter) {
      U->replaceUsesOfWith(iter->first, iter->second.second);
      iter->first->removeDeadConstantUsers();
      }
    } // End Replace Uses
    // CleanUp Old ObjC GVs
    for (GlobalVariable *GV : objCStrings) {
      if (GV->getNumUses() == 0) {
      GV->dropAllReferences();
      old2new.erase(GV);
      GV->eraseFromParent();
      }
    }
    // CleanUp Old Raw GVs
    for (map<GlobalVariable *, pair<GlobalVariable *, GlobalVariable *>>::iterator iter =
             old2new.begin();
         iter != old2new.end(); ++iter) {
      GlobalVariable *toDelete = iter->first;
      toDelete->removeDeadConstantUsers();
      if (toDelete->getNumUses() == 0) {
      toDelete->dropAllReferences();
      toDelete->eraseFromParent();
      }
    }
    GlobalVariable *StatusGV = encstatus;
    /*
      - Split Original EntryPoint BB into A and C.
      - Create new BB as Decryption BB between A and C. Adjust the terminators
      into: A (Alloca a new array containing all)
            |
            B(If not decrypted)
            |
            C
    */
    BasicBlock *A = &(Func->getEntryBlock());
    BasicBlock *C = A->splitBasicBlock(A->getFirstNonPHIOrDbgOrLifetime());
    C->setName("PrecedingBlock");
    BasicBlock *B =
      BasicBlock::Create(Func->getContext(), "StringDecryptionBB", Func, C);
    // Change A's terminator to jump to B
    // We'll add new terminator to jump C later
    BranchInst *newBr = BranchInst::Create(B);
    ReplaceInstWithInst(A->getTerminator(), newBr);
    IRBuilder<> IRB(A->getFirstNonPHIOrDbgOrLifetime());
    // Insert DecryptionCode
    HandleDecryptionBlock(B, C, GV2Keys);
    // Add atomic load checking status in A
    LoadInst *LI = IRB.CreateLoad(StatusGV, "LoadEncryptionStatus");
    LI->setAtomic(AtomicOrdering::Acquire); // Will be released at the start of
                                          // C
    LI->setAlignment(4);
    Value *condition = IRB.CreateICmpEQ(
      LI, ConstantInt::get(Type::getInt32Ty(Func->getContext()), 0));
    A->getTerminator()->eraseFromParent();
    BranchInst::Create(B, C, condition, A);
    // Add StoreInst atomically in C start
    // No matter control flow is coming from A or B, the GVs must be decrypted
    IRBuilder<> IRBC(C->getFirstNonPHIOrDbgOrLifetime());
    StoreInst *SI = IRBC.CreateStore(
      ConstantInt::get(Type::getInt32Ty(Func->getContext()), 1), StatusGV);
    SI->setAlignment(4);
    SI->setAtomic(AtomicOrdering::Release); // Release the lock acquired in LI

} // End of HandleFunction

GlobalVariable *ObjectivCString(GlobalVariable *GV, string name, GlobalVariable *oldrawString, GlobalVariable *newString, ConstantStruct *CS) {
      Value *zero = ConstantInt::get(Type::getInt32Ty(GV->getContext()), 0);
      vector<Constant *> vals;
      vals.push_back(CS->getOperand(0));
      vals.push_back(CS->getOperand(1));
      Constant *GEPed = ConstantExpr::getInBoundsGetElementPtr(nullptr, newString, {zero, zero});
      if (GEPed->getType() == CS->getOperand(2)->getType()) {
      vals.push_back(GEPed);
      } else {
      Constant *BitCasted = ConstantExpr::getBitCast(newString, CS->getOperand(2)->getType());
      vals.push_back(BitCasted);
      }
      vals.push_back(CS->getOperand(3));
      Constant *newCS = ConstantStruct::get(CS->getType(), ArrayRef<Constant *>(vals));
      return new GlobalVariable(
          *(GV->getParent()), newCS->getType(), false, GV->getLinkage(), newCS,
          name.c_str(), nullptr, GV->getThreadLocalMode(),
          GV->getType()->getAddressSpace());
}

void HandleDecryptionBlock(BasicBlock *B, BasicBlock *C,
                           map<GlobalVariable *, pair<Constant *, GlobalVariable *>> &GV2Keys) {
    IRBuilder<> IRB(B);
    Value *zero = ConstantInt::get(Type::getInt32Ty(B->getContext()), 0);
    for (map<GlobalVariable *, pair<Constant *, GlobalVariable *>>::iterator iter = GV2Keys.begin();
         iter != GV2Keys.end(); ++iter) {
      ConstantDataArray *CastedCDA = cast<ConstantDataArray>(iter->second.first);
      // Prevent optimization of encrypted data
      appendToCompilerUsed(*iter->second.second->getParent(),
                           {iter->second.second});
      // Element-By-Element XOR so the fucking verifier won't complain
      // Also, this hides keys
      for (unsigned i = 0; i < CastedCDA->getType()->getNumElements(); i++) {
      Value *offset = ConstantInt::get(Type::getInt32Ty(B->getContext()), i);
      Value *EncryptedGEP = IRB.CreateGEP(iter->second.second, {zero, offset});
      Value *DecryptedGEP = IRB.CreateGEP(iter->first, {zero, offset});
      LoadInst *LI = IRB.CreateLoad(EncryptedGEP, "EncryptedChar");
      Value *XORed = IRB.CreateXor(LI, CastedCDA->getElementAsConstant(i));
      IRB.CreateStore(XORed, DecryptedGEP);
      }
    }
    IRB.CreateBr(C);
} // End of HandleDecryptionBlock
bool doFinalization(Module &M) override {
    encstatus.clear();
    return false;
}
};
```

## Bitcode

```cpp
// 1.cpp
#include <stdio.h>
int main(int argc, char** argv) {
        puts("hello");
        return 0;
}
```


```bash
# 生成未混淆Bitcode
build/bin/clang -isysroot `xcrun --sdk macosx --show-sdk-path` -arch x86_64 -emit-llvm -S -o /tmp/12.ll /tmp/1.cpp
```

```IR
@.str = private unnamed_addr constant c"hello\00", align 1

define i32 @main(i32 %argc, i8** %argv) #0 {
entry:
%retval = alloca i32, align 4
%argc.addr = alloca i32, align 4
%argv.addr = alloca i8**, align 8
store i32 0, i32* %retval, align 4
store i32 %argc, i32* %argc.addr, align 4
store i8** %argv, i8*** %argv.addr, align 8
%call = call i32 @puts(i8* getelementptr inbounds (, * @.str, i32 0, i32 0))
ret i32 0
}
declare i32 @puts(i8*) #1
```

```bash
# 生成混淆Bitcode
build/bin/clang -isysroot `xcrun --sdk macosx --show-sdk-path` -arch x86_64 -emit-llvm -S -mllvm -enable-strcry -o /tmp/12.ll /tmp/1.cpp
```

```IR
@0 = private global i32 0
@EncryptedString = private global c"*d\D2\15-A"
@DecryptSpace = private global c"\A7\F1\D9*\82\C8"
@llvm.compiler.used = appending global , * @EncryptedString, i32 0, i32 0)], section "llvm.metadata"

define i32 @main(i32 %argc, i8** %argv) #0 {
entry:
%LoadEncryptionStatus = load atomic i32, i32* @0 acquire, align 4
%0 = icmp eq i32 %LoadEncryptionStatus, 0
br i1 %0, label %StringDecryptionBB, label %PrecedingBlock

StringDecryptionBB:                               ; preds = %entry
%EncryptedChar = load i8, i8* getelementptr inbounds (, * @EncryptedString, i32 0, i32 0)
%1 = xor i8 %EncryptedChar, 66
store i8 %1, i8* getelementptr inbounds (, * @DecryptSpace, i32 0, i32 0)
%EncryptedChar1 = load i8, i8* getelementptr inbounds (, * @EncryptedString, i32 0, i32 1)
%2 = xor i8 %EncryptedChar1, 1
store i8 %2, i8* getelementptr inbounds (, * @DecryptSpace, i32 0, i32 1)
%EncryptedChar2 = load i8, i8* getelementptr inbounds (, * @EncryptedString, i32 0, i32 2)
%3 = xor i8 %EncryptedChar2, -66
store i8 %3, i8* getelementptr inbounds (, * @DecryptSpace, i32 0, i32 2)
%EncryptedChar3 = load i8, i8* getelementptr inbounds (, * @EncryptedString, i32 0, i32 3)
%4 = xor i8 %EncryptedChar3, 121
store i8 %4, i8* getelementptr inbounds (, * @DecryptSpace, i32 0, i32 3)
%EncryptedChar4 = load i8, i8* getelementptr inbounds (, * @EncryptedString, i32 0, i32 4)
%5 = xor i8 %EncryptedChar4, 66
store i8 %5, i8* getelementptr inbounds (, * @DecryptSpace, i32 0, i32 4)
%EncryptedChar5 = load i8, i8* getelementptr inbounds (, * @EncryptedString, i32 0, i32 5)
%6 = xor i8 %EncryptedChar5, 65
store i8 %6, i8* getelementptr inbounds (, * @DecryptSpace, i32 0, i32 5)
br label %PrecedingBlock

PrecedingBlock:                                 ; preds = %entry, %StringDecryptionBB
store atomic i32 1, i32* @0 release, align 4
%retval = alloca i32, align 4
%argc.addr = alloca i32, align 4
%argv.addr = alloca i8**, align 8
store i32 0, i32* %retval, align 4
store i32 %argc, i32* %argc.addr, align 4
store i8** %argv, i8*** %argv.addr, align 8
%7 = getelementptr inbounds , * @DecryptSpace, i32 0, i32 0
%call = call i32 @puts(i8* %7)
ret i32 0
}

declare i32 @puts(i8*) #1
```


## 分析

&emsp;&emsp;Hikari是用异或方式将静态区字符串在编译期加密,并在函数入口处动态解密到预分配的静态区,支持C/OC字符串

* 因为字符串属于模块范围可操作的元素而非函数, 因此需要注册为ModulePass, 入口点为runOnModule
* 入口点使用toObfuscate判断是否需要字符串加密, HandleFunction为加密处理函数
* HandleFunction区分出哪些全局数据是字符串, 以及哪些字符串需要混淆
* 需要处理编译期优化, 防止静态区数据丢失, 或者混淆逻辑被还原, 混淆和优化其实是2个方向相反的过程

加解密过程:
* 编译期Hikari将字符串异或加密并存储为可执行模块的静态数据, 预分配解密后的存储区, 对同一字符串的多个引用, Hikari会创建多份加密副本防止冲突
* 编译期Hikari将异或解密逻辑StringDecryptionBB插入到函数入口点
* 运行时可执行模块解密静态数据到预分配存储区, 使用LoadEncryptionStatus变量记录是否已解密, 如果未解密则执行StringDecryptionBB否则执行PrecedingBlock

优缺点:
* 异或加密简单可靠,兼容性较强
* 异或加密算法较简单;执行一次函数即可在静态内存取获取解密的字符串




lichao 发表于 2024-9-28 00:37:43

本帖最后由 lichao 于 2024-9-28 00:41 编辑



## 实验: 将静态字符串转换为栈字符串

以下代码在LLVM8-18下测试, 仅使用NewPass. 注意本节只是为了验证静态转栈的可行性, 不推荐实际使用.

```cpp
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Pass.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Support/raw_ostream.h"
#if LLVM_VERSION_MAJOR <= 15
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#endif

using namespace llvm;

#define PASSNAME          "MyPassDemo"

static void doModule(Module& M);

// ---------------- New Pass ---------------- //
#if LLVM_VERSION_MAJOR <= 13
#define OptimizationLevel PassBuilder::OptimizationLevel
#endif

class MyPassDemo : public PassInfoMixin<MyPassDemo> {
public:
    PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM) {
      doModule(M);
      return PreservedAnalyses::all();
    };
    static bool isRequired() { return true; }
};

extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() {
    return {
      .APIVersion = LLVM_PLUGIN_API_VERSION,
      .PluginName = PASSNAME,
      .PluginVersion = "1.0",
      .RegisterPassBuilderCallbacks = [](PassBuilder &PB) {
            PB.registerPipelineStartEPCallback(
                [](ModulePassManager &MPM
#if LLVM_VERSION_MAJOR >= 12
                , OptimizationLevel Level
#endif
                ) {
                  MPM.addPass(MyPassDemo());
            });
            PB.registerPipelineParsingCallback(
                [](StringRef Name, ModulePassManager& MPM, ArrayRef<PassBuilder::PipelineElement>) {
                  MPM.addPass(MyPassDemo());
                  return true;
            });
      }
    };
}
// ---------------- New Pass ---------------- //
#include <vector>
class TodoItem {
public:
    Instruction*    inst;
    unsigned      idx;
    StringRef       data;   
};

void doModule(Module& M) {   
    std::vector<TodoItem> todo_list;
    auto handle_gv = [&todo_list](Instruction* I, unsigned i, GlobalVariable* GV) {
      if (GV->isConstant() && GV->hasInitializer()) {
            Constant* GVI = GV->getInitializer();
            ConstantDataArray* CDA = dyn_cast<ConstantDataArray>(GVI);
            if (CDA != 0) {
                StringRef data = CDA->getAsString(); // 如果是字符串则包括'\0'
                if (data.size() >= 2) {
                  errs() << "Add todo_list: " << data << "\n";
                  todo_list.push_back({I, i, data});
                }
            }
      }
    };
    for (Function& F : M) {
      for (BasicBlock& bb : F) {
            for (Instruction& I : bb) {
                for (unsigned i = 0; i < I.getNumOperands(); i++) {
                  Value* v = I.getOperand(i);
                  unsigned valueID = v->getValueID();
                  if (valueID == Value::GlobalVariableVal) { // LLVM>=15
                        GlobalVariable* GV = dyn_cast<GlobalVariable>(v);
                        handle_gv(&I, i, GV);
                        // @printf(ptr noundef @.str) -> @printf(ptr noundef %str)
                  } else if (valueID == Value::ConstantExprVal) { // LLVM<=14
                        ConstantExpr* CE = dyn_cast<ConstantExpr>(v);
                        unsigned op = CE->getOpcode();
                        if (op == Instruction::GetElementPtr) {
                            Value* v0 = CE->getOperand(0);
                            Value* v1 = CE->getOperand(1);
                            Value* v2 = CE->getOperand(2);
                            unsigned vID0 = v0->getValueID();
                            unsigned vID1 = v1->getValueID();
                            unsigned vID2 = v2->getValueID();
                            if (vID0 == Value::GlobalVariableVal && vID1 == Value::ConstantIntVal && vID2 == Value::ConstantIntVal ) {
                              if (dyn_cast<ConstantInt>(v1)->getSExtValue() == 0 && dyn_cast<ConstantInt>(v2)->getSExtValue() == 0) {
                                    GlobalVariable* GV = dyn_cast<GlobalVariable>(v0);
                                    handle_gv(&I, 0, GV);
                                    // @printf(i8* noundef getelementptr inbounds (, * @.str, i64 0, i64 0)) -> @printf(i8* noundef %str)
                              }
                            }
                        }
                  }
                }
            }
      }
    }
    for (TodoItem& item : todo_list) {
      Instruction* inst = item.inst;
      unsigned idx = item.idx;
      StringRef data = item.data;   
      BasicBlock* bb = inst->getParent();
      IRBuilder<> IRB(&bb->front());
      AllocaInst* alloca = IRB.CreateAlloca(IRB.getInt8Ty(), IRB.getInt32(data.size()));
      for (unsigned i = 0; i < data.size(); i++) {
            Value* gep = IRB.CreateConstGEP1_64(IRB.getInt8Ty(), alloca, i);
            Constant* n = ConstantInt::get(IRB.getInt8Ty(), data);
            IRB.CreateStore(n, gep);
      }
      inst->setOperand(idx, alloca);
    }
}
```

## 测试

```cpp
// /tmp/1.cpp
#include <stdio.h>
int main(int argc, char** argv) {
        printf("helloworld");
        return 0;
}
```

### 编译为Debug

```bash
llvm15/build/bin/clang -isysroot `xcrun --sdk iphoneos --show-sdk-path` -arch arm64 -fpass-plugin=build/MyPassDemo15.dylib -o /tmp/1.bin /tmp/1.cpp
```

```asm
__text:0000000100007E8C               SUB             X0, X29, #-var_13 ; char *
__text:0000000100007E90               MOV             W9, #0x68
__text:0000000100007E94               STURB         W9,
__text:0000000100007E98               MOV             W9, #0x65
__text:0000000100007E9C               STURB         W9,
__text:0000000100007EA0               MOV             W9, #0x6C
__text:0000000100007EA4               STURB         W9,
__text:0000000100007EA8               STURB         W9,
__text:0000000100007EAC               MOV             W10, #0x6F
__text:0000000100007EB0               STURB         W10,
__text:0000000100007EB4               MOV             W11, #0x77
__text:0000000100007EB8               STURB         W11,
__text:0000000100007EBC               STURB         W10,
__text:0000000100007EC0               MOV             W10, #0x72
__text:0000000100007EC4               STURB         W10,
__text:0000000100007EC8               STURB         W9,
__text:0000000100007ECC               MOV             W9, #0x64
__text:0000000100007ED0               STURB         W9,
__text:0000000100007ED4               STURB         WZR,
__text:0000000100007ED8               STR             WZR,
__text:0000000100007EDC               STR             W8,
__text:0000000100007EE0               STR             X1,
__text:0000000100007EE4               BL            _printf
```

```cpp
// IDA伪代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
//

v4 = 104;
v5 = 101;
v6 = 108;
v7 = 108;
v8 = 111;
v9 = 119;
v10 = 111;
v11 = 114;
v12 = 108;
v13 = 100;
printf(&v4);
return 0;
}
```

注意:此结果为IDA7.0版生成,如果使用IDA7.7+则识别为strcpy

```bash
llvm15/build/bin/clang -isysroot `xcrun --sdk macosx --show-sdk-path` -fpass-plugin=build/MyPassDemo15.dylib -o /tmp/1.bin /tmp/1.cpp
```

```asm
__text:0000000100003F06               mov   , 68h
__text:0000000100003F0A               mov   , 65h
__text:0000000100003F0E               mov   , 6Ch
__text:0000000100003F12               mov   , 6Ch
__text:0000000100003F16               mov   , 6Fh
__text:0000000100003F1A               mov   , 77h
__text:0000000100003F1E               mov   , 6Fh
__text:0000000100003F22               mov   , 72h
__text:0000000100003F26               mov   , 6Ch
__text:0000000100003F2A               mov   , 64h
__text:0000000100003F2E               mov   , 0
__text:0000000100003F32               mov   , 0
__text:0000000100003F39               mov   , edi
__text:0000000100003F3C               mov   , rsi
__text:0000000100003F40               lea   rdi, ; char *
__text:0000000100003F44               mov   al, 0
__text:0000000100003F46               call    _printf
```

```cpp
// IDA伪代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
//

v4 = 104;
v5 = 101;
v6 = 108;
v7 = 108;
v8 = 111;
v9 = 119;
v10 = 111;
v11 = 114;
v12 = 108;
v13 = 100;
v14 = 0;
printf(&v4, argv, envp);
result = __stack_chk_guard;
if ( __stack_chk_guard == v15 )
    result = 0;
return result;
}
```

### 编译为Release

```bash
llvm15/build/bin/clang -isysroot `xcrun --sdk iphoneos --show-sdk-path` -arch arm64 -fpass-plugin=build/MyPassDemo15.dylib -o /tmp/1.bin /tmp/1.cpp -O3
```

```asm
__text:0000000100007ED8               LDR             D0, =0x726F776F6C6C6568
__text:0000000100007EDC               STR             D0,
__text:0000000100007EE0               MOV             W8, #0x646C
__text:0000000100007EE4               STRH            W8,
__text:0000000100007EE8               STRB            WZR,
__text:0000000100007EEC               ADD             X0, SP, #0x20+var_18 ; char *
__text:0000000100007EF0               BL            _printf
```

```cpp
// IDA伪代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
//

strcpy(v4, "helloworld");
result = printf(v4, argv, envp);
if ( __stack_chk_guard == v5 )
    result = 0;
return result;
}
```

注意:此时IDA已经把上述指令集识别成内部函数的strcpy, 这里strcpy非动态库里的那个函数

```bash
llvm15/build/bin/clang -isysroot `xcrun --sdk macosx --show-sdk-path` -fpass-plugin=build/MyPassDemo15.dylib -o /tmp/1.bin /tmp/1.cpp -O3
```

```asm
__text:0000000100003F46               mov   rax, 726F776F6C6C6568h
__text:0000000100003F50               mov   qword ptr , rax
__text:0000000100003F54               mov   , 646Ch
__text:0000000100003F5A               mov   , 0
__text:0000000100003F5E               lea   rdi, ; char *
__text:0000000100003F62               xor   eax, eax
__text:0000000100003F64               call    _printf
```

```cpp
// IDA伪代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
//

strcpy(v4, "helloworld");
printf(v4, argv, envp);
if ( __stack_chk_guard != v5 )
    __stack_chk_fail();
return 0;
}
```

### 介入时机指定为EP_OptimizerLast,编译为Release

```bash
llvm15/build/bin/clang -isysroot `xcrun --sdk iphoneos --show-sdk-path` -arch arm64 -fpass-plugin=build/MyPassDemo15.dylib -o /tmp/1.bin /tmp/1.cpp -O3
```

```asm
__text:0000000100007ED4               MOV             X8, #0x6568
__text:0000000100007ED8               MOVK            X8, #0x6C6C,LSL#16
__text:0000000100007EDC               MOVK            X8, #0x776F,LSL#32
__text:0000000100007EE0               MOVK            X8, #0x726F,LSL#48
__text:0000000100007EE4               STUR            X8,
__text:0000000100007EE8               MOV             W8, #0x646C
__text:0000000100007EEC               STURH         W8,
__text:0000000100007EF0               STRB            WZR,
__text:0000000100007EF4               ADD             X0, SP, #0x20+var_13 ; char *
__text:0000000100007EF8               BL            _printf
```

```cpp
// IDA伪代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
//

strcpy(v4, "helloworld");
result = printf(v4, argv, envp);
if ( __stack_chk_guard == v5 )
    result = 0;
return result;
}
```

```bash
llvm15/build/bin/clang -isysroot `xcrun --sdk macosx --show-sdk-path` -fpass-plugin=build/MyPassDemo15.dylib -o /tmp/1.bin /tmp/1.cpp -O3
```

```asm
__text:0000000100003F46               mov   rax, 726F776F6C6C6568h
__text:0000000100003F50               mov   qword ptr , rax
__text:0000000100003F54               mov   , 646Ch
__text:0000000100003F5A               mov   , 0
__text:0000000100003F5E               lea   rdi, ; char *
__text:0000000100003F62               xor   eax, eax
__text:0000000100003F64               call    _printf
```

```cpp
// IDA伪代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
//

strcpy(v4, "helloworld");
printf(v4, argv, envp);
if ( __stack_chk_guard != v5 )
    __stack_chk_fail();
return 0;
}
```


页: [1]
查看完整版本: OLLVM学习之五 —— 字符串加密模块分析