Kasutatavad kokkulepped pluss esialgsed märkused
Kärpin GDB väljundit lühiduse huvides, kuna see näitab autoriõigusi ja muud teavet alati seansi alguses. Väljundi taasesitamisel alustan esimesest viipareast (gdb)
või juhul, kui esimesest ehtsast väljundreast täidetakse automaatselt käske.
Selleks GDB viipale sisestatud käskude eristamiseks on neil juhtpilt (gdb)
nagu reaalses maailmas. Shelli käsu korral pole see kas üldse eesliide või $
, kuna see näib olevat kombeks enamikus unixoid-süsteemides.
Kui kasutan konkreetset käsku, näiteks vim
minu toimetajana võite loomulikult kasutada oma lemmiktoimetajat. Olgu selleks emacs
või nano
, ma ei mõista teid üle;)
Alustamine
See jaotis käsitleb keskkonna gdb
seadistamist ja protsessi alustamist. Lisan ka mõned uustulnukad uustulnukatele.
Nipid, mida peaksite teadma
GDB-l on kena viip, mille korral teie kursor peatub pärast programmi pausi või alati, kui olete astumine või mõni muu selline.
- Pärast GDB käsu käivitamist vajutage RETURN (aka ENTER ) vajutades sama käsk uuesti. See on kasulik, kui astute läbi koodiga
step
või next
ja soovite lihtsalt ükshaaval jätkata. - Käsud saab lühendada seni, kuni need on üheselt mõistetavad. Mõne sageli kasutatava käsu jaoks on olemas konkreetne lühikirjeldus, mis on mitmetähenduslikkusest hoolimata ülimuslik:
-
b
break
(vaatamata bt
ja backtrace
) -
c
või cont
jaoks keep
(vaatamata catch
, kõne
ja nii edasi) -
n
jaoks next
(vaatamata ni
ja nexti )
- Käsu
call
abil saate helistada tegelikele raamatukogu funktsioonidele või isegi silutud programmi funktsioonidele. See tähendab, et saate proovida käitumist või sundkäitumist. - GDB-d saate käivitada arvuga
gdbtui
või gdb -tui
, et saada - väidetavalt mugavam - visuaalsema teksti kasutajaliides. Selle ülaservas kuvatakse lähtekood ja allpool viip (gdb)
. Sellele paigutusele saate ka üle minna, käivitades käsu layout src
käsul (gdb)
. - GDB-l on käsurea lõpuleviimise funktsioon sarnaselt paljusid kestasid, nii et kasutage oma huvides vahelehte ja kasutage alati
help
või help [keyword | käsk]
alati, kui vajate abi. -
shell
võimaldab teil shellis käske täita, et saaksite käske käivitada oma GDB seansi sees. Arenduse ajal võiks näiteks olla shell make
. -
print
, uurima
ja display
know mitmesugused vormingud ( / FMT
), mida saate kasutada väljundi loetavamaks muutmiseks. - Allikataseme silumisel saate väärtuste kuvamiseks kasutada C-tüüpi heiteid. Kujutage ette C-koodi
void *
taga (mida GDB tunneb sellisel juhul tänu sümbolitele). Lihtsalt valage (char *)
ja printige see: print (char *) muutuja
.
Protsessi käivitamine
Kuna me tahame binaarset dünaamiliselt analüüsida, peame selle kõigepealt käivitama.
Käsurida
Saame seda teha otse käsurealt, läbides mitte ainult tee binaarse juurde, aga ka argumendid, millega soovime seda alustada. Kogu protsess näeb välja järgmine:
$ gdb --args ./exe argument1 argument2
Piisavalt lihtne. Seejärel saate viipast (gdb)
anda käsu run
(lühike r
), et käivitada ./exe
käsureal antud parameetritega. Ma eelistan seda meetodit, kuid teie läbisõit võib erineda.
GDB viip
GDB käivitamine ja (gdb)
viip, kasutage binaarkaardi laadimiseks käsku file
ja seejärel käsku run
, et käivitada see argumentidega, mille soovite edastada:
$ gdb (gdb) fail exe (gdb) run argument1 argument2
alternatiiv ülaltoodule oleks set args
-i kasutamine järgmiselt:
$ gdb (gdb) fail exe (gdb) set argumendid argument1 argument2 (gdb) käivitamine
Saate vaadake igal juhul ka seda, millised argumendid run
edastaksid alustatud protsessile, väljastades järgmise:
(gdb) show args
btw: kui mõtlesite keskkonnamuutujate kohta, kasutage GDB sisseehitatud käsku help
kui help set
ja help show
. Kursorid: seatud keskkond VARNAME = VALUE
ja näitavad keskkonda [VARNAME]
ja keskkonna VARNAME seadistamata
.
Phew, aga miks kas programm peatub SIGSEGV
-ga (segmendi viga)?
Noh, me ei tea seda veel, kuid tundub, et see väike metsaline tahab õiget ravi . Kuna me harjutame kaitsvat arvutust, ei taha me käivitada midagi, millest me ei tea palju, eks? Alustame siis otsast peale. Kui see oleks olnud pahavara, peame masina loputama ja hetktõmmise uuesti installima või taastama, kui see on VM-i külaline.
Kõigepealt tahame käivitada käsu info
järgmiselt:
(gdb) infofail
Jälgige:
On kaks olulist teavet, meie jaoks kõige olulisem on rida:
Sisestuspunkt: 0x400710
olgu, nii et saame sellele ühele seada katkestuspunkti ja seejärel käivitada
protsessi soovitud argumentidega.
.gdbinit
võit
Aga oota, see muutub juba tüütuks. Pole ühtegi lihtsat meetodit nende sammude mingil moel automatiseerimiseks? Tegelikult on. Faili nimega .gdbinit
saab käivitamisel GDB-le käske anda. Faili saate edastada ka GDB-käskudega, kasutades (shell) käsureal olevat argumenti -x
. Kui mul on arvukalt projekte, asuvad need tavaliselt alamkaustades, kus iga fail on .gdbinit
.
Side-note: -nx
takistab .gdbinit
sisu käivitamisel käivitamist.
Nii et me teame, milliseid argumente soovime edastada, ja teame katkestuspunkti aadressi, see tõlgib järgmisele failile .gdbinit
:
file exebreak * 0x400710run argument1 argument2
Väljund, mille saan, kui käivitan gdb
ilma muude argumentideta on järgmine:
Breakpoint 1 at 0x400710Breakpoint 1, 0x0000000000400710 in ?? () (gdb)
Tore! Kuid see näeb välja teisiti ...
Assamblee ja GDB
Nii et olete harjunud nägema järgmist rida, mida kavatsete täita, ja siis oma usaldusväärset vana (gdb )
viip. Aga pole midagi sellist. Selle binaarse ja lisaks sümbolite jaoks pole meil allikat. Doh! Niisiis mõtiskleme vilkuva märkega (gdb)
-viibel ja mõtleme, mida teha. Ärge pahandage, GDB saab hakkama ka koostekoodiga. Ainus probleem, vaikimisi on see minu arvates ebamugav AT&T assamblee süntaks. Eelistan Inteli maitset ja järgmine käsk käsib GDB-l just seda teha:
(gdb) set demonteerimismaitse intel
Assamblee koodi kuvamine
Ja kuidas see meile monteerimiskoodi näitab? Noh, sarnaselt TUI-režiimiga (kontrollige märgendiviki gdb), kasutades järgmist käsku:
(gdb) layout asm
ja kui olete nii kaldunud, siis ka:
(gdb) paigutusregistrid
mis näitavad teile ka registrisisu ülevaade.
Käivitame selle uuesti
Nii et lõpuks on meie jaoks järgmine .gdbinit
:
fail exebreak * 0x400710set demonteerimine-maitse intellayout asmlayout regsrun argument1 argument2
Ja kui alustame gdb
argumentideta, jõuame lõpuks selleni:
Armas. Nii et koodist läbi astudes näeme demonteerimist. Võiksime selle järeldada siin, kuid muidugi on veel rohkem trikke, mida õppida, miks mitte minna natuke kaugemale.
MÄRKUS: valge / halli taustaga registrid näitavad, et väärtus on muutunud. Mitte liiga sisukas, kui me just programmi käivitasime, kuid võib-olla on kasulik hiljem koodi läbimisel.
btw, kui eelistate ekraaniomandi salvestamist
... ja teil on seda vähem visuaalne, alates GDB 7.0-st saate kasutada: automaatne kuva
:
display / i $ pc
või lühem disp / i $ pc
kus vorming / i
, saate seda kõige paremini meelde jätta, kui mõelda "juhend" ja $ pc
olla käskude kursor, tuntud ka kui programmiloendur - seega pc
.
Samuti on hea teada
Mõnikord võib kokkupanekul astudes regs
ja asm
vaated häiritakse. Vana hiilguse taastamiseks täitke lihtsalt vastavad käsud layout
uuesti:
(gdb) layout asm (gdb) layout regs
"Silumine" koostetasandil
Selgub, et kui olete monteerimisrežiimis, siis mõned käsklused, millega olete lähtekoodi silumisel harjunud, lihtsalt ei tööta. See on mõistlik, sest üks allikarida tähendab tavaliselt tosinat või enamat juhist. Käskudel next
ja step
on aga käsutaseme vasted:
-
nexti
(lühikirje ni
... keegi teine mõtleb võsastamisest?) -
stepi
(shorthand si
)
Ülaltoodud lahtivõtmise põhjal teame:
0x40072d mov rdi, 0x40f961
ja kõigil praktilistel eesmärkidel on see funktsioon main
. Muidugi, kui peaksite pahavara tagasi töötama, peaksite olema ettevaatlikum, kuid antud juhul on see nii. Lisame sellele aadressile katkestuspunkti ( 0x40f961
) sisestuspunkti asemel:
break * 0x40f961
Kui me uurige
(lühike x
) koodi, kus me praegu oleme, näeme:
(gdb) x / 5i $ pcx / 5i $ pc = > 0x40f961: push rbp 0x40f962: mov rbp, rsp 0x40f965: mov eax, 0x0 0x40f96a: call 0x40911f 0x40f96f: pop rbp
Olgu, kõne
on see, mida me tahame järgida, seega astume sellest sisse, kasutades si
. Funktsiooni sisestamisel näeme kohe käskude kursori juures uut kõnet
:
(gdb) x / 5i $ pcx / 5i $ pc = > 0x40911f: call 0x400b8c 0x409124: push rbp 0x409125: mov rbp, rsp 0x409128: push r10 0x40912a: push r11
call
viib meid funktsiooni juurde, mis kutsub ptrace (PTRACE_TRACEME, ...) , miks ta seda siis teeks?
0x400bab kõne 0x4006b8 <ptrace @ plt>
Noh, see on vana silumisvastane trikk, mida Mellowcandle on kirjeldanud siin teises Q&A-s:
Aga kuidas me sellest mööda saame? Peaksime call
üle kirjutama funktsioonile, mis kutsub ptrace ()
välja koodiga nop
või millelgi muul.
Siin muutub GDB veidi kohmakaks. Kuid me võime kasutada set
, nii et tehke seda ka meie jaoks. Kontrollime kõigepealt käskude baidid:
(gdb) x / 10b $ pcx / 10b $ pc0x40911f: 0xe8 0x68 0x7a 0xff 0xff 0x55 0x48 0x890x409127: 0xe5 0x41
0xe8
on kõnejuhis ja me teame nüüd, et see on 5 baiti pikk. Nii et laseme selle nop
välja. ( x / 10b $ pc
tähendab programmi loenduri 10 baiti uurimist - vaikevorming on juba kuusnurkne).
Nii et me teeme peatudes 0x40911f
:
(gdb) set write (gdb) set {unsigned int} $ pc = 0x90909090 (gdb) set {unsigned char} ($ pc + 4) = 0x90 (gdb) seatud mahakandmine
ja kinnitage lappitud asukoht:
(gdb) x / 10i $ pcx / 10i $ pc = > 0x40911f: nop 0x409120: nop 0x409121: nop 0x409122 : nop 0x409123: nop 0x409124: push rbp 0x409125: mov rbp, rsp 0x409128: push r10 0x40912a: push r11 0x40912c: push rbx
Suurepärane. Nüüd saame selle täita.
Alternatiivid antud meetodile
- alternatiiv lappimiseks:
set {unsigned int} 0x40911f = 0x90909090
, millele järgneb set {unsigned char} 0x409123 = 0x90
- Selle asemel saate manipuleerida programmiloenduriga (käsuosuti):
-
set $ pc + = 5
või selgesõnalisem set $ pc = $ pc + 5
-
jump * $ pc + 5
Paremad viisid veel töötava programmi manipuleerimiseks / lappimiseks
On ka alternatiivseid (ja palju paremad) meetodeid, näiteks see on Tavis Ormandy poolt. Kopeerin allolevat makrot assemble
(juhul kui see läheb teisest kohast võrguühenduseta):
define assemble
# ärge sisestage rutiini uuesti, kui kasutaja tabab sisestusklahvi, ärge korrake, kui ($ argc) kui (* $ arg0 = * $ arg0) # kontrollige, kas meil on kehtiv aadress, viidates sellele alla, # kui meil pole, põhjustab see rutiini väljumine. end printf "Juhised kirjutatakse aadressile% # x. \ n", $ arg0 else printf "Juhised kirjutatakse stdouti. \ n" end printf "Sisestage juhised, üks rea kohta. \ n" printf "Lõpeta reaga öeldes just \ "end \". \ n "kui ($ argc) # argument on määratud, koondage juhised määratud aadressil mällu # shell nasm -f bin -o / dev / stdout / dev / stdin \ <<< "$ (kaja" BITS 32 "; lugedes -ep '>' r && test" $ r "! = end; \ do echo -E $ r "; valmis)" | hexdump -ve \ '1/1 "komplekt * ((märkimata märk *) $ arg0 +% # 2_ax) =% # 02x \ n"' \ > ~ / .gdbassemble # laadige fail, mis sisaldab komplekti juhiste allikat ~ / .gdbassemble # kõik tehtud. shell rm -f ~ / .gdbassemble else # no argument, installi käsud stdout shell nasm -f bin -o / dev / stdout / dev / stdin \ <<< "$ (kaja" BITS 32 "; lugedes -ep '>' r && test "$ r"! = lõpp; \ tee kaja -E "$ r"; valmis) "| ndisasm -i -b32 / dev / stdin endenddocument assembleKomplekteeri juhised nasmi abil. Lõpule viitamiseks tippige rida, mis sisaldab lõppu. Kui aadress on määratud, sisestage sellele aadressile juhised. Kui aadressi pole määratud, prinditakse kokkupandud juhised stdout. Põhiaadressi määramiseks kasutage pseudokäsku "org ADDR". end
Jällegi ei kirjutanud ülaltoodud skripti koodilõik mitte minu, vaid Tavis Ormandy - vaadake ülaltoodud linki.
See lõpetab selle väikese Q&A.