安卓逆向03. 动态调试、抓包分析与 Frida Hook

本章目标是让 App 跑起来观察真实行为:用 adb/logcat 定位流程,用 Burp 或 mitmproxy 验证接口,用 Frida 在授权 Demo 上观察和修改运行时方法。

1. 动态逆向解决什么问题

静态分析回答"代码里可能有什么",动态分析回答"运行时实际发生了什么"。

场景 静态分析不足 动态分析价值
混淆严重 类名和方法名不可读 通过调用日志和参数观察真实行为
反射调用 jadx 不容易看完整调用链 Hook 反射入口或目标方法
加密签名 只看到算法但不知道明文 在加密前打印参数
网络接口 代码里 URL 可能动态拼接 抓包确认真实请求
环境检测 检测分散在多处 Hook 返回值验证影响
服务端校验 客户端代码不能代表服务端逻辑 改包、重放、篡改请求验证服务端

动态分析必须和授权边界绑定:只抓自建 Demo 或授权测试环境,不采集真实用户隐私、真实 token 或第三方业务数据。

2. adb 与 logcat 动态观察

2.1 进程和页面定位

bash 复制代码
adb shell pm list packages | grep reversedemo
adb shell dumpsys package com.example.reversedemo | sed -n '/Activity Resolver Table/,$p'
adb shell pidof com.example.reversedemo
adb shell ps -A | grep reversedemo
adb shell dumpsys activity top

理解要点:

  • 包名是所有动态工具的基础输入。
  • Activity 名可以用于直接启动隐藏页面或测试导出组件。
  • PID 用于 Native 调试、/proc/<pid> 观察和 Frida attach。

2.2 日志分析

bash 复制代码
adb logcat -c
adb logcat | grep -E "ReverseDemo|OkHttp|FATAL|Exception"

日志要关注:

日志类型 说明
业务日志 可能暴露账号、token、接口参数
崩溃栈 能定位类名、方法、行号或混淆后的调用点
网络日志 OkHttp logging interceptor 可能输出请求响应
安全检测日志 root、proxy、debug、frida 检测路径

正式包原则:不能输出敏感字段,不能依赖日志作为安全控制。

3. 抓包分析

3.1 代理配置

Burp 或 mitmproxy 都可以。以 mitmproxy 为例:

bash 复制代码
mitmproxy -p 8080
adb shell settings put global http_proxy <电脑IP>:8080
adb shell settings get global http_proxy

清理代理:

bash 复制代码
adb shell settings put global http_proxy :0

设备需要信任代理证书。Android 7 以后,App 默认不信任用户安装的 CA。自建 Demo 可在 debug 配置中允许用户证书,release 则应更严格。

3.2 Network Security Config 示例

debug 练习环境可配置:

xml 复制代码
<network-security-config>
    <debug-overrides>
        <trust-anchors>
            <certificates src="user" />
            <certificates src="system" />
        </trust-anchors>
    </debug-overrides>
</network-security-config>

知识点:

  • 这只应存在于 debug 或授权测试包。
  • release 包不应为了方便抓包而信任用户 CA。
  • 如果启用了证书绑定,抓包失败是正常现象,后续要记录"被防护阻断"而不是强行绕过未授权目标。

3.3 Demo:接口风险验证

Demo 请求:

http 复制代码
POST /api/profile HTTP/1.1
Authorization: Bearer demo-token
X-Timestamp: 1710000000
X-Nonce: abc123
X-Sign: <client sign>

{"userId":"10002","action":"readProfile"}

测试问题:

测试 操作 安全预期
userId 10002 改成 10001 服务端拒绝越权
重放请求 复制旧请求再次发送 服务端拒绝过期或重复 nonce
删除签名 移除 X-Sign 服务端拒绝
修改 body 修改 action 签名不匹配,被拒绝
替换 token 使用无效 token 401 或 403

结论要写服务端结果,不要只写"抓到了请求"。抓包的重点是验证服务端是否承担最终安全判断。

4. Frida Java Hook 基础

4.1 启动方式

Attach 已运行进程:

bash 复制代码
frida -U -n ReverseDemo -l hook.js

Spawn 启动进程:

bash 复制代码
frida -U -f com.example.reversedemo -l hook.js

常见差异:

  • attach 适合 App 已经运行的场景。
  • spawn 适合 Hook 启动早期逻辑。
  • 如果目标方法在启动阶段就执行,attach 可能错过。

4.2 Demo:Hook 会员判断

javascript 复制代码
Java.perform(function () {
  const UserCenter = Java.use("com.example.reversedemo.UserCenter");

  UserCenter.isVip.implementation = function (userId) {
    const original = this.isVip(userId);
    console.log("[isVip] userId=" + userId + ", original=" + original);
    return true;
  };
});

运行:

bash 复制代码
frida -U -f com.example.reversedemo -l hook-vip.js

验证:

验证项 通过标准
Hook 命中 控制台打印 [isVip]
参数可见 能看到 userId
行为改变 非会员进入会员功能
风险结论 客户端判断不能保护核心权益

4.3 Demo:打印签名前明文

javascript 复制代码
Java.perform(function () {
  const Signer = Java.use("com.example.reversedemo.Signer");

  Signer.sign.implementation = function (path, timestamp, nonce, body) {
    console.log("[sign] path=" + path);
    console.log("[sign] timestamp=" + timestamp);
    console.log("[sign] nonce=" + nonce);
    console.log("[sign] body=" + body);
    const result = this.sign(path, timestamp, nonce, body);
    console.log("[sign] result=" + result);
    return result;
  };
});

知识点:

  • Hook 加密前的入口,比反推加密后的密文更直接。
  • 如果客户端持有长期密钥,动态分析可以帮助定位密钥使用点。
  • 正确防护不是"把函数名混淆掉"就结束,而是降低客户端掌握核心秘密的程度。

5. Frida 常用 Hook 模式

5.1 Hook 重载方法

javascript 复制代码
Java.perform(function () {
  const Cls = Java.use("com.example.reversedemo.SecurityChecker");
  Cls.check.overload("java.lang.String").implementation = function (name) {
    console.log("check(String): " + name);
    return this.check(name);
  };
});

5.2 Hook 构造函数

javascript 复制代码
Java.perform(function () {
  const Request = Java.use("com.example.reversedemo.ApiRequest");
  Request.$init.overload("java.lang.String", "java.lang.String")
    .implementation = function (path, body) {
      console.log("ApiRequest path=" + path + ", body=" + body);
      return this.$init(path, body);
    };
});

5.3 Hook 系统 API

javascript 复制代码
Java.perform(function () {
  const File = Java.use("java.io.File");
  File.exists.implementation = function () {
    const path = this.getAbsolutePath();
    if (path.indexOf("/su") >= 0) {
      console.log("[File.exists] bypass " + path);
      return false;
    }
    return this.exists();
  };
});

这个示例只用于自建 Demo 验证 root 检测可被运行时影响。正式设计中,不应把 root 检测作为唯一安全门槛。

6. 动态调试与 Android Studio

如果有源码,优先用 Android Studio 调试自建 Demo:

  1. 给登录、会员判断、签名函数、网络拦截器打断点。
  2. 运行 debug 包。
  3. 输入账号,观察调用链和变量。
  4. 对比 jadx 反编译结果和真实源码。
  5. 编译 release 包,再观察混淆后的差异。

源码调试和逆向工具结合能帮助理解:

  • Kotlin 代码如何变成 DEX。
  • R8 混淆对类名、方法名、控制流有什么影响。
  • 哪些信息即使混淆后仍可通过字符串、日志、网络行为暴露。

7. 本章 Demo:动态分析闭环

7.1 实验任务

  1. adb logcat 捕获 Demo 登录和会员页日志。
  2. 用 Burp/mitmproxy 捕获 /api/profile 请求。
  3. 修改请求体和 Header,验证服务端鉴权、签名、重放保护。
  4. 用 Frida Hook UserCenter.isVip(),验证本地判断可变。
  5. 用 Frida Hook Signer.sign(),打印签名前明文和签名结果。
  6. 输出动态分析报告。

7.2 验证矩阵

验证项 工具 预期结果 证据
日志捕获 adb logcat 记录关键流程,无敏感日志更好 01-logcat.txt
请求捕获 Burp/mitmproxy 看到 Demo 请求和响应 02-http-flow.txt
参数篡改 Repeater 服务端拒绝越权 03-tamper-result.md
重放请求 Repeater 服务端拒绝过期或重复 nonce 04-replay-result.md
Hook 会员 Frida App 行为被影响 05-hook-vip.log
Hook 签名 Frida 打印签名前参数 06-hook-sign.log

8. 动态分析结论写法

好的结论必须包含"证据、影响、边界、修复":

markdown 复制代码
## 风险:会员权益依赖客户端判断

- 证据:Frida Hook `UserCenter.isVip()` 返回 true 后,非会员账号可进入 Demo 会员页。
- 影响:攻击者不需要重打包,只要能注入运行时 Hook,就可能影响客户端展示逻辑。
- 边界:本实验只验证自建 Demo;真实业务还要看服务端是否二次校验。
- 修复:会员权益由服务端按订单和账号状态授权,客户端只展示服务端返回结果;关键接口必须校验 token、签名、时间戳和业务归属。

9. 常见问题

问题 原因 处理
Frida 找不到类 类未加载、混淆、进程不对 Hook ClassLoader 或先触发页面
Hook 没生效 重载选择错误或 attach 太晚 使用 overload,尝试 spawn
App 闪退 Hook 返回类型错误或检测 Frida 先只打印不修改,逐步缩小
抓不到 HTTPS 证书不信任或证书绑定 记录防护现象,在授权 Demo 中测试配置
请求篡改无变化 服务端没检查该字段或字段不生效 找关键业务请求重新验证

10. 本章交付物

text 复制代码
case-reversedemo/
  03-dynamic/
    01-logcat.txt
    02-http-flow.txt
    03-tamper-result.md
    04-replay-result.md
    05-hook-vip.js
    06-hook-vip.log
    07-hook-sign.js
    08-hook-sign.log
    09-dynamic-report.md

11. 动态分析策略

11.1 动态分析三条线

动态分析建议同时推进三条线:

线索 目标 工具 产出
行为线 用户操作触发了什么页面、日志、请求 Android Studio、adb、logcat 行为时间线
数据线 参数、token、签名、响应如何变化 Burp/mitmproxy、Frida 数据流图
控制线 哪些函数决定分支、权限、风控 Frida、调试器 Hook 点清单

如果只抓包,容易看不到本地判断;如果只 Hook,容易看不到服务端结果;如果只看日志,容易被开发日志误导。

11.2 动态分析时间线模板

markdown 复制代码
## 行为时间线

| 时间 | 用户动作 | App 日志 | 网络请求 | Hook 命中 | 结论 |
|---|---|---|---|---|---|
| 10:00:01 | 打开 App | MainActivity onCreate | 无 | 无 | 启动 |
| 10:00:10 | 登录 | login clicked | POST /login | Signer.sign | 登录请求签名 |
| 10:00:20 | 进入会员页 | check vip | GET /vip/resource | UserCenter.isVip | 会员判断 |

这种时间线可以把工具输出串起来,避免零散截图无法形成结论。

12. adb 动态诊断

12.1 dumpsys 常用场景

命令 用途
adb shell dumpsys activity top 当前前台 Activity
adb shell dumpsys activity activities Activity 栈
adb shell dumpsys package <pkg> 包信息、权限、组件
adb shell dumpsys meminfo <pkg> 内存使用
adb shell dumpsys netstats 网络统计
adb shell dumpsys connectivity 网络状态
adb shell dumpsys window windows 窗口和焦点

12.2 am 命令扩展

bash 复制代码
adb shell am start -n com.example.reversedemo/.MainActivity
adb shell am force-stop com.example.reversedemo
adb shell am start -a android.intent.action.VIEW -d "reversedemo://open/vip"
adb shell am broadcast -a com.example.reversedemo.DEBUG_ACTION --es cmd dump

验证点:

  • 组件是否能被外部启动。
  • Deep Link 参数是否被校验。
  • force-stop 后重启是否有持久化状态。
  • 广播是否触发敏感行为。

12.3 logcat 过滤技巧

bash 复制代码
adb logcat -c
adb logcat -v time | grep -E "ReverseDemo|OkHttp|FATAL|AndroidRuntime"
adb logcat -b crash
adb logcat '*:E'

日志分析表:

线索 说明 风险
Authorization token 出现在日志 敏感信息泄露
password 密码或验证码输出 高风险
sign raw 签名前明文输出 签名可复现
debug panel 调试页面日志 隐藏入口
root detected 环境检测日志 可作为 Hook 入口

13. 抓包专题

13.1 HTTP 请求拆解

每个请求至少拆成 8 个部分:

部分 检查点
Method GET/POST/PUT/DELETE 是否符合业务
Path 是否含用户 ID、订单 ID、资源 ID
Query 是否可篡改分页、价格、类型
Header token、签名、设备 ID、版本
Body JSON 字段、嵌套对象、金额
Cookie 会话、CSRF、过期时间
Response 状态码、错误码、敏感字段
Timing 时间戳、nonce、重放窗口

13.2 Burp Repeater 测试矩阵

测试编号 改动 预期 结果
B-001 删除 token 401/403 记录
B-002 token 改为无效值 401/403 记录
B-003 userId 改为他人 403 记录
B-004 删除签名 400/401 记录
B-005 修改 body 不改签名 签名失败 记录
B-006 重放同一 nonce 重放拒绝 记录
B-007 时间戳改旧 过期拒绝 记录
B-008 金额改小 业务拒绝 记录
B-009 会员等级改高 业务拒绝 记录
B-010 添加未知字段 不应越权 记录

13.3 mitmproxy 脚本观察

授权 Demo 可用 mitmproxy 记录目标接口:

python 复制代码
from mitmproxy import http

def request(flow: http.HTTPFlow):
    if "reversedemo" in flow.request.pretty_host:
        print(flow.request.method, flow.request.path)
        print(flow.request.headers)
        print(flow.request.get_text())

注意:只在授权环境记录,不保存真实用户隐私数据。

13.4 证书问题排查

现象 可能原因 处理
完全无请求 代理未配置或 App 走直连 检查系统代理和网络
只有 CONNECT TLS 未解密 安装并信任 CA
证书错误 App 不信任用户 CA debug 配置允许用户证书
连接失败 证书绑定 记录防护现象,做授权复测
部分请求抓不到 使用 WebSocket、QUIC、Native 网络库 结合日志和 Hook

14. Frida Java 深入

14.1 枚举已加载类

javascript 复制代码
Java.perform(function () {
  Java.enumerateLoadedClasses({
    onMatch: function (name) {
      if (name.indexOf("reversedemo") >= 0) {
        console.log(name);
      }
    },
    onComplete: function () {
      console.log("done");
    }
  });
});

用途:

  • 混淆后找目标类。
  • 判断类是否已经加载。
  • 发现动态加载的模块。

14.2 Hook ClassLoader

javascript 复制代码
Java.perform(function () {
  const ClassLoader = Java.use("java.lang.ClassLoader");
  ClassLoader.loadClass.overload("java.lang.String").implementation = function (name) {
    if (name.indexOf("reversedemo") >= 0) {
      console.log("[loadClass] " + name);
    }
    return this.loadClass(name);
  };
});

适合处理:

  • 插件化。
  • 加固壳加载后的真实类。
  • 动态 dex。
  • 类延迟加载。

14.3 Hook OkHttp

javascript 复制代码
Java.perform(function () {
  const RequestBuilder = Java.use("okhttp3.Request$Builder");
  RequestBuilder.url.overload("java.lang.String").implementation = function (url) {
    console.log("[OkHttp url] " + url);
    return this.url(url);
  };
});

更常见的是 Hook 拦截器或请求构造:

Hook 点 目的
Request$Builder.url URL
Request$Builder.addHeader Header
RequestBody.create Body
Interceptor.intercept 完整请求响应
CertificatePinner.check 证书绑定观察

14.4 Hook SharedPreferences

javascript 复制代码
Java.perform(function () {
  const Editor = Java.use("android.app.SharedPreferencesImpl$EditorImpl");
  Editor.putString.implementation = function (key, value) {
    console.log("[SP putString] " + key + "=" + value);
    return this.putString(key, value);
  };
});

用途:

  • 观察 token 是否落盘。
  • 观察本地开关是否可被改。
  • 验证敏感字段是否明文保存。

15. Frida 实战脚本库

15.1 Hook root 检测 Java API

javascript 复制代码
Java.perform(function () {
  const File = Java.use("java.io.File");
  File.exists.implementation = function () {
    const path = this.getAbsolutePath();
    if (path.indexOf("su") >= 0 || path.indexOf("magisk") >= 0) {
      console.log("[bypass file] " + path);
      return false;
    }
    return this.exists();
  };
});

15.2 Hook Debug 检测

javascript 复制代码
Java.perform(function () {
  const Debug = Java.use("android.os.Debug");
  Debug.isDebuggerConnected.implementation = function () {
    console.log("[Debug.isDebuggerConnected]");
    return false;
  };
});

15.3 Hook Base64

javascript 复制代码
Java.perform(function () {
  const Base64 = Java.use("android.util.Base64");
  Base64.encodeToString.overload("[B", "int").implementation = function (input, flags) {
    const result = this.encodeToString(input, flags);
    console.log("[Base64] " + result);
    return result;
  };
});

15.4 Hook MessageDigest

javascript 复制代码
Java.perform(function () {
  const MessageDigest = Java.use("java.security.MessageDigest");
  MessageDigest.getInstance.overload("java.lang.String").implementation = function (algo) {
    console.log("[Digest algo] " + algo);
    return this.getInstance(algo);
  };
});

这些脚本的目的都是观察授权 Demo 的运行时数据流,而不是绕过第三方业务。

16. 动态风险库

风险编号 风险 动态证据 业务判断
D-001 token 出现在日志 logcat 高风险
D-002 本地会员可 Hook Frida 日志和截图 看服务端是否拒绝
D-003 签名前明文可打印 Frida Hook 客户端秘密不可信
D-004 请求可重放 Burp Repeater 缺 nonce
D-005 参数可篡改 修改 body 缺签名或业务校验
D-006 证书可被代理 抓到 HTTPS 明文 release 防护不足
D-007 Root 检测可 Hook Hook File.exists 单点检测无效
D-008 导出页面可启动 am start 缺入口鉴权
D-009 本地存储明文 run-as 查看 数据保护不足
D-010 崩溃暴露栈 logcat crash 信息泄露

17. 动态报告模板

markdown 复制代码
# Dynamic Analysis Report

## 测试范围
- App:
- 版本:
- 包名:
- 授权范围:

## 行为时间线
| 时间 | 操作 | 日志 | 请求 | Hook |

## 抓包结果
| 接口 | 方法 | 鉴权 | 签名 | 重放 | 越权 |

## Hook 结果
| Hook 点 | 参数 | 原返回 | 修改返回 | 行为变化 |

## 风险结论
| 编号 | 风险 | 证据 | 影响 | 修复 |

## 限制
- 未覆盖:
- 未复现:
- 需要后端配合:

18. 进阶练习

练习 目标 通过标准
Hook 登录函数 打印用户名,不记录真实密码 能看到授权 Demo 参数
Hook 签名函数 打印签名前 raw string 能复现签名输入
Hook OkHttp 输出 URL 和 Header 能和抓包结果对上
Hook SP 写入 观察 token 是否落盘 能判断存储风险
重放请求 验证 nonce 服务端拒绝旧请求
改 userId 验证越权 服务端拒绝
关闭代理 验证网络恢复 App 正常请求
模拟 root 验证检测路径 Hook 日志命中

19. 动态分析、抓包与 Hook

动态分析关注真实运行时行为:参数、请求、返回值、分支和服务端结果。它用来验证静态结论,而不是替代业务判断。

动态观察

知识点 核心理解 Demo/验证 常见误区
行为时间线 把用户操作、日志、请求和 Hook 命中放到同一时间轴。 登录、进入会员页、请求资源三步记录。 证据散落,无法证明因果。
logcat 日志能暴露流程、异常和敏感字段。 过滤 `ReverseDemo OkHttp
Activity 栈 当前页面和跳转路径可通过 dumpsys 观察。 dumpsys activity top 不知道当前页面就乱 Hook。
进程状态 PID、进程名、启动时机影响 Hook 策略。 pidofps -A attach 太晚导致漏 Hook。
本地数据观察 SP、SQLite、文件写入能揭示 token 和开关。 Hook SP 或用 debug run-as 看不到文件就认为没保存。

抓包与接口

知识点 核心理解 Demo/验证 常见误区
HTTPS 信任链 抓包依赖设备信任代理证书,release 应更严格。 debug 和 release 分别抓包。 抓不到就认为服务端安全。
证书绑定 pinning 提高中间人攻击成本。 release 下代理失败并记录错误。 用 pinning 替代服务端鉴权。
Header 篡改 Authorization、签名、设备 ID 都需要服务端校验。 删除或替换 Header。 只改 body 不测 Header。
Body 篡改 金额、等级、userId、订单 ID 是关键字段。 Burp Repeater 修改 JSON。 服务端返回 200 就直接判成功,不看业务码。
重放请求 旧 nonce 或 timestamp 应被拒绝。 重复发送同一请求。 客户端生成 nonce 但服务端不记录。
越权验证 资源归属必须由服务端按 token 判断。 userId 或订单 ID。 只验证登录,不验证资源归属。

Frida Hook

知识点 核心理解 Demo/验证 常见误区
spawn 适合 Hook 启动早期逻辑。 Hook Application 初始化。 只用 attach 导致错过检测。
attach 适合观察已运行流程。 打开会员页后 Hook isVip 目标方法已执行还期待命中。
重载方法 Java 方法可能有多个 overload,必须选对签名。 Hook check(String) 不指定 overload 导致脚本失败。
构造函数 Hook $init 可观察对象创建参数。 Hook 请求对象构造。 只 Hook 业务方法漏掉参数来源。
ClassLoader 插件化和加固会使用不同 ClassLoader。 Hook loadClass 类找不到就认为不存在。
返回值修改 返回值修改只证明客户端可变,不等于服务端被绕过。 Hook isVip() 后访问 /vip/resource 把 UI 变化写成核心越权。
签名前明文 Hook 加密前入口可观察真实参数。 Hook Signer.sign() 只看密文,不分析覆盖范围。
相关推荐
一起搞IT吧3 小时前
相机Camera日志实例分析之二十:相机Camx【照片后置4800/5000/6400万拍照】单帧流程日志详解
android·嵌入式硬件·数码相机·智能手机
jinanwuhuaguo4 小时前
(第三十三篇)五月的文明奠基:OpenClaw 2026.5.2版本的文明级解读
android·java·开发语言·人工智能·github·拓扑学·openclaw
千码君20165 小时前
Trae:一些关于flutter和 go前后端开发构建的分享
android·flutter·gradle·android-studio·trae·vibe code
重生之我是Java开发战士8 小时前
【MySQL】事务 & 用户与权限管理
android·数据库·mysql
怣疯knight10 小时前
Windows不安装 Android Studio如何打包安卓软件
android·windows·android studio
ke_csdn11 小时前
从Java演变到Kotlin下的jet pack
android
wenzhangli711 小时前
在低代码设计中践行 Harness Engineering
android·低代码·rxjava
xingpanvip12 小时前
星盘接口开发文档:组合三限盘接口指南
android·开发语言·前端·python·php·lua
TechMix12 小时前
【fkw学习笔记】Android 13 AOSP 源码添加系统预置应用实战指南
android·笔记·学习