37.网络游戏逆向分析与漏洞攻防-游戏网络通信数据解析-解码器细化类的实现

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

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

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

上一个内容:36.数据解码器的实现

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

码云版本号:855b495f1be08ac56b15baa71d8f2accf5c3325b

代码下载地址,在 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的分享

以 36.数据解码器的实现 它的代码为基础进行修改

上一个内容中的代码存在问题,解析的登录人物数据不正确,本次已经对它进行的修改,详情看后面GameWinSock.cpp文件里OnloginOk函数

然后分析数据包发现,相同的职业的角色在下图红框中都是一样的英文,这个英文所代表的的意思,应该是武器

划分结构:

如下图,可以搞成C++类,下图必须看懂否则会看不懂下方的代码,下方的代码完全依据图中划分的结构来写的,也就是ROLE_DATA类,如果看不懂下图的内容,那就看不懂 ROLE_DATA类,下图的内容很简单不能看不懂

效果图:

GameWinSock.cpp文件的修改:

#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) { /* 修改账号密码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 Onloginfailed(char*&buff, unsigned& len) { int* code = (int*)&buff[1];Client->loginfailed(code[0]);return true; 
}bool OnloginOk(char*& buff, unsigned& len) {CStringA txt;CStringA tmp;CString utmp;PDATALOGINOK _p = (PDATALOGINOK)&buff[1];ROLE_DATA* roleDatas = nullptr;if (_p->RoleCount > 0) {/*	char* buffStart = buff + 1 + sizeof(DATA_LOGIN_OK);roleDatas = new ROLE_DATA[_p->RoleCount];for (int i = 0; i < _p->RoleCount; i++){roleDatas[i].byte.Init(buffStart);roleDatas[i].un.Init(buffStart);roleDatas[i].un1.Init(buffStart);roleDatas[i].name.Init(buffStart);roleDatas[i].infos.Init(buffStart);roleDatas[i].un2.Init(buffStart);roleDatas[i].un3.Init(buffStart);}Client->loginok(roleDatas, _p->RoleCount);*/char* buffStart = buff + 1 + sizeof(DATA_LOGIN_OK);char* buffEnd = buff + len;while (buffStart < buffEnd) {/*由于 EnCode 的入参带了& 所以可以在EnCode里直接对buffStart或len进行操作也就是说只要入参带了&在函数里的操作会影响它原本的值比如 char*a = 0;aaa(a)aaa(char*&AA){AA = new char[10]; // 它就会把a的值改成new char[10]}*/EnCode* _coder = new EnCode(buffStart, len);tmp.Format("%s:",data_desc[_coder->index][_coder->op].name);txt = txt + tmp;if (_coder->op == 0x7) {utmp = (wchar_t*)_coder->pointer;tmp = utmp;txt = txt + tmp;}elseif (_coder->op == 0x3) {tmp.Format("[%f]", _coder->fval);txt = txt + tmp;}else {tmp.Format("[%d]", _coder->val);txt = txt + tmp;}}#ifdef  Anly// 长度24的原因,它是宽字节要,一个文字要2个字节,一共是10个文字加上结尾的0是11个// 所以 11 乘以2,然后再加2 anly->SendData(TTYPE::I_DIS, 0, txt.GetBuffer(), txt.GetAllocLength() + 1);
#endif}return true;
}// 这个函数拦截了游戏的连接
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;// 注册数据登录失败数据包处理函数RecvDealProc[S_LOGINFAIL] = &Onloginfailed;RecvDealProc[S_LOGINOK] = &OnloginOk;
}

EnCode.cpp文件的修改:新加 GBYTE类实现operator重载函数、GSHORT类实现operator重载函数、GINT类实现operator重载函数、GFLOAT类实现operator重载函数、GINT64类实现operator重载函数、GDOUBLE类实现operator重载函数、GCHAR类实现operator重载函数、GUTF16类实现operator重载函数

#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, unsigned int& _len,char EnIndex)
{Init(buff,EnIndex);
}EnCode::~EnCode()
{if(pointer)delete[] pointer;
}EnCode::EnCode()
{
}GBYTE::operator char()
{return this->byte;
}GSHORT::operator short()
{return this->stval;
}GINT::operator int()
{return this->val;
}GFLOAT::operator float()
{return this->fval;
}GINT64::operator long long()
{return this->lval;
}GDOUBLE::operator double()
{return this->dbval;
}GCHAR::operator const char*()
{return this->pointer;
}GUTF16::operator const wchar_t*()
{return (wchar_t*)this->pointer;
}

EnCode.h文件的修改:新加 GBYTE类、GSHORT类、GINT类、GFLOAT类、GINT64类、GDOUBLE类、GCHAR类、GUTF16类

#pragma onceclass EnCode
{
private:char un[2]{};// 这个是为了内存对齐
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;
public:EnCode();EnCode(char*& buff, unsigned int& _len, char ExIndex = 0);int Init(char*& buff, char EnIndex = 0);~EnCode();
};class GBYTE :public EnCode {using EnCode::EnCode;
public:operator char();
};
class GSHORT :public EnCode {using EnCode::EnCode;
public:operator short();
};
class GINT :public EnCode {using EnCode::EnCode;
public:operator int();
};
class GFLOAT :public EnCode {using EnCode::EnCode;
public:operator float();
};
class GINT64 :public EnCode {using EnCode::EnCode;
public:operator long long();
};
class GDOUBLE :public EnCode {using EnCode::EnCode;
public:operator double();
};
class GCHAR :public EnCode {using EnCode::EnCode;
public:operator const char*();
};
class GUTF16 :public EnCode {using EnCode::EnCode;
public:operator const wchar_t*();
};

NetClient.h文件的修改:放开 loginok函数声明

#pragma once
#include "NetClass.h"
class NetClient // 监视客户端每一个操作
{
public:/*模拟登陆的方法Id是账号Pass是密码它要基于发送的方法实现,因为我们没有连接socket的操作*/bool login(const char* Id, const char*Pass);
public:// 登陆失败,参数是错误码void loginfailed(int code);void loginok(ROLE_DATA*roles, int count);
};

NetClient.cpp文件的修改:新加 loginok函数

#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;WinSock->OnSend(buff, sizeof(buff));return true;
}void NetClient::loginfailed(int code)
{CString txt;if (code == 51001) {txt = L"登陆失败,易道云通行证不存在!";}else if (code == 51002) {txt = L"登录失败,密码错误!";}else txt = L"未定义错误!";#ifdef  Anlyanly->SendData(TTYPE::I_LOG, 0, txt.GetBuffer(), txt.GetLength()*2);
#endif
}void NetClient::loginok(ROLE_DATA* roles, int count)
{CString txt;CString tmp;txt.Format(L"游戏登录成功!角色数量[%d]\r\n", count);for (int i = 0; i < count; i++){tmp.Format(L"byte=%d\r\n", (char)roles[i].byte);txt += tmp;tmp.Format(L"un=%d\r\n", (int)roles[i].un);txt += tmp;tmp.Format(L"un1=%d\r\n", (int)roles[i].un1);txt += tmp;tmp.Format(L"name=%s\r\n", (const wchar_t*)roles[i].name);txt += tmp;tmp.Format(L"infos=%s\r\n", (const wchar_t*)roles[i].infos);txt += tmp;tmp.Format(L"un2=%d\r\n", (int)roles[i].un2);txt += tmp;tmp.Format(L"un3=%d\r\n", (long long)roles[i].un3);txt += tmp;}AfxMessageBox(txt);
}

NetClass.h文件的修改:新加 ROLE_DATA类

#pragma once
#include "EnCode.h"
/*数据包还原结构体要注意内存对齐,如果数据不满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;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/776006.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker进阶:Docker-compose 实现服务弹性伸缩

Docker进阶&#xff1a;Docker-compose 实现服务弹性伸缩 一、Docker Compose基础概念1.1 Docker Compose简介1.2 Docker Compose文件结构 二、弹性伸缩的原理和实现步骤2.1 弹性伸缩原理2.2 实现步骤 三、技术实践案例3.1 场景描述3.2 配置Docker Compose文件3.3 使用 docker-…

今天起,Windows可以一键召唤GPT-4了

现在&#xff0c;OpenAI 大模型加持的 Copilot 功能终于登陆 Windows 了。 把 Copilot 按钮放在 Windows 桌面的任务栏&#xff0c;甚至实体键盘上&#xff0c;用大模型提升每个人的生产效率。 美东时间 3 月 21 日周四&#xff0c;生成式 AI 领军的微软又为我们带来了一点小…

Web系统开发之——文章管理

原文地址&#xff1a;Web系统开发之——文章管理 - Pleasure的博客 下面是正文内容&#xff1a; 前言 经过一番考量&#xff0c;关于Web应用系统功能部分的开发&#xff0c;决定采取基础的文字文章管理为核心功能。 不再采取前后端分阶段完成的方式&#xff0c;而是以一个一个…

【QT入门】 QTabWidget各种常见用法详解

往期回顾&#xff1a; 【QT入门】 Qt代码创建布局之分裂器布局详解-CSDN博客 【QT入门】 Qt代码创建布局之setLayout使用-CSDN博客 【QT入门】 Qt代码创建布局之多重布局变换与布局删除技巧-CSDN博客 【QT入门】 QTabWidget各种常见用法详解 一般来说&#xff0c;学一个新的控…

报错there is no HDFS_NAMENODE_USER defined

在Hadoop安装目录下找到sbin文件夹&#xff0c;修改里面的四个文件 1、对于start-dfs.sh和stop-dfs.sh文件&#xff0c;添加下列参数&#xff1a; HDFS_DATANODE_USERroot HDFS_DATANODE_SECURE_USERhdfs HDFS_NAMENODE_USERroot HDFS_SECONDARYNAMENODE_USERroot 2、对于st…

2024-2029年中国中子探测器行业市场深度调研及发展前景预测报告

中子探测器可应用范围广泛 全球市场规模持续扩大 中子探测器&#xff0c;是一种检测中子辐射的仪器&#xff0c;采用中子吸收材料&#xff0c;利用中子与材料相互作用&#xff0c;来检测是否存在中子以及中子能量强度。中子是不带电荷的粒子&#xff0c;无法利用电离辐射探测器…

20221124 kafka实时数据写入Redis

一、上线结论 实现了将用户线上实时浏览的沉浸式视频信息&#xff0c;保存在Redis中这样一个功能。为实现沉浸式视频离线推荐到实时推荐提供了强有力的支持。目前只是应用在沉浸式场景&#xff0c;后续也能扩展到其他所有场景。用于两个场景&#xff1a;&#xff08;1&#xf…

2024年天津财经大学珠江学院退役大学生士兵专升本专业课报名须知

天津财经大学珠江学院2024年高职升本科&#xff08;面向退役大学生士兵&#xff09;职业技能综合考查报考须知 一、报名条件 报考天津财经大学珠江学院2024年高职升本科职业技能综合考查的退役大学生士兵应符合天津市及我院规定的报考资格。考生须完成天津市高职升本科文化考…

数据结构/C++:位图 布隆过滤器

数据结构/C&#xff1a;位图 & 布隆过滤器 位图实现应用 布隆过滤器实现应用 哈希表通过映射关系&#xff0c;实现了O(1)的复杂度来查找数据。相比于其它数据结构&#xff0c;哈希在实践中是一个非常重要的思想&#xff0c;本博客将介绍哈希思想的两大应用&#xff0c;位图…

HTTPS:原理、使用方法及安全威胁

文章目录 一、HTTPS技术原理1.1 主要技术原理1.2 HTTPS的工作过程1.2.1 握手阶段1.2.2 数据传输阶段 1.3 CA证书的签发流程1.4 HTTPS的安全性 二、HTTPS使用方法三、HTTPS安全威胁四、总结 HTTPS&#xff08;全称&#xff1a;Hyper Text Transfer Protocol over Secure Socket …

git提交-分支开发合并-控制台操作

git提交-分支开发合并-控制台操作 git的基本概念工作区、暂存区和版本库工作区&#xff1a;就是你在电脑里能看到的目录&#xff08;隐藏目录 .git不算工作区&#xff09;。暂存区&#xff1a;英文叫 stage 或 index。一般存放在本地的.git目录下的index 文件&#xff08;.git/…

036—pandas 按行将列名根据值由大到小排序

前言 数据处理中&#xff0c;按行排列的列名可以提供更直观的数据探索和分析方式。 你可以逐行查看列名&#xff0c;了解每列的含义和特征&#xff0c;有助于更好地理解数据集的结构和内容。 需求&#xff1a; 需要增加一列「分布方式」&#xff0c;每行的值是本行基金名称对…

双指针算法:三数之和

文章目录 一、[题目链接&#xff1a;三数之和](https://leetcode.cn/problems/3sum/submissions/515727749/)二、思路讲解三、代码演示 先赞后看&#xff0c;养成习惯&#xff01;&#xff01;&#xff01;^ _ ^<3 ❤️ ❤️ ❤️ 码字不易&#xff0c;大家的支持就是我坚持…

对BSV区块链网络访问规则NAR通俗易懂的解释

​​发表时间&#xff1a;2024年2月21日 我们可以把BSV区块链网络想象成在公园里举办的一场大型公共足球比赛。虽然这是一场友谊赛&#xff0c;但在比赛前&#xff0c;每个人都要理解并同意基本规则。举例来说&#xff0c;除了守门员之外&#xff0c;任何球员不得用手触球。 在…

JetBrains全家桶激活,分享 WebStorm 2024 激活的方案

大家好&#xff0c;欢迎来到金榜探云手&#xff01; WebStorm公司简介 JetBrains 是一家专注于开发工具的软件公司&#xff0c;总部位于捷克。他们以提供强大的集成开发环境&#xff08;IDE&#xff09;而闻名&#xff0c;如 IntelliJ IDEA、PyCharm、和 WebStorm等。这些工具…

【动手学深度学习】深入浅出深度学习之线性神经网络

目录 &#x1f31e;一、实验目的 &#x1f31e;二、实验准备 &#x1f31e;三、实验内容 &#x1f33c;1. 线性回归 &#x1f33b;1.1 矢量化加速 &#x1f33b;1.2 正态分布与平方损失 &#x1f33c;2. 线性回归的从零开始实现 &#x1f33b;2.1. 生成数据集 &#x…

Linux:文件增删 文件压缩指令

Linux&#xff1a;文件增删 & 文件压缩指令 文件增删touch指令mkdir指令cp指令rm指令rmdir指令 文件压缩zip & unzip 指令tar指令 文件增删 touch指令 功能&#xff1a;touch命令参数可更改文档或目录的日期时间&#xff0c;包括存取时间和更改时间&#xff0c;或者新…

离线数仓(八)【DWD 层开发】

前言 1、DWD 层开发 DWD层设计要点&#xff1a; &#xff08;1&#xff09;DWD层的设计依据是维度建模理论&#xff08;主体是事务型事实表&#xff08;选择业务过程 -> 声明粒度 -> 确定维度 -> 确定事实&#xff09;&#xff0c;另外两种周期型快照事实表和累积型…

第19篇:基本RS锁存器

Q&#xff1a;本期开始我们来设计实现时序逻辑电路&#xff0c;首先来设计由与非门构成的基本RS锁存器。 A&#xff1a;基本RS锁存器工作原理&#xff1a;锁存器的2个输入端均为低电平有效&#xff0c;一般情况下&#xff0c;2个输入端均为1时输出状态维持不变&#xff0c;只有…