以前大家说LLVM的时候是指编译后端,现在的LLVM是一个集编译前端,优化器和编译后端于一体的一整套编译框架
我这段时间重点重新梳理了LLVM相关的知识,用的是最新的LLVM-11.1.0版本,同时在这个版本上移植了OLLVM
编译过程按照官方初学者文档操作即可
- https://llvm.org/docs/GettingStarted.html#overview
整体一遍下来除了耗时间之外,就是耗硬盘空间了
以C语言为例,Clang会把源码编译成一种叫作LLVM IR的中间语言,然后LLVM项目里的llvm会调用多个LLVM Pass对LLVM IR进行处理,最后编译成目标平台的二进制固件
OLLVM全称Obfuscator-LLVM,它会向Pass管理器注册多个自定义Pass,每个Pass都是通过处理LLVM IR来进行混淆
LLVM-9.0移植OLLVM需要处理的错误很少,公开的文章都有描述,我这里就不过多啰嗦了
主要讲一下LLVM-11.1.0移植的时候额外需要做的操作
报错点一
../lib/Transforms/Obfuscation/Flattening.cpp:129:14: error: no matching constructor for initialization of 'llvm::LoadInst'
load = new LoadInst(switchVar, "switchVar", loopEntry);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
找到LoadInst
的构造函数,可以看到新版本的LLVM多了一个参数Type *Ty
// llvm 4.0
class LoadInst : public UnaryInstruction {
...
public:
LoadInst(Value *Ptr, const Twine &NameStr, Instruction *InsertBefore);
// llvm 11.1.0
class LoadInst : public UnaryInstruction {
...
public:
LoadInst(Type *Ty, Value *Ptr, const Twine &NameStr, Instruction *InsertBefore);
通过搜索这个构造函数的使用例子,我们修改代码
load = new LoadInst(switchVar->getAllocatedType(), switchVar, "switchVar", loopEntry);
报错点二和报错点一是类似的
../lib/Transforms/Obfuscation/BogusControlFlow.cpp:557:19: error: no matching constructor for initialization of 'llvm::LoadInst'
opX = new LoadInst ((Value *)x, "", (*i));
^ ~~~~~~~~~~~~~~~~~~~~
../lib/Transforms/Obfuscation/BogusControlFlow.cpp:558:19: error: no matching constructor for initialization of 'llvm::LoadInst'
opY = new LoadInst ((Value *)y, "", (*i));
^ ~~~~~~~~~~~~~~~~~~~~
修改方式和第一处报错一致,参考构造函数添加第一个参数
// llvm-project-11.1.0.src/llvm/lib/IR/Instructions.cpp
LoadInst::LoadInst(Type *Ty, Value *Ptr, const Twine &Name, bool isVolatile,
Align Align, AtomicOrdering Order, SyncScope::ID SSID,
Instruction *InsertBef)
: UnaryInstruction(Ty, Load, Ptr, InsertBef) {
assert(Ty == cast<PointerType>(Ptr->getType())->getElementType());
opX = new LoadInst (cast<PointerType>(((Value *)x)->getType())->getElementType(), (Value *)x, "", (*i));
opY = new LoadInst (cast<PointerType>(((Value *)y)->getType())->getElementType(), (Value *)y, "", (*i));
报错点三,找了半天发现这函数被删了
../lib/Transforms/Obfuscation/Substitution.cpp:232:26: error: no member named 'CreateFNeg' in 'llvm::BinaryOperator'; did you mean 'CreateNeg'?
op = BinaryOperator::CreateFNeg(bo->getOperand(0), "", bo);
~~~~~~~~~~~~~~~~^~~~~~~~~~
CreateNeg
../include/llvm/IR/InstrTypes.h:378:26: note: 'CreateNeg' declared here
把删了的代码加回去,这里的处理仅保证代码能运行,具体业务逻辑我还没分析不知道会不会有影响
// llvm-project-11.1.0.src/llvm/include/llvm/IR/InstrTypes.h
static BinaryOperator *CreateFNeg(Value *Op, const Twine &Name = "",
Instruction *InsertBefore = nullptr);
static BinaryOperator *CreateFNeg(Value *Op, const Twine &Name,
BasicBlock *InsertAtEnd);
// llvm-project-11.1.0.src/llvm/lib/IR/Instructions.cpp
BinaryOperator *BinaryOperator::CreateFNeg(Value *Op, const Twine &Name,
Instruction *InsertBefore) {
Value *zero = ConstantFP::getZeroValueForNegation(Op->getType());
return new BinaryOperator(Instruction::FSub, zero, Op,
Op->getType(), Name, InsertBefore);
}
BinaryOperator *BinaryOperator::CreateFNeg(Value *Op, const Twine &Name,
BasicBlock *InsertAtEnd) {
Value *zero = ConstantFP::getZeroValueForNegation(Op->getType());
return new BinaryOperator(Instruction::FSub, zero, Op,
Op->getType(), Name, InsertAtEnd);
}
我们需要再做几处修改修复LowerSwitch的问题,参考修改
- https://github.com/amimo/goron/commit/e943a8a78325632df64988e05d66ad5fa0e0c6f6
给不熟悉OLLVM的同学感受一下这个混淆的强度
原始GRAPH
开启虚假控制流选项
开启控制流平坦化选项
我有一年参加阿里的移动安全挑战赛,一打开那个So,其中一个函数用了代码膨胀,汇编九万多行,都没法F5,我单步调试的时候一步一卡的
以至于后来我都没敢找逆向岗,怕一打开又是九万行汇编的函数
今儿个为了确认这个具体行数我又打开看了一眼,哎呀妈呀
代码膨胀以我目前的知识水平确实是没法处理,我不知道它是怎么膨胀的,自然没有优化的方案
这两天看到一个比较好玩的文章是TikTok的One Click RCE,我有空仔细写篇文章聊一聊
- https://medium.com/@dPhoeniixx/tiktok-for-android-1-click-rce-240266e78105
大家在测试APP的时候看到什么TestActivity之类的,那都要仔细看看,因为有的开发人员为了测试方便,会在这种组件里面主动调用很多东西,而且代码根本就不review,方便了他们,也方便了咱们
现在安卓平台流行插件化开发,会有很多动态下发的插件,那些插件往往也是review的少,业务有时候随便打个包就放上去了,漏洞相当多,用Frida写个Hook脚本监控这些动态加载的行为或者源码层面修改一劳永逸
还有一个是利用CodeQL来挖漏洞,不过我只是把Demo跑起来了,具体的还没有开始看,这个月忙的焦虑了都
下个月估计就好了