在Visual Studio中,我们都有过“baadf00d”,在运行时检查c++调试器中的变量时看到过“CC”和“CD”。
根据我的理解,“CC”是在调试模式下,仅表示当内存已被new()或alloc()和单元化。而“CD”表示已删除或已释放的内存。我只在RELEASE版本中看到过“baadf00d”(但我可能错了)。
有时,我们会遇到解决内存泄漏、缓冲区溢出等问题的情况,这些信息就会派上用场。
有没有人能好心地指出,为了调试目的,内存在什么时候以及在什么模式下被设置为可识别的字节模式?
特别是0xCC和0xCD,它们是20世纪80年代Intel 8088/8086处理器指令的遗留物。0xCC是软件中断操作码INT 0xCD的特殊情况。特殊的单字节版本0xCC允许程序生成中断3。
虽然软件中断号原则上是任意的,但INT 3传统上用于调试器中断或断点函数,这一惯例一直延续到今天。每当启动调试器时,它都会为INT 3安装一个中断处理程序,以便在执行该操作码时触发调试器。通常它会暂停当前正在运行的程序,并显示一个交互式提示符。
通常,x86 INT操作码是两个字节:0xCD,后面跟着从0到255的所需中断号。现在,虽然您可以为INT 3发出0xCD 0x03,但英特尔决定添加一个特殊版本——不带额外字节的0xCC——因为操作码必须只有一个字节,以便作为未使用内存的可靠“填充字节”。
这里的重点是允许在处理器错误地跳转到不包含任何预期指令的内存时进行优雅恢复。多字节指令不适合这一目的,因为错误的跳转可能落在任何可能的字节偏移处,在那里它将不得不继续使用正确形成的指令流。
显然,单字节操作码在这方面的工作很简单,但也可能有奇怪的例外:例如,考虑填充序列0xCDCDCDCD(也在本页提到),我们可以看到它是相当可靠的,因为无论指令指针停在哪里(可能除了最后一个填充字节),CPU都可以继续执行有效的双字节x86指令CD,在这种情况下生成软件中断205 (0xCD)。
更奇怪的是,CD CC CD CC是100%可解释的——给出INT 3或INT 204——序列CC CD CC CD不太可靠,如图所示只有75%,但作为INT大小的内存填充符重复使用时,通常是99.99%。
宏汇编参考,1987年
实际上,在调试分配中添加了相当多有用的信息。下表较为完整:
http://www.nobugs.org/developer/win32/debug_crt_heap.html#table
Address Offset After HeapAlloc() After malloc() During free() After HeapFree() Comments
0x00320FD8 -40 0x01090009 0x01090009 0x01090009 0x0109005A Win32 heap info
0x00320FDC -36 0x01090009 0x00180700 0x01090009 0x00180400 Win32 heap info
0x00320FE0 -32 0xBAADF00D 0x00320798 0xDDDDDDDD 0x00320448 Ptr to next CRT heap block (allocated earlier in time)
0x00320FE4 -28 0xBAADF00D 0x00000000 0xDDDDDDDD 0x00320448 Ptr to prev CRT heap block (allocated later in time)
0x00320FE8 -24 0xBAADF00D 0x00000000 0xDDDDDDDD 0xFEEEFEEE Filename of malloc() call
0x00320FEC -20 0xBAADF00D 0x00000000 0xDDDDDDDD 0xFEEEFEEE Line number of malloc() call
0x00320FF0 -16 0xBAADF00D 0x00000008 0xDDDDDDDD 0xFEEEFEEE Number of bytes to malloc()
0x00320FF4 -12 0xBAADF00D 0x00000001 0xDDDDDDDD 0xFEEEFEEE Type (0=Freed, 1=Normal, 2=CRT use, etc)
0x00320FF8 -8 0xBAADF00D 0x00000031 0xDDDDDDDD 0xFEEEFEEE Request #, increases from 0
0x00320FFC -4 0xBAADF00D 0xFDFDFDFD 0xDDDDDDDD 0xFEEEFEEE No mans land
0x00321000 +0 0xBAADF00D 0xCDCDCDCD 0xDDDDDDDD 0xFEEEFEEE The 8 bytes you wanted
0x00321004 +4 0xBAADF00D 0xCDCDCDCD 0xDDDDDDDD 0xFEEEFEEE The 8 bytes you wanted
0x00321008 +8 0xBAADF00D 0xFDFDFDFD 0xDDDDDDDD 0xFEEEFEEE No mans land
0x0032100C +12 0xBAADF00D 0xBAADF00D 0xDDDDDDDD 0xFEEEFEEE Win32 heap allocations are rounded up to 16 bytes
0x00321010 +16 0xABABABAB 0xABABABAB 0xABABABAB 0xFEEEFEEE Win32 heap bookkeeping
0x00321014 +20 0xABABABAB 0xABABABAB 0xABABABAB 0xFEEEFEEE Win32 heap bookkeeping
0x00321018 +24 0x00000010 0x00000010 0x00000010 0xFEEEFEEE Win32 heap bookkeeping
0x0032101C +28 0x00000000 0x00000000 0x00000000 0xFEEEFEEE Win32 heap bookkeeping
0x00321020 +32 0x00090051 0x00090051 0x00090051 0xFEEEFEEE Win32 heap bookkeeping
0x00321024 +36 0xFEEE0400 0xFEEE0400 0xFEEE0400 0xFEEEFEEE Win32 heap bookkeeping
0x00321028 +40 0x00320400 0x00320400 0x00320400 0xFEEEFEEE Win32 heap bookkeeping
0x0032102C +44 0x00320400 0x00320400 0x00320400 0xFEEEFEEE Win32 heap bookkeeping
特别是0xCC和0xCD,它们是20世纪80年代Intel 8088/8086处理器指令的遗留物。0xCC是软件中断操作码INT 0xCD的特殊情况。特殊的单字节版本0xCC允许程序生成中断3。
虽然软件中断号原则上是任意的,但INT 3传统上用于调试器中断或断点函数,这一惯例一直延续到今天。每当启动调试器时,它都会为INT 3安装一个中断处理程序,以便在执行该操作码时触发调试器。通常它会暂停当前正在运行的程序,并显示一个交互式提示符。
通常,x86 INT操作码是两个字节:0xCD,后面跟着从0到255的所需中断号。现在,虽然您可以为INT 3发出0xCD 0x03,但英特尔决定添加一个特殊版本——不带额外字节的0xCC——因为操作码必须只有一个字节,以便作为未使用内存的可靠“填充字节”。
这里的重点是允许在处理器错误地跳转到不包含任何预期指令的内存时进行优雅恢复。多字节指令不适合这一目的,因为错误的跳转可能落在任何可能的字节偏移处,在那里它将不得不继续使用正确形成的指令流。
显然,单字节操作码在这方面的工作很简单,但也可能有奇怪的例外:例如,考虑填充序列0xCDCDCDCD(也在本页提到),我们可以看到它是相当可靠的,因为无论指令指针停在哪里(可能除了最后一个填充字节),CPU都可以继续执行有效的双字节x86指令CD,在这种情况下生成软件中断205 (0xCD)。
更奇怪的是,CD CC CD CC是100%可解释的——给出INT 3或INT 204——序列CC CD CC CD不太可靠,如图所示只有75%,但作为INT大小的内存填充符重复使用时,通常是99.99%。
宏汇编参考,1987年