用 Python 复现极域 V6.0 教师端通信协议
本文介绍一个基于 Python 的第三方极域课堂管理系统 V6.0 教师端模拟器。项目通过静态逆向分析与网络抓包,还原教师端和学生端之间的 UDP 通信过程,并将发现、登录、保活、消息、学生信息上报与屏幕缩略图等能力组合成一个可观察、可验证的协议实验平台。
本项目仅用于协议学习、安全研究与教学演示。请只在自己拥有或已获得明确授权的网络和设备上测试。
项目源码:yunsjxh/Third-party-JiYu-Teacher-Endpoint
为什么要重新实现一个教师端
课堂管理软件看起来是一个普通的桌面应用,但它背后其实包含了一套完整的局域网通信协议:教师端需要先在网络中宣告自己的存在,学生端发现教师后发起登录,双方继续交换保活包、状态包和控制包;当教师请求屏幕预览时,学生端还要把一张图像拆成多个 UDP 数据片段再发送回来。
如果只观察软件界面,我们看到的是“学生上线”“发送消息”“查看缩略图”等按钮;从协议研究的角度看,真正值得追踪的是另一组问题:
- 学生端如何判断网络中存在一个合法的教师端?
- 登录握手由哪些数据包组成,顺序是否重要?
- 同一个界面功能是否可能由多条协议路径共同完成?
- UDP 不保证到达和顺序,缩略图应当如何重组?
- 二进制协议中的长度、字节序、编码和填充字段如何确定?
这个项目的目标不是简单模仿一个界面,而是把这些藏在可执行文件和网络流量里的规则,整理成一份能够运行的 Python 实现。
从黑盒到协议地图
协议还原主要依靠两类证据。
第一类是 IDA Pro 静态分析。它适合定位协议魔数、字段偏移、分支条件和关键常量。例如,一个 32 位整数在内存中以小端序出现时,0x434E4F4F 对应 ASCII 字符串 OONC;沿着接收函数继续追踪,就能看到程序如何按照不同 Magic 进入不同处理分支。
第二类是真实环境中的网络抓包。静态分析可以告诉我们“代码可能怎样解释这个字段”,抓包则能验证“正常教师端实际上发送了什么”。将多组样本逐字节比较后,才可能区分固定值、序列号、GUID、IP 地址、长度字段和可选负载。
最终得到的通信结构可以概括为两条 UDP 通道:
| 通道 | 地址 | 主要职责 |
|---|---|---|
| 主通道 | 224.50.50.42:4705 | 教师发现、保活、控制命令、屏幕缩略图 |
| 会话通道 | 225.2.2.1:5512 | 登录宣告、MESS 消息、状态与信息上报 |
教师端会周期性发送 OONC、CANC 和 ANNO,学生端通过 LOGI 进入登录流程,并用 KACA、TRMC 等报文维持会话。教师端随后回送 WACA、LPNT 和 DMOC,让学生端确认教师身份并进入稳定连接状态。
教师端模拟器 极域学生端
| |
|---- OONC / CANC / ANNO --------->| 发现教师
|<------------- LOGI --------------| 请求登录
|---- MESS / LPNT / DMOC ---------->| 完成握手
|<------------- KACA ---------------| 保活/发现
|---------------- WACA ------------>| 保活响应
|---- TNRS / 信息请求 / 消息 ------->| 业务请求
|<---- LANT / TNAL / MESS ----------| 数据回流这张图也是整个程序的骨架。实现不再是一堆彼此孤立的十六进制常量,而是广播线程、会话接收线程、主通道接收线程和命令行交互之间的一组协作关系。
Python 如何表达二进制协议
项目没有引入复杂的协议框架,而是直接使用标准库中的 socket 和 struct。原因很直接:协议字段大多是固定长度的小端整数、GUID、IPv4 地址和 UTF-16LE 字符串,struct.pack 足以准确表达它们。
以教师在线宣告包为例,其实现由 Magic、版本、包体长度、教师 GUID、本机 IP 和递增序列号组成:
pkt = (
struct.pack('<II', 0x434E4F4F, 0x10000)
+ struct.pack('<I', 16)
+ teacher_guid.bytes_le
+ socket.inet_aton(local_ip)
+ struct.pack('<III', 1, 1, sequence)
)这里最容易出错的并不是 Python 语法,而是协议细节:
<I表示小端无符号 32 位整数;- Windows GUID 的内存布局需要使用
bytes_le; - 中文文本必须按 UTF-16LE 编码,并正确计算字符数和结尾空字符;
- 包头中的长度不一定等于最终 UDP 数据报长度,而可能只描述某一层负载;
- 某些接收路径会额外读取负载尾部,因此构造包时需要保留兼容填充。
二进制协议复现的核心不是“让程序发出数据”,而是让每个字节都能对应到可解释、可验证的字段。
难点一:黑屏和键鼠锁定其实是两条路径
项目中最有代表性的发现,是黑屏功能并不由一个命令独立完成。
逆向分析显示,学生端存在两条相关处理路径:MESS type=0x20 负责创建黑屏窗口并设置内部状态位,COMD case 6 负责实际锁定键盘和鼠标。只发送其中一条路径,表面上可能暂时有效,却会在解锁时暴露问题。
例如,仅通过 COMD 显示黑屏时,学生端内部的 bit 0x20 没有被正确设置。后续解锁命令进入另一段分支,无法执行预期的解锁函数。真实教师端的做法是组合两条路径,模拟器也因此采用同样的流程:
- 通过会话通道组播 MESS 黑屏包,建立黑屏状态;
- 需要锁定输入时,再向目标学生单播 COMD 命令;
- 定时黑屏到期后,由教师端主动发送解锁消息;
- 手动解锁时取消已有定时器,避免重复动作。
这说明逆向工程中一个很重要的原则:不能只根据界面效果判断协议是否还原正确。真正可靠的实现,还要匹配状态变化、后续行为和异常路径。
难点二:UDP 分片不是简单拼接
屏幕预览是另一个典型问题。学生端会通过 LANT/TNAL 报文分片发送 JPEG 数据,教师端需要维护每个学生独立的接收状态,包括总长度、当前缓冲区和已接收字节数。
项目使用学生 IP 作为重组键:新图像开始时创建状态,后续分片写入对应位置;只有在确认数据完整后才尝试交给 Pillow 解码。这样可以避免多个学生同时回传缩略图时互相覆盖。
成功解码并不代表图像已经正确。抓包样本还暴露了两个图像层问题:画面是 bottom-up 的,色度通道也存在反相。项目的修复流程是:
img = Image.open(io.BytesIO(buffer))
img = img.transpose(Image.FLIP_TOP_BOTTOM)
y, cb, cr = img.convert('YCbCr').split()
cb = ImageOps.invert(cb)
cr = ImageOps.invert(cr)
img = Image.merge('YCbCr', (y, cb, cr)).convert('RGB')这段处理体现了协议分析经常遇到的情况:传输层的数据结构正确,只能说明“收到了一张图”;要得到视觉上正确的结果,还需要理解发送端图像缓冲区的存储方式和颜色空间。
难点三:同一个 MESS,必须先判断语义类别
MESS 是双向的通用消息容器。聊天、黑屏、周期状态、系统信息、进程列表和窗口列表都会经过它。如果只读取某个固定偏移并直接把数字解释成 subtype,就可能把进程 PID 当成 Wi-Fi 数量,把窗口句柄当成另一类状态值。
模拟器的处理方式是先读取 category,再进入对应解析器:
0x800000表示信息上报;- type 5 是系统信息;
- type 6 是窗口列表;
- type 7 是进程列表;
- 周期状态则走另一套 subtype 解释规则。
系统信息包含计算机名、用户、MAC、操作系统、CPU 和内存等定长字段。窗口和进程列表采用分片形式,每个条目由一个 32 位 ID 和一个以空字符结尾的 UTF-16LE 名称组成。收到首片时清空旧数据,再将后续分片追加到对应学生的状态中。
这套设计让项目从“能发控制包”进一步变成了“能维护学生状态的教师端”。命令行中的 list、ps 和 wins 读取的正是这份动态建档数据。
让研究脚本具备可调试性
协议实验往往会产生大量重复数据。若每次在线宣告和心跳都输出完整十六进制转储,真正有价值的异常包很快会被淹没。
项目为此增加了几项工程化处理:
- 使用四个后台线程分别处理主通道广播、会话广播和两个接收循环;
- 过滤本机发出的多播,避免模拟器处理自己的数据;
- 将日常广播与高频心跳降到 DEBUG 级别;
- 使用轮转日志限制单个日志文件大小,并保留有限备份;
- 主窗口只负责
teacher>命令交互,独立 PowerShell 窗口实时显示日志; - 对未知包保留 hexdump,便于继续补充协议知识;
- 对黑屏操作设置自动解锁,降低实验中因参数错误造成的影响。
这些设计并不会改变协议本身,却显著改善了研究体验。协议实现最怕“偶尔能用但无法解释”,而良好的日志、状态隔离和错误输出,能够把一次偶发现象变成可重复验证的结论。
运行与观察
项目运行环境很轻量,只依赖 Python 3.8+ 和 Pillow:
pip install Pillow
python "teacher_sim .py"程序启动后会自动选择本机网络地址,加入两个多播组,并启动四个后台线程。学生端完成登录后,可以通过 list 查看已建档设备,通过 preview <ip> 请求屏幕缩略图,或使用 msg <ip> <text> 验证双向消息链路。
仓库还包含一个纯静态项目主页,由 index.html、styles.css 和 script.js 组成。页面用 Canvas 动画呈现数据包在教师端与学生端之间流动的效果,并把功能、协议通道、运行方式和安全边界整理成更直观的展示页面。它不参与教师端协议通信,因此核心模拟器仍然可以作为单文件 Python 程序独立运行。
项目带来的几个启发
回顾整个实现过程,有几条经验尤其值得保留。
第一,抓包和静态分析必须互相校验。只看流量,很难知道字段为何这样排列;只看反编译代码,也可能误判实际执行路径。二者结合,才能逐步建立可靠的协议模型。
第二,要优先还原状态机,而不是孤立的数据包。一个报文能被接收,不代表功能已经完成;登录顺序、状态位、定时器和响应包往往同样重要。
第三,解析器要先分层再解释。Magic、外层头、category、type 和具体负载各自属于不同层次,跳过上层语义直接解释下层字段,很容易得到“数值合理但含义错误”的结果。
第四,研究工具也需要安全护栏。自动解锁、目标登录校验、清晰日志和明确的授权范围,不只是使用体验问题,也是负责任地开展协议研究所必需的部分。
结语
这个项目把一个封闭桌面软件背后的通信过程,转化成了可阅读的代码、可观察的日志和可重复的实验。从教师在线宣告、学生登录,到消息解析和图像分片重组,每一项功能都建立在对具体字节和状态变化的理解上。
它目前仍是面向极域 V6.0 的研究性实现,并不追求替代成熟的课堂管理产品。它更有价值的身份,是一份活的协议笔记:当新的字段、新的包类型或更准确的握手顺序被发现时,代码、文档和实验结果可以继续相互印证。
项目采用 MIT License。无论阅读源码、复现实验还是继续扩展协议,都请始终把合法授权和最小影响放在第一位。
完整源码和后续更新可在 GitHub 仓库 yunsjxh/Third-party-JiYu-Teacher-Endpoint 查看。
还没有评论,来说点什么吧。