|
一个编写传奇封外挂(反外挂)系统的完成过程 - API检测篇
游戏外挂的编写离不开对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;
- }
复制代码 总结
本文是介绍HOOK API的常见方法,以及通过校验API函数是否被非法HOOK 来判断玩家是否使用了外挂的方法。具体编写实现方法可自行设计,尽量让设计隐晦一些不那么容易被发现和破解,程序设计时尽量不要有API参考和字符串产考、尽量不要使用静态全局变量保存重要数据,指针层数越深越好。不要通过本地检测之后直接得出结论并弹出提示。
|
|