Port to LLVM 14.06
Docker for llvm environment: ssageparuders/android_llvm_14.06:18.04
Made by SsageParuders
SsagePass为out-of-tree编译的LLVM动态库插件
- 
各参数介绍: # __attribute((__annotate__(("xx")))) // 填写注解 以控制单个函数的Pass ##-----======= 详解 =======-----## # bcf --- 虚假控制流 ## bcf_prob -- 每个基本块被虚假控制流混淆的概率 -- 0 < bcf_prob < 100 ## bcf_loop -- 每个函数被虚假控制流重复多少次 -- 无限制 建议 2~5 ## bcf_cond_compl -- 用于生成分支条件的表达式的复杂性 -- 无限制 建议 3~10 # ofla --- 控制流平坦化 # enfla --- 控制流平坦化增强版 # mba --- 线性混合布尔算术混淆 # funwra --- 函数嵌套包装 ## fw_prob -- 每个函数被包装的可能性 -- 0 < fw_prob < 100 ## fw_times -- 每个函数被包装嵌套多少次 -- 无限制 建议 2~5 # split -- 基本块分割 ## split_num -- 原先一个基本块被分割为多少基本块 -- 无限制 建议 2~5 # indibr --- 间接跳转 # vmf --- 虚拟机控制流平坦化 # strenc --- 字符串加密 以上介绍 #标记的为注解填写 用于控制混淆开关
 ##标记的为cl::opt参数 用于传递混淆粒度
- 
替代symbols 该功能是llvm自带的 只不过我专门注册一下罢 # symbols_obf.yaml # function: { source: _Z3addii, target: dfffff} # 函数替换 # global variable: { source: _ZL3aaa, transform: ccccc} # 变量替换 clang++ -fpass-plugin=../build/libSsageObfuscator.so -mllvm --rewrite-map-file=symbols_obf.yaml main.cpp -o main # 通过-mllvm --rewrite-map-file=symbols_obf.yaml传递替换信息进入编译 
- 
clang中触发指定Pass的临时方案: # opt样例 -- 默认开启全部Pass 但是是否真的启用 依然需要读取并且判断函数注解 opt --load-pass-plugin=../build/SsageObfuscator.so -O1 -S main.ll -o main_fla.ll # opt样例 -- 指定开启全局某Pass 但是是否真的启用某Pass 依然需要读取并且判断函数注解 opt --load-pass-plugin=../build/SsageObfuscator.so -passes=split,fla -S main.ll -o main_fla.ll # clang样例 clang++ -fpass-plugin=../build/SsageObfuscator.so main.cpp -o main 在clang的NEW PM中 我始终无法成功通过传入特定参数触发指定Pass 
 因此 我在代码的PMRegistration.cpp里 默认注册全部的Pass 并且默认为不开启
 只有读取函数注解 成功匹配到相应Pass的字符串 才会对相应函数开启特定Pass
 这里也建议其他使用者 非必要 不开启全局混淆 这会导致不必要的性能损耗
 针对关键函数启用指定混淆 这种方案在我眼中最佳
- 
传递 SplitNum这种混淆粒度的临时解决方案:
 把动态库用两种方案都加载一遍,但是Pass用NEW PM控制# opt样例 opt --load-pass-plugin=../build/SsageObfuscator.so -passes=split,fla -load ../Build/SsageObfuscator.so -split_num=7 -S main.ll -o main_fla.ll # clang样例 clang++ -fpass-plugin=../build/SsageObfuscator.so -Xclang -load -Xclang ../build/SsageObfuscator.so -mllvm -split_num=7 main.cpp -o main 在clang的NEW PM中 貌似暂时不支持传递cl::opt内容 
 不知道以后会不会优化 或者是有什么其他较优方案
 然后又因为我们什么地方启用什么Pass完全是由函数的注解控制的
 所以我们只需要用NEW PM的方案加载进Pass插件即可使其生效
 然后再用Legacy Pass Manager的方案载入插件
 这样传入cl::opt内容 如此可以指定混淆程度
- 
什么是函数注解: // 样例 如果熟悉ollvm应该一下子就知道是什么意思了 void say_hello() __attribute((__annotate__(("fla split strenc")))){ printf("Hello~\n"); } 
这两种临时方案都是受限于个人水平有限的无奈之举
如果有人知道如何更好的解决 欢迎提交PR
LLVM Pass的源代码
本Pass采用out-of-tree方式编译为动态库
以方便作为插件便捷载入
用于测试的代码 后续会添加Android的测试样本
chmod +x demo.sh && ./demo.sh学习LLVM过程中的一些笔记和个人积累
- 
实现对单个Function启用PASS 
- 
初步完善LLVM API文档 
- 
测试Hikari的14适配 
- 
解决LowerSwitchPass在LLVM-9以上的适配问题 
- 
更换PASS管理器为 NEW PM
- 
适配来自Hikari的字符串加密 
- 
解决 SplitNum混淆程度在NEW PM上的传递问题
- 
解决 NEW PM中 clang如何触发指定PASS的功能
- 
初步完善README和Docs文档 
- 
初步适配上Android编译链[ndk_r25] 
- 
适配来自Hikari的间接跳转 
- 
适配来自Hikari的函数包装 
- 
适配Hikari优化过的虚假控制流 
- 
优化函数包装为随机字符串 
- 
完善控制流平坦化 
- 
完善英文文档 
OLLVM By heroims
llvm-pass-tutorial By LeadroyaL
llvm-tutor By banach-space
Pluto-Obfuscator By bluesadi
goron By amimo
Hikari By HikariObfuscator
LLVMMyPass By za233