9.网络游戏逆向分析与漏洞攻防-游戏网络架构逆向分析-接管游戏连接服务器的操作
内容参考于:易道云信息技术研究院VIP课
上一个内容:游戏底层功能对接类GameProc的实现
码云地址(master 分支):https://gitee.com/dye_your_fingers/titan
码云版本号:44c54d30370d3621c1e9ec3d7fa1e2a028e773e9
代码下载地址,在 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的分享
以 游戏底层功能对接类GameProc的实现 它的代码为基础进行修改
HOOK下图红框位置:0x10617046(通过之前分析找到的)
然后在注入的时候做HOOK发现,会崩溃,然后发现hook的点没有被游戏加载,解决办法我们在HOOK之前手动的把模块加载进入,不等游戏加载了
黑兔sdk升级:首先点击属性
找到下图红框位置,主要时看$(htd)\indude,如果下图红框位置不显示 $(htd)\indude
而显示不同选项,如下图红框
这时把配置这一行设置成跟下图一样,就好了
下载完升级版的黑兔std头文件可能无法正常加载到项目中,所以要手动加,首先检查它的目录对不对,如下图红框,我这里是对的,我的头文件就放在了那里
如果不对,首先点击下图红框位置
选择头文件所在目录,这样就把头文件设置好了
然后设置dll库,库目录是下图红框位置,手动设置方法与上方设置头文件一样
全部搞完之后,选择下图重新扫描解决方案,就不会报红了(就算报红也能编译通过)
这里补充一个东西:就是当执行了黑兔SDK,但是在创建项目的时候下图红框里的东西不存在
首先不打开项目启动 Visual Studio 然后选择选项:
然后看下图红框里的目录
查看目录中是否有下图红框里的两个文件,如果没有就复制到上图红框目录下
如下图
GameProc.cpp文件的修改:新加 _OnConnect函数,修改了 InitInterface函数、LoadBase函数
#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;
/*
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]);
CString str;
str.Format(L"===vtable:%X", vtable[0x34 / 4]);
AfxMessageBox(str);
DWORD oldPro,backProc;
VirtualProtect(vtable, 0x100, PAGE_EXECUTE_READWRITE, &oldPro);
vtable[0x34 / 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);
hooker->SetHook((LPVOID)0x10617046, 0x1, _OnConnect, 0, true);
}
新加GameWinSock.h文件:
#pragma once
class GameWinSock
{
typedef bool(GameWinSock::* PROC)(char*, unsigned);
public:
static PROC _OnConnect;
bool OnConnect(char* ip, unsigned port);
};
新加GameWinSock.cpp文件:
#include "pch.h"
#include "GameWinSock.h"
#include "extern_all.h"
GameWinSock::PROC GameWinSock::_OnConnect{};
// 这个函数拦截了游戏的连接
bool GameWinSock::OnConnect(char* ip, unsigned port)
{
// this是ecx,HOOK的点已经有ecx了
WinSock = this;
MessageBoxA(0, ip, ip, MB_OK);
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, 0x100, PAGE_EXECUTE_READWRITE, &oldPro);
vtable[0x34 / 4] = vproc.value;
VirtualProtect(vtable, 0x100, oldPro, &backProc);*/
return b;
}
htdMfcDll.cpp文件的修改:修改了 InitInstance函数
// htdMfcDll.cpp: 定义 DLL 的初始化例程。这是一个MFC的dll
//
#include "pch.h"
#include "framework.h"
#include "htdMfcDll.h"
#include "extern_all.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 注释掉下方宏,以入口点注入的方式注入
// #define WNDHOOK
#ifdef WNDHOOK
typedef struct htdDll
{
HHOOK keyHook;
unsigned KbdProc;
unsigned SetDll;
}*PHtdDll;
void htdSetDll(htdDll hDll);
htdDll mDll;
#endif
BEGIN_MESSAGE_MAP(ChtdMfcDllApp, CWinApp)
END_MESSAGE_MAP()
// ChtdMfcDllApp 构造
ChtdMfcDllApp::ChtdMfcDllApp()
{
}
ChtdMfcDllApp theApp;
ChtdMfcDllApp* PtheApp;
HHOOK keyHook;
LRESULT CALLBACK KeyCallBack(int nCode, WPARAM w, LPARAM l);
BOOL ChtdMfcDllApp::InitInstance()
{
CWinApp::InitInstance();
PtheApp = this;
#ifdef WNDHOOK
mDll.KbdProc = (DWORD)(KeyCallBack);
mDll.SetDll = 0;
#else
keyHook = SetWindowsHook(WH_KEYBOARD, KeyCallBack);// 代码一开始,在这注册一个键盘事件的钩子
#endif
PGameProc = new GameProc();
return TRUE;
}
// 钩子处理事件
LRESULT CALLBACK KeyCallBack(int nCode, WPARAM w, LPARAM l)
{
if (nCode == 0)
{
if ((l & (1 << 31)) == 0)
{
switch (w)
{
case VK_HOME:
if (PtheApp->wndMain == NULL)
{
PtheApp->wndMain = new CUI();
PtheApp->wndMain->Create(IDD_MAIN);
}
PtheApp->wndMain->ShowWindow(TRUE);
break;
}
}
}
return CallNextHookEx(keyHook, nCode, w, l);
}
#ifdef WNDHOOK
void htdInit(htdDll* hDll)
{
hDll->KbdProc = mDll.KbdProc;
hDll->keyHook = mDll.keyHook;
hDll->SetDll = mDll.SetDll;
}
void htdSetDll(htdDll hDll)
{
mDll = hDll;
}
#else
void htdInit()
{
}
#endif
新加extern_all.h文件:
/*
此文件是用来存放全局变量、全局函数(通用函数)
*/
#pragma once
#include "GameWinSock.h"
#include "GameProc.h"
extern GameWinSock* WinSock;
extern GameProc* PGameProc;
extern void InitClassProc(LPVOID proc_addr, unsigned value);
新加extern_all.cpp文件
/*
此文件是用来存放全局变量、全局函数(通用函数)
*/
#include "pch.h"
#include "extern_all.h"
GameWinSock* WinSock = nullptr;
GameProc* PGameProc = nullptr;
void InitClassProc(LPVOID proc_addr, unsigned value)
{
unsigned* writer = (unsigned*)proc_addr;
writer[0] = value;
}