У меня есть дамп ядра исполняемого файла, который НЕ был создан с использованием символов отладки. Могу ли я восстановить содержимое argv?

У меня есть дамп ядра исполняемого файла, который НЕ был создан с использованием символов отладки.

Могу ли я восстановить содержимое argv, чтобы увидеть, что было в командной строке?

Если я запускаю gdb, я вижу обратную трассировку и могу перейти к фрейму main(). Оказавшись там, есть ли способ восстановить argv, не зная его точного адреса?

Я использую x86_x64 (процессор Intel Xeon) с дистрибутивом/ядром CEntOS Linux,

Одна из причин, по которой я надеюсь, заключается в том, что дамп ядра, кажется, показывает частичный argv.

(Это программа postgres, и когда я загружаю основной файл, gdb печатает сообщение, которое включает имя пользователя postgres db, адрес клиентской операционной системы и первые 10 символов запроса))


person misterbee    schedule 28.01.2012    source источник
comment
Насколько я знаю, соглашение о вызовах x86_64 передает первые несколько аргументов в регистры, поэтому значение argv не обязательно где-либо сохраняется.   -  person Neil    schedule 29.01.2012


Ответы (1)


В x86_64 аргументы передаются в регистрах %rdi, %rsi и т. д. (соглашение о вызовах).

Таким образом, когда вы войдете в кадр main, вы сможете:

(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

К сожалению, GDB обычно не восстанавливает $rdi и $rsi при переключении кадров. Так что этот пример не работает:

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

Так что придется еще поработать...

Что вы можете сделать, так это использовать знания о настройке стека Linux на запуск процесса в сочетании с тем фактом, что GDB будет восстанавливать указатель стека:

(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 <__libc_start_main@plt>
=> 0x0000000000400469 <+41>:    hlt    
   0x000000000040046a <+42>:    nop
   0x000000000040046b <+43>:    nop
End of assembler dump.

Итак, теперь мы ожидаем, что исходный %rsp будет $rsp+8 (один POP, два PUSH), но он может быть равен $rsp+16 из-за выравнивания, выполненного в инструкции 0x0000000000400449.

Посмотрим что там...

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

Это выглядит многообещающе: 4 (подозревается argc), за которыми следуют 4 ненулевых указателя, за которыми следует NULL.

Посмотрим, получится ли:

(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"

Действительно, именно так я вызвал двоичный файл. В качестве последней проверки здравомыслия, 0x00007fffbe5d6ecf выглядит как часть окружающей среды?

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

Да, это начало (или конец) среды.

Итак, у вас есть это.

Заключительные замечания: если бы GDB не печатал так много <optimized out>, мы могли бы восстановить argc и argv из кадра №5. И на стороне GDB, и на стороне GCC ведется работа, чтобы заставить GDB печатать намного меньше "оптимизированного" ...

Также при загрузке ядра мой GDB печатает:

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

отрицание необходимости всего этого упражнения. Однако это работает только для коротких командных строк, а приведенное выше решение будет работать для любой командной строки.

person Employed Russian    schedule 28.01.2012
comment
Спасибо за начальную информацию, и приятно знать, что ваша идея работает, если программа запущена под gdb, но я не знаю, что делать дальше... - person misterbee; 29.01.2012