需用virtualqueryex遍历mem_commit且可读内存区,跳过mem_free/reserve,以baseaddress+size头写入二进制文件,避开aslr偏移、堆栈无效页及文本模式写入问题。

用 WriteProcessMemory + CreateFile 直写内存到文件,但别直接调
Windows 下没法用一句 C++ 代码把整个进程内存“dump”出来——WriteProcessMemory 只能读其他进程(且需权限),而你自己的进程要 dump 自身,得靠 VirtualQueryEx 遍历内存区域 + ReadProcessMemory(对自己 PID 调用也合法)+ CreateFile/WriteFile 直写二进制。关键不是“能不能”,而是“哪些区域值得写、哪些会失败、哪些写了也没用”。
常见错误现象:ReadProcessMemory 返回 FALSE,GetLastError() 是 ERROR_PARTIAL_COPY 或 ERROR_ACCESS_DENIED;生成的文件里大片 0x00 或乱码;加载回调试器时找不到模块基址。
- 必须跳过
MEM_FREE和MEM_RESERVE区域——它们没物理页,读必然失败 -
MEM_COMMIT且PAGE_READABLE(如PAGE_READONLY、PAGE_READWRITE、PAGE_EXECUTE_READ等)才真正可读 - 绕开 ASLR 基址偏移:dump 文件里要记录每个区块的
BaseAddress和RegionSize,否则还原时无法映射 - 别读
Heap或Stack的未使用部分——VirtualQueryEx会告诉你State == MEM_COMMIT,但实际内容可能是野指针或已释放内存,写进去反而干扰分析
手动遍历内存区用 VirtualQueryEx,不是 EnumProcessModules
EnumProcessModules 只给你模块(DLL/EXE)加载基址和大小,但 dump 需要所有可读内存页,包括堆、线程栈、内存映射文件、私有数据段等。必须用 VirtualQueryEx 从 0x10000 开始逐块查(Windows 用户空间从 0x10000 起,避开 NULL 页),直到返回 0。
使用场景:你想做轻量级 crash 后现场保存,不依赖 dbghelp 或 minidump 机制,也不引入 pdb 依赖。
- 起始地址别用
NULL——VirtualQueryEx对0x0返回无效信息,从0x10000安全 - 每次调用后,把
MEMORY_BASIC_INFORMATION::BaseAddress+RegionSize当作下一轮lpAddress,别自增字节——页边界不对齐会导致重复或遗漏 -
AllocationBase字段可用来合并连续同属性区块(比如多个MEM_COMMIT+PAGE_READWRITE的堆页),减少ReadProcessMemory调用次数
ReadProcessMemory 失败时别重试,先检查 Protect 和 State
90% 的读失败不是权限问题,而是区域状态不对:State != MEM_COMMIT 或 Protect & PAGE_NOACCESS。直接重试或提权毫无意义。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
性能影响:对每个 MEM_COMMIT 区域都调一次 ReadProcessMemory 开销不小,尤其大堆;但跳过检测又会漏数据。折中做法是只读 Protect & (PAGE_READONLY | PAGE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE) 的子集。
-
PAGE_GUARD和PAGE_NOCACHE位要屏蔽掉再判断可读性——它们修饰行为,不否定可读 - 遇到
ERROR_PARTIAL_COPY,说明目标页部分被回收或跨页保护不一致,应放弃整块,记日志,继续下一块 - 不要用
SuspendThread全局暂停——dump 过程本身可能被中断,且挂起线程栈可能导致死锁(比如正在 malloc)
dump 文件格式越简单越好,别学 minidump 套嵌结构
如果你只是想用 WinDbg / x64dbg 加载看内存,不需要符号、线程上下文、异常记录——那就用纯二进制拼接:每块前加 16 字节头(BaseAddress uint64 + Size uint64),后面紧跟原始内存字节。总大小可控,解析脚本三行搞定。
容易踩的坑:用 std::ofstream 默认文本模式写二进制,Windows 下会把 0x0A 换成 0x0D 0x0A;或者用 fwrite 但没设 FILE_FLAG_NO_BUFFERING,导致 SSD 上小块写放大。
- 务必用
CreateFile(..., GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL)——绕过系统缓存,避免 dump 中断时文件损坏 - 每块写入前,用
SetFilePointerEx定位,别依赖顺序写——某些区域BaseAddress可能小于前一块,顺序写会错位 - 最后写一个 EOF 标记(比如 8 字节
0xFFFFFFFFFFFFFFFF),方便 loader 判断结束,而不是靠文件尺寸硬算
真正麻烦的是还原:dump 里没有重定位信息,也没有 PE 头,WinDbg .loadby 会失效;你得手动 .addmap 每个模块基址——这点常被忽略,以为写完就完事了。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!








