Pinon linjaus ja red zone
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
| Tilanne | RSP % 16 |
|---|---|
Kernel käynnistää _start:n | 0 |
call show_flag työntää paluuosoitteen | 8 |
push rbp työntää 8 tavua | 0 |
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.
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 rajanJos 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.
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.
Hakkeroinnin oppiminen alkaa tästä
Sadat interaktiiviset kurssit, virtuaalilabrat ja CTF-haasteet selaimessasi. Aloita ilmainen kokeilu ilman korttitietoja.