.. _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.