内容参考于:易道云信息技术研究院VIP课
上一个内容:接管游戏连接服务器的操作
码云地址(master 分支):染指/titan
码云版本号:00820853d5492fa7b6e32407d46b5f9c01930ec6
代码下载地址,在 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的分享
以 接管游戏连接服务器的操作 它的代码为基础进行修改
首先通过 通过逆向分析确定游戏明文发送数据过程 分析得出数据发送的位置,然后它与connect用的是同一个对象,所以还是可以用虚函数表:如下图虚函数表3C位置里的函数里调用了send函数
然后它的参数,第一个参数是发送的数据包,第二个数据是发送的数据包的长度,它的返回值是一个bool类型,如下图它的返回值是al,al寄存器是1字节
然后通过下图两个红框位置的赋值操作,也能看出这是一个bool类型(true是1,false是0)
它的函数原型:
bool GameWinSock::Send(char* buff, int len);
模拟游戏发送数据:首先现在无法制作游戏的数据包,所以用游戏生成一个数据包,复制出来
通过按钮发送聊天数据:
资源视图新加按钮:
CUIWnd_0.cpp文件的修改:新加新加按钮点击事件
// 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));
}
GameWinSock.cpp文件的修改:新加 OnSend函数、_OnSend函数指针变量
#include "pch.h"
#include "GameWinSock.h"
#include "extern_all.h"
GameWinSock::PROC GameWinSock::_OnConnect{};
GameWinSock::PROC GameWinSock::_OnSend{};
// 这个函数拦截了游戏的连接
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);;
}
GameWinSock.h文件的修改:新加 OnSend函数、_OnSend函数指针变量
#pragma once
class GameWinSock
{
typedef bool(GameWinSock::* PROC)(char*, unsigned);
public:
static PROC _OnConnect;
static PROC _OnSend;
bool OnConnect(char* ip, unsigned port);
bool OnSend(char* buff, unsigned len);
};
GameProc.cpp文件的修改:修改了 _OnConnect函数
#include "pch.h"
#include "GameProc.h"
#include "extern_all.h"
// typedef bool(GameWinSock::* U)(char*, unsigned);
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);
}