app 公众号文章列表 so 加解密算法分析还原 | 加密 rsa base64 分析

android 逆向 13 / 38

仅供学习研究 。请勿用于非法用途,本人将不承担任何法律责任。

const 龙哥 = Function() {}

const a = 全能的傻宝,每天都会换一种姿势来分享各种操作,最近 10 月在分享密码学;
const b = 点击加入星球;
return "无敌的龙哥";

前言

想学习 unidbg android逆向 密码学 算法还原 的小伙伴可以 call 龙哥 ();
接着上一篇文章分析,本次来分析下 rsa base64 的加密逻辑

0x1

上一篇文章分析到了 so 的静态注册逻辑

也就是这里点进去

一共五个参数,前两个是默认的,分别是 JNIEnv jObject or jClass,后面三个就是我们传进来的
上面是做了一些字符串初始化然后调用了 j_Sc_EncryptWallEncode 这个函数,点进去看看

进来之后有三个函数需要我们关注,前两经过分析都不是很重要,主要分析最后一个

要感谢代码符号没去掉,才能看到里面密密麻麻的算法

在往下分析,就看到我们熟悉字符串,不正是前面请求包的请求参数吗,这里就是全部算法的加密逻辑了

0x2

里面的算法很多,这次就来分析下,rsa 跟 base64

rsa

前面是一些常规的判断赋值操作

v10 = operator new(48u);
这一行就是创建一个大小 48字节 的内存,v10 就只内存地址

EncryptWall::WallKey::WallKey(v10);
这里调用一个函数,把内存地址进去,点进去看看

EncryptWall::WallKey::WallKey

点进来逻辑也比较简单,两个 do while 一共循环 48 次,刚好把 48字节大小的 内存填空

linux 下的 lrand48() 函数
随机生成 0 - 2 ^ 31 之间的长整数

Tips: 继续往下

v12 = RSA_Encrypt(v10 + 16, 32u, &v60, &v59);
这里就是调用 rsa 加密函数,四个参数分别是 v10 的首地址加上 1632数字v60 的地址v59 的地址

RSA_Encrypt

n_crypto::SetSignPubKey
这里是生成 public_key,模数,指数都传进去了

n_crypto::PublicEnc(v8, v9, v6, &v11, v4);
调用这个函数开始加密,一共有五个参数
分别是 1:32字节的地址2:32数字3:128字节大小的内存4:内存地址5:80字节大小的内存地址

Tips: rsa 分析结束

base64Encode

这里就是对 rsa 的加密结果,再来一层 base64 编码,稍后可以验证下是否是标准的 base64

0x3

算法分析的差不多了,在使用 unidbg 的 console debugger 跟 hook 功能,验证下数据,不熟悉的可以看下上面的推荐文章

unidbg unicorn inline hook

EncryptWall::WallKey::WallKey 函数看看生成的啥数据

public void hookUnicornWallKey() {
    emulator.getBackend().hook_add_new(new CodeHook() {
        @Override
        public void onAttach(Unicorn.UnHook unHook) {
        }

        @Override
        public void detach() {
        }

        @Override
        public void hook(Backend backend, long address, int size, Object user) {
            if (address == (module.base + 0xB332)) {
                System.out.println("Hook By Unicorn hookUnicornWallKey");
                RegisterContext ctx = emulator.getContext();
                Pointer input1 = ctx.getPointerArg(0);
                Inspector.inspect(input1.getByteArray(0, 0x100), " 参数1");
            }
            if (address == (module.base + 0xB336)) {
                RegisterContext ctx = emulator.getContext();
                Inspector.inspect(ctx.getPointerArg(0).getByteArray(0, 0x100), " Unicorn hook hookUnicornWallKey");
            }
        }
    }, module.base + 0xB332, module.base + 0xB336, null);
}

运行结果可以看到,刚好是 48字节 的数据

接着分别 hook rsa base64 函数

public void hookUnicornRsa() {
    emulator.getBackend().hook_add_new(new CodeHook() {
        @Override
        public void onAttach(Unicorn.UnHook unHook) {
        }

        @Override
        public void detach() {
        }

        @Override
        public void hook(Backend backend, long address, int size, Object user) {
            if (address == (module.base + 0xB34E)) {
                RegisterContext ctx = emulator.getContext();
                Pointer input1 = ctx.getPointerArg(0);
                Inspector.inspect(input1.getByteArray(0, 0x100), " hookUnicornRsa 参数1");
            }
            if (address == (module.base + 0xB352)) {
                RegisterContext ctx = emulator.getContext();
                Inspector.inspect(ctx.getPointerArg(0).getByteArray(0, 0x100), " Unicorn hook hookUnicornRsa");
            }
        }
    }, module.base + 0xB34E, module.base + 0xB352, null);
}

public void hookUnicornBase64Encode() {
    emulator.getBackend().hook_add_new(new CodeHook() {
        @Override
        public void onAttach(Unicorn.UnHook unHook) {
        }

        @Override
        public void detach() {
        }

        @Override
        public void hook(Backend backend, long address, int size, Object user) {
            if (address == (module.base + 0xB372)) {
                RegisterContext ctx = emulator.getContext();
                Pointer input1 = ctx.getPointerArg(0);
                Inspector.inspect(input1.getByteArray(0, 0x100), " hookUnicornBase64Encode 参数1");
            }
            if (address == (module.base + 0xB376)) {
                RegisterContext ctx = emulator.getContext();
                Inspector.inspect(ctx.getPointerArg(0).getByteArray(0, 0x100), " Unicorn hook hookUnicornBase64Encode");
            }
        }
    }, module.base + 0xB372, module.base + 0xB376, null);
}

代码写完跑起来

rsa 的第一个参数,是 32 个字节,也就是前面随机生成 48 字节数据的后 32个字节数据,返回值是 128 个字节

base64 的第一个参数,刚好是前面 rsa 的返回值

使用 CyberChef 网站来验证,是否是标准的 base64 ,结果不出所料,就是标的

最后

rsa base64 就分析完了,下一篇文章在分析 zip + aes 算法

暂无评论
本文作者:
本文链接: https://www.qinless.com/?p=485
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 qinless 的博客!
100

发表评论

返回顶部