Dex文件混淆(一):BlackObfuscator
文章目录
1. 前言
由于工作需要,最近有比较多的需求需要我研究应用的保护手段。但是我本身又没有做个代码保护
相关的经验。最快的学习手段是参考前人优秀的项目进行学习。所以,我们今天的主角是 BlackObfuscator
2.小试牛刀
使用AndroidStudio
打开工程,运行main/src/main/java/Main
java
public class Main {
public static void main(String[] args) {
System.out.println("Hello Black");
BlackObfuscatorCmd.main("d2j-black-obfuscator",
"-d", "2",
"-i", "testDir/classes.dex",
"-o", "testDir/classes_out.dex",
"-a", "filter.txt");
}
}
filter.txt
内容
#it is annotation
#cn.kaicity
#class
#cn.kaicity.gk.cdk.BuildConfig
#package
com.alipay.sdk.pay.demo
#blackList
!cn.kaicity.gk.cdk
#blackList中的包或者类不会进行混淆
运行完成以后,看到了classes_out.dex
文件
使用jadx反编译原版classes.dex
和通过Ob混淆后的dex文件classes_out.dex
.
可以看到下图的效果非常的恶心.效果也很好,那么我们进入源码的分析学习阶段.
3. 参考学习
由于该项目是由dex2jar
的修改版,我们要把它全部实现一遍,所以我们需要对它进行从0-1
的复现
1. dex2jar源码简析
为了更好的理解,我们有必要对dex2jar的源码进行研究.
2. BlackObfuscator简析
1. 控制流平坦化
由于原作者的介绍较为简单,同时自己对这些混淆手段也不是特别清楚,只是知道有这么一个东西. 所以这边在其他地方查阅一些资料,补充下基础的理论知识.
1. 控制流平坦化基本介绍
控制流平坦化是指将正常程序控制流中基本块之间的跳转关练删除,用一个集中的主分发块来调度基本块的执行顺序。相当于把原有程序正常的逻辑改为一个循环嵌套一个switch的逻辑。
正常情况:
控制流平坦化之后:
控制流平坦化的基本结构如下:
- 入口块:进入函数第一个执行的基本块
- 分发块:负责跳转到下一个要执行的原基本块
- 原基本块:混淆之前的基本块,实际完成程序工作的基本块
- 返回块:返回到主分发块
修改了程序的控制流,导致逆向分析人员不容易直接的理清程序执行流程,增加分析难度。
2. Dex解析
提取出关键Dex为 {timestamp} + split.dex
. 它是由DexLib2Utils.splitDex(input.toFile(), splitDex, whileList, blackList);
实现的.
分割完Dex以后,程序就进入了第一个核心内容
java
new Dex2jarCmd(new ObfuscatorConfiguration() {
@Override
public int getObfDepth() {
return depth;
}
}).doMain("-f", splitDex.getPath(), "-o", tempJar.toString());
convert
-> reBuildInstructions
-> IRObfuscator.get(obf).reBuildInstructions(ir);
java
public void ir2j(IrMethod irMethod, MethodVisitor mv, ClzCtx clzCtx) {
new IR2JConverter()
.optimizeSynchronized(0 != (V3.OPTIMIZE_SYNCHRONIZED & v3Config))
.clzCtx(clzCtx)
.ir(irMethod)
.asm(mv)
.obf(obfuscatorConfiguration)
.convert();
}
- ir.stmts : 方法主要的IR指令
- chain.reBuildEnd : 混淆的主要方法. 这里会走到各个混淆的真正实现的地方.
java
for (ObfuscatorChain chain : chains) {
for (int i = 0; i < configuration.getObfDepth(); i++) {
List<Stmt> newStmts = new ArrayList<>();
List<Stmt> origStmts = new ArrayList<>();
for (Stmt value : ir.stmts) {
origStmts.add(value);
}
RebuildIfResult rebuildIfResult;
for (Stmt stmt : ir.stmts) {
if (chain.canHandle(ir, stmt)) {
rebuildIfResult = chain.reBuild(ir, stmt, origStmts);
if (rebuildIfResult != null) {
newStmts.addAll(rebuildIfResult.getResult());
}
} else {
newStmts.add(stmt);
}
}
chain.reBuildEnd(ir, newStmts, origStmts);
ir.stmts.clear();
ir.stmts.addAll(newStmts);
if (!chain.canDepth()) {
break;
}
}
}
3. YPObfuscator制作
1. 祖安混淆(娱乐)
祖安混淆主要来源于这位哥.在群里面说了一声,骂人混淆 == 祖安混淆. 那么经过对BlackObfuscator的简单查阅后,发现了一个ObfDic.java
文件,里面存放的内容就是混淆的字典,我们稍微的修改修改
java
public static String[] dic =("小\n胖\nYP\nfont\na\nb\nc\nd\n白\n树\n海\n小\n坤\n坤\n坤\n操你妈\n反编译你妈死了\n草你妈隔壁\n回头是岸\n车公庙\n反编译\n逆向\n编辑框\nab\ncccccccc\ndakakk\nasjdajdj\nasdjawlk1j\nq\nmmm\nsdasd\nnnnn\n坤哥牛逼\n花花\n王旺\n反编译哈哈哈\n你破解不了的\n嘻嘻嘻拜拜\n芜湖\nggggg").split("\n");
然后就成果了~ 成果的问候了各位逆向工作者,无恶意无恶意!