参考:https://mayuyu.io/2017/06/01/LLVMHacking-0x1/
一、目的:
使用xcode更方便的调试和开发pass
二、创建llvm的xcode工程:
cmake指定生成xcode工程之前,先创建一个新的空的pass,然后在cmake生成xcode工程,在xcode项目中可以看到新模块,我使用的版本是llvm9.0.0
- 创建空文件:
- 头文件:
include/llvm/Transforms/Obfuscation/SymbolObfuscation.h
- cpp文件:
lib/Transforms/SymbolObfuscation/SymbolObfuscation.cpp
- 相关 CMakeLists 的配置不在赘述,看过前几小节的应该知道
- cmake指定xcode工程:
mkdir build_xcode
cd build_xcode
cmake -G Xcode CMAKE_BUILD_TYPE="Debug" ../llvm
- 打开xcode工程:
- 首次打开时,时间充足时可以全部构建一遍,选择ALL BUILD,时间略长;
- 也可以在 manage scheme 选择时选择手动管理,添加 opt 模块 、clang模块和 LLVMSymbolObfuscation 模块,这样节省时间,也只需要编译这俩模块;
-
可以看到新创建的pass模块:loadabl modules --> LLVMSymbolObfuscation
image.png
三、pass的调试与开发:
- 完善头文件与cpp文件内容,我这里参考张总的教程,创建一个简单符号混淆的pass
- 头文件内容:
#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);
- 单独调试这一个pass需要使用opt模块调用:
-
构建库LLVMSymbolObfuscation:
选择 模块 LLVMSymbolObfuscation 构建,command + B 编译;
image.png
-
构建opt模块:
选择 模块 opt 构建,command + B 编译,时间略长;
image.png
-
设置opt调用参数:
opt的用法不再赘述,文档:https://llvm.org/docs/CommandGuide/opt.html
image.png
输入的 .ll 文件时自己写的一个测试代码,如下:
#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
-
断点调试:
上述全部OK后,可以直接在源文件打断点调试:
如图,断点调试查看设置前的随机字符串。
image.png
- 查看符号替换的结果:
-
可以直接调试打印IR查看:
image.png
- 直接查看生成的文件:
opt生成的bitcode,需要先转换为文本IR:
llvm-dis /Users/qinyao/LLVM/test_symbolobf/test_symbolobf.ll.bc
查看 llvm-dis 生成的IR:
image.png
四、扩展:
符号混淆是一个比较难处理的点,涉及系统函数以及导出函数,所以 hikari 也并没有开放这个功能,作为学习还不错;
现在 llvm 在utils目录下有自带的 SymbolRewriter.cpp Pass,下一节章节分析一下。
网友评论