可以使堆栈的安全性足够安全以应对堆栈溢出:
http://msdn.microsoft.com/en-us/library/9a89h429(VS.80).aspx
关于为什么需要注册的异常处理程序(安全-SEH)以及为什么普通的异常处理程序不会削减它的问题是因为您会遇到非常大的堆栈溢出。
假设我有开始的功能
try {
char buffer[N];
strcpy(&buffer, &attacker);
} __except(...) { }
这可能会转化为汇编代码
push ebp
mov ebp, esp
; GS if you want to here
; install the exception handler:
push lbl_Exceptionhandler
push dword ptr [fs:0]
mov dword ptr[fs:0], esp
; setup the locals inside the stack
sub esp, LOCALS
; GS if you want to here
; call strcpy
lea ecx [ebp + offset_to_buffer];
push ecx
lea edx, [ebp + offset_to_attacker]
push edx
call _strcpy
add esp, 8
; uninstall the locals
mov esp, ebp
; uninstall the exception handler
pop dword ptr [fs:0]
; return
pop ebp
; optionally check GS cookies that we might have also inserted at any point in this function.
call _checksecuritycookie
ret
或者换句话说,堆栈看起来像这样:
RET PTR
/GS1
SAVED EBP
/GS2
SAVED FS:0
/GS3
LOCAL char buffer[N]
GS1、GS2 和 GS3 是堆栈金丝雀可能选择写入堆栈 cookie 的位置。注意cookie只会在函数结束时被检查(这在计算机安全中很重要。当你引入检查时,你不仅需要考虑检查是否会检测到溢出,还要考虑它是否会在溢出之前检测到它已经太晚了;这需要考虑检查将在哪里进行。对于堆栈cookie,cookie仅在函数退出时检查,因为堆栈cookie通常只是为了保护返回地址,而不是保护局部变量)。
正常异常处理程序的问题是如果攻击者缓冲区非常大会发生什么。假设它太大了,它会破坏整个堆栈,写入线程的保护页面并触发错误?
好吧,内核回调到 ntdll 并告诉它排序它的进程,而 ntdll 的第一个调用端口是查看是否有任何注册的异常处理程序。现在它如何找到要调用的异常处理程序?好吧,它查看 fs:0,它指向堆栈上的异常处理程序,并调用异常处理程序指针。除了攻击者刚刚销毁的堆栈上的异常处理程序。
哎呀。现在攻击者控制了 EIP 而你输了。
Safe-SEH 通过指出您可能想要调用的异常处理程序列表实际上是一个完全在编译时确定的有限列表来解决这个问题。通过将此列表写入 PE 文件本身,ntdll 有机会再次检查它应该跳转到的异常处理程序实际上是一个真正的异常处理程序,而不是某些邪恶攻击者阴谋接管您的 EIP 的原因。
Safe-SEH 有成本(因此选择加入),但成本是捕获异常变得更加昂贵,因为 ntdll 现在将在您的异常处理程序接管之前完成更多工作。
尽管如此,我的建议是 SafeSEH 应该始终打开。由于您的应用程序的性能严重依赖于抛出异常的速度,因此更容易丢失客户的信用卡详细信息,这表明开发人员的心态如此糟糕,以至于他们应该立即投入大炮并被发射到阳光下以避免他们的破坏社会的可怕代码。