c++ 游戏反作弊 c++如何检测内存修改和dll注入

千静君_8135

千静君_8135

2026-03-23

859人浏览

原创

virtualqueryex 可识别三类危险内存页:①mem_commit+page_execute_readwrite;②mem_commit+page_readwrite且不在已知模块内;③mem_reserve后被virtualprotectex改权限;需结合enumprocessmodules白名单与区间重叠判断规避win10+ allocationbase模糊化。

c++ 游戏反作弊 c++如何检测内存修改和dll注入

怎么用 VirtualQueryEx 发现可疑内存页

游戏运行时,外挂常把代码写进进程内存(比如 MEM_COMMIT | MEM_WRITE 的可写页),或把 DLL 注入后改成可执行(PAGE_EXECUTE_READWRITE)。直接扫全内存太慢,但你可以只查那些「不该可写的可执行页」或「刚分配出来的写页」。

关键不是查所有页,而是盯住三类危险组合:MEM_COMMIT + PAGE_EXECUTE_READWRITE、MEM_COMMIT + PAGE_READWRITE 且基址不在已知模块内、MEM_RESERVE 后立刻 VirtualProtectEx 改权限。

  • 调用前先用 EnumProcessModules 拿到所有已加载模块的地址范围,存成 std::vector<:pair size_t>></:pair>
  • 遍历内存时用 VirtualQueryEx 分块查,每次传入上次返回的 lpInformation->BaseAddress + lpInformation->RegionSize 继续往下走
  • 别在主线程里跑全量扫描——卡顿明显,建议每帧只查 2–3MB,用 std::this_thread::sleep_for(1ms) 避免占满 CPU
  • 注意:Windows 10 1809+ 对 VirtualQueryEx 返回的 AllocationBase 做了随机化模糊,不能直接比对,得结合 BaseAddress 和大小做区间重叠判断

怎么识别非白名单 DLL 的注入行为

DLL 注入不等于恶意,但游戏里绝大多数非白名单 DLL(比如 GameOverlayRenderer.dll 或 discord-rpc.dll)都是外挂入口。重点不是“有没有新 DLL”,而是“谁在没走正常 LoadLibrary 路径的情况下把它塞进来”。

系统级 API 如 LoadLibraryExA、LoadLibraryW 会触发模块加载回调(LdrRegisterDllNotification),但注入者常用 WriteProcessMemory + CreateRemoteThread 绕过——这类操作不会触发回调,但会在目标进程里留下痕迹。

  • 启用 LdrRegisterDllNotification 监听所有模块加载,记录 FullDllName 和 LoadedBaseAddress,和预置白名单比对(白名单必须含完整路径,不能只比文件名)
  • 同时轮询 EnumProcessModules,发现新增模块但没收到对应通知 → 极大概率是远程线程注入
  • 检查新增模块的 IMAGE_DOS_HEADER 和 IMAGE_NT_HEADERS 是否完整(注入者有时只拷贝 PE 头+部分节,导致 SizeOfImage 异常小或节表校验失败)
  • 注意:Steam Overlay、NVIDIA Freestyle 等合法组件也会动态注入,它们通常带签名且路径固定,别一刀切封禁 nvngx.dll 或 gameoverlayrenderer64.dll

SetWindowsHookEx 和 WH_KEYBOARD_LL 是不是反作弊该拦的

低级钩子本身不危险,危险的是它被用来拦截输入、篡改按键事件流,或者作为注入跳板(通过 hMod 参数把 DLL 强制载入目标进程)。但直接禁掉所有 WH_KEYBOARD_LL 会导致无障碍软件、录屏工具、键盘宏全部失效,玩家投诉爆炸。

C++14
C++14

C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。

下载

真正要盯的是钩子的宿主模块是否异常:比如一个叫 helper.dll 的无签名 DLL,通过 SetWindowsHookEx(WH_KEYBOARD_LL, ...) 注册后,又没出现在 EnumProcessModules 列表里——说明它被手动映射(manual map)进了进程,这是典型外挂手法。

  • 用 GetWinEventHookInfo(需 Windows 10 2004+)或遍历 HHOOK 句柄对应的线程上下文,反查其所属模块基址
  • 对每个注册了 WH_KEYBOARD_LL 或 WH_MOUSE_LL 的钩子,检查其回调函数所在的模块是否在白名单中,且是否通过正常 LoadLibrary 加载
  • 如果钩子来自 ntdll.dll 或 kernel32.dll 内存区域(即回调地址落在这些模块的 .text 段内),基本可判定为 inline hook 或代码洞,立即告警
  • 注意:SetWindowsHookEx 返回的 HHOOK 是全局句柄,不能仅靠 GetModuleHandle 查——得用 GetMappedFileName + VirtualQueryEx 定位真实模块路径

为什么自己实现 ReadProcessMemory 校验容易误杀

很多新手想每秒读一次关键内存地址(比如血量、坐标),对比上一帧值是否被改写。这思路没错,但直接裸调 ReadProcessMemory 会撞上三个硬坑:权限问题、时机问题、混淆问题。

外挂早就不硬改数值了,它改的是计算过程——比如把 player.hp = 100 换成 player.hp = (int)(100 * GetObfuscatedFactor()),你读到的还是 100,但实际逻辑已被劫持;更糟的是,有些内存地址本身受硬件断点保护,连续读多次会触发 STATUS_GUARD_PAGE_VIOLATION 导致游戏崩溃。

  • 不要只读单个变量,读整个结构体(比如 PlayerState 类的 sizeof),再用 CRC32 校验字段布局一致性
  • 避免高频轮询:用 WaitForDebugEvent 或 SetThreadContext + 单步异常捕获代替轮询,只在关键函数入口/出口打点
  • 对指针字段做二级校验:比如 player->weapon 是个指针,就顺便读它指向的 vtable 前 8 字节,看是否匹配已知合法类的虚表地址
  • 注意:某些驱动级外挂(如 EvilLyrics、Cheat Engine 的 Kernel Mode Driver)能拦截 ReadProcessMemory 并返回伪造数据,此时必须配合用户态内存扫描 + 内核态 CR3 切换检测交叉验证

最麻烦的永远不是“怎么检测”,而是“怎么区分调试器附加、合法工具注入、和真外挂”——这三个行为在 API 层几乎一模一样,最终得靠行为时序(比如 OpenProcess 后立刻 VirtualAllocEx)、模块签名链、以及是否绕过 PatchGuard 的间接调用痕迹来交叉判断。漏掉任意一环,要么放行太多,要么封禁太多。

C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!

相关文章

在线游戏
在线游戏

海量精品小游戏合集,无需安装即点即玩,休闲益智、动作闯关应有尽有,秒开即玩,轻松解压,快乐停不下来

下载

相关标签:

c++

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

2025.06.09

4454

18

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

2025.07.04

4611

25

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.02

5959

3

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.08.29

2925

6

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

2025.08.29

3728

10

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

2025.08.29

2625

10

C++ 智能指针与现代内存管理
C++ 智能指针与现代内存管理

深入讲解 C++ 现代内存管理的核心工具——智能指针,涵盖 unique_ptr 独占所有权语义、shared_ptr 引用计数机制与循环引用问题、weak_ptr 弱引用的应用场景、make_unique/make_shared 工厂函数的性能优势、自定义删除器的编写、RAII 资源管理思想的实践,以及从裸指针迁移到智能指针的重构策略,帮助开发者编写安全无泄漏的现代 C++ 代码。

2026.04.23

339

31

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

2023.08.10

3958

6

golang map内存释放
golang map内存释放

本专题整合了golang map内存相关教程,阅读专题下面的文章了解更多相关内容。

2025.09.05

530

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C++ Core Guidelines
C++ Core Guidelines

共0课时 | 0人学习

C++ Reference
C++ Reference

共0课时 | 0人学习

C++ 官方标准说明
C++ 官方标准说明

共0课时 | 0人学习