找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 605|回复: 2

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

[复制链接]
发表于 2024-9-21 11:56:16 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
本帖最后由 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相关源码

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:inkageTypes:rivateLinkage,
            S, "");
        encstatus[F] = 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[GV] = make_pair(EncryptedRawGV, DecryptSpaceGV);
      GV2Keys[DecryptSpaceGV] = 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[oldrawString].first, CS);
      GlobalVariable *DecryptSpaceOCGV = ObjectivCString(GV, "DecryptSpaceObjC", oldrawString, old2new[oldrawString].second, CS);
      old2new[GV] = 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[Func];
    /*
      - 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

// 1.cpp
#include <stdio.h>
int main(int argc, char** argv) {
    puts("hello");
    return 0;
}
# 生成未混淆Bitcode
build/bin/clang -isysroot `xcrun --sdk macosx --show-sdk-path` -arch x86_64 -emit-llvm -S -o /tmp/12.ll /tmp/1.cpp
@.str = private unnamed_addr constant [6 x i8] 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 ([6 x i8], [6 x i8]* @.str, i32 0, i32 0))
  ret i32 0
}
declare i32 @puts(i8*) #1
# 生成混淆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
@0 = private global i32 0
@EncryptedString = private global [6 x i8] c"*d\D2\15-A"
@DecryptSpace = private global [6 x i8] c"\A7\F1\D9*\82\C8"
@llvm.compiler.used = appending global [1 x i8*] [i8* getelementptr inbounds ([6 x i8], [6 x i8]* @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 ([6 x i8], [6 x i8]* @EncryptedString, i32 0, i32 0)
  %1 = xor i8 %EncryptedChar, 66
  store i8 %1, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @DecryptSpace, i32 0, i32 0)
  %EncryptedChar1 = load i8, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @EncryptedString, i32 0, i32 1)
  %2 = xor i8 %EncryptedChar1, 1
  store i8 %2, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @DecryptSpace, i32 0, i32 1)
  %EncryptedChar2 = load i8, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @EncryptedString, i32 0, i32 2)
  %3 = xor i8 %EncryptedChar2, -66
  store i8 %3, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @DecryptSpace, i32 0, i32 2)
  %EncryptedChar3 = load i8, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @EncryptedString, i32 0, i32 3)
  %4 = xor i8 %EncryptedChar3, 121
  store i8 %4, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @DecryptSpace, i32 0, i32 3)
  %EncryptedChar4 = load i8, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @EncryptedString, i32 0, i32 4)
  %5 = xor i8 %EncryptedChar4, 66
  store i8 %5, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @DecryptSpace, i32 0, i32 4)
  %EncryptedChar5 = load i8, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @EncryptedString, i32 0, i32 5)
  %6 = xor i8 %EncryptedChar5, 65
  store i8 %6, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @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 [6 x i8], [6 x i8]* @DecryptSpace, i32 0, i32 0
  %call = call i32 @puts(i8* %7)
  ret i32 0
}

declare i32 @puts(i8*) #1

分析

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

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

加解密过程:

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

优缺点:

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

使用道具 举报

 楼主| 发表于 2024-9-28 00:37:43 | 显示全部楼层
本帖最后由 lichao 于 2024-9-28 00:41 编辑

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

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

#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:assPluginLibraryInfo llvmGetPassPluginInfo() {
    return {
        .APIVersion = LLVM_PLUGIN_API_VERSION,
        .PluginName = PASSNAME,
        .PluginVersion = "1.0",
        .RegisterPassBuilderCallbacks = [](PassBuilder &B) {
            PB.registerPipelineStartEPCallback(
                [](ModulePassManager &MPM
#if LLVM_VERSION_MAJOR >= 12
                , OptimizationLevel Level
#endif
                ) {
                    MPM.addPass(MyPassDemo());
            });
            PB.registerPipelineParsingCallback(
                [](StringRef Name, ModulePassManager& MPM, ArrayRef<assBuilder:ipelineElement>) {
                    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 ([11 x i8], [11 x i8]* @.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[i]);
            IRB.CreateStore(n, gep);
        }
        inst->setOperand(idx, alloca);
    }
}

测试

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

编译为Debug

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 
__text:0000000100007E8C                 SUB             X0, X29, #-var_13 ; char *
__text:0000000100007E90                 MOV             W9, #0x68
__text:0000000100007E94                 STURB           W9, [X29,#var_13]
__text:0000000100007E98                 MOV             W9, #0x65
__text:0000000100007E9C                 STURB           W9, [X29,#var_12]
__text:0000000100007EA0                 MOV             W9, #0x6C
__text:0000000100007EA4                 STURB           W9, [X29,#var_11]
__text:0000000100007EA8                 STURB           W9, [X29,#var_10]
__text:0000000100007EAC                 MOV             W10, #0x6F
__text:0000000100007EB0                 STURB           W10, [X29,#var_F]
__text:0000000100007EB4                 MOV             W11, #0x77
__text:0000000100007EB8                 STURB           W11, [X29,#var_E]
__text:0000000100007EBC                 STURB           W10, [X29,#var_D]
__text:0000000100007EC0                 MOV             W10, #0x72
__text:0000000100007EC4                 STURB           W10, [X29,#var_C]
__text:0000000100007EC8                 STURB           W9, [X29,#var_B]
__text:0000000100007ECC                 MOV             W9, #0x64
__text:0000000100007ED0                 STURB           W9, [X29,#var_A]
__text:0000000100007ED4                 STURB           WZR, [X29,#var_9]
__text:0000000100007ED8                 STR             WZR, [SP,#0x30+var_18]
__text:0000000100007EDC                 STR             W8, [SP,#0x30+var_1C]
__text:0000000100007EE0                 STR             X1, [SP,#0x30+var_28]
__text:0000000100007EE4                 BL              _printf
// IDA伪代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  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

llvm15/build/bin/clang -isysroot `xcrun --sdk macosx --show-sdk-path` -fpass-plugin=build/MyPassDemo15.dylib -o /tmp/1.bin /tmp/1.cpp 
__text:0000000100003F06                 mov     [rbp+var_13], 68h
__text:0000000100003F0A                 mov     [rbp+var_12], 65h
__text:0000000100003F0E                 mov     [rbp+var_11], 6Ch
__text:0000000100003F12                 mov     [rbp+var_10], 6Ch
__text:0000000100003F16                 mov     [rbp+var_F], 6Fh
__text:0000000100003F1A                 mov     [rbp+var_E], 77h
__text:0000000100003F1E                 mov     [rbp+var_D], 6Fh
__text:0000000100003F22                 mov     [rbp+var_C], 72h
__text:0000000100003F26                 mov     [rbp+var_B], 6Ch
__text:0000000100003F2A                 mov     [rbp+var_A], 64h
__text:0000000100003F2E                 mov     [rbp+var_9], 0
__text:0000000100003F32                 mov     [rbp+var_18], 0
__text:0000000100003F39                 mov     [rbp+var_1C], edi
__text:0000000100003F3C                 mov     [rbp+var_28], rsi
__text:0000000100003F40                 lea     rdi, [rbp+var_13] ; char *
__text:0000000100003F44                 mov     al, 0
__text:0000000100003F46                 call    _printf
// IDA伪代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  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

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
__text:0000000100007ED8                 LDR             D0, =0x726F776F6C6C6568
__text:0000000100007EDC                 STR             D0, [SP,#0x20+var_18]
__text:0000000100007EE0                 MOV             W8, #0x646C
__text:0000000100007EE4                 STRH            W8, [SP,#0x20+var_10]
__text:0000000100007EE8                 STRB            WZR, [SP,#0x20+var_E]
__text:0000000100007EEC                 ADD             X0, SP, #0x20+var_18 ; char *
__text:0000000100007EF0                 BL              _printf
// IDA伪代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

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

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

llvm15/build/bin/clang -isysroot `xcrun --sdk macosx --show-sdk-path` -fpass-plugin=build/MyPassDemo15.dylib -o /tmp/1.bin /tmp/1.cpp -O3
__text:0000000100003F46                 mov     rax, 726F776F6C6C6568h
__text:0000000100003F50                 mov     qword ptr [rbp+var_18], rax
__text:0000000100003F54                 mov     [rbp+var_10], 646Ch
__text:0000000100003F5A                 mov     [rbp+var_E], 0
__text:0000000100003F5E                 lea     rdi, [rbp+var_18] ; char *
__text:0000000100003F62                 xor     eax, eax
__text:0000000100003F64                 call    _printf
// IDA伪代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

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

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

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
__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, [SP,#0x20+var_13]
__text:0000000100007EE8                 MOV             W8, #0x646C
__text:0000000100007EEC                 STURH           W8, [SP,#0x20+var_B]
__text:0000000100007EF0                 STRB            WZR, [SP,#0x20+var_9]
__text:0000000100007EF4                 ADD             X0, SP, #0x20+var_13 ; char *
__text:0000000100007EF8                 BL              _printf
// IDA伪代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  strcpy(v4, "helloworld");
  result = printf(v4, argv, envp);
  if ( __stack_chk_guard == v5 )
    result = 0;
  return result;
}
llvm15/build/bin/clang -isysroot `xcrun --sdk macosx --show-sdk-path` -fpass-plugin=build/MyPassDemo15.dylib -o /tmp/1.bin /tmp/1.cpp -O3
__text:0000000100003F46                 mov     rax, 726F776F6C6C6568h
__text:0000000100003F50                 mov     qword ptr [rbp+var_13], rax
__text:0000000100003F54                 mov     [rbp+var_B], 646Ch
__text:0000000100003F5A                 mov     [rbp+var_9], 0
__text:0000000100003F5E                 lea     rdi, [rbp+var_13] ; char *
__text:0000000100003F62                 xor     eax, eax
__text:0000000100003F64                 call    _printf
// IDA伪代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

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

回复 赞! 靠!

使用道具 举报

发表于 2024-12-13 20:22:46 | 显示全部楼层
学习了,,,,,
回复

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-12-31 00:14 , Processed in 0.037501 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表