栈相关检查

1. Base Runtime Checks(/RTC1 …)

在vc开发程序的时候,我们在编译代码的时候,可以设置选项:Base Runtime Checks 如下图所示:

Base Runtime Checks

那么什么是基础的运行时检查呢?作用是什么?

在这个部分,我们可以查看文档 /RTC (运行时错误检查)

在汇编中,我们能看到什么?

RTC functions

去掉RTC检查

如下图,在选项中设置 Default 就可以了

那么在汇编中,现在的情况是什么呢?

我们能看到,已经没有了相关的函数,只有一个初始化和结束的函数

2. Buffer Security Check(/GS)

检测一些缓冲区溢出,这些溢出覆盖了函数的返回地址、异常处理程序地址或某些类型的参数。导致缓冲区溢出是黑客用来利用不强制执行缓冲区大小限制的代码的一种技术。

什么是受保护的?

在/ GS编译器选项保护下列项目:

  • 函数调用的返回地址。

  • 函数的异常处理程序的地址。

  • 易受攻击的函数参数。

在所有平台上,/GS尝试检测缓冲区溢出到返回地址。缓冲区溢出在 x86 和 x64 等平台上更容易被利用,这些平台使用调用约定将函数调用的返回地址存储在堆栈上。

在 x86 上,如果函数使用异常处理程序,编译器会注入安全 cookie 以保护异常处理程序的地址。在帧展开期间检查 cookie。

/GS保护传递给函数的易受攻击的参数。易受攻击的参数是指针、C++ 引用、包含指针的 C 结构(C++ POD 类型)或 GS 缓冲区。

在 cookie 和局部变量之前分配了一个易受攻击的参数。缓冲区溢出可以覆盖这些参数。在函数返回和执行安全检查之前,使用这些参数的函数中的代码可能会导致攻击。为了尽量减少这种危险,编译器在函数 prolog 期间制作了易受攻击的参数的副本,并将它们放在任何缓冲区的存储区域之下。

在以下情况下,编译器不会复制易受攻击的参数:

  • 不包含 GS 缓冲区的函数。

  • 未启用优化(/O 选项)。

  • 具有可变参数列表 (…) 的函数。

  • 标有裸体的函数。

  • 在第一条语句中包含内联汇编代码的函数。

  • 参数仅以在缓冲区溢出时不太可能被利用的方式使用。

什么不受保护?

在/GS编译器选项并不能防止所有的缓冲区溢出的安全攻击。例如,如果对象中有一个缓冲区和一个 vtable,则缓冲区溢出可能会损坏 vtable。

即使您使用/GS,也要始终尝试编写没有缓冲区溢出的安全代码。

详细的信息可以查看微软官方文档

在汇编中我们能看到什么?

Buffer Security Check

3. Buffer Security Check原理

我们在IDA 中查看反编译汇编代码,如下图:

观看以下代码:

1
2
3
4
5
6
7
8
var_4= dword ptr -4

push ebp
mov ebp, esp
sub esp, 70h
mov eax, ___security_cookie
xor eax, ebp
mov [ebp+var_4], eax

其中 ___security_cookie 是一个固定值,如下:

1
___security_cookie dd 0BB40E64Eh

在函数结束位置,我们看到如下汇编:

1
2
3
mov     ecx, [ebp+var_4]
xor ecx, ebp ; cookie
call j_@__security_check_cookie@4 ; __security_check_cookie(x)

我们查看 __security_check_cookie(x) 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
; void __fastcall __security_check_cookie(unsigned int cookie)
.text:00411310 @__security_check_cookie@4 proc near ; CODE XREF: __security_check_cookie(x)j
.text:00411310
.text:00411310 cookie = dword ptr -4
.text:00411310
.text:00411310 cmp ecx, ___security_cookie
.text:00411316 jnz short failure
.text:00411318 rep retn
.text:0041131A ; ---------------------------------------------------------------------------
.text:0041131A
.text:0041131A failure: ; CODE XREF: __security_check_cookie(x)+6j
.text:0041131A jmp j____report_gsfailure
.text:0041131A @__security_check_cookie@4 endp

我们比较上面的内容发现,如果在使用过程中,变量改变了,那么就出现错误。所以这样就保证了缓存区溢出造成的漏洞。

参考

  1. 目录projects 下有功能 test_safeSEH 和 testc_bufcheck

4. /JMC (@__CheckForDebuggerJustMyCode@4) 选项

微软官方文档:https://learn.microsoft.com/en-us/cpp/build/reference/jmc?view=msvc-170

指定编译器支持 Visual Studio 调试器中的本机 Just My Code 调试。此选项支持允许 Visual Studio 跨过系统、框架、库和其他非用户调用并在调用堆栈窗口中折叠这些调用的用户设置。 /JMC 编译器选项从 Visual Studio 2017 版本 15.8 开始可用。

选项位置:

启用 /JMC 时,编译器会在函数序言中插入对辅助函数 __CheckForDebuggerJustMyCode 的调用。帮助函数提供支持 Visual Studio 调试器 Just My Code 步骤操作的挂钩。

如果启用了 /JMC 通过反汇编,可以查看如下代码:

启用 /JMC 选项后,PDB 文件会使用额外的行号信息进行注释。在 Visual Studio 2019 版本 16.8 之前的版本中,此信息可能会出现在代码覆盖率报告中,出现在第 15732480 (0xF00F00) 行或 16707566 (0xFEEFEE) 行。这些虚构的行号用作区分用户代码和非用户代码的标记。要在没有这些意外行号的情况下在代码覆盖率报告中包含非用户代码,请使用 /JMC- 选项构建您的代码。


栈相关检查
https://xxxxnnxxxx.github.io/2023/07/05/栈相关检查/
作者
xxxxnnxxxx
发布于
2023年7月5日
许可协议