2012-01-28 11 views
9

Mam zrzut główny pliku wykonywalnego NIE zbudowanego z symbolami debugowania.Mam zrzut główny pliku wykonywalnego NIE zbudowanego z symbolami debugowania. Czy mogę odzyskać zawartość argv?

Czy mogę odzyskać zawartość argv, aby zobaczyć, jaka była linia poleceń?

Jeśli uruchomię gdb, widzę ślad wstecz i mogę przejść do ramki głównej(). Czy istnieje sposób na odzyskanie argv, bez znajomości dokładnego adresu?

jestem na x86_x64 (Intel Xeon CPU) uruchomiony CentOS distro linux/kernel,

Jednym z powodów, mam nadzieję, że zrzut wydaje się wskazywać częściowy argv.

(Program jest postgres, a kiedy załadować plik core, gdb drukuje wiadomość, która zawiera nazwę Postgres db-user, klient adres OP, a pierwsze 10 znaków zapytania))

+0

Z tego, co wiem, konwencja wywoływania x86_64 przekazuje kilka pierwszych argumentów w rejestrach, przez co wartość argv niekoniecznie jest zapisywana w dowolnym miejscu. – Neil

Odpowiedz

12

Na x86_64 argumenty są przekazywane w rejestrach %rdi, %rsi itp. (calling convention).

Dlatego, kiedy po wejściu do ramki main, powinieneś być w stanie:

(gdb) p $rdi   # == argc 
(gdb) p (char**) $rsi # == argv 

(gdb) set $argv = (char**)$rsi 
(gdb) set $i = 0 
(gdb) while $argv[$i] 
> print $argv[$i++] 
> end 

Niestety, GDB nie będzie normalnie przywrócić $rdi i $rsi podczas przełączania ramek. Więc ten przykład nie działa:

cat t.c 

#include <stdlib.h> 

int bar() { abort(); } 
int foo() { return bar(); } 
int main() 
{ 
    foo(); 
    return 0; 
} 

gcc t.c && ./a.out 
Aborted (core dumped) 

gdb -q ./a.out core 
Core was generated by `./a.out'. 
Program terminated with signal 6, Aborted. 
#0 0x00007fdc8284aa75 in *__GI_raise (sig=<optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64 
64 ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory. 
    in ../nptl/sysdeps/unix/sysv/linux/raise.c 
(gdb) bt 
#0 0x00007fdc8284aa75 in *__GI_raise (sig=<optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64 
#1 0x00007fdc8284e5c0 in *__GI_abort() at abort.c:92 
#2 0x000000000040052d in bar() 
#3 0x000000000040053b in foo() 
#4 0x000000000040054b in main() 
(gdb) fr 4 
#4 0x000000000040054b in main() 
(gdb) p $rdi 
$1 = 5524 ### clearly not the right value 

Więc trzeba pracować trochę więcej ...

Co może zrobić, to wykorzystać wiedzę o tym, jak Linux stos jest ustawiony na process startup w połączeniu z faktem, że GDB będzie przywrócenia wskaźnika stosu:

(gdb) set backtrace past-main 
(gdb) bt 
#0 0x00007ffff7a8da75 in *__GI_raise (sig=<optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64 
#1 0x00007ffff7a915c0 in *__GI_abort() at abort.c:92 
#2 0x000000000040052d in bar() 
#3 0x000000000040053b in foo() 
#4 0x0000000000400556 in main() 
#5 0x00007ffff7a78c4d in __libc_start_main (main=<optimized out>, argc=<optimized out>, ubp_av=<optimized out>, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdad8) at libc-start.c:226 
#6 0x0000000000400469 in _start() 

(gdb) frame 6 
(gdb) disas 
Dump of assembler code for function _start: 
    0x0000000000400440 <+0>: xor %ebp,%ebp 
    0x0000000000400442 <+2>: mov %rdx,%r9 
    0x0000000000400445 <+5>: pop %rsi 
    0x0000000000400446 <+6>: mov %rsp,%rdx 
    0x0000000000400449 <+9>: and $0xfffffffffffffff0,%rsp 
    0x000000000040044d <+13>: push %rax 
    0x000000000040044e <+14>: push %rsp 
    0x000000000040044f <+15>: mov $0x400560,%r8 
    0x0000000000400456 <+22>: mov $0x400570,%rcx 
    0x000000000040045d <+29>: mov $0x40053d,%rdi 
    0x0000000000400464 <+36>: callq 0x400428 <[email protected]> 
=> 0x0000000000400469 <+41>: hlt  
    0x000000000040046a <+42>: nop 
    0x000000000040046b <+43>: nop 
End of assembler dump. 

Więc teraz oczekujemy oryginalny %rsp być $rsp+8 (jeden P PO, dwie popycha), ale może to być co $rsp+16 powodu wyrównania że została sporządzona w instrukcji 0x0000000000400449

Zobaczmy co tam ...

(gdb) x/8gx $rsp+8 
0x7fffbe5d5e98: 0x000000000000001c 0x0000000000000004 
0x7fffbe5d5ea8: 0x00007fffbe5d6eb8 0x00007fffbe5d6ec0 
0x7fffbe5d5eb8: 0x00007fffbe5d6ec4 0x00007fffbe5d6ec8 
0x7fffbe5d5ec8: 0x0000000000000000 0x00007fffbe5d6ecf 

To wygląda obiecująco: 4 (podejrzenie argc), a następnie przez 4 wskaźniki nie-NULL, po których następuje NULL.

Zobaczmy, czy to patelnie z:

(gdb) x/s 0x00007fffbe5d6eb8 
0x7fffbe5d6eb8: "./a.out" 
(gdb) x/s 0x00007fffbe5d6ec0 
0x7fffbe5d6ec0: "foo" 
(gdb) x/s 0x00007fffbe5d6ec4 
0x7fffbe5d6ec4: "bar" 
(gdb) x/s 0x00007fffbe5d6ec8 
0x7fffbe5d6ec8: "bazzzz" 

Istotnie, tak właśnie wywoływany binarny. Czy ostateczna kontrola poczytalności, czy 0x00007fffbe5d6ecf wygląda jak część środowiska?

(gdb) x/s 0x00007fffbe5d6f3f 
0x7fffbe5d6f3f: "SSH_AGENT_PID=2874" 

Tak, to początek (lub koniec) środowiska.

Więc już go masz.

Ostateczne uwagi: jeśli GDB nie wydrukował tak dużo, to można by było odzyskać argc i argv z ramki # 5. Jest praca po obu stronach GDB i GCC, aby GDB drukować znacznie mniej „zoptymalizowane out” ...

Również podczas ładowania rdzenia, moi drukuje GDB:

Core was generated by `./a.out foo bar bazzzz'. 

negując potrzebę tego całe ćwiczenie. Działa to jednak tylko dla krótkich linii poleceń, podczas gdy powyższe rozwiązanie działa dla dowolnej linii poleceń.

+0

Dzięki za informację początkową i miło jest wiedzieć, że twój pomysł działa, jeśli program jest uruchamiany pod gdb, ale nie wiem, gdzie pójść dalej ... – misterbee

+0

@misterbee Niestety, nie do końca ukończyłem odpowiedź. Teraz gotowe. –

+0

Doskonale! Spróbuję tego. – misterbee