常见加解密算法03 - RC4逆向认识

各位聪明绝顶,才高八斗的读者们你们好!今天我们主要讨论编译之后的RC4算法识别。

题外话,之前看到一个蛋疼的小知识,说“势”这个字最好不好查词典释义。我是很好奇的,果然后来无法直视势不可挡这个成语。

言归正传,我们将上一篇的Java与C的代码都编译一下,分别反编译看看。

Java版

Java版的就很好识别,几乎与源码没啥区别。

C版

这里我们先给出RC4加密算法在逆向分析过程中的快速识别经验:

  1. 首先判断明文和密文长度是否相等,等长则代表是序列密码。

  2. 接下来判断是否是RC4。RC4算法中的初始化算法(KSA)中有两轮非常显著的长度为256的循环体,用于根据给定的key生成s盒。伪随机子密码生成算法(PRGA)会根据上一步得到的扰乱的s盒,进一步生成子密钥流,最终和给定的明文进行逐字节的异或。

由于 so 是可以加 ollvm 的,而且还可以做字符混淆,所以逆向起来不会像Java那么容易,在我们找到一个怀疑对象函数之后,就可以通过编写frida脚本或xposed插件完成对相应函数的主动调用,判断当输入明文为任意长度时的密文长度。

关于 ndk 的混淆是有很多文章的,自己可以尝试一下控制流混淆与字符串混淆。

我们看一个混淆之后的例子:

  sub_F658(&unk_38008, dest);

unk_38008实际上是一个字符串,不过这个字符串被混淆了,在 init 阶段后才会被解密。

看里面的逻辑:

  sub_F0AC((__int64)v7, (__int64)a1, v4);
  v2 = strlen(a2);
  return sub_F3C4(v7, a2, v2);

有2个函数比较可疑,先看 sub_F0AC

先看1处,有一个很明显的交换数组元素的逻辑。

再看2处,只有当 v7 = 915845509 的时候才会交换逻辑,循环次数是 256 次,这些都与  RC4 的 init 阶段是一样。

当然这里也只是大胆猜测,而且混淆级别开的没那么高,所以还能看清楚一定的逻辑。实际上这里就已经很难看出来第一个给 S 盒赋值的循环在哪里了,只能根据 result 的位置来看。

后面我们可以使用hook来验证我们的想法。

再看 sub_F3C4 函数:

看 1,2处,发现循环会执行的次数与第3个参数一样,看前面的逻辑:

这就是说,循环次数是第二个字符串的长度。配合第3处的异或,也可以合理怀疑这里就是加密函数。

Hook验证

 sub_F0AC((__int64)v7, (__int64)a1, v4);

通过上面的分析,第一个参数是返回值,第二个参数是密钥,第三个参数是密钥长度。所以返回的是一个密钥流。

sub_F3C4(v7, a2, v2);

第一个参数是密钥流,第二个参数是明文,第三个参数是明文的长度。返回值(第一个参数)是加密后的结果。

sub_F658()

所以,这个函数就是一个 RC4 算法,我们 hook 这些函数,观察参数与结果。

export function invoke_sub_F658(arg1: string, arg2: string) {
    var offset = 0xF658;
    var module = Process.getModuleByName("libnative-lib.so");
    var sub_address = module.base.add(offset);

    var arg1_address = Memory.alloc(10);
    let arg2_address = Memory.alloc(10);
    arg1_address.writeUtf8String(arg1);
    arg2_address.writeUtf8String(arg2);

    var hooked_sub_f658 = new NativeFunction(sub_address, 'pointer', ['pointer', 'pointer']);
    var result = hooked_sub_f658(arg1_address, arg2_address);
    console.log("result:", hexdump(result));
}

function hook_libnativelib() {
    var nativelibmodule = Process.getModuleByName("libnative-lib.so");
    var subf0ac = nativelibmodule.base.add(0xf0ac);
    var subf3c4 = nativelibmodule.base.add(0xf3c4);
    Interceptor.attach(subf0ac, {
        onEnter: function (args) {
            console.log("secret key: ", hexdump(args[1]), '\n length:', args[2]);
        }, onLeave: function () {
        }
    });
    Interceptor.attach(subf3c4, {
        onEnter: function (args) {
            this.arg1 = args[1];
            console.log("input: ", hexdump(args[1]), args[2]);
        }, onLeave: function () {
            console.log("output: ", hexdump(this.arg1));
        }
    });

}

function main() {
    if (Java.available) {
        Java.perform(function () {
            console.log("go into main");
            var RuntimeClass = Java.use('java.lang.Runtime');
            RuntimeClass.loadLibrary0.implementation = function (arg0: object, arg1: string) {
                var result = this.loadLibrary0(arg0, arg1);
                console.log("Runtime.loadLibrary0:", arg1);
                if (arg1.indexOf('native-lib') != -1) {
                    hook_libnativelib();
                }

                return result;
            }
        })
    }
}

rpc.exports = {
    invokesubf658: invoke_sub_F658
};

setImmediate(main);

注意,我们hook so 的加载选择的是 Runtime 类,这是因为直接hook System 类会导致 Reflection.getCallerClass() 的值出问题。还有就是我的手机系统换到了 Android 10,注意自行查看源码区别。

hook 逻辑也很简单,就不细说了,看一下就明白。

注入脚本,看一下输出:

┌──(root㉿kali)-[~/workspace/frida-agent-example]
└─# frida -U -f com.kanxue.rc4 --no-pause -l _agent.js 
secret key:
              0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
724f339008  72 63 34 74 65 73 74 00 31 32 33 34 35 36 37 38  rc4test.12345678

可以看到,secret key 是 rc4test

input:
              0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
7fe568aa10  31 32 33 34 35 36 37 38 00 00 00 00 00 00 00 00  12345678........

加密的明文是 12345678

加密后的结果是:

output:
              0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
7fe568aa10  a6 df 7a 5d dd ea 97 47 00 00 00 00 00 00 00 00  ..z]...G........

从 a6 到 47 这8个字节。

我们也可以主动调用函数,反复确认输入与输出是否一样长。

  • 13
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值