KeepALifeUS/sfs2x-py

GitHub: KeepALifeUS/sfs2x-py

纯 Python 实现的 SmartFoxServer 2X 二进制协议库,支持完整类型系统的编解码、XOR 混淆、压缩和 AES 加密,专为协议分析、MITM 代理和游戏机器人开发而构建。

Stars: 0 | Forks: 0

# sfs2x-py [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/25634d9ec6033224.svg)](https://github.com/KeepALifeUS/sfs2x-py/actions/workflows/ci.yml) [![PyPI](https://img.shields.io/pypi/v/sfs2x-py.svg)](https://pypi.org/project/sfs2x-py/) [![Python](https://img.shields.io/pypi/pyversions/sfs2x-py.svg)](https://pypi.org/project/sfs2x-py/) [![Downloads](https://img.shields.io/pypi/dm/sfs2x-py.svg)](https://pypi.org/project/sfs2x-py/) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) **SmartFoxServer 2X 二进制协议的纯 Python 实现。** 以完整的类型保真度编码、解码和操作 SFS2X 数据包 —— 专为协议分析、MITM 代理和游戏机器人开发而构建。 ## 什么是 SmartFoxServer 2X? [SmartFoxServer 2X](https://www.smartfoxserver.com/) (SFS2X) 是一个被数百款移动端和网页游戏使用的实时多人游戏服务器。它通过 TCP 使用专有的二进制协议进行通信,该协议具有以下特点: - 包含 19 种数据类型的丰富类型系统 (SFSObject / SFSArray) - 对客户端到服务器的数据包进行 XOR 混淆 - 可选的 zlib/zstd 压缩 - AES-128-CBC 会话加密 **sfs2x-py** 是首个完全实现此传输协议的开源 Python 库。目前不存在公开的官方 SDK 或二进制格式文档 —— 本项目完全通过逆向工程构建。 ## 功能特性 | 功能 | 描述 | |---------|-------------| | **完整类型系统** | 所有 19 种 SFS2X 数据类型:NULL, BOOL, BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, UTF_STRING, 类型化数组, SFS_ARRAY, SFS_OBJECT | | **数据包分帧** | 编码和解码 C2S (客户端到服务器) 和 S2C (服务器到客户端) 数据包 | | **XOR 混淆** | 源自数据包大小的旋转 4 字节密钥 —— 在编码/解码时自动应用 | | **压缩** | 针对 S2C 的 zlib 和 zstd 解压;针对 C2S 的 zlib 压缩 | | **AES 加密** | 通过 `/BlueBox/CryptoManager` 端点进行会话密钥交换 | | **类型保留解码** | `decode_typed()` 保留传输类型 (BYTE vs INT vs LONG) —— 对于需要重新编码数据包的 MITM 代理至关重要 | | **数据包构建器** | 高级辅助函数:`make_extension_request()`, `make_keepalive()`, `parse_s2c_command()` | | **零配置** | 除 `pycryptodome`, `aiohttp`, `zstandard` 外无外部依赖 | ## 安装 ``` pip install sfs2x-py ``` 从源码安装: ``` git clone https://github.com/KeepALifeUS/sfs2x-py.git cd sfs2x-py pip install -e ".[dev]" ``` 需要 Python 3.10+。已在 3.10 到 3.14 版本上测试。 ## 快速开始 ### 编码和解码 SFSObject ``` from sfs2x import SFSCodec, TypedValue, INT, LONG # 将 dict 编码为二进制 SFSObject obj = { "username": "player1", "level": TypedValue(INT, 42), "score": TypedValue(LONG, 1_000_000), } data = SFSCodec.encode(obj) # 将其解码还原 decoded, bytes_consumed = SFSCodec.decode(data) print(decoded) # {'username': 'player1', 'level': 42, 'score': 1000000} ``` ### 构建并发送扩展请求 ``` from sfs2x import make_extension_request, TypedValue, INT # 构建即发即用的 C2S 数据包(应用 XOR 混淆) packet = make_extension_request( cmd="chat.send", params={"msg": "Hello!", "channel": TypedValue(INT, 1)}, server_id=1234, ) # 通过 TCP 发送 sock.sendall(packet) ``` ### 解码捕获的 S2C 流量 ``` from sfs2x import decode_s2c_packet, parse_s2c_command, iter_s2c_packets # 单数据包 raw = bytes.fromhex("80001f...") obj, consumed = decode_s2c_packet(raw) cmd, params = parse_s2c_command(obj) print(f"{cmd}: {params}") # 数据包流(跳过数据包间的噪声字节) for obj, offset in iter_s2c_packets(tcp_stream): cmd, params = parse_s2c_command(obj) print(f"[{offset:#x}] {cmd}: {params}") ``` ### 用于 MITM 代理的类型保留解码 在代理流量时,您需要重新编码数据包而不改变传输类型。服务器可能会将 `BYTE(5)` 和 `INT(5)` 区别对待,尽管它们都代表数字 5。 ``` from sfs2x import decode_c2s_packet_typed, encode_c2s_packet # 解码时保留精确的 wire types obj, consumed = decode_c2s_packet_typed(raw_packet) # obj["p"]["level"] 是 TypedValue(INT, 5),而不仅仅是 5 # 修改字段 obj["p"]["p"]["gold"] = TypedValue(INT, 9999) # 重新编码 —— 所有其他字段保留原始 wire types modified_packet = encode_c2s_packet( obj["c"].value, obj["a"].value, obj["p"], server_id=1234, ) ``` ### AES 会话加密 ``` from sfs2x import KeyExchange kx = KeyExchange() # 选项 1:从 CryptoManager 端点获取密钥 aes = await kx.fetch_crypto_key("game.example.com", 8443, session_token) # 选项 2:从原始字节设置密钥(如果您捕获了密钥交换) aes = kx.set_from_bytes(raw_32_bytes) # first 16 = key, last 16 = IV # 加密/解密数据包载荷 encrypted = aes.encrypt(payload) decrypted = aes.decrypt(encrypted) ``` ## 传输协议参考 ### 数据包分帧 **S2C (服务器到客户端):** ``` [0x80] [size: 2B BE] [SFSObject payload] [0x88] [size: 4B BE] [SFSObject payload] (big-sized, >64KB) [0xA0] [size: 2B BE] [zlib/zstd compressed] (compressed) [0xB0] [dec_size: 4B] [zstd frame] (zstd with size prefix) ``` **C2S (客户端到服务器):** ``` [0xC4] [server_id: 2B BE] [size: 2B BE] [XOR-obfuscated SFSObject] [0xE4] [server_id: 2B BE] [size: 2B BE] [XOR(zlib(SFSObject))] ``` **XOR 密钥推导:** ``` key = [size & 0xFF, (size >> 8) & 0xFF, 0x00, 0x00] ``` 作为旋转的 4 字节掩码应用。位置 2 和 3 是空操作 (密钥字节为 0)。 ### SFSObject 封装 每个数据包的载荷都是一个根 SFSObject: ``` "c" -> controller (BYTE: 0 = system, 1 = extension) "a" -> action (SHORT: 0 = handshake, 1 = login, 13 = extension, 29 = keepalive) "p" -> parameters (SFS_OBJECT) ``` 扩展命令 (c=1, a=13) 具有嵌套参数: ``` "p"."c" -> command name (UTF_STRING, e.g. "chat.send") "p"."r" -> room ID (INT, usually -1) "p"."p" -> command params (SFS_OBJECT) ``` ### SFS2X 类型系统 | ID | 类型 | Python | 传输大小 | |----|------|--------|-----------| | 0 | NULL | `None` | 0 | | 1 | BOOL | `bool` | 1 byte | | 2 | BYTE | `int` | 1 byte (无符号) | | 3 | SHORT | `int` | 2 bytes (有符号) | | 4 | INT | `int` | 4 bytes (有符号) | | 5 | LONG | `int` | 8 bytes (有符号) | | 6 | FLOAT | `float` | 4 bytes | | 7 | DOUBLE | `float` | 8 bytes | | 8 | UTF_STRING | `str` | 2B 长度 + UTF-8 | | 9 | BOOL_ARRAY | `list[bool]` | 2B 计数 + 数据 | | 10 | BYTE_ARRAY | `bytes` | 4B 计数 + 数据 | | 11-16 | 类型化数组 | `list` | 2B 计数 + 数据 | | 17 | SFS_ARRAY | `list` | 2B 计数 + 类型化元素 | | 18 | SFS_OBJECT | `dict` | 2B 计数 + 键值对 | 自动调整大小:普通的 Python `int` 值会自动编码为能容纳该值的最小类型 (BYTE -> SHORT -> INT -> LONG)。使用 `TypedValue` 强制指定特定的传输类型。 ## API 参考 ### `sfs2x.objects` | 函数 | 返回值 | 描述 | |----------|---------|-------------| | `SFSCodec.encode(obj)` | `bytes` | 将字典编码为 SFSObject 二进制 | | `SFSCodec.decode(data)` | `(dict, int)` | 解码 SFSObject,返回 (dict, bytes_consumed) | | `SFSCodec.decode_typed(data)` | `(dict, int)` | 解码并将传输类型保留为 `TypedValue` | | `TypedValue(type_id, value)` | — | 在编码时强制指定特定的传输类型 | ### `sfs2x.protocol` | 函数 | 返回值 | 描述 | |----------|---------|-------------| | `encode_c2s_packet(ctrl, action, params, server_id, compress)` | `bytes` | 构建带有 XOR 混淆的 C2S 数据包 | | `decode_c2s_packet(data)` | `(dict, int)` | 解码 C2S 数据包 | | `decode_c2s_packet_typed(data)` | `(dict, int)` | 解码 C2S 并保留传输类型 | | `encode_s2c_packet(obj, compress)` | `bytes` | 构建 S2C 数据包 | | `decode_s2c_packet(data)` | `(dict, int)` | 解码 S2C 数据包 (处理压缩) | | `make_extension_request(cmd, params, room_id, server_id)` | `bytes` | 构建扩展请求数据包 | | `make_keepalive(client_time, server_id)` | `bytes` | 构建保活 (keepalive) 数据包 | | `parse_s2c_command(obj)` | `(str\|None, dict)` | 从 S2C 中提取命令名称和参数 | | `iter_s2c_packets(data)` | `Iterator` | 迭代字节流中的数据包 | ### `sfs2x.crypto` | 函数 | 返回值 | 描述 | |----------|---------|-------------| | `AESCipher(key, iv)` | — | 每个会话具有固定 IV 的 AES-128-CBC | | `KeyExchange()` | — | 通过 CryptoManager 管理密钥交换 | | `await kx.fetch_crypto_key(host, port, token)` | `AESCipher` | 从服务器获取密钥 | | `kx.set_from_bytes(data)` | `AESCipher` | 从原始 32 字节设置密钥 | | `make_password_hash(token, password)` | `str` | MD5 登录哈希 | ## 使用案例 - **协议分析** —— 解码从 tcpdump/Wireshark 捕获的流量以理解游戏通信 - **MITM 代理** —— 实时拦截、检查和修改数据包 (参见 `examples/mitm_proxy.py`) - **游戏机器人** —— 构建直接与 SFS2X 服务器通信的无头客户端 - **安全研究** —— 审计游戏服务器实现中的漏洞 - **模组工具** —— 构建与基于 SFS2X 的游戏交互的自定义工具 ## 示例 `examples/` 目录包含: - **`decode_packet.py`** —— 演示编码和解码示例数据包 - **`mitm_proxy.py`** —— 透明 TCP 代理,双向记录所有 SFS2X 命令 ## 贡献 有关设置说明和指南,请参阅 [CONTRIBUTING.md](CONTRIBUTING.md)。 ## 许可证 [MIT](LICENSE) —— 可用于任何用途。
标签:IP 地址批量处理, Python, SFS2X, SmartFoxServer, TCP/IP, 中间人攻击, 二进制协议, 云资产清单, 协议分析, 协议实现, 开源库, 搜索引擎爬虫, 数据加密, 数据包编解码, 数据压缩, 数据解析, 无后门, 权限提升, 游戏开发, 游戏服务器, 游戏机器人, 网络安全, 网络库, 网络通信, 逆向工具, 逆向工程, 防御绕过, 隐私保护