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

相关文章

关于pandas的read_csv方法使用解读

《关于pandas的read_csv方法使用解读》:本文主要介绍关于pandas的read_csv方法使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录pandas的read_csv方法解读read_csv中的参数基本参数通用解析参数空值处理相关参数时间处理相关

Spring Shell 命令行实现交互式Shell应用开发

《SpringShell命令行实现交互式Shell应用开发》本文主要介绍了SpringShell命令行实现交互式Shell应用开发,能够帮助开发者快速构建功能丰富的命令行应用程序,具有一定的参考价... 目录引言一、Spring Shell概述二、创建命令类三、命令参数处理四、命令分组与帮助系统五、自定义S

MySQL中FIND_IN_SET函数与INSTR函数用法解析

《MySQL中FIND_IN_SET函数与INSTR函数用法解析》:本文主要介绍MySQL中FIND_IN_SET函数与INSTR函数用法解析,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一... 目录一、功能定义与语法1、FIND_IN_SET函数2、INSTR函数二、本质区别对比三、实际场景案例分

Python 迭代器和生成器概念及场景分析

《Python迭代器和生成器概念及场景分析》yield是Python中实现惰性计算和协程的核心工具,结合send()、throw()、close()等方法,能够构建高效、灵活的数据流和控制流模型,这... 目录迭代器的介绍自定义迭代器省略的迭代器生产器的介绍yield的普通用法yield的高级用法yidle

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

Python通过模块化开发优化代码的技巧分享

《Python通过模块化开发优化代码的技巧分享》模块化开发就是把代码拆成一个个“零件”,该封装封装,该拆分拆分,下面小编就来和大家简单聊聊python如何用模块化开发进行代码优化吧... 目录什么是模块化开发如何拆分代码改进版:拆分成模块让模块更强大:使用 __init__.py你一定会遇到的问题模www.

Spring Security基于数据库的ABAC属性权限模型实战开发教程

《SpringSecurity基于数据库的ABAC属性权限模型实战开发教程》:本文主要介绍SpringSecurity基于数据库的ABAC属性权限模型实战开发教程,本文给大家介绍的非常详细,对大... 目录1. 前言2. 权限决策依据RBACABAC综合对比3. 数据库表结构说明4. 实战开始5. MyBA

Java图片压缩三种高效压缩方案详细解析

《Java图片压缩三种高效压缩方案详细解析》图片压缩通常涉及减少图片的尺寸缩放、调整图片的质量(针对JPEG、PNG等)、使用特定的算法来减少图片的数据量等,:本文主要介绍Java图片压缩三种高效... 目录一、基于OpenCV的智能尺寸压缩技术亮点:适用场景:二、JPEG质量参数压缩关键技术:压缩效果对比

使用Python开发一个简单的本地图片服务器

《使用Python开发一个简单的本地图片服务器》本文介绍了如何结合wxPython构建的图形用户界面GUI和Python内建的Web服务器功能,在本地网络中搭建一个私人的,即开即用的网页相册,文中的示... 目录项目目标核心技术栈代码深度解析完整代码工作流程主要功能与优势潜在改进与思考运行结果总结你是否曾经

关于WebSocket协议状态码解析

《关于WebSocket协议状态码解析》:本文主要介绍关于WebSocket协议状态码的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录WebSocket协议状态码解析1. 引言2. WebSocket协议状态码概述3. WebSocket协议状态码详解3