gRPC+Proto 实现键盘记载器 —— 深度实战解析

打印 上一主题 下一主题

主题 1763|帖子 1763|积分 5289

在当今的分布式系统开发领域,RPC(Remote Procedure Call,远程过程调用) 技能犹如一颗璀璨的明星,依附其强大的透明性和卓越的高性能,在微服务架构中占据着举足轻重的职位。本文将全方位、深入地剖析 RPC 的基本原理、显著上风以及潜在的范围性。同时,我们将以键盘记载器项目为例,详细演示怎样奇妙运用 gRPC 与 Protocol Buffers 实现跨语言通讯。通过丰富的代码讲解和直观的图示,助力你迅速掌握从情况搭建到前后端代码实现的每一个核心细节。
一、RPC 技能详解

1. RPC 的基本概念

RPC(Remote Procedure Call,远程过程调用) 是一种极具创新性的技能,它打破了传统当地调用的范围,允许应用程序通过网络无缝调用远程服务端的方法。对于开发者而言,就仿佛在调用当地函数一样自然流畅,底层复杂的网络通讯细节被奇妙地隐藏起来,极大地提升了开发效率。
其主要特点表现在以下两个关键方面:

  • 透明性
    当开发者进行远程接口调用时,无需为底层的序列化操纵、传输协议的选择以及网络异常的处理等繁琐问题而烦恼。全部这些复杂的细节都由强大的 RPC 框架精心封装,让开发者能够专注于业务逻辑的实现。
  • 封装性
    客户端和服务端通过接口左券(IDL: Interface Definition Language) 进行紧密协作。在开发之初,双方就需要明确约定好数据格式和方法签名。目前,常用的格式包罗 Protocol Buffers 和 JSON,其中 Protocol Buffers 以其更高的性能和更小的数据体积脱颖而出,成为浩繁开发者的首选。
2. RPC 的工作流程

RPC 调用的流程可以细致地拆解为以下几个关键步骤:

  • 客户端调用当地代理(Stub)
    当我们在代码中调用如 userService.getUser(id) 这样的方法时,Stub 会迅速发挥作用。它会将方法名和参数进行序列化处理,常见的序列化格式包罗二进制或 JSON 格式。经过序列化后的数据将被交由网络传输模块,为后续的网络传输做好准备。
  • 网络传输
    序列化后的数据通过 TCP、HTTP 或其他高效的传输协议,超过网络的界限,被准确无误地发送到远程服务器。在这个过程中,网络传输协议的选择会直接影响数据传输的效率和稳定性。
  • 服务端处理请求
    服务端乐成接收到数据后,会立即进行反序列化操纵,将数据还原为原始的格式。然后,通过精确的识别机制,确定详细调用的服务和方法。接着,执行相应的业务逻辑,并将执行结果再次进行序列化处理,最终将结果返回给客户端。
  • 客户端接收结果
    客户端接收到响应数据后,会再次进行反序列化操纵,将数据转换为可处理的格式。最后,将结果传递给原始调用者,以便继续后续的业务逻辑处理。
3. 典型组件介绍

RPC 框架通常由以下几个重要的组成部分协同工作:
组件作用客户端代理(Stub)它的主要职责是封装复杂的网络请求,模拟当地调用的方式,让开发者无需关注网络细节,从而实现对远程服务的透明调用。序列化协议负责将对象转换为二进制或文本数据格式,常见的如 Protobuf。序列化协议的选择直接影响数据传输的效率和数据的安全性。网络通讯层作为数据传输的桥梁,负责将序列化后的数据准确无误地传输到目的服务器。常见的协议包罗 TCP、UDP 和 HTTP2 等,差别的协议适用于差别的应用场景。服务端框架承担着接收客户端请求的重任,通过精确的路由机制,将请求分发到详细的服务实现中,并将执行结果返回给客户端。4. RPC 的应用场景与优缺点

应用场景


  • 微服务架构内部通讯
    在微服务架构中,各个微服务之间可以通过 RPC 实现高效、跨语言的数据交换。这种方式能够显著降低系统间的耦合度,提高系统的可维护性和可扩展性。例如,订单服务可以通过 RPC 调用支付服务,实现订单支付的功能。
  • 高性能分布式系统
    在实时数据处理或高频交易系统中,对系统的性能要求极高。采取二进制序列化协议(如 Protobuf)能够显著降低传输延迟,从而满足高性能需求。例如,高频交易系统需要在极短的时间内处理大量的交易数据,RPC 技能可以确保数据的快速传输和处理。
  • 跨平台系统集成
    RPC 能够奇妙地解决老旧系统和新开发应用间的数据交互问题,纵然它们利用差别的编程语言宁静台。例如,一个旧的 C++ 系统可以通过 RPC 与新开发的 Node.js 微服务进行无缝对接,实现系统的升级和扩展。
优缺点

优点:

  • 高开发效率:利用雷同当地函数调用的方式,大大简化了远程调用逻辑。开发者无需编写复杂的网络请求代码,只需专注于业务逻辑的实现,从而提高了开发效率。
  • 卓越性能:采取二进制协议(如 Protobuf),传输数据量小、效率高;长毗连机制也能减少握手时间,进一步提升系统的性能。例如,在大数据量的传输场景下,Protobuf 能够显著减少传输时间和带宽占用。
  • 跨语言支持:通过同一的接口定义,轻松实现差别编程语言之间的互调。这使得开发者可以根据项目的需求选择最合适的编程语言,而无需担心语言之间的兼容性问题。
缺点:

  • 调试难度较高:网络延迟、异常处理及超时重试等问题需要开发者额外关注。在调试过程中,由于涉及到网络通讯,问题的定位和解决相对复杂,需要开发者具备丰富的网络知识和调试履历。
  • 接口耦合风险:接口升级时要求客户端和服务端严酷同步,否则大概出现调用异常。在系统的升级和维护过程中,需要谨慎处理接口的变革,确保客户端和服务端的兼容性。
  • 技能复杂性:需要引入并学习额外的框架和序列化机制,初期学习曲线较陡峭。对于初学者来说,掌握 RPC 技能需要泯灭一定的时间和精力。
二、准备阶段与情况搭建

在本实例中,我们将精心选用前后端差别的技能栈来实现一个功能强大的键盘记载器。前端利用 JavaScript 结合 grpc-web 库,后端则采取 Go 语言搭配 Protocol Buffers。接下来,我们将详细介绍技能栈的选择、依赖的安装以及代码的生成过程。
1. 技能栈


  • 前端:JavaScript 与 grpc-web 库。JavaScript 作为前端开发的主流语言,具有广泛的应用场景和丰富的生态系统。grpc-web 库则为前端与后端的 gRPC 服务提供了无缝的毗连。
  • 后端:Go 语言及 Protocol Buffers。Go 语言以其高效的性能和简便的语法,成为后端开发的抱负选择。Protocol Buffers 则作为一种高效的序列化协议,能够确保数据在传输过程中的高效性和准确性。
  • 辅助工具:Node.js、npm、protoc 编译器。Node.js 为前端开发提供了强大的运行情况,npm 则是 JavaScript 包管理工具,方便我们安装和管理各种依赖。protoc 编译器则用于生成 Protocol Buffers 代码。
2. 安装依赖

起首,我们需要确保 Node.js 情况已安装。为了方便管理 Node.js 版本,保举利用 nvm(Node Version Manager):
  1. # 安装 nvm
  2. curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
  3. # 重启终端后安装最新 LTS 版本 Node.js
  4. nvm install --lts
复制代码
安装完成 Node.js 后,我们需要安装 gRPC 和 Protobuf 相干插件:
  1. # 安装 protoc 和插件
  2. go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
  3. go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest
  4. go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
  5. npm install -g protoc-gen-grpc-web
  6. npm install --global protoc-gen-js
复制代码
安装完成后,我们可以通过以下命令进行验证:
  1. # 确认已安装所有必要插件
  2. protoc-gen-go --version        # 需要 v1.28+
  3. protoc-gen-go-grpc --version   # 需要 v1.2+
  4. protoc-gen-grpc-web --version  # 需要 1.5.0+
  5. protoc-gen-js --version        # 无版本信息,报错参数不存在说明安装成功
复制代码
3. 生成代码

接下来,我们将编写 keylogger.proto 定义文件,并利用 protoc 编译器生成对应的 Go 和 JavaScript 代码。
keylogger.proto (路径:proto/keylogger.proto)
  1. syntax = "proto3";  
  2.   
  3. package keylogger;  
  4.   
  5. // 指定 Go 包路径:模块名/目录;包名  
  6. option go_package = "grpcKeyboardRecord/proto/keylogger;keylogger";  
  7.   
  8. service KeyLogger {  
  9.   rpc SendKey (KeyRequest) returns (KeyResponse);  
  10. }  
  11.   
  12. message KeyRequest {  
  13.   string key = 1;  
  14. }  
  15.   
  16. message KeyResponse {  
  17.   string status = 1;  
  18. }
复制代码
利用以下命令生成代码:
  1. # 生成 Go 和 gRPC-Web 代码
  2. protoc \                                      # 调用 Protocol Buffers 编译器
  3.   -I=./proto \                                # 指定 .proto 文件的搜索路径
  4.   --go_out=./proto/gen/go \                   # Go 代码输出路径(消息结构)
  5.   --go_opt=paths=source_relative \            # Go 代码的路径映射规则
  6.   --go-grpc_out=./proto/gen/go \              # Go gRPC 服务代码输出路径
  7.   --go-grpc_opt=paths=source_relative \       # Go gRPC 代码路径映射规则
  8.   --js_out=import_style=commonjs:./proto/gen/js \  # JS 代码输出路径和模块风格
  9.   --grpc-web_out=import_style=commonjs,mode=grpcwebtext:./proto/gen/js \  # gRPC-Web 代码配置
  10.   proto/keylogger.proto                       # 输入的原型文件
  11. # 根据当前需求修改命令
  12. protoc \
  13.   -I=./proto \
  14.   --go_out=./proto/keylogger \
  15.   --go_opt=paths=source_relative \
  16.   --go-grpc_out=./proto/keylogger \
  17.   --go-grpc_opt=paths=source_relative \
  18.   --js_out=import_style=commonjs:./proto \
  19.   --grpc-web_out=import_style=commonjs,mode=grpcwebtext:./proto \
  20.   proto/keylogger.proto
复制代码
代码生成详解

在生成代码的过程中,路径映射选项起着至关重要的作用。常见的两种路径映射模式为:

  • import 模式:根据 go_package 生成路径,输出会依照模块名称构造目次结构。这种模式适用于需要将生成的代码集成到大型项目中的场景,能够确保代码的目次结构与项目的整体架构相匹配。
  • source_relative 模式:生成的代码与 .proto 文件的相对路径保持一致,便于管理和更新。这种模式使得生成的代码能够直接对应原始文件的位置,从而简化了后续项目的代码维护工作。
执行上述命令后,将在以下目次中生成相应的文件:

  • 在 proto 目次下生成:keylogger_grpc_web_pb.js 和 keylogger_pb.js
  • 在 proto/keylogger 目次下生成:keylogger.pb.go 和 keylogger_grpc.pb.go
三、后端代码实现

接下来,我们将进入后端代码的实现阶段。我们将利用 Go 语言编写一个强大的 gRPC 服务,并借助 grpc-web 库实现 HTTP 网关,使得前端能够通过浏览器方便地访问 gRPC 服务。
下面是 main.go 的完备代码及详细注释说明:
  1. package main  
  2.   
  3. import (  
  4.     "context"  
  5.     "fmt"   
  6.     "log"   
  7.     "net"   
  8.     "net/http"   
  9.     "path/filepath"   
  10.     "strings"  
  11.     "google.golang.org/grpc"   
  12.     "google.golang.org/grpc/reflection"  
  13.     "github.com/improbable-eng/grpc-web/go/grpcweb"  
  14.     pb "grpcKeyboardRecord/proto"  
  15. )  
  16.   
  17. // 定义服务实现结构体,嵌入生成的未实现接口
  18. type server struct {  
  19.     pb.UnimplementedKeyLoggerServer  
  20. }  
  21.   
  22. // SendKey 方法:接收前端传入的按键,并打印,同时返回处理状态
  23. func (s *server) SendKey(ctx context.Context, req *pb.KeyRequest) (*pb.KeyResponse, error) {  
  24.     fmt.Printf("收到按键:%s\n", req.GetKey())  
  25.     return &pb.KeyResponse{Status: "接收成功"}, nil  
  26. }  
  27.   
  28. func main() {  
  29.     // 实例化 gRPC 服务器  
  30.     grpcServer := grpc.NewServer()  
  31.     // 注册自定义服务实现  
  32.     pb.RegisterKeyLoggerServer(grpcServer, &server{})  
  33.     // 注册服务反射,用于调试和客户端自动生成调用信息  
  34.     reflection.Register(grpcServer)  
  35.   
  36.     // 使用 grpc-web 包将 gRPC 服务器包装为支持 HTTP 请求的服务  
  37.     grpcWebServer := grpcweb.WrapServer(grpcServer)  
  38.   
  39.     // 设置静态文件服务,分别托管 public 和 dist 下的文件  
  40.     publicFS := http.FileServer(http.Dir("public"))  
  41.     distFS := http.FileServer(http.Dir("dist"))  
  42.   
  43.     // 自定义 HTTP 路由处理器
  44.     handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {  
  45.        // 首先判断是否为 gRPC-Web 请求,如是则交给 grpcWebServer 处理  
  46.        if grpcWebServer.IsGrpcWebRequest(r) || grpcWebServer.IsAcceptableGrpcCorsRequest(r) {  
  47.           grpcWebServer.ServeHTTP(w, r)  
  48.           return  
  49.        }  
  50.   
  51.        // 对 URL 以 /dist/ 开头的请求进行特殊处理
  52.        if strings.HasPrefix(r.URL.Path, "/dist/") {  
  53.           // 使用 StripPrefix 去掉 URL 中的 /dist/ 部分,再交由 distFS 处理  
  54.           http.StripPrefix("/dist/", distFS).ServeHTTP(w, r)  
  55.           return  
  56.        }  
  57.   
  58.        // 对于 "/" 或没有文件后缀的请求(SPA 应用场景),返回 index.html  
  59.        if r.URL.Path == "/" || filepath.Ext(r.URL.Path) == "" {  
  60.           http.ServeFile(w, r, "public/index.html")  
  61.           return  
  62.        }  
  63.   
  64.        // 其他静态资源请求默认交由 publicFS 处理  
  65.        publicFS.ServeHTTP(w, r)  
  66.     })  
  67.   
  68.     // 在指定端口启动服务器  
  69.     listener, err := net.Listen("tcp", ":8080")  
  70.     if err != nil {  
  71.        log.Fatalf("监听失败: %v", err)  
  72.     }  
  73.   
  74.     log.Println("服务启动在 http://localhost:8080")  
  75.     if err := http.Serve(listener, handler); err != nil {  
  76.        log.Fatalf("服务失败: %v", err)  
  77.     }  
  78. }
复制代码
代码详解


  • gRPC 服务器的初始化与注册
    代码中起首创建了一个 gRPC 服务器实例,并将自定义的 KeyLogger 服务注册到该服务器中。通过反射(reflection)功能,还可以在调试时利用 gRPC 客户端工具自动获取服务形貌。这使得开发和调试过程更加高效和便捷。
  • grpc-web 的支持
    利用 grpcweb.WrapServer 方法,gRPC 服务器被包装,使其支持来自浏览器的 HTTP 请求。这样,前端开发者不必直接处理复杂的 gRPC 协议细节,只需要通过 HTTP 请求即可与后端服务进行交互。
  • 静态文件的路由配置
    自定义 HTTP 处理函数中,对请求 URL 进行细致的判断:

    • 若为 gRPC-Web 请求则交由 grpc-web 服务处理,确保 gRPC 服务的正常运行。
    • 若 URL 包含 /dist/ 则利用 http.StripPrefix 来映射到静态资源目次,方便前端获取静态资源。
    • 对于根路径或没有后缀的请求,返回单页面应用的入口文件 index.html ,确保单页面应用的正常访问。
    • 其余请求则利用 public 目次下的资源,提供同一的静态资源服务。

四、前端代码实现

前端部分我们将利用 JavaScript 编写,通过 grpc-web 实现与后端服务的高效交互,并借助 webpack 进行打包优化。下面详细介绍每个文件的作用和实现逻辑。
1. 情况搭建

先安装 webpack 及开发服务器:
  1. npm install webpack -g
  2. npm install webpack-dev-server -g
复制代码
2. index.html —— 页面入口文件

此文件用于展示页面内容,并引用打包后的 JavaScript 文件:
  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>  
  4.     <meta charset="UTF-8" />  
  5.     <title>gRPC Keylogger</title>  
  6. </head>  
  7. <body>  
  8. <h1>键盘记录器(Webpack 构建)</h1>  
  9. <p>请尝试按键,会发送到后端</p>  
  10.   
  11. </body>  
复制代码
3. index.js  —— js入口文件
  1. import { KeyLoggerClient } from "../proto/keylogger_grpc_web_pb";  
  2. import { KeyRequest } from "../proto/keylogger_pb";  
  3.   
  4. const client = new KeyLoggerClient("http://localhost:8080");  
  5.   
  6. document.addEventListener("keydown", (event) => {  
  7.     const request = new KeyRequest();  
  8.     request.setKey(event.key);  
  9.   
  10.     client.sendKey(request, {}, (err, response) => {  
  11.         if (err) {  
  12.             console.error("Error:", err.message);  
  13.         } else {  
  14.             console.log("Server acknowledged:", response.getMessage());  
  15.         }  
  16.     });  
  17. });
复制代码
4. webpack.config.js  —— webpack配置文件
  1. const path = require("path");  
  2.   
  3. module.exports = {  
  4.     entry: "./src/index.js",  
  5.     output: {  
  6.         filename: "bundle.js",  
  7.         path: path.resolve(__dirname, "dist"),  
  8.     },  
  9.     mode: "development",  
  10.     devServer: {  
  11.         static: "./public",  
  12.         port: 3000,  
  13.     },  
  14.     resolve: {  
  15.         fallback: {  
  16.             buffer: require.resolve("buffer"),  
  17.         },  
  18.     },  
  19.         };
复制代码
执行


  • 在根目次下执行 npx webpack 将 src/index.js 文件构建打包到 dist/bundle.js  。

  • 在 public/index.html 中引用为绝对路径
  • 启动 服务器 go run main.go ,利用浏览器进行访问 http://localhost:8080



RPC(含 gRPC)技能在网络安全领域的应用及优缺点

一、WebSocket 与 gRPC/RPC 在红队攻击中的对比

在红队攻击和工具开发里,WebSocket 和 RPC(如 gRPC)上风与劣势各异,对比情况如下:
对比维度WebSocketgRPC/RPC协议基础基于 HTTP/1.1 Upgrade 机制,支持文本或二进制帧,长期化全双工通讯,加密可选基于 HTTP/2(gRPC)或自定义协议,逼迫二进制,多路复用短毗连,gRPC 逼迫 TLS潜伏通讯能力握手头显着,文本模式易被记载,长毗连易被检测利用标准 HTTP/2 流,Protobuf 二进制编码难明析,多路复用流量可伪装工具开发效率主流语言有成熟库,但需手动设计消息格式和分帧gRPC 支持 11+ 语言,Protobuf IDL 自动生成代码,自动分帧防御绕过能力WAF 检测规则成熟,时序特性显着,但可通过代理和域名前置二进制编码绕过率高,多路复用流量难区分,但 gRPC-Web 需特化代理,域名前置需定制策略典型场景选型建议


  • 优先选 WebSocket:快速 PoC 开发、兼容 Web 基础设施、低权限情况。
  • 优先选 gRPC/RPC:APT 长期潜伏、跨平台武器化、数据复杂传输。
混合攻击架构示例
  1. 高级 C2 架构
  2. ├── 入口层(WebSocket over WSS)
  3. │   ├── 伪装为合法 Web 应用
  4. │   └── 前端 JS 植入
  5. ├── 中继层(gRPC over HTTP/2)
  6. │   ├── 内部微服务通信
  7. │   └── 与云原生组件混合
  8. └── 植入层
  9.     ├── 轻量级 WebSocket 探针
  10.     └── 高隐蔽 gRPC 后门
复制代码
二、AI 加强型防火墙/WAF 对 gRPC C2 的对抗

即便防火墙/WAF 引入 AI 技能,gRPC C2 通讯仍可维持潜伏性。
AI 防御检测维度

从元数据层(调用频率、服务/方法名语义、请求 - 响应时间)、举动层(流量周期性、数据包大小分布)、内容层(Protobuf 语法校验、二进制熵值分析)分析流量。
gRPC C2 对抗策略


  • 元数据层:复用目的企业服务定义,动态化方法名。
  • 举动层:基于正常流量特性生成负载,混入伪随机数据块。
  • 内容层:分层加密,利用 Trailing Metadata 传递指令。
利用 AI 模型缺陷

数据污染攻击、对抗样本生成、模型逆向工程。
现实案例与未来方向

现实中,如 APT29 动态端口跳变和协议嫁接,Lazarus 组织上下文感知心跳和流量镜像。未来,防御方会引入新技能,攻击方则开发新架构和优化性能。
三、利用 gRPC 实现键盘记载器说明

利用 gRPC 技能实现键盘记载器,主要是为介绍该技能,未充分发挥其在网络安全领域上风,gRPC 更适合构建复杂分布式安全系统和工具。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

惊雷无声

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表