S.l.e!ep.¢%

像打了激速一样,以四倍的速度运转,开心的工作
简单、开放、平等的公司文化;尊重个性、自由与个人价值;
posts - 1098, comments - 335, trackbacks - 0, articles - 1
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

内存崩溃 CASE 3

Posted on 2009-04-06 00:13 S.l.e!ep.¢% 阅读(2035) 评论(4)  编辑 收藏 引用 所属分类: WinDbg

#include <iostream>
using namespace std;

int main()
{
 const char* p = "test";
 char szBuf[53221] = {0};
 
 memcpy(szBuf, p, sizeof(szBuf)); // Debug "0x004082f0" 指令引用的 "0x0043e000" 内存。该内存不能为 "read"
 // Relase 无报错
 
 /*
 0:000> g
 (410.c88): Access violation - code c0000005 (first chance)
 First chance exceptions are reported before any exception handling.
 This exception may be expected and handled.
 eax=0043e001 ebx=7ffd9000 ecx=00000000 edx=00000001 esi=0043e000 edi=0012ff78
 eip=004082f0 esp=00122f2c ebp=00122f34 iopl=0         nv up ei pl nz na po nc
 cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
 *** WARNING: Unable to verify checksum for test.exe
 test!TrailUpVec+0x18:
 004082f0 8a06            mov     al,byte ptr [esi]          ds:0023:0043e000=??
 
   0:000> !address esi
   0043e000 : 0043e000 - 7c3c2000
   Type     00000000
   Protect  00000001 PAGE_NOACCESS
   State    00010000 MEM_FREE
   Usage    RegionUsageFree
  
        很明显访问到不应该访问的内存了
  
    0:000> knL
    # ChildEBP RetAddr 
    00 00122f34 00401084 test!TrailUpVec+0x18
    01 0012ff80 00408749 test!main+0x54
    02 0012ffc0 7c816fd7 test!mainCRTStartup+0xe9
    03 0012fff0 00000000 kernel32!BaseProcessStart+0x23

      首先 bp main
   再 p

      test!main+0x22:
   00401052 c745fc1c104300  mov     dword ptr [ebp-4],offset test!`string' (0043101c)

      0:000> dc 0043101c 
      0043101c  74736574 00000000 00432880 00402dd0  test.....(C..-@.
   0043102c  00000000 00000004 004328e8 00402e10  .........(C...@.
   0043103c  00432938 00402e40 00000000 00000008  8)C.@.@.........
   0043104c  004329a0 00402eb0 00402550 004027b0  .)C...@.P%@..'@.
   0043105c  004020d0 00402980 004029e0 00402180  . @..)@..)@..!@.
   0043106c  004022b0 00402bb0 00402c40 00402d40  ."@..+@.@,@.@-@.
   0043107c  00402d90 00402430 004329d0 00402e80  .-@.0$@..)C...@.
   0043108c  00402080 004020b0 004020d0 004020e0  . @.. @.. @.. @.

      0:000> !address 0043DFFF
      00400000 : 0043c000 - 00002000
                    Type     01000000 MEM_IMAGE
                    Protect  00000002 PAGE_READONLY
                    State    00001000 MEM_COMMIT
                    Usage    RegionUsageImage
                    FullPath test.exe

   0:000> !address 0x0043e000
      0043e000 : 0043e000 - 7c3c2000
                    Type     00000000
                    Protect  00000001 PAGE_NOACCESS
                    State    00010000 MEM_FREE
                    Usage    RegionUsageFree

    计算机内存运行分配的区域分为3个
    程序段区域:不允许写的
    数据段区域:静态全局变量是位于数据段并且在程序开始运行的时候被加载
    堆栈区域:放置程序的动态的用于计算的局部和临时变量则分配在堆栈里面和在过程调用中压入的返回地址数据。堆栈是一个先入后出的队列。一般计算机系统堆栈的方向与内存的方向相反。压栈的操作push=ESP-4,出栈的操作是pop=ESP+4.
 */
 
 return 0;
}

BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。

数据段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。

代码段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)

栈(stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

事实上,当 release 取消优化时,也会报错
 char szBuf[20320] = {0};  改成这样,有可能不会报错

release 版的
"0x004044f0"指令引用的"0x00413000"内存。该内存不能为"read"。

debug 版的
"0x004082f0" 指令引用的 "0x0043e000" 内存。该内存不能为 "read"

相差这么多? 中间一大截是干嘛的?

Feedback

# re: 内存崩溃 CASE 3  回复  更多评论   

2009-04-07 08:58 by flyingzhang
请注意,这里的提示的是不允许读。并非不允许写。请考虑你的const char *p 指向何处:它是指向数据段中的某一位置,而你的代码预期将由此处开始读取sizeof(szBuf)大的一块空间,而这块空间是否都是应用程序被许可读取的,是并不确定的。(

# re: 内存崩溃 CASE 3  回复  更多评论   

2009-04-07 09:53 by guest
有人告诉过博主这些段是连续的吗?

# re: 内存崩溃 CASE 3  回复  更多评论   

2009-04-07 13:26 by freeeyes
以我的理解,你的memcpy用的似乎有些问题吧。
memcpy(szBuf, p, sizeof(szBuf));
这句话,首先,你的szBuf是目的缓存(拷贝的结果),你的p是目标缓存(拷贝的目标),而你给的memcpy长度应该是*p的长度。也就是strlen(P),而你使用的sizeof(szBuf)的长度是53221,也就是超过了你的*p的字符长度,产生越界错误。而越界后的内存地址是不可知的,所以出读错误也就是正常的了(似乎很多微软的溢出漏洞原因就在这里)。
而且,建议你在memcpy前做几个操作。
int nSize = strlen(P);
if(nSize > sizeof(szBuf))
{
return;
}
防止内存越界。
如果你用的是2005以后的版本,且你的代码在Win下,推荐使用memcpy_s()这个函数。它会自动帮你切割防止内存溢出。

# re: 内存崩溃 CASE 3  回复  更多评论   

2009-04-12 16:30 by yyyy1985
@freeeyes
跟你的看法是一致的。

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理