http://topic.csdn.net/u/20100809/10/9fbee996-c43e-4928-93b2-d49ce5702ea8.html
访问冲突
当某指令或程序执行引发的内存访问不能满足由处理器体系结构或内存管理单元结构定义的特定条件时,现代处理器会产生访问冲突异常 (0xc0000005= STATUS_ACCESS_VIOLATION)。
虽然单纯的故障只会产生拒绝服务的状况,但是不能贸然认为故障不会被用来产生更多的危险结果(包括代码执行)。在分析故障时,您应假设整个内存体(除了一些微小的例外)都在潜在攻击者的控制之下,因而访问冲突在大多数情况下都可以导致数据被攻击者所控制。这种假设适用于当指令读取或写入数据时发生的异常。
如果访问冲突可导致您的数据被攻击者控制,那么从内存进行读取所引起的每次访问冲突都会转变为加载攻击者控制的数据。这类操作的安全效果并不总是很容易确定。您可以对二进制或源代码执行完整的数据流分析,找出源地址控制的范围以及在某些执行点向程序提供随机数据的后果。这是一项既耗时又艰巨的任务。为了应对这种情况,我们开发了简单的试探法以快速分析读取访问冲突故障并找到可能的代码执行。
正如下面的示例所示,寄存器 eax 中无效的内存指针引起了故障。在这种情况下,对内存内容的控制会让攻击者完全控制程序流:
Application!Function+0x133:
3036a384 eb32 call [eax] ds:0023:6c7d890d=??
0:000> ub
mov eax, [ebx] -> eax = invalid memory pointer
... (instructions not affecting register eax)
call [eax] -> crash
如果攻击者无法完全控制正在读取的地址,这种情况可被视为拒绝服务状况。例如,在典型的 Windows 用户模式环境下,在已初始化并且攻击者无法影响的 NULL 指针处所发生的故障本身不能导致代码执行。
在下面的示例中,您可以看到故障是由引用零地址(通过寄存器 eax 中的值)引起的:
Application!Function+0x133:
3036a384 8b14 mov ecx, [eax] ds:0023:00000000=??
0:000> ub
xor eax, eax
... (instructions not affecting register eax)
cmp ebx, 2
jne label123
mov ecx, [eax] -> crash, eax = 0 (NULL)
通过反汇编(使用 Visual Studio® 命令行调试器中的 ub 命令),我们可以跟踪此寄存器中的数据流,直到确定恶意输入无法影响寄存器中的值为止。在此示例中,该寄存器实际上被 XOR 自身清零了,并且在到达故障指令之前没有使用它。
有时候,缺陷的可利用性不会立即显现在出故障的指令中。例如,将下列指令反汇编以后,您可以看到它是一个控制流的结果,其中失败指令(在前一个段落中介绍过)后跟一个关键指令:
(1258.1638): Access violation - code c0000005 (second chance)
Application!Function+0x123:
3036a384 8b12 mov eax, [ebx] ds:0023:6c7d890d=??
0:000> u
mov eax, [ebx] -> crash, ebx points to invalid memory
... (instructions not affecting regist an example er eax)
call [eax] -> possibility of code execution
本示例与第一个示例类似,但这次故障是发生在将数据加载到寄存器 eax 的指令处。尽管此操作本身没有显现出安全问题,但是反汇编清楚地表明,控制寄存器 ebx 的值就意味着控制寄存器 eax 的值,这可能会引发代码执行。
其余情况可以在程序运行时通过模拟恶意数据注入来进行分析,可在调试器下手动分析,也可通过调试工具自动分析。到达引起访问冲突的指令后,我们可以更改源地址以指向有效内存地址并再次执行该指令,也可以将随机数据放入目标寄存器并跳过错误指令,继续执行。我们会对遇到的每一个访问冲突重复此过程,直到碰到可利用的情况。
让我们来分析两个示例。首先来看一下我们更改源地址时的跟踪数据流:
Application!Function+0xa70:
3036a37e 8b4708 mov eax,dword ptr [edi+8] ds:0023:040fd004=????????
这里的故障是由寄存器 edi 中错误的值引起的。我们要将其更改为指向正确的内存区域。虽然存在很多可能的选择,但实际上,我们通常都会使用寄存器 eip 的当前值。这可以确保 edi 的新值附近的相当大一部分内存都是正确的,并使捕获对(包含代码的)内存块进行任何后续写入成为可能,因为它始终都标记为只读:
0:000> r edi=eip
0:000> g
将寄存器 edi 设置为寄存器 eip 的当前值后,我们继续执行,在寄存器 esi 上碰到另一个异常,在此重复这个过程:
(1258.1638): Access violation - code c0000005 (second chance)
Application!Function+0xa76:
3036a384 f60601 test byte ptr [esi],1 ds:0023:6c7d890d=??
0:000> r esi=eip
0:000> g
继续执行,在尝试将数据写入代码段的指令上我们碰到一个写访问冲突异常。这意味着攻击者也可以将数据写入任何内存地址:
(1258.1638): Access violation - code c0000005 (second chance)
Application!Function+0xbef:
3036a4fd 894710 mov dword ptr [edi+10h],eax ds:0023:3036a38e=0c46f60d
可以看出,最初的读取访问冲突最终成了真正的安全问题,存在代码执行的可能性。
现在让我们通过设置目标数据并跳过指令来看一看跟踪数据流。像以前一样分析同一个故障,我们将继续更改目标数据。碰到最初的异常以后,我们可以将 eax 寄存器设置成任何易于跟踪的值,并在寄存器 eip 上加上当前指令的大小,以跳过它:
Application!Function+0xa70:
3036a37e 8b4708 mov eax,dword ptr [edi+8] ds:0023:03f93004=????????
0:000> r eax=deadbeef
0:000> r eip=eip+3
0:000> g
继续执行,我们碰到下一个异常,该异常没有目标,因此我们只要跳过该指令即可:
(1258.1638): Access violation - code c0000005 (second chance)
Application!Function+0xa76:
3036a384 f60601 test byte ptr [esi],1 ds:0023:deadbefb=??
0:000> r eip=eip+3
0:000> g
进一步执行,我们遇到同一个写入访问冲突异常,表明有可能存在可利用问题:
(1258.1638): Access violation - code c0000005 (second chance)
Application!Function+0xbef:
3036a4fd 894710 mov dword ptr [edi+10h],eax ds:0023:03f9300c=0c46f60d
两种模拟恶意数据注入的方法通常会产生相同的结果,从实践中我们可以看到,在很多情况下,它们会覆盖不同的代码路径,其中只有一种方法显现了问题的可利用性。至今为止讨论过的所有方法都可以非常快速地找到潜在的可利用读取访问冲突故障,但是我们必须记住一点,它们不会就某一故障不是可利用的问题提供最终验证。
数据写入过程中的访问冲突表明可能存在内存损坏,这几乎总是会引发可利用情况和潜在的代码执行。通常,这类写入是出现故障的程序中存在缓冲区溢出情况的信号。实际上,有些写入访问冲突故障可以显示为不可利用。但是,这样的情况要求您执行全面的数据流分析,以了解问题的根本原因。您需要确保该损坏不会导致攻击者能够覆盖数据并随意影响执行流。
大多数有关数据写入的访问冲突都可以引发一些将导致恶意代码执行的可利用情形。通常,代码执行是通过覆盖堆栈或堆上的(任意)一部分内存实现的。
在下面的示例中,当目标寄存器到达堆栈的上限,并遇到未分配的内存区域时,某个内存复制指令发生了故障。该复制操作的大小来自地址 [ebp-8] 处的变量,并且该故障表明它处于攻击者的控制之下:
Application!Function+0x143:
3036a384 f3a4 rep movsb es:0023:00140000=?? ds:0023:0125432c=41414141
0:000> ub
mov esi, [ebp-4]
mov edi, [ebp+4]
mov ecx, [ebp-8]
rep movsb -> write access violation if value in ecx is big enough
我们可以很自信地说,只有当攻击者控制的目标地址指向无效的内存或指向对程序执行没有影响的数据时,写入访问冲突才不会引发代码执行。实际上,只有小部分写入访问冲突能满足这个条件。在 Windows 用户模式环境下,对 NULL 指针或系统地址空间(通常是 2GB 以上的地址)的写入就是不可利用问题的一个示例(假设地址为 0x00000000 的页面不可分配)。此外,在服务器情形中,该指令会引发拒绝服务,如果它由非管理员用户触发就可视为安全漏洞。
此示例与前面关于读取访问冲突所讨论的情况相似。快速的反汇编表明,寄存器 eax 已初始化为零值,并且在到达故障指令之前没有发生更改:
Application!Function+0x133:
3036a384 8d14 mov [eax], ecx ds:0023:00000000=??
0:000> ub
xor eax, eax
... (instructions not affecting register eax)
cmp ebx, 2
jne label123
mov [eax], ecx -> crash, eax = 0 (NULL)
在用户模式下(不是在内核模式下),这类异常不可能用于单级利用。通常这些异常会导致拒绝服务。只有在某些情况下,当异常处理程序由于其他漏洞而被更改时,这些异常才可能引发恶意代码执行。拒绝服务在服务器平台中属于高优先级的 bug,而在工作站中则是中等优先级的 bug。
这些异常的一个例子是静态/全局取消引用(读取和写入)。此示例中的进程无法对页面 0x310000 进行读取访问,使读取访问冲突发生,也无法对同一页面进行写入访问从而触发写入访问冲突。
mov ebp, 310046h
mov eax, [ebp+4h]
inc eax
mov [ebp+8h], eax
前两种异常需要进行仔细的分析。如果静态/全局值指向内存中的地址(在这里恶意代码可以不限长度地写入,并可以覆盖其他结构以进行复杂的利用),那么这种情况必须标记为可利用。如果它只允许您在不属于任何控制结构组成部分的地址上存储单个 DWORD,那么它就极有可能是不可利用的。但是,这种假设必须要通过运行程序并观察值(也称为运行时分析)来验证。如果存储的值后来未在代码中使用,它就不会引起另一个可利用的情况,可将其忽略。如果该值可用作内存地址或可用于内存复制操作,则为可利用的可能性就会比较高。
在不可控制的地址空间(页面 0 和类似的情况)中的静态/全局地址上执行代码是另一个例子,在这里可利用性依赖于来自操作数的地址的内存页面对于写入是否为可控制:
0040137F B8 DE C0 AD DE mov eax, 0DEADC0DEh
00401384 FF D0 call eax
分析静态/全局地址上的代码执行有更多的意义。如果地址属于永远不会和普通进程执行相关联的页面(页面 0 或地址超过 0x80000000 的页面),那它就是不可利用的。
除零操作也会触发异常,但是它此时不可直接利用。这种情况需要进行额外的分析,才能确定此异常的结果是否可将 CPU 设为执行可导致成功利用的代码:
004013D6 33 C9 xor ecx, ecx
004013D8 8B C1 mov eax, ecx
004013DA 40 inc eax
004013DB F7 F1 div ecx
未处理的 C++ 异常可能破坏进程的执行。运行时,它们将导致终止应用程序。在调试器下,在未处理的异常之后可以继续运行。当运行时库的异常引发函数被调用时则发生 C++ 异常:
00401902 mov [ebp+var_4], 1
00401909 push offset __TI1H
0040190E lea eax, [ebp+var_4]
00401911 push eax
00401912 call __CxxThrowException@8 ; _CxxThrowException(x,x)
如果该异常的处理程序机制已被覆盖,那么这种情况就只能是可利用的。否则,未处理的 C++ 异常就不是可利用的。此异常的堆栈跟踪如图 2 所示。
注意,堆栈溢出异常可能会隐藏运行时的其他问题,将代码流转向其他路径,禁用编译器生成的保护机制(例如 /GS),或由于释放仍在使用的内存在应用程序中引入不一致。当堆栈空间几乎耗尽时,应用程序可能会在出现堆栈溢出异常的 C++ 方法内部失败,而且重要的代码段将不会按顺序执行。在这种情况下将需要进行额外会审。
/GS (0xc0000409=STATUS_STACK_BUFFER_OVERRUN) 异常是指当 Windows 检测到保护返回地址的安全 Cookie 遭到篡改时所引发的异常。由于 /GS 的目标是将导致代码执行的缓冲区溢出转换成拒绝服务攻击,因此无论何时检测到这类故障,您都可以确定存在安全 bug。(遗憾的是,由于存在错误的内存、时钟超过的母板、有故障的硬件和其他问题,有时候验证 Cookie 的代码会在没有真正缓冲区溢出的情况下犯错。)
在 Windows Vista® 中,当检测到 STATUS_STACK_BUFFER_OVERRUN 时(假设存在调试器),操作系统会引发一个 int 3 异常。在较早的 Windows 版本中,断点应放在 kernel32!UnhandledExceptionFilter 中以检测安全 Cookie 是否遭到了篡改(否则,进程会被终止,并且用户不会获得通知)。
在图 3 中,函数 foo 通过将过多的数据复制到堆栈缓冲区来溢出缓冲区,这样会导致 /GS Cookie 被覆盖。
一旦 Windows 在未标记为可执行的页面(换句话说,页面没有 PAGE_EXECUTE、PAGE_EXECUTE_READ 或其他相关标志)上检测到了代码执行,它就会引发 NX (0xc0000005=STATUS_ACCESS_VIOLATION) 异常。NX 在 64 位版本的操作系统中是强制实施的。诸如解包器和数字权限管理 (DRM) 的一些应用程序依赖堆上的执行代码,因此不是每个 NX 异常都应被视为安全漏洞。但是,了解 bug 的根本原因以确保它没有安全隐患仍然非常有意义。
值得注意的是,这种类型的异常使用错误代码 0xc0000005,该错误代码并不特定于 NX;任何行为异常(例如,从未分配的内存读取)的应用程序都可能引发此错误。为了解异常/错误代码是否确实与 NX 相关,我们需要查看页面上的保护设置。如果页面未标记为可执行,我们会遇到 NX 异常;否则会遇到其他类型的问题。例如,在图 4 中,出现了第一次异常,但是该指令似乎是有效的,并且引用了有效数据。
您可以看出,分析程序故障的安全隐患是一项非常复杂又容易出错的任务。我们已讨论了在分析故障时可能遇到的最常见异常,包括读取/写入访问冲突、除零、C++ 异常、/GS 异常和 NX 相关问题。要记住的要点是,只有详尽的根本原因分析才能确保您已正确诊断出给定的故障是否可利用。
为了尽可能向您提供一些有用的指导原则来分析您自己的应用程序,我们将信息归纳成了一个可以存放在工作站上的快速参考。图 5 以表格形式记录了本文讨论的指导原则。例如,如果 CPU 在没有写入访问权限的情况下尝试向内存页面写入数据,从而引发写入访问冲突,那么就需要解决该问题。
同样,图 1(在上文中提及过)的目的在于通过使用图形结构来提供该过程的另一种视图,从而帮助您确定特定的故障是否为可利用的。您可从顶级节点开始,然后问自己下一个要访问的是哪个节点。您可以继续该过程,直至遇到了不可利用的节点或可利用的节点为止。
1楼 一般是程序用到了非法内存地址。 | |
|
| 2楼 看看你的析构部分,可能有些资源已经释放了,你又释放了一遍? | |
| ||
| 3楼 使用了未初始化的指针 | |
|
4楼 分析故障以查找您应用程序中的安全漏洞 | |
|
5楼 操作空指针.Debug跟踪一下吧, |
int main()
{
console();
delete db;
return 0;
}
就是用链表的时候push_back那里出了问题,我不知道是怎么回事
0xcdcd…………这典型就是一个未分配指针
0xcdcd…………这典型就是一个未分配指针
未分配指针都是Oxcdcdcdc吗?
0xcdcd…………这典型就是一个未分配指针
未分配指针都是Oxcdcdcdc吗?
不一定,release下就不是,直接声明的栈指针也不是
Oxcdcdcdc已经超出了win32的用户地址空间,进入系统及空间了,内存分配根本分不到这个地址
0xcdcd…………这典型就是一个未分配指针
未分配指针都是Oxcdcdcdc吗?
不一定,release下就不是,直接声明的栈指针也不是
Oxcdcdcdc已经超出了win32的用户地址空间,进入系统及空间了,内存分配根本分不到这个地址
恩,大概能理解。。但是程序究竟是哪里出问题了?是指针吗?
内存问题 , 访问越界了