好友
阅读权限10
听众
最后登录1970-1-1
|
本帖最后由 吾苏同在 于 2026-7-3 14:23 编辑
声明:分析所用的脚本文件,都在帖子最后的链接里面
App :某MINO,某追忆等等同类型影视壳子
工具:Blutter,IDA Pro,Frida,HttpCanary
涉及加密方式:RSA-OAEP,AES-GCM,HMAC-SHA256
准备工作:
1. 用Blutter解包,flutter应用
2. 去除抓包检测:搜vpn,复制匿名闭包的地址(0x4ba614),然后去IDA Pro或者其它汇编工具
第一行修改为 MOV X0,#0
第二行修改为 RET
抓包分析:
这里看见api/sync/push,出现的次数比较多
oss-rg-china-mainland.aliyuncs.com:响应加密
/api/health:无加密,验证软件是否修改
api/captcha/rotate/init:验证码请求,有带头部,响应带一个64位的id加密
api/sync/preferences:带头部,请求数据("data": 密文)和响应加密("data": 密文)
api/sync/push:带头部,请求数据("bundle":密文)和响应加密("bundle":密文)
整理验证,主要逆向点:
api/sync/preferences:可以重发多次,响应数据会变(不带头部也可以重发)
api/sync/push:不可重发,响应数据出现401非法请求(必须带头部,验证有效token)
按照其它影视壳的手段,大概率是AES加密
分析流程:
so混淆,正常的手段无法用,所以只能用正则搜索偏移,写了一个find_string.py脚本,定位字符串位置在pp.txt搜索AES,结果有14个,通过筛选发现可疑的
把偏移值填入find_string.py
在IDA PRO运行文件得到一直地址
AES字符串位置
用blutter_frida.js(修改过的),hook这个函数0x477A00
操作手机,无继续输出日志,看来不是这个函数,继续查看0x477a00的引用,有三个函数,通过hook三个函数,其中0x98CE90线路加密数据,是oss-rg-china-mainland.aliyuncs.com响应解密的函数,不过无后续输出日志,再往上找结果:Hook了一圈无任何有用信息
换思路:通过查找api/sync/push字符串地址这里我手动补上字符串
通过确定hook最终sub_9B8A2C函数是正确位置
返回值有解密之后的数据,同时有输出libnative-crypto.so
调用libnative-crypto.so的函数进行解密
查看libnative-crypto.so
通过hook发现,关键函数有_h9m , _a7x , _d3k , _r2e , _g5b , _p6y
函数分析:
_h9m(HMAC-SHA256):参数:a1 :HMAC_key ,a2 :密文,a3 :数据长度
原理:aes密文拼接13位时间戳和_g5b返回的随机base64(16位),得到data,然后mbedtls_md_hmac计算HMAC(key, data),将 32 字节结果转换为 64 字符小写 hex(x-req-token的值)
_a7x(AES-256-GCM加密):参数:a1 :32 字节 aes密钥, a2 :明文,a3 :明文长度
原理:先Base64 解码 key_b64 得到 32 字节 AES key,再调用 mbedtls_ctr_drbg_random 生成 12 字节 IV,mbedtls_gcm_setkey(v21, MBEDTLS_CIPHER_ID_AES, key, 256)sub_39764 + sub_3992C 对结果做 Base64 编码后返回字符串
_d3k(AES-256-GCM解密):参数:a1 :32 字节 aes密钥,a2 :密文,a3 :数据长度原理:取密文hex前面12字节作为IV,取密文后16字节作为Tag
_r2e(RSA-OAEP 加密)参数:公钥, 明文,长度
原理:调用_p6y函数获取公钥,mbedtls_pk_parse_public_key 解析 PEM 公钥,mbedtls_rsa_set_padding(rsa, 1, 9) ,1 =v21, 9 =MBEDTLS_MD_SHA256,即 RSA-OAEP-SHA256
_p6y(公钥获取位置)原理:把字符串片段拼接成完整 PEM 公钥
拼接完整之后是
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAum4qpnurnzUExfnum4UY
3fmE2370KvssufETUZCfqf9o5953xAQ3sciU/FrOTQY29LrwbYgBizZUFVBNUgeJ
9hMkdywRUU3qnQlarz2D4FMDIJo3Z7x0G8CD4f47CpQ9c5LsiyWi4GTVEEMrkiDr
KHn2/AtJ4sHdC/LsYiuv6tCNrY7Qoy0zrKFk0OcNL+8uNLZyCvEp7I8MoQlxWGmR
siCXwQZUIdDjbPpZ92sNt+OH9THi2QcPlZxfoYPgNo5VfJAm4FUpGnr/R7k+2Sy7
JSYGCWcpd8fDCx5GIn/CmhblUQqlYD2+Au+S9SMpL+BTelGebss8/eK2nVHL7Uh5xwIDAQAB
-----END PUBLIC KEY-----
_g5b(随机数生成)参数:int len(字节长度)
原理:mbedtls_ctr_drbg_random 生成指定长度随机字节,返回 Base64 编码字符串(主要生成aes密钥(32位)和x-req-trace的值(16位))。
hook脚本:hook_all.js 抓包和hook结合分析,得到下面,加解密流程图:
后续的aes的密钥都是从来api/sync/preferences响应数据解密得来的key和hmac的key。
分析验证:rsa_encrypt.py,aes_decrypt.py,hmac_encrypt.py的脚本在帖子最后的链接里面。
注意:
1.每次请求的body都带Authorization":"user_id+JWT+iat",这个jwt签名值,好像是随机生成的,但是有时效(Authorization的值可以用base64直接解密,第一次请求api/sync/preferences的头部带这个给服务器记录,iat是签名值到期时间戳)。
2.请求头部的content-length,这个长度是通过“aes加密数据+x-req-ts(时间戳)+x-req-trace(_g5b(随机数)”的总字节减24字节,得到的值。
总结:对比一代版本,作者也是下功夫了。不过加解密方式放在另一个so文件,脱离dart层,给逆向难度降低少了很多,希望作者继续改进一下。
帖子所用的脚本文件和流程图下载链接:https://wwbkx.lanzoum.com/b02sgpn2xg 密码:bx3e
|
|
发帖前要善用【论坛搜索】功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。 |
|
|
|
|
|