/RTCs开关这个开关是用来检查和Stack相关的问题: 1. Debug模式下把Stack上的变量初始化为0xcc,检查未初始化的问题 2. 检查数组变量的Overrun 3. 检查ESP是否被毁坏 Debug模式下初始化变量为0xcc假设我们有下面的代码:
对应的汇编代码如下:
1. sub esp, 228:s编译器为栈分配了228个byte 2. 接着3个push指令保存寄存器 3. Lea edi, DWORD PTR [ebp-228]一直到repstosd指令是初始化从ebp-228开始写57个0xcccccccc,也就是57*4=228个0xcc,正好填满之前sub esp, 228所分配的空间。这段代码会把所有的变量初始化为0xcc。 选择0xcc是有一定理由的: 1. 0xcc不同于一般的初始化值,人们一般倾向于把变量初始化为0, 1, -1等比较简单的值,而0xcc一般情况下足够大,而且是负数,容易引起注意,而且一般变量的值很有可能不允许是0xcc,比较容易造成错误 2. 0xcc = int 3,如果作为代码执行,则会引发断点异常,比较容易引起注意
检查数组变量的Overrun假设我们有下面的代码:
在scanf调用之后,会执行下面的代码:
这段代码会调用_RTC_CheckStackVars@8函数会在数组的开始和结束的地方检查0xcccccccc有否被破坏,如果是,则报告错误。_RTC_CheckStackVars由于代码过长这里就不给出了,这个函数主要是利用编译器保存的数组位置和长度信息,检查数组的开头和结尾:
$LN5@func纪录了数组的个数,而$LN4@func保存了数组的偏移量ebp - 112和数组的长度104,而$LN3@func则保存了变量的名称(0x62, 0x75, 0x66, 0 = “buf”)。 检查ESPESP的错误很有可能是由调用协定的mistach造成,或者Stack本身没有平衡。编译器会在调用其他函数和在函数Prolog和Epilog(开始和结束代码)的时候插入对ESP的检查: 1. 在调用其他外部函数的时候: 假设我们有下面的代码:
对应的汇编代码如下:
可以看到检查的代码非常简单直接,把ESP保存在ESI之中,当调用printf,平衡堆栈之后,检查esp和esi的是否一致,然后调用__RTC_CheckESP,__RTC_CheckESP代码也很简单:
如果不一致,跳转到esperror标号报告错误。
2. 函数返回的时候: 以下面的代码为例:
Func函数故意push eax来破坏堆栈的平衡性,对应的汇编代码如下:
在函数的初始化代码中,func会将ebp保存在Stack中,并且把当前esp保存在ebp中。
关键的检查代码在后面,当func函数恢复了堆栈之后,堆栈会恢复到之前刚保存esp到ebp的那个状态,这个时候ebp必然等于esp,否则出错
出错的时候显示的对话框如下: OK,这次就写到这里。下面几篇文章预定会写到下面这些内容: 1. /GS & Security Cookie 2. Calling Conventions 3. Name Mangling 4. Structured Exception Handling 5. Passing by Reference 6. Member functions 7. Object layout 8. Virtual functions 9. Virtual Inheritance 10. C++ Exceptions 11. Templates 敬请关注。
作者: ATField |
|