应用
某咖啡
抓包
新买的pixel 6, 用公司网络, 设置代理后,charles 没有任何反应, 请求压根就没经过charles. 但是用自己的另外一个手机开热点,可以抓到请求.
问题已解决: 在连接wifi 的时候设置使用设备 MAC, 不要随机mac 就可以使用公司网络进行抓包了.
背景
打开 发现用了 某数字厂商的壳. 于是尝试用 frida 脱壳. 结果发现这个apk 有anti-frida. 然后 hluda-server, Strong-frida 等都过不了.
脱壳
https://github.com/LLeavesG/eBPFDexDumper
我是用的这个基于 ebpf 的 dexDumper. 对内核有一定的要求, 本人亲测 pixel 6, 官方 rom, android 14. 可正常使用并完成脱壳.
1 | ./eBPFDexDumper dump -n com.example.app -o /sdcard/dex_out |
脱壳后将 dex 拖入 jadx, 发现有部分代码已经可以正常显示了. 全局搜索 sign, 定位几处可疑的地方。
于是想要使用 frida hook 验证猜想, 但是有双进程保护,注入的时候找不到进程.
spawn 模式注入也会崩溃. 本人使用的是内核模块过 frida 检测. 但使用 spawn 模式注入的时候, 不能先注入hook代码。 要先注入一段空代码, 然后d等应用启动后, 再把注入代码粘贴到hook 脚本中.
本人猜测是 init_array, 或者 jni_onload 中有 crc 检测. 但线程不但轮询检测中没有 crc 检测. 当然只能猜测.
ok 现在 frida 可以正常使用了
frida 环境
本人使用 frida 环境
frida-server https://github.com/ultrafunkamsterdam/undetected-frida undetected_f1783
frida==17.8.3
frida-tools==14.8.1
题外
如果frida 版本是 17.0 以下的, 只要 hook 导致app闪退一次, 第二次即使是正常打开, 不使用 frida, 应用也会立马闪退. 原因是 低版本的 frida spawn 模式会污染 zygote 进程. 导致即使是正常启动,应用也会检测到一些特征。
面对低版本闪退这种,就只能重启手机了。 很长一段时间, hook一次, 就要重启一次. 特别坑爹.
但是高版本 frida spawn hook 采用 Frida-Zymbiote 机制. 参考文章 https://bbs.kanxue.com/thread-289866.htm
整篇文章粘给 claude code, 它给出的结论是 zymbiote 机制处理后的 zygote 会恢复原样. 但是 传统 spawn 会永久修改 zygote, 导致没有 frida 也会崩溃. 就是因为第一次 hook 在 zygote 留下了痕迹.
抓包以及字符串定位
frida hook String.getBytes 方法
1 |
|
根据堆栈,定位到 md5_crypt 函数. frida hook md5_crypt 函数.
1 |
|
hook 结果如下
1 | localAESWork bytesToString(bArr2) : ��o�_��ug�� ��ŖI �P�Wbr�l|a�4; |
可以看到 先 localAESWork 再 md5_crypt, localAESWork 的结果会参与 md5_crypt 的计算.
CryptoHelper.md5_crypt is called: i2=1 可以看到 i2 = 1 这点跟 localAESWork 的 2 是不一样的.
md5_crypt 是一个 native 函数

加载的so 文件被编码了, frida hook 一下即可
1 | let StubApp = Java.use("com.stub.StubApp"); |
ida 打开so 文件. 进入 jni_onload 函数。

进入 off_4BCE0, 可以看到是个结构体, 里面存放的是 java函数名, 方法签名, native层绑定的 jni 函数.

进入 android_native_md5 函数看看,可以看到, 有大面积的 ollvm .


将整个函数丢给 chatgpt 和 gemini,这两个我喜欢混着用。 但是最近我发现 claude code 真的非常强大. 例如分析某个函数时, chatgpt 和 gemini 还在用非常含糊的语言表达时.
claude code 会用非常肯定语气告诉你. 并且十分靠谱, 这一点是我觉得 cc 远比 gpt 和 gemini 好用的地方
继续问 ai 哪里可能是关键函数
大概率 sub_4095C 这个函数是关键函数.
进入这个函数. 这个函数也是有 ollvm 的, 我们使用 d810 插件去除, 去除完的效果是这样的.

可以看到, 去除效果非常的 nice, 基本能看到主脉络.
进入到 sub_41084, 继续交给 ai 分析.

它怀疑这个函数 魔改的 md5. DecryptSpace_3 是md5 的 T表或者K 表(wiki). 而 DecryptSpace_5 是 S表(位移表),
我们使用 unidbg 补环境, 然后 看看 DecryptSpace_3 和 DecryptSpace_5 是否被魔改.
在ida 中查看 DecryptSpace_3 和 DecryptSpace_5 的地址

unidbg 中在调用完 jni_onload 后 查看内存
1 | dm.callJNI_OnLoad(emulator); // 调用JNI OnLoad |

可以看到, 并不是标准的 md5 常量.
但这里我们犯了一个错. 那就是 DecryptSpace_3 和 DecryptSpace_5 在实际使用之前,进行了异或计算.

所以我们应该是在 快用到 DecryptSpace_3 或者DecryptSpace_3 快要参与计算的时候,打断点, 然后查看 K表和 S 表的值.
于是我们找一个好时机. 在循环右移的时候, DecryptSpace_3 已经解密了.
我们的hook 点选在 0x40460. unidbg 下断点.
1 | dm.callJNI_OnLoad(emulator); // 调用JNI OnLoad |
unidbg 运行后, 断在了 0x40460

然后我们查看真实参与运算后的 K(T)表的值. 输入 m0x40052870 1000, 表示查看绝对地址 0x40052870 的内存数据. 查看 1000字节
0x40052870 是 0x40000000 + 0x52870 得来.
0x40000000 是 unidbg 加载一个 so 的基地址, 0x52870 是 DecryptSpace_3 在 so 中的偏移
把这个常量表交给 gpt, gemini 和 cc, 都说这是一个标准的 K(T表)
相同操作 得到 S表也是标准的.
我们返回到上层函数, hook 这个md5 函数 sub_41084 的入参, 猜测 v18 保存的是 md5 的结果.

入参如下, 马赛克部分是 salt.

根据 arm64 调用约定, x2 保存的是第三个参数. 我们先记录下 x2 的地址.
然后 输入 n 单步执行, 然后查看 保存的x2 寄存器 对应的内存空间, 不出意外就是 md5 的值.
结果如下

我们使用cyberchef 测试一下, 看看是不是标准的 md5. 因为刚才我们只测试了 T(K)表 和 S表. 初始化向量我们并没有测试.

可以看到 执行结果跟 unidbg 结果一样.
接下来把 剩下的部分 交给 ai 帮忙分析

得出结果如下

我们我们把 md5 的输出 还有最终结果 交给 claude code. 分析结果如下

让cc 生成代码, 代码如下
1 | import hashlib, struct |
运行 python 结果为 10748593791588244702661900501773497185
再看看unidbg 的结果

可以看到 结果一致.
心得
cc 在代码处理部分, 尤其是当我给出 输入 输出 和 ida 伪代码, cc几乎都生成了直接可用的 python 算法. 太厉害了.
例如当我给出如下提示词:
最终结果是 10748593791588244702661900501773497185, md5 结果是 BF EE F2 8D F6 88 87 EA 0F DD BC E2 69 B5 6B 61,
它分析了很久,返回的 文本中包含 “用你给的数据验证一下。 逐块推算”, 我在怀疑,它后台是不是真的在做运算, 在核对数据. 在验证猜想. 太强了 claude code.
AI 告诉我们



























































