app 公众号文章列表 so 加解密算法分析还原 | response 内容解密分析

android 逆向 15 / 37

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

const 龙哥 = Function() {}

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

前言

想学习 unidbg android逆向 密码学 算法还原 的小伙伴可以 call 龙哥 ();
前几篇文章分析了,参数加密的逻辑,这一篇就来分析下 response 的解密

0x1

解密逻辑就是这个 decrypt 函数,点进去

需要一个参数,就是我们传进来的加密字符串然后调用 j_Sc_EncryptWallDecode 函数完成解密

这里先有个 if 判断,看看 byte_3A0C0 这是啥

table 键查看汇编代码,这里点击去

这个变量是在加密逻辑返回的,所以后面在使用 unidbg 调用的时候还需要先调用加密逻辑才行

进到 EncryptWall::DecryptHttpRequest3 函数看一下,一大堆密密麻麻的解密函数,这函数一共有 6 个参数,先来分析一下

1、v4 = dword_3A0C4 = 也就是加密逻辑的返回值
1、v2 = 传进来的加密串
1、v6 = 加密字符串的长度
1、&v8 = 地址
1、0 = 0
1、v7 = 变量

0x2

这里分析下 else 的逻辑,经过分析得知这里走的是 else 分支

*v9 = (4 a3);

v9 = 4 * 字符串的长度

*v10 = operator new[](4 a3);

v10 = 内存地址,大小是 字符串长度的 3 倍

v14 = n_crypto::Decode_Base64(v10, v9, v11, v13);

这里是 base64 decode 逻辑

v8 = AES_Decrypt(v12, v14, v20, v7 + 16, 32u, v7, 16u);

这里是 aes 解密,参数有 7 个分析一下
1、v12 = 内存地址
2、v14 = base64 解密结果
3、v20 = v14
4、v7 + 16 = 参数1 + 16
5、32u = 32 字节
6、v7 = 参数一
7、16u = 16 字节

AES_Decrypt

分析下 AES_Decrypt 的逻辑,看看 key iv 分别是啥

有了前面分析加密逻辑的经验这里看起来大概就了解了

_aeabi_memcpy(&v16, a6, 16);

v16 = 内存地址,a6 = 参数6,16 = 16个字节,这里应该是从 a6 里取出前 16 个字节的数据存到 v16 里

*n_crypto::SetDecKeySym(&v15, v11, 8 a5);

这个应该是生成 key 的逻辑
v15 = 内存地址,v11 = 参数4, 8 a5 = 8 32 = 256,这里应该是 AES-256 解密

n_crypto::DecSym(&v16, v9, v8, v7, &v15);

这里开始解密
v16 = 内存地址,值是 16 个字节数据,v9 = 内存地址,v8 = 参数1,v7 = 参数2,v15 = 应该是 key,这里可以猜测 v16 = iv

AES Decrypt 的解密逻辑就分析到这里

zip_uncompress

最后在来看下 zip_uncompress 解压缩数据的逻辑

*zip_uncompress(v8 + 1, v20 - 4, v15, v18, 0);
主要看这行代码
v8 = aes 解密结果,v8 + 1 也就是地址 + 1,16位的1地址 = 2字节,32位的1地址 = 4字节,64位的1地址 = 8字节
所以这里是 aes 解密结果 去掉前 四个字节 在 解压缩

Tips: so 解密逻辑分析完了,下面使用 unidbg 验证一些数据

0x3

先使用 unidbg 把 解密函数跑起来

unidbg

public void decrypt() {
    List<Object> list = new ArrayList<>(10);
    list.add(vm.getJNIEnv());
    list.add(0);
    list.add(vm.addLocalObject(new StringObject(vm, "加密字符串")));
    Number number = module.callFunction(emulator, 0x9DA0 + 1, list.toArray())[0];

    System.out.println("decrypt: " + new String((byte[]) vm.getObject(number.intValue()).getValue()));
}

代码跑起来,发现报错了,具体因为啥,这里先不说我们继续往下分析

unidbg console debugger

使用 console debugger 下端点调试

现在分析 byte_3A0C0 看看加密逻辑返回的是个啥

0xA35E 地址下端点,查看 r6 的值,前 48个字节 的数据事不是有些熟悉,没错正是加密时随机生成的数据
48个 字节的 前 16个aes 加密的 iv,后 32个aes 加密的 key,所以猜测 aes 解密时用的也是这个 key iv,下面来验证一下

0x1158C 下端点,查看 r1 寄存器的数据,前 32个 字节刚好是,encrypt 返回的结果

v11 = v4 = 参数4 = 解密逻辑结果 + 16

0x115A6 下端点,查看 r0 寄存器的数据,前 16个 字节的数据也正好是 encrypt 返回的结果

现在就真想大白了,参数加密用的 key iv,跟响应解密用的是同一套,如果不对的话,解密结果就不对
经过测试每次执行程序 加密逻辑的结果都是一样,这里随机生成的 key iv 应该也是固定的,就拿着这个加密结果去请求一次,获取到响应结果再来调用解密函数

加解密的 key iv 对应起来之后,decrypt 函数也就正常执行了

python

再来看看 使用 python 解密

这里在 zlib.decompress 报错了

加个参数 -15 就可以了: 参考文章

zlib 库可以解以下的格式
deflate 需要加上 wbits = -zlib.MAX_WBITS
zlib 需要加上 wbits = zlib.MAX_WBITS
gzip 需要加上 wbits = zlib.MAX_WBITS | 16

最后

整个某狗搜索 app 的加解密文章就算写完了

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

1 条评论

Asepawex

As the expert, I can assist. Together we can come to a right answer.
https://how6youtoknowc.org/map.php

回复

发表评论

返回顶部