Debugowanie za pomocą IDA Pro

https://chacker.pl/

IDA Pro zawiera solidną obsługę debugowania, zarówno lokalną, jak i zdalną. Lokalne debugowanie jest obsługiwane przez platformy, na których zainstalowany jest IDA Pro, w tym macOS, Linux i Windows. Zdalne debugowanie jest obsługiwane na różnych platformach, w tym iOS, XNU, BOCHS, Intel PIN, Android i innych. W tej sekcji skupimy się na przykładzie zdalnego debugowania przy użyciu serwera GDB działającego na docelowej maszynie wirtualnej Kali Linux i IDA Pro działającego na maszynie wirtualnej z systemem Windows 10. IDA Pro zawiera wiele fragmentów zdalnego debugowania, które można skopiować do wybranego systemu docelowego, w którym aplikacja ma być debugowana. Przejdźmy od razu do przykładu zdalnego debugowania za pomocą IDA Pro. W tym przykładzie używamy skompilowanej wersji programu myProg dostarczonej w folderze ~/GHHv6/ch05, po uprzednim sklonowaniu repozytorium Git Gray Hat Hacking 6th Edition. To nie jest laboratorium; jeśli jednak chcesz kontynuować, ten program jest potrzebny w systemie, w którym zainstalowany jest IDA, a także w docelowym systemie Kali Linux. Wymagana jest łączność sieciowa pomiędzy systemem, na którym działa IDA Pro (debugger) a systemem, na którym działa program docelowy (debuggee), ponieważ serwer GDB nasłuchuje na wyznaczonym numerze portu TCP i oczekuje na żądanie połączenia. Poniższe polecenie uruchamia serwer GDB dla programu myProg i nakazuje mu nasłuchiwanie na porcie TCP 23946 w oczekiwaniu na połączenie przychodzące. Opcja –once kończy pracę Serwera GDB po zamknięciu sesji TCP, zamiast automatycznie uruchamiać ją od nowa.

Gdy serwer GDB działa w docelowym systemie debugowania, czas załadować program myProg do IDA Pro w systemie debugera. Pozwalamy IDA Pro przeprowadzić automatyczną analizę i wybrać opcję Remote GDB Debugger, jak pokazano na rysunku

Teraz klikamy Debuger | Opcje procesu z menu IDA Pro. Spowoduje to wyświetlenie okna dialogowego pokazanego na rysunku

Opcje aplikacji i pliku wejściowego są ustawione na folder lokalny, w którym znajduje się program myProg. Na przykład, gdybyśmy debugowali bibliotekę DLL załadowaną przez aplikację docelową, opcje aplikacji i pliku wejściowego byłyby inne. W opcji Hostname wprowadziliśmy adres IP docelowego systemu debugowanego. Domyślny numer portu to 23946, dlatego użyliśmy tej samej opcji w systemie docelowym z serwerem GDB. Po zaakceptowaniu tych opcji klikamy przycisk Odtwórz, pokazany na rysunku . Następnie pojawia się wyskakujące okienko z informacją: „Istnieje już proces debugowany zdalnie. Czy chcesz się do tego przywiązać?” Klikamy Tak, umożliwiając IDA Pro podłączenie się do zdalnego serwera GDB. Próba debugowania zakończyła się pomyślnie i po dołączeniu wstrzymuje wykonywanie, jak pokazano na rysunku

W oknie debugowania znajduje się kilka sekcji. Jeśli znasz inne debugery, sekcje powinny wyglądać znajomo. Główną i większą sekcją, zwaną IDA View-RIP, jest widok demontażu. Obecnie widzimy, że wskaźnik instrukcji (RIP) wskazuje adres pamięci zawierający instrukcję mov rdi, rsp. Większość sekcji w oknie debugowania można przewijać. Sekcja poniżej okna deasemblacji, zwana Hex View-1, zrzuca dowolny żądany segment pamięci w postaci szesnastkowej. Na prawo od widoku szesnastkowego-1 znajduje się widok stosu. Domyślnie zaczyna się od adresu wskaźnika stosu (RSP), zrzucając zawartość pamięci dla stosu bieżącego wątku. Nad sekcją Widok stosu znajdują się sekcje Wątki i Moduły. Wreszcie w prawym górnym rogu znajduje się sekcja Rejestry ogólne. W tej sekcji przedstawiono rejestry procesora ogólnego przeznaczenia, a także rejestry dodatkowe, w tym rejestr FLAGS i rejestry segmentowe. Elementy sterujące debugera są aktywowane za pomocą przypisanych skrótów klawiszowych, ikon menu na pasku wstążki lub przechodząc przez menu debugowania. Jeśli klikniemy Odtwórz, aby umożliwić kontynuowanie wykonywania programu, program po prostu się zakończy, ponieważ nie podaliśmy żadnych argumentów wiersza poleceń. Patrząc na tabelę Imports w tym programie, widzimy, że istnieje wywołanie przestarzałej funkcji strcpy, jak pokazano na rysunku

Następnie używamy przeglądarki zbliżeniowej do śledzenia ścieżki od funkcji głównej do strcpy, jak pokazano na rysunku

Patrząc na funkcję func1, możemy zobaczyć wywołanie strcpy, a także rozmiar bufora dla miejsca docelowego wynoszący 0x40, czyli 64 bajty. Następnie ustawiamy punkt przerwania w wywołaniu strcpy, jak pokazano na rysunku  klikając adres i naciskając klawisz skrótu F2 punktu przerwania.

Po ustawieniu punktu przerwania w funkcji strcpy i zrozumieniu rozmiaru bufora docelowego, podajmy 100 bajtów jako nasz argument, aby sprawdzić, czy uda nam się spowodować awarię procesu. Modyfikujemy naszą komendę gdbserver tak, aby zawierała na końcu składnię języka Python:

Następnie klikamy przycisk Odtwórz wewnątrz IDA, aby zainicjować połączenie z debugowanym narzędziem. Po podłączeniu musimy ponownie kliknąć przycisk Odtwórz, aby przejść do punktu przerwania funkcji strcpy. Wynik pokazano na rysunku .

Argument źródłowy został zrzucony do sekcji Widok szesnastkowy, dzięki czemu możemy zobaczyć, co zostanie skopiowane do bufora docelowego na stosie. Naciśnięcie klawisza skrótu Kontynuuj wykonywanie F9 w IDA powoduje oczekiwaną awarię, jak pokazano na rysunku

Z debugera pobierane są fragmenty, które pokazują ostrzeżenie o błędzie segmentacji, czyli wynik Ogólne Sekcja rejestrów i sekcja widoku stosu.

Uwagi

https://chacker.pl/

Powszechną praktyką jest dołączanie komentarzy do kodu podczas pisania aplikacji. Dzięki temu inni, którzy patrzą na Twój kod, zrozumieją Twój proces myślowy i znajdą się w tym samym kontekście. Jako autorowi ułatwia to również powrót do pełnej szybkości podczas otwierania kopii zapasowej bazy kodu. Ta sama praktyka dotyczy inżynierii odwrotnej. Patrzenie na deasemblowany lub dekompilowany pseudokod jest czasochłonną praktyką. IDA dodaje kilka komentarzy w oparciu o dostępne informacje o typie. Istnieje wiele rodzajów komentarzy, ale dwa z nich to najczęściej spotykane komentarze i komentarze powtarzalne. Aby dodać zwykły komentarz, kliknij żądaną linię demontażu i naciśnij klawisz dwukropka (:). Wpisz swój komentarz i kliknij OK. Przykład komentarza pokazano na rysunku . Według Hex-Rays powtarzalny komentarz jest „w zasadzie odpowiednikiem zwykłych komentarzy z jedną małą różnicą: są one powtarzane w dowolnym miejscu, które odnosi się do pierwotnej lokalizacji komentarza. Na przykład, jeśli dodasz powtarzalny komentarz do zmiennej globalnej, zostanie on wydrukowany w każdym miejscu, w którym znajduje się odwołanie do zmiennej.

Skróty

https://chacker.pl/

Istnieje wiele domyślnych skrótów i klawiszy skrótu, które nie są intuicyjne, np. naciśnięcie W w celu pomniejszenia i cyfry 1 w celu powiększenia do wcześniej określonego rozmiaru. Liczby 2 i 3 pozwalają na bardziej kontrolowane powiększanie i pomniejszanie. Skąd znamy te różne opcje? Kliknij Opcje | Skróty umożliwiające wyświetlenie okna sterującego tymi ustawieniami. Pokazano to na rysunku . Tutaj znajdziesz domyślne skróty klawiszowe, a także te, które zostały zmienione. Wartości domyślne mogą się różnić w zależności od wersji, np. IDA Pro, IDA Home i IDA Free

Kody operacyjne i adresowanie

https://chacker.pl/

Być może zauważyłeś w głównym widoku wykresu IDA, że kody operacji i adresowanie nie są domyślnie wyświetlane. Niektórzy analitycy uważają te informacje za rozpraszające i mogą zajmować niepotrzebne miejsce na ekranie, zwłaszcza w programach 64-bitowych. Dodanie tej informacji do wyświetlacza jest bardzo proste i odbywa się poprzez kliknięcie Opcje | Ogólny. Rysunek pokazuje zrzut ekranu tego menu na karcie Demontaż, gdzie zaznaczyliśmy opcję Line Prefixes (Graph) w widoku wykresu i ustawiliśmy pole Number of Opcode Bytes (Graph) na 10

Na rysunku może zobaczyć te informacje zawarte na wyświetlaczu. Możesz nacisnąć CTRL-Z, aby cofnąć te zmiany.

Przeglądarka zbliżeniowa

https://chacker.pl/

Funkcja przeglądarki zbliżeniowej, znana również jako przeglądarka zbliżeniowa (PV), jest przydatna do śledzenia ścieżek w programie. Według strony internetowej Hex-Rays: „Możemy użyć PV na przykład do wizualizacji pełnego wykresu wywołań programu, aby zobaczyć ścieżkę między 2 funkcjami lub do jakich zmiennych globalnych odwołuje się jakaś funkcja.”2 Na rysunku używamy przeglądarki zbliżeniowej do śledzenia ścieżki między funkcją główną a wywołaniem memcpy. Funkcja memcpy ma argument count, który określa liczbę bajtów do skopiowania. Ta funkcja często powoduje przepełnienia bufora z powodu nieprawidłowego obliczenia argumentu licznika, dlatego używa się jej jako przykładu.

Aby otworzyć przeglądarkę zbliżeniową kliknij Widok | Otwórz subviews | Bliska Przeglądarka. Tam, jeśli domyślnie cokolwiek jest wyświetlane, możesz zwinąć dowolne węzły podrzędne lub nadrzędne, klikając prawym przyciskiem myszy węzeł środkowy i wybierając odpowiednią opcję. Jeśli klikniesz prawym przyciskiem myszy w dowolnym miejscu okna, które nie jest węzłem, zostaną wyświetlone opcje menu. Najłatwiejszą metodą jest wybranie opcji Dodaj węzeł według nazwy i wybranie żądanej nazwy funkcji z listy jako punktu początkowego lub końcowego. Następnie wykonaj tę samą operację, aby wybrać drugi punkt. Na koniec możesz kliknąć prawym przyciskiem myszy jeden z węzłów i wybrać opcję Znajdź ścieżkę.

Wywołania funkcji

https://chacker.pl/

Rysunek  przedstawia przykład funkcji składającej się z kilku bloków.

Funkcje są często znacznie większe niż w tym przykładzie. Często zdarza się, że chcemy przyjrzeć się nie tylko wszystkim odsyłaczom do funkcji, ale także wywołaniom funkcji. Aby uzyskać te informacje w jednym miejscu, wybierz opcję Widok | Otwórz widoki | Wywołania funkcji. Skrócony przykład na rysunku  przedstawia trzy wywołania aktualnie analizowanej funkcji oraz sporo wywołań tej funkcji.

Odsyłacze (odnośniki zewnętrzne)

https://chacker.pl/

Dość często zdarza się, że chcemy wiedzieć, skąd w pliku binarnym pochodzą wywołania interesującej nas funkcji. Są to tak zwane odsyłacze, znane również jako odnośniki zewnętrzne. Być może chcesz wiedzieć, skąd i kiedy wykonywane jest wywołanie funkcji HeapAlloc. Jedną z metod jest kliknięcie karty Importy, posortowanie kolumny Nazwa alfabetycznie i zlokalizowanie żądanej funkcji. Po zlokalizowaniu kliknij dwukrotnie nazwę, aby przejść do sekcji Import danych (.idata) żądanej funkcji, takiej jak pokazana na rysunku. Po wybraniu funkcji w sekcji .idata naciśnij CTRL-X, aby wyświetlić okno odnośników zewnętrznych. Rysunek przedstawia wyniki w naszym przykładzie HeapAlloc. Możemy wybrać dowolne z wymienionych połączeń, aby przejść do tej lokalizacji w pliku wejściowym.

UWAGA. Istnieje również klawisz skrótu odsyłacza do argumentów o nazwie JumpOpXref, który jest wykonywany przez naciśnięcie X. Przykładowym przypadkiem użycia może być zmienna przechowywana w segmencie danych programu, do którego odwołuje się wiele miejsc w segmencie kodu. Naciśnięcie przycisku X na zmiennej, gdy jest podświetlona, powoduje wyświetlenie powiązań do tej zmiennej

Funkcje i funkcjonalność IDA Pro

https://chacker.pl/

IDA Pro ma dużą liczbę wbudowanych funkcji, narzędzi i funkcjonalności; jednakże, podobnie jak w przypadku wielu złożonych aplikacji, przy pierwszym uruchomieniu należy się uczyć. Wiele z tych opcji nie jest dostępnych w bezpłatnej wersji IDA. Zaczniemy od najbardziej podstawowego ustawienia preferencji, czyli schematu kolorów, a następnie omówimy niektóre z bardziej przydatnych funkcji. IDA Pro oferuje różne predefiniowane opcje ze schematem kolorów. Aby ustawić te opcje, kliknij menu Opcje, a następnie Kolory. Na rysunku pokazano menu rozwijane i opcje kolorów IDA.

Możesz wybrać pomiędzy domyślnym, darcula i ciemnym. Opcja ciemna dotyczy w całości IDA Pro. Opcja darcula dotyczy tylko okna demontażu. Rysunek pokazuje przykład trybu ciemnego.

Nawigacja w IDA Pro

https://chacker.pl/

Ważne jest, aby zrozumieć, jak prawidłowo pracować i poruszać się po IDA Pro, ponieważ istnieje wiele domyślnych zakładek i okien. Zacznijmy od przykładu załadowania podstawowego pliku binarnego do IDA Pro jako pliku wejściowego. Podczas pierwszego ładowania programu myAtoi do IDA Pro pojawia się następujące okno:

IDA Pro przeanalizował metadane pliku obiektowego i ustalił, że jest to 64-bitowy plik binarny ELF. IDA przeprowadza dużą część wstępnej analizy, takiej jak śledzenie przepływu wykonywania, przekazywanie podpisów technologii szybkiej identyfikacji i rozpoznawania bibliotek (FLIRT), śledzenie wskaźników stosu, analiza tabeli symboli i nazewnictwo funkcji, wstawianie danych o typach, jeśli są dostępne, oraz przypisanie nazw lokalizacji. Po kliknięciu OK IDA Pro przeprowadza automatyczną analizę. W przypadku dużych plików wejściowych analiza może zająć trochę czasu. Zamknięcie wszystkich okien i ukrycie paska narzędzi Nawigatora przyspiesza analizę. Po zakończeniu kliknięcie opcji Windows w opcjach menu i wybranie Resetuj pulpit przywraca układ do ustawień domyślnych. Gdy IDA Pro zakończy automatyczną analizę programu myAtoi, otrzymamy wynik pokazany na rysunku

UWAGA Wiele funkcji i elementów przedstawiono na rysunku 5-1. Pamiętaj, aby wracać do tego obrazu, gdy będziemy przeglądać różne sekcje, funkcje i opcje.

Pasek narzędzi Nawigatora na rysunku zapewnia przegląd całego pliku wejściowego, podzielonego na różne sekcje.

Każdy oznaczony kolorem obszar na pasku narzędzi można kliknąć, aby uzyskać łatwy dostęp do tej lokalizacji. W naszym przykładzie z programem myAtoi duża część ogólnego obrazu jest identyfikowana jako zwykłe funkcje. Oznacza to funkcje wewnętrzne i kod wykonywalny skompilowany do postaci binarnej, w przeciwieństwie do symboli zewnętrznych, które są zależnościami dynamicznymi, oraz funkcji bibliotecznych, które wskazywałyby statycznie skompilowany kod biblioteki. Okno Funkcje, pokazane na rysunku, zawiera listę nazw wszystkich funkcji wewnętrznych i zależności dynamicznych.

Dwukrotne kliknięcie wpisu powoduje wyświetlenie tej funkcji w głównym oknie widoku wykresu. Klawisz skrótu G umożliwia także bezpośrednie przejście do adresu. Jeśli w IDA Pro dostępna jest tabela symboli, wszystkie funkcje otrzymują odpowiednie nazwy. Jeśli informacja o symbolu nie jest dostępna dla danej funkcji, podaje się przedrostek podrzędny, po którym następuje przesunięcie względnego adresu wirtualnego (RVA), takie jak sub_1020. Poniżej znajduje się przykład sytuacji, w której tabela symboli nie jest dostępna, a kiedy jest dostępna. Poniżej okna Funkcje znajduje się okno Przegląd wykresu. . Jest to po prostu interaktywne okno przedstawiające całą aktualnie analizowaną funkcję. Okno danych wyjściowych na dole domyślnego układu IDA Pro pokazano na rysunku  wraz z interaktywnym paskiem Pythona lub IDC.

Okno Dane wyjściowe to miejsce, w którym wyświetlane są komunikaty oraz wyniki wprowadzonych danych polecenia za pośrednictwem IDA Python lub IDC. IDA Python i IDC zostały omówione w rozdziale 13. W naszym przykładzie ostatni wyświetlony komunikat brzmi: „Początkowa autoanaliza została zakończona”. Główne okno pośrodku domyślnego układu IDA Pro nosi nazwę IDA View-A w naszym przykładzie i pokazano na rysunku .  To jest widok wykresu, który wyświetla funkcje i bloki w obrębie tych funkcji w rekurencyjnym stylu opadania. Naciśnięcie spacji w tym oknie powoduje przełączenie widoku z widoku wykresu na widok tekstowy, jak pokazano na rysunku. Widok tekstowy to bardziej liniowy sposób patrzenia na demontaż. Ponowne naciśnięcie spacji przełącza pomiędzy dwiema opcjami widoku. Na rysunku  pokazano główną funkcję i jest tylko jeden blok kodu. Informacje o typie są wyświetlane u góry funkcji, po nich następują zmienne lokalne i dezasemblacja.

Rysunek przedstawia zakładkę Importy. To okno wyświetla wszystkie dynamiczne zależności pliku wejściowego od kodu biblioteki. Najwyższy wpis na liście to funkcja printf. Obiekt współdzielony zawierający tę funkcję jest wymagany do działania programu i musi zostać mapowany do procesu w czasie wykonywania. Nie pokazana jest karta Eksporty. W tym oknie wyświetlana jest lista normalnie dostępnych wyeksportowanych funkcji i skojarzonych z nimi adresów. Z tej sekcji korzystają obiekty współdzielone i biblioteki dołączane dynamicznie (DLL).

Co to jest dezasemblacja?

https://chacker.pl/

Najpierw przyjrzyjmy się procesowi deasemblacji kodu maszynowego. Jest to omówione na różne sposoby w innych miejscach tej książki, ale ważne jest, aby upewnić się, że rozumiesz podstawowy cel dezasemblera . W tym przykładzie używamy skompilowanej wersji programu myAtoi . Użyj narzędzia objdump zainstalowanego na Kali Linux z następującymi opcjami, aby zdemontować pierwsze osiem linii głównej funkcji programu myAtoi. Flaga -j pozwala określić sekcję; w tym przypadku wybieramy segment .text lub „code”. Flaga -d jest opcją demontażu. Grepujemy ciąg „<main>:” i drukujemy osiem wierszy po nim z flagą -A8.

W pierwszym wierszu wyniku w (1) widzimy, że główna funkcja zaczyna się od przesunięcia względnego adresu wirtualnego (RVA) wynoszącego 0x00000000000011ca w całym obrazie binarnym. Pierwsza linia zdezasemblowanego wyjścia w main zaczyna się od przesunięcia 11ca, jak widać w (2), po którym następuje kod operacji języka maszynowego 55, jak widać w (3). Na prawo od kodu operacji w (4) znajduje się odpowiednia zdemontowana instrukcja lub mnemoniczne push, po której następuje operand rbp w (5). Ta instrukcja spowoduje, że adres lub wartość przechowywana w rejestrze rbp zostanie wypchnięta na stos. Każdy z kolejnych wierszy wyjścia dostarcza tych samych informacji, pobierając kody operacji i wyświetlając odpowiedni demontaż. Jest to plik binarny w formacie wykonywalnym i linkującym (ELF) x86-64-bitowy. Gdyby ten program został skompilowany dla innego procesora, takiego jak ARM, kody operacji i zdezasemblowane instrukcje byłyby inne, ponieważ każda architektura procesora ma swój własny zestaw instrukcji. Dwie podstawowe metody demontażu to przeciągnięcie liniowe i zejście rekurencyjne (znane również jako przechodzenie rekurencyjne). Narzędzie objdump jest przykładem deasemblera liniowego, który rozpoczyna się na początku segmentu kodu lub określonego adresu początkowego, dezasemblując kolejno każdy kod operacji. Niektóre architektury mają zestaw instrukcji o zmiennej długości, na przykład x86-64, a inne architektury mają ustawione wymagania dotyczące rozmiaru, takie jak MIPS, gdzie każda instrukcja ma szerokość 4 bajtów. IDA jest przykładem dezasemblera rekurencyjnego, w którym kod maszynowy jest deasemblowany liniowo, aż do osiągnięcia instrukcji zdolnej do modyfikacji przepływu sterowania, takiej jak skok warunkowy lub rozgałęzienie. Przykładem skoku warunkowego jest instrukcja jz, która oznacza skok przy zera. Ta instrukcja sprawdza flagę zerową (zf) w rejestrze FLAGS, aby sprawdzić, czy jest ustawiona. Jeśli flaga jest ustawiona, skok jest wykonywany. Jeżeli flaga nie jest ustawiona, licznik programu przechodzi do kolejnego adresu sekwencyjnego, gdzie wykonanie jest kontynuowane. Aby dodać kontekst, poniższy obraz przedstawia dowolny przykład w IDA Pro skoku warunkowego po zwróceniu sterowania z funkcji alokacji pamięci:

Ten widok graficzny wewnątrz IDA Pro jest w formacie rekursywnego wyświetlania.

Najpierw wywoływana jest funkcja GetProcessHeap (1) . Jak sama nazwa wskazuje, to wywołanie funkcji zwraca adres bazowy lub uchwyt domyślnej sterty procesu. Adres sterty jest zwracany wywołującemu za pośrednictwem rejestru RAX. Argumenty wywołania HeapAlloc są teraz konfigurowane, przy czym pierwszym argumentem jest rozmiar, kopiowany z r14 do r8 w punkcie (2) przy użyciu instrukcji mov. Argument dwFlags jest ustawiany na 0 za pomocą instrukcji xor edx, edx w (3) , wskazując brak nowych opcji dla żądania alokacji. Adres sterty jest kopiowany z rax do rcx w (4) . Teraz, gdy argumenty funkcji HeapAlloc są już ustawione, instrukcja call jest wykonywana w punkcie (5) . Oczekiwany zwrot z wywołania HeapAlloc jest wskaźnikiem do przydzielonego fragmentu pamięci. Wartość przechowywana w rax jest następnie kopiowana do r15 w (6). Następnie wykonywana jest instrukcja test rax, rax w punkcie (7). Instrukcja testowa wykonuje operację bitową i. W tym przypadku testujemy rejestr rax przeciwko sobie samemu. Celem instrukcji testowej w tym przykładzie jest sprawdzenie, czy wartość zwracana z wywołania HeapAlloc wynosi 0, co oznaczałoby błąd. Jeśli rax przechowuje 0, a my i rejestr przeciwko sobie, ustawiana jest flaga zera (zf). Jeśli opcja HEAP_GENERATE_EXCEPTIONS jest ustawiona poprzez dwFlags podczas wywołania HeapAlloc, zwracane są kody wyjątków zamiast 0,1. Ostatnią instrukcją w tym bloku jest instrukcja skoku do zera (jz), at(8) . Jeśli zf jest ustawione, co oznacza, że wywołanie HeapAlloc nie powiodło się, wykonujemy skok; w przeciwnym razie przechodzimy liniowo do następnego adresu sekwencyjnego i kontynuujemy wykonywanie kodu.