内容参考于:易道云信息技术研究院VIP课
上一个内容:接管游戏发送数据的操作
码云地址(master 分支):https://gitee.com/dye_your_fingers/titan
码云版本号:8256eb53e8c16281bc1a29cb8d26d352bb5bbf4c
代码下载地址,在 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的分享
以 接管游戏发送数据的操作 它的代码为基础进行修改
通过 接管游戏发送数据的操作 分析得到 0x10617FD7 位置是调用recv函数之后接收到数据处理一个数据包的函数位置,它调用的函数不是虚函数表里的函数了,而是一个普通的函数,如下图红框位置,它调用的是0x10618480(0x10618480是函数地址)函数
然后这个函数不知道是由于编译器优化还是什么原因,它是用eax传递的连接对象,但它本质上还是thiscall(一个类调用它的成员函数就是thiscall,Visual Studio里c++里的this就是取ecx寄存器里的值),所以调用的时候要特殊处理(因为类调用成员函数用的是ecx)
用游戏生成一个接收到的数据包,这里的数据包时聊天数据的数据包,用于模拟服务器欺骗客户端
游戏种发送的聊天数据:
修改一下数据包:原本时0x31现在改成0x32
效果图:成功欺骗了客户端,让客户端接收了伪造的数据
GameProc.cpp文件的修改:修改了 InitInterface函数,新加 _OnRecv函数
#include "pch.h" #include "GameProc.h" #include "extern_all.h" // typedef bool(GameWinSock::* U)(char*, unsigned); bool _OnRecv(HOOKREFS2) { unsigned* _esp = (unsigned*)_ESP; _EAX = WinSock->RecvPoint; WinSock->OnRecving((char*)_esp[2], _esp[1]); return true; } bool _OnConnect(HOOKREFS2) { /* 根据虚函数表做HOOK的操作 截取 ecx 获取 winsock 的值(指针) */ unsigned* vtable = (unsigned*)_EDX; //WinSock = (GameWinSock *)_ECX; /* 联合体的特点是共用一个内存 由于 GameWinSock::OnConnect 的 OnConnect函数是 GameWinSock类的成员函数 直接 vtable[0x34 / 4] = (unsigned)&GameWinSock::OnConnect; 这样写语法不通过 所以使用联合体,让语法通过 */ union { unsigned value; bool(GameWinSock::* _proc)(char*, unsigned); } vproc; DWORD oldPro, backProc; VirtualProtect(vtable, 0x100, PAGE_EXECUTE_READWRITE, &oldPro); /* vproc._proc = &GameWinSock::OnConnect; 这一句是把我们自己写的调用connect函数的地址的出来 */ vproc._proc = &GameWinSock::OnConnect; /* InitClassProc函数里做的是给指针赋值的操作 InitClassProc(&GameWinSock::_OnConnect, vtable[0x34/4]);这一句的意思是把 GameWinSock类里的_OnConnect变量的值赋值成vtable[0x34/4],这个 vtable[0x34/4] 是虚表里的函数 vtable[0x34/4]是游戏中调用connect函数的函数地址,经过之前的分析调用connect是先调用了虚表中的 一个函数,然后从这个函数中调用了connect函数 */ InitClassProc(&GameWinSock::_OnConnect, vtable[0x34/4]); vtable[0x34 / 4] = vproc.value; vproc._proc = &GameWinSock::OnSend; InitClassProc(&GameWinSock::_OnSend, vtable[0x3C / 4]); vtable[0x3C / 4] = vproc.value; VirtualProtect(vtable, 0x100, oldPro, &backProc); return true; } GameProc::GameProc() { hooker = new htd::hook::htdHook2(); Init(); InitInterface(); } void GameProc::LoadBase() { LoadLibraryA("fxnet2.dll"); } void GameProc::Init() { } void GameProc::InitInterface() { LoadBase(); // MessageBoxA(0, "1", "1", MB_OK); // 只会HOOK一次,一次性的HOOK hooker->SetHook((LPVOID)0x10617046, 0x1, _OnConnect, 0, true); /* 第一个参数是HOOK的位置 第二个参数是HOOK的位置的汇编代码的长度(用于保证执行的汇编代码完整) 第三个参数是HOOK之后当游戏执行到第一个参数的位置的时候跳转的位置 */ hooker->SetHook((LPVOID)0x10618480, 0x1, _OnRecv); /* 在这里绑定游戏处理数据包函数(0x10618480函数) 然后0x10618480函数在上面一行代码已经进行了HOOK 所以在调用_OnRecv函数指针时,它就会进入我们HOOK */ InitClassProc(&GameWinSock::_OnRecv, 0x10618480); }
GameWinSock.h文件的修改:新加 _OnRecv函数指针变量,OnRecving函数、OnRecv函数、vatble变量、un变量、RecvPoint变量
#pragma once class GameWinSock { typedef bool(GameWinSock::* PROC)(char*, unsigned); public: unsigned* vatble;// 虚函数表 unsigned un[17]; unsigned RecvPoint; // 游戏recv之后调用处理一个数据包函数时的eax,这里偏移是0x48 public: static PROC _OnConnect; static PROC _OnSend; static PROC _OnRecv; bool OnConnect(char* ip, unsigned port); bool OnSend(char* buff, unsigned len); bool OnRecving(char* buff, unsigned len); bool OnRecv(char* buff, unsigned len); };
GameWinSock.cpp文件的修改:新加 _OnRecv函数指针变量,OnRecving函数、OnRecv函数、vatble变量、un变量、RecvPoint变量
#include "pch.h" #include "GameWinSock.h" #include "extern_all.h" GameWinSock::PROC GameWinSock::_OnConnect{}; GameWinSock::PROC GameWinSock::_OnSend{}; GameWinSock::PROC GameWinSock::_OnRecv{}; // 这个函数拦截了游戏的连接 bool GameWinSock::OnConnect(char* ip, unsigned port) { // 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) { /* 这里就可以监控游戏发送的数据了 */ return (this->*_OnSend)(buff, len); } bool GameWinSock::OnRecving(char* buff, unsigned len) { // MessageBoxA(0, "11111111111111", "0", MB_OK); /* 监控游戏接收的数据包 */ return true; } bool GameWinSock::OnRecv(char* buff, unsigned len) { //AfxMessageBox(L"%s", buff); return (this->*_OnRecv)(buff, len); }
CUIWnd_0.cpp文件的修改:修改了 OnBnClickedButton1函数(按钮点击事件处理函数)
// CUIWnd_0.cpp: 实现文件 // #include "pch.h" #include "htdMfcDll.h" #include "CUIWnd_0.h" #include "afxdialogex.h" #include "extern_all.h" // CUIWnd_0 对话框 IMPLEMENT_DYNAMIC(CUIWnd_0, CDialogEx) CUIWnd_0::CUIWnd_0(CWnd* pParent /*=nullptr*/) : CDialogEx(IDD_PAGE_0, pParent) { } CUIWnd_0::~CUIWnd_0() { } void CUIWnd_0::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CUIWnd_0, CDialogEx) ON_BN_CLICKED(IDC_BUTTON1, &CUIWnd_0::OnBnClickedButton1) END_MESSAGE_MAP() // CUIWnd_0 消息处理程序 void CUIWnd_0::OnBnClickedButton1() { /* char buff[] = { 0xA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x02, 0x01, 00 ,0x00, 0x00, 0x07, 0x0E, 0x00, 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x31, 0x00, 0x32 ,0x00, 0x33, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; WinSock->OnSend(buff, sizeof(buff)); */ char buff[] = { 0x27, 0x46, 0x92, 0x02, 0x00, 0x00, 0x89, 0x02, 0x00, 0x00, 0x06, 0x00, 0x06, 0x05, 0x00, 0x00, 0x00, 0x63, 0x68, 0x61, 0x74, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x07, 0x0A, 0x00, 0x00, 0x00, 0x34, 0x00, 0x33, 0x00, 0x39, 0x00, 0x39, 0x00, 0x00, 0x00, 0x07, 0x5A, 0x02, 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x34, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x38, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00 }; WinSock->OnRecv(buff, sizeof(buff)); }