各位聪明绝顶,才高八斗的读者们你们好!今天我们主要讨论编译之后的RC4算法识别。
题外话,之前看到一个蛋疼的小知识,说“势”这个字最好不好查词典释义。我是很好奇的,果然后来无法直视势不可挡这个成语。
言归正传,我们将上一篇的Java与C的代码都编译一下,分别反编译看看。
Java版

Java版的就很好识别,几乎与源码没啥区别。
C版
这里我们先给出RC4加密算法在逆向分析过程中的快速识别经验:
- 首先判断明文和密文长度是否相等,等长则代表是序列密码。
- 接下来判断是否是RC4。RC4算法中的初始化算法(KSA)中有两轮非常显著的长度为256的循环体,用于根据给定的key生成s盒。伪随机子密码生成算法(PRGA)会根据上一步得到的扰乱的s盒,进一步生成子密钥流,最终和给定的明文进行逐字节的异或。
由于 so 是可以加 ollvm 的,而且还可以做字符混淆,所以逆向起来不会像Java那么容易,在我们找到一个怀疑对象函数之后,就可以通过编写frida脚本或xposed插件完成对相应函数的主动调用,判断当输入明文为任意长度时的密文长度。
关于 ndk 的混淆是有很多文章的,自己可以尝试一下控制流混淆与字符串混淆。
我们看一个混淆之后的例子:
unk_38008实际上是一个字符串,不过这个字符串被混淆了,在 init 阶段后才会被解密。看里面的逻辑:
有2个函数比较可疑,先看
sub_F0AC:
先看1处,有一个很明显的交换数组元素的逻辑。
再看2处,只有当 v7 = 915845509 的时候才会交换逻辑,循环次数是 256 次,这些都与 RC4 的 init 阶段是一样。
当然这里也只是大胆猜测,而且混淆级别开的没那么高,所以还能看清楚一定的逻辑。实际上这里就已经很难看出来第一个给 S 盒赋值的循环在哪里了,只能根据 result 的位置来看。
后面我们可以使用hook来验证我们的想法。
再看
sub_F3C4 函数:
看 1,2处,发现循环会执行的次数与第3个参数一样,看前面的逻辑:

这就是说,循环次数是第二个字符串的长度。配合第3处的异或,也可以合理怀疑这里就是加密函数。
Hook验证
通过上面的分析,第一个参数是返回值,第二个参数是密钥,第三个参数是密钥长度。所以返回的是一个密钥流。
第一个参数是密钥流,第二个参数是明文,第三个参数是明文的长度。返回值(第一个参数)是加密后的结果。
所以,这个函数就是一个 RC4 算法,我们 hook 这些函数,观察参数与结果。
注意,我们hook so 的加载选择的是 Runtime 类,这是因为直接hook System 类会导致
Reflection.getCallerClass() 的值出问题。还有就是我的手机系统换到了 Android 10,注意自行查看源码区别。hook 逻辑也很简单,就不细说了,看一下就明白。
注入脚本,看一下输出:
可以看到,secret key 是
rc4test。加密的明文是
12345678。加密后的结果是:
从 a6 到 47 这8个字节。
我们也可以主动调用函数,反复确认输入与输出是否一样长。










