この投稿 から、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
は、プロセス実行の最初の段階で非常に疑わしい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_prctl
syscall を介して行われます。TLSの初期化後security_init
function が呼び出され、スタックガードの値が生成され、fs:[0x28]
が指すメモリロケーションに書き込まれます。
また、0x28
は、TLSの開始点にある構造体のstack_guard
フィールドのオフセットです。
あなたが見ているものは(GCCでは) Stack Smashing Protector(SSP) と呼ばれ、コンパイラによって生成された バッファオーバーフロー保護 の形式です。この値は、起動時にプログラムによって生成される乱数であり、Wikipediaの記事で言及されているように、 Thread Local Storage(TLS) に配置されます。他のコンパイラは、このタイプの保護を実装するために異なる戦略を使用する場合があります。
なぜ値をTLSに保存するのですか?値はそこにあるため、CS、DSおよびSSレジスターからはそのアドレスにアクセスできません。悪意のあるコードからスタックを変更しようとすると、格納された値の推測が非常に困難になります。