web-dev-qa-db-ja.com

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]を設定するバイナリにコンパイルしたりできますか?カナリアは再生成されますか-ブート時、またはプロセススポーンですか?これはどこに文書化されていますか?

11
Evan Carroll

(ほとんど)すべてのプロセスstraceは、プロセス実行の最初の段階で非常に疑わしいsyscallを示しているため、この初期化を追跡するのは簡単です。

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セグメントベースは、プログラムのロード中にglibcの一部であるld-linuxによって設定されます(プログラムが静的にリンクされている場合、これはコードはバイナリに埋め込まれています。ここですべてが行われます。

起動時に、ローダー TLSを初期化 。これには、メモリの割り当てと設定が含まれますFS TLSの開始を指すベース値。これはArch_prctlsyscall を介して行われます。TLSの初期化後security_initfunction が呼び出され、スタックガードの値が生成され、fs:[0x28]が指すメモリロケーションに書き込まれます。

また、0x28は、TLSの開始点にある構造体のstack_guardフィールドのオフセットです。

18
Danila Kiver

あなたが見ているものは(GCCでは) Stack Smashing Protector(SSP) と呼ばれ、コンパイラによって生成された バッファオーバーフロー保護 の形式です。この値は、起動時にプログラムによって生成される乱数であり、Wikipediaの記事で言及されているように、 Thread Local Storage(TLS) に配置されます。他のコンパイラは、このタイプの保護を実装するために異なる戦略を使用する場合があります。

なぜ値をTLSに保存するのですか?値はそこにあるため、CS、DSおよびSSレジスターからはそのアドレスにアクセスできません。悪意のあるコードからスタックを変更しようとすると、格納された値の推測が非常に困難になります。

6
ErikF