背景
在逆向 B 站 Android APP 8.91.1(tv.danmaku.bili,build 8911100)时,发现 B 站使用了两套独立的 appkey/secret 体系,分别用于账号体系和业务接口,混用会导致签名失败。
逆向方法:Frida OkHttp hook + APK DEX 字符串扫描
两套签名体系
| 套件 | AppKey | 用途 |
|---|---|---|
| Passport | 783bbb7264451d82 |
注册/登录/SMS/SSO |
| App | 1d8b6e7d45233436 |
播放/推荐/互动/投稿/心跳 |
差异细节
两套的签名算法结构相同,但 URL-encoding 行为不同:
- Passport:
urllib.parse.quote(v, safe='')— 全编码 - App:
urllib.parse.quote(v, safe='-_.~')— 保留 unreserved chars
签名算法实现
import hashlib, time, urllib.parse
def sign_app(params: dict, appkey: str, secret: str) -> dict:
params = dict(params)
params['appkey'] = appkey
params['ts'] = str(int(time.time()))
# 按 key 字典序排列
sorted_items = sorted(params.items())
# App 套件:safe='-_.~'
raw = '&'.join(f'{k}={urllib.parse.quote(str(v), safe="-_.~")}' for k, v in sorted_items)
raw += secret
params['sign'] = hashlib.md5(raw.encode()).hexdigest()
return params
x-bili-ticket:HMAC-SHA256 鉴权票据
B 站 gRPC 和部分 REST 接口需要携带 x-bili-ticket 请求头,这是一个基于 HMAC-SHA256 的时间戳签名,从 APK DEX 扫描中定位到密钥。
算法(已通过 /x/web-interface/nav HTTP 200 验证):
import hmac, hashlib, time
_TICKET_KEY = b'XgwSnGZ1p'
def gen_ticket(ts: int = None) -> str:
ts = ts or int(time.time())
msg = f'ts{ts}'.encode()
return hmac.new(_TICKET_KEY, msg, hashlib.sha256).hexdigest()
def ticket_headers(ts: int = None) -> dict:
ts = ts or int(time.time())
return {
'x-bili-ticket': gen_ticket(ts),
'x-bili-ticket-ts': str(ts),
}
SSO 链路:App access_token → Web Cookie
B 站支持将 APP 登录态转换为 Web Cookie,全链路:
access_token
→ POST /x/passport-login/web/sso/ticket -> sso_ticket(一次性短效)
-> GET /x/passport-login/web/crossDomain -> Set-Cookie: SESSDATA / bili_jct
def sso_get_cookie(access_token: str) -> dict:
# Step 1: 换取 sso_ticket
r1 = session.post('https://passport.bilibili.com/x/passport-login/web/sso/ticket',
data=sign_app({'access_key': access_token}, PASSPORT_KEY, PASSPORT_SECRET))
sso_ticket = r1.json()['data']['sso_ticket']
# Step 2: 用 sso_ticket 获取 Web Cookie
r2 = session.get('https://passport.bilibili.com/x/passport-login/web/crossDomain',
params={'sso_ticket': sso_ticket},
allow_redirects=True)
return {c.name: c.value for c in r2.cookies}
逆向依据:APK DEX 扫描发现 SSOActivity、isSupportSSOLogin、setSSOCookie 等类名
已发现的 GAIA 风控接口
APK DEX 扫描中发现了隐秘的风控上报接口,目前未公开文档:
| 接口 | 说明 |
|---|---|
POST /x/internal/gaia-gateway/ExClimbCongLing |
设备环境特征上报 |
POST /x/internal/gaia-gateway/ExClimbKunLun |
行为特征上报 |
POST /x/internal/gaia-gateway/ExClimbAppleTrees |
轨迹/图形上报 |
这些接口在 SMS 发送触发风控时被调用,Protobuf 格式,字段含义待进一步逆向。