Skip to content

Latest commit

 

History

History
158 lines (114 loc) · 5.75 KB

README.md

File metadata and controls

158 lines (114 loc) · 5.75 KB

Port to LLVM 14.06
Docker for llvm environment: ssageparuders/android_llvm_14.06:18.04
Made by SsageParuders

HowToUse

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过程中的一些笔记和个人积累


TODO LIST

  • 实现对单个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优化过的虚假控制流

  • 优化函数包装为随机字符串

  • 完善控制流平坦化

  • 完善英文文档


Credits

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