gdb也用了好几年了,虽然称不上骨灰级玩家,但也有一些自己的经验,因此分享出来给大家,顺便也作为一个存档记录。
多进程调试
最近在调试一个漏洞的exploit时遇到一个问题。目标漏洞程序是一个 CGI 程序,由主进程调起,而且运行只有一瞬的时间;我的需求是想要在在该程序中下断点,在内存布局之后可以调试我的 shellcode,该如何实现?当然目标程序是没有符号的,而且我希望下的断点是一个动态地址。在 lldb 中有--wait-for
,gdb 里却没有对应的命令,经过多次摸索,终于总结出一个比较完美的解决方案。
示例程序
这里构建一个简单的示例来进行实际演示。首先是父进程:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char **argv, char **env) {
printf("parent started, pid=%dn", getpid());
char *line = NULL;
size_t len = 0;
ssize_t read;
while ((read = getline(&line, &len, stdin)) != -1) {
pid_t pid = fork();
if (pid == -1) {
perror("fork");
break;
}
if (pid == 0) {
printf("1 fork return in child, pid=%dn", getpid());
char *const av[] = {
"child", line, NULL};
if (-1 == execve("./child", av, env)) {
perror("execve");
break;
}
} else {
printf("2 fork return in parent, child pid=%dn", pid);
int status = 0;
wait(&status);
}
}
return 0;
}
子进程很简单:
#include <stdio.h>
#include <string.h>
void vuln(char *str) {
char buf[4];
strcpy(buf, str);
printf("child buf: %s", buf);
}
int main(int argc, char **argv) {
puts("child started");
vuln(argv[1]);
return 0;
}
这里编译子进程时候指定-no-pie
,并且strip
掉符号。我们的调试目标是断点在子进程的strcpy
中,拓展来说是希望能断点在子进程的任意地址上。
踩坑过程
通过搜索可以找到一个 stackoverflow 的回答: gdb break when entering child process。根据其说法,使用 set follow-fork-mode child
即可。这是一个 gdb 命令,其目的是告诉 gdb 在目标应用调用fork
之后接着调试子进程而不是父进程,因为在 Linux 中