อะไรที่ทำให้ fs: [0x28] (กองนกขมิ้น)?


13

จากโพสต์นี้จะแสดงให้เห็นว่า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]หรือไม่? การฟื้นฟูหรือการวางไข่เกิดขึ้นอีกหรือไม่? เอกสารนี้อยู่ที่ไหน

คำตอบ:


18

มันง่ายที่จะติดตามการเริ่มต้นนี้เนื่องจาก (เกือบ) ทุกขั้นตอน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มาหา backtrace:

(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 นี้จะกระทำผ่านsyscallarch_prctl หลังจากเรียกใช้งานsecurity_init ฟังก์ชั่นการเริ่มต้น TLS ซึ่งสร้างค่าของ stack Guard และเขียนลงในตำแหน่งหน่วยความจำซึ่งfs:[0x28]ชี้ไปที่:

และ0x28เป็นออฟเซ็ตของstack_guardสนามในโครงสร้างซึ่งอยู่ที่จุดเริ่มต้น TLS


zomfg คำตอบที่ดีจริงๆ ฉันพยายามแยกเลขฐานสองด้วย radare นี่คือรูปแบบและเนื้อหาที่ฉันกำลังมองหา ขอบคุณมาก
Evan Carroll

กระบวนการเริ่มต้นอะไรกับarch_prctl(ARCH_SET_FS..)ฉันไม่เห็นว่าในปฏิบัติการ? เป็นรหัสเคอร์เนลหรือไม่
Evan Carroll

ดูลิงก์ "syscall" ในโพสต์ มันจะนำไปสู่ไซต์การโทรจริง ( git.launchpad.net/glibc/tree/sysdeps/x86_64/nptl/tls.h#n153 ) ที่มีการเรียกใช้ syscall มันถูกดำเนินการโดยld-linuxในระหว่างการเริ่มต้น TLS
Danila Kiver

6

สิ่งที่คุณเห็นเรียกว่า (ใน GCC) Stack Smashing Protector (SSP)ซึ่งเป็นรูปแบบของการป้องกันบัฟเฟอร์ล้นที่สร้างโดยคอมไพเลอร์ ค่าเป็นจำนวนสุ่มสร้างขึ้นโดยโปรแกรมที่เริ่มต้นและเป็นบทความวิกิพีเดียกล่าวถูกวางไว้ในกระทู้เก็บข้อมูลท้องถิ่น (TLS) คอมไพเลอร์อื่น ๆ อาจใช้กลยุทธ์ที่แตกต่างเพื่อดำเนินการป้องกันประเภทนี้

ทำไมต้องเก็บค่าไว้ใน TLS เนื่องจากค่าตั้งอยู่ที่นั่นที่อยู่นั้นไม่สามารถเข้าถึงที่อยู่ของที่ลงทะเบียน CS, DS และ SS ทำให้คาดเดาค่าที่เก็บไว้ได้ยากมากหากคุณพยายามเปลี่ยนสแต็กจากรหัสที่เป็นอันตราย


นี่ไม่ใช่สิ่งที่ฉันกำลังมองหาดังนั้นฉันได้ชี้แจงเล็กน้อยในความพยายามที่จะชัดเจน "หมายเลขสุ่มที่สร้างโดยโปรแกรมเมื่อเริ่มต้น" คุณสามารถแสดงตำแหน่งที่สร้างไฟล์ที่รันได้และสิ่งที่วางโค้ดเพื่อสร้างมัน
Evan Carroll
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.