游戏协议安全之Asyncore,Protocol Buffer使用

Asyncore

  • 概念
    Basic infrastructure for asynchronous socket service clients and servers. There are only two ways to have a program on a single processor do “more than one thing at a time”. Multi-threaded programming is the simplest and most popular way to do it, but there is another very different technique, that lets you have nearly all the advantages of multi-threading, without actually using multiple threads. it’s really only practical if your program is largely I/O bound. If your program is CPU bound, then pre-emptive scheduled threads are probably what you really need. Network servers are rarely CPU-bound, however. If your operating system supports the select() system call in its I/O library (and nearly all do), then you can use it to juggle multiple communication channels at once; doing other work while your I/O is taking place in the “background.” Although this strategy can seem strange and complex, especially at first, it is in many ways easier to understand and control than multi-threaded programming. The module documented here solves many of the difficult problems for you, making the task of building sophisticated high-performance network servers and clients a snap.
  • 使用
    简单来说asyncore即是一个异步的socket封装,其中包含了很多异步调用的socket操作方法。具体使用方法可查阅官方文档~

protobuf

安装

wget https://github.com/protocolbuffers/protobuf/releases/download/v3.11.2/protobuf-all-3.11.2.tar.gz
tar zxf protobuf-all-3.11.2.tar.gz
cd protobuf-3.11.2
./configure -prefix=/usr/local/
make && make install

验证是否安装成功
protoc --version
若报错执行 ldconfig
显示正常的版本号即可

使用

  1. 编辑协议.proto文件
syntax = "proto3";
package ProtoPac;

message AddressBook {
  repeated Person people = 1;
}

message Person {
  string name = 1;
  int32 id = 2;
  string email = 3;
  float money = 4;
  bool work_status = 5;

  repeated PhoneNumber phones = 6;
  MyMessage maps = 7;

}

message PhoneNumber {
    string number = 1;
    PhoneType type = 2;
}

enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
}

message MyMessage {
  map<int32, int32> mapfield = 1;
}
  1. 项目中引包
pip3 install protobuf==3.11.2
  1. 编辑add_person.py文件
from ProtoPac import addressbook_pb2

address_book = addressbook_pb2.AddressBook()
person = address_book.people.add()

person.id = 1
person.name = "AprilY"
person.email = "AprilY@qq.com"
person.money = 100000000.11
person.work_status = True

phone_number = person.phones.add()
phone_number.number = "123456"
phone_number.type = addressbook_pb2.MOBILE

maps = person.maps
maps.mapfield[1] = 1
maps.mapfield[2] = 2

# 序列化
serializeToString = address_book.SerializeToString()
print(serializeToString, type(serializeToString))

address_book.ParseFromString(serializeToString)

for person in address_book.people:
    print("p_id{},p_name{},p_email{},p_money{},p_workstatu{}"
          .format(person.id, person.name, person.email, person.money, person.work_status))

    for phone_number in person.phones:
        print(phone_number.number, phone_number.type)

    for key in person.maps.mapfield:
        print(key, person.maps.mapfield[key])

输出:
b'\n8\n\x06AprilY\x10\x01\x1a\rAprilY@qq.com% \xbc\xbeL(\x012\x08\n\x06123456:\x0c\n\x04\x08\x01\x10\x01\n\x04\x08\x02\x10\x02' <class 'bytes'>
p_id1,p_nameAprilY,p_emailAprilY@qq.com,p_money100000000.0,p_workstatuTrue
123456 0
1 1
2 2

协议安全工具

  • 基础运行原理
    before: [Client] <—> [Server]

    after: [Client] <—> [Proxy] <—> [Server]

    开启一个中间代理,将原本客户端直接连接服务器的方式,客户端连接代理,代理转发信息给服务器的方式,在这个过程中,代理获取服务器和客户端之间的收/发包数据,并进行解析。

# coding=utf-8
import asyncore
import socket
import logging

# Server 专门用于接收客户端请求
from proto.base.util import GetId

bind_ip = {}
bind_ports = {}


class Proxy(asyncore.dispatcher):
    def __init__(self, server_address, pCallback, param=None, server_port=None):
        asyncore.dispatcher.__init__(self)
        self.client = {}
        self.server = {}
        self.id = 0
        if len(server_address) == 3:
            self.server_port = server_address[2]
            self.port = server_address[1]
        else:
            self.server_port = server_address[1]
            self.port = server_address[1]
        self.ip = server_address[0]
        logging.info("ip=%s--------port=%d-----------server_port=%d" % (self.ip, self.port, self.server_port))
        self.param = param
        self.pCallback = pCallback
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)

        self.bind(("0.0.0.0", self.server_port))
        self.listen(5)

        # print("** start", pCallback.__name__, self.ip, self.port)
        if server_port is None:
            server_port = self.port

        bind_ip[self.port] = (self.ip, int(server_port))

    def handle_accept(self):
        self.id = GetId()
        client_proxy, addr = self.accept()
        server_proxy = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_proxy.connect((self.ip, self.port))
        param = []
        for i in range(len(self.param)):
            param.append(self.param[i])
        param.append(self.id)
        server_address = bind_ip[self.port]
        client, server = self.pCallback(client_proxy, server_proxy, server_address, param)
        self.client[self.id] = client
        self.server[self.id] = server

运行逻辑

  1. 启动后,会创建一个Proxy类,创建后代理会在本地对一个端口进行监听,并且连接到服务器对应的端口。模拟本地的一个服务器。
  2. 游戏客户端连接本地的端口,往本地的端口发包,被监听着端口的代理获取到。
  3. 代理解包(从底层代码调用到handle_read读取),然后解析后会找到相应的响应函数对特定的协议数据进行响应(各Base类的on_protocol_handle方法)。
  4. 解析完之后,控制台输出协议信息,并重新打包后(handle_write方法)转发到服务器。
  5. 在此之上封装一层套壳逻辑对历史收发协议进行保存修改的操作,从而达到手动篡改协议并发包的目的。

具体代码就不贴了~

之后使用对应的类去执行不通服务的协议,比如说游戏中的网关服务(Portalserver)、登录服务(LoginServer)、游戏服务(GameServer)、房间服务(RoomServer)等。
如何解析使用协议就要看具体的场景。

引用

https://cloud.tencent.com/developer/article/1719013
https://www.cnblogs.com/tomato0906/articles/4777959.html
https://www.cnblogs.com/tomato0906/articles/4807774.html
https://blog.csdn.net/qq_41962612/article/details/128712838

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Loganer

感谢

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值