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.

Wprowadzenie do IDA Pro do inżynierii odwrotnej

https://chacker.pl/

Dlaczego przy dużej liczbie dostępnych darmowych i alternatywnych dezasemblerów wybrać IDA Pro? Do darmowych alternatywnych deasemblerów zalicza się Ghidra , radare2 i kilka innych. Komercyjne alternatywy obejmują Binary Ninja i Hopper. Każda z tych alternatyw jest świetnym deasemblerem; jednakże IDA jest bardzo szanowana, obsługuje większość architektur procesorów i posiada największą liczbę wtyczek, skryptów i innych rozszerzeń. Jest szeroko stosowany przez społeczność badaczy bezpieczeństwa i oferuje niezliczone funkcje pomagające w analizie plików binarnych. Dostępne są także bezpłatne wersje IDA Pro, z których najnowsza to IDA 7.0; jednakże ich funkcjonalność jest zazwyczaj ograniczona. W rozdziale 18 będziemy używać IDA Pro i powiązanych z nim wtyczek do przeprowadzenia analizy poprawek firmy Microsoft w celu zlokalizowania zmian w kodzie, które mogą wskazywać na załataną lukę. Wczesne wykorzystanie załatanej luki w zabezpieczeniach to potężna technika stosowana podczas ofensywnych działań związanych z bezpieczeństwem.

IDAPro

https://chacker.pl/

Deasembler i debugger znany jako Interactive Disassembler (IDA) Pro to bogata w funkcje, rozszerzalna aplikacja do inżynierii wstecznej, której właścicielem i administratorem jest firma Hex-Rays w Belgii. Jest to produkt komercyjny z dostępnymi alternatywnymi wersjami, takimi jak IDA Home i IDA Free. Rodzina dezasemblerów IDA jest aktywnie rozwijana dzięki dużej liczbie swobodnie dostępnych wtyczek i skryptów dostarczonych przez Hex-Rays i społeczność użytkowników. Hex-Rays oferuje także Dekompilator Hex-Rays, prawdopodobnie najlepszy dostępny dekompilator. W porównaniu do innych deasemblerów jest najbardziej dojrzały, obsługuje największą liczbę architektur i funkcji procesorów.

Streszczenie

https://chacker.pl/

Omówiliśmy podstawowe funkcje i funkcjonalność Ghidry, od których możesz zacząć, pozostawiając otwarte drzwi do zgłębiania bardziej zaawansowanych tematów. Przyjrzeliśmy się takim tematom, jak interfejs Ghidry, poprawa czytelności za pomocą adnotacji oraz sposób używania Ghidry do różnicowania binarnego i analizy poprawek. Poświęć trochę czasu na poznanie innych potężnych i zaawansowanych funkcji Ghidra, takich jak automatyzacja zadań inżynierii wstecznej za pomocą skryptów Ghidra i nieskończonych możliwości, na jakie pozwala system wtyczek.

Analiza poprawek

https://chacker.pl/

Narzędzie ujawniło różnice pomiędzy programami korzystającymi z funkcji ViewStudentGrades, jak pokazano poniżej:

Szybka kontrola dekompilacji funkcji obu wersji ujawnia, że podczas analizowania danych wejściowych użytkownika za pomocą funkcji atoi nie sprawdzono granic indeksu tablicy studentów. Oznacza to, że możemy wybrać dowolny dodatni lub ujemny numer indeksu, co pozwala nam traktować dowolny adres z wyrównaniem do 32 bajtów jako strukturę danych Ucznia. Opcja „Zmień oceny” pozwala na zmianę ocen uczniów, jeśli ustawione zostanie prawidłowe hasło. Okazuje się, że tę podatność możemy wykorzystać na swoją korzyść. Jeśli przejdziemy do Windows | Tabela symboli i wyszukaj admin_password symbol, zauważymy, że znajduje się on pod offsetem 0x001040a0. To jest dokładnie 64 bajty przed bazowym adresem tablicy uczniów (0x001040e0). Co by się stało, gdybyśmy skorzystali z opcji „Wyświetl oceny” i wybrali ucznia o numerze -2?

Jak widać, traktowanie pamięci admin_password jako zmiennej typu struktury Studenta zakończy się tym, że hasło będzie znajdować się dokładnie w pozycji „nazwa” struktury. Znaleźliśmy prymityw wykorzystywania odczytu i możemy teraz odczytać 24 bajty z dowolnej wartości pamięci wyrównanej do 32 bajtów. Ale to nie wszystko. Zwróć uwagę, jak kontrolujemy wartość indeksu i wartość składową ocen dla struktury uczniów w funkcji ChangeStudentGrades. Oznacza to, że możemy zapisać 4 bajty w dowolnym miejscu i 28 bajtów z dowolnego 32-bajtowego adresu pamięci.

Organizować coś

https://chacker.pl/

Ghidra udostępnia funkcję różnicowania kodów, która umożliwia porównanie różnic pomiędzy dwoma plikami binarnymi o tym samym układzie adresów i pozycji. Jest to przydatne w przypadku porównań binarnych z korelacją przesunięcia jeden do jednego, ale nie koreluje kodu pod względem kontekstu i przepływu wykonania. Na szczęście możemy rozszerzyć możliwości Ghidry, instalując wtyczki takie jak BinDiffHelper1 dla doskonałego narzędzia BinDiff. Aby to zrobić, wykonaj następujące kroki:

1. Zainstaluj narzędzie do automatyzacji kompilacji Gradle w wersji 6.5, uruchamiając następujące polecenia:

  1. Sklonuj i skompiluj wtyczkę BinExport2 z oficjalnego repozytorium. Ta wtyczka automatyzuje proces generowania bazy danych różnic BinExport:

Proces kompilacji może zająć kilka minut. Po zakończeniu należy utworzyć plik ZIP wtyczki BinExport w folderze ~/binexport/java/BinExport.

3. W oknie projektu Ghidry przejdź do Plik | Menu Zainstaluj rozszerzenie i kliknij ikonę znaku plus (+), aby dodać plik ZIP wtyczki do folderu ~/binexport/java/BinExport/dist, jak pokazano poniżej:

  1. Kliknij OK i zrestartuj Ghidrę, aby zmiany wtyczki zostały zastosowane.
  2. W oknie terminala pobierz i zainstaluj BinDiff v6 z oficjalnej strony:

Instalacja pakietu .deb wyświetli monit o podanie ścieżki IDA Pro. Pozostaw to pole puste, aby określić, że jesteśmy zainteresowani eksperymentalnymi rozszerzeniami Ghidra.

  1. Sklonuj i skompiluj wtyczkę BinDiffHelper z oficjalnego repozytorium:

7. W oknie projektu Ghidry przejdź do Plik | Zainstaluj menu rozszerzenia i dodaj plik Zip wtyczki do folderu ~/BinDiffHelper/dist/, jak pokazano poniżej.

  1. Uruchom ponownie Ghidrę, aby zastosować zmiany wtyczek.

Różnicowanie binarne

Teraz, gdy wtyczki zostały zainstalowane, kontynuujmy laboratorium, badając proces różnicowania plików binarnych:

9. Otwórz plik programu z poprawką uczniów. Zostaniesz poproszony o wykrycie nowych rozszerzeń:

Wybierz Tak, aby skonfigurować nowe wtyczki, a w kolejnym oknie kliknij OK:

  1. Uruchom Auto-Analizę i zapisz projekt.
  2. Powtórz kroki 9 i 10, ale tym razem z plikiem programu uczniów.
  3. Otwórz okno wtyczki Window/BinDiffHelper. Kliknij ikonę konfiguracji, aby ustawić poprawną ścieżkę binarną BinDiff 6 (/opt/bindiff/bin/bindiff), jak pokazano poniżej:

  1. Otwórz poprawiony przez uczniów program, klikając ikonę „Otwórz plik do porównania”. Powinieneś teraz zobaczyć wyniki podobieństwa i pewności dla każdej funkcji. Przejdź do funkcji ViewStudentGrades na dole, zaznacz pole wyboru importu i kliknij ikonę „Importuj wybraną funkcję”.

Binarna analiza różnic i poprawek

https://chacker.pl/

Po wykryciu i zgłoszeniu luk w zabezpieczeniach dostawcy przystępują do łatania swoich produktów i publikowania aktualizacji. Czasami dziennik zmian aktualizacji jest ograniczony szczegółowo w odniesieniu do załatanego błędu i aby zrozumieć zmiany i opracować exploity, konieczne staje się różnicowanie binarne. W tym laboratorium przeprowadzisz Cię przez proces odkrywania luki w narzędziu do zarządzania ocenami uczniów za pomocą różnicowania binarnego. Luka powinna być łatwa do wykrycia po prostu poprzez sprawdzenie kodu, ale ponownie będziemy udawać, że mamy dostęp tylko do plików binarnych, aby lepiej symulować scenariusz ze świata rzeczywistego.

Poprawa czytelności dzięki adnotacjom

https://chacker.pl/

Dla początkujących bardzo frustrującą i uciążliwą częścią inżynierii odwrotnej jest brak jasnego pojęcia, co oznaczają różne parametry i wartości danych. Ten brak kontekstu w zakresie różnych ustawień wartości rejestrów, przesunięć pamięci, odwołań do wskaźników i argumentów funkcji można przezwyciężyć poprzez właściwe użycie typów danych. Jak być może już wiesz, środowisko wykonawcze architektur komputerów jest niezależne od typów danych, które są istotne dla twórcy oprogramowania w czasie programowania i są wykorzystywane przez kompilator do prawidłowego przypisywania alokacji pamięci, przesunięć elementów struktury, indeksów tablic i innych ustawień w czasie kompilacji. Jeśli porównamy oryginalny kod z domyślnym widokiem dekompilacji dla funkcji LoadStudents, pokazanym poniżej, funkcja dekompilacji może nie okazać się tak przydatna, jak to tylko możliwe. Będziemy kontynuować poprawę czytelności programu studentów poprzez przypisanie typów danych do wartości w funkcji main.

Kod źródłowy przedstawia pętlę for zwiększającą zmienną licznika o 1 w każdej iteracji, która jest używana jako indeks dla zmiennej globalnej studentów, która jest tablicą zdefiniowanego przez nas typu Student. Przed adnotacją zdekompilowana reprezentacja odpowiedniego kodu asemblera w języku C pokaże zmienną licznika indeksu pomnożoną przez 0x20 (co jest rozmiarem danych Ucznia). Ponadto, biorąc pod uwagę, że dekompilator nadal nie jest świadomy typów danych dla każdej zmiennej, każde odwołanie do wartości zostanie poddane rzutowaniu typu, co jeszcze bardziej komplikuje czytelność kodu źródłowego. Możemy łatwo poprawić czytelność, ustawiając zmienne z poprawnymi adnotacjami typu danych i zmieniając nazwy zmiennych. Załóżmy, że nie mamy kodu źródłowego, abyśmy mogli doświadczyć najczęstszego scenariusza, jakiego można się spodziewać podczas prawdziwej inżynierii odwrotnej. Wykonaj następujące kroki:

  1. Przejdź do funkcji LoadStudents, wyszukując ją w widoku drzewa symboli, a następnie przejdź do okna Dekompilacja, aby wprowadzić adnotacje. Zmienimy nazwy zmiennych, typy danych i sygnatury funkcji w oparciu o operacje i funkcje, z którymi są powiązane w kodzie.
  2. Bazując na sposobie wyłuskiwania zmiennej i ustawianiu jej z przesunięciem wynoszącym 32 (0x20) pomnożonym przez zmienną indeksującą liczbę, wiemy, że jest to tablica. Ustawione są niektóre wartości w pobliżu przesunięcia, jak pokazano poniżej:

  • W linii 25. wartość całkowita jest dereferowana w odległości 24 (0x18) bajtów od przesunięcia (liczba * 32), więc można bezpiecznie założyć, że jest to wskaźnik do wartości całkowitej (int *). Nazwa powinna brzmieć „id”, ponieważ jest ustawiana ze zmiennej indeksowej licznika.
  • W linii 26. funkcja strncpy kopiuje ciąg znaków odpowiadający nazwisku ucznia odczytanemu z pliku CSV do przesunięcia bazowego (liczba * 32), więc jest to tablica znaków o nieznanym rozmiarze. Możemy jednak zgadnąć, że są to 24 bajty, ponieważ to tam znajduje się przesunięcie poprzedniej wartości i nie powinno ono nadpisywać elementu własnej struktury (char [24]). Nazwiemy ten element struktury „imięm”.
  • W linii 28. iVar1 jest ustawiany za pomocą funkcji atoi wywoływanej na podstawie wartości ocen w pliku CSV, która zwraca liczbę całkowitą, a następnie ustawiany jest z przesunięciem 0x1c od przesunięcia bazowego (liczba * 32). Załóżmy zatem, że jest to również liczba całkowita. Jest to członek „klasy” struktury ucznia.
  1. Teraz możemy zdefiniować własny typ danych struktury Studenta dla elementów tablicy studentów. Przejdź do okna Menedżer typów danych, kliknij prawym przyciskiem myszy typy danych programu „studenckiego”, wybierz podmenu Nowy i kliknij element „struktura”.
  2. Nazwij strukturę Student i ustaw jej rozmiar na 32 bajty.
  3. Przejdź do pierwszego wiersza w tabeli (offset 0), kliknij dwukrotnie DataType

pole i wpisz char[24]. Następnie kliknij dwukrotnie pole Nazwa i wpisz nazwę.

  1. W drugim wierszu (przesunięcie 24) ustaw pole DataType na int i ustaw pole Name na id.
  2. Powtórz to samo w trzecim wierszu (przesunięcie 28) i ustaw pole Nazwa na oceny.
  3. Jeśli okno Edytora struktury wygląda jak to na rysunku , kliknij ikonę Zapisz i zamknij okno. Konstrukcja jest już gotowa do użycia.

  1. Umieść kursor nad dowolną instancją zmiennej globalnej studentów i naciśnij CTRL-L, aby zmienić jej typ danych z niezdefiniowany[1024] na Student[32] (nasza struktura ma rozmiar 32, a 1024 podzielone przez 32 równa się 32) .
  2. Zmień pozostałe zmienne i funkcje w oparciu o ich kontekst. Na przykład zmienna local_20 jest ustawiana w wyniku funkcji fopen; dlatego powinien być ustawiony jako typ danych FILE *, a jego nazwa powinna wyglądać mniej więcej tak: fh.
  3. Naciśnij CTRL-L, aby zmienić jego typ na PLIK *.
  4. Wybierz nazwę zmiennej i naciśnij L lub kliknij prawym przyciskiem myszy i wybierz

Zmień nazwę zmiennej, aby zmienić nazwę zmiennej na fh.

C. Aby uniknąć rzutowania wywołania na fopen, kliknij funkcję prawym przyciskiem myszy, kliknij opcję Edytuj sygnaturę funkcji i, jeśli to konieczne, zmień sygnaturę funkcji, aby ustawić poprawny argument wywołania i zwrócić typy danych.

Jeśli nie masz pewności co do sygnatury standardowych funkcji, skorzystaj z podręcznika programisty, uruchamiając man 3 fopen w oknie terminala. Po zakończeniu tego procesu powinieneś zauważyć, że czytelność zarówno zdekompilowanego, jak i wyszczególniającego zdezasemblowany kod została znacznie poprawiona, jak pokazano poniżej. Ponadto każda inna funkcja odwołująca się do zmiennych, funkcji i typów danych z adnotacjami odniesie korzyści z tego wysiłku

Wykresy

https://chacker.pl/

Ghidra zapewnia zaawansowane funkcje generowania wykresów. Czasami przebieg wykonywania i warunki warunkowe mogą stać się nieuporządkowane, a bez wykresów zrozumienie kodu może wydawać się zadaniem niemożliwym. Wykresy składają się z wierzchołków (lub bloków) i krawędzi (lub przepływu sterowania) i mogą pomóc w zrozumieniu rozgałęzień, przepływu sterowania, pętli, odniesień, a nawet korelacji między funkcjami i etykietami w programie.

Istnieją dwa typy wykresów:

  • Wykresy przepływu Wyświetlają przepływ (przeskoki przelotowe i bezwarunkowe) pomiędzy wybranymi blokami kodu.
  • Wykresy połączeń Wyświetla sekwencję wywołań pomiędzy funkcjami.

Możesz wygenerować wykres wybranego kodu lub funkcji, przechodząc do menu Wykres i wybierając żądany wykres. Menu Wykres udostępnia następujące narzędzia:

  1. Pasek narzędzi Umożliwia szybki dostęp do ustawień i odświeżania wyświetlania wykresów oraz innych opcji.
  2. Widok wykresu Wyświetlane są tutaj wszystkie bloki (wierzchołki) i przepływ (krawędzie), co ułatwia nawigację, grupowanie i kontrolę. Można przesuwać, przeciągając myszą, a także przybliżać i oddalać za pomocą kółka myszy lub gładzika.

3. Widok satelitarny Pomaga szybko poruszać się po wykresie, pokazując małą mapę wszystkich przedstawionych na wykresie bloków.

Możesz także eksportować wykresy do wielu formatów graficznych i danych, takich jak CSV, DOT, GML, JSON, Visio i inne.

Adnotacje programu

https://chacker.pl/

Adnotacje pomagają poprawić czytelność, zapewniają wyjaśnienia i śledzą pracę wykonaną nad odwróconym programem. Adnotacje wpływają również na wynikowy wynik dekompilatora. Ghidra udostępnia wiele rodzajów adnotacji. Oto niektóre z najważniejszych:

  • Możesz używać specjalnych adnotacji w komentarzach jako ciągów formatujących. Wpływają one na wynikowy wynik, określając wartości jako adresy łańcuchowe, symbole, adresy URL i inne formaty.
  • Adnotacje zmiennych umożliwiają zmianę nazwy symbolu zmiennej, jej typu danych i lokalizacji przechowywania.
  • Zmiana nazwy etykiety umożliwia zmianę nazwy etykiety i wywnioskowanych nazw na bardziej szczegółowe w celu lepszego zrozumienia kodu.

• Adnotacji funkcji można używać do zmiany nazwy funkcji, podpisu, konwencji wywoływania i typu danych wartości zwracanej.