HAKATEMIA
28x86-64 ABI ja kutsutavat

Pinon linjaus ja red zone

Keskitaso25MIN

Pinon linjaus ja red zone

Pinon linjaus on lopullisesti se asia, joka erottaa "miksi tämä kaatuu printf:ssä" -ohjelmasta toimivaan ohjelmaan.

16-tavun sääntö

SysV AMD64 -kutsutapa vaatii: call-käskyn HETKELLÄ RSP on jaollinen 16:lla. Tasan silloin kun käsky lukee opcoden ja työntää paluuosoitteen pinoon, RSP:n alimmat 4 bittiä ovat 0.

Tämä ei ole CPU:n vaatimus eikä rajoitus opcode-tasolla. Se on kutsuttavan funktion oletus: callee saa luottaa siihen että pino oli 16-aligned ennen call:ia, jolloin call:n jälkeen + 8 tavun push:n jälkeen sen oma RSP on parittomassa 16-kohdistuksessa (RSP % 16 == 8). Tämän jälkeen prologi (push rbp + sub rsp, N jossain) palauttaa linjauksen 16-tasoon.

Mitä menee pieleen ilman linjausta? glibc:n printf (ja monet muut) käyttää SSE-käskyjä (MOVAPS, MOVDQA) joiden operandien on oltava 16-tavuun kohdistettuja. Kohdistamaton MOVAPS = SIGSEGV.

Linjaus-aritmetiikka, koottuna

TilanneRSP % 16
Kernel käynnistää _start:n0
call show_flag työntää paluuosoitteen8
push rbp työntää 8 tavua0
sub rsp, N(16 - N % 16) % 16
call printf työntää paluuosoitteen(16 - N % 16 - 8) % 16

Jotta viimeinen rivi olisi 0 (printf onnellinen), tarvitaan N % 16 == 8. Eli sub rsp, 8 toimii. sub rsp, 24 toimii. sub rsp, 16 EI toimi.

Red zone: 128 tavua kuoppaa pinon alle

x86-64 ABI antaa leaf-funktioille (funktiot jotka EIVÄT kutsu muita) hyödyn nimeltä red zone: 128 tavua RSP:n alapuolella on käytettävissä ilman sub rsp, ...-varausta. Kernel ja signaalikäsittelijät lupaavat olla koskematta siihen.

X86
1; leaf-funktio voi vapaasti käyttää [rsp - 128 ... rsp - 1]
2mov     dword [rsp - 4], 42     ; OK, red zonessa
3mov     dword [rsp - 130], 42   ; EI OK, ylittää 128 tavun rajan

Jos funktio kutsuu mitä tahansa toista funktiota, red zone HÄVIÄÄ: call työntää paluuosoitteen pinoon ja korvaa red zonen alkuosan. Kääntäjä tietää tämän ja kytkee red zonen pois jos funktio on ei-leaf.

gcc -mno-red-zone poistaa red zonen kokonaan: tämä on kernel- ja signaalikoodin oletus, koska kernel-tason koodi ei voi luottaa siihen että interrupt-käsittelijä jättäisi red zonen rauhaan.

TERMINALASCIINEMA

Linjaus käytännössä

Yksinkertainen sääntö: jos _start-tasolla kutsut yhden libc-funktion, lisää sub rsp, 8 heti aluksi. Jos teet oman main-tyyppisen rakenteen (push rbp / sub rsp, N / ... / leave / ret), varmista että N on 8 + 16:n monikerta.

1 / 2
Hakatemia Pro

Hakkeroinnin oppiminen alkaa tästä

Sadat interaktiiviset kurssit, virtuaalilabrat ja CTF-haasteet selaimessasi. Aloita ilmainen kokeilu ilman korttitietoja.