46.网络游戏逆向分析与漏洞攻防-角色管理功能通信分析-设计通用的解码处理过程

本文主要是介绍46.网络游戏逆向分析与漏洞攻防-角色管理功能通信分析-设计通用的解码处理过程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!

如果看不懂、不知道现在做的什么,那就跟着做完看效果

现在的代码都是依据数据包来写的,如果看不懂代码,就说明没看懂数据包

内容参考于: 易道云信息技术研究院VIP课

上一个内容:45.解码发送数据内容输出到日志

码云地址(master 分支):https://gitee.com/dye_your_fingers/titan

码云版本号:e7a286370f35406ff68ce43325dd2ef5dd65418c

代码下载地址,在 titan 目录下,文件名为:titan-设计通用的解码处理过程.zip

链接:https://pan.baidu.com/s/1W-JpUcGOWbSJmMdmtMzYZg

提取码:q9n5

--来自百度网盘超级会员V4的分享

HOOK引擎,文件名为:黑兔sdk升级版.zip

链接:https://pan.baidu.com/s/1IB-Zs6hi3yU8LC2f-8hIEw

提取码:78h8

--来自百度网盘超级会员V4的分享

以 45.解码发送数据内容输出到日志 它的代码为基础进行修改

本次不做新的东西,本次去改改之前的代码,主要为了以后用起来方便,为了写代码写的更少,设计一个通用的解码的过程,下方的代码如果看不懂,那就根据说明瞥一眼,当之后使用时就能懂了,看不懂是正常的毕竟代码很乱

EnCode.h文件的修改:修改了 Init函数入参、EnCode构造函数入参

#pragma once/*EnCode结构体 un[6]、index、op是8字节union里也是8字节pointer、back它俩虽然是char类型,但它俩是指针类型,所以它们一共是8字节EnCode结构体一共是24字节
*/
class EnCode// 用于表示数据解析约定的数据
{
private:char un[6]{};// 
public:char index = 0;// op + un[3]是四字节,op与buffer是6字节,如果自动内存对齐op与buffer可能不会挨在一起char op = 0;union{char dataPool[0x8];int lenth;char byte;short stval;int val;float fval;double dbval;long long lval = 0;};char* pointer = 0;char* back = 0;
public:EnCode();EnCode(char*& buff, char ExIndex = 1);EnCode& operator=(char*& buff);int Init(char*& buff, char EnIndex = 1);~EnCode();
};class GBYTE :public EnCode {
public:using EnCode::EnCode;using EnCode::operator=;operator char();char value();
};
class GSHORT :public EnCode {
public:using EnCode::EnCode;using EnCode::operator=;operator short();short value();
};
class GINT :public EnCode {
public:using EnCode::EnCode;using EnCode::operator=;operator int();int value();
};
class GFLOAT :public EnCode {
public:using EnCode::EnCode;using EnCode::operator=;operator float();float value();
};
class GINT64 :public EnCode {
public:using EnCode::EnCode;using EnCode::operator=;operator long long();long long value();
};
class GDOUBLE :public EnCode {
public:using EnCode::EnCode;using EnCode::operator=;operator double();double value();
};
class GCHAR :public EnCode {
public:using EnCode::EnCode;using EnCode::operator=;operator const char*();const char* value();
};
class GUTF16 :public EnCode {
public:using EnCode::EnCode;using EnCode::operator=;operator const wchar_t*();const wchar_t* value();
};

EnCode.cpp文件的修改:修改了 Init函数入参、EnCode构造函数入参

#include "pch.h"
#include "EnCode.h"
#include "extern_all.h"int EnCode::Init(char*& buff, char EnIndex)
{index = EnIndex;op = buff[0];int len = data_desc[index][op].lenth;/*比如 07 0A 00 00 00 34 00 33 00 39 00 39 00 00 00buff + 1 的结果是 0A 00 00 00 34 00 33 00 39 00 39 00 00 00len是 07 所代表的类型的长度,也就是0A 00 00 00 这个东西memcpy(dataPool, buff + 1, len);也就是把 0A 00 00 00 复制到 dataPool里然后从下方的if ((op == 0x7))得到它的字符串*/memcpy(dataPool, buff + 1, len);buff = buff + 1 + len;// 下一个结构if (index == 0) {if ((op == 0x7)) {if (pointer)delete[]pointer;/*dataPool 与 lenth是一个联合体,联合体的特性就是所有变量共用一个内存,所以 dataPool 的值就是lenth的值从上方的注释得知现在dataPool的值是0A 00 00 00然后通过 lenth去读 0A 00 00 00结果就是十进制的 10然后创建一个10字节的空间给pointer然后在通过 memcpy(pointer, buff, lenth); 把字符串赋值给pointer*/pointer = new char[lenth];memcpy(pointer, buff, lenth);buff = buff + lenth;// 指向下一个字符串}}if (index == 1) {if ((op == 0x06) || (op == 0x7)) {if (pointer)delete[]pointer;pointer = new char[lenth];memcpy(pointer, buff, lenth);buff = buff + lenth;}}return 0;
}
/*buff是数据包_len暂时没用ExIndex是解析方式,因为分析的时候发现6有时是char类型有时是char*类型看懂此方法需要分析手动分析一次数据包(数据解析约定的数据包)然后带着数据包去看这个函数
*/
EnCode::EnCode(char*& buff,char EnIndex)
{Init(buff,EnIndex);
}EnCode::~EnCode()
{if(pointer)delete[] pointer;
}EnCode::EnCode()
{
}EnCode& EnCode::operator=(char*& buff)
{Init(buff);return *this;
}GBYTE::operator char()
{return this->byte;
}char GBYTE::value()
{return this->byte;
}GSHORT::operator short()
{return this->stval;
}short GSHORT::value()
{return this->stval;
}GINT::operator int()
{return this->val;
}int GINT::value()
{return this->val;
}GFLOAT::operator float()
{return this->fval;
}float GFLOAT::value()
{return this->fval;
}GINT64::operator long long()
{return this->lval;
}long long GINT64::value()
{return this->lval;
}GDOUBLE::operator double()
{return this->dbval;
}double GDOUBLE::value()
{return this->dbval;
}GCHAR::operator const char*()
{return this->pointer;
}const char* GCHAR::value()
{return this->pointer;
}GUTF16::operator const wchar_t*()
{return (wchar_t*)this->pointer;
}const wchar_t* GUTF16::value()
{return (wchar_t*)this->pointer;
}

NetClass.h文件的修改:新加 MAX_SEND_COUNT宏、NET_SEND_CHEAD类、NET_SEND_CHOOSECAMP类

#pragma once
#include "EnCode.h"
/*最多的数据项, 0A开头的数据包里有数据参数个数,这个红就是用来设定数据参数个数最大数量是多少,0A开头的数据包目前见过最大数据参数个数是十几个,这里写20应该完全够用,如果不够用到时候再增加
*/
#define MAX_SEND_COUNT 20
// 发送数据操作码定义
#define SC_CHOOSECAMP 591 // 阵营选择操作码
// 发送数据标准头
typedef struct NET_SEND_HEAD {// 为了内存对齐,当前结构不可能超过255,所以这样写是没问题的char len = sizeof(NET_SEND_HEAD) - 1;char op = 0x0A;short unst = 0;int unnt[4]{};short unst1 = 0;short count = 0;
}*PNET_SEND_HEAD;typedef class  NET_SEND_CHEAD {
public:GINT opcode;// 实现编码操作,模拟游戏数据包给服务端发送数据,这个模拟的数据包通过调用 CodeMe函数得到unsigned CodeMe(int size, const char* buffer);
}*PNET_SEND_CHEAD;typedef class NET_SEND_CHOOSECAMP :public NET_SEND_CHEAD {
public:GCHAR camps;
}*PNS_CHOOSECAMP;// 申请创建角色结构体
typedef struct NET_CREATEROLE_START {char un[0x02];// 用来做内存对齐char len = sizeof(NET_CREATEROLE_START) - 3;// 用来做内存对齐char op = 0x03;int code = 0x01;
}*PNET_CREATEROLE_START;// 删除角色数据结构
typedef struct DATA_DELROLE {char op;char buff[0x1F];int len;int un[8] = { 0x01 , 0x03};}*PDATADELROLE;/*数据包还原结构体要注意内存对齐,如果数据不满4字节,它字段会补齐比如结构体里有一个char变量,它是1字节,在内存里它可能会为了内存对齐让它变成4字节,所以这要注意
*/
// 登录数据
typedef struct DATA_LOGIN {int op = 0x0300;char buff[0x10]{};int lenId = 0x10;/*这个是登录的账号,它可能会变成0x20或更长,现在默认让它0x10读的时候以长度为准就好了*/char Id[0x10]{};int lenPass = 0x10;/*这个是登录的密码,它可能会变成0x20或更长,现在默认让它0x10读的时候以长度为准就好了*/char Pass[0x10]{};int lenCode = 0x10;char Code[0x10]{};int eop = 0x01;
}*PDATALOGIN;
typedef struct DATA_LOGIN_OK {// 登录成功数据包头int un[8] = { 0, 0, 0x76B, 0x0C, 0x1E,0, 0, 0 };int index = 0;int RoleCount = 0;
}*PDATALOGINOK;
typedef class ROLE_DATA {// 登录成功数据包
public:GBYTE byte;GINT un;GINT un1;GUTF16 name;GUTF16 infos;GINT un2;GINT64 un3;
}*PROLEDATA;

新加 NetClass.cpp文件

#include "pch.h"
#include "NetClass.h"unsigned NET_SEND_CHEAD::CodeMe(int size, const char* buffer)
{return 0;
}

NetClient.h文件的修改:新加 SelectCamp函数,修改了 OnSendCustom函数,DecodeChooseCamp函数改名为OnChooseCamp

#pragma once
#include "NetClass.h"
class NetClient // 监视客户端每一个操作
{
private:PROLEDATA roles;unsigned rolecount;bool logined = false;bool DelRole(wchar_t* rolename, unsigned _len);
public:/*模拟登陆的方法Id是账号Pass是密码它要基于发送的方法实现,因为我们没有连接socket的操作*/bool login(const char* Id, const char* Pass);bool DelRole(wchar_t* rolename);bool StartCreateRole();// 用于创建角色bool SelectCamp(const char* _campname);// 选择阵营// 根据角色名字获取一个登录成功数据包(选择角色列表里的一个数据)PROLEDATA GetRoleByName(wchar_t* rolename);
public:// 用于拦截游戏删除角色功能bool virtual OnDelRole(wchar_t* rolename, unsigned _len);// 用于拦截游戏登录功能void virtual Onlogin(const char* Id, const char*Pass);// 用于拦截游戏创建角色功能bool virtual OnStartCreateRole(int code);// opcode意思是操作码,count意思是数量,buffStart意思是解码的内容开始,buffend意思是解码的内容结束,buffer是原始的数据,len是原始数据的长度// char& buffer, int& len这俩参数带&的原因是,在 OnSendCustom 里进行修改之后,通过&的方式传递回去bool virtual OnSendCustom(PNET_SEND_CHEAD _coder, char*& buffer, unsigned& len);
public:bool virtual OnChooseCamp(PNS_CHOOSECAMP _coder);
public:// 处理失败,参数是错误码bool virtual Tips(int code);void virtual loginok(ROLE_DATA* _roles, int count);bool virtual OnScrStartCreateRole(short code,wchar_t* _txt);
};

NetClient.cpp文件的修改:新加 SelectCamp函数,修改了 OnSendCustom函数、OnChooseCamp函数

#include "pch.h"
#include "NetClient.h"
#include "extern_all.h"bool NetClient::login(const char* Id, const char* Pass)
{const int bufflen = sizeof(DATA_LOGIN) + 1;char buff[bufflen];DATA_LOGIN data;// 有些操作系统这样写会报错,因为内存不对齐,现在Windows下没事//PDATALOGIN _data = (PDATALOGIN)(buff + 1);// 这样写就能解决内存对齐问题PDATALOGIN _data =&data;int len = strlen(Id);memcpy(_data->Id, Id, len);len = strlen(Pass);memcpy(_data->Pass, Pass, len);memcpy(buff+1, _data, sizeof(DATA_LOGIN));buff[0] = I_LOGIN;return  WinSock->OnSend(buff, sizeof(buff));}bool NetClient::DelRole(wchar_t* rolename)
{PROLEDATA _role = GetRoleByName(rolename);if (_role == nullptr) {return false;}else {return DelRole(rolename, _role->name.lenth);}return false;
}bool NetClient::StartCreateRole()
{NET_CREATEROLE_START _data;return WinSock->OnSend(&_data.op, _data.len);
}bool NetClient::SelectCamp(const char* _campname)
{NET_SEND_HEAD _head;NET_SEND_CHOOSECAMP _data;/* sizeof(_data) / sizeof(EnCode)的原因NET_SEND_CHOOSECAMP结构体里面,没别 东西全是 EnCode 这个结构*/short count = sizeof(_data) / sizeof(EnCode);_head.count = count;// _data.CodeMe(count, buffer);return false;
}PROLEDATA NetClient::GetRoleByName(wchar_t* rolename)
{//PROLEDATA result = nullptr;for (int i = 0; i < rolecount; i++){// StrCmpW判断两个字符串是否相同// 比较时区分大小写,如果字符串相同返回0if (StrCmpW(roles[i].name.value(), rolename) == 0) {return &roles[i];}}return nullptr;
}bool NetClient::DelRole(wchar_t* rolename, unsigned _len)
{DATA_DELROLE _data;_data.op = 0x06;_data.len = _len;memcpy(_data.buff, rolename, _len);return WinSock->OnSend((char*)&_data, sizeof(DATA_DELROLE) - 1);
}bool NetClient::OnDelRole(wchar_t* rolename, unsigned _len)
{AfxMessageBox(rolename);return true;
}void NetClient::Onlogin(const char* Id, const char* Pass)
{/*const int bufflen = sizeof(DATA_LOGIN) + 1;char buff[bufflen];DATA_LOGIN data;// 有些操作系统这样写会报错,因为内存不对齐,现在Windows下没事//PDATALOGIN _data = (PDATALOGIN)(buff + 1);// 这样写就能解决内存对齐问题PDATALOGIN _data =&data;int len = strlen(Id);memcpy(_data->Id, Id, len);len = strlen(Pass);memcpy(_data->Pass, Pass, len);memcpy(buff+1, _data, sizeof(DATA_LOGIN));buff[0] = I_LOGIN;return  WinSock->OnSend(buff, sizeof(buff));*/
}bool NetClient::OnStartCreateRole(int code)
{return true;
}bool NetClient::OnSendCustom(PNET_SEND_CHEAD _coder, char*& buffer, unsigned& len)
{switch (_coder->opcode.value()){case SC_CHOOSECAMP:return OnChooseCamp((PNS_CHOOSECAMP)_coder);break;default:break;}return true;
}bool NetClient::OnChooseCamp(PNS_CHOOSECAMP _coder)
{PNS_CHOOSECAMP _p = (PNS_CHOOSECAMP)_coder;MessageBoxA(0, _p->camps, _p->camps, MB_OK);return true;
}bool NetClient::Tips(int code)
{
#ifdef  AnlyCString txt;if (code == 51001) {txt = L"登陆失败,易道云通行证不存在!";}else if (code == 51002) {txt = L"登录失败,密码错误!";}else {txt.Format(L"未知登录错误:%d", code);}anly->SendData(TTYPE::I_LOG, 0, txt.GetBuffer(), txt.GetLength()*2);
#endifreturn true;
}void NetClient::loginok(ROLE_DATA* _roles, int count)
{logined = true;if(roles) delete[] roles;roles = _roles;rolecount = count;
}bool NetClient::OnScrStartCreateRole(short code, wchar_t* _txt)
{return true;
}

GameWinSock.cpp文件的修改:修改了 OnloginOk函数、OnSendCustom函数、AnlyBuff函数

#include "pch.h"
#include "GameWinSock.h"
#include "extern_all.h"
#include "NetClass.h"
#include "EnCode.h"typedef bool(*DealProc)(char*&, unsigned&);DealProc SendDealProc[0x100];
DealProc RecvDealProc[0x100];GameWinSock::PROC GameWinSock::_OnConnect{};
GameWinSock::PROC GameWinSock::_OnSend{};
GameWinSock::PROC GameWinSock::_OnRecv{};bool DeafaultDeal(char*&, unsigned&) { return true; }// 登录数据包的处理
bool Onlogin(char*& buff, unsigned& len) {PDATALOGIN _data = (PDATALOGIN)(buff + 1);char* _id = _data->Id;_data = (PDATALOGIN)(buff + 1 + _data->lenId - 0x10);char* _pass = _data->Pass;Client->Onlogin(_id, _pass);/* 修改账号密码len = sizeof(DATA_LOGIN) + 1;buff = new char[len];DATA_LOGIN data;PDATALOGIN _data = &data;buff[0] = 0x2;CStringA _id = "";// 补充账号CStringA _pass = "";// 补充密码memcpy(_data->Id, _id.GetBuffer(), _id.GetLength());memcpy(_data->Pass, _pass.GetBuffer(), _pass.GetLength());memcpy(buff + 1, _data, len - 1);*//* 监控登录数据PDATALOGIN _data = (PDATALOGIN)buff;CStringA _id = _data->Id;_data = (PDATALOGIN)(buff + _data->lenId - 0x10);CStringA _pass = _data->Pass;CStringA _tmp;// 请求登录 账号[% s]密码[% s] 这个内容别人在逆向的时候就会看到// 所以这种东西需要自己搞个编码来代替它_tmp.Format("请求登录 账号[%s]密码[%s]", _id, _pass);
#ifdef  Anlyanly->SendData(TTYPE::I_DIS, 1, _tmp.GetBuffer(), _tmp.GetAllocLength());
#endif*//*返回false,游戏无法发送数据包原因看调用此此函数的位置 OnSend 函数(if (SendDealProc[buff[0]]((buff + 1), len - 1)))*/return true;
}bool OnTips(char*& buff, unsigned& len) {int* code = (int*)&buff[1];return Client->Tips(code[0]);
}bool OnDelRole(char*& buff, unsigned& len) {PDATADELROLE p = (PDATADELROLE)buff;return Client->OnDelRole((wchar_t*)(p->buff), p->len);// 返回值改为false将拦截发送的删除角色数据包// 详情看注册 OnDelRole 函数的位置,Init函数// return true;
}bool OnloginOk(char*& buff, unsigned& len) {PDATALOGINOK _p = (PDATALOGINOK)&buff[1];ROLE_DATA* roleDatas = nullptr;if (_p->RoleCount > 0) {char* buffStart = buff + 1 + sizeof(DATA_LOGIN_OK);WinSock->AnlyBuff(buffStart, buff + len);roleDatas = new ROLE_DATA[_p->RoleCount];for (int i = 0; i < _p->RoleCount; i++){roleDatas[i].byte.Init(buffStart, 0);roleDatas[i].un.Init(buffStart, 0);roleDatas[i].un1.Init(buffStart, 0);roleDatas[i].name.Init(buffStart, 0);roleDatas[i].infos.Init(buffStart, 0);roleDatas[i].un2.Init(buffStart, 0);roleDatas[i].un3.Init(buffStart, 0);}Client->loginok(roleDatas, _p->RoleCount);}return true;
}bool OnStartCreateRole(char*& buff, unsigned& len){// 申请进入创建角色界面int* code = (int*)&buff[1];return Client->OnStartCreateRole(code[0]);
}bool OnSendCustom(char*& buff, unsigned& len) {PNET_SEND_HEAD head = (PNET_SEND_HEAD)(buff - 1);int icount = head->count;if (icount < 1)return true;char* buffStart = (char*)head + sizeof(NET_SEND_HEAD);if (buffStart[0] != 0x02) {#ifdef  Anlyif(icount < MAX_SEND_COUNT)anly->SendData(TTYPE::I_DIS, I_SEND_CUSTOM, "SEND_CUSTOM MAX_SEND_COUNT 内存解码器空间不足", 46);anly->SendData(TTYPE::I_DIS, I_SEND_CUSTOM, "SEND_CUSTOM 发现异常数据", 25);
#endifreturn true;}#ifdef  AnlyWinSock->AnlyBuff(buffStart, buff + len, 1);
#endifint stDecode = 0;EnCode codes[MAX_SEND_COUNT]{};while (stDecode < icount) {codes[stDecode++] = buffStart;}return Client->OnSendCustom((PNET_SEND_CHEAD)codes, buff, len);}bool OnSvrStartCreateRole(char*& buff, unsigned& len) {short* _st = (short*)&buff[1];wchar_t* _txt = (wchar_t*)&buff[3];
#ifdef  AnlyCString txt;CStringA txtA;txt.Format(L"code:%d\r\n%s", _st[0], _txt);txtA = txt;//AfxMessageBox(txtA);anly->SendData(TTYPE::I_DIS, S_CREATEROLE_START, txtA.GetBuffer(), txt.GetAllocLength() + 1);
#endifreturn Client->OnScrStartCreateRole(_st[0], _txt);
}// 这个函数拦截了游戏的连接
bool GameWinSock::OnConnect(char* ip, unsigned port)
{
#ifdef  Anly// 长度24的原因,它是宽字节要,一个文字要2个字节,一共是10个文字加上结尾的0是11个// 所以 11 乘以2,然后再加2 anly->SendData(TTYPE::I_LOG, 0, L"服务器正在连接。。。", 24);
#endif// this是ecx,HOOK的点已经有ecx了WinSock = this;bool b = (this->*_OnConnect)(ip, port);// 下方注释的代码时为了防止多次注入,导致虚函数地址不恢复问题导致死循环,通过一次性HOOK也能解决/*unsigned* vtable = (unsigned*)this;vtable = (unsigned*)vtable[0];union {unsigned value;bool(GameWinSock::* _proc)(char*, unsigned);} vproc;vproc._proc = _OnConnect;DWORD oldPro, backProc;VirtualProtect(vtable, 0x10x00, PAGE_EXECUTE_READWRITE, &oldPro);vtable[0x34 / 4] = vproc.value;VirtualProtect(vtable, 0x10x00, oldPro, &backProc);*/return b;
}bool GameWinSock::OnSend(char* buff, unsigned len)
{/*这里就可以监控游戏发送的数据了*/#ifdef  Anlyanly->SendData(TTYPE::I_SEND, buff[0], buff, len);
#endif/*数据包的头只有一字节所以它的取值范围就是0x0-0xFF*/if (SendDealProc[buff[0]]((buff), len)) {// 执行失败不让游戏发送数据包return (this->*_OnSend)(buff, len);}else {// 发送失败屏蔽消息return true;// 屏蔽消息}}bool GameWinSock::OnRecving(char* buff, unsigned len)
{// MessageBoxA(0, "11111111111111", "0", MB_OK);/*监控游戏接收的数据包*/
#ifdef  Anlyanly->SendData(TTYPE::I_RECV, buff[0], buff, len);
#endifreturn RecvDealProc[buff[0]](buff, len);
}bool GameWinSock::OnRecv(char* buff, unsigned len)
{
//#ifdef  Anly
//	anly->SendData(1, buff, len);
//#endifreturn (this->*_OnRecv)(buff, len);
}void GameWinSock::Init()
{for (int i = 0; i < 0x100; i++) {SendDealProc[i] = &DeafaultDeal;RecvDealProc[i] = &DeafaultDeal;}// 注册登录数据包处理函数// SendDealProc[I_LOGIN] = &Onlogin;SendDealProc[I_DELROLE] = &OnDelRole;SendDealProc[I_CREATEROLE_START] = &OnStartCreateRole;SendDealProc[I_SEND_CUSTOM] = &OnSendCustom;// 注册数据登录失败数据包处理函数RecvDealProc[S_TIPS] = &OnTips;RecvDealProc[S_LOGINOK] = &OnloginOk;RecvDealProc[S_CREATEROLE_START] = &OnSvrStartCreateRole;
}// 它会生成一个结构体,详情看效果图
void GameWinSock::AnlyBuff(char* start, char* end, char index)
{
#ifdef  AnlyCStringA txt;CStringA tmp;CString utmp;EnCode _coder;GBYTE* _bytecoder;GSHORT* _shortcoder;GINT* _intcoder;GFLOAT* _floatcoder;GDOUBLE* _doublecoder;GCHAR* _asccoder;GUTF16* _utfcoder;GINT64* _int64coder;while (start < end) {_coder.Init(start, index);CStringA _opname = data_desc[_coder.index][_coder.op].name;// _opname.MakeLower()是变为小写字母,会影响 _opname它的值// 所以又写了一边 data_desc[_coder.index][_coder.op].nametmp.Format("%s %s;//", data_desc[_coder.index][_coder.op].name, _opname.MakeLower());txt = txt + tmp;if (_coder.index == 0) {switch (_coder.op){case 1:_shortcoder = (GSHORT*)&_coder;tmp.Format("%d\r\n", _shortcoder->value());txt = txt + tmp;break;case 2:_intcoder = (GINT*)&_coder;tmp.Format("%d\r\n", _intcoder->value());txt = txt + tmp;break;case 4:_floatcoder = (GFLOAT*)&_coder;tmp.Format("%f\r\n", _floatcoder->value());txt = txt + tmp;break;case 6:_bytecoder = (GBYTE*)&_coder;tmp.Format("%d\r\n", _bytecoder->value());txt = txt + tmp;break;case 7:_utfcoder = (GUTF16*)&_coder;utmp.Format(L"[%s]\r\n", _utfcoder->value());tmp = utmp;txt = txt + tmp;break;// 5号之前分析的忘记截图了,现在找不到它的数据包了,如果后面再见到05的时候再详细补充说明// 之前的分析05就是double类型case 5:_doublecoder = (GDOUBLE*)&_coder;tmp.Format("%lf\r\n", _doublecoder->value());txt = txt + tmp;break;case 8:case 3:_int64coder = (GINT64*)&_coder;tmp.Format("%l64d\r\n", _int64coder->value());txt = txt + tmp;break;default:break;}}if (_coder.index == 1) {switch (_coder.op){case 1:_shortcoder = (GSHORT*)&_coder;tmp.Format("%d\r\n", _shortcoder->value());txt = txt + tmp;break;case 2:_intcoder = (GINT*)&_coder;tmp.Format("%d\r\n", _intcoder->value());txt = txt + tmp;break;case 4:_floatcoder = (GFLOAT*)&_coder;tmp.Format("%f\r\n", _floatcoder->value());txt = txt + tmp;break;case 6:_asccoder = (GCHAR*)&_coder;tmp.Format("%s\r\n", _asccoder->value());txt = txt + tmp;break;case 7:_utfcoder = (GUTF16*)&_coder;utmp.Format(L"[%s]\r\n", _utfcoder->value());tmp = utmp;txt = txt + tmp;break;case 5:_doublecoder = (GDOUBLE*)&_coder;tmp.Format("%lf\r\n", _doublecoder->value());txt = txt + tmp;break;case 8:case 3:_int64coder = (GINT64*)&_coder;tmp.Format("%l64d\r\n", _int64coder->value());txt = txt + tmp;break;default:break;}}} anly->SendData(TTYPE::I_DIS, 0, txt.GetBuffer(), txt.GetAllocLength() + 1);
#endif
}

这篇关于46.网络游戏逆向分析与漏洞攻防-角色管理功能通信分析-设计通用的解码处理过程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/880193

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

软考系统规划与管理师考试证书含金量高吗?

2024年软考系统规划与管理师考试报名时间节点: 报名时间:2024年上半年软考将于3月中旬陆续开始报名 考试时间:上半年5月25日到28日,下半年11月9日到12日 分数线:所有科目成绩均须达到45分以上(包括45分)方可通过考试 成绩查询:可在“中国计算机技术职业资格网”上查询软考成绩 出成绩时间:预计在11月左右 证书领取时间:一般在考试成绩公布后3~4个月,各地领取时间有所不同

安全管理体系化的智慧油站开源了。

AI视频监控平台简介 AI视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程,实现芯片、算法、应用的全流程组合,从而大大减少企业级应用约95%的开发成本。用户只需在界面上进行简单的操作,就可以实现全视频的接入及布控。摄像头管理模块用于多种终端设备、智能设备的接入及管理。平台支持包括摄像头等终端感知设备接入,为整个平台提

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

怎么让1台电脑共享给7人同时流畅设计

在当今的创意设计与数字内容生产领域,图形工作站以其强大的计算能力、专业的图形处理能力和稳定的系统性能,成为了众多设计师、动画师、视频编辑师等创意工作者的必备工具。 设计团队面临资源有限,比如只有一台高性能电脑时,如何高效地让七人同时流畅地进行设计工作,便成为了一个亟待解决的问题。 一、硬件升级与配置 1.高性能处理器(CPU):选择多核、高线程的处理器,例如Intel的至强系列或AMD的Ry

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者