一些杂七杂八的调试技巧整理

Table of Contents

大多数时候去做调试,无非是几个情况:

1、程序、库遇到bug

2、遇到靠思考也难以解决的故障

3、学习原理

4、分析漏洞

调试的目的不同,方法就有好多,但是我认为最重要的还是具备对系统调用和网络协议的熟悉,其次就是掌握各种工具。

1 跟踪函数调用

好多高级语言都能找到可以跟踪函数调用的工具,借助这些工具可以快速摸清函数调用流程。如PHP有Xdebug,只用在PHP源码中调用Xdebug提供的函数即可:

xdebug_start_trace('输出的日志路径');
....
xdebug_stop_trace();

1.1 strace

对于操作系统层面,strace命令可以帮我们跟踪程序的系统调用情况。时常会遇到下载的软件没有在文档里说清楚配置文件路径,导致启动时找不到配置文件,借助strace命令,我们只用分析程序启动时读取了哪些文件。

比如我的一台CentOS上运行snort时遇到加载库的问题:

$ sudo snort -v
snort: error while loading shared libraries: libdnet.1: cannot open shared object file: No such file or directory

但是系统已经安装了libdnet:

$ sudo yum list installed libdnet
Installed Packages
libdnet.x86_64                      1.12-13.1.el7                      @anaconda

通过strace,看看发生了什么问题:

$ sudo strace snort -vd
execve("/sbin/snort", ["snort", "-vd"], [/* 19 vars */]) = 0
...
open("/usr/lib64/libdnet.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
...
exit_group(127)                         = ?
+++ exited with 127 +++

上面的输出显示,启动时找不到/usr/lib64/libdnet.1这个文件,因为系统里已经安装了这个包,所以我只用手动链接下即可解决:

$ sudo ln -s /usr/lib64/libdnet.so.1 /usr/lib64/libdnet.1

要看程序启动时加载了哪些文件,还通过以下表达式就可以捕获所有的open调用:

$ sudo strace -e 'trace=open' snort -v

1.2 Ktrace

Ktrace是BSD下类似Linux中strace的工具,用于跟踪进程的系统调用。ktrace输出的是二进制文件,需要kdump辅助解析。

例1,解决找不到文件问题

比如我修改MySQL的配置文件/etc/my.cnf里数据存储路径后,依旧提示找不到某文件。用ktrace命令执行:

# ktrace mysqld_safe

然后会在当前目录下生成ktrace.out的二进制文件,用kdump(参数-f指定dump文件)即可看到调用过程,类似:

22866 sh       CALL  munmap(0x32be7206000,0xff0)
22866 sh       RET   munmap 0
22866 sh       CALL  write(2,0x32c3dc7b410,0x6a)
22866 sh       GIO   fd 2 wrote 106 bytes
"/usr/local/bin/mysqld_safe[956]: cannot create /var/mysql/host.err: No such file or directory
"
22866 sh       RET   write 106/0x6a
22866 sh       CALL  read(10,0x32c3dc7bc58,0x200)
22866 sh       RET   read 0
22866 sh       CALL  close(10)
22866 sh       RET   close 0

其中CALL表示调用某个系统函数,RET是调用的返回值,NAMI是访问的文件路径。比如我想找出命令调用操作了哪些外部文件,以及它的返回值:

# kdump | fgrep -A 2 NAMI

类似结果如下:

22866 sh       CALL  sigprocmask(SIG_BLOCK,0x80000<SIGCHLD>)
22866 sh       NAMI  "/var/mysql/host.pid"
22866 sh       RET   stat -1 errno 2 No such file or directory
22866 sh       CALL  pipe(0x7f7ffffda880)
22866 sh       NAMI  "/var/mysql/host.err"
22866 sh       RET   open -1 errno 2 No such file or directory
22866 sh       CALL  issetugid()
22866 sh       NAMI  "/usr/share/nls/C/libc.cat"

或者查看系统调用返回失败的:

kdump | fgrep errno -B 2

例2,寻找配置文件

运行Nginx时,想知道加载的哪个目录配置文件:

# ktrace -t n nginx

参数-t指定要跟踪的系统调用类型,n表示跟踪namei。

# kdump -f ktrace.out | fgrep .conf