x86_64 utasítások keresése binárisban adott szimbólum címéhez képest

Pythonban, vagy egyszerűen parseolható cmd utility-ben keresnék megoldást a következő feladatra.

Van egy nagy binárisom, lehet az library, futtatható fájl, maga a vmlinux is. Ebben szeretném megkeresni egyszerűen egy szombólum címét, pl. a func_d nevű függvény címét:

objdump --prefix-addresses --disassemble=func_d a.out

0000000000001169 <func_d> endbr64 
000000000000116d <func_d+0x4> push   %rbp
000000000000116e <func_d+0x5> mov    %rsp,%rbp
0000000000001171 <func_d+0x8> lea    0xe8c(%rip),%rdi        # 0000000000002004 <_IO_stdin_used+0x4>
0000000000001178 <func_d+0xf> callq  0000000000001060 <puts@plt>
000000000000117d <func_d+0x14> nop
000000000000117e <func_d+0x15> pop    %rbp
000000000000117f <func_d+0x16> retq 

Az után pedig szeretném valahogy kihalászni az offsetet, ahol már jól van beállítva a frame. A fenti példában ez a 0x8, ahol már a stack pointer be lett rakva base pointernek. Sajnos ez az offset amiket néztem függvények, azokban más és más, van ahol előtte még csinál pár másik utasítást is. Létezik erre valami egyszerű python lib, vagy jól paraméterezett util? Minél robosztusabb a megoldás annál jobb, végső esetben megpróbálom valahogy manuálisan megkeresni vagy kiszedni objdump kimenetéből ha valaki megerősít benne hogy nem lehet nagy baj belőle/nem lövöm lábon magam.

Előre is köszönöm!

Hozzászólások

Szerkesztve: 2020. 07. 27., h - 10:53

Mi a cel?

Fentebb emlitett radare 2-vel el lehet hulyeskedni, esetleg meg manualis kereseshez a Zydis jut eszembe, aminek van valamilyen allapotban levo Python tamogatasa.

Viszont, ha te csak meg akarod hookolni a fuggvenyt; kiolvasni/modositani a bemenetet/kimenetet akkor erre vannak kesz eszkozok, pl. a Frida.

Szerk.: Anno meg ez (Angr) jutott eszembe, viszont ezt soha nem hasznaltam, de ez legalabb Python.

Nem szeretném módosítani a függvényt, csak tracinghez kellene ez. perf trace (DWARF nélkül), BCC trace.py és bpftrace képes uprobe-ot rakni egyes füvvényekbe és ha meghívódik a függvény akkor képes kiírni a callstacket. Viszont mindegyik tool hibásan írja ki, mert az uprobe-ot default 0x0 offsetre tehát a függvény legelejére rakják be. Így aki a függvényt hívja, az nincs a callstackben, csak az azt megelőzőek.

Annyit szeretnék elérni, hogy egy script vagy kis tool kikeresi azt az offsetet ahol ez a fenti regiszter már be van állítva és oda tölteném be az uprobe-ot. Manuálisan kiválóan működik, objdumpból látom az offsetet, hozzáadom a függvény kezdőcíméhez és varázsütésre megjavul a kiiratott stack. Viszont ez egy nagyobb tool egyik része lenne, ahol nem tudom manuálisan megoldani az offsetek kikeresését. 

Igy mar vilagos. Mostansag nem tul sokat linuxozok, ugyhogy uprobe-hoz nem ertek, de igy felkerult meg egy dolog a listamra. :-)

Nekem osszessegeben az jon le, hogy valami olyan megoldas lenne a legjobb, amit programba tudsz integralni es nem hivogatsz kulso scriptet/stb, de javits ki ha tevednek. Ezert arra tudok gondolni, hogy Zydis vagy valami hasonlo libbel megkeresheted a stack frame-et beallito utasitasokat, esetleg csak a byte pattern-t, a kezdocimhez kepest.  Ha mas nem akkor marad az objdump hivogatasa es parsolas. Szerk.: vagy mondjuk a fentebb emlitett radare2-vel kommunikalsz.

En pl. valami olyasmit probalnek, hogy az elso 10-20 utasitast nezze meg, hogy ott van-e frame setup. Viszont annyibol problemas lehet ez, hogy bizonyos compiler opcioknal (-fomit-frame-pointer) nem lesz beallitva a stack.

Ami erre eleg jo heurisztika lehet, hogy ha kereses kozben RET/CALL-t talal akkor ott meg is allhat rogton, nem kell vegignezni az elso N utasitast. Mivel RET-nel biztosan nem tortent semmilyen setup, pl. nagyon rovid fuggvenyek eseteben. CALL eseten pedig valoszinuleg mar tovabbhaladt es nem is lesz. JMP-t lekezelni nehez, de altalaban stack frame setup elott nem szokott lenni ugras, de ha megis van, akkor emiatt nem biztos, hogy jol le fogja kezelni az adott fuggvenyt.

Szerintem a legtisztabb az lenne, hogy fogsz 10+ fuggvenyt es leteszteled mindegyiken: kigeneralni az offset tablazatot es hivogatni a uprobe-ot.

Köszönöm a tippeket!

Igen, az -fomit-frame-pointer lehet gond, viszont ahogy mondod, RET-ig nézné csak és ha nincs akkor beszúrja 0x0-ra. Amiben nem vagyok biztos, hogy egyáltalán képes-e oda uprobe-ot rakni, ha nincs frame pointer beállítás. Mert ezek a trace toolok elhasalnak, ha mondjuk rossz offsetre akarok uprobe-ot rakni (unaligned vagy adat rész) így ezt a részét legalább nem kell már lekezelnem. 

Megírom majd itt mire jutottam, köszönöm mindenkinek a tippeket. 

Meg annyi jutott eszembe a tracingrol, hogy a Frida es hasonlo eszkozok tudnak altalaban stack tracet is generalni es a Fridanal maradva, mert en azt hasznaltam, tetszoleges cimen levo fuggvenyt is meg tud hookolni. Utana pedig kiiratod a stack tracet anelkul, hogy barmi mast modositanal. Azert irom, mert hatha jol jon, akar teszteleshez, attol fuggoen, hogy vegul mennyit akarsz vele foglalkozni.

Szerkesztve: 2020. 07. 28., k - 06:43

Én anno ddd-vel dibagoltam linux alatt. Létezik még?

 

ps: most nézem, slack_current alatt létezik. az mintha tudna ilyesmit, de nem esküszöm meg rá, rég volt.

> Sol omnibus lucet.

Igen, bár a DDD igazából csak egy (nagyon ősi widget rendszerrel) GDB fölé implementált frontend. És igen, a GDB az jól mutatja a stacket, mert az valahogy megcsinálja az binary analízist meg tudja rendesen kezelni a debug infót. De az sajnos más mint ami nekem kellene ebben a helyzetben, eléggé nehézsúlyú megoldás :(

Ha van szimbólumtábla (pl. a vmlinuxban is van, de kódoltan), akkor megvan a cím. Ha megvan a cím, akkor egyszerű pattern kereséssel megkeresed a veremkeretet létrehozó utasítást. Az utasítás utáni offset kell neked.

Ha nincs szimbólumtábla, akkor ida-zni kell egy kicsit, hogy megtaláld a függvényt.

Esetleg megpróbálhatod azt, mint amit pl. a themida/winlicence, vmprotect stb. toolok alkalmaznak. A forrásba, közvetlenül a függvény elejére beraksz egy olyan bátjpatternt, amit biztosan nem generál a fordító. Ezt keresed, majd foltozod, ahogy szeretnéd.

Köszönöm de más a use-case. Az offsetet meg tudom keresni manuálisan (ezt fentebb írtam) csak ezt szeretném automatizálni. IDA az nem is tudom van-e linuxra egyáltalán. Foltozni nem lehetséges, mert szeretném tracelni konkrétan az oprendszert is és ha ott elkezdenék valamit az én hozzáértésemmel livepatchelni az abban a pillanatban elcrashelne :) A kprobe megcsinálja ami nekem kell, csak az offset kitalálása érdekes feladat.

Használhatsz kész opkód értelmezőt, hogy megtudd az egyes utasítások bátjhosszát vagy kereshetsz fix bit/nibble/bájtpatterneket. Az első esetben (kész opkód értelmező) az adott utasítás bájthossza azonnal rendelkezésedre áll, de magát az utasítást úgymond értelmezned kell. Pl. igen, ez az az utasítás, ami rbp-t beállítja.
Ezért én inkább az egyszerűbb bit/nibble/bátjpatternes keresést alkalmaznám. Ehhez megnézném 10-15 függvény prológusát, hogy lássam, hogy az adott bináris esetén a compiler milyen konvenciót alkalmazott. Majd venném a lehetséges konkrét utasításokat azonosító patterneket (pl. mov rsp,rbp) és azt keresném. Az utasítás hossza így ismert, így ismerem a keresett offszetcímet is.