抖音开放平台目前已逐渐转向封闭,不再“开放”了。2025年4月14日抖音开放平台通知:
”将于2025年4月14日起停止私信群聊能力权限申请和开放。对历史已开通私信能力的外部移动/网站应用,平台将分批回收以下私信能力权限(历史开通过的将无法继续使用):近90天无调用的,平台将于公告发布之日起,直接回收私信能力权限;近90天有调用的,平台将于2025年5月19日起,分批回收私信能力权限。“
然后,5月15日继续通知:“从2025年5月19日起,平台将对有调用的应用能力权限进行分批回收,于2025年5月30日回收完毕。检测到你所属的移动/网站应用有私信能力的调用(有授权/有Webhook调用/有OpenAPI调用),为避免应用报错,请充分自查,并及时完成调整和改造。”
此时,我们手里的私信并没有真正回收。直到2025年11月26日的通知:
“为维护平台良好生态,避免隐私安全风险,平台自2025年4月14日起,对移动应用、网站应用的私信群聊能力权限做调整,详情请参考:移动/网站应用的私信群聊能力下线/回收公告。此前,你所属的移动/网站应用通过内部业务方申请了服务私信能力延期回收 。现因业务调整,重新启动回收:平台将于2025年12月26日回收服务私信能力权限。为避免应用影响,请充分自查并及时完成调整改造。”
至此,私信能力真正回收掉了。不再能收发私信。
那目前,普通客户可以拿到私信的只有2种方式:1、手机里的抖音app里;2、网页端的抖音创作者平台。
手机端要拿到私信,如果是协议式的,手机需要root的。不是协议式的,可以利用无障碍开发者方式获取,也是需要大量适配工作的,每升级一个版本都要重新适配。
本文只探讨网页端的抖音创作者平台里如何获取私信。
打开:https://creator.douyin.com/creator-micro/data/following/chat
可以看到抖音进行了特殊的处理,POST
https://imapi.snssdk.com/v2/message/get_by_user_init ,并且使用了
content-type :application/x-protobuf 的协议,protobuf是Google开发的新型网络协议,压缩率非常高,想破解这个,需要 .proto文件,如果没有这个.proto文件,工作量就会非常巨大。
常用的逆向破解方法:
即使没有原始的 .proto 文件,开发者通常会通过以下手段进行破解:
1、静态分析(反汇编): 如果是安卓 App 或 PC 软件,可以通过反编译代码(如使用 jadx 逆向 Java,或使用 IDA Pro 逆向 C++)。Protobuf 的解析代码通常会包含字段编号与变量名的映射关系,逆向者可以据此手动还原出 .proto 文件。
2、动态抓包与猜测: 使用工具(如 Charles 或 Fiddler)配合插件,可以将二进制流自动解析为结构化数据。通过观察数据的变化(例如:修改用户名后,哪个字段变了),可以推断出每个编号代表的含义。
3、自动化工具(protoc --decode): Google 官方工具支持在不知道结构的情况下,强制以“原始 Wire Format”打印数据。
我们这里不去破解,使用巧劲。因为我们的目的只是拿到解密后的明文,完全不需要走这么复杂的方案。
通过跟踪js调用堆栈,我们找到加解密的函数:
sendRequest(e, t) {
return (0,
n.__awaiter)(this, void 0, void 0, (function*() {
const r = yield this.send(e, "POST", this.encode(t));
return this.decode(r)
}
))
}
encode(e) {
return (0,
s.serialize)(e).buffer
}
decode(e) {
return o.im_proto.Response.create((0,
s.deserialize)(this.ctx, e))
}我们只需要在 decode 函数里,把解码后的明文数据打印出来即可。
这里可以采用浏览器打断点的方式,输出调试信息,也可以打入 logprint,输出日志。还可以使用 Chrome 本地覆盖(Local Overrides)的方式覆盖js,具体的方法就不展开细述了。
经过调试,可以看出发出请求时,包的结构为:
{
"t": {
"headers": {
"aid_new": 2906,
"app_name": "douyin_creator"
},
"body": {
"messages_per_user_init_v2_body": {
"cursor": {
"low": 0,
"high": 0,
"unsigned": false
}
}
},
"cmd": 203,
"sequence_id": {
"low": 10001,
"high": 0,
"unsigned": false
},
"refer": 3,
"token": "YSHGAwEBBwECBIEaKUQBZwFVqNYCl1B...",
"device_id": 0,
"sdk_version": "0.7.2-fix.1",
"build_number": "2f4951d:fix/douyin-creator-fix",
"inbox_type": 1,
"device_platform": "douyin_creator",
"auth_type": 3,
"biz": "douyin_creator",
"access": "web_sdk",
"sdk_cert": "LS0tLS1CRUdJTiBDRVJU...",
"ts_sign": "#KVBSUXpXvAQF1rk+r6db..."
},
"e": "https://imapi.snssdk.com/v2/message/get_by_user_init"
}接收到的明文数据如下:
{
"headers": {},
"cmd": 203,
"sequence_id": {
"low": 10002,
"high": 0,
"unsigned": false
},
"status_code": 0,
"error_desc": "OK",
"inbox_type": 1,
"body": {
"messages_per_user_init_v2_body": {
"messages": [
{
"ext": {
"awe:from:commerce": "1",
"s:visible": "111",
"chat_scene": "normal",
"s:refer": "4",
"s:high_priority_message": "commerce",
"s:saas_sdk": "false",
"a:msg_not_float": "receive",
"echat_forward_msg_info": "{\"ai\":337122,\"mi\":111,\"csi\":111,\"ci\":\"111\",\"ct\":2}",
"s:biz_aid": "1128",
"a:is_server": "true",
"a:access": "server",
"s:s_aid": "1128",
"a:msg_scene": "2",
"s:client_message_id": "7d2e8ce3-111",
"s:vcd_shark_detail": "",
"a:plv": "n",
"a:relation_type": "0:1",
"s:server_message_create_time": "1762163906284",
"source_aid": "1128",
"s:mode": "0",
"im_callback_status_code": "0",
"a:s_awe_push_close": "1",
"s:is_stranger": "false",
"s:sync_2_newdx": "1",
"s:vcd_shark_decision": "PASS",
"s:do_not_increase_unread": "true",
"a:biz": "aweme_im_service_script"
},
"property_list": {},
"conversation_id": "0:1:111:222",
"conversation_type": 1,
"server_message_id": {
"low": 873219633,
"high": 1,
"unsigned": false
},
"index_in_conversation": {
"low": -814695360,
"high": 1,
"unsigned": false
},
"conversation_short_id": {
"low": -2138947009,
"high": 1,
"unsigned": false
},
"message_type": 1,
"sender": {
"low": 419171323,
"high": 1,
"unsigned": false
},
"content": "{\"tips\":\"对方转移了账号主体,为保护你的隐私,已向对方隐藏了历史消息,你授权后对方才可查看 {{1}}\",\"aweType\":195}",
"create_time": {
"low": 1227314609,
"high": 1,
"unsigned": false
},
"version": {
"low": 1227315343,
"high": 1,
"unsigned": false
},
"status": 0,
"order_in_conversation": {
"low": 1348426240,
"high": 1,
"unsigned": false
},
"sec_sender": "MS4wLjABAAAARv3VI03dODthsvOt30"
}
],
"conversations": [
{
"conversation_id": "0:1:111:222",
"conversation_short_id": {
"low": -2138947009,
"high": 1,
"unsigned": false
},
"conversation_type": 1,
"ticket": "111",
"first_page_participants": {
"participants": [
{
"user_id": {
"low": 1330098236,
"high": 1,
"unsigned": false
},
"sec_uid": "MS4wLjABAAAA4HNV1M9GCjyV3Vc5"
},
{
"user_id": {
"low": 419171323,
"high": 1,
"unsigned": false
},
"sec_uid": "MS4wLjABAAAARv3VI03dODthsvOt30"
}
]
},
"participants_count": 2,
"is_participant": true,
"conversation_core_info": {
"ext": {
"a:ds_b_scene": "1",
"s:s_aid": "1128",
"s:high_priority_conversation": "commerce",
"awe:commerce": "1"
},
"conversation_id": "0:1:111:222",
"conversation_short_id": {
"low": -2138947009,
"high": 1,
"unsigned": false
},
"conversation_type": 1,
"info_version": {
"low": 1762163906,
"high": 1,
"unsigned": false
},
"name": "",
"desc": "",
"icon": "",
"inbox_type": 0,
"notice": "",
"owner": {
"low": 1330098236,
"high": 1,
"unsigned": false
},
"sec_owner": "MS4wLjABAAAA4HNV1M9GCjyV3Vc5KqNB4GhUBuCQA8YQ-YZdjKu5iwo"
},
"conversation_setting_info": {
"ext": {
"a:btm_tracking": ""
},
"conversation_id": "0:1:111:222",
"conversation_short_id": {
"low": -2138947009,
"high": 1,
"unsigned": false
},
"conversation_type": 1,
"min_index": {
"low": 0,
"high": 0,
"unsigned": false
},
"read_index": {
"low": -814705360,
"high": 1,
"unsigned": false
},
"mute": 0,
"stick_on_top": 0,
"inbox_type": 0,
"setting_version": {
"low": 1695617385,
"high": 0,
"unsigned": false
},
"favorite": 0,
"set_top_time": {
"low": 0,
"high": 0,
"unsigned": false
},
"set_favorite_time": {
"low": 0,
"high": 0,
"unsigned": false
}
}
}
],
"per_user_cursor": {
"low": -649801037,
"high": 1,
"unsigned": false
},
"next_cursor": {
"low": 20,
"high": 0,
"unsigned": false
},
"has_more": true
}
},
"log_id": "20260000",
"request_arrived_time": {
"low": -618177778,
"high": 1,
"unsigned": false
},
"server_execution_end_time": {
"low": -618175822,
"high": 1,
"unsigned": false
}
}我们只需要解析 body -> messages_per_user_init_v2_body -> messages 就可以获取私信的明文数据了。
注意,这里还没有用户的昵称和头像信息,我们需要通过加密的用户id(sec_sender) 去匹配另一个请求。
昵称+头像请求:
https://creator.douyin.com/aweme/v1/creator/im/user_detail/
这个请求里有具体的昵称、头像:
{
"extra": {
"now": 1768383888000
},
"status_code": 0,
"status_msg": "",
"user_list": [
{
"user": {
"SecretUseId": "MS4wLjABAAAAH3pc3DValg",
"ShareQrcodeUri": "",
"ShortId": "111",
"avatar_thumb": {
"uri": "aweme-avatar/tos-cn-i-0813_8de015",
"url_list": [
"https://p3.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-i-0813_8de015.jpeg?from=2956013662",
"https://p11.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-i-0813_8de015.jpeg?from=2956013662",
"https://p26.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-i-0813_8de015.jpeg?from=2956013662"
]
},
"nickname": "飘易",
"signature": "一个青年。飘易。"
},
"user_id": "MS4wLjABAAAAH3pc3DValg"
},
]
}然后,通过加密的用户id 来匹配私信的发送者。
上面只是调试的信息,如果我们需要自动化部署,可以开发一套软件自动去捕获,目前飘易就是采用的软件捕获的方案,目前稳定获取私信中。