什么设置 fs:[0x28] (堆栈金丝雀)?

什么设置 fs:[0x28] (堆栈金丝雀)?

这个帖子可以看出这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]吗?金丝雀是否在启动时或进程生成时重新生成?这是在哪里记录的?

答案1

跟踪此初始化很容易,因为(几乎)每个进程strace在进程运行的一开始都显示一个非常可疑的系统调用:

arch_prctl(ARCH_SET_FS, 0x7fc189ed0740) = 0

就是这样man 2 arch_prctl说的:

   ARCH_SET_FS
          Set the 64-bit base for the FS register to addr.

是的,看起来这就是我们所需要的。为了找到谁调用了arch_prctl,让我们寻找回溯:

(gdb) catch syscall arch_prctl
Catchpoint 1 (syscall 'arch_prctl' [158])
(gdb) r
Starting program: <program path>

Catchpoint 1 (call to syscall arch_prctl), 0x00007ffff7dd9cad in init_tls () from /lib64/ld-linux-x86-64.so.2
(gdb) bt
#0  0x00007ffff7dd9cad in init_tls () from /lib64/ld-linux-x86-64.so.2
#1  0x00007ffff7ddd3e3 in dl_main () from /lib64/ld-linux-x86-64.so.2
#2  0x00007ffff7df04c0 in _dl_sysdep_start () from /lib64/ld-linux-x86-64.so.2
#3  0x00007ffff7dda028 in _dl_start () from /lib64/ld-linux-x86-64.so.2
#4  0x00007ffff7dd8fb8 in _start () from /lib64/ld-linux-x86-64.so.2
#5  0x0000000000000001 in ?? ()
#6  0x00007fffffffecef in ?? ()
#7  0x0000000000000000 in ?? ()

因此,FS 段基数是在程序加载期间由 设定的ld-linux,它是 的一部分glibc(如果程序是静态链接的,则此代码将嵌入到二进制文件中)。这就是一切发生的地方。

在启动过程中,加载程序初始化 TLS。这包括内存分配和设置 FS 基值以指向 TLS 开头。这是通过以下方式完成的arch_prctl 系统调用。 TLS 初始化后security_init 功能被调用,它生成堆栈保护的值并将其写入内存位置,该位置fs:[0x28]指向:

并且是位于 TLS 开头的结构体中字段0x28的偏移量。stack_guard

答案2

你所看到的(在 GCC 中)被称为堆栈粉碎保护器 (SSP),这是一种形式缓冲区溢出保护由编译器生成。该值是程序在启动时生成的随机数,正如维基百科文章提到的那样,放置在线程本地存储 (TLS)。其他编译器可能使用不同的策略来实现这种类型的保护。

为什么将值存储在 TLS 中?由于该值位于此处,因此 CS、DS 和 SS 寄存器无法访问其地址,因此如果您试图通过恶意代码更改堆栈,则很难猜测存储的值。

相关内容