从这篇文章中可以看出这FS:[0x28]
是一个堆栈金丝雀。我在这个函数上使用 GCC 生成相同的代码,
void foo () {
char a[500] = {};
printf("%s", a);
}
具体来说,我得到这个程序集..
0x000006b5 64488b042528. mov rax, qword fs:[0x28] ; [0x28:8]=0x1978 ; '(' ; "x\x19"
0x000006be 488945f8 mov qword [local_8h], rax
...stuff...
0x00000700 488b45f8 mov rax, qword [local_8h]
0x00000704 644833042528. xor rax, qword fs:[0x28]
0x0000070d 7405 je 0x714
0x0000070f e85cfeffff call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
; CODE XREF from 0x0000070d (sym.foo)
0x00000714 c9 leave
0x00000715 c3 ret
设置的值是fs:[0x28]
什么?内核,还是 GCC 在代码中抛出?你能在内核中显示代码,或者编译成设置的二进制文件fs:[0x28]
吗?金丝雀是在启动时重新生成的,还是在进程生成时重新生成的?这是在哪里记录的?
跟踪这个初始化很容易,因为(几乎)每个进程
strace
在进程运行的一开始都显示出一个非常可疑的系统调用:这就是说
man 2 arch_prctl
:是的,看起来这就是我们需要的。要查找谁调用
arch_prctl
,让我们查找回溯:因此,FS 段基由 设置
ld-linux
,它是 的一部分glibc
,在程序加载期间(如果程序是静态链接的,则此代码嵌入到二进制文件中)。这就是一切发生的地方。在启动期间,加载程序初始化 TLS。这包括内存分配和将 FS 基值设置为指向 TLS 开始。这是通过
arch_prctl
syscall完成的。调用TLS 初始化security_init
函数后,会生成堆栈守卫的值并将其写入内存位置,该位置fs:[0x28]
指向:并且是结构中位于 TLS 开始处
0x28
的字段的偏移量。stack_guard
您所看到的称为(在 GCC 中)Stack Smashing Protector (SSP),它是编译器生成的一种缓冲区溢出保护形式。该值是程序在启动时生成的随机数,正如 Wikipedia 文章所提到的,它被放置在Thread Local Storage (TLS)中。其他编译器可能使用不同的策略来实现这种类型的保护。
为什么将值存储在 TLS 中?由于该值位于那里,因此 CS、DS 和 SS 寄存器无法访问其地址,如果您试图从恶意代码更改堆栈,则很难猜测存储的值。