正文
游戏外挂的编写离不开对API的操作调用。通过逆向思维思考,我们要反外挂第一个要保护和监测的就是游戏进程的API函数。比如ws2_32.dll里面的send、recv,WSASend,WSARecv ,connect是必须要监测的,外挂程序可能会通过hook这些函数来达到监听和篡改游戏封包的目的。
hook API函数常用的方法有使用微软提供的detours库或者使用inline hook 来修改。下面我们来了解一下hook api函数的方法。
一、detours库的使用
detours下载 比较简单,直接上演示代码,下面是hook connect函数的演示:
#pragma once
#include "pch.h"
#include "detours.h"
#include <Windows.h>
#include <WinSock2.h>
#pragma comment (lib,"detours.lib")
typedef int (WINAPI* type_connect)(
_In_ SOCKET s,
_In_reads_bytes_(namelen) const struct sockaddr FAR* name,
_In_ int namelen
);
type_connect g_fnconnect_real = ::connect;
HANDLE g_thread_hook = NULL;
void HookApi(LPVOID *trueFunction, LPVOID newFunction, bool install)
{
if (install)
{
g_thread_hook = GetCurrentThread();
//装载Hook
DetourRestoreAfterWith();
DetourTransactionBegin();
DetourUpdateThread(g_thread_hook);
DetourAttach(trueFunction, newFunction);
DetourTransactionCommit();
}
else
{
//卸载
DetourTransactionBegin();
DetourUpdateThread(g_thread_hook);
DetourDetach(trueFunction, newFunction);
DetourTransactionCommit();
}
}
//调用函数 hook connect
HookApi(&(LPVOID&)g_fnconnect_real, (LPVOID)&Hook_connect, true);
//hook 后会,每当执行connect函数时会首先执行转向本函数
int WINAPI Hook_connect(
_In_ SOCKET s,
_In_reads_bytes_(namelen) const struct sockaddr FAR* name,
_In_ int namelen
)
{
//编写你的处理过程
...
return g_fnconnect_real (s,name,namelen;
}
二、inline hook的使用
inline hook 其实就是通过直接修改可行代码,使之转向到执行我们的函数的过程。理论上inline hook 可以在任意位置进行hook转向 ,只要保证好堆栈平衡就行。
inline hook的原理就是修改一条跳转代码(jmp xxxxx) ,机器码为 E9 。比如我们要 hook connect ,代码如下
//1、保存真实connect的地址
type_connect g_fnconnect_real = ::connect;
//2、保存connect 函数开头5字节
char g_first5chars[5];
memcpy(g_first5chars,0,sizeof(g_first5chars));
//修改机器码使之跳转到 Hook_connect
//这里如果代码不可写,需要先调用 VirtualProtect 修改为可写属性
*(BYTE*)g_fnconnect_real = 0xe9 ;
*(DWORD*)((BYTE*)g_fnconnect_real +1) = (DWORD)Hook_connect - (DWORD)g_fnconnect_real - 5;
//hook 后会,每当执行connect函数时会首先执行转向本函数
int WINAPI Hook_connect(
_In_ SOCKET s,
_In_reads_bytes_(namelen) const struct sockaddr FAR* name,
_In_ int namelen
)
{
//编写你的处理过程
...
//还原原始函数头5字节
memcpy(g_fnconnect_real ,g_first5chars,sizeof(g_first5chars));
return connect_real (s,name,namelen;
}
三、监测API是否被hook
我们已经了解外挂作者可能会如何 hook 我们的函数了,总之要hook API 必定会修改到我们的可执行代码。对应的版本有两个:一是不停地覆盖还原原始的代码,让hook失效。二是检测代码是否被修改,如果修改了就告知服务器(注意:一般封挂都不是在客户端直接弹出提示,让破解者轻易找到应对办法),由服务器端处理发送消息、断开连接、记录日志等。
一般可使用crc校验法或者自己编写累加和算法校验。可随意设计,下面是自己编写的校验和例子:
DWORD game_memory::calc_checksum(BYTE* pBuffer, DWORD dwCheckLen, DWORD dwStartSum )
{
DWORD dwSUM = dwStartSum;
WORD* pWord = (WORD*)pBuffer;
while (dwCheckLen >= 2)
{
dwSUM += *pWord;
if (dwSUM >= 0xffffffff - 0xffff * 2)
{
dwSUM = (dwSUM & 0xffff) + (dwSUM >> 16);
}
pWord++;
dwCheckLen -= 2;
}
pBuffer = (BYTE*)pWord;
while (dwCheckLen > 0)
{
dwSUM += *pBuffer;
pBuffer++;
dwCheckLen--;
}
return dwSUM;
}
如果你也有这方面兴趣爱好交流欢迎加扣58085250与我直接沟通交流指正。
总结
本文是介绍HOOK API的常见方法,以及通过校验API函数是否被非法HOOK 来判断玩家是否使用了外挂的方法。具体编写实现方法可自行设计,尽量让设计隐晦一些不那么容易被发现和破解,程序设计时尽量不要有API参考和字符串产考、尽量不要使用静态全局变量保存重要数据,指针层数越深越好。不要通过本地检测之后直接得出结论并弹出提示。