Android VPN 应用逆向实战:从APK到节点配置提取

本文记录了一次对某VPN应用的完整逆向分析过程,从静态分析到实机动态调试,最终成功提取全部节点配置。

0. 前置条件

  • 真机: 已Root (Magisk)
  • ADB: 已配置好环境变量
  • 基本工具: strings、sqlite3、unzip

1. 静态分析:解包APK

APK本质是一个ZIP文件,直接解压查看内部结构:

bash
unzip -o -q base.apk -d apk_extracted

解压后目录结构:

code
apk_extracted/
├── AndroidManifest.xml    # 二进制格式,需要工具解析
├── classes.dex            # 主程序代码 (3.5MB)
├── lib/arm64-v8a/
│   ├── libbox.so          # sing-box 核心库 (44MB)
│   └── ...
├── assets/
│   ├── dashboard.html     # H5前端页面
│   ├── demo_nodes.json    # 节点配置模板
│   ├── h5/                # 前端资源
│   └── ...
└── resources.arsc         # 资源表

1.1 分析前端代码

这是一个混合开发应用,前端使用H5 + WebView。查看JavaScript文件可以了解业务逻辑:

bash
find apk_extracted/assets/h5 -name "*.js"

dashboard.js 中可以看到:

javascript
// 节点获取通过原生桥接
window.AndroidBridge.fetchNodesFromApi('handleApiNodes');

节点数据不在前端硬编码,而是通过原生层API动态获取。

1.2 提取API地址

classes.dex 中提取字符串:

bash
strings apk_extracted/classes.dex | grep -E "https?://[a-zA-Z0-9./:-]+"

找到关键信息:

code
https://appshouye.com:8888        # API基础地址
/api/v1/auth/login                # 登录接口
/api/v1/auth/register             # 注册接口
/api/v1/user/getSubscribe         # 订阅信息接口

1.3 提取硬编码节点

classes.dex 中发现一个硬编码的备用节点配置:

bash
strings apk_extracted/classes.dex | grep -E "\"server\"|\"uuid\"|\"server_name\""

找到一个VLESS节点,但这是降级用的备用节点,完整节点列表需要登录后从API获取。

1.4 确定技术栈

从分析中得出:

  • 开发语言: Kotlin
  • VPN核心: sing-box (libbox.so)
  • 前端: H5 + WebView
  • 包名: com.ailian.accelerator

2. 实机安装与登录

2.1 安装APK

bash
adb devices                    # 确认设备连接
adb install base.apk           # 安装应用

2.2 启动应用

bash
adb shell monkey -p com.ailian.accelerator -c android.intent.category.LAUNCHER 1

2.3 登录账号

在手机上完成登录操作。登录后,应用会从API拉取节点配置。

2.4 观察日志

bash
adb logcat | grep -i "NodeDataManager\|DashboardFragment"

登录成功后日志显示:

code
NodeDataManager: [新方式] 没有 token,无法获取订阅配置  # 登录前
DashboardFragment: 返回API节点: 83 个                  # 登录后

3. Root权限读取数据

3.1 确认Root权限

bash
adb shell "su -c 'id'"
# 输出: uid=0(root) gid=0(root) groups=0(root) context=u:r:magisk:s0

3.2 查找数据库文件

bash
adb shell "su -c 'ls -la /data/user/0/com.ailian.accelerator/databases/'"

发现关键数据库:

code
profiles.db      # VPN配置数据库
settings.db      # 应用设置数据库

3.3 分析数据库结构

bash
adb shell "su -c 'sqlite3 /data/user/0/com.ailian.accelerator/databases/profiles.db \".tables\"'"
# 输出: android_metadata  profiles  room_master_table

adb shell "su -c 'sqlite3 /data/user/0/com.ailian.accelerator/databases/profiles.db \".schema profiles\"'"
# 输出: CREATE TABLE `profiles` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
#        `userOrder` INTEGER NOT NULL, `name` TEXT NOT NULL, `typed` BLOB NOT NULL);

3.4 查询配置数据

bash
adb shell "su -c 'sqlite3 /data/user/0/com.ailian.accelerator/databases/profiles.db \"SELECT id, userOrder, name FROM profiles\"'"

输出:

code
1|0|全局模式
2|1|API配置

3.5 解析BLOB字段

typed 字段存储的是文件路径(UTF-16LE编码):

bash
adb shell "su -c 'sqlite3 /data/user/0/com.ailian.accelerator/databases/profiles.db \"SELECT hex(typed) FROM profiles WHERE id=2\"'"

将hex解码后得到文件路径:

code
/data/user/0/com.ailian.accelerator/files/configs/config_2.json

3.6 提取完整配置

bash
adb shell "su -c 'cat /data/user/0/com.ailian.accelerator/files/configs/config_2.json'" > nodes_config.json

这就是完整的 sing-box 配置文件,包含所有节点信息!

4. 配置文件分析

提取到的配置是一个标准的 sing-box JSON 配置,主要结构:

json
{
  "dns": { ... },           // DNS配置
  "inbounds": [ ... ],      // 入站代理 (TUN/SOCKS/混合)
  "outbounds": [ ... ],     // 出站节点列表 ← 这是我们要的
  "route": { ... }          // 路由规则
}

节点统计

地区数量协议服务器
香港22VLESS+WS / AnyTLSCloudflare CDN / 自建
台湾10AnyTLS自建服务器
新加坡10AnyTLS自建服务器
日本10AnyTLS自建服务器
美国30AnyTLS自建服务器

协议说明

  • VLESS + WebSocket + TLS: 通过Cloudflare CDN中转,伪装性好
  • AnyTLS: sing-box独有的TLS代理协议,性能更优

5. 总结

整个逆向流程:

code
APK解包 → 静态分析(找API地址)
安装到真机 → 登录账号(获取节点配置)
Root权限 → 读取数据库 → 找到配置文件路径
直接读取JSON配置文件 → 获得全部节点

关键发现:

  1. 应用使用 sing-box 作为VPN核心引擎
  2. 节点配置存储在本地数据库关联的JSON文件中
  3. 登录后,API返回的完整配置会被缓存到本地
  4. 有了Root权限,可以直接读取这些缓存文件

免责声明:本文仅用于安全研究和学习目的,请勿用于非法用途。

登录后发表评论

请先登录账号后再发表评论