什么是 core dump
对于程序,由于各种异常或者 bug,导致在运行过程中,并且在满足一定条件下,产生一个叫做 core 的文件。
通常情况下,core 文件会包含程序运行时的:
- 内存
- 寄存器状态
- 堆栈指针
- 内存管理信息
- 各种函数调用堆栈信息等
许多程序出错的时候,会产生一个 core 文件。通过工具分析这个文件,我们可以定位到,程序异常退出的时候对应的堆栈调用等信息。
开启 core dump
生成 core dump 文件
看一段有问题的代码:
1 2 3 4 5 6 7 8
| #include<stdio.h>
int main(){ int *p=NULL; *p=0; puts("error"); return 0; }
|
给空指针赋值
linux下编译和执行:
1 2 3 4 5 6 7 8
| 13:13 taqini@qm /tmp/a % gcc a.c -o test -no-pie -g 13:14 taqini@qm /tmp/a % ./test [1] 23405 segmentation fault (core dumped) ./test 13:14 taqini@qm /tmp/a % ls a.c core test
|
执行编译后的程序会产生 core dump文件。
调试 core dump
dmesg
1 2 3 4 5 6
| 13:18 taqini@qm /tmp/a % sudo dmesg| grep test [ 5391.121950] test[23973]: segfault at 0 ip 000000000040114e sp 00007ffd215c5ee0 error 6 in test[401000+1000] 13:18 taqini@qm /tmp/a % addr2line -e test 000000000040114e /tmp/a/a.c:5
|
先通过dmesg找到对应出错的地址,再用 addr2line -e 将地址解析到对应的代码行。
addr2line命令只对有调试信息且没开pie的程序管用,现在大部分程序都开了地址随机化,且没有调试信息
gdb
直接gdb a.out core
即可,常用命令:
- bt :查看堆栈信息(调用链)
- i locals :查看当前程序栈的局部变量
- i args :查看当前程序栈的参数
- i r :查看当前寄存器的值
源代码:
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <stdio.h> #include <stdlib.h> #include <fcntl.h>
int main(){ setvbuf(stdin,0,2,0); setvbuf(stdout,0,2,0); char buf[32]; printf("%p\n",printf); scanf("%s",buf); printf("%s",buf); }
|
存在缓冲区溢出
编译、运行、溢出缓冲区:
1 2 3 4 5 6 7 8 9 10 11 12
| 13:47 taqini@qm /tmp/b % gcc a.c -fno-stack-protector 13:47 taqini@qm /tmp/b % cyclic 128 | ./a.out 0x7fcb1b1efd90 aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaab[1] 30098 done cyclic 128 | 30099 segmentation fault (core dumped) ./a.out 13:47 taqini@qm /tmp/b % ls a.c a.out core 13:47 taqini@qm /tmp/b % gdb a.out core
|
直接用gdb调试,加载core文件,可以对溢出点进行快速定位:
32 - rbp
40 - ret address
strace
strace
是一个集诊断、调试、统计与一体的工具,我们可以使用strace,对应用的系统调用和信号传递的跟踪结果,来对应用进行分析,以达到解决问题,或者是了解应用工作过程的目的。
strace 的简单的用法就是,执行一个指定的命令,在指定的命令结束之后,它也就退出了。
在命令执行的过程中,strace 会记录和解析命令进程的所有系统调用,以及这个进程所接收到的,所有的信号值。
-c ,统计每一系统调用的所执行的时间,次数和出错的次数等
-p ,指定进程pid
-i ,输出系统调用的入口指针
-c 还挺好玩儿,(有点插桩的感觉了)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 20:17 taqini@qm /tmp/b % strace -c ./a.out 0x7fbe17220d90 2333333 2333333 % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 0.00 0.000000 0 9 read 0.00 0.000000 0 2 write 0.00 0.000000 0 2 close 0.00 0.000000 0 2 fstat 0.00 0.000000 0 8 mmap 0.00 0.000000 0 4 mprotect 0.00 0.000000 0 1 munmap 0.00 0.000000 0 1 brk 0.00 0.000000 0 4 pread64 0.00 0.000000 0 1 1 access 0.00 0.000000 0 1 execve 0.00 0.000000 0 2 1 arch_prctl 0.00 0.000000 0 2 openat ------ ----------- ----------- --------- --------- ---------------- 100.00 0.000000 39 2 total
|
还是上面的例子,生成48个字节数据溢出缓冲区:
1 2
| 20:12 taqini@qm /tmp/b % cyclic 48 | strace -i ./a.out
|
使用strace
跟踪程序流:
