DIVA 通关笔记 — Android 逆向漏洞方法论

核心理念只有一句话:

不读判断逻辑,找 API 特征。

每类漏洞都对应一组固定的 Android API。搜到 API,就确定了攻击方向;剩下的,只是确认路径。

本文按 DIVA 各关卡整理一套「API 特征驱动」的 Android 漏洞方法论,覆盖存储泄露、输入验证、访问控制和 Native 层问题。


Level 1 — Insecure Logging(日志泄露)

项目 内容
靶心 API Log.e() / Log.d() / Log.v() / Log.w()
数据终点 logcat(系统级环形缓冲区,所有 APP 共享)
发现手法 反编译搜 Log\.,或 strings classes.dex | grep -i "Log\."
攻击命令 adb logcat -d | grep <tag>,或直接 adb logcat -d dump 全部

平台特征: logcat 是 Android 的内核级日志设施,不区分进程。任何能执行 adb logcat 的人,都能读到所有 APP 写的日志。APP 自身 Log.i/d/e 调用的目标就是 logcat——这是平台决定的,不是开发者发明的。

逆向链路:

看到 Log.e("diva-log", ...) → 知道数据流向 logcat → adb logcat -d | grep diva-log 直接收数据。


Level 2 — Hardcoding Issues(硬编码凭证)

项目 内容
靶心 API String.equals("硬编码字面量") / setText("硬编码字面量")
数据终点 DEX 文件中的字符串常量池
发现手法 不要读 if-else 判断逻辑;用 strings classes.dex | grep -i "key|secret|password|api|vendor"
攻击命令 strings classes.dex | grep -i "vendor|secret"

为什么 strings 有效: Java 字面量编译后会进入 DEX 常量池,strings 直接从二进制提取所有可读字符串。任何硬编码字符串都会裸露出现,不需要理解控制流。

示例:

  • strings classes.dex | grep vendorvendorsecretkey
  • strings classes.dex | grep APIAPI Key: 123secretapikey123divap@ssword

Level 3 — Insecure Data Storage(不安全数据存储)

这一关包含 4 种存储方式,每种都对应不同的 Android 存储 API 和固定系统路径。

Part 1 — SharedPreferences

项目 内容
靶心 API PreferenceManager.getDefaultSharedPreferences() / getSharedPreferences()
存储路径 /data/data/<pkg>/shared_prefs/*.xml
存储格式 明文 XML
发现手法 SharedPreferencesgetDefaultSharedPreferences
攻击命令 cat /data/data/<pkg>/shared_prefs/*.xml

SharedPreferences 是 Android SDK 内置键值存储,写入位置由平台固定。看到这个 API,就知道数据一定在 shared_prefs/*.xml

逆向链路: 反编译看到 getDefaultSharedPreferences → 攻击路径确定 → 无需分析保存/读取逻辑细节。

Part 2 — SQLite 数据库

项目 内容
靶心 API openOrCreateDatabase() / SQLiteOpenHelper
存储路径 /data/data/<pkg>/databases/*
存储格式 SQLite 二进制,可用 stringssqlite3 读取
发现手法 openOrCreateDatabaseSQLiteOpenHelper
攻击命令 strings /data/data/<pkg>/databases/*,或 sqlite3 <db> "SELECT * FROM <table>"

与 Web 开发不同,Android 数据库路径是框架固化的:看到 openOrCreateDatabase("ids2", ...),攻击路径就是 /data/data/<pkg>/databases/ids2

Part 3 — 内部存储文件(无固定文件名)

项目 内容
靶心 API File.createTempFile() / FileWriter / FileOutputStream
存储路径 /data/data/<pkg>/ 内部,文件名由代码定义
发现手法 FileWriterFileOutputStreamcreateTempFiledataDir
攻击命令 先读代码确认文件名,再 strings /data/data/<pkg>/<file_name>*

这是开发者自定义文件操作,文件名不是平台固定的,必须看源码确认写到了哪个文件;但攻击方向(搜索文件 I/O API)是确定的。

一个实用技巧:

看到 File.createTempFile("prefix", "suffix", dir) + getApplicationInfo().dataDir
→ 搜索 strings /data/data/<pkg>/prefix*suffix

Part 4 — 外部存储文件

项目 内容
靶心 API Environment.getExternalStorageDirectory()
存储路径 /sdcard//storage/emulated/0/
发现手法 getExternalStorageDirectory / getExternalStoragePublicDirectory
攻击命令 ls -la /sdcard/,注意隐藏文件,再 cat /sdcard/<file>

外部存储是共享区域,无 root 也可能读。API 29+ 的 Scoped Storage 会加限制,但低版本和部分 ROM 仍较宽松。注意隐藏文件(如 .uinfo.txt)要用 ls -la 才能看到。


Level 7 — SQL Injection(SQL 注入)

项目 内容
靶心 API SQLiteDatabase.rawQuery() + 字符串拼接
攻击面 搜索输入框,输入拼进 SQL
数据终点 /data/data/<pkg>/databases/*
发现手法 rawQuery / execSQL,看参数是否用 + 拼接
攻击命令 搜索框输入 ' OR 1=1 --;或用 sqlite3 直接查库验证

Android 版 SQL 注入和 Web 版本质一样:用户输入拼进查询语句。区别只在于 Android 用 rawQuery(),数据库是本地 SQLite。

通用 payload:

1
' OR 1=1 --

漏洞确认后,建议直接 dump 数据库对比:

1
run-as <pkg> sqlite3 /data/data/<pkg>/databases/<db> "SELECT * FROM <table>"

Level 8 — URI Scheme Bypass(URL Scheme 绕过)

项目 内容
靶心 API WebView.loadUrl() 无 scheme 白名单
攻击面 URL 输入框 → WebView 加载
发现手法 loadUrl / WebView,看是否有 scheme 校验
攻击命令 输入 file:///etc/hostscontent://<provider_uri>

loadUrl() 若接受任意 URL scheme,http://file://content:// 都会进来。开发者只想着加载网页,却可能没拦住本地文件读取,也没禁止访问 ContentProvider。

常见攻击:

  1. file:///etc/hosts → 读系统敏感文件
  2. content://jakhar.aseem.diva.provider.notesprovider/notes → 跨 Provider 数据泄露

这关还要和 Level 11 联动:如果 APP 有 exported ContentProvider,loadUrl("content://...") 可以绕过 PIN 等前端保护直接访问 Provider 数据。


Level 9 — Exported Activity(导出组件 — Activity)

项目 内容
靶心 API Manifest 中 <intent-filter>android:exported="true"(隐式默认)
攻击面 带 intent-filter 的 Activity 可被外部调用
发现手法 读 Manifest,或 adb shell dumpsys package <pkg>
攻击命令 adb shell am start -n <pkg>/<Activity>

Android 四大组件里,一旦声明 <intent-filter>android:exported 默认就会变成 true,意味着外部可通过 am start 或 Intent 调用它。很多开发者并不清楚这个默认行为。

攻击流程(API 特征法):

  1. 不先读 APICredsActivity 的业务代码
  2. Manifest 看到它有 <intent-filter action="jakhar.aseem.diva.action.VIEW_CREDS"/>
  3. 执行:adb shell am start -n jakhar.aseem.diva/.APICredsActivity
  4. 直接显示 API Key

Activity 名本身(APICredsActivity)往往就是线索——不一定要先看代码。


Level 10 — Intent Data Forging(Intent 数据伪造)

项目 内容
靶心 API Intent.getBooleanExtra() 信任客户端传入的值
攻击面 Intent extra 由发送方控制,不可信
发现手法 getBooleanExtra / getStringExtra,看是否用于权限判断
攻击命令 正常流程中不勾选 RadioButton → 绕过 PIN 检查

Intent extras 完全由调用方设置,就像 HTTP 请求参数由客户端控制。把 putExtra("chk_pin", false) 当作安全校验,等价于把 ?is_admin=true 放在 URL 参数里。

常见漏洞模式:

1
2
3
4
5
6
7
8
// Activity A
i.putExtra("chk_pin", chk_pin); // 来自客户端 RadioButton

// Activity B
boolean bcheck = i.getBooleanExtra("chk_pin", true);
if (!bcheck) {
showCredentials();
}

攻击思路:

  1. Manifest 看到 APICreds2Activity 无 intent-filter → 只能内部调用
  2. 反编译搜 startActivity,找到调用方 AccessControl2Activity
  3. 看到 getBooleanExtra("chk_pin") → 权限判断依赖 Intent 数据
  4. 追溯到 RadioButton → 不勾选即可绕过

补充:getBooleanExtra("chk_pin", true) 默认值是 true。有时直接构造 --ez chk_pin false 会因传递问题不稳定,但 UI 流程(不勾选)通常更稳。


Level 11 — Exported ContentProvider(导出组件 — ContentProvider)

项目 内容
靶心 API Manifest <provider android:exported="true"> + ContentProvider.query()
攻击面 ContentProvider 是跨 APP 数据共享接口
发现手法 读 Manifest,找 exported="true" 的 provider
攻击命令 adb shell content query --uri content://<authority>/<path>

ContentProvider 是 Android 特有 IPC 组件。content 命令是系统提供的命令行工具,标准 Linux 没有它。

关键特征:

  • Activity 层有 PIN 保护,看起来安全
  • Provider 自身没有认证,NotesProvider.query() 直接返回数据
  • 前端防御 ≠ 后端防御

攻击链路:

  1. Manifest 看到
    android:exported="true"
    authorities="jakhar.aseem.diva.provider.notesprovider"
  2. 执行:
1
adb shell content query --uri content://jakhar.aseem.diva.provider.notesprovider/notes
  1. 私密笔记直接返回,完全绕过 PIN

而且不仅能读:content insert / update / delete 同样可用。


Level 12 — Native Hardcoding(Native 层硬编码)

项目 内容
靶心 API System.loadLibrary() + .so 中的 strcmp / 字符串常量
数据终点 ELF 的 .rodata 段(不是 DEX 常量池)
发现手法 strings lib/*/lib*.so | grep -i "key|secret|password"
攻击命令 unzip -p app.apk lib/*/lib*.so | strings

和 Level 2 的区别只是存放位置:

  • Level 2:密钥在 DEX(Java)层
  • Level 12:密钥被搬到 Native(.so)层

开发者常以为编译成 .so 就等于“加密”,但 ELF 的 .rodata 仍然明文存放字符串常量,strings 照读不误。

1
2
unzip -p DivaApplication.apk lib/x86/libdivajni.so | strings | grep -i key
# → olsdfgad;lh

strings 是万能钥匙: 不管是 DEX、ELF、资源文件还是数据库,它都能抽出可读字符串。


Level 13 — Native Buffer Overflow(Native 层缓冲区溢出)

项目 内容
靶心 API strcpy() 无长度检查
攻击面 用户输入 → JNI → Native strcpy → 栈溢出
发现手法 strcpy / strcat / gets / sprintf
攻击命令 输入 500+ 字符 → 观察 SIGSEGV

Java 本身没有缓冲区溢出,但 JNI 把参数送到 C 层后,安全边界就断了。

不读 C 源码也能确认漏洞:

  1. 反编译看到 JNI 方法接收用户输入
  2. 输入超长字符串
  3. logcat 出现 SIGSEGV,且 crash 地址落在 libdivajni.so
  4. 即可确认缓冲区溢出
1
2
Fatal signal 11 (SIGSEGV), code 128 (SI_KERNEL), fault addr 0x0
in tid 7615 ... libdivajni.so (Java_jakhar_aseem_diva_DivaJni_initiateLaunchSequence+67)

在 DIVA 这类靶场里,PoC 通常是 DoS;真实场景中,精确控制溢出可进一步劫持控制流做 ROP。


方法论总结

Level 7–13 速查

关卡 漏洞类型 靶心 API
Level 7 SQL 注入 rawQuery() + 字符串拼接
Level 8 URL Scheme 绕过 WebView.loadUrl() 无 scheme 校验
Level 9 导出 Activity <intent-filter> → exported=true
Level 10 Intent 数据伪造 getBooleanExtra() 信任客户端
Level 11 导出 ContentProvider <provider exported="true">
Level 12 Native 硬编码 System.loadLibrary() + .so 字符串
Level 13 Native 缓冲区溢出 strcpy() 无边界检查

Level 1–13 分类

  • 存储类:Level 1(logcat)/ Level 2(DEX 常量池)/ Level 3(SharedPrefs、SQLite、文件)/ Level 12(.so.rodata
  • 输入验证类:Level 7(SQL 注入)/ Level 8(URL Scheme)/ Level 13(Buffer Overflow)
  • 访问控制类:Level 9(导出 Activity)/ Level 10(Intent 伪造)/ Level 11(导出 ContentProvider)

贯穿 13 关的核心原则

  1. 搜 API,不读逻辑 — 每类漏洞都有固定 Android API 特征
  2. strings 是万能钥匙 — DEX / ELF / DB 都能抽字符串
  3. Manifest 是第一入口 — 导出组件一览无余
  4. 平台特征决定攻击路径 — 存储位置、IPC、导出规则由 Android 固化
  5. JNI 打破 Java 安全边界 — Native 引入 C 类漏洞分析方法
  6. 先搜 strings,再反编译 — 命中方向 → 定位代码 → 构造攻击,效率最高

写给以后的自己:

Android 漏洞分析最省时间的路径,往往不是“先读懂业务”,而是“先识别平台 API 暴露的攻击面”。DIVA 这 13 关,本质就是在反复练习这件事。


DIVA 通关笔记 — Android 逆向漏洞方法论
http://82.156.189.140:8085/2026/07/18/DIVA-通关笔记/
作者
fortuneh2c
发布于
2026年7月18日
许可协议