Inżynieria wsteczna, część 2 — analiza dynamiczna

Data: 17.11.2020, 19.11.2020

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: bsk01

Zadanie: znaleźć poprawne hasło do programu.