在 Visual Studio C++ 中,内存分配表示形式是啥?

Posted

技术标签:

【中文标题】在 Visual Studio C++ 中,内存分配表示形式是啥?【英文标题】:In Visual Studio C++, what are the memory allocation representations?在 Visual Studio C++ 中,内存分配表示形式是什么? 【发布时间】:2010-09-12 17:54:36 【问题描述】:

在 Visual Studio 中,我们都有过“baadf00d”,在运行时在 C++ 中的调试器中检查变量时看到过“CC”和“CD”。

据我了解,“CC”处于调试模式仅用于指示内存何时被 new() 或 alloc() 和未初始化。而“CD”代表删除或释放的内存。我只在 RELEASE 版本中看到过“baadf00d”(但我可能错了)。

有时,我们会遇到处理内存泄漏、缓冲区溢出等情况,这些信息会派上用场。

是否有人愿意指出何时以及以何种模式将内存设置为可识别的字节模式以进行调试?

【问题讨论】:

When and why will an OS initialise memory to 0xCD, 0xDD, etc. on malloc/free/new/delete? @Lưu Vĩnh Phúc :这不是操作系统,而是调试器。 “D”(在 0xCD 和 0xDD 上)用于调试(即 malloc_dbg 是通过 malloc 调用的,如 msdn.microsoft.com/en-us/library/aa270812(v=vs.60).aspx 中所述)。我相信它还会在堆周围添加栅栏/帖子以跟踪缓冲区溢出。当您遇到双重删除或多次释放错误(甚至可能调用删除而不是删除[])和已释放的悬空指针并且当您检查数据时,它是“0xDD”时捕获问题非常有用(或者当未初始化的堆显示 0xCD 时) 我没有说它是操作系统。是另一个提问者写错了标题 When and why will an OS initialise memory to 0xCD, 0xDD, etc. on malloc/free/new/delete?的可能重复 【参考方案1】:

此链接有更多信息:

https://en.wikipedia.org/wiki/Magic_number_(programming)#Debug_values

* 0xABABABAB : Microsoft 的 HeapAlloc() 用于在分配堆内存后标记“无人区”保护字节 * 0xABADCAFE : 启动此值以初始化所有空闲内存以捕获错误指针 * 0xBAADF00D : 被微软的 LocalAlloc(LMEM_FIXED) 用来标记未初始化的分配堆内存 * 0xBADCAB1E : 与调试器的连接断开时返回到 Microsoft eVC 调试器的错误代码 * 0xBEEFCACE:被 Microsoft .NET 用作资源文件中的幻数 * 0xCCCCCCCC : 被微软的 C++ 调试运行时库用来标记未初始化的堆栈内存 * 0xCDCDCDCD : 被微软的 C++ 调试运行时库用来标记未初始化的堆内存 * 0xDDDDDDDD : 被微软的 C++ 调试堆用来标记释放的堆内存 * 0xDEADDEAD:用户手动启动崩溃时使用的 Microsoft Windows STOP 错误代码。 * 0xFDFDFDFD :被微软的 C++ 调试堆用来标记分配堆内存前后的“无人区”保护字节 * 0xFEEEFEEE:被微软的 HeapFree() 用来标记释放的堆内存

【讨论】:

在这里,我看到了BAADF00D(糟糕的食物)、BEEFCACE(牛肉蛋糕)、BAADCAB1E(糟糕的电缆)、BADCAFE(糟糕的咖啡馆)和DEADDEAD(死了死的)。这是故意的吗? @AndersonGreen 当然是故意的。它被称为hexspeak。 我们过去在做一些低级(操作系统内核)编程时使用 C0CAC01A...;) 0xDEADBEEF,0xC0EDBABE也是经典,即使它们不属于MS的白话 作为保罗麦卡特尼的粉丝,我很喜欢BEA71E5【参考方案2】:

实际上在调试分配中添加了很多有用的信息。这张表比较完整:

http://www.nobugs.org/developer/win32/debug_crt_heap.html#table

HeapAlloc() 之后的地址偏移量 malloc() 之后 free() HeapFree() 之后的地址 0x00320FD8 -40 0x01090009 0x01090009 0x01090009 0x0109005A Win32堆信息 0x00320FDC -36 0x01090009 0x00180700 0x01090009 0x00180400 Win32堆信息 0x00320FE0 -32 0xBAADF00D 0x00320798 0xDDDDDDDD 0x00320448 Ptr 到下一个 CRT 堆块(分配时间较早) 0x00320FE4 -28 0xBAADF00D 0x00000000 0xDDDDDDDD 0x00320448 Ptr 到上一个 CRT 堆块(稍后分配) 0x00320FE8 -24 0xBAADF00D 0x00000000 0xDDDDDDDD 0xFEEEFEEE malloc() 调用的文件名 0x00320FEC -20 0xBAADF00D 0x00000000 0xDDDDDDDD 0xFEEEFEEE malloc() 调用的行号 0x00320FF0 -16 0xBAADF00D 0x00000008 0xDDDDDDDD 0xFEEEFEEE 要 malloc() 的字节数 0x00320FF4 -12 0xBAADF00D 0x00000001 0xDDDDDDDD 0xFEEEFEEE 类型(0=释放,1=正常,2=CRT使用等) 0x00320FF8 -8 0xBAADF00D 0x00000031 0xDDDDDDDD 0xFEEEFEEE 请求编号,从0增加 0x00320FFC -4 0xBAADF00D 0xFDFDFDFD 0xDDDDDDDD 0xFEEEFEEE 无人区 0x00321000 +0 0xBAADF00D 0xCDCDCDCD 0xDDDDDDDD 0xFEEEFEEE 你想要的 8 个字节 0x00321004 +4 0xBAADF00D 0xCDCDCDCD 0xDDDDDDDD 0xFEEEFEEE 你想要的 8 个字节 0x00321008 +8 0xBAADF00D 0xFDFDFDFD 0xDDDDDDDD 0xFEEEFEEE 无人区 0x0032100C +12 0xBAADF00D 0xBAADF00D 0xDDDDDDDD 0xFEEEFEEE Win32 堆分配四舍五入到 16 字节 0x00321010 +16 0xABABABAB 0xABABABAB 0xABABABAB 0xFEEEFEEE Win32堆簿记 0x00321014 +20 0xABABABAB 0xABABABAB 0xABABABAB 0xFEEEFEEE Win32堆记账 0x00321018 +24 0x00000010 0x00000010 0x00000010 0xFEEEFEEE Win32堆簿记 0x0032101C +28 0x00000000 0x00000000 0x00000000 0xFEEEFEEE Win32堆簿记 0x00321020 +32 0x00090051 0x00090051 0x00090051 0xFEEEFEEE Win32堆簿记 0x00321024 +36 0xFEEE0400 0xFEEE0400 0xFEEE0400 0xFEEEFEEE Win32堆簿记 0x00321028 +40 0x00320400 0x00320400 0x00320400 0xFEEEFEEE Win32堆簿记 0x0032102C +44 0x00320400 0x00320400 0x00320400 0xFEEEFEEE Win32堆簿记

【讨论】:

【参考方案3】:

特别是关于 0xCC0xCD,这些是 1980 年代 Intel 8088/8086 处理器指令集的遗留物。 0xCC 是 software interrupt 操作码 INT 0xCD 的特例。特殊的单字节版本0xCC 允许程序生成interrupt 3

虽然原则上软件中断号是任意的,但INT 3 传统上用于调试器中断 或breakpoint 函数,这一惯例一直沿用至今。每当启动调试器时,它都会为INT 3 安装一个中断处理程序,以便在执行该操作码时触发调试器。通常它会暂停当前正在运行的程序并显示交互式提示。

通常,x86 INT 操作码是两个字节:0xCD 后跟从 0 到 255 的所需中断号。现在虽然您可以为INT 3 发出0xCD 0x03,但英特尔决定添加一个特殊版本——0xCC,没有额外的字节——因为操作码必须只有一个字节才能用作可靠的“填充字节”用于未使用的内存。

这里的重点是允许优雅恢复如果处理器错误地跳转到不包含任何预期指令的内存中。多字节指令不适合此目的,因为错误跳转可能会落在任何可能的字节偏移处,它必须继续使用正确形成的指令流。

显然,单字节操作码对此很有效,但也可能有一些奇怪的例外:例如,考虑到填充序列0xCDCDCDCD(也在本页中提到),我们可以看到它相当可靠,因为无论如何其中instruction pointer 着陆(也许最后一个填充字节除外),CPU 可以继续执行有效的两字节 x86 指令CD CD,在这种情况下用于生成软件中断 205 (0xCD)。

更奇怪的是,虽然CD CC CD CC 是 100% 可解释的——给出INT 3INT 204——序列CC CD CC CD 不太可靠,如图所示只有 75%,但当作为一个重复序列时通常为 99.99% int 大小的内存填充。

宏汇编器参考,1987

【讨论】:

哇,我没有意识到(连接两者)0xCC 是 INT3。这是有道理的(即不是巧合)。我曾经在有 JMP 的地方注入“NOP + INT3”来检查寄存器,然后它在几次跳转之前(回溯到什么时候)。感谢您的洞察力,谜团解开了! NOP 是干什么用的?使用eb(输入字节)命令输入单个0xCC 字节不够吗? 只是一个习惯,当时有的代码会读取两个字节并尝试将其用作跳转表,或者在某些情况下,当我列出汇编代码时,通过添加NOP,它不会显示作为 '???'或拆卸时的东西(更清晰);总而言之,出于多种原因,在 BRK 之前或之后注入 NOP 已成为一种习惯;哦,在某些情况下,一些应用程序会尝试对地址块进行校验和,所以我会用 INT3 + [some-hex] grin 平衡 JMP $XYZW

以上是关于在 Visual Studio C++ 中,内存分配表示形式是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Visual Studio 2008 C++ 误报内存地址位置

Visual Studio Express 中 C++ 中的内存分配问题

Visual Studio2008 C++结构体成员需要内存对齐吗?

巧用Visual Studio中的数据断点去排查C++内存越界问题

visual C++ 6.0 是先点“Compile”还是“Build”?分不分点击顺序??作用各是啥??

Visual Studio 2010 C++:获取 malloc 分配的内存块大小