101.网游逆向分析与插件开发-网络通信封包解析-解读聊天数据包并且利用Net发送

本文主要是介绍101.网游逆向分析与插件开发-网络通信封包解析-解读聊天数据包并且利用Net发送,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

上一个内容:C++还原网络通信系统发送功能

下一个内容:解读喊话道具数据包并且利用Net发送

码云地址(游戏窗口化助手 分支):https://gitee.com/dye_your_fingers/sro_-ex.git

码云版本号:c78a135393b36cc4a6a6956b1309472951eb2acd

代码下载地址,在 SRO_EX 目录下,文件名为:SRO_Ex-解读聊天数据包并且利用Net发送.zip

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

提取码:q9n5

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

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

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

提取码:78h8

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

以 C++还原网络通信系统发送功能 它的代码为基础进行修改

效果图:点击设置按钮消息发送成功

首先来到

然后断下来

下图红框位置,是把数据包拼入类的地方

然后分析数据包:现在的长度是E,数据是01 01 00 09 00 31 32 33 34 35 36 37 38 39 

然后再发送一个

这时它的长度是B,数据是 01 02 00 06 00 31 32 33 34 35 36

然后再发一个消息

这时的长度是F,数据是 01 03 00 0A 00 39 38 37 36 35 34 33 32 31 30

开始分析:

第一个字节的数据是01,它是固定的

第二个字节的数据每次都自增1,它不是一个固定的东西,不知道是什么

第四个字节09、06、0A也都在变,所以接下来就要破解第四个字节的数据跟数据有怎样的关系

第四个数据也很容易看出,第一个数据里的第四个字节的数据是09,然后我们的数据是9个字节,第二个数据里的第四个字节的数据是06,然后我们的数据是6个字节,第三个数据里的第四个字节的数据是0A,然后我们的数据是10个字节,所以第四个字节的数据是我们聊天发送的内容的长度

它的一个结构体

char 01 不变的

short un

short lenth

char* data 聊天数据

重新登陆游戏之后说话

下方是游戏重新登录之后的第二次说话,然后把这次说话的第二个字节原本是01改成了00(第一次说话时是00),这时发现,它说话成功了,但是没有任何内容

然后第四次说话

然后把原本的03改成了01

这时发现,我们说话的内容变了,原本是66666,现在变成了2222.。。。也就是我们的第二句话,这时很奇怪,我们并没有把第二句话发送给服务器,服务器也不可能保存我们说的话

它的作用,这就是韩国的这个游戏它设计的比较经典的地方,就是我们在下图红框位置说话,会对所有人广播

断线的时候说话,在下图红框位置是不显示内容的,只有在线的时候才会显示内容,也就是说在线的时候服务器是有响应的,所以当说完一句话之后服务器会给一个响应,会告诉你你说了一句什么样的话,周围的人也会看到你说了一句什么话,那么服务器再给予你说的什么话的内容的时候,这个游戏它的设计是,它给你发送消息前给每个消息编一个号,将来发送完消息之后,它告诉你第几号消息发送成功了,第几号消息发送成功,它就会把这个消息显示出来,这样的话它就不用把你说的这个话再广播给你了,这样就节约了网络,虽然节约了一点点,但也能看出它的一个设计

然后再登录一个账号,然后说话,这时说的是123

然后把数据改成456,这时网络发送的是456,不是123

然后查看两个账户的内容,另一个账号显示的是456,发送消息的账号显示的是123,这样证明了上方的想法,它会根据序号,读取本地之前发送的时候的存储的记录,然后显示说的话

然后现在就知道了,聊天数据包的结构,但是现在还有个问题,就是频道是哪个,然后在另一个频道说话

这时的数据包,数据包长度是8,数据是 05 05 00 3 00 31 31 31,与之前不同的是第一个字节的数据变成了05,所以第一个字节代表了频道

然后更新结构体:

它的一个结构体

char 01 频道 01 是公共频道 02 是单独聊天 04 是组队频道 05 是帮会频道

short un

short lenth

char* data 聊天数据

然后跟某个人单独聊天的数据结构,首先发送消息断点住

然后它的数据,长度是10,数据是 02 09 00 06 00 4D 61 73 74 65 72 03 00 31 32 33

 单独聊天的结构分析

第四个字节这时代表的是聊天目标角色名的长度

倒数第五个字节代表了聊天内容的长度

可以看出单独聊天与公共聊天是两种完全不同的数据结构,这个需要单独处理

然后测试C++代码,执行C++代码游戏闪退了,查看C++代码的方式,在代码中加一句MessageBox,然后通过x96dbg,在MessageBox函数头打断点,然后执行它的ret,就回到了我们写的C++代码里,这时就能分析我们的C++代码了

执行MessageBox函数的ret,就会来到下方我们的C++代码里,这样就可以对比我们的C++还原代码与游戏中的代码有什么不同了

然后修改C++代码(看后面的代码)之后,发现游戏不崩溃了,但是掉线了,这说明我们发送的数据有问题,严重不符合规定让服务器给我们断线了

extern_all.cpp文件的修改,修改了 InitClassProc函数

#include "pch.h"
#include "extern_all.h"void InitClassProc(LPVOID proc_addr, unsigned value)
{unsigned* uWrite = (unsigned*)proc_addr;uWrite[0] = value;
}void InitClassProc(LPVOID proc_addr, unsigned* vtable, unsigned index) {unsigned* addr = (unsigned*)vtable;InitClassProc(proc_addr, addr[index]);
}

CHelperUI.cpp文件的修改,修改了 OnBnClickedOk函数

// CHelperUI.cpp: 实现文件
//#include "pch.h"
#include "CHelperUI.h"
#include "afxdialogex.h"
#include "extern_all.h"LRESULT _stdcall CallWndProc(int nCode, WPARAM wParam, LPARAM lParam) {if (nCode == 0) {// 这里接收到的不只有游戏窗口的消息,还有我们的窗口消息// 所以要排除掉我们的窗口PCWPSTRUCT tmp = (PCWPSTRUCT)lParam;// 判断当前触发消息的窗口句柄是不是我们的游戏窗口句柄if (tmp->hwnd == _ui_helper->hwndGame) {// 拦截移动窗口消息if (tmp->message == WM_MOVE) {// 移动我们的窗口_ui_helper->MoveHelper();}if (tmp->message == WM_CLOSE) {// 游戏窗口右上角的X关闭按钮屏蔽掉了,这里我们给它处理一下// 让它点击之后可以隐藏游戏窗口并且显示我们的窗口/**_ui_helper->HideGame(); 里执行的代码如下面的两行this->ShowWindow(TRUE);::ShowWindow(hwndGame, GameShow = false);*/_ui_helper->HideGame();}}}return CallNextHookEx(_ui_helper->hookGameWnd, nCode, wParam, lParam);
}void _stdcall TimeProcHelper(HWND, UINT, UINT_PTR, DWORD) {if (_ui_helper)_ui_helper->ShowData();
}//获取程序当前所在显示器的分辨率大小,可以动态的获取程序所在显示器的分辨率
SIZE GetScreenResolution(HWND hWnd) {SIZE size{};if (!hWnd)return size;//MONITOR_DEFAULTTONEAREST 返回值是最接近该点的屏幕句柄//MONITOR_DEFAULTTOPRIMARY 返回值是主屏幕的句柄//如果其中一个屏幕包含该点,则返回值是该屏幕的HMONITOR句柄。如果没有一个屏幕包含该点,则返回值取决于dwFlags的值HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);MONITORINFOEX miex;miex.cbSize = sizeof(miex);if (!GetMonitorInfo(hMonitor, &miex))return size;DEVMODE dm;dm.dmSize = sizeof(dm);dm.dmDriverExtra = 0;//ENUM_CURRENT_SETTINGS 检索显示设备的当前设置//ENUM_REGISTRY_SETTINGS 检索当前存储在注册表中的显示设备的设置if (!EnumDisplaySettings(miex.szDevice, ENUM_CURRENT_SETTINGS, &dm))return size;size.cx = dm.dmPelsWidth;size.cy = dm.dmPelsHeight;return size;
}IMPLEMENT_DYNAMIC(CHelperUI, CDialogEx)CHelperUI::CHelperUI(CWnd* pParent /*=nullptr*/): CDialogEx(IDD_HELPER, pParent)
{}CHelperUI::~CHelperUI()
{
}BOOL CHelperUI::OnInitDialog()
{CDialogEx::OnInitDialog();this->SetBackgroundColor(RGB(255, 255, 255));HPBar.SetBkColor(RGB(0 ,0, 0));MPBar.SetBkColor(RGB(0 ,0, 0));RageBar.SetBkColor(RGB(0 ,0, 0));ExBar.SetBkColor(RGB(0 ,0, 0));HPBar.SetBarColor(RGB(255 ,0, 0));MPBar.SetBarColor(RGB(0x0, 0x0, 0x99));RageBar.SetBarColor(RGB(0x66, 0x0, 0x66));ExBar.SetBarColor(RGB(0x00, 0xFF, 0xCC));HPBar.SetRange(0, 999);MPBar.SetRange(0, 1000);RageBar.SetRange(0, 5);ExBar.SetRange(0, 1000);//HPBar.SetPos(50);//MPBar.SetPos(50);//RageBar.SetPos(50);//ExBar.SetPos(50);::SetTimer(this->m_hWnd, 0x100002, 100, TimeProcHelper);return TRUE;
}void CHelperUI::DoDataExchange(CDataExchange* pDX)
{CDialogEx::DoDataExchange(pDX);DDX_Control(pDX, IDC_PRO_HP, HPBar);DDX_Control(pDX, IDC_PRO_MP, MPBar);DDX_Control(pDX, IDC_PRO_RAGE, RageBar);DDX_Control(pDX, IDC_PRO_RAGE2, ExBar);
}BEGIN_MESSAGE_MAP(CHelperUI, CDialogEx)ON_BN_CLICKED(IDOK, &CHelperUI::OnBnClickedOk)ON_BN_CLICKED(IDOK2, &CHelperUI::OnBnClickedOk2)ON_WM_CLOSE()
END_MESSAGE_MAP()// CHelperUI 消息处理程序struct ChatData {char un; // 内存对齐char id;// 频道short index; // 说话内容的序号short lenth; // 说话内容的长度char text[0x50]; // 说话的内容
};void CHelperUI::OnBnClickedOk()
{char talk[]{ "欢迎来到地球!" };char buff[0xFF]{};int len;auto netdata = _pgamebase->SRO_Net->CreateNetData(0x7025, 0x0);ChatData chat;chat.id = 1;chat.index = 0;chat.lenth = sizeof(talk) -1 ;memcpy(chat.text, talk, chat.lenth);len = chat.lenth + 5;netdata->MakeData(&chat.id, len);_pgamebase->SRO_Net->SendData(&netdata);// CDialogEx::OnOK();//CString tmp;//tmp.Format(L"%d", _pgamebase->SRO_Player->MapId);//AfxMessageBox(tmp);////CString city;//city.Format(L"%s", _pgamebase->SRO_Res->ReadTitle(tmp.GetBuffer())->wcstr());//AfxMessageBox(city);// _ui->UIShow();
}void CHelperUI::Init()
{if (hwndGame) return;wchar_t buff[0xFF]{};// 获取主窗口句柄HWND _hwnd = ::GetActiveWindow();// 获取窗口标题::GetWindowText(_hwnd, buff, 0xFF);CString _title = buff;if (_title == L"SRO_CLIENT") {hwndGame = _hwnd;CRect rect_me;// 获取当前窗口句柄GetWindowRect(&rect_me);helper_Width = rect_me.Width();SetWindowsHook(WH_CALLWNDPROC, CallWndProc);}
}void CHelperUI::MoveHelper()
{if (hwndGame) {CRect rect;// 获取游戏窗口(主窗口)样式::GetWindowRect(hwndGame, &rect);int helper_left = rect.left + rect.Width();SIZE windowSize = GetScreenResolution(this->m_hWnd);if ((helper_left + helper_Width) > windowSize.cx) {helper_left -= helper_Width;}// 设置窗口大小::MoveWindow(this->m_hWnd, helper_left, rect.top, helper_Width, rect.Height(), TRUE);}
}void CHelperUI::ShowData()
{CString tmp;CString city;auto _player = _pgamebase->SRO_Player;if (_player) {tmp.Format(L"%s Lv %d", _player->Name.wcstrByName(), _player->Lv);this->SetWindowText(tmp);float hpStep = _player->HP * 1000;hpStep = hpStep / _player->MaxHP;HPBar.SetPos(hpStep);float mpStep = _player->MP * 1000;mpStep = mpStep / _player->MaxMP;MPBar.SetPos(mpStep);RageBar.SetPos(_player->Rage);unsigned max_exp = _pgamebase->SRO_Core->GetLvMaxExp(_player->Lv)->Exp;float expSetp = _player->Exp * 1000;expSetp = expSetp / max_exp;ExBar.SetPos(expSetp);tmp.Format(L"%.1f %.1f %.1f", _player->x, _player->h, _player->y);GetDlgItem(IDC_STATIC_CORD)->SetWindowText(tmp);tmp.Format(L"%d", _pgamebase->SRO_Player->MapId);city.Format(L"%s", _pgamebase->SRO_Res->ReadTitle(tmp.GetBuffer())->wcstr());GetDlgItem(IDC_STATIC_MAP)->SetWindowText(city);}
}void CHelperUI::Show()
{MoveHelper();this->ShowWindow(TRUE);
}void CHelperUI::OnBnClickedOk2()
{if (hwndGame) {::ShowWindow(hwndGame, GameShow = !GameShow);}
}void CHelperUI::OnClose()
{if (hwndGame) {::ShowWindow(hwndGame, GameShow = true);this->ShowWindow(FALSE);}
}void CHelperUI::HideGame()
{this->ShowWindow(TRUE);::ShowWindow(hwndGame, GameShow = false);
}

这篇关于101.网游逆向分析与插件开发-网络通信封包解析-解读聊天数据包并且利用Net发送的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Python中的Walrus运算符分析示例详解

《Python中的Walrus运算符分析示例详解》Python中的Walrus运算符(:=)是Python3.8引入的一个新特性,允许在表达式中同时赋值和返回值,它的核心作用是减少重复计算,提升代码简... 目录1. 在循环中避免重复计算2. 在条件判断中同时赋值变量3. 在列表推导式或字典推导式中简化逻辑

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

SpringBoot中配置文件的加载顺序解读

《SpringBoot中配置文件的加载顺序解读》:本文主要介绍SpringBoot中配置文件的加载顺序,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录SpringBoot配置文件的加载顺序1、命令⾏参数2、Java系统属性3、操作系统环境变量5、项目【外部】的ap

Golang HashMap实现原理解析

《GolangHashMap实现原理解析》HashMap是一种基于哈希表实现的键值对存储结构,它通过哈希函数将键映射到数组的索引位置,支持高效的插入、查找和删除操作,:本文主要介绍GolangH... 目录HashMap是一种基于哈希表实现的键值对存储结构,它通过哈希函数将键映射到数组的索引位置,支持

Go语言开发实现查询IP信息的MCP服务器

《Go语言开发实现查询IP信息的MCP服务器》随着MCP的快速普及和广泛应用,MCP服务器也层出不穷,本文将详细介绍如何在Go语言中使用go-mcp库来开发一个查询IP信息的MCP... 目录前言mcp-ip-geo 服务器目录结构说明查询 IP 信息功能实现工具实现工具管理查询单个 IP 信息工具的实现服

Python使用getopt处理命令行参数示例解析(最佳实践)

《Python使用getopt处理命令行参数示例解析(最佳实践)》getopt模块是Python标准库中一个简单但强大的命令行参数处理工具,它特别适合那些需要快速实现基本命令行参数解析的场景,或者需要... 目录为什么需要处理命令行参数?getopt模块基础实际应用示例与其他参数处理方式的比较常见问http

Python利用ElementTree实现快速解析XML文件

《Python利用ElementTree实现快速解析XML文件》ElementTree是Python标准库的一部分,而且是Python标准库中用于解析和操作XML数据的模块,下面小编就来和大家详细讲讲... 目录一、XML文件解析到底有多重要二、ElementTree快速入门1. 加载XML的两种方式2.

Java的栈与队列实现代码解析

《Java的栈与队列实现代码解析》栈是常见的线性数据结构,栈的特点是以先进后出的形式,后进先出,先进后出,分为栈底和栈顶,栈应用于内存的分配,表达式求值,存储临时的数据和方法的调用等,本文给大家介绍J... 目录栈的概念(Stack)栈的实现代码队列(Queue)模拟实现队列(双链表实现)循环队列(循环数组

Mysql用户授权(GRANT)语法及示例解读

《Mysql用户授权(GRANT)语法及示例解读》:本文主要介绍Mysql用户授权(GRANT)语法及示例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录mysql用户授权(GRANT)语法授予用户权限语法GRANT语句中的<权限类型>的使用WITH GRANT

java解析jwt中的payload的用法

《java解析jwt中的payload的用法》:本文主要介绍java解析jwt中的payload的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java解析jwt中的payload1. 使用 jjwt 库步骤 1:添加依赖步骤 2:解析 JWT2. 使用 N