基于 CEF + C++ 的《洛克王国》Flash 游戏辅助框架开发复盘
作者基于个人兴趣,为经典 Flash 游戏《洛克王国》开发了一款辅助工具框架:底层采用 C++ 进行内存与数据包处理,上层通过嵌入 CEF(Chromium Embedded Framework)内核提供 JavaScript 开发接口,实现了数据包拦截、修改、转发及前端可视化等完整能力,整个项目以“架构清晰、便于扩展、快速开发”为核心设计目标。
项目背景与动机
为什么做这个项目
- 作者在吃饭时经常观看直播,无意间刷到《洛克王国》的直播内容,觉得节目效果很好。
- 《洛克王国》是一款老旧的 Flash 游戏,属于作者的童年回忆(小学初中时期游玩)。
- 由于近期工作较忙,视频更新暂停,但这个项目已经完成了框架搭建,故以此作为分享内容。
适用范围澄清
重要定位说明(引用原文):*“我录这个视频呢,包括我做这个小助手呢,他呢并不是针对广大的玩家的……因为这个的话呢使用起来的话呢,肯定不是说是像那些现成的网上那些辅助啊。”*
- 该助手并非面向普通玩家的现成辅助工具。
- 使用者需要具备一定的开发经验,能够基于框架进行二次开发。
- 相对于现成的辅助工具(作者评价其大多基于“按键精灵”等老旧的自动化方式),“那种开发的感觉太老太老了,就不是这个时代的产品开发了”。
整体技术架构
架构分层
| 层次 | 技术选型 | 职责说明 |
|---|---|---|
| 底层 | C++ | 内存操作、数据包处理、Socket 通信等基础功能支持 |
| 中间层 | CEF(Chromium Embedded Framework) | 嵌入浏览器内核,提供 JS 执行环境与前后端通信桥梁 |
| 上层 | JavaScript | 辅助功能逻辑开发,以网页形式呈现 UI 和交互 |
| 后端 | Python Flask(示例) | 简单数据处理与记录,可自由替换 |
各层的具体说明
底层选择 C++ 的原因:
- 进行内存操作时性能更好、更方便。
- 对上层语言提供支持时,上层无需关注过多底层细节。
- 作者个人习惯:*“写多了他就习惯了,就习惯用 C++ 了”*——但这并非绝对要求,换成其他语言“其实也无所谓”。
- 关键原因:需要与 CEF 配合使用。
关于 CEF 选择的重要考量——安全性:
- 作者最初尝试使用 Windows 自带的浏览器内核(IE 内核),“体验极差,不仅慢,而且非常不安全”。
- 由于游戏本身依赖 Flash,安全性需要做得更好,因此选择了谷歌开源的 CEF 内核。
- 上层通过 CEF 提供 JavaScript 开发环境,后端语言可以自由选择(作者使用 Flask 作为简单示例)。
架构的核心设计理念:
- 整体架构为经典的 B/S(Browser/Server)模式。
- 底层开发者维护 C++ 侧的 Socket 截持等复杂逻辑,上层使用者*“并不需要去关注这个 C++ 代码是怎么做的,因为这一块是我来维护就完事了”*。
- 上层开发者只需编写 JS 代码即可实现完整功能,甚至连辅助 UI 都是纯网页(由 VUE 构建)。
Flash 兼容性与浏览器支持问题
Flash 插件支持的现状
- 从 Firefox 86 版本之后,浏览器已禁止使用 Flash 插件——并非技术上不支持,而是安全策略上的明确禁用。
- 当前主流浏览器(如 360 浏览器)均已明确显示“不支持该插件”,因为 Flash“太不安全了”。
- 作者实测发现 Firefox 86 是支持 Flash 的最后一个版本,但即使在这个最后的支持版本中限制也很大。
作者的解决方案
- 分析阶段:使用 360 浏览器(唯一实测支持 Flash 的现代浏览器),配合“答辩意”(即反编译)软件进行 SWF 分析。
- 运行时方案:CEF 内核中嵌入指定版本的 Flash 插件(作者携带的是 Flash 30.8 版本)。
Flash 插件的具体运行细节
- Firefox 76 系列版本可以通过命令行参数设置自动运行 Adobe Flash Player。
- 但 Firefox 86 即使加上所有命令行参数也无法自动运行 Flash,只能通过右键菜单选择“运行此插件”来手动激活。
SWF 资源分析与逆向过程
SWF 文件获取
- 作者通过分析游戏中的 XML 配置文件,编写脚本批量下载了大量 SWF 文件。
- 下载后的 SWF 文件需要专门的工具处理,因为现代浏览器无法直接打开查看。
反编译工具的选择与处理
- 使用的反编译软件为付费软件,分个人版和企业版。
- 个人版不支持全部导出功能,使用不便。
- 企业版功能不够完善——作者通过逆向手段为其增加了一个“全部导出”的功能支持。
- 导出后就可以像使用 IDEA 一样方便地分析 Flash 的 ActionScript 代码。
加密与解包处理
- 洛克王国的 SWF 文件存在一层小加密,其中包含一个关键的加密环节,比较复杂。
- 作者的处理方式类似于做 CTF 逆向:在内存中进行暴力搜索,找到对应的特征码后进行 dump(脱壳)。
ActionScript 代码分析
作者本身并不会 ActionScript 语言,但评价道:*“这个语法极其简单,基本上你不需要去学习,你只要会编程你就能看懂,太简单了。”*
代码结构与评估:
- 核心逻辑包:以
com开头的是最重要的包,其中tenant下面的qq Angela等包为核心处理逻辑,其他包主要负责渲染展示。 - 对代码质量的评价:*“它的框架搭的非常好,在当时年代来说的话,它已经是一个非常非常赞的框架了”*——属于一种英文的注册机制。
- 但存在严重问题:*“这个架子中间被各种人给破坏一样,迭代更新,他已经没有原本那种简洁清楚的那种感觉了。”*——游戏年代久远,经过了多代开发人员迭代,代码协调性很差。
数据包格式演变
作者逆向分析了游戏数据包从早期到后期的格式演变,分为几个阶段:
| 阶段 | 包类型 | 格式特征 | 使用方式 |
|---|---|---|---|
| 早期 | plus | 结构体类型(short 等基本类型拼接) | 已有框架包(如 input/output 类) |
| 中期 | Skt(位于 net 包下) | 结构体风格,继承自 c to s,格式本质不变但风格大变 | 自行解析,不再继承原有基类 |
| 后期 | PB(protobuf) | 经典的 protobuf 二进制结构 | 需要 protobuf 库解析,也可手动解析但“太不好了” |
具体格式说明(早期结构体型):
- 每个数据包会有一个 A F 头(固定标识),包含结构体头。
- 头部下面带有 body 部分,每个 body 是实现了 update/input/output 接口的实例。
- 关键字段包括 flag 和 product ID,根据 flag 进行分发处理。
HTTP 请求类型数据包
- 游戏后期还出现了 HTB(HTTP)类型的数据包,采用 C++ 命名的请求方式。
- 带有 CGR 异常等机制,当时年代久远,“很久没有什么特别大的一些革命性改进”。
协议逆向成果与数据规模
协议分析统计
- 作者对游戏中的协议进行了全面分析:
- Skt/T 类型的协议约 300 个。
- PB(protobuf)协议约 300 个。
- 总计约 600 多个协议全部完成了分析,并提供了给上层的 JS 使用。
- 实际代码中只写了 300 多个——因为部分 PB 协议“没有名字”,字段名不好命名,就没有放入代码文档。
分析工作量
- PB 协议的结构需要逐一分析——作者表示“我一个一个分析出来的”。
- 进行分析的依据:
- 看到
read from等方法名,推测与 protobuf 相关。 - 看到名字像 PB,确认是 protobuf。
- 根据 protobuf 结构特征进行还原转换。
协议处理的技术细节
protobuf 与 Qt 的兼容性问题:
- 创建项目时为了防止滥用,作者启用了 MT(多线程)模式。
- Qt 和 protobuf 同时使用 MT 模式时,在 Debug 版本(第八版本)下 protobuf 支持有问题。
- 具体表现为:通过反射获取 message 的字符串后,返回的 string 在局部变量中会被立即释放,导致内存错误。
- 原因推测:type name 应该是引用类型,但实际返回后即释放。
- 解决方式:测试时去掉 Debug 版本的 protobuf 支持,Release 版本一切正常。
代码规模:
- 作者展示的一些协议结构体定义文件非常庞大——有 7000~8000 行的。
- 还有部分协议代码 13,000 多行。
- 这些工作“并没有任何难度,它只是说非常反锁(繁琐)”。
数据截持与通信机制
截持方式
Socket 层截持:
- 作者 Hook 了 send 和 receive 函数来实现数据截持。
- 但游戏实际使用的是 Windows 的异步 Socket 机制(非标准 send),CF 框架虽然完善但存在一个奇怪的 bug。
HTTP/HTTPS 请求处理:
- 不仅需要截持 Socket 数据包,还需要处理 PB 的请求和回复。
- 拦截到的请求与数据包会全部转发给上层 JS,由上层自行修改展示或重新发包。
数据发送通道设计
- 游戏客户端开了两个 Socket 连接:
- 一个是 login 服务器(登录服务)
- 一个是 data 服务器(数据传输)
- 作者通过拦截并转发两个通道的数据实现了完整的通信控制。
- 发送数据包时需要指定发送到哪个通道(login 还是 data)。
数据包展示与解析
作者实现了对数据包的完整可视化展示:
- 可以查看每个数据包的 command 类型。
- 点击“查看详情”可以查看完整的数据内容与解析结果。
- 对于不同类型的数据(整数、字符串等),提供了转换方法(如
byteToString方法可将字节数组转为字符串展示)。 - 示例:房间号 382 在传输中以有符号整数形式出现,显示为负数,实际值应为 331730899——即真正的整数类型。
上层 JS 开发框架功能设计
辅助功能开发模式
作者在底层(C++)中实现了核心逻辑后,上层使用 JavaScript 进行功能开发:
- 辅助功能全部以 JS 编写,本质上是一个网页。
- 甚至可以搭建一个 Web 服务,用户只需将网址填入即可动态加载和执行辅助功能。
- 开发模式为前后端分离:前端(VUE)负责 UI 展示,后端可自由选择(作者用 Flask 做的示例)。
核心 API 设计
消息订阅与回调机制:
// 注册回调示例(伪代码)
// 当数据包请求发过来或收到响应时,回调已注册的函数
// 每个回调接收一个 message 对象- 用户可以通过
addCallback方式注册回调函数。 - 实现方式:将 JS 函数传递到底层数组中进行保存;当真正回调时,遍历该数组并逐一调用。
- 回调类型包括:
- 数据包请求回调(request)
- 数据包响应回调(reply/receive)
数据包拦截与修改流程示例(瞬移功能):
// 1. 通过 command ID 获取一个对象
// 2. 修改对象中的字段(如房间号 oScene/roomId)
// 3. 通过 getDataByObject 将对象序列化为字节数组
// 4. 通过 sendService 发送字节数组到指定通道(login/data)具体字段说明:
- 通过
getObjectCommandId方法获取对象后,包含adf等多个字段,可以通过 console 输出查看。 - 瞬移相关字段说明:
- oScene:目标场景/房间号
- 原始地址字段、零/一标志位等。
- 回家操作必须将房间号设置为 0(即家园)——因为可能存在好友概念,改房间号也可能去好友家园。
前端页面结构
作者展示了辅助工具的前端截图(未完整美化):
- 使用 QT 做外壳,UI 使用了 QSS 样式美化。
- 前端是 VUE 项目编译后由 Flask 直接提供(运行在 localhost:8080)。
- 配置项通过 YAML 格式管理(引入了 YAML 库)。
页面配置功能包括:
- command 类型配置
- 皮肤类型选择(两套,找了免费开源的主题——付费的都是国内厂商出品,作者选择不付费)
- 是否使用本地路径 + CWD(当前工作目录)
- 黑夜模式(配置项 tap 设置为 0 开启)
- 默认请求地址修改
已实现功能模块
| 功能 | 说明 |
|---|---|
| 登录信息展示 | 显示当前登录的 QQ 号、房间号等 |
| 瞬移 | 修改场景/房间坐标,实现地图传送 |
| 声音控制(关闭) | 关闭游戏声音 |
| 数据包拦截与查看 | 展示所有发出的请求和收到的响应数据包 |
| 数据包修改 | 修改数据包内容后转发 |
| 获取房间列表 | 请求房间列表并展示(含 IP 地址转换) |
| 虚假房间信息 | 修改返回的房间号(如改为 9999) |
| 修改房间范围 | 将获取房间范围改为 16(获取 16 个房间) |
| 历史数据记录 | 记录登录信息等基础数据 |
快捷开发能力
作者强调开发脚本非常简单快速:
- 不需要修改底层 C++ 代码即可完成功能开发。
- “画完界面,然后 JS 本身就非常非常简单了”。
数据包类型处理详解
Socket 结构体类型数据包
- 特征:结构体序列化,先有头部标识,再有 body 内容。
- 开发时对照原版逐个字段定义结构体即可。
- 代码示例(伪代码):ip 地址从服务端返回为 uint 类型,需要自行转换展示。
Protobuf(PB)类型数据包
- 特点:有非常明显的 protobuf 结构特征。
- 解析需要使用 protobuf 库,手动解析不现实。
- 作者在使用 protobuf 时遇到 Qt+protobuf 在 Debug 模式下的兼容性问题(前面已说明)。
HTTP/XML 配置文件处理
- 游戏包含大量 XML 配置文件。
- 这些 XML 是作者批量下载 SWF 文件的途径,也是修改游戏行为的一个切入点。
- 作者实现了 XML 文件的拦截修改能力:
// 1. 注册一个 AB 的 callback(HTTP 请求回调)
// 2. 在回调函数中获取 content(返回内容)和 maxContentLen(最大可修改长度限制)
// 3. 将 content 替换为修改后的内容(如空字符串或其他自定义内容)
// 4. 修改后页面加载行为会相应改变HTTP 回调的限制与规避
问题:
- AB(HTTP)返回包的 callback 中,fill 函数是同步的——因为 V8 引擎的限制(broker 进程和 render 进程不在同一个进程中),异步操作不可行。
- 只能返回 done(完成)或 cancel(取消)。
- 为什么需要这类回调:某些返回内容达到 65535 字节上限时无法处理,或者需要更多数据修改时,同步回调无法满足。
作者的解决方案——LoA 库:
- 自己编写了一个 LoA 库来配合处理子线程的限制。
- LoA 无法在子线程中做 UI 处理,所以作者又回归 GS 方案。
- 最终通过 callback 类中的 LoA 函数处理:
- 传入四个参数:方法(POST/GET)、URL、content(返回内容)、内容长度与最大修改长度限制。
- 开发者可以在 ContentReplace 相关回调中处理这些内容。
CEF 进程模型与开发者工具
- 使用 CEF 后,Flash 会开启多个进程(render 进程、broker 进程等)。
- 开发者可以通过 F12 键打开开发者工具(DevTools)进行页面调试。
- 作者未默认开启开发者工具——因开启后会比较麻烦,涉及多个进程。
- 框架默认集成了最新版 Flash(308/30.8),虽然官方已停止维护,但仍有社区开发者在维护。
数据抓取与分析示例
登录过程
- 游戏启动时会连接本地 QQ 进程的端口(作者在分析时发现 localhost 的端口请求,如 4301),用于获取当前登录的 QQ 信息。
- 点击登录按钮后,会显示出当前登录的 QQ 号(作者示例中为“奥古斯特”)和登录状态。
房间列表获取与修改
作者演示了一个完整的拦截流程:
1. 发送获取房间列表请求(get room list / get room data)
2. 服务端返回房间列表 reply
3. 拦截 reply,解析返回的房间数据(含 IP 地址、房间号、人数等)
4. 修改数据后转发给游戏客户端
5. 游戏客户端展示修改后的房间列表- 每个游戏服务器大约能开十几个端口,每个端口代表一个服务器(登录服务器)。
- 拦截 reply 的类型包括 room index、room map 等字段。
瞬移功能数据流
// 1. 通过 getObjectCommandId(196612) 获取场景对象
// 2. 修改对象中的位置字段(如:将 oScene 从 382 改为 13,或改为 0 表示回家)
// 3. 通过 getDataByObject 获取字节数组
// 4. 通过 sendService 发送到 data 通道
// 5. 服务端确认后返回响应包(304 类型),游戏完成场景跳转- Command ID 196612 对应操作“T4”,含义为切换场景。
项目规模与源码交付情况
资源体积
- 项目整体体积较大(约 500MB)。
- 不带 Flash 游戏原版内容(300MB)也有 200MB。
- 体积大的原因:
- 携带了完整的 Qt5 库。
- 携带了 CEF 库及附属文件。
- 携带了 YAML 库、LoA 库、V8 引擎等。
- Flash 插件:用户本机无需安装 Flash,直接使用当前路径下的指定 Flash 插件。
项目源码包含
- 完整的 C++ 底层框架源码。
- JS 层开发模板与示例(含瞬移、回家、房间列表、数据修改等 DEMO)。
- 协议分析成果(600+协议的结构体与解析代码)。
适宜开发人群
- 作者明确说明:适合有开发经验的人进行二次开发。
- 零基础用户不推荐使用这套框架。
- 已将“原版”(即原始 Flash 游戏的 SWF 文件)打包,方便开发者对照分析。
开发过程总结与展望
作者对开发难度的评估
- 数据包分析本身没有技术难点,但极其繁琐。
- 代码规模巨大主要来自逐一反推结构体定义。
- 作者表示开发过程是利用每天晚上下班回家后的业余时间完成的,“很干”。
Q&A 与后续计划
- 作者计划将源码公开发布。
- 后续内容规划:
- 回归正轨,继续分享漏洞相关知识。
- 因近期做了较多 CEF 相关工作,可能分享浏览器漏洞相关内容。
- 之后还会录制一些溢出(漏洞利用)相关教程。
关键技术术语表
| 术语 | 说明 |
|---|---|
| CEF | Chromium Embedded Framework,谷歌开源的浏览器内核嵌入框架 |
| C++ | 底层开发语言,负责内存操作与数据包处理 |
| Flash/SWF | 游戏使用的老旧多媒体平台,已停止官方维护 |
| ActionScript | Flash 的脚本语言,语法简单,作者评价“会编程就能看懂” |
| protobuf | 谷歌的二进制序列化协议,游戏中后期数据包格式 |
| Socket | 网络通信接口,游戏的两个连接分别用于登录与数据传输 |
| Hook(Hook) | 拦截系统函数调用的技术,用于数据截持 |
| QT | C++ 跨平台 GUI 框架,作者用于构建外壳程序 |
| VUE | 前端 JS 框架,用于构建辅助功能网页界面 |
| Flask | Python Web 框架,作为后端示例服务 |
| LoA | 作者自己编写的库,用于处理 V8 引擎限制下的子线程回调问题 |