千家信息网

FPS游戏反作弊系统设计方法是什么

发表于:2025-01-18 作者:千家信息网编辑
千家信息网最后更新 2025年01月18日,这篇文章主要讲解了"FPS游戏反作弊系统设计方法是什么",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"FPS游戏反作弊系统设计方法是什么"吧!游戏外挂常
千家信息网最后更新 2025年01月18日FPS游戏反作弊系统设计方法是什么

这篇文章主要讲解了"FPS游戏反作弊系统设计方法是什么",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"FPS游戏反作弊系统设计方法是什么"吧!

游戏外挂常见注入方式

目前大部分游戏外挂不再是以前那种createremotethread + loadlibary注入方式了,因为大部分反作弊有自己的minifilter文件过滤驱动与imageloadcallback镜像加载回调做判断,大部分反作弊软件在这种过滤钩子中做这种操作:

if(!CheckFileCertificateByR3(FilePatch)){  //把文件路径传回r3,r3判断文件数字签名是否在白名单数字签名里面(比如微软数字签名),如果是白名单文件,就放行,如果不是白名单文件,就拦截  //不是白名单文件...拦截  block;}//放行pass;

所以,外挂是特别难通过dll直接注入到游戏里面.因此大部分外挂通过一种 无文件落地注入方式 所谓无文件落地注入方式,就是直接在游戏进程里面开辟一个内存空间,把外挂的dll的shellcode写入,之后手动修复输入表,然后解析pe文件头拿到dllmain,再通过createremotethread,apc或者hook方式让游戏执行这块内存地址,这样子外挂就注入了

具体代码如下(抄自google):

//以下代码来自与谷歌搜索void InjectorDLLByManualMap(const char* filepath, HANDLE hProcess){    LPVOID lpBuffer;    HANDLE hFile;    DWORD dwLength;    DWORD dwBytesRead;    DWORD dwThreadId;    ULONG_PTR lpReflectiveLoader;    LPVOID lpRemoteDllBuffer;    //打开文件    hFile = CreateFileA(filepath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);    //得到文件大小    dwLength = GetFileSize(hFile, NULL);    lpBuffer = HeapAlloc(GetProcessHeap(), 0, dwLength);    //读入文件    ReadFile(hFile, lpBuffer, dwLength, &dwBytesRead, NULL);    //修复导入表    dwReflectiveLoaderOffset = GetReflectiveLoaderOffset(lpBuffer);    //给游戏进程分配一段内存空间    lpRemoteDllBuffer = VirtualAllocEx(hProcess, NULL, dwLength, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);    //写入文件shellcode到分配的内存空间    WriteProcessMemory(hProcess, lpRemoteDllBuffer, lpBuffer, dwLength, NULL)    lpReflectiveLoader = (ULONG_PTR)lpRemoteDllBuffer + dwReflectiveLoaderOffset;    //启动进程    CreateRemoteThread(hProcess, NULL, 1024*1024, (LPTHREAD_START_ROUTINE)lpReflectiveLoader, NULL, NULL, &dwThreadId)}

其特点是:内存标志为PAGE_EXECUTE_READWRITE,MEM_PRIVATE,无文件,无模块,不会触发minifilter和imageloadcallbacks,无法通过正常方式枚举到外挂模块,隐蔽性非常高.

检测内存加载外挂

之前的方法看起来非常的"无敌"实际上也是可以对抗的,因为其特征也非常明显:

内存属性为MEM_PRIVATE,内存标志为PAGE_EXECUTE_READWRITE.大小会很大.

所以检测方法也有几个:

1.暴力搜索PE头,大部分这种内存加载的dll都有pe头.一个内存属性为mem_private居然还有pe头,就说明是外挂了.目前大部分反作弊都有这个机制

外挂反制: 抹掉pe头.不止pe头,还可以抹掉一切pe特征.

2.createthreadcallbacks得到线程地址,判断线程地址是否在一个内存属性的mem_private的内存里面.如果是,说明就是外挂了.

外挂反制:不创建线程,使用hook方启动外挂.

3.api调用回溯.顾名思义,外挂总要调用一些api地址的,我们可以通过回溯是谁调用了api地址,然后判断这个调用地方内存属性是不是mem_private.有两种方法,一个是hook所有关键api,在hook部位用_returnaddres()得到调用地址(其实是读ESP/RSP寄存器)第二种通过int3断点触发异常,使用异常处理函数处理这个异常,判断调用者.

外挂反制: 第一种内联hook方式,直接写跳转跳过hook,比如你hook的时候:

jmp 你的hook地址

push ebp

push eax

call xxxx;

外挂可以直接从push ebp调用,不再调用你jmp ,就可以绕过

第二种外挂反制目前没有特别的能反制的地方.除非外挂自己构造api函数调用更底层的api.当然我们可以混淆原底层api的地址(无限套娃),具体以后在说.

实现调用回溯

为了实现调用回溯,我们需要实现如下步骤:

1. 设置异常处理程序去捕获异常,代码如下:

AddVectoredExceptionHandler

2. 拷贝原API地址到自己的内存区域,然后填充原API地址为int,代码如下:

     LPVOID pHOOKAdress;        pHOOKAdress = Megrez_GetProAdress(pszModuleName, pszProcName);        vecInt3HookedAdress.push_back((DWORD)pHOOKAdress);              //用于检测        if (pHOOKAdress == 0)        {                return 0;        }        DWORD dProSize = 0;        LPBYTE pTemp = (LPBYTE)pHOOKAdress;        BYTE bTemp = 0;        for (dProSize = 0; ; )        {                bTemp = *pTemp++;                dProSize++;                if (bTemp == 0xcc)                {                        break;                }        }        DWORD dFileSize = dProSize - 1;        PVOID pNewAddr = VirtualAlloc(NULL, dFileSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);        if (pNewAddr == NULL)        {                return 0;        }        Megrez_SetMemoryAttr(pHOOKAdress, dProSize);        memcpy(pNewAddr, pHOOKAdress, dProSize - 1);        memset(pHOOKAdress, 0xcc, 1);        memset((PBYTE)pHOOKAdress + 1, 0xc3, 1);        memset((PBYTE)pHOOKAdress + 2, 0x90, dProSize - 1  -2);        memset((PBYTE)pHOOKAdress + 2 + dProSize - 1 - 2 - 1, 0xcc, 1);        //memset((PBYTE)pHOOKAdress + 2 + dProSize - 3 - 2 , 0xcc, 2);        mapAdress.insert(pair((DWORD)pHOOKAdress, (DWORD)pNewAddr));        Megrez_SetMemoryAttr(pHOOKAdress, dProSize);        Megrez_SetMemoryAttr(pNewAddr, dFileSize);

这样子原api函数就会变成int3 当调用时候就回触发int3异常 然后被我们的异常处理捕获

3. 查询异常位置内存信息,如果是meme_private者调用的代码,则报告给服务端,代码如下(记住,x32位下保存调用者地址的是esp,x64位下保存调用者地址的是rsp,):

        size_t sizeQuery = VirtualQuery((PVOID)caller_function, lpBuffer, sizeof(MEMORY_BASIC_INFORMATION));        bool non_commit = lpBuffer->State != MEM_COMMIT;        bool foreign_image = lpBuffer->Type != MEM_IMAGE && lpBuffer->RegionSize > 0x2000;        bool spoof = *(PWORD)caller_function == 0x23FF; // jmp qword ptr [rbx],这是为了防止被欺骗        return sizeQuery || non_commit || foreign_image || spoof; //返回

处理完异常后,我们要跳到原来的保存的api内存里面正常调用(设置eip保存的内存地址)

 ExceptionInfo->ContextRecord->Eip = mapAdress[(DWORD)ExceptionInfo->ExceptionRecord->ExceptionAddress];#ifdef DEBUG        WCHAR _buf[256] = { 0 };        swprintf_s(_buf, 256, L"eIP:0xX\n", ExceptionInfo->ContextRecord->Eip);        OutputDebugStringW(_buf);#endif        //已经处理了异常要再调用下一个异常处理来处理此异常        return EXCEPTION_CONTINUE_EXECUTION;    }    //调用下一个处理器    return EXCEPTION_CONTINUE_SEARCH;

可以看到,这样子就得到了api调用者的信息,从而做出判断.

这样,一个能检测出绝大部分内存加载外挂的东西就做好了(谁调用谁就会被检测)

感谢各位的阅读,以上就是"FPS游戏反作弊系统设计方法是什么"的内容了,经过本文的学习后,相信大家对FPS游戏反作弊系统设计方法是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

外挂 内存 文件 地址 处理 方法 方式 代码 大部分 检测 系统 设计 名单 属性 用者 函数 就是 数字 数字签名 样子 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 学了软件开发能做软件测试吗 生物学数据库的数据存储格式 小程序开发个性化软件开发支持 万得数据库怎么查询价格变动 网络技术答案两面性 wapm访问不了数据库 邵阳软件开发培训去哪里 我的世界斗罗大陆服务器有光影吗 国家开源软件开发与应用操作能力 六安医院软件开发需要多少钱 双软企业软件开发人员个税 软件开发资质 信产部 db数据库修改器 网络安全责任制实施办法大学生 广东警官学院网络安全与执法代码 网络安全知识竞赛登录平台 云数据库保障安全性 无锡进口服务器生产商 深圳富瑞盈网络技术 注册辽事通提示服务器异常 数据库表格设置字母排序 手机软件开发工程师专业技能 网络安全防护功能宽度分配 软件开发过程步骤内容 数据库安全态势感知 给政府做软件开发 网络安全关于产品异构介绍 服务器高海拔 联想服务器来电自启设置 电商erp软件开发服务价格
0