參考:https://mayuyu.io/2017/06/01/LLVMHacking-0x1/
一、目的:
使用xcode更方便的調(diào)試和開發(fā)pass
二洒敏、創(chuàng)建llvm的xcode工程:
cmake指定生成xcode工程之前龄恋,先創(chuàng)建一個(gè)新的空的pass,然后在cmake生成xcode工程凶伙,在xcode項(xiàng)目中可以看到新模塊郭毕,我使用的版本是llvm9.0.0
- 創(chuàng)建空文件:
- 頭文件:
include/llvm/Transforms/Obfuscation/SymbolObfuscation.h
- cpp文件:
lib/Transforms/SymbolObfuscation/SymbolObfuscation.cpp
- 相關(guān) CMakeLists 的配置不在贅述,看過前幾小節(jié)的應(yīng)該知道
- cmake指定xcode工程:
mkdir build_xcode
cd build_xcode
cmake -G Xcode CMAKE_BUILD_TYPE="Debug" ../llvm
- 打開xcode工程:
- 首次打開時(shí)函荣,時(shí)間充足時(shí)可以全部構(gòu)建一遍显押,選擇ALL BUILD扳肛,時(shí)間略長(zhǎng);
- 也可以在 manage scheme 選擇時(shí)選擇手動(dòng)管理乘碑,添加 opt 模塊 挖息、clang模塊和 LLVMSymbolObfuscation 模塊,這樣節(jié)省時(shí)間兽肤,也只需要編譯這倆模塊套腹;
-
可以看到新創(chuàng)建的pass模塊:loadabl modules --> LLVMSymbolObfuscation
三、pass的調(diào)試與開發(fā):
- 完善頭文件與cpp文件內(nèi)容资铡,我這里參考張總的教程电禀,創(chuàng)建一個(gè)簡(jiǎn)單符號(hào)混淆的pass
- 頭文件內(nèi)容:
#include "llvm/Pass.h"
#define DEBUG_TYPE "symbolobf"
namespace llvm {
ModulePass* createSymbolObfuscationPass();
// void initializeSymbolObfuscationPass(PassRegistry &Registry);
}
- cpp文件代碼:
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Transforms/Obfuscation/SymbolObfuscation.h"
#include <string>
#include <iostream>
#include <cstdlib>
using namespace llvm;
using namespace std;
static string obfcharacters="qwertyuiopasdfghjklzxcvbnm1234567890";
namespace llvm{
struct SymbolObfuscation : public ModulePass {
static char ID;
int seed = 0;
SymbolObfuscation() : ModulePass(ID) {}
string randomString(int length){
string name;
name.resize(length);
srand(seed);
seed++;
for(int i=0;i<length;i++){
name[i]=obfcharacters[rand()%(obfcharacters.length())];
}
return name;
}
bool runOnModule(Module &M) override {
//F.setName(randomString(16));
errs()<<"Start Symbol Rewrite!\n";
for(Module::iterator Fun=M.begin();Fun!=M.end();Fun++){
Function &F=*Fun;
if (F.getName().str().compare("main")==0){
errs()<<"Skipping main\n";
}
else if(F.empty()==false){
//Rename
string newname = randomString(16);
errs()<<"Renaming Function: "<<F.getName()<<" --> New Name: "<<newname<<"\n";
F.setName(newname);
}
else{
errs()<<"Skipping External Function: "<<F.getName()<<"\n";
}
}
return true;
}
};
ModulePass * createSymbolObfuscationPass() {return new SymbolObfuscation();}
}
char SymbolObfuscation::ID = 0;
//INITIALIZE_PASS(SymbolObfuscation, "symbolobf", "Rewrite Symbols",
// false, false)
static RegisterPass<SymbolObfuscation> X("symbolobf", "Rewrite Symbols", false, false);
- 單獨(dú)調(diào)試這一個(gè)pass需要使用opt模塊調(diào)用:
-
構(gòu)建庫LLVMSymbolObfuscation:
選擇 模塊 LLVMSymbolObfuscation 構(gòu)建,command + B 編譯笤休;
-
構(gòu)建opt模塊:
選擇 模塊 opt 構(gòu)建尖飞,command + B 編譯,時(shí)間略長(zhǎng)店雅;
-
設(shè)置opt調(diào)用參數(shù):
opt的用法不再贅述政基,文檔:https://llvm.org/docs/CommandGuide/opt.html
輸入的 .ll 文件時(shí)自己寫的一個(gè)測(cè)試代碼,如下:
#include <stdio.h>
static int add(int x, int y) {
return x + y;
}
int sub(int x, int y) {
return x - y;
}
int main(){
printf("%d",add(3,4));
printf("%d",sub(5,4));
return 0;
}
生成 .ll 文件:
clang -c test_symbolobf.c -o test_symbolobf.ll -emit-llvm -S
-
斷點(diǎn)調(diào)試:
上述全部OK后闹啦,可以直接在源文件打斷點(diǎn)調(diào)試:
如圖沮明,斷點(diǎn)調(diào)試查看設(shè)置前的隨機(jī)字符串。
- 查看符號(hào)替換的結(jié)果:
-
可以直接調(diào)試打印IR查看:
- 直接查看生成的文件:
opt生成的bitcode亥揖,需要先轉(zhuǎn)換為文本IR:
llvm-dis /Users/qinyao/LLVM/test_symbolobf/test_symbolobf.ll.bc
查看 llvm-dis 生成的IR:
四珊擂、擴(kuò)展:
符號(hào)混淆是一個(gè)比較難處理的點(diǎn),涉及系統(tǒng)函數(shù)以及導(dǎo)出函數(shù)费变,所以 hikari 也并沒有開放這個(gè)功能摧扇,作為學(xué)習(xí)還不錯(cuò);
現(xiàn)在 llvm 在utils目錄下有自帶的 SymbolRewriter.cpp Pass挚歧,下一節(jié)章節(jié)分析一下扛稽。