Tól til kvikrar greiningar
Kvik greining (e. dynamic analysis) felst í að skoða forrit á meðan það er í keyrslu.
GDB (GNU Debugger)
GDB er helsta kembiforritið á Linux. Með því getum við:
- Sett rofstaði sem stöðva keyrslu á ákveðnum stöðum.
- Skoðað minni og gista á meðan keyrslan stöðvast.
- Keyrt eina vélarskipun í einu.
GDB-viðbætur
GDB er öflugt en getur verið óaðgengilegt í grunnuppsetningu. Þrjár vinsælar viðbætur bæta við yfirliti yfir gista, stafla og kóða:
| Viðbót | Uppsetning |
|---|---|
| pwndbg | https://pwndbg.re/stable/setup/ |
| GEF | https://hugsy.github.io/gef/install/ |
| peda | https://github.com/longld/peda#installation |
Í þessum leiðbeiningum notum við pwndbg en skipanirnar eru að mestu þær sömu í öllum þremur viðbótunum.
Grunnskipanir
gdb ./challenge # Opnar forrit í GDB
Innan GDB:
| Skipun | Flýtilykill | Lýsing |
|---|---|---|
run |
r |
Keyrir forritið |
run < input.txt |
Keyrir með inntaksskrá | |
break main |
b main |
Setur rofstað á main |
break *0x401234 |
b *0x401234 |
Setur rofstað á vistfang |
continue |
c |
Heldur áfram eftir stöðvun |
next |
n |
Keyrir að næstu frumkóðalínu án þess að fara inn í föll |
step |
s |
Keyrir að næstu frumkóðalínu og fer inn í föll |
nexti |
ni |
Keyrir eina vélarskipun án þess að fara inn í kallað fall |
stepi |
si |
Keyrir eina vélarskipun og fer inn í kallað fall |
finish |
Keyrir þar til núverandi fall lýkur | |
attach <pid> |
Tengist ferli sem er í keyrslu | |
set follow-fork-mode child |
Fylgir dótturferli þegar fork() er kallað |
|
quit |
q |
Hættir úr GDB |
Skoða minni og gista
# Skoða gista
info registers # Sýnir alla gista
print $rax # Sýnir gildi rax
print/x $rax # Sýnir í sextándakerfi
# Skoða minni með x skipuninni: x/[fjöldi][stærð][snið] [vistfang]
# Stærð: b=1 bæti h=2 w=4 g=8
# Snið: x=sextándakerfi d=tugakerfi s=strengur i=skipanir
x/10gx $rsp # tíu átta bæta gildi við rsp (sextándakerfi)
x/s 0x402000 # Strengur á vistfangi
x/20i $rip # Næstu 20 smalamálsskipanir
# Sérstakar pwndbg-skipanir
telescope $rsp # (einnig: tele) Sýnir staflann á læsilegan hátt
disassemble main # Sýnir smalakóða falls
# Minniskort forritsins
info proc mappings # Sýnir hvaða svæði eru hlaðin (stafli, hrúga, libc o.fl.)
Breyta minni og gildum í gistum
set $rax = 0 # Setur rax í 0
set *0x404000 = 0x1234 # Skrifar á vistfang
Dæmi: Greining á lykilorðsathugun
gdb ./challenge
b strcmp # Stöðva þegar strcmp er kallað
r # Keyra
# ... forritið biður um lykilorð, slá inn 'test' ...
# Kembiforritið stöðvast við strcmp
x/s $rdi # Skoða 1. frumbreytu (inntakið okkar)
x/s $rsi # Skoða 2. frumbreytu (rétta lykilorðið)
ltrace
ltrace hlerar köll í bókasafnsföll á meðan forritið keyrir.
ltrace ./challenge
Dæmi um úttak:
puts("Sláðu inn lykilorðið: ") = 23
fgets(0x7ffd..., 64, stdin) = 0x7ffd...
strcmp("test\n", "swordfish\n") = -1
puts("Rangt lykilorð") = 15
Tip
ltrace sýnir okkur oft lykilorð og samanburðargildi beint. Því er gagnlegt að prófa ltrace ./challenge snemma í greiningunni.
strace
strace hlerar kerfisköll, það er samskipti forritsins við stýrikerfið.
strace ./challenge
strace -e openat,read,write ./challenge # Sía eftir tegund kalls
Gagnlegt til að sjá:
- Hvaða skrár forritið les
- Nettengingar
- Minnisúthlutun
perf: hliðarrás byggð á vinnumagni
perf getur talið skipanir sem örgjörvinn framkvæmir. Þetta má nota í verkefnum sem byggja á hliðarrás tengdri vinnumagni, þar sem forritið vinnur meira eftir því sem fleiri stafir eru réttir.
Segjum að við þekkjum fyrstu stafina í fánanum, til dæmis gg{. Þá getum við prófað jafn langan rangan streng og kannað hvort munur sé á fjölda skipana.
perf stat -e instructions ./challenge <<< "gg{AA"
perf stat -e instructions ./challenge <<< "AAAAA"
Ein mæling getur verið villandi vegna breytileika milli keyrslna. Við endurtökum því hverja ágiskun að minnsta kosti tíu sinnum við sambærilegar aðstæður og berum saman miðgildi skipanafjöldans:
for i in {1..10}; do
perf stat -x, -e instructions ./challenge <<< "gg{AA"
done
Ef munurinn helst greinilegur yfir endurteknar mælingar getum við fundið afganginn af fánanum, einn staf í einu. Bakgrunnsálag, slembni og mismunandi upphafsstilling forritsins geta annars falið eða líkt eftir merkinu sem við leitum að.
Að breyta tvíundaskrá
Stundum er einfaldast að breyta tvíundaskránni beint í stað þess að skilja allt reikniritið.
Dæmi: Breyta hoppi
Ef forritið gerir jne wrong_path þar sem við viljum alltaf fara í correct_path:
# Með radare2
r2 -w ./challenge # -w = skrifhamur
s 0x401234 # Hoppa á vistfang jne-skipunarinnar
pd 1 # Athuga kóðun og lengd skipunarinnar
wx 9090 # Aðeins ef pd sýnir tveggja bæta stutt jne-stökk
Hér gerum við ráð fyrir að correct_path sé leiðin sem keyrslan heldur áfram eftir ef stökkið er ekki tekið. Stutt jne-stökk er tvö bæti en nærstökk (e. near jump) er oft sex bæti. Við megum því ekki afrita wx 9090 blint: fyrst staðfestum við lengd skipunarinnar og skrifum nákvæmlega jafnmörg NOP-bæti. Best er að vinna á afriti af tvíundaskránni.
# Með Python og pwntools
from pwn import *
elf = ELF('./challenge')
elf.asm(0x401234, 'jmp 0x401260') # Breyta skipun á vistfangi
elf.save('./challenge_patched')
Í Binary Ninja getum við hægrismellt á línu og valið Patch. Best er að gera það í smalamálsýninni.
Að fella út skipanir með NOP
NOP (e. No Operation, aðgerðarkóði 0x90) breytir engu og keyrslan heldur áfram í næstu skipun. Við getum stundum notað NOP-skipanir til að fella út athuganir. Við verðum þó að skrifa jafn mörg bæti og upprunalega skipunin tók, svo við skemmdum ekki næstu skipun.
; Upprunalegt
0x401234: cmp rax, rbx
0x401237: jne fail ; 75 27
0x401239: ... ; rétta leiðin
; Eftir breytingu
0x401234: cmp rax, rbx
0x401237: nop ; 90
0x401238: nop ; 90
0x401239: ... ; keyrslan heldur alltaf áfram hér
Samanburðurinn er enn keyrður en niðurstaða hans hefur ekki lengur áhrif. Óháð því hvort gildin eru jöfn heldur keyrslan áfram inn á réttu leiðina.
Æfingar
Verkefni til að æfa kvika greiningu, GDB og breytingar á tvíundaskrám:
- Wiretap: Við fylgjumst með hvaða bókasafnsföll forritið kallar á meðan það keyrir.
- Disappearing Ink: Við ákveðum hvar stöðva eigi keyrslu til að skoða gildi sem eru aðeins til staðar tímabundið.
- Tumblers: Við könnum hvort breytingar á inntaki hafi mælanleg áhrif á keyrslu forritsins.
- Detour: Við greinum stýriflæði forrits og skoðum áhrif breytinga á tvíundaskránni.