显示结构体

 gdb-pead$ p *(struct _IO_FILE_plus *)

标准输入

在漏洞挖掘或者 CTF 比赛中经常遇到的情况是某些输入触发了进程崩溃,因此要挂 gdb 进行分析,这时候就需要gdb 挂载的程序能够以指定的标准输入运行。如果标准输入是文件,那很简单:

$ gdb demo
(gdb) run < file

但更多时候为了方便调试,希望能以其他程序的输出来运行,比如:

$ python -c 'print "A"*100' | ./demo

可惜 gdb 不支持这种管道,不过可以通过下面的方法实现:

$ gdb demo
(gdb) run < <(python -c 'print "A"*100')
Starting program: /pwn/demo < <(python -c 'print "A"*100')
argv[0] =/pwn/demo
first read: 10
second read: 10

或者:

$ gdb demo
(gdb) run <<<$(python -c 'print "A"*100')
Starting program: /pwn/demo < <(python -c 'print "A"*100')
argv[0] =/pwn/demo
first read: 10
second read: 10

后者实际上是 shell 命令 here string的一种形式。这两种方式是有区别的,注意示例程序中 read 调用会提前返回,所以如果我们想要第一次读取3个字符,第二次读取4个字符的话,就不能一次性全部输入。比如下面这样就不符合预期了:

$ gdb demo
(gdb) run < <(echo -n 1112222)
Starting program: /pwn/demo < <(echo -n 1112222)
argv[0] = /pwn/demo
first read: 7
second read: 0

正确的方式应该是这样:

$ gdb demo
(gdb) run < <(echo -n 111; sleep 1; echo -n 2222)
Starting program: /pwn/demo < <(echo -n 111; sleep 1; echo -n 2222)
argv[0] = /pwn/demo
first read: 3
second read: 4

值得注意的是,这种情况下,使用here string是没用的,因为该字符串是计算完再一次性传给命令:

(gdb) run <<<$(echo -n 111; sleep 1; echo -n 2222)
Starting program: /pwn/demo <<<$(echo -n 111; sleep 1; echo -n 2222)
argv[0] = /pwn/demo
first read: 8
second read: 0

而且这里是8字节,因为末尾还带了个回车。 所以我更偏向于使用第一种方式。

环境变量

对于运行程序而言,还有个重要的参数来源是环境变量,比如在调试 CGI 程序的时候。这在 gdb 中可以使用environment参数,不过需要注意的是该参数的设置是以空格为切分而不是传统的以=对环境变量赋值。

(gdb)help set environment
Set environment variable value to give the program.
Arguments are VAR VALUE where VAR is variable name and VALUE is value.
VALUES of environment variables are uninterpreted strings.
This does not affect the program until the next "run" command.

还有要注意的是这个参数要求变量是uninterpreted strings,也就是说只能指定可打印字符。如果我们要传输一个的 payload 或者 shellcode 还要用 gdb 调试怎么办呢?我一般使用的方式是在调用 gdb 时指定,比如:

$ env CONTENT_TYPE="$(python -c "print 'A'*10 + '\x04\x03\x02\x01'")" gdb demo
(gdb) run