java deobf开发日志1
说明:这篇文章仅仅是记录deobf的transformer的开发,或者算是一个小教程。代码存储在https://github.com/luiox/jvm-things-archieve/blob/main/morpher-plugin/src/main/java/com/github/luiox/gruntdeobf/Sample001Pass1.java
为了方便,以及减少相同代码的反复编写,我已经开发了一些基础工具。地址是https://github.com/luiox/morpher-api。
引入库
首先需要加jitpack,因为包在jitpack存储。
1 | repositories { |
其次,引入依赖,前面是依赖的库。
1 | dependencies { |
注意库的版本,尽可能使用最新版本。
样本来源说明
不针对任何人,此样本来自Discord的频道https://discord.gg/tRU27KtPAZ分享,在https://github.com/luiox/jvm-things-archieve仓库的sample目录下,名字是sample-001.jar,原本名字为nCrypt-Resolve.jar。
这个混淆比较弱,非常适合入门。
分析到构建pass
首先进行样本分析,我们利用Recaf得到cfr反编译观察到下面这个结构。
1 | if ((0x6B4E0A72 ^ 0x6B4E0A72) != 0) { |
观察main方法的字节码,前面这个if结构对应的字节码如下。
1 | ldc 1800276594 |
其实核心在下面这三条指令。
1 | ldc 1800276594 |
这个是一个必定为0的表达式,一个数异或他自己必定是0。即a ^ a = 0。
所以,我们可以把它替换为iconst_0。
我们需要先新建一个MethodPass来处理MethodNode级别的转换。
1 |
|
利用库内的matcher来构建替换。使用方法是,先定义一个PatternMatcher,然后再在static里面初始化这个matcher的规则,StepUtil提供了一些step,这样子不需要自己去new了。addStep可以添加一个匹配的step,loadInt这个方法会返回一个可以匹配所有加载一个int常数的step,然后后面的setStrategy是处理策略,如果匹配到以后怎么处理,默认情况下会全部丢掉匹配中的部分,利用ctx.builder就可以把自己需要的部分加进去,比如,我现在要把匹配到的这个第一个加载int的这个指令加回去,那么就应该写为ctx.builder.addInsnNode(ctx.original.get(ctx.startIdx));。这个索引是左闭右开区间,匹配中的下标是[startIdx, endIdx)。
然后ctx.original是原始的那个InsnList,只需要拿就完事了。ctx.builder是一个InsnBuilder,需要添加新的指令,就跟写指令一样就可以了。
1 | public class Sample001Pass1 extends MethodPass { |
那么应用这个matcher的规则,apply会对传入的InsnList匹配,然后应用对应的修改策略,返回修改后的InsnList。
运行pass
那么写好一个pass以后,如何运行呢。只需要在main里面写下面这段代码即可。
需要一个PassContext作为上下文,这个里面需要一个JarCaches来管理class那些数据,库里面已经封装了基础的jar的读写器。SimplePassRunner使用的时候,需要addPass添加pass,然后transform会运行这些pass。
1 | public class Main { |
这个框架基本上不需要改变,只需要改变addPass传入的pass对象即可。
那么我们就可以拿到下面这样子的结果了,很容易看出来这个是可以被删除的。下一篇再写如何处理。
1 | if (false) { |
