.. _re2: ================================================= InĹźynieria wsteczna, czÄĹÄ 2 â analiza dynamiczna ================================================= Data: 17.11.2020, 19.11.2020 .. toctree:: .. contents:: NarzÄdzia ========= - gdb (debugger) - Python 3 ObsĹuga gdb ----------- Uruchomienie gdb:: gdb ./program NajwaĹźniejsze polecenia: - ``help``: listuje polecenia - ``help <nazwa polecenia>``: pokazuje dokumentacjÄ do polecenia - ``break nazwa_funkcji``: ustawia breakpoint na danÄ funkcjÄ - ``break *0x12345``: ustawia breakpoint na dany adres - ``hbreak nazwa_funkcji``: ustawia breakpoint na danÄ funkcjÄ uĹźywajÄ c breakpointa sprzÄtowego - ``attach <pid>``: wpina debugger w juĹź uruchomiony program - ``run``: uruchamia program - ``run abc def``: uruchamia program z argumentami - ``run <wejscie >wyjscie``: uruchamia program z przekierowanym wejĹciem / wyjĹciem - ``start``: jak ``run``, ale zatrzymuje program na funkcji ``main`` (nie dziaĹa, jeĹli nie mamy symboli) - ``p wyraĹźenie``: drukuje wartoĹÄ wyraĹźenia (skĹadnia jak w C), oprĂłcz funkcjonalnoĹci jÄzyka C moĹźna rĂłwnieĹź uĹźywaÄ wartoĹci rejestrĂłw (np. piszÄ c ``$rax``) - ``p/x wyraĹźenie``: drukuje wartoĹÄ wyraĹźenia jako liczbÄ szesnastkowÄ - ``x/20bx adres``: pokazuje zawartoĹÄ pamiÄci pod danym adresem, jako 20 bajtĂłw w systemie szesnastkowym - ``x/10gx adres``: jak wyĹźej, ale jako 10 liczb 64-bitowych - ``x/10i adres``: disassembluje 10 instrukcji pod danym adresem - ``x/s adres``: pokazuje null-terminated string pod danym adresem - ``display``: dziaĹa dokĹadnie jak ``x``, ale wykonuje siÄ samo po kaĹźdym wykonaniu kodu (w szczegĂłlnoĹci polecam ``display/20i $pc``, aby mieÄ podglÄ d wykonywanych instrukcji) - ``c``: kontynuuje wykonanie aĹź do nastÄpnego breakpointa - ``si``: wykonuje jednÄ instrukcjÄ procesora - ``ni``: jak ``si``, ale w przypadku instrukcji ``call`` wykonuje caĹÄ funkcjÄ na raz - ``s``: wykonuje jednÄ linijkÄ kodu ĹşrĂłdĹowego (nie dziaĹa, jeĹli program nie byĹ skompilowany z informacjami dla debuggera) - ``n``: ma siÄ do ``s`` jak ``ni`` do ``si`` - ``fin``: wykonuje program aĹź do momentu, gdy aktualna funkcja siÄ zakoĹczy - ``bt``: pokazuje stos wywoĹaĹ funkcji - ``frame <numer ramki z bt>``: ustawia kontekst na danÄ ramkÄ ze stosu wywoĹaĹ (ewaluacja wyraĹźeĹ bÄdzie uĹźywaĹa zmiennych z danej ramki) - ``set <zmienna lub $rejestr> = <wyraĹźenie>``: ustawia wartoĹÄ zmiennej bÄ dĹş rejestru - ``kill``: przerywa debugowany program (przydatne, jeĹźeli nieodwracalnie go popsuliĹmy) - ``Ctrl-C`` gdy program jest wykonywany: zatrzymuje wykonanie tak, jakby zostaĹ trafiony breakpoint PeĹna dokumentacja: https://sourceware.org/gdb/current/onlinedocs/gdb/ Mechanizm LD_PRELOAD ==================== Przy analizie dynamicznej przydatna bywa moĹźliwoĹÄ doĹÄ czenia wĹasnego kodu do istniejÄ cego programu. SĹuĹźy do tego mechanizm ``LD_PRELOAD`` â jest to zmienna Ĺrodowiskowa, ktĂłrÄ moĹźemy ustawiÄ na ĹcieĹźkÄ do naszej biblioteki wspĂłĹdzielonej. Przy uruchamianiu dowolnego programu zlinkowanego dynamicznie ta biblioteka bÄdzie automatycznie Ĺadowana razem z programem. W takiej bibliotece moĹźemy: - definiowaÄ wĹasne funkcje (ktĂłre bÄdÄ przesĹaniaÄ funkcje z biblioteki systemowej) - jeĹli chcemy uĹźyÄ przesĹonionych funkcji, moĹźemy uĹźyÄ ``dlsym`` z ``RTLD_NEXT`` - wykonaÄ dodatkowy kod przed startem programu, prze zawarcie go w funkcji bez argumentĂłw oznaczonej przez ``__attribute__((constructor))`` PrzykĹad: zeszĹoroczne zadanie zaliczeniowe =========================================== Program: :download:`bsk01` Zadanie: znaleĹşÄ poprawne hasĹo do programu. .. 1. Ĺadujemy do programu GHIDRA .. 2. Widzimy, ze hasĹo musi mieÄ 29 znakĂłw i jest sprawdzane przez funkcjÄ ``check`` .. 3. PrĂłbujemy zdekompilowaÄ funkcjÄ ``check`` i ze smutkiem stwierdzamy, Ĺźe jej kod nie ma Ĺźadnego sensu .. 4. ZauwaĹźamy, Ĺźe funkcja ``decrypt`` operuje na kodzie funkcji ``check`` .. 5. UĹźywamy debuggera, by zdobyÄ rozszyfrowany kod funkcji ``check`` z pamiÄci: .. .. - Ĺadujemy program pod gdb .. - stawiamy breakpoint na funkcji: ``break check`` .. - uruchamiamy program wpisujemy losowe 29-znakowe hasĹo .. - prĂłbujemy przejĹÄ przez funkcjÄ ``check`` w debuggerze, ale dostajemy wyjÄ tek, spowodowany przez uĹźycie software breakpointu (i uĹźycie kodu instrukcji breakpoint zamiast zaszyfrowanych danych) .. - prĂłbujemy jeszcze raz, tym razem przez ``hbreak check`` .. - tym razem kod zostaje odszyfrowany poprawnie .. - zrzucamy kod funkcji do pliku: ``dump memory check.bin 0x4040f0 0x404170`` .. .. 6. UĹźywamy ``readelf -a bsk01``, Ĺźeby dowiedzieÄ siÄ, jakiej pozycji w pliku odpowiada adres ``0x4040f0`` (funkcja ``check``) â jest to ``0x30f0`` .. 7. UĹźywamy Pythona, by podmieniÄ kod funkcji ``check`` na rozszyfrowanÄ wersjÄ:: .. .. with open('bsk01', 'rb') as f: .. orig = f.read() .. with open('check.bin', 'rb') as f: .. patch = f.read() .. .. data = bytearray(orig) .. data[0x30f0:0x3170] = patch .. .. with open('bsk01-patched', 'wb') as f: .. f.write(data) .. .. 8. Ĺadujemy tÄ wersjÄ do programu GHIDRA i dowiadujemy siÄ, jak dziaĹa ``check`` .. 9. Piszemy kod do Ĺamania hasĹa metodÄ brute force:: .. .. #include <stdio.h> .. int *correct_pass = 0x404060; .. int *key = 0x4040e0; .. void (*decrypt) (int *, int *) = 0x4011e0; .. __attribute__((constructor)) void h4x(void) { .. for (int i = 0; i < 0x1d; i++) { .. for (int j = 32; j < 128; j++) { .. int tab[2] = { .. j, 0xfeed .. }; .. decrypt(tab, key); .. if (tab[0] == correct_pass[i]) { .. putchar(j); .. } .. } .. } .. putchar('\n'); .. } .. .. 10. Kompilujemy kod i Ĺadujemy go do pamiÄci naszego programu:: .. .. gcc -shared -fpic h4x.c -o h4x.so .. LD_PRELOAD=./h4x.so ./bsk01 .. .. 11. Otrzymujemy poprawne hasĹo.