吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 94|回复: 4
上一主题 下一主题
收起左侧

[Android 原创] 初探 Flutter 的猫腻

[复制链接]
跳转到指定楼层
楼主
utf8 发表于 2026-3-16 22:46 回帖奖励
本帖最后由 utf8 于 2026-3-16 22:49 编辑

初探 Flutter 的猫腻

本文仅用于逆向工程技术学习与安全研究交流,所有分析均基于个人学习记录。逆向的目的不是破坏,而是为了学习原理、理解机制、提升防护能力。

特别鸣谢:感谢 正己 大佬的文章

大佬文章在这里: https://www.52pojie.cn/thread-1951619-1-1.html

推荐认真阅读!!! 再次鸣谢大佬的文章

材料准备

  1. 某漫画APP v3.0.3
  2. frida == 16.6.6 使用 florida服务
  3. Blutter(我会直接提供学习素材,这里你下不下都无所谓了)
  4. 几款大伙都很熟悉的分析工具啦

抓取目标接口 - 任意一部漫画的具体章节列表

最开始通过使用wifi代理,但是我们可以发现是完全抓不到任何包的,这个时候我就很奇怪,难道和某宝一样做了什么特色的手段处理吗?

抓包如下图:

酷安社区抓取是没有任何问题的,但是抓取这个漫画软件的包就是只能抓到广告的sdk

这时候就切换使用 postern 进行 vpn 全局流量转发,欸,柳暗花明又一村。

这里大概讲一下二者区别:

  • Wi-Fi 代理抓包流程(HTTP Proxy)

                ┌─────────────────┐
                │      App        │
                │ (HTTP Client)   │
                └────────┬────────┘
                         │
                         │ HTTP / HTTPS
                         │
                ┌────────▼────────┐
                │  System Proxy   │
                │ (Wi-Fi Proxy)   │
                │ 例如: Charles   │
                └────────┬────────┘
                         │
                         │ 转发请求
                         │
                ┌────────▼────────┐
                │     Internet    │
                │   服务器/接口    │
                └─────────────────┘
    只有 走系统代理的请求 才会经过这里
    如果 App 直接建 socket 那么就会完全抓不到包
  • VPN 抓包流程(TUN 虚拟网卡)

                ┌─────────────────┐
                │       App       │
                │  TCP / UDP / DNS│
                └────────┬────────┘
                         │
                         │ 所有网络请求
                         │
                ┌────────▼────────┐
                │   OS 网络栈      │
                └────────┬────────┘
                         │
                         │ 路由
                         │
                ┌────────▼────────┐
                │   TUN 虚拟网卡   │
                │   (VPN Tunnel)  │
                └────────┬────────┘
                         │
                         │ VPN Client 拦截
                         │
                ┌────────▼────────┐
                │  抓包 / 解析     │
                │  修改 / 转发     │
                └────────┬────────┘
                         │
                         │
                ┌────────▼────────┐
                │     Internet    │
                └─────────────────┘
    VPN 直接将流量强制转发
  • 对比如下:

                ┌──────────┐
                │   App    │
                └─────┬────┘
                      │
        ┌─────────────┴─────────────┐
        │                           │
    WiFi Proxy                  VPN Proxy
    (应用层)                    (网络层)
        │                           │
        │HTTP/HTTPS                 │所有IP流量
        ▼                           ▼
    ┌──────────┐                ┌──────────┐
    │  Proxy   │                │   TUN    │
    │ Charles  │                │  虚拟网卡 │
    └─────┬────┘                └─────┬────┘
         │                           │
         ▼                           ▼
      Internet                    Internet

我们惊喜的发现,是可以抓到包的,但是证书校验不通过。这里上网搜了一下,发现此 大佬 所言一致。
具体证书绕过推荐去看一下文章

那么我们简单对比一下两次请求的差距。

实际上每次变化的参数只有两个:X-Auth-TimestampX-Auth-Signature

我们既然已经知道了此app使用了Flutter技术(当使用 Flutter 构建Android APP时,在assets文件夹下会有dexoptflutter_assets两个文件夹。lib文件夹会有两个so文件:libapp.solibflutter.so(flutter动态链接库,与实际业务代码无关)。)

那么我们先采用 blutter反编译尝试解决。

Blutter 项目结构

关键几个文件:
blutter_output/  
│  
├─ asm/  Dart结构恢复
│ ├─ class_xxx.asm  
│ ├─ network.asm  
│ ├─ http_client.asm  
│ └─ ...  名字不固定
│  
├─ pp.txt   -->  Object Pool(对象池)
├─ objs.txt  -->  objs.txt —— 完整对象结构
│  
├─ blutter_frida.js   动态Hook脚本

一般流程:
1 pp.txt 找关键字符串  
2 asm 找使用这些字符串的函数  
3 dump.dart 看类结构  
4 在 IDA/Ghidra 定位函数  
5 Frida hook 验证

所需文件均在材料中

逆向分析开始(按理来说直接使用那个py文件替换,但是我使用的ida 9 貌似不太支持部分函数,所以我干脆直接分析代码了)

1 pp.txt 找关键字符串 
[pp+0x8d68] "X-Auth-Signature"

先简单确认pp 是哪个寄存器(一般是x27)
集中观看几个指令:
1. add x1, x27, #0x8d68
2. ldr x1, [x27, #0x8d68]

在arm文件下查找:
findstr /m /c "0x8d68" *.dart  --> 查出结果为空,说明 它指令很可能做了拆分
findstr /n /c "[pp+" *.dart --> 统一输出观看内容

第一条:abi.dart:62:    //     0xa36944: ldr             x4, [PP, #0x3e0]  ; [pp+0x3e0] List(5) [0, 0x3, 0x3, 0x3, Null]  --> 去ida查看 0xa36944

.text:0000000000A36944                 LDR             X4, [X27,#0x3E0]
那么 pp 有可能是 x27 ,对查看几条也是准确的
但是 直接搜索 0x8d68 是搜不到还是,将注意力放到 x27 上,因为Flutter AOT 代码中有一个重要结构ObjectPool就是 x27。继续观看libapp.so文件的代码,关注 x27 的位置。发现:

.text:0000000000A36B50                 ADD             X4, X27, #0x19,LSL#12
.text:0000000000A36B54                 LDR             X4, [X4,#0x7D8]

这就很有意思了, X4 = X27 + (0x19 << 12) + 0x7D8 = x27 + 0x197D8; 刚刚好对应 pp.txt里面的 [pp+0x197d8] List(7) [0, 0x4, 0x4, 0x3, "ase", 0x3, Null]
验证一下:
PS D:\work\study\hooks\output\output\asm> findstr /n /c /i "0xA36B54" *.dart
FINDSTR: 忽略 /c
abi.dart:244:    //     0xa36b54: ldr             x4, [x4, #0x7d8]

继续向下看去发现很多都是这种。
那么 [pp+0x8d68] "X-Auth-Signature" 就可以拆分为:

ADD Xn, X27, #0x8, LSL #12  
LDR Xm, [Xn, #0xD68] 搜索 #0xD68 发现一堆。那么再找一个锚点锚定它

[pp+0x8d60] String: "X-Auth-Timestamp"
ADD Xn, X27, #0x8, LSL #12  
LDR Xm, [Xn, #0xD60]

锚定点

X-Auth-Signature

Address        Function        Instruction
.text:00000000002AFC88        sub_2AFC28                        LDR             X16, [X27,#0xD68]
.text:00000000002DA4D0        sub_2DA30C                        LDR             X17, [X17,#0xD68]
.text:00000000002FB7CC        sub_2F9BEC                        LDR             X0, [X0,#0xD68]
.text:0000000000306A78        sub_3068F8                        LDR             X3, [X3,#0xD68]
.text:0000000000311E4C        sub_311D0C                        LDR             X17, [X17,#0xD68]
.text:0000000000335C38        sub_335B80                        LDR             X1, [X1,#0xD68]
.text:000000000033946C        sub_3390B4                        LDR             X16, [X16,#0xD68]
.text:0000000000339A5C        sub_339908                        LDR             X1, [X1,#0xD68]
.text:00000000003441C0        sub_344170                        LDR             X1, [X1,#0xD68]
.text:000000000034E524        sub_34E520                        LDR             X0, [X0,#0xD68]
.text:0000000000353AE0        sub_353ADC                        LDR             X0, [X0,#0xD68]
.text:000000000035B878        sub_35B874                        LDR             X0, [X0,#0xD68]
.text:0000000000387288        sub_387250                        LDR             X16, [X16,#0xD68]
.text:000000000038B68C        sub_38AFF0                        LDR             X16, [X16,#0xD68]
.text:000000000038BF30        sub_38BE98                        LDR             X2, [X2,#0xD68]
.text:000000000038D99C        sub_38D8E8                        LDR             X2, [X2,#0xD68]
.text:00000000003D1AAC        sub_3D1A8C                        LDR             X30, [X30,#0xD68]
.text:00000000003E9084        sub_3E8E1C                        LDR             X3, [X3,#0xD68]
.text:000000000044A4CC        sub_44A400                        LDR             X3, [X3,#0xD68]
.text:000000000046C658        sub_46C5F0                        LDR             X1, [X1,#0xD68]
.text:0000000000486C3C        sub_486AF4                        LDR             X8, [X8,#0xD68]
.text:0000000000486CB0        sub_486AF4                        LDR             X8, [X8,#0xD68]
.text:00000000004CAD94        sub_4CAB94                        LDR             X16, [X16,#0xD68]
.text:00000000005053A4        sub_5052B8                        LDR             D0, [X17,#0xD68]
.text:000000000054D5D0        sub_54D560                        LDR             X1, [X1,#0xD68]
.text:000000000055BBBC        sub_55BACC                        LDR             X1, [X1,#0xD68]
.text:000000000056FB60        sub_56F3FC                        LDR             X4, [X4,#0xD68]
.text:0000000000582418        sub_5823F0                        LDR             X16, [X16,#0xD68]
.text:000000000058A98C        sub_58A8B8                        LDR             X0, [X0,#0xD68]
.text:000000000059452C        sub_594458                        LDR             X0, [X0,#0xD68]
.text:00000000005A3C98        sub_5A3C70                        LDR             X16, [X16,#0xD68]
.text:00000000005C3548        sub_5C3250                        LDR             X3, [X3,#0xD68]
.text:00000000005E53A0        sub_5E531C                        LDR             X3, [X3,#0xD68]
.text:00000000005E935C        sub_5E8A08                        LDR             X30, [X30,#0xD68]
.text:00000000005EBC10        sub_5E8A08                        LDR             X30, [X30,#0xD68]
.text:00000000005F30E8        sub_5F2F40                        LDR             X16, [X16,#0xD68]
.text:00000000005F3E60        sub_5F3CC0                        LDR             X16, [X16,#0xD68]
.text:0000000000607B90        sub_607B78                        LDR             X0, [X0,#0xD68]
.text:0000000000613BEC        sub_613BB8                        LDR             X17, [X17,#0xD68]
.text:0000000000622A08        sub_62295C                        LDR             X30, [X30,#0xD68]
.text:000000000064FE64        sub_64FA18                        LDR             X1, [X1,#0xD68]
.text:000000000065B9D8        sub_65B8AC                        LDR             X16, [X16,#0xD68]
.text:000000000065E9A0        sub_65E7D4                        LDR             X0, [X0,#0xD68]
.text:000000000066327C        sub_6611B4                        LDR             X16, [X16,#0xD68]
.text:00000000007A7E90        sub_7A7DA0                        LDR             X16, [X16,#0xD68]
.text:00000000008E738C        sub_8E6FE0                        LDR             X0, [X0,#0xD68]
.text:00000000008F7DDC        sub_8F3ED0                        LDR             X0, [X0,#0xD68]
.text:00000000008F9558        sub_8F3ED0                        LDR             X0, [X0,#0xD68]
.text:00000000008FB174        sub_8F3ED0                        LDR             X0, [X0,#0xD68]
.text:0000000000903190        sub_8FF284                        LDR             X0, [X0,#0xD68]
.text:000000000090490C        sub_8FF284                        LDR             X0, [X0,#0xD68]
.text:0000000000906528        sub_8FF284                        LDR             X0, [X0,#0xD68]
.text:00000000009147C4        sub_9146B4                        LDR             X16, [X16,#0xD68]
.text:00000000009435D4        sub_942EE4                        LDR             X1, [X1,#0xD68]
.text:0000000000991FDC        sub_991F98                        LDR             X1, [X1,#0xD68]
.text:00000000009B8990        sub_9B8974                        LDR             X16, [X16,#0xD68]
.text:00000000009B8C80        sub_9B8974                        LDR             X16, [X16,#0xD68]
.text:00000000009B99B0        sub_9B976C                        LDR             X16, [X16,#0xD68]
.text:00000000009B9CDC        sub_9B976C                        LDR             X16, [X16,#0xD68]
.text:00000000009BC7C8        sub_9BC770                        LDR             X2, [X2,#0xD68]
.text:0000000000A4F638        sub_A4F50C                        LDR             X1, [X1,#0xD68]
.text:0000000000A7779C        sub_A7776C                        LDR             X0, [X0,#0xD68]
.text:0000000000A77810        sub_A777D4                        LDR             X0, [X0,#0xD68]
.text:0000000000A7793C        sub_A7790C                        LDR             X0, [X0,#0xD68]
.text:0000000000A7E59C        sub_A7E348                        LDR             X1, [X1,#0xD68]
.text:0000000000A833E8        sub_A833A0                        LDR             X0, [X0,#0xD68]
.text:0000000000A8352C        sub_A834E4                        LDR             X0, [X0,#0xD68]
.text:0000000000A83780        sub_A83738                        LDR             X0, [X0,#0xD68]
.text:0000000000A83CA0        sub_A83C4C                        LDR             X0, [X0,#0xD68]
.text:0000000000A83E60        sub_A83C4C                        LDR             X16, [X16,#0xD68]
.text:0000000000A83F4C        sub_A83F04                        LDR             X0, [X0,#0xD68]
.text:0000000000A89FF0        sub_A89FC4                        LDR             X5, [X5,#0xD68]
.text:0000000000A8C808        sub_A8C7D8                        LDR             X30, [X30,#0xD68]
.text:0000000000A8F314        sub_A8EEEC                        LDR             X30, [X30,#0xD68]
.text:0000000000AA0738        sub_AA06C4                        LDR             X3, [X3,#0xD68]

X-Auth-Timestamp

Address        Function        Instruction
.text:00000000002AFC6C        sub_2AFC28                        LDR             X1, [X27,#0xD60]
.text:00000000002C5C90        sub_2C5B60                        LDR             X17, [X17,#0xD60]
.text:00000000002C64F4        sub_2C5E04                        LDR             X3, [X3,#0xD60]
.text:00000000002DA474        sub_2DA30C                        LDR             X17, [X17,#0xD60]
.text:00000000002FB2F0        sub_2F9BEC                        LDR             X4, [X4,#0xD60]
.text:00000000002FB314        sub_2F9BEC                        LDR             X4, [X4,#0xD60]
.text:00000000002FB330        sub_2F9BEC                        LDR             X4, [X4,#0xD60]
.text:00000000002FB364        sub_2F9BEC                        LDR             X4, [X4,#0xD60]
.text:00000000002FB384        sub_2F9BEC                        LDR             X4, [X4,#0xD60]
.text:00000000002FB3C4        sub_2F9BEC                        LDR             X4, [X4,#0xD60]
.text:00000000002FB40C        sub_2F9BEC                        LDR             X4, [X4,#0xD60]
.text:00000000002FB458        sub_2F9BEC                        LDR             X4, [X4,#0xD60]
.text:00000000002FBD7C        sub_2F9BEC                        LDR             X4, [X4,#0xD60]
.text:0000000000306A30        sub_3068F8                        LDR             X16, [X16,#0xD60]
.text:0000000000311DE4        sub_311D0C                        LDR             X17, [X17,#0xD60]
.text:00000000003440E4        sub_343FE8                        LDR             X1, [X1,#0xD60]
.text:000000000035035C        sub_350358                        LDR             X0, [X0,#0xD60]
.text:0000000000368D5C        sub_368D58                        LDR             X0, [X0,#0xD60]
.text:00000000003690BC        sub_3690B8                        LDR             X0, [X0,#0xD60]
.text:000000000038002C        sub_37FDEC                        LDR             X3, [X3,#0xD60]
.text:000000000038BED4        sub_38BE98                        LDR             X16, [X16,#0xD60]
.text:00000000003D1924        sub_3D1904                        LDR             X30, [X30,#0xD60]
.text:00000000003FB488        sub_3FB340                        LDR             X1, [X1,#0xD60]
.text:00000000004090B4        sub_40901C                        LDR             X3, [X3,#0xD60]
.text:0000000000424B14        sub_42487C                        LDR             X3, [X3,#0xD60]
.text:00000000004421A4        sub_4420A8                        LDR             X3, [X3,#0xD60]
.text:000000000046BBCC        sub_46B97C                        LDR             X1, [X1,#0xD60]
.text:0000000000486B40        sub_486AF4                        LDR             X16, [X16,#0xD60]
.text:00000000004CACD8        sub_4CAB94                        LDR             X16, [X16,#0xD60]
.text:0000000000505194        sub_505180                        LDR             X1, [X1,#0xD60]
.text:000000000054D0BC        sub_54CDCC                        LDR             X1, [X1,#0xD60]
.text:000000000055BB90        sub_55BACC                        LDR             X1, [X1,#0xD60]
.text:0000000000582AE0        sub_582A50                        LDR             X30, [X30,#0xD60]
.text:000000000058A814        sub_58A780                        LDR             X4, [X4,#0xD60]
.text:00000000005A3C4C        sub_5A3C1C                        LDR             X30, [X30,#0xD60]
.text:00000000005C3474        sub_5C3250                        LDR             X1, [X1,#0xD60]
.text:00000000005D7E50        sub_5D7A64                        LDR             X2, [X2,#0xD60]
.text:00000000005E9348        sub_5E8A08                        LDR             X30, [X30,#0xD60]
.text:00000000005EBBFC        sub_5E8A08                        LDR             X30, [X30,#0xD60]
.text:00000000005F3090        sub_5F2F40                        LDR             X16, [X16,#0xD60]
.text:00000000005F3E08        sub_5F3CC0                        LDR             X16, [X16,#0xD60]
.text:00000000005FF1D8        sub_5FF060                        LDR             X3, [X3,#0xD60]
.text:0000000000603594        sub_603590                        LDR             X0, [X0,#0xD60]
.text:0000000000622990        sub_62295C                        LDR             X30, [X30,#0xD60]
.text:0000000000626480        sub_62642C                        LDR             X17, [X17,#0xD60]
.text:000000000064FCE0        sub_64FA18                        LDR             X17, [X17,#0xD60]
.text:0000000000652F9C        sub_652E18                        LDR             X4, [X4,#0xD60]
.text:000000000065B9CC        sub_65B8AC                        LDR             X30, [X30,#0xD60]
.text:000000000065E8B0        sub_65E7D4                        LDR             X4, [X4,#0xD60]
.text:000000000065EF24        sub_65E7D4                        LDR             X4, [X4,#0xD60]
.text:0000000000663258        sub_6611B4                        LDR             X16, [X16,#0xD60]
.text:000000000066326C        sub_6611B4                        LDR             X30, [X30,#0xD60]
.text:0000000000773494        sub_773334                        LDR             X1, [X1,#0xD60]
.text:00000000007A7DEC        sub_7A7DA0                        LDR             X1, [X1,#0xD60]
.text:00000000007B0410        sub_7B00F0                        LDR             X2, [X2,#0xD60]
.text:00000000007D23B4        sub_7D2368                        LDR             X3, [X3,#0xD60]
.text:00000000008E7360        sub_8E6FE0                        LDR             X0, [X0,#0xD60]
.text:00000000008EE5A8        sub_8E8C28                        LDR             X17, [X17,#0xD60]
.text:00000000008EEF58        sub_8E8C28                        LDR             X17, [X17,#0xD60]
.text:00000000008F9850        sub_8F3ED0                        LDR             X17, [X17,#0xD60]
.text:00000000008FA200        sub_8F3ED0                        LDR             X17, [X17,#0xD60]
.text:00000000008FB068        sub_8F3ED0                        LDR             X17, [X17,#0xD60]
.text:0000000000904C04        sub_8FF284                        LDR             X17, [X17,#0xD60]
.text:00000000009055B4        sub_8FF284                        LDR             X17, [X17,#0xD60]
.text:0000000000915BCC        sub_915B88                        LDR             X3, [X3,#0xD60]
.text:000000000092A710        sub_92A434                        LDR             X16, [X16,#0xD60]
.text:0000000000969CFC        sub_969CD8                        LDR             X16, [X16,#0xD60]
.text:0000000000991F18        sub_991D64                        LDR             X16, [X16,#0xD60]
.text:0000000000992FA8        sub_992F14                        LDR             X2, [X2,#0xD60]
.text:0000000000993AAC        sub_993A24                        LDR             X2, [X2,#0xD60]
.text:0000000000993C34        sub_993A24                        LDR             X2, [X2,#0xD60]
.text:0000000000994298        sub_99420C                        LDR             X2, [X2,#0xD60]
.text:0000000000994508        sub_994424                        LDR             X2, [X2,#0xD60]
.text:0000000000994694        sub_994424                        LDR             X2, [X2,#0xD60]
.text:0000000000994868        sub_994424                        LDR             X2, [X2,#0xD60]
.text:0000000000994A10        sub_994424                        LDR             X1, [X1,#0xD60]
.text:0000000000994A40        sub_994424                        LDR             X16, [X16,#0xD60]
.text:00000000009CA748        sub_9CA688                        LDR             X1, [X1,#0xD60]
.text:00000000009DB078        sub_9DA904                        LDR             X3, [X3,#0xD60]
.text:00000000009F1880        sub_9F15F0                        LDR             X17, [X17,#0xD60]
.text:0000000000A3DE10        sub_A3DC24                        LDR             X3, [X3,#0xD60]
.text:0000000000A5054C        sub_A50024                        LDR             X3, [X3,#0xD60]
.text:0000000000A7E3C8        sub_A7E348                        LDR             X2, [X2,#0xD60]
.text:0000000000A90C94        sub_A90C28                        LDR             X2, [X2,#0xD60]
.text:0000000000A91E40        sub_A91CA8                        LDR             X2, [X2,#0xD60]
.text:0000000000A958F8        sub_A957E0                        LDR             X1, [X1,#0xD60]
.text:0000000000A98378        sub_A98304                        LDR             X3, [X3,#0xD60]
.text:0000000000A9E3FC        sub_A9E3A8                        LDR             X3, [X3,#0xD60]

按照我们上面分析的格式,双重定位到并查看格式,我们最后定位到sub_311D0C,这个函数同时抽取了这两个字符串。
-->  0x311de4 访问 pp+0x8d60
-->  0x311e4c 访问 pp+0x8d68

函数如下:

__int64 sub_311D0C()
{
  __int64 v0; // x15
  __int64 v1; // x21
  __int64 v2; // x26
  _QWORD *v3; // x27
  unsigned __int64 v4; // x28
  __int64 v5; // x29
  __int64 v6; // x30
  __int64 v7; // x29
  __int64 v8; // x0
  __int64 *v9; // x15
  __int64 v10; // x1
  __int64 v11; // x3
  __int64 v12; // x0
  __int64 v13; // x3
  __int64 v14; // x16
  _QWORD *v15; // x15
  __int64 v16; // x0
  _QWORD *v17; // x15
  __int64 v18; // x0
  __int64 v19; // x16
  _QWORD *v20; // x15
  __int64 v21; // x0
  __int64 v22; // x16
  __int64 *v23; // x15
  __int64 v24; // x0
  __int64 v25; // x16
  _QWORD *v26; // x15
  __int64 v27; // x1
  _QWORD *v28; // x15
  __int64 v29; // x0
  __int64 v30; // x2
  __int64 v31; // x0
  _QWORD *v32; // x15
  __int64 v33; // x1
  __int64 v34; // x2
  __int64 v35; // x0
  __int64 v36; // x16

  *(v0 - 16) = v5;
  *(v0 - 8) = v6;
  v7 = v0 - 16;
  if ( (v0 - 64) <= *(v2 + 56) )
    sub_A9B1EC();
  v8 = sub_A11BA4();
  v10 = v8 >> 1;
  if ( (v8 & 1) != 0 )
    v10 = *(v8 + 7);
  v11 = v10 / 1000 / 1000; 
  v12 = 2 * v11;
  if ( v11 != v12 >> 1 )
  {
    v12 = sub_A9B36C();
    *(v12 + 7) = v13;
  }
  *(v7 - 8) = v12;
  *v9 = v12;
  *(v7 - 16) = sub_254ED8();
  v14 = v3[518];
  *v15 = *(v7 + 16);
  v15[1] = v14;
  *(v7 - 24) = (sub_8D45D8)();
  v16 = sub_A23FDC();
  *(v7 - 32) = v16;
  *v17 = *(v7 - 24);
  v17[1] = v16;
  v18 = sub_311EA0();
  v19 = v3[518];
  *v20 = *(v7 - 16);
  v20[1] = v19;
  v21 = sub_8D45D8(v18);
  v22 = *(v7 - 32);
  *v23 = v21;
  v23[1] = v22;
  v24 = sub_8D28F4();
  v25 = v3[4313];
  *v26 = *(v24 + 7) + (v4 << 32);
  v26[1] = v25;
  *(v7 - 16) = sub_8D45D8(v24);
  v27 = sub_A9B030();
  *(v7 - 24) = v27;
  *(v27 + 15) = v3[4524];
  v29 = *(v7 - 8);
  v30 = 59;
  if ( (v29 & 1) != 0 )
    v30 = *(v29 - 1) >> 12;
  *v28 = v29;
  v31 = (*(v1 + 8 * (v30 + 30391)))();
  v33 = *(v7 - 24);
  *(v33 + 19) = v31;
  if ( (v31 & 1) != 0 && (*(v31 - 1) & (*(v33 - 1) >> 2) & HIDWORD(v4)) != 0 )
    sub_A99640();
  v34 = *(v7 - 24);
  *(v34 + 23) = v3[4525];
  v35 = *(v7 - 16);
  *(v34 + 27) = v35;
  if ( (v35 & 1) != 0 && (*(v35 - 1) & (*(v34 - 1) >> 2) & HIDWORD(v4)) != 0 )
    v35 = sub_A99640();
  v36 = v3[1127];
  *v32 = v34;
  v32[1] = v36;
  return sub_260234(v35);
}
function sign(tapString):

    now = DateTime.now()

    timestamp = now / 1000 / 1000

    tsString = intToString(timestamp)

    str1 = convertString(tsString)

    digest = hash(str1)

    str2 = convertString(digest)

    signature = convertString(str2)

    map = {}

    map["X-Auth-Timestamp"] = tsString

    map["X-Auth-Signature"] = signature

    return map
  • 0x311d0c 同时引用了 X-Auth-TimestampX-Auth-Signature
  • 0xa23de8 唯一调用 0x311d0c  ida 按一下 x 就知道了
  • 0xa23d70 处加载 tapString,并传入 0x311d0c

这里我们简单的验证一下猜想:

function hook_flutter() {

    var module = Process.findModuleByName("libflutter.so");

    if (!module) return;

    var addr = module.base.add(0x5DC3CC);

    console.log("[+] ssl_verify_result:", addr);

    Interceptor.attach(addr, {

        onLeave: function (retval) {

            console.log("[+] SSL bypass");

            retval.replace(1);

        }

    });
}

function wait_flutter() {

    var m = Process.findModuleByName("libflutter.so");

    if (m) {

        console.log("[+] flutter loaded");

        hook_flutter();

    } else {

        setTimeout(wait_flutter, 1000);

    }
}

setImmediate(wait_flutter);

const SO_NAME = 'libapp.so';

const OFF = {
  signFn: 0x311d0c,
  strConv: 0x8d45d8,
  intToString: 0x254ed8,
  signCallSite: 0xa23de8,
};

let base = null;

function hx(v) {
  return '0x' + ptr(v).toString(16);
}

function rangeOf(p) {
  try {
    return Process.findRangeByAddress(ptr(p));
  } catch (e) {
    return null;
  }
}

function canRead(p) {
  const r = rangeOf(p);
  return !!r && r.protection.indexOf('r') !== -1;
}

function toAscii(u8) {
  let s = '';
  for (let i = 0; i < u8.length; i++) s += String.fromCharCode(u8[i]);
  return s;
}

const CidString = 93;
const CidTwoByteString = 94;
const CidExternalOneByteString = 95;
const CidExternalTwoByteString = 96;

function getCidFromTagged(taggedPtr) {
  try {
    const p = ptr(taggedPtr);
    if (p.isNull()) return -1;
    if (p.and(1).toInt32() === 0) return -1;
    const obj = p.sub(1); 
    if (!canRead(obj)) return -1;
    const tag = obj.readU32();
    return (tag >>> 12) & 0xfffff;
  } catch (e) {
    return -1;
  }
}

function readDartString(taggedPtr) {
  try {
    const p = ptr(taggedPtr);
    if (p.isNull()) return null;
    if (!canRead(p)) return null;

    if (p.and(1).toInt32() === 0) return null;

    const obj = p.sub(1); 
    if (!canRead(obj)) return null;

    const cid = getCidFromTagged(p);

    const lenTagged = obj.add(0x8).readU32();
    const len = lenTagged >>> 1;
    if (len <= 0 || len > 4096) return null;

    if (cid === CidString) {
      const bytes = obj.add(0x10).readByteArray(len);
      if (!bytes) return null;
      return toAscii(new Uint8Array(bytes));
    }

    if (cid === CidTwoByteString) {
      return obj.add(0x10).readUtf16String(len);
    }

    if (cid === CidExternalOneByteString || cid === CidExternalTwoByteString) {
      return null;
    }

    return null;
  } catch (e) {
    return null;
  }
}

function readTaggedValue(taggedPtr) {
  try {
    const p = ptr(taggedPtr);
    if (p.isNull()) return 'null';
    if (p.and(1).toInt32() === 0) {
      const smi = p.toInt32() >> 1;
      return `smi(${smi})`;
    }
    const s = readDartString(p);
    if (s !== null) return `"${s}"`;
    const cid = getCidFromTagged(p);
    return `${hx(p)}(cid=${cid})`;
  } catch (e) {
    return `<err:${e}>`;
  }
}

function hookSignFlow() {
  const signFn = base.add(OFF.signFn);
  const strConv = base.add(OFF.strConv);
  const intToString = base.add(OFF.intToString);
  const signCallSite = base.add(OFF.signCallSite);

  Interceptor.attach(signCallSite, {
    onEnter(args) {
      const tap = readTaggedValue(this.context.x0);
      console.log(`[sign_callsite] x0 (tapString?) = ${tap}`);
    }
  });
  console.log(`[+] hook sign_callsite @ ${signCallSite}`);

  Interceptor.attach(signFn, {
    onEnter(args) {
      const tap = readTaggedValue(this.context.x0);
      console.log(`[sign_fn] IN tapString = ${tap}`);
    },
    onLeave(retval) {
      console.log(`[sign_fn] OUT map = ${hx(retval)}`);
    }
  });
  console.log(`[+] hook sign_fn @ ${signFn}`);

  Interceptor.attach(intToString, {
    onEnter(args) {
      this.isTs = false;
      const raOff = ptr(this.returnAddress).sub(base).toInt32();
      if (raOff === 0x311d60) {
        this.isTs = true;
      }
    },
    onLeave(retval) {
      if (!this.isTs) return;
      const s = readDartString(retval);
      console.log(`[sign_out] x-auth-timestamp = ${s !== null ? s : hx(retval)}`);
    }
  });
  console.log(`[+] hook intToString @ ${intToString}`);

  Interceptor.attach(strConv, {
    onEnter(args) {
      this.isSig = false;
      const raOff = ptr(this.returnAddress).sub(base).toInt32();
      if (raOff === 0x311dc8) this.isSig = true;
    },
    onLeave(retval) {
      if (!this.isSig) return;
      const s = readDartString(retval);
      console.log(`[sign_out] x-auth-signature = ${s !== null ? s : hx(retval)}`);
    }
  });
  console.log(`[+] hook strConv @ ${strConv}`);
}

function waitAndHook() {
  const timer = setInterval(() => {
    const m = Process.findModuleByName(SO_NAME);
    if (!m) return;
    clearInterval(timer);
    base = m.base;
    console.log(`[+] ${SO_NAME} base = ${base}`);
    hookSignFlow();
  }, 100);
}

waitAndHook();

得到日志:

[sign_fn] IN tapString = "3af08590311032efe0660550a0563a53"
[sign_out] x-auth-timestamp = 1773668340
[sign_out] x-auth-signature = ecdfa909b94e2d7b015fad36cdb843b7b122905dcdc9a7c92b75e272e0e8e91f
[sign_fn] OUT map = 0x7500adf3b9

动态日志反证:为什么可以确认是 HMAC-SHA256

  1. tapString = 3af08590311032efe0660550a0563a53
  2. x-auth-timestamp = 1773668340
  3. x-auth-signature = ecdfa909b94e2d7b015fad36cdb843b7b122905dcdc9a7c92b75e272e0e8e91f

最后做本地复算:

import hmac
import hashlib

tap = "3af08590311032efe0660550a0563a53"
ts = "1773668340"
print(hmac.new(tap.encode(), ts.encode(), hashlib.sha256).hexdigest())

输出:

ecdfa909b94e2d7b015fad36cdb843b7b122905dcdc9a7c92b75e272e0e8e91f

与动态日志完全一致。猜想正确。其实你直接看代码里面有魔术字符的

x-auth-signature = HMAC_SHA256(key=tapString, msg=str(epoch_seconds)).hexdigest()

另外,sub_311EA0 的行为特征也和 HMAC 结构一致(逆向等价):

  1. key 长度按 0x40(64 字节)分支处理
  2. 长 key 先做一次摘要后再参与后续流程
  3. 两段摘要流程(inner/outer)后输出 32 字节摘要,再转 hex 字符串

关键函数定位总表

函数地址 作用
0x311d0c 生成签名头 map(X-Auth-Timestamp / X-Auth-Signature
0x311ea0 HMAC 核心计算(被 0x311d0c 调用)
0xa23d70 读取 tapStringpp+0x8740
0xa23de8 调用 0x311d0c 的 callsite
0x9fe574 计算 umstring(摘要后转 hex)
0x9fe5e4 写入 tapString 到 map
0x9fe4d8 写入 umString 到 map
0x9fd094 生成 16 位 pseudoid
0xaa28fc / 0xaa2964 pseudoid 初始化:有值复用,无值生成
0x46e0bc cartoons 参数组装(free_type/limit/offset/ordering/theme

剩下那两个参数定位也是一样的:

  1. 先通过头部构造函数 0xa22ff0 确认 umString 是动态写入字段(key 在 pp+0x86b0)。
  2. umString 键名引用,命中 0x9fe4d8/0x9fe500(写 map)。
  3. 继续往上看数据来源,命中 0x9fe574(输入字符串 -> 摘要 -> hex)。
  4. 该函数内部调用链显示“摘要后十六进制字符串化”模式(0x8d281c + 0x8d45d8)。

  1. 先在常量池找到:
    • key 名 pseudoID -> pp+0x8630
    • 字符集 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ -> pp+0x89d8
  2. pseudoID 写入点:
    • 0xaa28fc/0xaa2964(初始化流程)
    • 0x9fcf24/0x9fcf4c(写入 map)
  3. 追生成函数:
    • 命中 0x9fd094
    • 看到固定循环 16 次、每次从 pp+0x89d8 字符集中取一个字符拼接。

libapp.zip (3.63 MB, 下载次数: 0) objs.zip (1.85 MB, 下载次数: 0)

免费评分

参与人数 2吾爱币 +2 热心值 +2 收起 理由
814493000 + 1 + 1 我很赞同!
ytfh1131 + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
coc0123 发表于 2026-3-17 23:20
还是看不懂...额
沙发
JackyLan 发表于 2026-3-17 23:16
4#
 楼主| utf8 发表于 2026-3-18 00:00 |楼主
5#
wang287ke 发表于 2026-3-18 00:17
很详细,就是完全不懂,不懂。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-3-18 01:43

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表