我目前正在查看Win32可执行文件主模块中的函数。在堆栈(sub esp)上分配内存并在堆栈上保存了一些寄存器之后,esp的值与全局变量进行异或。

是某种堆栈保护技术?

编辑:我从内存中写了这个问题。实际的指令顺序为:

mov eax, esp
xor eax, DWORD PTR ds:[<some address>]
push eax


#1 楼

是的,这是通常所谓的“堆栈金丝雀”的实现,这是堆栈缓冲区溢出保护的一种方法。您要描述的示例是Visual Studio使用的方法,自Visual Studio 2005开始默认启用,自Visual Studio 2003开始实施。由于Visual Studio提供了启用标记GS protection和启用标记/GS的事实,也称为/GS-禁用该保护并覆盖默认行为。

什么是堆栈缓冲区溢出保护?

有许多由不同的编译器和第三方保护工具实现的堆栈缓冲区溢出保护技术,但是它们都围绕着相同的基本思想:当利用堆栈缓冲区溢出时,攻击者通常会覆盖位于堆栈上的返回地址,以将代码执行重定向到受控地址。堆栈金丝雀的工作方式是在ret指令前添加某种验证,以确保攻击者在执行ret指令之前未更改堆栈(特别是返回地址)(这将导致从堆栈弹出并放置弹出窗口)

所提供的示例如何保护堆栈免受此类攻击?

要回答这个问题,我们需要提供以下代码的完整实现细节您刚刚提供了前半部分。大多数堆栈溢出金丝雀保护通常包括插入函数prolog和Epilog,而您只提供了前者。

下面是一个带注释的示例prolog:

>此序言从分配堆栈变量的空间开始,就像任何“常规”函数一样。然后,它继续获取在进程开始时随机生成的值,并将其存储在特定的内存地址中,并保存到寄存器EAX中,然后使用当前的堆栈指针对其进行xor寻址。然后将结果值存储在堆栈中。

还有一个注释的示例Epilog:攻击者控制的代码,验证了保留原始堆栈cookie值的情况,如果该值与预期的堆栈cookie不同,则会触发安全失败。

大多数基于cookie / canary的逻辑保护措施如下:


在功能入口处存储特定的值,该值是确定的,但从攻击者的角度而言是不可预测的。最好是依赖于函数的执行条件(例如当前的ESP)的函数。该值应放置在函数的返回地址与任何可能发生缓冲区溢出的缓冲区或变量之间。
在函数退出时,在使用可能被覆盖的返回地址之前,请验证存储的cookie是否与预期的相同,如果失败,则失败
由于经典的缓冲区溢出攻击正在按顺序重写整个堆栈(这意味着要覆盖堆栈变量ret的返回地址,位于返回地址和buf之间的堆栈上的所有数据也可能会被覆盖),返回地址的任何覆盖都必须修改堆栈金丝雀。只要攻击者无法在触发函数调用之前预测金丝雀,由于在执行buf之前执行的金丝雀验证,攻击将失败。 br />
不,出于几个原因,关于堆栈溢出开发和保护的斗争一直在进行着(在某些情况下仍在继续):在Visual Studio中实现第一个金丝雀保护后不久,便开始了针对SEH异常结构(在堆栈上分配以处理异常)的攻击,并提供了多种反SEH缓冲区溢出保护(例如SafeSEH),它经历了多种版本直到完全可靠地防御此类攻击,包括后来的ret)。
此外,信息泄漏错误被用来预测(并增加预测机会)金丝雀值,从而绕过金丝雀检查并使得基于堆栈的缓冲区溢出成为可能。
与#2稍有相似,但特定于金丝雀保护,在某些情况下,攻击者可以利用进程的执行流程逐字节缓慢提取金丝雀值,从而减少2 ** 32(4294967296可能性)的暴力强制仅使用256 * 4(1024种可能性)的蛮力,使许多攻击更加合理。
还使用了允许非线性覆盖的缓冲区溢出错误,以“跳过”覆盖堆栈金丝雀(或其中的大部分),以完全避免需要预测金丝雀值,或将修改范围减小较低的1个字节范围。这样的常见示例看起来仅在某些条件下或仅覆盖dword的1个字节的情况下才覆盖。这些也将使返回地址修改受到限制,但仍然有用(在某些情况下,还使用ASLR的情况下,有时更有用)。


评论


因此,与Igor的答案相反,返回地址不是立即通过与cookie进行异或来保护,而是通过检查canary是否已被覆盖(假设,如您所述,堆栈溢出将覆盖它)来保护?

– InkassoHeinzi
17年9月2日在18:19

在您提供的代码段(与我提供的代码段更相似)中,将随机生成的cookie与ESP寄存器(指向堆栈的当前位置)进行异或。在引用的代码片段Igor中,cookie与返回地址的放置位置(在堆栈上)进行异或。两种方法都行得通,我可以看到两种方法都有一点优势。但是,我认为从这个答案中获取的重点是堆栈cookie背后的整体推理和逻辑,而不是所使用的特定实现。更改可能来自不同的保护版本。

– NirIzr
17年9月2日在18:27

#2 楼

是的,这是Microsoft的堆栈溢出保护,通常称为“ GS cookie”。从编译器安全检查深度:


使用/ GS编译函数时,函数prolog将留出另外四个字节,并添加另外三个指令作为
/>内容如下:

sub   esp,24h
mov   eax,dword ptr [___security_cookie (408040h)]
xor   eax,dword ptr [esp+24h]
mov   dword ptr [esp+20h],eax


序言中包含一条获取Cookie副本的指令,
后面是一条对cookie进行逻辑异或的指令和
返回地址,最后是一条将
cookie存储在返回地址正下方的堆栈中的指令。从这一点开始,该功能将像往常一样执行。函数返回后,最后要执行的是函数的结尾,与序言相反。

用/ GS编译时,安全检查也放在
Epilog中:

mov   ecx,dword ptr [esp+20h]
xor   ecx,dword ptr [esp+24h]
add   esp,24h
jmp   __security_check_cookie (4010B2h)


检索cookie,然后在
XOR指令后加上返回地址。 ECX寄存器应
包含与存储在
__security_cookie变量中的原始cookie匹配的值。然后回收堆栈空间,然后执行__security_check_cookie例程的JMP指令代替执行RET指令。