导语:一个名为 seeksocial.io 的项目最近公开了一份长达数万字的技术文档,详解如何绕过 TikTok(抖音海外版)的反爬体系,直接访问其 Android 客户端背后的私有 HTTP+JSON API(超文本传输+数据交换格式接口)。作者声称依托这套系统,三周内采集了 32.3 亿创作者档案、59.4 亿条视频和 28 亿条评论。这份文档更像是一份商业产品销售页,但剥开营销话术,里面的技术细节硬核到让人倒吸一口凉气——字节跳动为保护 API 设了四道独立关卡,每一道都让绝大多数爬虫死在门口。
一、先把立场摆清楚
这不是一篇学术论文,也不是某个安全研究员心血来潮的技术分享。这是 seeksocial.io 这个商业项目的销售页——文档末尾明码标价:”一次性付费、永久访问、完整源码”。换句话说,你看到的每一个技术细节,都是为了推销那套 Go 写的爬虫服务。
但销售归销售,里面的东西不掺水:协议逆向、加密算法还原、TLS 指纹伪造(让服务器误以为是真实手机发起的握手)、代理隧道设计——全是实战中的硬技术。剥开商业外壳,对红队、对防御方、对任何一个做数据采集的人来说,里面都有值得咀嚼的内容。
下面进入原理部分。
二、四道关卡,缺一不可
TikTok 移动 API 的反爬体系由四个完全独立的检查组成。任何一个失败,结果都是同一个:HTTP 200,响应体为空字节。
没有错误码。没有错误信息。你的 HTTP 客户端认为请求成功,你的日志一片绿,你的数据库塞进了空对象。直到六小时后你才发现 40 万行数据全是空记录。
这种”软封禁”是整套体系最狠的地方——你没法二分排查,因为根本没有信号告诉你卡在哪一道。

四道关卡分别是:
2.1 设备凭证
TikTok 不会接受你自己编造的 device_id(设备 ID)。设备 ID 由 TikTok 的注册接口 /service/2/device_register/ 在收到一个”看起来像真手机”的请求体后颁发。
请求体是 JSON,被 TikTok 自研的 TTEncrypt 加密,以 application/octet-stream;tt-data=a 投递。里面的字段必须互相一致——三星 SM-A136U 这台机型有特定分辨率、特定 DPI(屏幕像素密度)、特定 CPU 架构(ABI,Application Binary Interface,应用二进制接口),只在某些国家销售。一个旗舰机型配上一个从未承载过它的运营商,TikTok 一眼识破,注册直接拒绝。
作者的解法不是程序化随机生成,而是建立了一个 250 个真实 Android 机型档案 + 2000 条 MCC/MNC(移动国家码/移动网络码,用于识别运营商)组合的目录,从中挑选”自洽”的组合。
2.2 请求签名
这是最硬的部分。TikTok 的请求头里塞了三个签名:
- X-Argus:最复杂,保护完整请求体的 protobuf(Protocol Buffers,Google 开源的结构化数据序列化格式)摘要
- X-Ladon:轻量门,先过滤掉完全没研究过 App 的人
- X-Gorgon:传统摘要
2.3 区域主机
TikTok 不是一套 API,而是按区域分了多个数据中心。新加坡(alisg)、美国东部(useast1a / useast5)等等。同一个端点在不同的数据中心有完全不同的可用性——比如 aweme/v1/user/profile/other/(用户详细资料)只在 useast5 才对新设备开放,而音乐相关端点反过来,只在 useast1A 响应。
没有文档告诉你这件事,只能靠实测一张一张端点踩出来。
2.4 TLS 指纹
在第一个 HTTP 字节发出之前,TLS(Transport Layer Security,传输层安全协议)握手的 ClientHello(客户端打招呼消息)就已经暴露了你的客户端库。JA3 算法(一种通过 TLS 握手特征识别客户端的指纹标准)把这五个字段(TLS 版本、加密套件、扩展、椭圆曲线、点格式)拼起来做 MD5(消息摘要算法第五版)哈希:
TLS版本,加密套件列表,扩展列表,椭圆曲线列表,点格式列表
OpenSSL、BoringSSL(Google 维护的 OpenSSL 分支)、Java 的 JSSE(Java 安全套接字扩展)、Go 的 crypto/tls 各自的 JA3 都不同。Go 客户端的指纹最显眼,因为没有任何 Android 应用会用 Go 标准库——它们都用 BoringSSL + OkHttp(Square 出品的 Android HTTP 客户端库)。
三、X-Argus:双重加密的签名黑盒
X-Argus 的明文是 protobuf 消息,序列化后跑两段加密:
第一层(内层):用 SIMON-128/256-ECB(Electronic Codebook,电子密码本模式)
- 密钥派生:
SM3(SIGN_KEY[0:32] || f(rand_lo, rand_hi) || SIGN_KEY[0:32]) - SM3 是中国国密哈希算法(GB/T 32905-2016),256 位输出
- 然后是字节反转、xor 混洗、framing(加版本字节和熵标记)
第二层(外层):用 AES-128-CBC(Advanced Encryption Standard,高级加密标准;CBC,Cipher Block Chaining,密码块链接模式)
- 密钥 = MD5(SIGN_KEY[0:16])
- IV(Initialization Vector,初始化向量)= MD5(SIGN_KEY[16:32])
- 密钥/IV 都来自同一个签名密钥的前 32 字节
最后 base64 编码输出。
为什么用 SIMON 和 Speck? NSA(美国国家安全局)2013 年发布的两种轻量级 ARX 密码(仅用加法 Addition、循环移位 Rotation、异或 XOR 三种运算构造的密码算法)。它们不是 AES,理由有三:
- 体积小——编译出来只有几百字节 ARM 指令,没有 S-box(查找表)、没有数据相关的内存访问
- 不在你的标准库里——你想用就得自己实现,参数空间(块大小、密钥大小、轮数、移位常量、密钥调度)大到足以让错误实现看起来”差不多对”
- 容易实现错——Speck 的密钥调度复用了轮函数本身,字序或字节序错了,32 个看起来合理的轮密钥全是错的
这种设计哲学很值得玩味——让攻击者猜不出参数,比加密本身更难。
四、激活调用:最反直觉的隐藏门
文档里有个让我最意外的发现。
注册成功的设备能拿到 95% 端点的数据,但 /aweme/v1/user/profile/other/(用户详细资料)始终返回空 200。
排查签名、调设备、改参数都没用。同样的代码、同样的签名,老一批几个月前生成的设备能正常用——唯一区别是生成方式。
差别是一个看似无关的额外 HTTP 调用:
GET /service/2/app_alert_check/?
cronet_version=...
&ttnet_version=...
&tt_info=<base64url(TTEncrypt(<60 字段 blob>))>
这个调用返回 {"message":"success"},没有可用数据,纯遥测。真实 App 启动时会先发这个,再发任何数据请求。
为什么?作者的判断:注册不让你变成”运行中的 App”。从字节视角,一个注册完立刻开始查 API 的设备,是”从未启动的安装”。注册 + 启动调用 = 真正活着的设备。
| 生成方式 | profile 端点成功率 |
|---|---|
| 仅注册 | 0/360 |
| 注册 + app_alert_check | 100/100 |
从 0 到 100%,只因为一个你扔掉的响应。
这给了我一个深刻的启发:最好的反爬不是加密签名,而是行为时序。加密可以逆向,行为模式难以伪造。
五、区域分区:每个端点都有专属主机
文档的另一个残酷事实:域名不是全局配置,而是每个端点的属性。
端点 主机 响应
user.profile/other/ api16-normal-useast5.tiktokv.us 50/50, 9517 字节
user.profile/other/ api16-normal-c-alisg.tiktokv.com 1/50, 空 200
music.detail/ api16-normal-c-useast1a.tiktokv.com 50/50
music.detail/ api16-normal-c-alisg.tiktokv.com 0/50
同样设备、同样签名、同样参数、同一秒——三个主机,三种命运。
更进一步:设备注册区域影响内容。同一段代码,跑一个美国注册池子、一个巴西注册池子,得到的”趋势榜单”是真的不一样。要拿不同国家的数据,不用写不同代码,用不同区域的设备池即可。
六、TLS 指纹:用 uTLS 伪装成 Android 手机
作者做了个精彩的隔离实验:
python urllib3/OpenSSL → 9517 字节 ✓
curl OpenSSL → 9519 字节 ✓
go crypto/tls → 0 字节 ✗
完全相同的请求,三个客户端,并发执行。Python 和 curl 拿到数据,Go 拿到空 200。
修复方案:用 uTLS 库强制指定 ClientHello 的指纹:
cfg := &utls.Config{ServerName: host, NextProtos: []string{"http/1.1"}}
conn := utls.UClient(raw, cfg, utls.HelloAndroid_11_OkHttp)
一行配置切换。profile 端点从 0% 跳到 100%。
这里有个 Go 特有的陷阱:http.Transport 一旦设置 Proxy 字段,就会默默丢弃你自定义的 DialTLSContext——它会自己走代理 CONNECT 隧道,然后用自己的标准库握手。你必须手动写 CONNECT 请求,然后再在隧道里跑 uTLS。文档里给了一段完整的 Go 代码展示这个手动隧道。
七、性能优化的关键洞察
作者跑了 1.74 亿次请求,从 88.2% 成功率优化到 99.3%。关键不是”加更多重试”——而是理解 HTTP keep-alive 的副作用。
HTTP keep-alive 让同一连接上的多个请求共享同一个代理出口 IP。但 TikTok 的限流是按出口 IP的。所以一次软封禁后,重试还是从被封的 IP 出发——重试再多次也没用。
keep-alive + 代理池:
attempt 1 → 203.0.113.44 → 空 200
attempt 2 → 203.0.113.44 → 空 200 ← 同一个 IP
attempt 3 → 203.0.113.44 → 空 200
attempt 4 → 203.0.113.44 → 空 200
修掉 keep-alive 立刻涨到 96.2%,但又触发新瓶颈:每个请求都跑 TLS 握手,代理网关自己先撑不住(proxy CONNECT: 466 Too Many Requests)。
最终方案是两个池子、一个开关:
// 首次请求池:keep-alive,省握手
r.proxyPool, _ = httpclient.New(httpclient.Config{ProxyURL: cfg.ProxyURL})
// 重试池:禁 keep-alive,每次换 IP
r.proxyPoolFresh, _ = httpclient.New(httpclient.Config{
ProxyURL: cfg.ProxyURL, DisableKeepAlives: true,
})
// 请求路径
pool := r.proxyPool
if attempt > 0 && r.proxyPoolFresh != nil {
pool = r.proxyPoolFresh // 精确旋转
}
| 配置 | 成功率 | 瓶颈 |
|---|---|---|
| keep-alive 全开 | 88.2% | 重试用同一 IP |
| keep-alive 全关 | 96.2% | 代理网关过载 |
| 仅首次 keep-alive | 99.3% | 无 |
1.74 亿次请求实测的优化曲线,干净利落。
八、红队视角的判断
8.1 这是商业产品,不是安全研究
文档末尾明确说”一次性付费、GitHub 邀请”——这是销售,不是披露。技术细节全是真的,但目的是卖系统,不是披露漏洞。同类技术如果再深入一步就是绕过反爬用于实际抓取,TikTok 服务条款明确禁止未经授权的自动化访问。
8.2 字节反爬的真正价值
字节搭建的体系不是防某一个具体工具,而是防四类完全独立攻击向量的同时出现。要做到 99.3%,得设备 ID 真、签名对、主机选对、TLS 长得像手机、代理会旋转、keep-alive 配对——任一环节掉链都前功尽弃。
这是分布式纵深防御的工程典范。
8.3 防御方的启示
四个观察:
- 加密不是反爬的全部。行为时序(启动调用)比加密签名更难伪造
- 软封禁比硬封禁更难调试。HTTP 200 + 空响应让对手的反馈机制完全失效
- 指纹要在协议栈最底层做。TLS 指纹藏在 HTTP 看不见的地方
- 不要全局化配置。区域主机、版本门控全是按端点颗粒度设计的
红队和蓝队都应该把这份文档当成”当前反爬对抗技术天花板”的样本读一遍。
九、24 个端点的成功率和代价
文档最后列出了 24 个端点的实测数据,按从最强到最弱:
| 端点 | 成功率 | 平均重试 | 平均响应 |
|---|---|---|---|
| user.info | 100% | 1.00 | 9 KB |
| music.trending | 100% | 1.00 | 85 KB |
| search.users | 100% | 1.00 | 86 KB |
| video.info | 94% | 1.96 | 658 KB |
| user.following | 93% | 1.90 | 23 KB |
| video.comments | 92% | 2.02 | 174 KB |
| user.posts | 90% | 2.26 | 1.1 MB |
| feed.recommended | 10-25% | 3.93 | 248 KB |
feed.recommended(个性化推荐流)只有 10%——一个匿名设备、零观看历史、问 TikTok 要个性化推荐,这正好是 TikTok 最想限流的流量形态。
作者在该端点标注”薄弱,已提供两个 100% 的替代方案”。承认弱项比吹嘘强项更显专业。
十、总结
这份文档的真正价值不在 24 个端点、不在 32 亿条数据,而在它揭示了当前商业级反爬对抗的技术水位:
- 加密原语从 AES 扩展到 SIMON/Speck/SM3 的混合栈
- 反爬维度从签名扩展到协议栈最底层的 TLS 指纹
- 优化方法从单端点扩展到代理池拓扑结构
- 调试方法从 HTTP 错误码扩展到端到端的成功率统计
字节跳动用这套体系保护了全球数亿用户的隐私数据。seeksocial.io 用逆向工程把同一套体系拆开摆在了阳光下。
攻防永远在赛跑。这份文档把当前这场比赛的”终点线”画得很清楚。
参考资料:seeksocial.io, “Scraping TikTok’s Mobile API”, September 2026. https://tiktok-api.seeksocial.io/














暂无评论内容