本文是淘米(Taomee)泄漏服务端源码的协议总导读。目标:把这套 2007–2013 年的网页 Flash 页游服务端协议讲清楚,便于与我们抓到的摩尔庄园 iOS 手游协议做字段/命令字对照。
返回总入口:../README.md · 模块文档:mole.md / mole2.md / gf.md / monster.md · 内容专题:content-data.md
业务对照(据淘米自有 gameid 字典 common-tools/.../docs/gameid):mole = 摩尔庄园(id 1),mole2 = 摩尔庄园2(id 8),gf = 功夫派(id 6,非小花仙),pea = 赛尔号(id 2),monster = 淘米小怪兽(id 13,非赛尔号),boke = 卜克 / POP 解密类(id 12)。所有服务都是给 Flash 网页客户端用的。
整套协议是命令字(cmdid)驱动的二进制 RPC:每个请求包头里带一个 16-bit 的命令字,后端按命令字分发到对应的处理函数。命令字本身是一个复合位域——高位 6 bit 编码"这个请求要路由到哪个 DB",0x0100 这一位编码"这个请求要不要写库"。理解了命令字的位编码,就理解了大半套协议。
核心字典文件是 mole/dbsvr/include/proto.h(约 15003 行,约 1300 个 _CMD 命令字),它是摩尔庄园 DB 服务端的协议总表,也是本文的主角。
命令字用 #define 定义,值是 16-bit 十六进制字面量,经常 OR 上一两个标志位。典型形态(均为真实片段):
// 客户端私有数据,获取 —— 纯读,不带任何标志
#define ROOMINFO_CLIENT_DATA_GET_CMD (0xB469)
// 客户端私有数据,设置 —— 写库,OR 上 NEED_UPDATE
#define ROOMINFO_CLIENT_DATA_SET_CMD (0xB46A | NEED_UPDATE)见 mole/dbsvr/include/proto.h#L32 与 #L35。
//在cmdid 中数据库识别标志
#define RROTO_ROUTE_FIELD 0xFC00 //使用前6个bit
//是否需要保存交易报文
//有涉及到更新数据库数据的协议,都要保存
#define NEED_UPDATE 0x0100RROTO_ROUTE_FIELD = 0xFC00(高 6 bit 作 DB 路由识别)—— 见 mole/dbsvr/include/proto.h#L25。 注意宏名拼写就是源码里的RROTO(疑似PROTO笔误,但全仓库一致沿用)。NEED_UPDATE = 0x0100(写库标志)—— 见 mole/dbsvr/include/proto.h#L29。
含义:
- 写库标志
0x0100:凡涉及更新 DB 的协议都 OR 上它。服务端据此决定是否落盘、是否记录"交易报文"以便回放/审计。命令字名里通常带SET_/ADD_/DEL_/UPDATE_,而GET_类几乎都不带。 - 路由位域
0xFC00:取命令字高 6 bit 作为"打到哪台/哪类 DB"的识别。例如0xB469、0x10EE、0x3046落在不同号段,反映不同的 DB/表分区。
各游戏的路由位域宽度并不一样。摩尔 1 代用前 6 bit(
0xFC00);摩尔 2 代用前 7 bit(0xFE00),见 mole2/mole2_db/src/proto.h#L28;但写库标志NEED_UPDATE = 0x0100各游戏一致,见 mole2/mole2_db/src/proto.h#L32。
DB 服务端的包头定义在 mole/dbsvr/include/proto_header.h#L28:
typedef struct proto_header{
uint32_t proto_length; ///< 包的全部长度
uint32_t proto_id; ///< 协议版本号
uint16_t cmd_id; ///< 命令编号
int32_t result; ///< 返回码(请求时一般为 0)
userid_t id; ///< 用户编号(userid_t = uint32_t)
} __attribute__((packed)) PROTO_HEADER;要点:
__attribute__((packed))—— 无对齐填充,字段紧贴;x86 Linux 上是小端字节序。- 头部固定 18 字节(4+4+2+4+4),正文紧随其后(
PROTO_HEADER_SIZE之后即私有数据区,见 #L37–L41)。 proto_id是协议版本/魔数;set_proto_buf里硬编码为0x12345678,见 proto_header.h#L83。- 回包用
set_std_return—— 直接把收到的头memcpy回去,只改proto_length与result,见 proto_header.h#L57。所以请求头与响应头同构。 userid_t即uint32_t,见 proto_header.h#L25。
DB 层之外还有几个不同结构的头,对照 iOS 抓包时要分清自己抓的是哪一层。它们定义在 mole/online/protoheaders.h:
- online ↔ DB / 中心服:
server_proto_t=len / seq / cmd / ret / id / body[](cmd是uint16_t)。 - online ↔ 广播器(Broadcaster):
communicator_header_t,多了online_id / mapid / opid等场景字段。 - 消息日志:
msglog_proto_t,len/cmd是uint16_t,更紧凑。
客户端面向的"在线服务"(mole/online)还另有一套业务事件命令字(数值很小、十进制),例如学校吧事件,见 mole/online/school_bar_cmd.h:LAHM_DIARY_CMD 1001、MOLE_LEVEL_UP_CMD 1002、TASK_FINI_CMD 1003。这套和 DB 层的 0xB469 式命令字是两个命名空间,别混用。
加密:在线服务里出现 DES 密钥
#define DES_KEY ">ta:mee<"(台服另有DES_GAME_SCORE_KEY),见 mole/online/proto.h#L15。即客户端↔在线服务这一段可能对(部分)报文做 DES;而 online↔DB 的内网段通常明文。对照 iOS 抓包时这点很关键(见第 6 节)。
下表为各文件中 #define ... _cmd(大小写不敏感)命令字总数,及其中带 NEED_UPDATE(写库)的数量。统计口径见下方"怎么自查"。
| proto.h 文件 | 业务 | 行数 | 命令字数 | 其中 NEED_UPDATE | 路由位域 |
|---|---|---|---|---|---|
| mole/dbsvr/include/proto.h | 摩尔庄园 DB 主表 | 15003 | 1300 | 698 | 0xFC00(6bit) |
| mole/cache_serv/pkg/proto.h | 摩尔缓存服 | 7945 | 634 | 381 | 0xFC00(6bit) |
| mole/register/pkg/proto.h | 摩尔注册/账号 | 4105 | 352 | 212 | 0xFC00(6bit) |
| gf/cache_svr/pkg/proto.h | 功夫派缓存服 | 3833 | 351 | 205 | (见文件内) |
| gf/db/ser/src/proto.h | 功夫派 DB | 4047 | 284 | 137 | (见文件内) |
| mole2/mole2_db/src/proto.h | 摩尔 2 代 DB | 2200 | 154 | 101 | 0xFE00(7bit) |
补充说明(已 Read 复核):
- 上表"命令字数"统计的是宏名以
_cmd结尾的#define。mole 系用大写_CMD,gf/mole2 的 DB 文件混用小写_cmd,故用大小写不敏感口径。任务背景里提到的"约 1347" 是用更宽的"行内含_CMD"口径(含个别非命令宏);本文采用更严格的"宏名以_cmd结尾"口径,得 1300,二者都对,只是口径不同。 - mole/online/proto.h(2202 行)是在线服务头,主体是错误码
enum(如ERR_invalid_session = 10001,见 #L24)、DES 密钥与函数声明,不用_cmd宏,故不计入上表。客户端事件命令字另见school_bar_cmd.h(十进制)。 - monster/online/src/cli_proto.h(466 行)是 淘米小怪兽(monster,gameid 13,非赛尔号)在线服务的分发函数声明(
dispatch/cache_a_pkg等),不含命令字宏。其命令字表在其它头里(待定位,本文未逐一统计 —— 推测/待证)。
mole/dbsvr/include/proto.h 的命令字按前缀分类。前缀粗略统计(顶部几类):USER_ 约 736、ROOMINFO_ 约 119、SYSARG_ 约 89、SU_ 约 69、USERINFO_ 约 58、MSGBOARD_ 约 17 等。下面每类给少量真实代表(名称 + 0x 值 + 中文注释 + 是否写库 + 行号链接),不穷举,末尾给"怎么自查"的可复制命令。
| 命令字 | 值 | 含义 | 写库 | 链接 |
|---|---|---|---|---|
USERINFO_GET_GAMEFLAG_CMD |
0x0007 |
取用户游戏标志位 | 否 | #L705 |
USERINFO_ADD_GAME_CMD |
0x0008 |
给账号开通某游戏 | 是 | #L708 |
USERINFO_SU_SET_PASSWD_CMD |
0x000A |
后台改密码 | 是 | #L713 |
USERINFO_LOGIN_BY_MD5_TWO_CMD |
0x005F |
MD5 二次校验登录 | 否 | #L769 |
USERINFO_GET_LOGIN_INFO_CMD |
0x0091 |
取登录信息 | 否 | #L828 |
账号/注册的完整入口在 mole/register/pkg/proto.h 与 mole/cache_serv/pkg/proto.h#L33(
USERINFO_LOGIN_CMD = 0x0001、USERINFO_REGISTER_CMD = 0x0002 | NEED_UPDATE)。
USER_GET_ 约 128 个、USER_SET_ 约 56 个。代表:
| 命令字 | 值 | 含义 | 写库 | 链接 |
|---|---|---|---|---|
USER_GET_INFO_EX_CMD |
0x1006 |
取用户扩展信息 | 否 | #L875 |
USER_GET_XIAOMEE_CMD |
0x100D |
取小蜜(货币/资源) | 否 | #L897 |
USER_GET_ATTIREID_AND_CNT_CMD |
0x3046 |
取装扮 ID 与数量 | 否 | #L252 |
USER_SET_TASK_CMD |
0x1007 |
写任务状态 | 是 | #L878 |
USER_SET_VIP_FLAG_CMD |
0x1014 |
设置 VIP 标志 | 是 | #L919 |
USER_SET_INT_VALUE_CMD |
0x3050 |
通用整型字段写入 | 是 | #L256 |
| 命令字 | 值 | 含义 | 写库 | 链接 |
|---|---|---|---|---|
USER_PET_GET_INFO_CMD |
0x106B |
取宠物信息 | 否 | #L1124 |
USER_ADD_USER_PET_CMD |
0x1051 |
新增宠物 | 是 | #L1063 |
USER_DEL_USER_PET_CMD |
0x1055 |
删除宠物 | 是 | #L1069 |
USER_SET_USER_PET_POSLIST_CMD |
0x1054 |
设置宠物位置列表 | 是 | #L1068 |
USER_GET_PET_ATTIRE_LIST_CMD |
0x1066 |
取宠物装扮列表 | 否 | #L1107 |
USER_USE_SKILL_ONE_CMD |
0x10EE |
宠物使用一次技能 | 是 | #L41 |
| 命令字 | 值 | 含义 | 写库 | 链接 |
|---|---|---|---|---|
USER_PET_TASK_GET_TASKLIST_CMD |
0x3053 |
取宠物任务列表 | 否 | #L262 |
USER_TASK_PET_FIRST_STAGE_SET_CMD |
0x1074 |
设置宠物任务首阶段 | 是 | #L1145 |
USER_TASK_TASKID_DEL_CMD |
0x107F |
删除指定任务 | 是 | #L1182 |
| 命令字 | 值 | 含义 | 写库 | 链接 |
|---|---|---|---|---|
USER_FARM_SET_COL_CMD |
0x10AD |
设置农田格子 | 是 | #L1293 |
USER_FARM_SET_WEB_CMD |
0x10B7 |
设置农场网格 | 是 | #L1303 |
| 命令字 | 值 | 含义 | 写库 | 链接 |
|---|---|---|---|---|
USER_ATTIRE_SET_USED_CMD |
0x103F |
设置已穿戴装扮 | 是 | #L1027 |
USER_ATTIRE_PUT_CHEST_CMD |
0x108A |
装扮入箱 | 是 | #L1211 |
| 命令字 | 值 | 含义 | 写库 | 链接 |
|---|---|---|---|---|
GET_FRIEND_BOX_ITEMS_CMD |
0x30B9 |
取好友礼物盒物品 | 否 | #L572 |
PUT_ITEM_TO_FRIEND_BOX_CMD |
0x30BB |
放物品进好友盒 | 是 | #L578 |
GET_ITEM_FROM_FRIEND_BOX_CMD |
0x30BC |
从好友盒取物品 | 是 | #L581 |
USER_BATTLE_GET_FRIEND_EVENT_INFO_CMD |
0x2220 |
取好友对战事件信息 | 否 | #L385 |
| 命令字 | 值 | 含义 | 写库 | 链接 |
|---|---|---|---|---|
ROOMINFO_CLIENT_DATA_GET_CMD |
0xB469 |
客户端私有数据读 | 否 | #L32 |
ROOMINFO_SET_CLIENT_BUF_CMD |
0xB480 |
设置前端 buf | 是 | #L68 |
ROOMINFO_GET_CLIENT_BUF_CMD |
0xB481 |
获取前端 buf | 否 | #L71 |
ROOMINFO_GET_ITEM_SELL_CMD |
0xB487 |
取道具寄售 | 否 | #L89 |
ROOMINFO_ADD_ITEM_SELL_CMD |
0xB488 |
上架寄售道具 | 是 | #L92 |
| 命令字 | 值 | 含义 | 写库 | 链接 |
|---|---|---|---|---|
ROOMINFO_SPORTS_MEET_SET_CMD |
0xB478 |
设置运动会队 teamid | 是 | #L59 |
ROOMINFO_SPORTS_MEET_MEDAL_SET_CMD |
0xB47C |
更新个人奖牌数 | 是 | #L65 |
ROOMINFO_SPORTS_REWARD_GET_CMD |
0xB484 |
取用户/拉姆奖励标志 | 否 | #L80 |
| 命令字 | 值 | 含义 | 写库 | 链接 |
|---|---|---|---|---|
LAMU_CLASSROOM_GET_FRIEND_RANK_CMD |
0x221A |
教室好友排行 | 否 | #L371 |
USER_LAMU_CLASSROOM_CREATE_CLASSROOM_CMD |
0x30C1 |
创建拉姆教室 | 是 | #L590 |
USER_LAMU_CLASSROOM_SET_INNER_STYLE_CMD |
0x30C3 |
设置教室内饰 | 是 | #L596 |
| 命令字 | 值 | 含义 | 写库 | 链接 |
|---|---|---|---|---|
USER_BATTLE_MAP_INFO_CMD |
0x324C |
取战斗地图信息 | 否 | #L1748 |
USER_FINISH_SOME_MAP_MINING_CMD |
0x3615 |
完成某地图挖矿 | 否 | #L2053 |
| 命令字 | 值 | 含义 | 写库 | 链接 |
|---|---|---|---|---|
MSGBOARD_GET_MSGLIST_USERID_CMD |
0x7008 |
取某用户留言列表 | 否 | #L2249 |
MSGBOARD_GET_MSG_NOCHECK_CMD |
0x7009 |
取留言(不校验) | 否 | #L2250 |
USERINFO_SET_PASSWDEMAIL_CMD |
0x0017 |
设置密保邮箱 | 是 | #L727 |
| 命令字 | 值 | 含义 | 写库 | 链接 |
|---|---|---|---|---|
SU_USER_GET_ALL_PIGLETS_CMD |
0x32DB |
后台:取某用户全部小猪 | 否 | #L1933 |
SU_USER_SWAP_LIST_CMD |
0x32E5 |
后台:交换列表 | 是 | #L1953 |
SU_USER_GET_WORK_PIGLET_INFO_CMD |
0x3648 |
后台:取打工小猪信息 | 否 | #L2125 |
| 命令字 | 值 | 含义 | 写库 | 链接 |
|---|---|---|---|---|
SYSARG_DAY_CLEAR_CMD |
0xC004 |
每日清理 | 是 | #L2329 |
SYSARG_DAY_SET_CMD |
0xC007 |
设置每日数据 | 是 | #L2337 |
SYSARG_DAY_GET_LIST_CMD |
0xC00C |
取每日列表 | 否 | #L2347 |
- 摩尔 2 代 mole2/mole2_db/src/proto.h:
mole2_login_cmd = 0x0801(否,#L38)、mole2_user_user_create_role_cmd = 0x0802 | NEED_UPDATE(是,#L40)、mole2_user_pet_add_cmd(宠物新增,写库)。 - 功夫派 gf/db/ser/src/proto.h:
gf_get_user_info_cmd = 0x0601(否,#L34)、gf_set_child_count_cmd = 0x0609 | NEED_UPDATE(是,#L42)、gf_sell_attire_cmd = 0x0610 | NEED_UPDATE(是,#L51)。注意功夫派沿用同名的写库位NEED_UPDATE,但命令字号段从0x06xx起。
路径含空格,务必整体加引号。
REPO="/Users/xiaochoumao/Documents/github repo/taomee-server-leak"
P="$REPO/mole/dbsvr/include/proto.h"
# 1) 这个文件里一共多少个命令字(宏名以 _cmd 结尾,大小写不敏感)
grep -ciE '^[[:space:]]*#[[:space:]]*define[[:space:]]+[A-Za-z0-9_]+_cmd[[:space:]]' "$P"
# 2) 其中多少个要写库(带 NEED_UPDATE)
grep -iE '^[[:space:]]*#[[:space:]]*define[[:space:]]+[A-Za-z0-9_]+_cmd[[:space:]]' "$P" | grep -ci NEED_UPDATE
# 3) 按一级前缀统计分布(USER_ / ROOMINFO_ / SU_ ...)
grep -oE '^#define ([A-Z0-9]+)_' "$P" | sed 's/^#define //' | sort | uniq -c | sort -rn | head -30
# 4) 按某前缀列出全部代表(例:好友盒 FRIEND_BOX),带行号
grep -nE '^#define [A-Z0-9_]*FRIEND_BOX[A-Z0-9_]*_CMD ' "$P"
# 5) 已知一个 16-bit 值(例 0x30bb),反查它是哪个命令字
grep -niE '0x30bb' "$P"
# 6) 把命令字名 -> 值 全量导出,便于和 iOS 抓包逐一对表
grep -oE '#define [A-Za-z0-9_]+_[Cc][Mm][Dd][[:space:]]*\(0x[0-9A-Fa-f]+' "$P"我们抓到的是摩尔庄园 iOS 手游的实际流量;本仓库是 2007–2013 年网页 Flash 版的服务端真源码。二者很可能同源但已演进,把它当"字典"而非"逐字真值"来用。建议方法:
iOS 抓包通常是客户端 ↔ 接入/在线服务这一层,对应本仓库的 mole/online(server_proto_t / 广播器头 / DES),不是 DB 层的 PROTO_HEADER。先判断你抓的包头长度与字段:
- 18 字节、
length/proto_id(0x12345678)/cmd16/result/uid形态 → 像 DB 层PROTO_HEADER。 - 含
seq字段、len/seq/cmd/ret/id→ 像server_proto_t(内网服务间)。 - 含场景字段(mapid/opid)→ 像广播器
communicator_header_t。 - 都不像 → iOS 版自定义了新包头(很可能),只能靠命令字数值/正文结构反推。
- 从 iOS 抓包里取出疑似命令字的 16-bit 字段。
- 在本仓库反查:
grep -niE '0xXXXX' "$P"(注意大小写,值字面量大小写混用)。 - 命中后,用命令字名字 + 中文注释推断该包语义,再回到对应模块源码看处理函数(参数结构、字段顺序)。
反查时记得剥掉标志位:iOS 抓到的可能是裸命令字(不含
NEED_UPDATE/路由位),也可能保留。对一个值v,既查v,也查v & 0x00FF(去掉写库位)与v & 0x03FF(去掉摩尔1代路由位),三种都试。
光对上 cmdid 不够,还要看正文字段是否吻合:PROTO_HEADER 里的 uid、USER_GET_* 返回结构里的字段顺序(装扮 id、宠物 id、货币"小蜜"等)。字段定义散落在各模块,从 mole.md 入手定位处理函数,内容/数值表见 content-data.md。
- 包头不同:iOS 版几乎肯定换了包头(可能加 token/session/设备号/版本号字段),不要假设 18 字节
PROTO_HEADER原样保留。 - 字节序:源码是 x86 小端 packed。但
mole/online/cache_svr.c里出现htonl(cmd)(网络字节序=大端),说明部分服务间字段走大端。iOS 抓包要逐字段判断端序,别一刀切。 - 加密:客户端段有
DES_KEY ">ta:mee<"(见第 2.1 节)。iOS 版很可能换了密钥/算法(AES?),抓到密文要先定位握手/密钥协商,别拿源码里的旧 DES 密钥硬解(推测/待证)。 - 命令字可能重排:多年演进后,同一玩法的 cmdid 数值可能变,但命名前缀体系(USER_/PET_/FARM_…)与玩法语义大概率保留。优先按"玩法分类 + 字段结构"对齐,cmdid 数值作辅助证据。
- 新增/废弃:iOS 版有手游独有功能(新活动/付费点),网页源码里查不到属正常;反之网页源码里大量后台
SU_/SYSARG_命令不会出现在客户端流量里。
- 命令字 = 16-bit 复合位域:高 6 bit(
0xFC00)路由到 DB,0x0100标记写库,其余为业务序号。 - 报文头 packed 小端、18 字节、请求/响应同构;但 online/广播器/iOS 各有不同的头,务必分层。
mole/dbsvr/include/proto.h是命令字总字典(约 1300 条),按USER_/ROOMINFO_/SU_/SYSARG_/USERINFO_等前缀组织,用第 5 节的 grep 自查、第 6 节的方法与 iOS 抓包对表。
返回:../README.md · mole.md · content-data.md