本章目标是在不依赖运行时 Hook 的情况下,通过 APK 文件、Manifest、资源、DEX 和 Smali 理解 App 行为,并在自建授权 Demo 上验证"客户端本地判断不可信"。
1. 静态逆向的工作边界
静态逆向不是"打开 jadx 看一眼源码"。完整静态分析应回答:
| 问题 | 证据来源 | 输出 |
|---|---|---|
| App 是谁 | 包名、版本、签名、渠道、targetSdk | 基本信息表 |
| 入口在哪里 | Manifest、launcher Activity、Deep Link | 页面入口图 |
| 权限和组件是否危险 | Manifest、Provider、Receiver、Service | 暴露面清单 |
| 关键逻辑在哪里 | DEX、字符串、调用链、资源 ID | 关键类和方法列表 |
| 是否有 Native 逻辑 | lib/、System.loadLibrary |
so 清单 |
| 是否可被重打包 | apktool、apksigner、安装结果 | 重打包验证记录 |
静态分析的结论必须能被后续动态分析验证。只凭反编译代码下结论,容易误判混淆、反射、动态下发和服务端逻辑。
2. APK 初步拆解
2.1 基本信息提取
bash
mkdir -p case-reversedemo/02-static
cp app-debug.apk case-reversedemo/02-static/
unzip -l app-debug.apk > case-reversedemo/02-static/01-zip-list.txt
apkanalyzer manifest application-id app-debug.apk
apkanalyzer manifest version-name app-debug.apk
apkanalyzer manifest target-sdk app-debug.apk
apksigner verify --print-certs --verbose app-debug.apk
如果没有 apkanalyzer,可以先用 apktool:
bash
apktool d -f app-debug.apk -o decoded-debug
sed -n '1,220p' decoded-debug/AndroidManifest.xml
2.2 文件结构解释
| 检查点 | 怎么看 | 风险判断 |
|---|---|---|
classes.dex 数量 |
`unzip -l app.apk | grep classes` |
lib/ ABI |
`unzip -l app.apk | grep "lib/"` |
assets/ 内容 |
`unzip -l app.apk | grep assets` |
res/raw 内容 |
apktool 解码后查看 | 证书、公钥、配置可能硬编码 |
META-INF 或签名块 |
apksigner verify --verbose |
判断签名方案和证书信息 |
3. Manifest 分析
Manifest 是静态分析的第一张地图。重点不是把每一行抄下来,而是判断每个声明是否扩大了攻击面。
3.1 必查字段
| 字段 | 知识点讲解 | Demo 检查 |
|---|---|---|
package / namespace |
包名是 adb、Frida、日志过滤、启动组件的基础 | 记录 com.example.reversedemo |
android:debuggable |
正式包如果可调试,会降低攻击门槛 | release 必须为 false 或不存在 |
android:allowBackup |
允许备份可能导致数据被导出 | 敏感 App 应关闭或配置规则 |
android:usesCleartextTraffic |
允许 HTTP 明文传输 | 正式业务应避免明文 |
android:exported |
组件是否可被外部 App 调用 | 非必要组件应为 false |
intent-filter |
Deep Link、分享入口、系统广播入口 | 参数必须做鉴权和校验 |
provider authorities |
ContentProvider 的访问入口 | 导出时必须权限控制 |
3.2 Demo:导出 Activity 检查
假设 Demo 中有一个测试页:
xml
<activity
android:name=".DebugPanelActivity"
android:exported="true">
<intent-filter>
<action android:name="com.example.reversedemo.OPEN_DEBUG" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
验证命令:
bash
adb shell am start -a com.example.reversedemo.OPEN_DEBUG
风险解释:
- 如果未登录也能打开 DebugPanel,说明组件导出缺少业务鉴权。
- 如果页面显示 token、环境、配置,说明内部调试能力泄露。
- 修复方式不是"隐藏按钮",而是关闭导出或在入口处做权限和登录态校验。
4. jadx 代码分析
4.1 检索策略
不要只搜索一个关键词。建议按主题分组检索:
| 主题 | 关键词 |
|---|---|
| 登录认证 | login、password、token、Authorization、Bearer |
| 会员权益 | vip、premium、isVip、member、pay |
| 签名加密 | sign、signature、Hmac、SHA、AES、RSA |
| 环境检测 | root、su、debug、proxy、emulator、frida |
| 网络请求 | OkHttpClient、Retrofit、Interceptor、baseUrl |
| 本地存储 | SharedPreferences、SQLite、Room、DataStore |
| WebView | addJavascriptInterface、setJavaScriptEnabled、loadUrl |
4.2 Demo:定位本地会员判断
在 jadx 中搜索:
text
isVip
vip-video
free-video
找到类似代码:
java
public final boolean isVip(String userId) {
return Intrinsics.areEqual(userId, "10001");
}
知识点:
- Java/Kotlin 反编译结果是近似源码,不是原始源码。
- Kotlin 的
object、伴生对象、默认参数、协程会生成额外类和方法。 - 如果开启混淆,类名可能变成
a.b.c,但字符串、调用行为、资源 ID 仍可作为线索。
输出记录:
markdown
## 会员判断定位
- 类:`com.example.reversedemo.UserCenter`
- 方法:`isVip(String userId): Boolean`
- 证据:反编译代码显示只比较本地字符串 `10001`
- 风险:会员权益依赖客户端判断,可能被 Smali 修改或 Hook
- 后续验证:修改 Smali 返回 true,观察非会员是否进入会员功能
5. apktool 与 Smali 基础
5.1 反编译与目录结构
bash
apktool d -f app-debug.apk -o decoded-debug
find decoded-debug -maxdepth 2 -type d | sort
常见目录:
| 目录 | 说明 |
|---|---|
smali/、smali_classes2/ |
DEX 转换后的 Smali 代码 |
res/ |
解码后的资源 |
AndroidManifest.xml |
解码后的 Manifest |
apktool.yml |
反编译元信息 |
5.2 Smali 语法最小知识
| 语法 | 含义 |
|---|---|
.class |
类定义 |
.super |
父类 |
.method / .end method |
方法开始和结束 |
.locals |
本地寄存器数量 |
const/4 v0, 0x1 |
给寄存器赋小整数 |
invoke-virtual |
调用实例方法 |
invoke-static |
调用静态方法 |
move-result |
接收上一次调用返回值 |
if-eqz / if-nez |
条件跳转 |
return v0 |
返回寄存器值 |
Smali 修改要克制。学习目标是验证风险,不是把业务逻辑改得不可维护。
6. Demo:修改 isVip() 返回值
6.1 定位 Smali 文件
bash
grep -R "isVip" -n decoded-debug/smali*
可能找到:
text
decoded-debug/smali/com/example/reversedemo/UserCenter.smali
原始片段可能类似:
smali
.method public final isVip(Ljava/lang/String;)Z
.locals 1
const-string v0, "10001"
invoke-static {p1, v0}, Lkotlin/jvm/internal/Intrinsics;->areEqual(Ljava/lang/Object;Ljava/lang/Object;)Z
move-result v0
return v0
.end method
修改成固定返回 true:
smali
.method public final isVip(Ljava/lang/String;)Z
.locals 1
const/4 v0, 0x1
return v0
.end method
6.2 重打包
bash
apktool b decoded-debug -o reversedemo-vip-patched-unsigned.apk
keytool -genkeypair \
-alias reverse-demo \
-keyalg RSA \
-keysize 2048 \
-validity 3650 \
-keystore reverse-demo.jks
apksigner sign \
--ks reverse-demo.jks \
--ks-key-alias reverse-demo \
--out reversedemo-vip-patched.apk \
reversedemo-vip-patched-unsigned.apk
apksigner verify --verbose reversedemo-vip-patched.apk
adb install -r reversedemo-vip-patched.apk
6.3 验证
| 验证项 | 方法 | 通过标准 |
|---|---|---|
| APK 可安装 | adb install -r |
显示 Success |
| 签名有效 | apksigner verify --verbose |
验证通过 |
| 行为改变 | 用非会员账号打开会员功能 | 原本不可进入,现在可进入 |
| 证据完整 | 截图、命令、修改前后代码 | 可复现 |
结论写法:
markdown
本地 `isVip()` 返回值被 Smali 修改后,非会员账号可以进入 Demo 的会员功能。
该风险说明核心权益不能依赖客户端布尔判断,必须由服务端按登录态、订单状态和风控结果做最终授权。
7. 静态分析常见风险
| 风险 | 静态证据 | 影响 | 修复方向 |
|---|---|---|---|
| 硬编码密钥 | jadx 搜到 SECRET、AES_KEY |
签名或加密可被复现 | 密钥服务端化,客户端只保留短期凭证 |
| 本地会员判断 | isVip()、premium 直接返回 |
权益可能被绕过 | 服务端授权,客户端只展示结果 |
| 组件导出 | exported=true 且无权限 |
外部可拉起敏感页面 | 关闭导出或加权限、登录态校验 |
| 明文传输 | usesCleartextTraffic=true |
请求可被窃听篡改 | HTTPS、HSTS、证书校验 |
| WebView 暴露 | addJavascriptInterface |
JSBridge 可能被滥用 | 限制域名、最小接口、参数校验 |
| 日志泄露 | Log.d 输出 token |
敏感数据泄露 | release 删除敏感日志 |
| 备份开启 | allowBackup=true |
私有数据可能被备份 | 关闭或配置备份规则 |
8. 本章 Demo:完整静态分析任务
8.1 任务清单
- 提取 APK 基本信息、签名、包名、版本。
- 分析 Manifest,列出导出组件和敏感权限。
- 用 jadx 找到登录、会员、签名、环境检测、网络请求相关代码。
- 用 apktool 找到
isVip()对应 Smali。 - 修改 Smali 并重打包签名。
- 安装修改后的 APK,验证行为变化。
- 输出静态分析报告。
8.2 报告模板
markdown
# Static Analysis Report
## APK 信息
- 包名:
- 版本:
- targetSdk:
- 签名摘要:
## Manifest 风险
| 组件 | exported | 权限 | 风险 | 建议 |
## 关键逻辑定位
| 逻辑 | 类/方法 | 证据 | 后续验证 |
## Smali 修改验证
- 修改目标:
- 修改前:
- 修改后:
- 重打包命令:
- 安装结果:
- 行为变化:
## 结论
- 已验证风险:
- 需要动态验证的项:
- 修复建议:
9. 常见问题
| 问题 | 原因 | 处理 |
|---|---|---|
| jadx 代码看不懂 | 混淆、Kotlin 语法、反射 | 结合字符串、调用链、动态日志 |
| apktool 编译失败 | 资源异常、工具版本不兼容 | 升级 apktool,保留报错,尝试 --use-aapt2 |
| 重签名后无法安装 | 原包签名不同或版本降级 | 卸载原包,检查 versionCode |
| 修改后闪退 | 寄存器数量、类型、控制流错误 | 对比 Smali,查看 logcat 崩溃栈 |
| 行为没变化 | 修改点不是实际调用点 | 用动态 Hook 或日志验证调用链 |
10. 本章交付物
text
case-reversedemo/
02-static/
01-zip-list.txt
02-manifest.md
03-key-methods.md
04-smali-before.smali
05-smali-after.smali
06-build-sign-install.txt
07-static-report.md
11. 静态分析方法论
11.1 静态分析的四层证据
静态逆向不能只依赖一个工具输出。建议把证据分成四层:
| 层级 | 证据 | 工具 | 适合回答的问题 |
|---|---|---|---|
| 包结构层 | APK 文件列表、签名、Manifest | unzip、apksigner、apktool |
这个 App 有什么组件和资源 |
| 字节码层 | DEX、Smali、近似源码 | jadx、apktool | 业务逻辑在哪里 |
| 资源层 | 字符串、布局、raw、assets | apktool、jadx | 页面、配置、证书、隐藏入口在哪里 |
| 关联层 | 调用链、字符串引用、类关系 | jadx、IDE 全文搜索 | 某个风险点如何被触发 |
每个结论至少要有两个证据来源。例如"会员判断在客户端"最好同时有 jadx 代码、Smali 位置、动态行为验证。
11.2 静态分析流程详解
text
APK 基本信息
-> Manifest 暴露面
-> 资源和字符串线索
-> Java/Kotlin 调用链
-> Smali 可修改点
-> Native 入口线索
-> 风险假设
-> 动态验证计划
每一步要输出文档,不要靠记忆推进:
| 阶段 | 输出文件 | 内容 |
|---|---|---|
| 基本信息 | apk-basic.md |
包名、版本、签名、hash、targetSdk |
| Manifest | manifest-audit.md |
权限、组件、导出状态、Deep Link |
| 字符串 | string-clues.md |
URL、密钥、错误文案、开关 |
| 代码 | code-map.md |
关键类、方法、调用链 |
| Smali | smali-targets.md |
可验证修改点 |
| 风险 | risk-hypothesis.md |
风险假设和验证计划 |
12. Manifest 审计扩展
12.1 application 级别字段
| 字段 | 风险解释 | Demo 验证 | 修复建议 |
|---|---|---|---|
android:debuggable |
可被调试器附加,降低动态分析门槛 | release 检查是否为 false | 正式包关闭 |
android:allowBackup |
可能导出私有数据 | adb backup 或配置检查 |
敏感 App 关闭或配置规则 |
android:usesCleartextTraffic |
允许 HTTP 明文请求 | 抓包检查 HTTP | 正式环境禁用 |
android:networkSecurityConfig |
决定信任哪些 CA | 查看 XML | debug/release 分离 |
android:extractNativeLibs |
so 是否解压到文件系统 | 查看安装目录 | 了解 Native 分析路径 |
android:requestLegacyExternalStorage |
旧外部存储模型 | 检查数据落盘位置 | 最小化外部存储 |
12.2 Activity 审计
| 检查项 | 说明 | 命令 |
|---|---|---|
| launcher Activity | App 默认入口 | dumpsys package |
| exported Activity | 可被外部启动 | adb shell am start -n |
| Deep Link | URL 可拉起页面 | adb shell am start -a VIEW -d |
| taskAffinity | 任务栈劫持相关 | Manifest 检查 |
| excludeFromRecents | 是否隐藏任务 | Manifest 检查 |
导出页面验证模板:
markdown
## Activity 导出验证
- 组件:
- exported:
- 触发命令:
- 未登录是否可访问:
- 参数校验:
- 敏感数据:
- 结论:
12.3 Service 审计
Service 风险通常来自外部 Intent 触发后台逻辑。
检查点:
- 是否
exported=true。 - 是否声明权限。
- 是否处理外部传入 action。
- 是否启动下载、同步、支付、推送、清理等任务。
- 是否能被重复触发导致资源消耗。
验证命令:
bash
adb shell am startservice -n com.example.reversedemo/.SyncService
adb shell am start-foreground-service -n com.example.reversedemo/.SyncService
12.4 Receiver 审计
Receiver 重点看广播是否可伪造。
bash
adb shell am broadcast -a com.example.reversedemo.DEBUG_ACTION --es cmd dump
风险判断:
- 广播触发敏感动作。
- action 未做权限保护。
- 参数直接参与文件、网络或账号操作。
- Receiver 在锁屏、后台也能触发。
12.5 Provider 审计
Provider 是数据泄露高风险点。
bash
adb shell content query --uri content://com.example.reversedemo.provider/user
adb shell content insert --uri content://com.example.reversedemo.provider/user --bind name:s:test
检查:
- 是否导出。
- 是否有 read/write 权限。
- authorities 是否可预测。
- SQL 查询是否可注入。
- 返回字段是否包含 token、手机号、身份证、内部 ID。
13. jadx 深度分析
13.1 关键词字典
| 类型 | 关键词 |
|---|---|
| 登录 | login、auth、token、session、refresh |
| 会员 | vip、member、premium、subscribe、entitlement |
| 支付 | pay、order、purchase、receipt、billing |
| 签名 | sign、signature、hmac、sha256、nonce |
| 加密 | aes、rsa、cipher、encrypt、decrypt |
| 风控 | risk、root、debug、proxy、emulator |
| Hook 检测 | frida、xposed、substrate、ptrace |
| WebView | webview、javascriptinterface、loadUrl |
| 存储 | sharedpreferences、sqlite、datastore、keystore |
| 网络 | okhttp、retrofit、interceptor、baseUrl |
13.2 调用链记录方法
不要只记录"找到了方法"。要记录它从哪里来、到哪里去。
markdown
## 调用链:会员功能
1. `VipActivity.onCreate()`
2. `VipViewModel.loadVipStatus()`
3. `UserCenter.isVip(userId)`
4. `ApiClient.getVipResource(token)`
判断:
- UI 展示依赖本地 `isVip`。
- 真实资源是否依赖服务端 `getVipResource` 还需抓包验证。
13.3 反编译误差识别
| 现象 | 说明 | 处理 |
|---|---|---|
代码出现 throw new UnsupportedOperationException |
jadx 反编译失败 | 看 Smali 或换 CFR/FernFlower |
| Kotlin 代码非常啰嗦 | 编译器生成空安全和默认参数逻辑 | 回到业务语义,不纠结每行 |
| 变量名无意义 | 混淆或无调试信息 | 用字符串和调用关系命名 |
| 方法很多但无引用 | 反射或动态注册 | 搜索字符串和运行时 Hook |
| 资源 ID 不直观 | 编译后的整数 ID | 对照 R 类和 resources.arsc |
14. Smali 深入
14.1 寄存器模型
Smali 方法里的寄存器分为:
| 类型 | 说明 |
|---|---|
v0、v1 |
本地寄存器 |
p0 |
实例方法中的 this |
p1、p2 |
方法参数 |
.locals |
本地寄存器数量 |
.registers |
总寄存器数量 |
修改 Smali 时最常见错误是寄存器数量不够、类型不匹配、返回类型错误。
14.2 常见返回类型
| Java 类型 | Smali 返回 | 示例 |
|---|---|---|
boolean |
Z |
return v0,v0 为 0x0 或 0x1 |
int |
I |
const/4 v0, 0x1 |
long |
J |
使用宽寄存器 |
String |
Ljava/lang/String; |
const-string v0, "demo" |
void |
V |
return-void |
14.3 条件跳转理解
| 指令 | 含义 |
|---|---|
if-eqz v0, :label |
v0 等于 0 跳转 |
if-nez v0, :label |
v0 不等于 0 跳转 |
if-eq v0, v1, :label |
两寄存器相等跳转 |
if-ne v0, v1, :label |
两寄存器不相等跳转 |
goto :label |
无条件跳转 |
Demo 练习:
- 找到
if-eqz会员判断。 - 只改跳转方向,不改返回值。
- 重打包后观察页面是否变化。
- 对比"改返回值"和"改跳转"的差异。
14.4 插入日志示例
Smali 插日志复杂度高,适合进阶练习。更推荐先用 Frida 打印参数。如果必须插入日志,要注意寄存器和方法签名:
smali
const-string v0, "ReverseDemo"
const-string v1, "isVip called"
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
插入日志后要检查:
.locals是否足够。- 返回值是否被覆盖。
- 日志是否只用于 debug,不进入 release。
15. 重打包和签名
15.1 APK 签名知识
| 签名方案 | 说明 | 检查 |
|---|---|---|
| V1 | JAR 签名,兼容老版本 | apksigner verify --verbose |
| V2 | APK Signature Scheme v2 | Android 7+ |
| V3 | 支持密钥轮换 | Android 9+ |
| V4 | 增量安装相关 | 新版本设备 |
重签名会改变证书摘要。任何依赖原签名的逻辑都可能失败,例如登录、SDK 初始化、服务端签名白名单。
15.2 签名冲突处理
| 错误 | 原因 | 处理 |
|---|---|---|
INSTALL_FAILED_UPDATE_INCOMPATIBLE |
已安装同包名不同签名 | 卸载原 App |
INSTALL_FAILED_VERSION_DOWNGRADE |
versionCode 降级 | 提高 versionCode 或卸载 |
INSTALL_PARSE_FAILED_NO_CERTIFICATES |
未签名或签名损坏 | 重新签名 |
App not installed |
ABI、SDK、签名、包名冲突 | 查看 adb install 详细错误 |
15.3 重打包实验记录
markdown
## 重打包记录
- 原 APK SHA256:
- apktool 版本:
- 修改文件:
- 修改说明:
- 构建命令:
- 签名证书:
- 安装结果:
- 行为变化:
- 崩溃日志:
16. 静态风险库
| 风险编号 | 风险 | 证据 | 验证方式 | 修复 |
|---|---|---|---|---|
| S-001 | Debuggable 开启 | Manifest | dumpsys package |
release 关闭 |
| S-002 | allowBackup 开启 | Manifest | 备份或配置检查 | 关闭或配置规则 |
| S-003 | 导出 Activity | Manifest | am start |
关闭导出或鉴权 |
| S-004 | 导出 Provider | Manifest | content query |
权限控制 |
| S-005 | Deep Link 未鉴权 | intent-filter | am start -d |
登录态和参数校验 |
| S-006 | 硬编码密钥 | jadx 字符串 | Hook 签名函数 | 服务端密钥 |
| S-007 | 本地会员判断 | jadx/Smali | Smali/Frida | 服务端授权 |
| S-008 | 明文 HTTP | Manifest/代码 | 抓包 | HTTPS |
| S-009 | 敏感日志 | Log.d |
logcat | release 移除 |
| S-010 | WebView 暴露 | addJavascriptInterface |
页面测试 | 域名白名单 |
| S-011 | 不安全存储 | SharedPreferences | 文件检查 | Keystore/最小化 |
| S-012 | 弱加密模式 | AES/ECB | 代码审计 | AEAD/GCM |
| S-013 | 无重放防护 | 签名缺 nonce | 接口重放 | nonce 和过期时间 |
| S-014 | Native 单点检测 | nativeCheck |
Hook | 风险评分 |
| S-015 | 混淆缺失 | release 类名清晰 | jadx 对比 | R8/ProGuard |
17. 完整 Demo 作业
17.1 作业目标
完成一次从静态分析到可复现验证的闭环:
- 找出 Demo 中 5 个静态风险点。
- 至少选择 2 个风险点做 Smali 或命令验证。
- 对每个风险点写"证据、影响、验证、修复"。
- 输出
static-risk-report.md。
17.2 评分表
| 项目 | 分值 | 要求 |
|---|---|---|
| 基本信息完整 | 10 | 包名、版本、签名、hash |
| Manifest 审计 | 20 | 覆盖四大组件和 application 字段 |
| 代码定位 | 20 | 至少 5 个关键方法 |
| Smali 验证 | 20 | 至少 1 个可运行修改 |
| 风险表达 | 20 | 影响和修复写清楚 |
| 证据归档 | 10 | 文件命名规范 |
18. 静态逆向与 Smali
静态逆向的重点是证据链:从包结构、Manifest、资源、DEX、Smali 到重打包验证,逐步证明风险是否存在。
静态分析核心
| 知识点 | 核心理解 | Demo/验证 | 常见误区 |
|---|---|---|---|
| 证据等级 | Manifest、Smali、签名信息更接近真实产物;jadx 是近似源码。 | 同一方法同时记录 jadx 和 Smali 证据。 | 只凭反编译 Java 下结论。 |
| 调用链 | 风险点是否成立取决于入口、参数来源、校验和最终行为。 | 从 VipActivity 追到 UserCenter.isVip()。 |
找到危险函数但不证明可触发。 |
| 字符串线索 | URL、token、sign、错误文案和检测路径常在混淆后保留。 | 搜索 vip、sign、/api/。 |
字符串命中就直接判漏洞。 |
| 资源分析 | 布局、raw、assets、证书文件可能暴露隐藏入口和配置。 | 用 apktool 查看 res/xml 和 assets。 |
只看代码,不看资源文件。 |
| 多 DEX | 大型 App 可能有多个 classes*.dex,检索必须覆盖全部。 |
检查 APK 中 classes2.dex 等文件。 |
只分析第一个 DEX。 |
| 混淆判断 | 混淆改变名字,不改变所有字符串和运行行为。 | 对比 debug/release jadx 输出。 | 看到类名短就停止分析。 |
| 反射线索 | 反射会打断静态调用链,但字符串和动态 Hook 可补齐。 | 搜索 Class.forName、getMethod。 |
调用链断了就认为不可达。 |
| 动态加载 | DexClassLoader、PathClassLoader 可能加载插件或二级 dex。 | 搜索 DexClassLoader、loadClass。 |
只分析 base APK。 |
Manifest 与组件
| 知识点 | 核心理解 | Demo/验证 | 常见误区 |
|---|---|---|---|
| 导出 Activity | 外部可启动页面,风险取决于是否绕过鉴权或展示敏感信息。 | 用 am start 打开 Debug 页面。 |
导出就一律判高危。 |
| 导出 Service | 外部可触发后台任务,可能导致越权或资源消耗。 | 用 am startservice 验证。 |
只看 Activity,忽略 Service。 |
| 导出 Receiver | 外部广播可触发逻辑,参数可能被伪造。 | 用 am broadcast 构造 action。 |
认为广播只能系统发送。 |
| ContentProvider | Provider 可能直接暴露本地数据。 | 用 content query 验证 URI。 |
忽略 authorities 和权限配置。 |
| Deep Link | URL 可从浏览器或其他 App 拉起页面。 | 构造 reversedemo://open/vip。 |
只验证打开,不验证参数和鉴权。 |
| allowBackup | 可能影响私有数据备份暴露。 | 检查 Manifest 和备份规则。 | 不区分普通 App 和敏感 App 场景。 |
Smali 与重打包
| 知识点 | 核心理解 | Demo/验证 | 常见误区 |
|---|---|---|---|
| 寄存器 | Smali 使用寄存器传参和保存局部变量。 | 分析 p0、p1、v0。 |
随意加指令不调整 locals。 |
| 返回类型 | 方法签名决定返回值类型,boolean、String、void 不能混用。 | 修改 ()Z 返回 true。 |
返回类型错导致崩溃。 |
| 条件跳转 | if-eqz、if-nez 控制分支。 |
反转会员判断分支。 | 不知道原条件含义就盲改。 |
| 方法调用 | invoke-virtual、invoke-static 等表示调用类型。 |
定位签名函数调用。 | 只改被调用函数,不确认调用点。 |
| 重打包 | apktool 重建 APK 后必须签名才能安装。 | 修改 Smali 后 apktool b。 |
构建成功就认为行为已改变。 |
| 签名冲突 | 不同证书不能覆盖安装同包名 App。 | 处理 INSTALL_FAILED_UPDATE_INCOMPATIBLE。 |
把安装失败误判为防护成功。 |
| 完整性影响 | 重签名可能触发签名校验或 SDK 校验。 | 对比原包和重签名包行为。 | 只看本地安装,不测关键接口。 |