二次开发
二次开发
本章节列举希望对sekiro框架进行二次开发的指导和相关资源
源码列表
Sekio是一个半开源项目,其核心功能在github开源,同时有一个带有高级功能的收费分支是闭源发展。你可以根据本章节的引导了解Sekiro的所有组成部分。以及考量选择商业版还是开源版本。
源码仓库列表:
- 商业版本服务器:https://github.com/yint-tech/sekiro3
- 服务端核心代码
- 文档
- sdk定义
- 前端代码(仅商业版本存在前端,但任何用户可以通过前端源码自行定义前端皮肤):https://github.com/yint-tech/sekiro-frontend
- 开源版本服务器:https://github.com/yint-tech/sekiro-open
- 各个语言的样例代码:https://github.com/yint-tech/sekiro-samples
- 各语言sdk
- 各语言使用demo
- 支持环境列表:Frida、Go、Java、Python、Xposed、WebJs、C#
客户端sdk开发
Sekiro已经为多种语言提供了sdk,这些语言包括:Frida、Java(Xposed)、Objective-c(IOS)、js(web)、python、go、c#, 如果您的需求场景并不是上诉语言,则可能需要自行完成sekiro的sdk开发,才能进行sekiro服务接入。
sekiro核心是一个socket的网络程序,他运行sekiro定义的一个私有协议结构,并且依靠包结构编码使得单一tcp隧道可以并行传输多个业务负载session,你需要了解sekiro的二进制协议才能进行对应的sdk开发。
当你开发自己的sdk的时候,建议选择上述任何一门您熟悉的语言作为参考
报文结构
magic
长度 8 byte; 魔数,固定值sekiro01
,其中 01 代表协议版本号,目前是 01 版本。
total_leng(total_length)
长度 4 byte; total_leng 的值为数据包从 type 开始直到该数据包结束占用的字节数,不包括魔数占用字节数和 total_leng 占用字节数,即 total_leng 的值为整体数据包总长度减去 12。
type
长度 1 byte; 代表该数据包的类型
- 0x00 心跳包数据包
- 0x10 客户端向服务端注册 group 数据包
- 0x11 客户端向服务端响应数据包
- 0x20 服务端调用客户端数据包
seq_id
长度 4 byte; 该数据包序列号,唯一标示该次请求;由服务端下发请求时设置,客户端在响应该次请求时设置为相同值
header_size
长度 1 byte; 代表该次数据包中有多少个消息头,当前协议要求最大值不能超过127
key_len
长度 1 byte; 该消息头 key 的内容长度,
key
长度不固定,由 key_len 的值决定,该消息头的 key,当前协议要求最长不能超过 127 个字节;
value_len
长度 1 byte; 该消息头 value 的长度
value
长度不固定,由 value_len 的值决定;该消息头的 value,当前协议要求最长不能超过 127 个字节;
data
长度不固定,由 total_leng 的值减去从 type 开始到 data 之前占用的字节数决定;该次数据报的负载数据
注:1.消息头可以出现零个或多个,但最多不能超过 127 个消息头 2.消息头 key 和 value 的长度均不能超过 127 个字节
整体调用流程
为了方便描述,后续报文内容按照结构体的方式展示,具体实现时需要按照规则进行编码
心跳
客户端连接上 sekiro 服务器之后如果超过一定时间未进行读写,服务器会发送心跳包给该客户端,客户端在接受到心跳包时必须回应服务端一个数据包, 否则服务端将主动断开该连接。当前版本服务端发送的心跳包与客户端响应的心跳包一致,报文结构大致如下:
struct{
magic: sekiro01
total_leng: 6
type: 0x00
seq_id: -1
header_size: 0
}
该结构按照规则编码后就是一个心跳包,客户端在实现时如果发现 type 为 0x00,可将该包原样返回给服务端。
注册 group
客户端在连接上服务端后的第一件事向服务端注册 group,这样服务端才能下发相应 group 的请求。客户端注册 group 报文结构大致如下:
magic: sekiro01
total_leng: 50
type: 0x10
seq_id: -1
header_size: 2
SEKIRO_CLIENT_ID:12345678
SEKIRO_GROUP: test
50(total_leng) = 1(type)+ 4(seq_id) + 1(header_size) + 1(key_length)+16("SEKIRO_CLIENT_ID")+1(value_length)+8("1234567") +1(key_length)+12("SEKIRO_GROUP")+1(value_length)+4("test") + 0(data)
上面展示了该数据报 total_leng 字段值的计算方式。这个数据包发送给服务端之后就标示该客户端的 clientId 为 12345678,group 为 test, 当服务器接受到 group 为 test 的请求任务时将会下发给该客户端
接受服务器请求
注册完成之后服务端在接受到任务之后会下发请求,请求报文格式大致如下:
magic: sekiro01
total_leng: ?
type: 0x20
seq_id: 1
data: "{"action":"test","a":""b","c":"d"}"
https://sekiro.iinti.cn/business/invoke?group=test&action=test&a=b&c=d
在浏览器请求该地址,sekiro 客户端就会接收到上面的报文内容。tatal_leng ?代替,并非实际值。该次请求报文的 seq_id 为 1,那么接受到该请求后对该请求的响应报文 seq_id 也需要设置为 1。
响应 Json 格式
sekiro 商业版响应 Json 时为了服务器不做 Json 解析但需要感知业务是否成功,数据包中的负载数据 data 内容并不是简单的 Json 字符串,有自己的编码规则; 编码格式如下:
- status: 状态,占4个字节;0 代表成功,1 代表失败
- msgLength: 占 4 字节,值表示附带信息字符串占用的字节数,为 0 代表没有 msg
- msg:附带信息
- stringLength, 占 4 字节,值表示后续字符串占用的字节数
- json string: 需要返回给业务的 json 字符串
接受到请求后需要进行响应,响应报文结构大致如下:
magic: sekiro01
total_leng: ?
type: 0x20
seq_id: 1
PAYLOAD_CONTENT_TYPE:CONTENT_TYPE_SEKIRO_FAST_JSON
data: 按规则编码
响应 Json 需要加入 PAYLOAD_CONTENT_TYPE:CONTENT_TYPE_SEKIRO_FAST_JSON 消息头表明负载数据格式。
答问:netty的lib为什么会重新修改包名,然后源码引入
这是因为netty是一个很底层的、API表面积过大、API多版本一致性较差的lib库,从这个方向来说netty不是一个优良的lib设计,然而netty在网络编程上的深度和沉淀方面又是非常强悍的, 他也算是世界顶级的项目。
这导致存在大量的其他sdk或者中间件服务会依赖netty,此时我们很容易发现多个lib共同依赖了netty,但是又是依赖的不同的netty版本。 这会导致有很大可能存在依赖冲突(选择任何一个版本的netty,会导致另一个lib报错)。这是因为不同netty版本提供的api不兼容。
这是在工程实践上遇到过多次的现象,实际上在遇到这个问题的时候需要很有经验调整各种lib库的版本,或者做一些源码hack操作。
而sekiro的场景一般运行在受限环境,比如Xposed环境下可能出现代码和宿主环境的netty不一致,但是受限环境我们无法调整netty版本。此时可能是一个无解的现状, 故Sekiro在sdk设计的时候,变将netty的源码copy了一份出来,然后修改了其包结构。这样sekiro使用的netty就是一个私有的netty发行版本,他永远不会导致依赖冲突