Skip to content

Server 开发指南

本指南介绍如何为IoM的Server组件进行开发和贡献。Server是IoM的核心数据处理和交互服务。

回到总览

返回开发者贡献指南 | 查看Client开发指南 | 查看Implant开发指南

环境配置

环境配置client与server完全一致, 按需取用

Go开发环境

版本要求: Go >= 1.20

推荐golang 1.20

golang 1.20是兼容Win7的最后一个版本

go version
protobuf环境

使用 aptapt-get:

apt install -y protobuf-compiler 
protoc --version  # 确保版本 >= 3

使用 Homebrew:

brew install protobuf
protoc --version

使用 Winget:

winget install protobuf 
protoc --version

protobuf Go插件 (指定版本)

go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.3.0  
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.34.1  

参考官方文档

更多安装选项请参考 protobuf.dev/installation

项目设置
  1. Fork并克隆仓库

    git clone --recurse-submodules https://github.com/your-username/malice-network.git
    cd malice-network
    git remote add upstream https://github.com/chainreactors/malice-network.git
    

  2. 安装依赖

    go mod tidy
    

  3. 生成protobuf文件 (如果修改了proto定义)

    go generate ./client
    

  4. 编译server

    go build ./server/
    

开发实践

扩展Proto协议

为了适用大部分场景中, 我们添加两个较为通用的proto。适配绝大部分场景, 如果可以满足你的需求, 请优先使用这两个proto

message Request {  
  string name = 1;  
  string input = 2;  
  repeated string args = 3;  
  map<string, string> params = 4;  
  bytes bin = 5;  
}  

message Response {  
  string output = 1;  
  string error = 2;  
  map<string, string> kv = 3;  
  repeated string array =4;  
}

如果这两个通用的proto无法满足, 才需要考虑修改proto

  1. 修改proto文件

proto/implant/implantpb/implant.proto中添加新的message:

message NewCommand {
    string param = 1; 
}

将message添加到Spite的body oneof中:

message Spite {  
  // ... 其他字段
  oneof body {  
    // ... 其他消息类型
    NewCommand new_command = 999;    
  }
}
  1. 修改server常量

helper/types/message.go中添加:

MsgNewCommand MsgName = "new_command"

buildSpite中添加对应的message处理:

case *implantpb.NewCommand:
   spite.Name = MsgNewCommand.String()
   spite.Body = &implantpb.Spite_NewCommand{NewCommand: msg.(*implantpb.NewCommand)} 
  1. 添加protobuf rpc

proto/services/clientrpc/service.proto中添加:

service MaliceRPC {  
  // ... 其他RPC
  rpc NewCommandRpc(implantpb.NewCommand) returns (clientpb.Task);
}

添加新的RPC接口

Server端提供两种类型的RPC实现:普通RPC和流式RPC。

1. 普通RPC

适用于简单的请求-响应模式:

func (rpc *Server) NewCommand(ctx context.Context, req *implantpb.Request) (*clientpb.Task, error) {
    greq, err := newGenericRequest(ctx, req)
    if err != nil {
        return nil, err
    }
    ch, err := rpc.asyncGenericHandler(ctx, greq)
    if err != nil {
        return nil, err
    }

    go greq.HandlerAsyncResponse(ch, types.MsgResponse)
    return greq.Task.ToProtobuf(), nil
}

2. 流式RPC

适用于需要分块传输或实时数据流的场景:

Execute示例 (实时输出流):

func (rpc *Server) Execute(ctx context.Context, req *implantpb.ExecRequest) (*clientpb.Task, error) {
    greq, err := newGenericRequest(ctx, req)
    if err != nil {
        return nil, err
    }

    if !req.Realtime {
        // 非实时模式:普通RPC
        ch, err := rpc.GenericHandler(ctx, greq)
        if err != nil {
            return nil, err
        }
        go greq.HandlerResponse(ch, types.MsgExec)
    } else {
        // 实时模式:流式RPC
        greq.Count = -1  // 无限流
        _, out, err := rpc.StreamGenericHandler(ctx, greq)
        if err != nil {
            return nil, err
        }

        go func() {
            for {
                resp := <-out
                exec := resp.GetExecResponse()

                // 验证响应
                err := handler.AssertSpite(resp, types.MsgExec)
                if err != nil {
                    greq.Task.Panic(buildErrorEvent(greq.Task, err))
                    return
                }

                // 处理响应
                err = greq.HandlerSpite(resp)
                if err != nil {
                    return
                }

                // 检查是否结束
                if exec.End {
                    greq.Task.Finish(resp, "")
                    break
                }
            }
        }()
    }

    return greq.Task.ToProtobuf(), nil
}

Listener开发

Listener与Server是解耦的,可以独立部署。开发新的Listener类型:

  1. 实现Pipeline接口
  2. 实现Parser接口
  3. 实现Cryptor接口

参考现有的TCP/HTTP实现进行开发。

todo

相关资源


⬅️ 返回开发者指南