我有一个不是用调试符号构建的可执行文件的核心转储。我可以恢复argv内容吗? [英] I have a core dump of an executable that was NOT built with debug symbols. Can I recover argv contents?

查看:147
本文介绍了我有一个不是用调试符号构建的可执行文件的核心转储。我可以恢复argv内容吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我可以恢复argv内容以查看命令行是什么吗?

>

如果我运行gdb,我可以看到一个回溯,并且我可以导航到main()框架。有一种方法可以在不知道确切地址的情况下恢复argv?



我使用运行CEntOS Linux distro / kernel的x86_x64(Intel Xeon CPU) / p>

我希望的一个原因是核心转储似乎显示了部分argv。

postgres,当我加载核心文件时,gdb会打印一条消息,其中包含postgres db-user名称,客户端OP地址和查询的前10个字符))

x86_64 参数在%rdi 中传递,%rsi 等寄存器(调用约定)。



因此,当你进入主要框架时,你应该能够:



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

(gdb)set $ argv =(char **)$ rsi
(gdb)set $ i = 0
(gdb)while $ a rgv [$ i]
>打印$ argv [$ i ++]
>结束

不幸的是,GDB通常不会恢复 $ rdi $ rsi 。所以这个例子不起作用:

  cat t.c 

#include< stdlib.h>

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

gcc t.c&& ./a.out
中止(核心转储)

gdb -q ./a.out核心
核心由`./a.out'生成。
程序以信号6终止,中止。
#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:没有这样的文件或目录。
in ../nptl/sysdeps/unix/sysv/linux/raise.c
(gdb)bt
#0 0x00007fdc8284aa75 in * __ GI_raise(sig =< optimize out>)at。 ./nptl/sysdeps/unix/sysv/linux/raise.c:64
#1 0x00007fdc8284e5c0 in * __ GI_abort()at abort.c:92
#2 0x000000000040052d bar()
#3 0x000000000040053b in foo()
#4在main()中的0x000000000040054b
(gdb)fr 4
#4在main()中的0x000000000040054b
(gdb)p $ rdi
$ 1 = 5524 ###显然不是正确的值

所以你必须工作一些更多......



你可以做的是使用关于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 :
#1 0x00007ffff7a915c0 in * __ GI_abort()at abort.c:92
#2 0x000000000040052d bar()
#3 0x000000000040053b foo()
#4 0x0000000000400556 in (优化出>,argc =<优化出> ;, ubp_av =<优化出> ;, init =<优化出>,fini =<优化出>中的main(),
#5 0x00007ffff7a78c4d out)>,rtld_fini =< optimized out>,stack_end = 0x7fffffffdad8)at libc-start.c:226
#6 0x0000000000400469 in _start()

(gdb)frame 6
(gdb)disas
函数_start汇编代码的转储:
0x0000000000400440< + 0> ;: xor%ebp,%ebp
0x0000000000400442< + 2> ;: mov%rdx,% r9
0x0000000000400445 <+ 5>:pop%rsi
0x0000000000400446 <+ 6>:mov%rsp,%rdx
0x0000000000400449 <+ 9>:和$ 0xfffffffffffffff0,%rsp
0x000000000040044d< + 13> ;: push%rax
0x000000000040044e< + 14> ;: push%rsp
0x000000000 040044f< + 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
汇编器转储结束。

现在我们预计原始的%rsp 成为 $ rsp + 8 (一个POP,两个PUSHes),但它可能在 $ rsp + 16 到期在指令 0x0000000000400449



处进行对齐让我们来看看有什么...

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

看起来很有希望:4(可疑的argc) 4个非NULL指针,接下来是NULL。



让我们看看是不是这样:

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

的确,这就是我调用二进制文件的方式。作为最终的完整性检查, 0x00007fffbe5d6ecf 看起来像enovironment的一部分吗?

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

,这是环境的开始(或结束)。



所以你有它。



Final注意:如果GDB不打印<优化出> 这么多,我们可以恢复 argc 和<$来自第5帧的c $ c> argv 。在GDB和GCC方面都有工作可以使GDB的打印少得多优化出来...

另外,当加载内核时,我的GDB会打印:

  Core是由`./a.out foo bar bazzzz'生成的。 

否定整个练习的需要。但是,这只适用于简短的命令行,而上面的解决方案适用于任何命令行。


I have a core dump of an executable that was NOT built with debug symbols.

Can I recover argv contents to see what the command line was?

If I run gdb, I can see a backtrace, and I can navigate to the main() frame. Once there, is there a way to recover argv, without knowing its exact address?

I am on x86_x64 (Intel Xeon CPU) running a CEntOS Linux distro/kernel,

One reason I am hopeful is that the core dump seems to show a partial argv.

(The program is postgres, and when I load the core file, gdb prints a message that includes the postgres db-user name, client OP address, and first 10 characters of the query))

解决方案

On x86_64 the arguments are passed in %rdi, %rsi, etc. registers (calling convention).

Therefore, when you step into the main frame, you should be able to:

(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

Unfortunately, GDB will not normally restore $rdi and $rsi when you switch frames. So this example doesn't work:

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

So you'll have to work some more ...

What you can do is use the knowledge of how Linux stack is set up at process startup, combined with the fact that GDB will restore stack pointer:

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

So now we expect the original %rsp to be $rsp+8 (one POP, two PUSHes), but it could be at $rsp+16 due to alignment that was done at instruction 0x0000000000400449

Let's see what's there ...

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

That looks promising: 4 (suspected argc), followed by 4 non-NULL pointers, followed by NULL.

Let's see if that pans out:

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

Indeed, that's how I invoked the binary. As a final sanity check, does 0x00007fffbe5d6ecf look like part of the enovironment?

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

Yep, that's the beginning (or the end) of the environment.

So there you have it.

Final notes: if GDB didn't print <optimized out> so much, we could have recovered argc and argv from frame #5. There is work on both GDB and GCC sides to make GDB print much less of "optimized out" ...

Also, when loading the core, my GDB prints:

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

negating the need for this whole exercise. However, that only works for short command lines, while the solution above will work for any command line.

这篇关于我有一个不是用调试符号构建的可执行文件的核心转储。我可以恢复argv内容吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆