2013-02-07 12 views
5

Mam aplikację, która tworzy zrzuty segmentu procesów win32 z .text. Następnie dzieli kod na podstawowe bloki. Blok podstawowy to zestaw instrukcji wykonywanych zawsze jeden po drugim (skoki są zawsze ostatnimi instrukcjami takich podstawowych bloków). Oto przykład:Jak podzielić zdemontowany kod C na funkcje?

Basic block 1 
    mov ecx, dword ptr [ecx] 
    test ecx, ecx 
    je 00401013h 

Basic block 2 
    mov eax, dword ptr [ecx] 
    call dword ptr [eax+08h] 

Basic block 3 
    test eax, eax 
    je 0040100Ah 

Basic block 4 
    mov edx, dword ptr [eax] 
    push 00000001h 
    mov ecx, eax 
    call dword ptr [edx] 

Basic block 5 
    ret 000008h 

Teraz chciałbym pogrupować takie podstawowe bloki w funkcje - powiedz, które podstawowe bloki tworzą funkcję. Jaki jest algorytm? Muszę pamiętać, że może być wiele instrukcji ret w jednej funkcji. Jak wykrywać funkcje fast_call?

Odpowiedz

6

Najprostszy algorytm grupowania bloków w funkcji byłoby:

  1. note wszystkie adresy, do których połączenia są wykonane z call some_address instrukcji
  2. jeśli pierwszy blok po takim adresem kończy ret, jesteś Zrobić za pomocą funkcji, inaczej
  3. podążać za skokiem w bloku do innego bloku i tak dalej, aż podążysz za wszystkimi możliwymi ścieżkami wykonania (pamiętaj o skokach warunkowych, z których każdy dzieli ścieżkę na dwie) i wszystkie ścieżki mają wykończone ret. Musisz rozpoznać skoki, które organizują pętle więc sam program nie powiesić wprowadzając nieskończonej pętli

problemy:

  1. liczba połączeń mogą być wykonane pośrednio poprzez odczyt wskaźników funkcji z pamięci npmusiałbyś call [some_address] zamiast call some_address
  2. niektóre połączenia pośrednie mogą być wykonane do obliczonych adresów
  3. funkcje, które wywołują inne funkcje przed powrotem mogą mieć jump some_address zamiast call some_address natychmiast następuje ret
  4. call some_address można symulować za pomocą kombinacji z push some_address + ret lUB push some_address + jmp some_other_address
  5. niektóre funkcje mogą udostępniać kodu na ich końcu (na przykład mają różne punkty wejścia, ale jeden lub więcej punktów wyjścia są takie same)

można korzystać z niektórych heurystyki do określenia, gdzie zaczynają funkcje szukając najczęstszych sekwencji instrukcji prolog:

push ebp 
mov ebp, esp 

Ponownie, to nie może działać, jeśli funkcje są kompilowane ze wskaźnika ramki tłumionego (tj użyliby esp zamiast ebp, aby uzyskać dostęp do swoich parametrów na stosie, jest to możliwe).

Kompilator (np. MSVC++) może również wypełniać przestrzeń międzyfunkcyjną za pomocą instrukcji int 3, która również może służyć jako wskazówka do rozpoczęcia nadchodzącej funkcji.

Jeśli chodzi o rozróżnienie między różnymi konwencjami wywoływania, prawdopodobnie najłatwiej jest spojrzeć na symbole (oczywiście, jeśli je masz). MSVC++ generates different name prefixes and suffixes, e.g.:

  • _function - cdecl
  • _function @ numer - stdcall
  • @ funkcji @ liczby - fastcall

Jeżeli nie można wyodrębnić te informacje z symboli, należy analizować kod, aby zobaczyć jak parametry są przekazywane do funkcji i czy funkcje lub ich wywoływanie usuwa je ze stosu.

3

Możesz użyć obecności enter, aby wskazać początek funkcji, lub certain code which sets up a frame.

push ebp 
mov ebp, esp 
sub esp, (bytes for "local" stack space) 

Później przekonasz odwrotny kod (lub leave) przed wywołaniem ret:

mov esp, ebp 
pop ebp 

Można również użyć liczbę bajtów do lokalnej przestrzeni stosu zidentyfikować zmienne lokalne.

, itp., Podejmie analizę kodu tuż przed call s, które wykorzystują początkową lokalizację i ocenę rejestrów używanych/oczyszczonych.

1

Zobacz oprogramowanie takie jak windasm lub ollydbg. Operacje call i ret oznaczają wywołania funkcji. Jednak kod nie działa sekwencyjnie, a skoki można wykonywać w dowolnym miejscu. call dword ptr [edx] zależy od rejestru edx, a tym samym nie będzie w stanie wiedzieć, gdzie idzie, chyba że robisz debugowanie w czasie wykonywania.

Aby rozpoznać funkcje fastcall, należy przyjrzeć się, w jaki sposób przekazywane są parametry. Fastcall umieści dwa pierwsze parametry wielkości wskaźnika w edeksach edx i ecx, gdzie stdcall popchnie je na stos. Aby uzyskać wyjaśnienie, patrz this article.