我刚试着在VS2010上编译了几个C++片段,并在IDA Pro上分析了这些可执行文件。我注意到大部分都在开始处(__security_check_cookie之后)有以下代码:
xor eax, ebp
以及类似的代码:
xor ecx, ebp
这是为什么呢?编译器优化已经关闭了。
我刚试着在VS2010上编译了几个C++片段,并在IDA Pro上分析了这些可执行文件。我注意到大部分都在开始处(__security_check_cookie之后)有以下代码:
xor eax, ebp
以及类似的代码:
xor ecx, ebp
这是为什么呢?编译器优化已经关闭了。
这些是缓冲区溢出保护方法,与编译器优化无关。如果您指定了 /GS
开关,MSVC会在返回地址附近的堆栈上推送一个安全cookie,以便检测常见的堆栈破坏情况。
堆栈破坏可能是由于不良代码造成的,例如:
char buff[5];
strcpy (buff, "Man, this string is waaay too long!!");
或者通过恶意用户利用不良编码实践,例如使用scanf(""%s", myBuff)
来处理用户输入。精心制作的攻击可以让您的程序执行您可能不想要的操作。
通过将cookie放置在返回地址附近,可以防止大量漏洞(和攻击向量),仅因为内存损坏往往是顺序性的。换句话说,如果您覆盖了返回地址,那很可能是因为您从cookie的一侧开始写入并破坏了所有内存直到另一侧的返回地址(因此cookie也会被覆盖)。
这种方法无法捕获全部的漏洞,因为您可能有一些代码如下:
char buff[5];
buff[87] = 'x';
这可能会潜在地破坏返回地址,而不需要接触cookie。但它将捕获所有那些依赖于输入比预期更长字符串的恶意攻击,这些攻击会破坏返回地址(包括cookie)。
你可能在代码中看到的序列类似于:
mov eax, dword ptr ds:___sec_cookie ; fixed value.
xor eax, ebp ; adjust based on base pointer.
mov [ebp+SOMETHING], eax ; store adjusted value.
根据当前的基指针定制cookie。
这将改变每个堆栈层实际放置在堆栈上的内容(也取决于参数数量和大小),可能是为了进一步保护代码免受恶意攻击,确保写入堆栈的是一个可变签名而不是固定值(否则攻击者可以输入字符,包括一个有效的cookie)。
最后的序列将按照以下方式运行:
mov ecx, [ebp+SOMETHING] ; get the adjusted cookie.
xor ecx, ebp ; un-adjust it, since
; ((N xor X) xor X) == N.
call @__sec_check_cookie ; check the cookie.
这基本上只是上面描述过程的反向过程。如果将ecx
设置为正确的cookie值,则@__sec_check_cookie
调用将返回;否则,它将引发故障,如此处所确认的那样:
__security_check_cookie()
例程很简单:如果cookie未更改,则执行RET
指令并结束函数调用。如果cookie不匹配,则例程调用report_failure()
。
report_failure()
函数然后调用__security_error_handler()
。这两个函数都在C运行时(CRT)源文件的seccook.c
文件中定义。需要CRT支持才能使这些安全检查工作。当发生安全检查失败时,程序控制权将传递给
__security_error_handler()
,其摘要如下:
void __cdecl __security_error_handler(int code, void *data)
{
if (user_handler != NULL) {
__try {
user_handler(code, data);
} __except (EXCEPTION_EXECUTE_HANDLER) {}
} else {
//...prepare outmsg...
__crtMessageBoxA(
outmsg,
"Microsoft Visual C++ Runtime Library",
MB_OK|MB_ICONHAND|MB_SETFOREGROUND|MB_TASKMODAL);
}
_exit(3);
}
默认情况下,安全检查失败的应用程序会显示一个对话框,其中写着“检测到缓冲区溢出!”。当对话框被关闭后,应用程序将终止。