IDA Pro – Tworzenie sygnatur FLIRT dla niestandardowych bibliotek

W pewnych sytuacjach mechanizmy rozpoznawania bibliotek w IDA Pro nie umieją sobie poradzić z bibliotekami z których korzysta dana binarka.

Efekt widoczny na screenie poniżej:

W oknie Functions widzimy, że wszystkie funkcje są rozpoznane jako pochodzące z zewnętrznych bibliotek (extern), a na pasku znajdującym się na górze zwizualizowany został spory fragment pamięci określony jako Unexplored (kolor khaki).

W tej sytuacji możemy skorzystać z mechanizmu Fast Library Identification and Recognition Technology (FLIRT), pozwalającego na samodzielne wygenerowanie sygnatur brakujących, statycznych blibliotek i dodanie ich do IDA’y.

Na początek musimy wiedzieć czego szukamy.

Warto wiedzieć, które sygnatury bibliotek musimy dostarczyć, aby ułatwić pracę IDA’dzie, w tym wypadku skorzystam z linuxowego programu readelf z przełącznikiem -d.

To polecenie przydaje się również podczas rozwiązywania problemów z uruchamianiem binarki w emulowanym środowisku (o tym w następnym poście).

Generowanie sygnatur

W swoim przykładzie posłużę się bibliotekami wygenerowanymi podczas kompilacji uClibc, nieco odchudzonej biblioteki standardowej języka C dedykowanej urządzeniom wbudowanym.

Przed procesem generacji należy znaleźć najbardziej zbliżoną bibliotekę, do użytej w analizowanym programie. Pod tym pojęciem mam na myśli:

Po nieco przydługim wstępie przejdźmy do “mięsa”.

1. Wyszukajmy interesujące nas blibioteki w katalogu uClibc

Przyda się nieśmiertelne polecenie find: find . -name “*.a”

2. Wygenerujmy wzorzec dla konkretnego typu blibioteki

Do dyspozycji mamy następujące parsery:

Wyżej wymienione programy znajdują się w [ścieżka_do_IDA_SDK]\flair[wersja_IDA]\bin[platforma: win\linux\mac].

Za pomocą polecenia pelf [ścieżka_do_biblioteki] [ścieżka_pliku_wzorca] generowany jest wzorzec interesującej mnie biblioteki.

Po pomyślnym zakończeniu procesu otrzymujemy podsumowanie operacji: ilość pominiętych funkcji (zależna od przełączników, z którymi uruchomiony był parser) oraz ilość oznakowanych funkcji.

W moim wypadku pierwsza linijka pliku libpthread.pat wygląda następująco (zawierająca inicjującą sekwencję bajtów, CRC16 funkcji, długość funkcji w bajtach oraz listę nazw symboli wykorzystanych w funkcji):

03E00008000010212CA300021060000324020016AC8500000000102103E00008 FF 3BC2 02F0 :0000 __GI_pthread_attr_destroy :0008 \_GI_pthread_attr_setdetachstate :0024 \_GI_pthread_attr_getdetachstate :0034 \_GI_pthread_attr_setschedpolicy :0050 \_GI_pthread_attr_getschedpolicy :0060 \_GI_pthread_attr_setinheritsched :007C \_GI_pthread_attr_getinheritsched :008C \_GI_pthread_attr_setscope :00B4 \_GI_pthread_attr_getscope :00C4 \_pthread_attr_getguardsize :00D4 __pthread_attr_setstackaddr :00E8 __pthread_attr_getstackaddr :00F8 __pthread_attr_setstacksize :0114 __pthread_attr_getstacksize :0124 __GI_pthread_attr_init :0190 \_pthread_attr_setguardsize :0210 __GI_pthread_attr_getschedparam :0250 \__GI_pthread_attr_setschedparam :0124 pthread_attr_init :0000 pthread_attr_destroy :0008 pthread_attr_setdetachstate :0024 pthread_attr_getdetachstate :0250 pthread_attr_setschedparam :0210 pthread_attr_getschedparam :0034 pthread_attr_setschedpolicy :0050 pthread_attr_getschedpolicy :0060 pthread_attr_setinheritsched :007C pthread_attr_getinheritsched :008C pthread_attr_setscope :00B4 pthread_attr_getscope :0190@ pthread_attr_setguardsize :00C4@ pthread_attr_getguardsize :00D4@ pthread_attr_setstackaddr :00E8@ pthread_attr_getstackaddr :00F8@ pthread_attr_setstacksize :0114@ pthread_attr_getstacksize ^0124 _gp_disp ^013C getpagesize ^022C memcpy ^0270 sched_get_priority_max ^028C sched_get_priority_min 08ACA300003C1C….279C….0399E02127BDFFD8AFBF0024AFBC00108F99….002008250320F809AFA400188FA400183C03002000621823AC83002024030001AC820014AC83000CAC800000AC800004AC800008AC800010AC80001CAC8000188FBF00240000102103E0000827BD00283C1C….279C….0399E02127BDFFD8AFBF0024AFBC00108F99….AFA400180320F809AFA5001C8FA5001C8FA4001824A3FFFF00621821144000020062001B0007000D8C86002000002812002008250020082500A20018000010120046302B10C0000324030016AC820014000018218FBF00240060102103E0000827BD00283C1C….279C….0399E02127BDFFE0AFBF001CAFBC0010248200088F99….00A02021240600040320F809004028218FBF001C0000102103E0000827BD00203C1C….279C….0399E02127BDFFD0AFBF002CAFB10028AFB00024AFBC00108F99….008080218C8400040320F809AFA500188FBC00108E0400048F99….002008250320F809004088218FA500188FBC00108CA30000002008250062102A144000090223882A16200007260400088F99….002008250320F809240600041000000200001021240200168FBF002C8FB100288FB0002403E0000827BD0030

3. Generowanie sygnatur z plików wzorca

Sygnatury dla wszystkich typów bibliotek tworzymy za pomocą programu sigmake, znajdującego się w tej samej lokalizacji co parsery z punktu poprzedniego.

Składnia sigmake jest identyczna jak parserów: sigmake [ścieżka_pliku_wzorca] [ścieżka_pliku_sygnatury].

Pliki sygnatur kopiujemy do lokalizacji: [ścieżka_do_IDA]\sig[architektura: arm, mips, ebc, etc.]

4. Dodanie plików sygnatur do IDA’y

Ostatnim krokiem jest dodanie wcześniej wygenerowanych plików do IDA’y, podczas pracy nad problematyczną binarką.

Z menu wybieramy opcję Load file -> FLIRT signature file:

A następnie jedną z wygenerowanych sygnatur:

Tym sposobem nauczyliśmy IDA’ę niestandardowej biblioteki i jej output powinien być bardziej satysfakcjonujący 🙂