Frida Hook技术(二)
抓包、Hook点与沙盒
加密字符串一反射,jadx 里搜关键字就断了。换思路:抓包先看数据长什么样,再用 HashMap.put、Collections.sort、Toast.show 这些公共点打调用栈,定位关键代码。Java 层最省事的是跑沙盒。
一、PC 客户端抓包:Fiddler + Proxifer
PC 客户端流量不一定走系统代理。做法是 Fiddler + Proxifer:用 Proxifer 做 VPN 转发,把客户端流量导到 Fiddler。Fiddler 和 Proxifer 两边都能看到抓到的数据。
二、为什么静态搜索经常废
入门案例里常见卡点:Java / C 基础不够;搜索不靠谱——字符串加密了、反射调用了,关键字就搜不到;标准算法在 Java / C 里怎么实现不熟;复现时用 JS,结果加密库也不熟;Hook 代码写着烦;还有加固、抓包检测、Hook 检测、Root 检测挡路。
定位思路要换:Java 侧——Hook 系统函数,或用沙盒吐出 smali 和函数调用关系;C 侧——Hook 系统函数;写 Hook 嫌烦——Java 可用 Objection(自动化 Hook)或沙盒;so 层暂时没什么特别省事的工具,多半自己封装 Frida API。逆向 H5 的 App,还得懂 JS——那是安卓逆向和 JS 逆向混搭。
三、Frida 注入与打印调用栈
注入还是老命令:frida -UF -l test2.js。Frida Hook 脚本里写的是对 Java 层的调用。打印调用栈用 showStacks:android.util.Log.getStackTraceString(new java.lang.Throwable()),参数就是 Throwable 异常信息,目的是追踪程序调用流程。
代码没问题,但 App 开久了有时 Hook 不到想要的东西——重启 App,或换一个 Hook 点。jadx 的反混淆不要开,开了以后 Hook 的函数名会被改,对不上。包名以 Frida 枚举出来的为准:enumerateLoadedClassesSync。
四、HashMap.put:提交参数入口
HashMap.put 一般在提交参数时用。Hook put,过滤 key 等于 username,命中就 showStacks,并打出 key、value。相关家族一起盯:HashMap、LinkedHashMap、ConcurrentHashMap;HashSet、LinkedHashSet 底层走的是 HashMap、LinkedHashMap。
五、一批高频 Hook 定位点
TextUtils.isEmpty:常用于控件内容判空,很多开发者也会自己再包一层。不知道类全名时,在工程里敲一下代码,提示里就能看到包路径。对已知密文或关键串做 equals 过滤,命中再打栈。
Log.w:Hook 日志打印,tag 和 message 一起打出来;需要时再开 showStacks。JSONObject.put、indexOf 子串查找、getString,也是常见卡点。协议逆向做到数据加密,常会落到 Base64 相关定位。
六、Collections.sort:签名前排序
ArrayList 在排序时用,签名算法之前经常排序。Hook Collections.sort,两个重载都要挂:第一个 sort(List) 会调第二个 sort(List, Comparator),第二个更底层,可自定义规则。参数类型是 List 接口,实际传入多是 ArrayList 等实现类,要 Java.cast 成 ArrayList 再 toString。Arrays.sort 也是排序,别漏。
七、Toast.show:强制升级弹窗
Hook Toast.show,弹 Toast 时打调用栈。做去强制升级时,对方往往会弹窗,从 Toast.show 往回追,能较快定位到升级逻辑。
八、字符串定位三板斧
协议里对明文字节动手时,可能用 String.getBytes;也可以 Hook isEmpty。注意:函数调得太勤,硬 Hook 可能把程序搞崩。
别直接 Hook String 构造函数——Android 上很多构造直接抛 UnsupportedOperationException,提示 Use StringFactory instead,Hook 它没用。真正要 Hook 的是 StringFactory:newStringFromBytes、newStringFromChars、newStringFromString、newStringFromStringBuffer、newStringFromStringBuilder。
字符串定位总结:① getBytes——定位字符串转字节;② 字符串构造(走 StringFactory);③ StringBuilder——定位拼接,构造最后会 toString,toString 会 new String。StringBuffer 和 StringBuilder 的区别:StringBuffer 支持多线程,因为加了锁。
九、findViewById 与控件 ID
获取控件 ID:可用 UI Automator Viewer。控件 ID 对应数值在 R 类里找,R 类在包名对应文件夹下,例如 R$id.btn_login.value。
findViewById 只在创建界面时执行,事后再 Hook 已经不跑了。手动重启 App 也不行,必须让 Frida 来拉起进程,例如:frida -U -f 包名 -l HookDemo.js -o log.txt --no-pause。-f 强制重启注入;--no-pause 不暂停主线程,程序直接跑。不恢复线程也可以,命令里带上不暂停即可。setOnClickListener 和 findViewById 一样,一开始执行过就不再执行,同样要 spawn 注入。
十、沙盒辅助定位(Java 层)
Java 层最好的关键代码定位,往往是跑沙盒。沙盒是直接 Hook 虚拟机解释器:不仅能打每一行 Java 相关执行信息,还能打每一行 smali,以及对应寄存器的值;同时打印函数调用关系。代码执行顺序从上到下,对着 trace 就能顺着业务走。
沙盒能跑出来的:① Java 层;② so 回调到 Java 层的。定位不到的:so 层 native 汇编,以及 H5 的 JS。H5 逆向面临的是安卓逆向和 JS 逆向混搭,要懂 JS。
十一、总结
1. PC 客户端抓包:Fiddler + Proxifer 做 VPN 转发,两边都能看数据 2. 加密串、反射调用导致静态搜索失效,要换 Hook / 沙盒思路 3. Java:Hook 系统函数或沙盒吐 smali 与调用关系;C:Hook 系统函数 4. Hook 嫌烦可用 Objection;H5 App 还要懂 JS 5. showStacks:Log.getStackTraceString(new Throwable) 追调用链 6. App 开久 Hook 不到:重启或换 Hook 点;jadx 反混淆别开 7. 包名以 enumerateLoadedClassesSync 枚举结果为准 8. HashMap.put 提交参数常用;过滤 username 等 key 再打栈 9. Map 家族:HashMap/LinkedHashMap/ConcurrentHashMap,Set 底层走 Map 10. TextUtils.isEmpty 控件判空;Log.w 打日志;JSONObject.put / indexOf / getString 11. Collections.sort 两重载都 Hook,List 转 ArrayList;另有 Arrays.sort 12. Toast.show 可定位强制升级弹窗逻辑 13. 协议加密常落到 Base64;getBytes 盯明文转字节 14. 别 Hook String 构造,改 Hook StringFactory 一串 newStringFrom* 15. 字符串定位:getBytes、StringFactory、StringBuilder(toString→new String) 16. StringBuffer 有锁支持多线程;StringBuilder 无 17. 控件 ID:UI Automator Viewer + R$id.xxx.value 18. findViewById / setOnClickListener 只执行一次,需 frida -f --no-pause 19. 沙盒 Hook 解释器:打调用关系 + 每行 smali 与寄存器 20. 沙盒能定位 Java 与 so→Java;定位不到 so 汇编和 H5 JS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
如果觉得有帮助,欢迎转发!
关注公众号,输出安卓逆向与底层技术干货。
#关键代码定位 #Frida #沙盒 #Fiddler #Proxifer #HashMap #Collections.sort #StringFactory #安卓逆向 #Objection