免责声明: 本博客内容仅供学习和研究使用,请勿用于非法用途。任何未经授权的逆向工程行为都可能违反相关法律法规和服务条款,风险自负。
前言: 继上一篇博客详述了使用 JS 注入绕过 msedgedriver 的字符处理逻辑并触发表单提交元素后, 为更深入地理解某知名社交平台的前端逻辑, 开始尝试使用浏览器的开发者工具, 以更深入地理解 Web 开发的底层原理.
即使在没有 JS 基础语法知识的情况下, 有一些更简单的方法也能帮助新手快速入门逆向工程.
通过分析网络请求来尝试理解该平台 API 的工作原理.
步骤:
m.xxx.cn
) 的发微博页面。F12
打开开发者工具,切换到 Network
面板。Preserve log
选项,这样在页面跳转后,之前的网络请求记录不会被清除。Network
面板中,查看发送微博的网络请求(通常是一个 POST 请求,可以通过 Method
列来筛选)。你能看到什么:
POST
。Content-Type
、Cookie
等信息。如何利用这些信息:
解决方法:
启用 “Preserve log” 选项:
Network
面板的工具栏中,确保勾选 Preserve log
(或 保留日志
)选项。这个选项可以让 Network
面板在页面跳转后仍然保留之前的网络请求记录。使用 “Disable cache” 选项:
Network
面板的工具栏中,勾选 Disable cache
(或 禁用缓存
)选项。这个选项可以确保你每次请求的都是最新的资源,而不是缓存中的旧资源。这对于调试网络请求非常有用。解决方法:
使用过滤器进行筛选:
Network
面板的工具栏中,有一个 Filter
(或 过滤
)输入框。你可以在这里输入一些关键词来过滤网络请求。method:POST
,然后按下回车键,Network
面板将只显示 POST
请求。根据请求的 URL 进行筛选:
update
。在 Filter
输入框中输入这个关键词来过滤网络请求。第一张图:负载(Payload)
content
: “请求体种包含了这条信息!!!”,这里就是发送的微博内容。visible
: 此处的测试我是通过发送了这条"仅自己可见" 的微博. 所以此处的 1
,表示发送的微博的可见范围是"仅自己可见"。据此, 可以猜测, 0
可以表示“所有人可见”,6
可能表示“仅好友圈可见”。我们可以通过修改这个参数来测试不同的可见范围对应的数字。st
: f79388
,看起来像是一个 token,可能是用于验证用户身份或防止重复提交的。_spr
: screen:1920x1080
,表示当前的屏幕分辨率。第二三张图:标头(Headers)
https://m.xxx.cn/api/statuses/update
,表明发送微博的 API 接口地址是 /api/statuses/update
。POST
,表明发送微博是通过 POST 请求来完成的。200 OK
,表明请求已成功处理。127.0.0.1:7890
,表示我使用了代理服务器, 后文将会详细分析这点。Accept
: application/json, text/plain, */*
,表示客户端可以接受 JSON、纯文本等多种类型的响应。Accept-Encoding
: gzip, deflate, br, zstd
,表示客户端支持的压缩算法。Accept-Language
: zh-CN,zh;q=0.9,en;q=0.8
,表示客户端接受的语言。Content-Length
: 193
,表示请求体的长度。Content-Type
: application/x-www-form-urlencoded
,表示请求体是使用 URL 编码的表单数据。Cookie
: 一长串的 经过处理的 Cookie
值,用于用户身份验证。Origin
: https://m.xxx.cn
,表示请求的来源。Referer
: https://m.xxx.cn/compose/
,表示用户是从哪个页面跳转过来的。User-Agent
: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36
,表示你使用的浏览器信息。X-Requested-With
: XMLHttpRequest
,表示这是一个 AJAX 请求。X-XSRF-TOKEN
: f79388
,看起来和负载中的 st
值相同,可能是一个 CSRF token,用于防止跨站请求伪造攻击。我们可以分析出什么?
https://m.xxx.cn/api/statuses/update
,请求方法是 POST
,请求体是使用 URL 编码的表单数据。content
参数来传递的。visible
参数来控制微博的可见范围。Cookie
和 X-XSRF-TOKEN
来进行用户身份验证和防止 CSRF 攻击。_spr
) 和浏览器信息 (User-Agent
)。接下来可以做什么?
content
和 visible
,看看会对发送结果产生什么影响。requests
库来构造和发送 POST 请求,模拟发送微博的过程。关于远程地址(Remote Address):
127.0.0.1:7890
以及相关的ip
概念确认
上面我的服务器请求中的远程地址, 确实是我使用的代理地址. 我使用的是 Clash 的“规则”模式而不是全局, 按理说, 会根据规则列表来判断哪些流量走代理,哪些流量直连。如果规则设置正确,访问国内网站时应该不会走代理. 那这里为什么走了代理?
可能原因:
求证方案:
114.114.114.114
或 8.8.8.8
,以避免 DNS 污染。解惑
我在 https://github.com/Loyalsoldier/clash-rules 查了 Clash 直连域名列表 direct.txt
, 确定了没有 m.xxx.cn
(连 xxx.cn
都没有). 现在可以确定确实使用了代理服务器. 127.0.0.1
是本地回环地址,用于指代本机。之所以显示这个地址,是因为 Clash 在我的本机上启动了一个代理服务,并监听 7890 端口。 当我的浏览器或其他应用程序通过 Clash 代理访问网络时,它们的请求会先发送到 Clash 的代理服务(也就是 127.0.0.1:7890
),然后由 Clash 根据规则决定是将请求直连还是转发到代理服务器。
所以,即使访问的 m.xxx.cn
是国内网站,如果浏览器设置了使用 Clash 代理,那么远程地址仍然会显示为 127.0.0.1:7890
。这并不意味着请求没有直连,而是因为 Clash 在中间做了一层转发。
请求实际上是:
我的应用程序 -> Clash 代理服务 (127.0.0.1:7890) -> (根据规则) -> 代理服务器 或 直连目标网站
根据我的终端 ipconfig
可以知道, 我的 ipv4 地址是 172.xx.xx.x
,而这个地址 是局域网 IP(私有 IP)。
电脑通过路由器连接到互联网,路由器会分配一个局域网 IP 给电脑,例如 172.xx.xx.x
。路由器还有一个公网 IP,用于与外部网络通信。
关于 192.168.2.1
这个以太网 ip, 很可能是路由器的 IP 地址,而不是 ISP(互联网服务提供商)的地址。
192.168.x.x
是一个常用的局域网 IP 地址段,很多路由器默认使用这个网段。网上很多人的以太网 IP 都是 192.168.x.x
,是因为他们都使用了路由器,并且路由器使用了默认的 IP 地址段。
关于 https://m.xxx.cn/api/statuses/update
的访问结果:
尝试直接访问 https://m.xxx.cn/api/statuses/update
得到以下结果:
{
"ok": 0,
"errno": "100007",
"msg": "不符合的请求方式",
"extra": ""
}
这是因为使用了 GET 方法访问了这个 API 接口,而它只接受 POST 请求。"不符合的请求方式"
指的就是这个意思。这也进一步佐证了发送微博的请求方式是 POST
.
关于 visible
、st
参数:
由于刚才我的一条微博设置了仅自己可见, 我们可以确定 visible: 1
表示“仅自己可见”. 观察更早的几分钟前发的几条微博的数据, 我发现, 这一段时间请求体中不同的微博内容, 有相同的 st: f79388
,接下来我新发送了 2 条微博, 一条公开, 发现请求体中更新为 st: f13778
,visible
这一行消失; 一条朋友圈可见, st: f13778
,visible: 6
. 不同时间段的 st
值会发生变化。这表明 st
很可能是一个 与时间相关的 token。它可能用于标识某个时间段内的请求,或者用于防止 CSRF 攻击。
可以知道:
visible: 0
(或不存在): 公开visible: 1
: 仅自己可见visible: 6
: 朋友圈可见可以尝试发送更多不同可见性的微博来验证这个推断
使用 requests
构造请求:
刚才我们已经抓包得到了请求头中的 User-Agent
字段, 所以接下来我们可以使用 Python 的 requests
库来构造一个 POST 请求,并设置请求头,来模拟发送微博的过程。
为什么要构造请求头?
User-Agent
字段来识别客户端的类型。如果我们想让服务器认为我们的请求来自一个正常的浏览器,就需要设置一个正确的 User-Agent
。Referer
、Origin
等,来判断请求是否合法。我们需要根据实际情况设置这些字段,才能通过服务器的验证。Cookie
、Accept-Encoding
等,这些信息可以告诉服务器更多关于客户端的信息。为什么之前的脚本(如 GhostwriterWeibo_v2.py
)没有构造请求头?
之前的脚本使用了 Selenium,Selenium 会自动控制浏览器发送请求,所以它会自动设置请求头,我们不需要手动设置。这也是这几行关于设置请求头的代码被注释掉的原因,因为对于 Selenium 来说,手动设置请求头是没有意义的.
# 设置请求头, 会被selenium忽略, 没有意义
# if headers:
# edge_options.add_argument(f'--user-agent={headers["User-Agent"]}')
代码示例:
下面是一个使用 requests
库构造请求并发送微博的示例代码:
import requests
def send_weibo(content, visible=0, cookie=None, st=None):
"""使用 requests 库发送微博
Args:
content (string): 微博内容
visible (int, optional): 可见性. 0: 公开, 1: 仅自己可见, 6: 朋友圈可见. Defaults to 0.
cookie (string, optional): 微博 Cookie. Defaults to None.
st (string, optional): 用于验证的 token, 需要抓包获取. Defaults to None.
"""
url = "https://m.xxx.cn/api/statuses/update"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
"Referer": "https://m.xxx.cn/compose/",
"Origin": "https://m.xxx.cn",
"Content-Type": "application/x-www-form-urlencoded",
"X-Requested-With": "XMLHttpRequest",
"X-XSRF-TOKEN": st, # 使用抓包获取到的 st 值
"Cookie": cookie, # 使用你的经过处理的 Cookie
}
if visible == 0:
data = {
"content": content,
"st": st,
"_spr": "screen:1920x1080" # 根据屏幕分辨率修改
}
else:
data = {
"content": content,
"visible": visible,
"st": st,
"_spr": "screen:1920x1080" # 根据屏幕分辨率修改
}
response = requests.post(url, headers=headers, data=data)
if response.status_code == 200:
result = response.json()
if result["ok"] == 1:
print("微博发送成功!")
else:
print(f"微博发送失败: {result['msg']}")
else:
print(f"请求失败: {response.status_code}")
# 使用示例
# 从浏览器开发者工具中复制 Cookie 和 st 的值
my_cookie = "经过处理的Cookie" # 替换成你的经过处理的 Cookie
my_st = "f13778" # 替换成抓包获取到的 st 值
send_weibo("发送一条公开微博!", cookie=my_cookie, st=my_st) # 发送一条公开微博
send_weibo("测试使用 requests 发微博, 仅自己可见!", visible=1, cookie=my_cookie, st=my_st) # 发送一条仅自己可见的微博
send_weibo("一条朋友圈可见的微博", visible=6, cookie=my_cookie, st=my_st) # 发送一条朋友圈可见的微博
代码说明:
send_weibo
函数:
content
:微博内容。visible
:可见性,默认为 0
(公开)。cookie
:微博 经过处理的 Cookie。需要从浏览器开发者工具中复制 Cookie,并粘贴到这里。st
:用于验证的 token。需要从抓包获取的 X-XSRF-TOKEN
中复制 st
的值,并粘贴到这里。url
: 微博发送 API 的地址。headers
: 请求头。需要根据抓包获取的信息来设置 User-Agent
、Referer
、Origin
、Content-Type
、X-Requested-With
、X-XSRF-TOKEN
和 Cookie
。data
: 请求体。需要根据 visible
的值来构造请求体。requests.post
: 发送 POST 请求。response.json()
: 将响应数据解析为 JSON 格式。ok
字段来判断微博是否发送成功。如何获取 Cookie
和 st
:
m.xxx.cn
) 的发微博页面。F12
打开开发者工具,切换到 Network
面板。Preserve log
和 Disable cache
选项。Network
面板中,找到发送微博的 POST 请求。Headers
选项卡中,找到 Request Headers
部分。Cookie
的一部分.X-XSRF-TOKEN
的值(就是 st
的值)。重要提示:
Cookie
和 st
都有有效期,过期后需要重新获取。具体时效将在后文继续探讨.现在, 尝试修改代码中的参数,例如微博内容、可见性、Cookie
和 st
, 使用抓包获取的值, 看看会对发送结果产生什么影响.
我尝试发送了 3 条不同可见性的微博, 可以验证 0
公开, 1
仅自己可见, 6
朋友圈可见的推断.
关于 st
值:
st
值可能会在一段时间内保持不变。抱着侥幸心理, 我先尝试了使用之前的 f13778
,看看能不能发送成功。
st
值在测试的时间段内仍然有效。st
值已经过期,或者该平台更新了安全策略。需要重新抓包获取新的 st
值。请注意: st
值很可能与时间有关,或者与登录状态有关。如果隔了很长时间再次运行代码,或者该平台账号退出了登录,很可能需要重新获取 st
值。
关于抓包:
以上研究网络请求并抓取数据的过程,就是抓包!
抓包(Packet Capture)是指捕获网络上传输的数据包的过程。我们通常使用一些工具来辅助抓包,例如浏览器开发者工具、Wireshark、Fiddler、Charles 等。
Network
面板来查看网络请求,就是一种非常常用的抓包方式。 它可以方便地查看 HTTP 请求和响应的详细信息,包括请求头、请求体、响应头、响应体等。Wireshark vs. 浏览器开发者工具:
特性 | 浏览器开发者工具 | Wireshark |
---|---|---|
捕获范围 | 仅限于当前浏览器的 HTTP/HTTPS 请求 | 可以捕获所有经过网卡的网络数据包 |
使用难度 | 相对简单,适合 Web 开发调试 | 更复杂,需要一定的网络协议知识 |
功能 | 主要用于 Web 开发调试,查看 HTTP 请求和响应的详细信息 | 功能更强大,可以进行更深入的网络分析 |
实时性 | 实时显示网络请求 | 可以实时捕获数据包,也可以保存数据包供以后分析 |
协议支持 | 主要支持 HTTP/HTTPS | 支持多种网络协议,例如 TCP、UDP、IP、ARP、ICMP 等 |
操作系统支持 | 跟随浏览器 | 跨平台,支持 Windows、macOS、Linux 等 |
小结:
当我使用刚才的 st
, 终端显示:
请求失败: 403
请求失败: 403
请求失败: 403
代码 403, 被服务器拒绝了, 所以我重新手动发送微博, 发现 st
已经更新, 而且 cookie
也跟着更新, 我的 cookie
从
_T_WM=...; XSRF-TOKEN=f13778
变成了
_T_WM=...; XSRF-TOKEN=8ea381; mweibo_short_token=bf3d747a9d
而 st
也藏在 cookie
中. 我怀疑 m.xxx.cn
比多数人使用的网页版该平台 xxx.com
的 cookie
更新更快, 基本上是几十分钟就更新(实际上这时候的说法并不准确, 请看下文继续分析).
更新 cookie
和 st
之后, 发送成功:
微博发送成功!
微博发送失败: 发微博太多啦,休息一会儿吧!
微博发送失败: 发微博太多啦,休息一会儿吧!
st
值和 Cookie
的更新: st
值和 Cookie
几乎是同时更新的,这表明它们之间存在很强的关联性。st
值很可能是基于 Cookie
生成的,用于验证用户的登录状态和防止 CSRF 攻击。st
值藏在 Cookie
中: XSRF-TOKEN
的值和 Cookie
中的某个字段值相同,这进一步证实了 st
值和 Cookie
之间的关联性。这很可能是该平台的一种安全机制,用于验证请求的合法性。Cookie
更新频率: 怀疑 m.xxx.cn
的 Cookie
更新频率比 xxx.com
更快,这很可能是因为移动端网站更注重安全性,所以会更频繁地更新 Cookie
。关于发送结果:
Cookie
和 st
值是有效的。time.sleep(60)
,让程序暂停 60 秒。m.xxx.cn
和 xxx.com
的 Cookie
更新机制,看看能不能发现更多有趣的规律。XSRF-TOKEN
和 SUB
: XSRF-TOKEN
的值和 Cookie
中 SUB
的一部分相同, 这是该平台的一种安全策略。SUB
是一个经过 Base64 编码的字符串,里面包含了用户的一些信息,以及一个用于验证请求合法性的 token。XSRF-TOKEN
的值通常是 SUB
中 token 部分的哈希值。为了避免频繁更新 Cookie
和 st
,将它们保存到文件中,并在每次发送微博之前检查它们是否过期。如果过期了,再重新获取。
另外, 把 XSRF-TOKEN
单独定义出来, 这样更方便:
import requests
import time
import json
def send_weibo(content, visible=0, cookie=None, x_xsrf_token=None):
"""使用 requests 库发送微博
Args:
content (string): 微博内容
visible (int, optional): 可见性. 0: 公开, 1: 仅自己可见, 6: 朋友圈可见. Defaults to 0.
cookie (string, optional): 微博 Cookie. Defaults to None.
x_xsrf_token (string, optional): 用于验证的 token, 需要抓包获取. Defaults to None.
"""
url = "https://m.xxx.cn/api/statuses/update"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
"Referer": "https://m.xxx.cn/compose/",
"Origin": "https://m.xxx.cn",
"Content-Type": "application/x-www-form-urlencoded",
"X-Requested-With": "XMLHttpRequest",
"X-XSRF-TOKEN": x_xsrf_token,
"Cookie": cookie,
}
if visible == 0:
data = {
"content": content,
"st": x_xsrf_token,
"_spr": "screen:1920x1080" # 根据屏幕分辨率修改
}
else:
data = {
"content": content,
"visible": visible,
"st": x_xsrf_token,
"_spr": "screen:1920x1080" # 根据屏幕分辨率修改
}
response = requests.post(url, headers=headers, data=data)
if response.status_code == 200:
result = response.json()
if result["ok"] == 1:
print("微博发送成功!")
return True
else:
print(f"微博发送失败: {result['msg']}")
return False
else:
print(f"请求失败: {response.status_code}")
return False
# 使用示例
# 从浏览器开发者工具中复制 Cookie 和 X-XSRF-TOKEN 的值
my_cookie = "修改成你的经过处理的Cookie" # 替换成你的经过处理的 Cookie
my_x_xsrf_token = "8ea381" # 替换成抓包获取到的 X-XSRF-TOKEN 值
# 首次发送
send_weibo("发送一条公开微博!", cookie=my_cookie, x_xsrf_token=my_x_xsrf_token)
# 增加延时
time.sleep(60)
# 再次发送
send_weibo("测试使用 requests 发微博, 仅自己可见!", visible=1, cookie=my_cookie, x_xsrf_token=my_x_xsrf_token)
# 增加延时
time.sleep(60)
# 再次发送
send_weibo("一条朋友圈可见的微博", visible=6, cookie=my_cookie, x_xsrf_token=my_x_xsrf_token)
Cookie
的生成机制: 尝试分析 Cookie
中各个字段的含义,看看能不能找到生成 Cookie
的算法。st
值的生成机制: 尝试找出 st
值与时间、Cookie
和其他参数之间的关系。CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种常见的 Web 安全漏洞,它允许攻击者诱导用户执行他们不打算执行的操作。
攻击原理:
Cookie
附加到请求中,因此伪造的请求看起来就像是由用户自己发送的一样。举个例子:
假设你已经登录了该平台,攻击者诱使你访问了一个恶意网站。恶意网站中包含以下代码:
<img src="https://xxx.com/api/statuses/update?content=我是被攻击者发布的微博&visible=0" width="0" height="0">
在这个例子中, 攻击者将恶意 URL 嵌入到了 HTML 的
标签的 src
属性中. 而且 不一定需要点击图片。 只要浏览器加载了这个
标签,就会自动向 src
属性指定的 URL 发送请求,无论图片是否可见或是否被点击。在这个例子中,width="0" height="0"
使得图片在页面上不可见,所以用户甚至察觉不到这个请求的发生。当你访问这个恶意网站时,浏览器会自动向 https://xxx.com/api/statuses/update
发送一个 GET 请求,并附带上你的该平台 Cookie。该平台服务器收到请求后,会认为是你自己发布的微博,并执行相应的操作。
针对m.xxx.cn
, 之前我们已经得出发送微博的 API 接口地址是 https://m.xxx.cn/api/statuses/update
, 所以应该为https://m.xxx.cn/api/statuses/update?content=我是被攻击者发布的微博&visible=0
, 这里原理相同.
?
: 问号表示 URL 中传递参数的开始。content=我是被攻击者发布的微博
: 这是微博内容的参数。&
: & 符号用于分隔多个参数。visible=0
: 这是微博可见性的参数,0
表示公开。width="0" height="0"
不是指屏幕分辨率,而是 HTML
标签的属性,用于设置图片的宽度和高度。这里将图片的宽度和高度都设置为 0
,是为了让图片在页面上不可见,从而隐藏攻击行为。
攻击者通常通过以下几种方式诱导用户访问包含恶意代码的页面:
Referer
字段,确保请求来自可信的来源。XSRF-TOKEN
的作用, 通常也会藏在 Cookie
中, 例如某个特定的字段, 就我们的抓包结果来看, 可能是 SUB
字段的一部分.XSRF-TOKEN
,通常它的值和 Cookie
中 SUB
字段的某一部分相同。Cookie
中。所以,st
(或 XSRF-TOKEN
)是用于防御 CSRF 攻击的 token,而 session ID 通常存储在 Cookie
中。它们之间有一定的关联,但不是同一个东西。
在 CSRF 攻击的例子中,Referer
的值取决于攻击者如何构建恶意页面。
![]()
标签嵌入到自己的网站中,那么 Referer
将是攻击者网站的地址。![]()
标签嵌入到其他存在 XSS 漏洞的网站中,那么 Referer
将是该存在 XSS 漏洞的网站的地址。Referer
将为空,因为不是从其他页面跳转过来的。需要注意的是,Referer
字段可以被伪造或禁用,因此它不能作为防御 CSRF 攻击的可靠手段。
SUB
的解码:我尝试使用 Base64 解码 SUB
,但解码结果是乱码:؍)î\7g/XU'ŏ< ui A_5z=B[Ѝ-yБmML%쟕鮨ߒĚŶ % O̝
。
这是因为 SUB
并不是一个简单的 Base64 编码字符串,它还包含了一些其他信息。尝试使用 Base64 解码 SUB
的值得到乱码是正常的,因为 SUB
的值不仅仅是 Base64 编码那么简单. 它里面可能包含了一些二进制数据或者使用了其他的编码方式。
st
的过期时间:我在脚本测试中发现 cookie
和 st
又过期了, 所以我又重新发微博, 计算出前后两次失败的时间是 15-16 分钟左右, 所以目前预估这个域名 m.xxx.cn
的 cookie
和 st
的过期时间大约是 15-20 分钟。后续我会继续通过实验和观察来验证这个过期时间。
提示: 可以设置一个定时任务,每隔一段时间(例如 10 分钟)自动使用脚本测试
Cookie
和st
的有效性。如果发现Cookie
或st
过期,则发出提醒。
总结:
这篇博客记录了对 m.xxx.cn
发微博 API 的逆向工程过程,包括:
POST
请求的参数和请求头requests
库构造 POST
请求并发送微博st
参数和 Cookie
的关联性和过期时间的初步探索通过这次逆向工程实践,我更深入地理解了 Web 开发的底层原理,也学习了如何使用 Python 进行网络编程和安全攻防。
未来的探索方向:
Cookie
和 st
的生成和过期机制。