哆啦校圈 API 分析
免责声明:本文仅供技术研究和学习交流之用。请勿将文中所述技术用于非法用途,作者不对任何滥用行为承担责任。
需求
哆啦校圈是一个同济学生比较活跃的校内交流平台,然而里面鱼龙混杂,时不时乌烟瘴气。对获取校内资讯造成了很大障碍。目前来看,有一个恶心的问题,便是拉黑系统。
拉黑操作是这样的,找到一个帖子,点击发帖人的头像。如果发帖人是非匿名发帖,则出现一个拉黑按钮,点击后便可以把对方拉黑 1个月。
如果是匿名发帖,您还真就拿对方没办法了,因为如上操作对匿名发帖无效,并不会有拉黑按钮的出现。那么就只能看着对方宣泄情绪、带节奏而无可奈何。
这样不好!
因此,本文章要完成的主要任务是提供一些背景知识,使得如下目的能够但不一定便捷实现:
- 得以拉黑匿名发帖;
- 实现永久拉黑
我们的美好期望能够实现吗?可能会遇到一些问题吧?比如说,如果定位不到匿名发帖人,怎么办?如果拉黑了发帖人,只能屏蔽正常发帖,仍然无法屏蔽匿名发帖,怎么办…
谢谢这个草台班子一样的世界,这些问题都能够得到攻克。
太长不看版
问题 1 的初步解决
注意到在帖子详情页,如果在发帖人部分出现了 关注
按钮,点击关注,然后在 我的
-> 设置
-> 关注列表
就可以查看没有马甲的页面,从而可以进行拉黑。
但是如果用户本身关闭了主页开放,这个按钮不会出现。这时候就只好抓包了,那就请看后面的内容(## 主观评价 => ### 问题 1 的最终解决
)。
总结一下,匿名 / 不匿名;主页开放 / 主页不开放 组成了四个状态,其中
发帖状态 | 主页状态 | 是否可查看用户信息 | 拉黑按钮 | 关注状态 |
---|---|---|---|---|
不匿名 | 开放 | 是 | 有 | 可关注 |
不匿名 | 不开放 | 否 | 有 | 无法关注 |
匿名 | 开放 | 否 | 无 | 可关注,然后拉黑 |
匿名 | 不开放 | 否 | 无 | 无法关注 |
前三种隐私的设置情况,可以通过图形界面的迂回方案来解决,而第四种必须抓包。
问题 2 的解决
注意到在 我的
-> 设置
-> 黑名单
中可以查看到所有被拉黑的用户,就算拉黑的 1 个月期限过了,也只是解除拉黑,但拉黑记录还在,很方便就可以进行反复拉黑,结合后面提到的 API,可以进行自动化操作。
软件与环境
- Windows 11 专业版 24H2
- 微信电脑版 3.9.12.37
- 校圈
- Charles v4.6.7
- Postman 11.31.1
熟悉接口…之前
在介绍接口之前,得讲讲校圈的特殊性。
因为校圈是微信小程序-only
,因此抓取接口比纯粹的网页内容要多一步,便是进行抓包。
使用 Charles
来抓包
Charles
的官网如下描述它的功能:
Charles is an HTTP proxy / HTTP monitor / Reverse Proxy that enables a developer to view all of the HTTP and SSL / HTTPS traffic between their machine and the Internet. This includes requests, responses and the HTTP headers (which contain the cookies and caching information).
简而言之,是充当了中间人角色来监听本机的所有流量请求。
它的配置,网络上有不少帖子表述得很清楚了:
如这篇 CSDN
上的文章。
这里要补充说明的是,PC 的配置按帖子中的描述就可以了,而移动端的配置略微复杂一些。
移动端的配置——没必要
- 问题一:为什么要配置证书?按 ChatGPT 的解释:
当我们使用 Charles 或其他类似的抓包工具时,Charles 需要在客户端和服务器之间充当一个中间人,也就是中间人攻击(MITM,Man-in-the-Middle)的方式来解密和查看 HTTPS 的通信内容。这就要求 Charles 拿到传输过程中加密数据的明文内容。
没有证书的情况下:
当你没有安装 Charles 生成的证书 时,浏览器(或任何其他客户端,如微信小程序)会在访问 HTTPS 链接时,发现其证书是 由 Charles 代理生成的,而不是由一个受信任的证书颁发机构(CA)颁发的。
浏览器会认为这是一个不安全的链接,因为它无法验证该证书的合法性,通常会显示 “您的连接不是私密连接” 或类似的警告信息。
这里举一个例子,例如如果在没有打开 Charles
的情况下,访问 www.baidu.com
,证书颁发者的公用名是 GlobalSign RSA OV SSL CA 2018
,而如果打开了 Charles
,并且要代理 HTTPS
的请求时,就会发现颁发者的公用名为 Charles Proxy CA (7 Feb 2025, XIALING)
。接下来会发生的事情,AI 已经解释得很清晰了,不再赘述。
- 问题二:为什么要配置移动端的证书?
简而言之,因为微信小程序在手机上运行啊!
思考几秒钟,我们突然想到:
谁说微信小程序一定要在手机上运行啊?电脑版微信不是也有小程序吗?
此言得之。因此,配置移动端的证书不是必要的!
不过,如果要配置的话,in case you need it,有以下几个问题是我遇到过的:
证书的安装
使用常见网络教程提供的chls.pro/ssl
无法下载证书,返回的内容为空,chrome 浏览器的提示是:chls.pro 未发送任何数据
,需要切换到www.charlesproxy.com/getssl
才能下载证书。只能抓包浏览器应用,其他 APP 不可以
问题的原因,官网已经解释的很清晰了,在Android
section 是这样说的:
As of Android N, you need to add configuration to your app in order to have it trust the SSL certificates generated by Charles SSL Proxying. This means that you can only use SSL Proxying with apps that you control.
我们只能代理我们控制的 APP,而微信是腾讯控制的,因此无法实现代理。不过有的解决方案提出,手机 ROOT 后经过一些设置就能够实现。我想想,太麻烦了,因此放弃,感兴趣的读者可以自行探索。
PC 的设置
安装好了软件,配置好了证书(参见上方 CSDN 的链接),时间来到抓包之前,现在要设置如下几个事项。
在 Proxy
页签中设置 Start Recoding
和 Start SSL Proxying
,懒得翻导航栏的话,快捷按钮是下图的两个:
此外,在 Proxy
> SSL Proxying Settings...
> Include下的(Add)
中添加个通配符,懒人设置,代理所有连接。具体见下图:
如果这样设置后,打开一些网络页面,左侧监听列表仍为空白,看看是不是在 Proxy
菜单勾选了 Windows Proxy
或者 macOS Proxy
(上图的对应位置是√
)。如果电脑上运行了不止一个代理软件的话,可以尝试关闭其他代理后,重启 Charles
。仅仅关闭某小猫是不行的,重启 Charles
后才好。
几个接口
可以右键感兴趣的链接,选择 Focus
,这样就会把我们感兴趣的链接单独划分出来,剩下的其他链接归为一类(Other Hosts)。
这些接口都需要登录才能访问,不过认证方法也很简单。在请求头的 token
字段中,把值复制一下就可以了。值应该是长时间不变的。
我们发现,在浏览校圈时,有这样几个非平凡的接口,在这些非平凡的接口中更非平凡的,将用 ※
标注出来。
获取最近发布的帖子列表
接口:https://cdn.dolacc.com
+ /api/Wxpostv2/getPostsCdnpw
方法:GET
Query String:
Parameter | Value |
---|---|
toId | 0 |
scId | 24 |
pageSize | 10 |
lastindex | -1 |
keyword | |
freshKey | 23333 |
这几个参数中:
scId
有用,sc
可能是 School Circle 的意思吧?24 表示同济大学的校圈编号;
lastIndex 也有用,表示的是上一次请求的最早时间的帖子。例如,下面返回格式中,data.posts[9].id
如果是 23333
,那么在下一次请求的时候,这个值就会是 23333
。
pageSize 没用,默认是 10,但是修改为其他大小,也不会返回更多内容。
返回格式:
1 | { |
定期获取新帖子数量
接口:https://luntan.dolacc.com
+ /api/wxUser/countNewMessagev4
方法:GET
返回格式:
1 | { |
补充说明:15s 调用一次,非平凡的数据是 data.newMessageCount
,这个数量统计的应该是最近一次请求 posts 后的新帖子数量。
获取所有主题
接口:https://www.dolacc.cn
+ /api/wxPost/getAllTopic
方法:GET
返回格式:
1 | { |
获取十大热帖
接口:https://www.dolacc.cn
+ /api/wxPostv2/getTop10v3
方法:GET
Query String:
Parameter | Value |
---|---|
scid | 24 |
返回格式:
1 | { |
※获取帖子详情
接口:https://cdn.dolacc.com
+ /api/wxPostv2/visitPostCdnpw
方法:GET
Query String:
Parameter | Value |
---|---|
pId | 2244815 |
nocache | -1 |
orderType | 1 |
freshkey | 3 |
pId 是帖子的 id,非平凡。剩下的带不带都可以。
返回格式:
1 | { |
获取悬赏详情(接口同上)
接口:cdn.dolacc.com
+ /api/wxPostv2/visitPostCdnpw
方法:GET
Query String:
Parameter | Value |
---|---|
pId | 2244299 |
nocache | -1 |
orderType | 1 |
freshkey | 2 |
返回格式:
1 | { |
※查成分(点击发帖人头像)
接口:luntan.dolacc.com
+ /api/wxUser/getUserMsg
方法:POST
Payload:
1 | { |
返回格式:
1 | { |
※用户登录
接口:luntan.dolacc.com
+ /api/wxUser/fetch
方法:GET
返回格式:
1 | { |
※获取拉黑列表
接口:luntan.dolacc.com
+ /api/wxUser/getMyBlock
方法:GET
返回格式:
1 | { |
※拉黑/取消拉黑用户
接口:luntan.dolacc.com
+ /api/wxUser/blockMyuser
方法:POST
Payload:
1 | { |
返回格式:
1 | { |
获取关注列表
接口:www.dolacc.cn
+ /api/Wxpostv3/getMyLike
方法:GET
返回格式:
1 | { |
关注某个用户
接口:luntan.dolacc.com
+ /api/Wxuserv2/addUserLike
方法:POST
Payload:
1 | { |
返回格式:
1 | { |
取关某个用户(在关注了的用户主页取消)
接口:luntan.dolacc.com
+ /api/Wxuserv2/cancelLike
方法:POST
Payload:
1 | { |
返回格式:
1 | { |
取关某个用户(在自己的关注列表取消)
接口:www.dolacc.cn
+ /api/Wxuserv2/cancleLike
方法:GET
Query String:
Parameter | Value |
---|---|
beLikeUser | 347868 |
返回格式:
1 | { |
name
字段的破解
在上面的接口中,查看最近 10 条帖子、查看帖子/悬赏的详细内容需要使用到 name
字段,然而它是被编码的,无法获得明文,如何对敌?
显然,这是一个 base64 编码后的内容,看看能不能直接转换为 utf-8?
以 n0gKQU2McCfEXG2g17KqDQ==
为例。
运行如下的 python
脚本:
1 | import base64 |
报错:
UnicodeDecodeError: ‘utf-8’ codec can’t decode byte 0x9f in position 0: invalid start byte
推测进行了加密。如何加密的呢?
思路一:抓包
顺着 web
逆向的思路,看看小程序有没有向哪里请求 js
文件?如果有的话,全文检索 decrypt
不就好了?找到 iv
和 key
就行了。为什么推测是 AES
而不是 RSA
,因为把私钥放到客户端很奇怪呀。
结果走不通。
因为微信小程序的原理是,js 在本地,而不需要向哪个服务器请求。
思路二:反编译微信小程序
首先,定位微信小程序的位置。它们被存储在 C:\Users\<username>\Documents\WeChat Files\Applet
下,文件夹以 wx
打头,形如 wx9ddd73d26fdbacba
,这个就是校圈的文件夹,而打包后的文件名是 __APP__.wxapkg
,目录结构是:Applet/wx9ddd73d26fdbacba/315/__APP__.wxapkg
咋就定死了这是校圈小程序呢?有两种确认方案:
- 把
Applet
下的所有小程序文件夹都删除,打开校圈,看多了哪个文件夹; - 看看
Charles
请求头中的Referer
,形如https://servicewechat.com/wx9ddd73d26fdbacba/315/page-frame.html
,315
露出了鸡脚(看看上文的目录结构)。
找到了 __APP__.wxapkg
,需要先解密。有一个 Github 项目,使用方法很清晰。不妨设解密后的文件是 dec.wxapkg
。
之后,在这个 Github 项目 中,把 dec.wxapkg
进行解包,虽然最后会报错,不过我们已经得到足够的文件了。
报错信息:
1 | C:\wxappUnpacker-main\node_modules\vm2\lib\transformer.js:86 |
在解包后的 dec/
文件夹中,找 js
文件,发现在 app-service.js
中,有一段代码如下:
1 | define("utils/ase.js", function( |
有了找到了 iv 和 key,就可以在 python
中进行破译:
1 | # 需要安装依赖:pip install pyCryptodome |
主观评价
总的来说,校圈的安全性还是比较低的。根源在于其后端返回的内容太多了,仅仅使用纯前端来阻止敏感信息的查看。这样的应用实例在浏览器的界面下很容易被破解,然而在微信小程序的外壳下,安全了一点,但不多。部分内容使用了 AES
加密,然而 inconsistent——不连贯。有的正文信息加密了,有的又没有加密:帖子的正文,没啥敏感的,需要额外一步破解;然而悬赏中只能让接单人看到的信息不根据用户身份来区分,随意从后端返回也就罢了,居然还是明文显示..信息的敏感程度和获得的加密水平不一致。
我想到的漏洞的利用有:
- 悬赏中的敏感信息,包括但不限于快递取件码、手机号码、微信等联系方式,存在被其他用户读取的可能,而且是明文传送,很简单就能读取;
- 仅限认证用户查看的内容,未认证用户也可以查看;
- 主页已关闭,还是能查看到昵称和发帖历史。
不过后端返回的内容丰富对爬虫来说是好事情,可以获取到更丰富的信息。得益于一些接口的设置,我们可以查找到匿名发言用户的 id
,进而通过 id
定位到真实昵称和最近的几条发帖。拉黑接口仅仅接受 id
,也让匿名拉黑成为了可能。
问题 1 的最终解决
如何拉黑一个匿名用户呢?首先,要么找到 TA 发的帖子,要么点 TA 的头像,从这两个包中可以定位到用户的 id
,id
有了,通过已经存在的接口便能够实现拉黑的功能。这一方法是万能的,在关注按钮没有出现的情况下,仍然可行。
这里需要说明,模拟 HTTP
请求,推荐使用 Postman,上手很简单。
个人收获
这次本来是想爬校圈的帖子和评论的,正好用来熟悉数据库,但是想想没啥必要,不如把接口分析分析,故有了本文。这次分析 APi 的经历让我体会到:
漏洞是常常存在的,网络并非十分安全。一些简单的应用压根没有做好前后端的沟通以及数据的保护;
一些 API 的设计是重复冗余的,可能和一开始没有定好文档有关,最后积累成了屎山;
有时候为了达成自己的目的,不一定需要用大炮轰蚊子的技术。这次为了拉黑一些用户,在没有充分考察校圈本身的功能之前,就决定使用压箱底的技术——抓包+逆向。其实没必要,以校圈的设计水平,多少会有一些可能出现的漏洞存在。还是应该循序渐进,切记切记。不过多熟悉熟悉大炮也是好事情。
这文章看似很长,实际上多数是 API
的罗列,我自己也不愿意看完。可以当个厕纸读物,得意而忘言吧!