2014-05-13 9 views
6

Uczę się kilku technik anty-debugowania w systemie Linux i znalazłem fragment kodu do sprawdzania bajtu 0xcc w pamięci, aby wykryć punkty przerwania w gdb. Oto kod:Anti-debugging: gdb nie zapisuje bajtu 0xcc dla punktów przerwania. Każdy pomysł, dlaczego?

 if ((*(volatile unsigned *)((unsigned)foo + 3) & 0xff) == 0xcc) 
    { 
        printf("BREAKPOINT\n"); 
        exit(1); 
     } 

    foo(); 

Ale to nie działa. Próbowałem nawet ustawić punkt przerwania w funkcji foo() i obserwować zawartość w pamięci, ale nie widziałem żadnego bajtu 0xcc zapisanego dla punktu przerwania. Oto co zrobiłem:

(gdb) b foo 
Breakpoint 1 at 0x804846a: file p4.c, line 8. 
(gdb) x/x 0x804846a 
0x804846a <foo+6>: 0xe02404c7 
(gdb) x/16x 0x8048460 
0x8048460 <frame_dummy+32>: 0x90c3c9d0 0x83e58955 0x04c718ec 0x0485e024 
0x8048470 <foo+12>: 0xfefae808 0xc3c9ffff ..... 

Jak widać, nie wydaje się być nie bajt 0xcc opisana w punkcie wejścia foo function(). Czy ktoś wie, co się dzieje i gdzie mogę się mylić? Dzięki.

+1

Istnieje interesujący artykuł na ten temat 'Jak działa debugger', spójrz na tę część artykułu' Magia za INT 3': http://www.alexonlinux.com/how-debugger-works # the_magic_behind_int_3 –

Odpowiedz

8

Druga część jest łatwa do wytłumaczenia (jak poprawnie podano Flortify): GDB pokazuje oryginalną zawartość pamięci, a nie punkt przerwania "bajtów". W trybie domyślnym nawet usuwa punkty przerwania, gdy debugger zawiesza się i wstawia ponownie przed kontynuowaniem. Użytkownicy zazwyczaj chcą zobaczyć swój kod, a nie dziwne zmodyfikowane instrukcje używane dla punktów przerwania.

Za pomocą kodu C pominięto punkt przerwania dla kilku bajtów. GDB ustawia punkt przerwania pofunction prologue, ponieważ prolog funkcyjny zwykle nie jest tym, co użytkownicy gdb chcą zobaczyć.Tak więc, jeśli wstawisz break na foo, rzeczywisty punkt przerwania będzie zazwyczaj znajdował się kilka bajtów po tym (zależy to od kodu prologu, który jest zależny od funkcji, ponieważ może lub nie musi zapisywać wskaźnika stosu, wskaźnika ramki i tak dalej). Ale łatwo to sprawdzić. Użyłem tego kodu:

#include <stdio.h> 
int main() 
{ 
    int i,j; 
    unsigned char *p = (unsigned char*)main; 

    for (j=0; j<4; j++) { 
     printf("%p: ",p); 
     for (i=0; i<16; i++) 
      printf("%.2x ", *p++); 
     printf("\n"); 
    } 
    return 0; 
} 

Jeśli uruchomić ten program sam to drukuje:

0x40057d: 55 48 89 e5 48 83 ec 10 48 c7 45 f8 7d 05 40 00 
0x40058d: c7 45 f4 00 00 00 00 eb 5a 48 8b 45 f8 48 89 c6 
0x40059d: bf 84 06 40 00 b8 00 00 00 00 e8 b4 fe ff ff c7 
0x4005ad: 45 f0 00 00 00 00 eb 27 48 8b 45 f8 48 8d 50 01

Teraz możemy uruchomić go w gdb (wyjściowe ponownie formatowane dla SO).

(gdb) break main 
Breakpoint 1 at 0x400585: file ../bp.c, line 6. 
(gdb) info break 
Num  Type   Disp Enb Address   What 
1  breakpoint  keep y 0x0000000000400585 in main at ../bp.c:6 
(gdb) disas/r main,+32 
Dump of assembler code from 0x40057d to 0x40059d: 
    0x000000000040057d (main+0): 55      push %rbp 
    0x000000000040057e (main+1): 48 89 e5     mov %rsp,%rbp 
    0x0000000000400581 (main+4): 48 83 ec 10    sub $0x10,%rsp 
    0x0000000000400585 (main+8): 48 c7 45 f8 7d 05 40 00 movq $0x40057d,-0x8(%rbp) 
    0x000000000040058d (main+16): c7 45 f4 00 00 00 00  movl $0x0,-0xc(%rbp) 
    0x0000000000400594 (main+23): eb 5a      jmp 0x4005f0 
    0x0000000000400596 (main+25): 48 8b 45 f8    mov -0x8(%rbp),%rax 
    0x000000000040059a (main+29): 48 89 c6     mov %rax,%rsi 
End of assembler dump.

Dzięki temu możemy zweryfikować, że program jest drukowanie poprawnych bajtów. Ale to również pokazuje, że punkt przerwania został wstawiony przy 0x400585 (czyli po prologu funkcji), a nie na pierwszej instrukcji funkcji. Jeśli teraz uruchomić program, w ramach gdb (z run), a następnie „dalej” po punkt przerwania jest trafiony, możemy uzyskać ten wynik:

(gdb) cont 
Continuing. 
0x40057d: 55 48 89 e5 48 83 ec 10 cc c7 45 f8 7d 05 40 00 
0x40058d: c7 45 f4 00 00 00 00 eb 5a 48 8b 45 f8 48 89 c6 
0x40059d: bf 84 06 40 00 b8 00 00 00 00 e8 b4 fe ff ff c7 
0x4005ad: 45 f0 00 00 00 00 eb 27 48 8b 45 f8 48 8d 50 01

To pokazuje teraz 0xcc drukowane na adres 9 bajtów na główne .

+0

Dlaczego gdb nie usunął bajtu '0xCC' po uzyskaniu punktu przerwania? –

+0

Jest to zgodne z projektem. GDB nie usuwa "standardowych" punktów przerwania po ich trafieniu. Tymczasowe punkty przerwania (wstawiane z tbreakiem) są trwale usuwane po trafieniu. – dbrank0

4

Jeśli twój sprzęt to obsługuje, GDB może używać Hardware Breakpoints, która nie łata kodu.

Chociaż nie potwierdziły to za pośrednictwem oficjalnych docs, this page wskazuje

Domyślnie gdb próbuje użyć sprzętowe wspomaganie punktach kontrolnych.

Ponieważ wskazują spodziewa 0xCC bajty, jestem przy założeniu, że działa na sprzęcie x86, jak int3 opcode jest 0xCC. Procesory x86 mają zestaw DR0 - , w którym można zaprogramować adres danych w celu spowodowania wyjątku punktu przerwania. DR7 to bitfield, który kontroluje zachowanie punktów przerwania, a DR6 wskazuje status.

Rejestry debugowania można odczytać/zapisać tylko z poziomu 0 (tryb jądra). Oznacza to, że jądro zarządza tymi rejestrami (prawdopodobnie za pośrednictwem interfejsu API ptrace).

Jednak ze względu na anty-debugowanie, nie tracimy nadziei! W systemie Windows API GetThreadContext pozwala uzyskać (kopię) z CONTEXT dla (zatrzymanego) wątku. Ta struktura obejmuje zawartość rejestrów DRx. This question dotyczy tego, jak zaimplementować to samo w systemie Linux.

+0

Myślę, że się mylisz, gdy mówisz: 'Chociaż nie potwierdziłem tego przez jakiekolwiek oficjalne dokumenty, ta strona wskazuje, że'. Ta strona wskazuje na punkty obserwacyjne, a nie na punkty przerwania. Wręcz przeciwnie, user1726119 pyta o punkty przerwania. Nawiasem mówiąc, oficjalny dokument GDB mówi również o punktach kontrolnych: 'gdb ustawia sprzętowy punkt obserwacyjny, jeśli to możliwe. Http://sourceware.org/gdb/current/onlinedocs/gdb/Set-Watchpoints.html#Set-Watchpoints –

2

To może być białe kłamstwo, które GDB mówi ... może być tam punkt przerwania w pamięci RAM, ale GDB zauważył, co było wcześniej (aby mógł później je przywrócić) i pokazuje, że zamiast tego prawdziwej zawartości pamięci RAM.

Oczywiście może być również używany Sprzętowe punkty przerwania, który jest obiektem dostępnym w niektórych procesorach. Ustawianie punktów przerwania h/w odbywa się poprzez przekazanie procesorowi adresu, na który powinien zwrócić uwagę (i wywołanie przerwania punktu przerwania, jeśli zostanie uderzony przez licznik programu podczas wykonywania kodu).

+1

While Twój pierwszy akapit może wyjaśnić zachowanie, które widzi podczas obserwacji pamięci za pośrednictwem GDB (* Myślałem, że to też na początku *), nie wyjaśnia to, że program sam się obserwuje. –

+0

Nie, pierwotne pytanie ** nie ** oznacza, że ​​kiedykolwiek uruchamiał program w GDB z zestawem punktów przerwania ** i ** gdyby obserwował siebie. Pytanie opisuje pisanie programu, który obserwuje się, a następnie ustawia punkt przerwania w GDB, a następnie ** używając GDB **, aby wyszukać punkt przerwania w pamięci. Tylko. – Flortify

+0

@ dbrank0 Dałem najlepszą odpowiedź, pokazując program sam się ** podczas ** działający pod GDB ** z ** zestawem punktów przerwania. – Flortify

Powiązane problemy