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
```
## 分析
  Hikari是用异或方式将静态区字符串在编译期加密,并在函数入口处动态解密到预分配的静态区,支持C/OC字符串
* 因为字符串属于模块范围可操作的元素而非函数, 因此需要注册为ModulePass, 入口点为runOnModule
* 入口点使用toObfuscate判断是否需要字符串加密, HandleFunction为加密处理函数
* HandleFunction区分出哪些全局数据是字符串, 以及哪些字符串需要混淆
* 需要处理编译期优化, 防止静态区数据丢失, 或者混淆逻辑被还原, 混淆和优化其实是2个方向相反的过程
加解密过程:
* 编译期Hikari将字符串异或加密并存储为可执行模块的静态数据, 预分配解密后的存储区, 对同一字符串的多个引用, Hikari会创建多份加密副本防止冲突
* 编译期Hikari将异或解密逻辑StringDecryptionBB插入到函数入口点
* 运行时可执行模块解密静态数据到预分配存储区, 使用LoadEncryptionStatus变量记录是否已解密, 如果未解密则执行StringDecryptionBB否则执行PrecedingBlock
优缺点:
* 异或加密简单可靠,兼容性较强
* 异或加密算法较简单;执行一次函数即可在静态内存取获取解密的字符串
本帖最后由 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]