【逆向实战 Tiktok-WSS私信】从零构建JS自动化私信工具,详解加密流程与参数构造

张开发
2026/6/18 16:30:30 15 分钟阅读
【逆向实战 Tiktok-WSS私信】从零构建JS自动化私信工具,详解加密流程与参数构造
1. 逆向工程基础准备在开始构建TikTok私信自动化工具之前我们需要先了解几个关键概念。WSSWebSocket Secure是WebSocket的加密版本它建立在TLS协议之上为实时通信提供了安全通道。与普通的HTTP请求不同WSS连接一旦建立就会保持长连接特别适合即时通讯场景。我刚开始接触这个项目时发现TikTok的私信功能使用了多重加密机制。首先消息内容会被转换成Uint8Array格式然后通过be函数进行第一次加密接着再用ke函数进行二次加密。这种层层加密的设计确实给逆向工作带来不小挑战。准备工作需要以下工具Chrome浏览器最新版Node.js环境建议v16任意代码编辑器VSCode推荐一个可用的TikTok账号非本土店铺账号注意所有操作仅用于学习交流目的请勿用于商业用途或违反平台规则。2. 定位WSS连接与关键参数2.1 捕获WSS连接打开Chrome开发者工具F12切换到Network面板选择WS过滤器。在TikTok私信页面发送一条消息你会看到一个wss连接出现。这个连接通常以wss://im-xxx.tiktokv.com/...开头。我实测发现连接建立后会立即发送4次hi作为心跳包。这个细节很重要因为我们需要在后续的代码中模拟这个行为否则连接可能会被服务器主动断开。2.2 关键参数分析通过分析网络请求我发现以下几个核心参数必须正确构造conversation_short_id聊天室唯一标识token授权令牌device_id设备标识seqId自增序列号这些参数主要通过两个API获取/api/v1/oec/affiliate/seller/im/get/token- 获取token和wss地址/api/v1/im/conversation/create- 创建或获取聊天室ID3. 解密加密流程3.1 第一次加密be函数在开发者工具的Sources面板中我们可以找到加密函数的调用堆栈。第一次加密由be函数完成它接收一个JSON对象输出Uint8Array格式的数据。我通过断点调试发现be函数内部还调用了_e函数进行辅助处理。扣取这段代码时需要注意保持原始的函数依赖关系。以下是关键代码结构function be(e) { const t _e(e); return d.Frame.encode(d.Frame.create(t)).finish() } function _e(e) { // 内部处理逻辑 return processedData; }3.2 第二次加密ke函数第一次加密的结果会被传递给ke函数进行二次处理。这个函数会添加一些额外的头部信息包括service ID固定为20345随机生成的X-MS-STUB值时间戳logid序列号seqid实测中发现X-MS-STUB虽然看起来是随机字符串但其实可以用简单的随机生成器模拟不影响功能function generateXMSStub() { const chars ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/; let result WR[Math.floor(Math.random() * 2)]; for (let i 0; i 15; i) { result chars.charAt(Math.floor(Math.random() * chars.length)); } return result; }4. 构建完整的私信工具4.1 WebSocket客户端类基于以上分析我们可以构建一个完整的WebSocket客户端类。这个类需要处理连接建立、消息发送、心跳维持等所有功能class TikTokWSClient { constructor(config) { this.url config.wssUrl; this.token config.token; this.conversationId config.conversationId; this.seqId 10000; // 初始序列号 this.ws null; } connect() { this.ws new WebSocket(this.url); this.ws.binaryType arraybuffer; this.ws.onopen () { // 发送初始心跳 for(let i0; i4; i) this.ws.send(hi); console.log(连接已建立); }; this.ws.onmessage (e) { if(e.data hi) return; // 忽略心跳回复 this.handleMessage(e.data); }; } // 其他方法... }4.2 消息结构体构造根据消息类型不同我们需要构造三种主要的结构体文本消息{ body: { send_message_body: { conversation_id: 123456, content: Hello, message_type: 1000, ext: { type: text, sender_role: 2 } } }, cmd: 100, sequence_id: {low: this.seqId, high: 0} }商品卡片消息{ body: { send_message_body: { conversation_id: 123456, content: [商品卡片], message_type: 1000, ext: { type: product, productId: 123456789 } } }, cmd: 100 }图片消息{ body: { send_message_body: { conversation_id: 123456, content: [图片], message_type: 1000, ext: { type: file_image, imageUrl: https://example.com/image.jpg } } }, cmd: 100 }4.3 完整发送流程最终的发送流程分为四个步骤构造原始消息体使用be函数进行第一次加密使用ke函数添加协议头并进行二次加密通过WebSocket发送最终数据我在实际测试中发现每次发送后seqId需要自增1否则服务器会拒绝后续消息。此外商品卡片的productId需要通过商品详情接口获取不能随意构造。5. 常见问题与解决方案在开发过程中我遇到了几个典型的坑这里分享下解决方案连接立即断开问题必须准确模拟初始的4个hi心跳包少一个都不行。我最初只发了3个结果连接3秒后就断开了。加密函数缺失依赖扣取be和ke函数时经常会遇到依赖的其他函数。我的经验是先扣主要函数运行看报错缺少什么再去源代码中查找对应的依赖函数重复这个过程直到没有报错消息发送失败检查三个关键点conversation_id是否正确token是否有效seqId是否连续自增海外图片加载问题发送的图片URL必须确保在目标地区可以访问。我建议使用AWS S3或Cloudflare等全球可用的存储服务。频率限制TikTok对私信有严格的频率限制未回复的对话通常只能发送5条消息。建议在代码中加入延迟和错误处理function sendWithRetry(message, maxRetry 3) { let retry 0; const send () { try { this.sendMessage(message); } catch(e) { if(retry maxRetry) { setTimeout(send, 1000 * retry); } } }; send(); }6. 进阶优化建议对于想要进一步优化工具的同学可以考虑以下几个方向自动获取token通过模拟登录流程自动获取token而不是手动复制。这需要分析登录接口和cookie处理。消息队列系统实现一个优先级消息队列处理发送失败自动重试、频率控制等问题。浏览器插件化将工具打包为Chrome插件直接在浏览器中运行更方便日常使用。支持更多消息类型除了文本、图片和商品卡片TikTok私信还支持视频、订单等多种消息类型可以继续扩展。消息接收与解析目前只实现了发送功能可以增加接收和解析他人消息的能力实现完整对话流程。我在实际使用中发现保持WebSocket连接的稳定性是关键。网络波动时连接可能会断开需要实现自动重连机制。另外建议定期更换device_id等参数避免被系统识别为自动化行为。

更多文章