安卓 App 接口逆向:从零到拿到直播源(小白实战教程)---成都橙视 cditv 平台

安卓 App 接口逆向:从零到拿到直播源(小白实战教程)

用你这次真实做过的「金温江 / 金牛」当主线,一步步带你复现。
目标:读完这份文档,你能独立还原一个 App 的接口签名,拿到它的数据。


0. 先建立全局观:到底在干什么?

一个 App 请求服务器数据时,会在请求里带上「签名」(一串看不懂的加密字符串)。服务器验证签名正确才返回数据。

我们要做的,就是搞清楚这串签名是怎么算出来的,然后自己写代码(PHP/Python)伪造出合法请求,绕过 App 直接拿数据。

整个流程就 7 步

① 抓包找接口  →  ② 判断加固  →  ③ 脱壳  →  ④ 反编译找关键代码
      ↓
⑤ hook 动态验证  →  ⑥ 还原算法并交叉验证  →  ⑦ 写代码交付

核心心法:第 ④ 步"看出来的算法"不一定是真的,必须靠第 ⑤⑥ 步用真实抓包数据验证。这一步是灵魂,做对了才算真正还原。


1. 先把这些词搞懂(术语速览)

大白话解释
APK 安卓安装包,本质是个 ZIP 压缩包
DEX 安卓 App 的"代码文件",Java 编译后的产物
加固 厂商给 App 套了层壳,把真实代码加密藏起来,防止你直接看
脱壳 把加固壳扒掉,还原出真实代码
抓包 截获 App 和服务器之间传输的请求/响应
接口 / API App 请求数据用的网址(如 https://api.xxx.cn/cms/show/...
Header 请求头,附带在请求里的信息(UA、签名、密钥等)
签名 / sign 那串加密字符串,服务器用来验证请求合法性
反编译 把 DEX 还原成能读的 Java 代码
hook 在 App 运行时"挂钩子",拦截它正在调用的方法、拿真实参数
frida 最主流的 hook 工具

2. 准备工具和环境

2.1 手机(关键,最好满足)

  • 一台安卓手机,最好是 root 过的(Pixel、一加、小米等好 root 的机型)
  • 开启「开发者选项 → USB 调试」
  • 用数据线连电脑

为什么必须 root?脱壳和 hook 都需要 root 权限。没 root 也能做一部分(抓包、反编译),但脱壳和 hook 基本做不了。

2.2 电脑(macOS / Windows 都行)

装这几样:

工具 作用 安装方式
Python 3 跑脚本 python.org 下载
jadx 反编译 DEX 看代码 GitHub 下载 zip 解压即可
frida hook 工具 pip install frida
frida-dexdump 脱壳 pip install frida-dexdump
adb 电脑操作手机 安卓 SDK 自带,或 Homebrew brew install android-platform-tools

2.3 验证环境通了

# 1. 电脑能看到手机
adb devices
# 应该输出类似: 99231FFAZ005SN   device

# 2. 手机 root 了(进手机 shell 后能切 root)
adb shell
su
# 出现 # 号说明 root 成功

3. 第 ① 步:抓包,找到目标接口

这是最轻松、最容易有成就感的一步,先从这里练手。

方法 A:手机装抓包 App(推荐新手)

装「HttpCanary」(小黄鸟)或「抓包精灵」,按提示装证书,然后打开目标 App 操作,就能看到它发的所有请求。

方法 B:Charles / Fiddler(电脑端)

手机设代理指向电脑,电脑开 Charles 抓包。

你要找什么?

找到拿直播源/数据的那个请求。以金温江为例,点进直播界面后,会看到类似:

GET https://api.wjnews.cn/cms/show/1533-243.html
Header:
  X-Key: gnhBWGSsfxAOslgTwVSkWW
  X-Token: 4503a3db26ba97503...(很长一串)
  X-Signature: 7.677309910358216
  X-Timestamp: 1787105995

记录三样东西

  1. 完整 URL(含参数)
  2. 所有 Header(尤其是带 sign/token/key 字样的)
  3. 响应内容(返回的 JSON,里面藏着直播源)

💡 抓包这一步就能直接看到直播源地址了(响应 JSON 的 android_url 字段)。但它是动态的、会过期,所以还得继续还原签名算法,才能随时自己生成。


4. 第 ② 步:判断 App 有没有加固

# 看 APK 里有没有加固特征文件(360 是 libjiagu,梆梆是 libSecShell 等)
unzip -l 你的.apk | grep -iE "jiagu|shell|protect|libexec"

# 看 dex 数量(加固后往往只剩 1 个壳 dex)
unzip -l 你的.apk | grep classes
MacBook-Pro Downloads % unzip -l com.cditv.rmt.jinniu_2.0.9.apk | grep -iE "jiagu|shell|protect|libexec"
  1177848  04-29-2026 10:02   assets/libjiagu_a64.so
   914088  04-29-2026 10:02   assets/libjiagu_x64.so
   
MacBook-Pro Downloads % unzip -l com.cditv.rmt.jinniu_2.0.9.apk | grep classes
 10324272  04-29-2026 10:02   classes.dex

判断标准

  • 出现 assets/libjiagu*.soassets/.jgapp360 加固
  • classes.dex 只有 1 个且很小 → 大概率加固
  • 反编译后打开 Manifest,android:namecom.stub.StubApp 之类 → 加固

金温江和金牛都是 360 加固,所以必须脱壳。

如果没加固,直接跳过第 ③ 步,从第 ④ 步反编译开始。


5. 第 ③ 步:脱壳(360 加固)

5.1 把 frida-server 装到手机

# 1. 电脑下载 frida-server(版本要和 pip 的 frida 一致)
#    下载地址: https://github.com/frida/frida/releases
#    找 frida-server-<版本>-android-arm64.xz

# 2. 解压后推到手机
adb push frida-server-16.7.19-android-arm64 /data/local/tmp/frida-server

# 3. 给权限 + 以 root 启动
adb shell
su
chmod 755 /data/local/tmp/frida-server
/data/local/tmp/frida-server -D &

5.2 脱壳

# 电脑上,先启动目标 App,再脱壳
frida-dexdump -U -f com.ydtc.goldwenjiang
# 或者 App 已经开着,用:
frida-dexdump -U -n com.ydtc.goldwenjiang

# 会在当前目录生成一堆 dex 文件,这些就是脱壳后的真实代码

⚠️ 版本坑(重要):frida 17.x 起,Java bridge 不再自动注入,Python 直接加载脚本会报 Java is not defined建议直接用 frida 16.7.19(pip 和 frida-server 都装 16.7.19),省一堆麻烦。


6. 第 ④ 步:反编译,找关键代码

# 用 jadx-gui 打开脱壳后的 dex 目录
jadx-gui 脱壳目录/

怎么搜?从抓包的关键词反推

  1. 接口路径的关键词,比如 getliveurlcditvgetLiveUrl
  2. 签名字段名,比如 X-KeyX-Tokensign
  3. 算法关键词,比如 MD5SHA-512MessageDigest

金温江案例:最终定位到

com.dlna.http.DlnaPlayController   ← 核心类,签名逻辑都在这

打开这个类,能看到几个关键常量和方法:

public static String KS = "CdItV.cN";                    // v 参数的盐
public static String CANDO_APP_KEY = "N98oH7wy86y74kX";  // 密钥(注意是默认值)
public static String HEAD_PLAYURL_ZB = "/mobile/cditvplay/getliveurl";

private static String e(JSONObject j) {   // 生成 __ 签名的方法
    // 1. 取所有 key 排序
    // 2. 按序拼接 value
    // 3. 加盐后 MD5
}

💡 这一步能看出算法的"雏形",但密钥可能是默认值,运行时会被覆盖。所以不能光看代码,必须做第 ⑤ 步 hook 拿运行时真实值。


7. 第 ⑤ 步:hook 动态验证(灵魂步骤)

写一个 frida 脚本,在 App 运行时拦截它真实发出的请求和真实密钥。

// hook_okhttp.js —— 拦截 okhttp 请求
Java.perform(function () {
    var RequestBuilder = Java.use("okhttp3.Request$Builder");
    RequestBuilder.build.implementation = function () {
        var req = this.build();
        var url = req.url().toString();
        if (url.indexOf("api.") !== -1) {   // 只关心目标域名
            console.log("[URL] " + url);
            var h = req.headers();
            for (var i = 0; i < h.size(); i++) {
                console.log("[HDR] " + h.name(i) + ": " + h.value(i));
            }
        }
        return req;
    };
});
# 电脑上跑,attach 到 App
python3 attach.py

更狠的招:直接读运行时密钥

不满足于抓请求,直接读关键类的静态字段(这才是真实密钥):

Java.perform(function () {
    var D = Java.use("com.dlna.http.DlnaPlayController");
    console.log("KEY    = " + D.CANDO_APP_KEY.value);
    console.log("SERECT = " + D.CANDO_APP_SERCET.value);
    console.log("域名   = " + D.SERVERIP_MOB.value);
});

金温江 hook 到的真实值(和代码里的默认值不一样!):

KEY    = gnhBWGSsfxAOslgTwVSkWW     ← 代码里写的是 N98oH7wy86y74kX
SERECT = KqJGObmMeoUHfeXxykQpxKBNcWdVNHXumeQsaEWX
域名   = https://api.wjnews.cn/cms

⚠️ 360 加固的坑:加固后主进程在 frida 的进程列表里会"隐藏",enumerate_processes 只能看到 :pushcore 子进程。用 adb shell ps -A | grep 包名$ 拿真实 PID,再按 PID attach。


8. 第 ⑥ 步:还原算法 + 交叉验证(别跳过)

结合第 ④ 步的代码 + 第 ⑤ 步的真实数据,写出算法,然后用真实抓包数据验证

金温江的完整算法(已 100% 验证)

import hashlib

# 1. v 参数:MD5( 时间戳 + "CdItV.cN" )
t = "1787105995"
v = hashlib.md5((t + "CdItV.cN").encode()).hexdigest()
# = 1a512f6ceaefc649153099bc0be65d4a

# 2. __ 签名:MD5( 按 key 字母序拼接 value(无分隔符) + "Fun.key.CdItV.cN" )
body = {"page":1,"module":"rmt","catid":"1533","contentid":"243",
        "reply":"","t":1787105995,"client":"app","v":v}
sorted_keys = sorted(body.keys())                      # 字母序
concat = "".join(str(body[k]) for k in sorted_keys)    # 拼接 value
__ = hashlib.md5((concat + "Fun.key.CdItV.cN").encode()).hexdigest()
# = a13d17adcfa60abf3944fba3d1426ed1

# 3. X-Token:SHA-512( KEY + (SERECT + signature + timestamp) )
KEY = "gnhBWGSsfxAOslgTwVSkWW"
SERECT = "KqJGObmMeoUHfeXxykQpxKBNcWdVNHXumeQsaEWX"
sig = "7.677309910358216"
token = hashlib.sha512((KEY + (SERECT + sig + t)).encode()).hexdigest()

怎么验证?

拿第 ① 步抓包得到的真实请求,把你算出来的签名和它逐字对比。完全一致才算对。

如果顺序不确定(比如不确定拼接顺序),就全排列爆破

import itertools, hashlib
parts = ["app", "243", "rmt", "1", "1787105995", "salt"]
target = "抓包得到的签名"
for perm in itertools.permutations(parts):
    if hashlib.md5("".join(perm).encode()).hexdigest() == target:
        print("匹配顺序:", perm)

9. 第 ⑦ 步:写代码交付(PHP/Python)

算法验证通过后,翻译成目标语言。金温江的 PHP 核心就几行:

$ts        = (string)time();                          // 秒级时间戳
$signature = strval(mt_rand()/mt_getrandmax()*10.0);  // 随机数
$token = hash('sha512', KEY . (SERECT . $signature . $ts));  // X-Token
// 然后带上一堆 X-* header 请求 cms/show 接口,取 android_url

完整可跑的版本见本次交付的 wjnews.php(含温江+金牛多区县版)。


10. 完整复盘:金温江/金牛我是怎么一步步做的

步骤 实际做了什么 关键产出
① 抓包 frida hook okhttp 拦请求 拿到真实 URL + headers + 响应
② 加固识别 unzip -l 看到 libjiagu_a64.so 确认 360 加固
③ 脱壳 frida-dexdump 53 个 dex
④ 反编译 jadx 搜 getliveurl 定位 DlnaPlayController
⑤ hook 读静态字段 拿到真实 KEY/SERECT/域名
⑥ 验证 Python 复算对比抓包值 三套算法全部 100% 匹配
⑦ 交付 写 PHP 302 跳转直播源

金牛的"偷懒"发现:金牛和金温江是同一套 SDK(成都橙视 cditv 平台),类结构、算法、盐值完全一样,唯一区别就是域名 + KEY + SERECT 三处。所以我做金牛时根本没重新脱壳反编译,直接 frida attach 读字段,几分钟搞定。

这个规律记牢:同平台的兄弟 App,逆向成本几乎为零


11. 我踩过的坑(帮你少走弯路)

现象 解决办法
frida 17 的 Java 不可用 Java is not defined 降级 frida 16.7.19
360 加固进程隐藏 frida 枚举不到主进程 adb shell ps -A 拿真实 PID attach
enumerateClassLoaders 报错 onMatch of undefined 别遍历 loader,默认 loader 直接 Java.use
密钥对不上 用代码默认值验证失败 必须 hook 读运行时值(被 init 覆盖)
接口返回"鉴权失败" 90003 / 90000 检查 X-Token 算法 + 运行时密钥 + 时间戳
直播源 5 分钟失效 wsSecret 过期 每次实时请求,别缓存

12. 给你的学习路径建议

  1. 先练抓包(第①步):拿任意 App 抓包,看懂请求/响应结构,这是最基础也最好上手的。
  2. 练反编译(第④步):拿一个没加固的小 App,用 jadx 打开读代码,找接口逻辑。
  3. 练 hook(第⑤步):装好 frida,hook 一个 App 的请求,能打印出 URL 就算过关。
  4. 完整走一遍(①→⑦):拿金温江当靶子,从头到尾复现一遍本文档。
  5. 举一反三:找个成都其他区县的 App(青羊、成华、郫都……大概率 com.cditv.*),用"读字段"的偷懒法,几分钟拿下。

附:关键命令速查

# 看设备
adb devices

# 判断加固
unzip -l app.apk | grep -iE "jiagu|shell|protect"

# 脱壳
frida-dexdump -U -f 包名

# 反编译
jadx-gui 脱壳目录/

# 手机装/启 frida-server
adb push frida-server /data/local/tmp/
adb shell su -c "/data/local/tmp/frida-server -D &"

# 找主进程真实 PID(360 加固必用)
adb shell ps -A | grep 包名$

# 装依赖
pip install frida==16.7.19 frida-dexdump

这份文档对应的实战交付物(PHP、Python 脚本、hook 脚本)都在同一个目录下,对着看更直观。

posted @ 2026-09-04 16:57  流年中渲染了微笑  阅读(10)  评论(0)    收藏  举报