LLVM 进阶一:符号混淆(LTO)
2021-11-16 本文已影响0人
鸣人的大哥
更新:编译参数添加对静态库的说明
二次更新:实际业务中意义不是很大
一、目的:
实现链接时符号混淆
参考文档:
https://llvm.org/docs/LinkTimeOptimization.html
https://mayuyu.io/2017/06/01/LLVMHacking-0x1/#more
二、思路:
- 编译阶段处理符号存在硬伤,抛弃
- 链接器 ld 只是一个调度器,核心逻辑在库 libLTO.dylib
- pass注册在文件 LTOBackend.cpp 里
- 链接时的module整合成了一个总的module, 所以链接时可以修改符号
三、实现流程:
从这篇文章开始基于LLVM13.0,pass的注册与实现不同于以前的版本,反复折腾,QAQ;
1、pass代码:
不再使用runOnModule
实现,我参考了 StripSymbols.cpp
的实现方式,官方在老的pass里加入了新的pass流程,官方都支持,我这里使用了新的pass调用方式,如下:
- 头文件 SymbolObfuscation.h:
#include "llvm/IR/PassManager.h"
namespace llvm {
struct SymbolObfuscationPass : PassInfoMixin<SymbolObfuscationPass> {
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
};
}
- cpp 文件 SymbolObfuscation.cpp:
#include "llvm/IR/Module.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/SymbolObfuscation/SymbolObfuscation.h"
#include <string>
#include <iostream>
#include <cstdlib>
using namespace llvm;
using namespace std;
static string obfcharacters="-_.|/\\`+,=()*:";
int seed = 0;
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 "f_" + name;
}
PreservedAnalyses SymbolObfuscationPass::run(Module &M, ModuleAnalysisManager &AM) {
//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()<<"\n";
errs()<<"New Function Name: "<<newname<<"\n";
F.setName(newname);
}
else{
errs()<<"Skipping External Function: "<<F.getName()<<"\n";
}
}
return PreservedAnalyses::all();
}
2、如何添加该pass:
在 llvm/LTO/LTOBackend.cpp
文件添加,选择在这里添加是因为我们的目的是实现链接时优化。
-
添加命令行参数:
image.png -
函数 runNewPMPasses 添加:
image.png
四、测试:
-
上述在llvm13.0编译通过后,可以先找一个简单的cpp文件编译测试下效果,例:
image.png -
为了展示更好的效果,我举例一个iOS的Xcode项目
确保原始ios项目运行没问题后,只需要添加几个参数即可使用,替换掉xcode的clang就可以了
1、xcode添加编译参数:
- 目标二进制的编译参数:
-flto
- 如果依赖了静态库,需要在静态库的target也配置编译参数
-flto
,如果依赖库不需要混淆符号,则该target不需要添加-flto
:
image.png
2、xcode添加链接参数:
image.png
- -Xlinker 将 clang 参数传递给 链接器 ld
- -lto_library 指定LLVM的链接库
3、编译,首先查看链接日志如下,说明链接时pass执行成功了:
image.png4、自测运行该app正常,二进制包大小也没有变化:
没有做过批量测试
5、nm查看二进制符号如下:
image.png