前言 之前知道老师是因为《软件调试》这本书,非常厉害,但是因为主要是windows的一直还没读(因为主要学习Linux,不过最近越发感觉不能局限于一个系统),最近看见一个大佬在朋友圈转发这个课,才发现张老师原来是有自己的公司和培训,看了下来太牛了,立马报名了。
(老师b站也有号, 官网nanocode.cn,) 还有其他很多优秀的课程,看来要好好买一波了..这才是真正有价值的知识付费!!
背景 老师将实际案例中的一个问题抽象出了一个很简单的demo, 用于搜索argv[0]的名字, 其实也就是当前可执行程序的名字,本身看着这个代码是没有什么问题的(不,有问题,没有include parser.c)
parser.c
1 2 3 4 5 6 7 8 9 #include <string.h> const char * get_name (const char * full_path) { const char * sep = strrchr (full_path, '/' ); return (sep == NULL )? "errname" : sep+1 ; }
ptrtrap.c
1 2 3 4 5 6 7 8 9 10 11 12 #include <stdio.h> int main (int argc, char * argv[]) { char * name = get_name(argv[0 ]); printf ("Demo of pointer trap by Raymond.\n" ); printf ("Name: %s\n" , name); return 0 ; }
在编译的时候会给出一个警告
gcc -g ptrtrap.c parser.c
1 2 3 4 5 6 7 root@VM-4-8-ubuntu:/home/ubuntu/youlan# gcc -g ptrtrap.c parser.c ptrtrap.c: In function ‘main’: ptrtrap.c:5:22: warning: implicit declaration of function ‘get_name’; did you mean ‘rename’? [-Wimplicit-function-declaration] 5 | char* name = get_name(argv[0]); | ^~~~~~~~ | rename ptrtrap.c:5:22: warning: initialization of ‘char *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
运行的时候会发生段错误
Program received signal SIGSEGV, Segmentation fault
段错误,访问了不该访问的
调试分析 回溯粗看 朱熹的不远复:“不远复”,出自《易经》“不远之复,以修身也”。和孔子的吾日三省吾身有异曲同工之妙,人要不断反思自己,反省走的路.
bt命令回溯 调用栈 , 库函数一般都经过很多修改和测试、一般没问题,所以更多的还是自己写的代码的问题
可以减少bt的显示,如 bt -frame-info short-location 不带源代码位置
frame 3查看自己写的函数里的栈帧的情况, list查看源代码, 这样就能够看到上下文, disass查看汇编
所以能够看到问题是在 call printf这里,产生了问题, 也就是传入的name有问题
问: __printf为什么带下划线?
答: 因为__printf是libc实现的, _ _通常是编译器的函数 编译器的优化?
info shared查看进程里的库, ld负责把程序从外存搬运到内存, libc负责实现标准库函数
1 2 3 4 pwndbg> info shared From To Syms Read Shared Object Library 0x00007ffff7fc5090 0x00007ffff7fee315 Yes /lib64/ld-linux-x86-64.so.2 0x00007ffff7daf700 0x00007ffff7f41b3d Yes /lib/x86_64-linux-gnu/libc.so.6
函数从哪里开始执行? elf从哪里开始呢,不是main 是_start(), bt有个选项 -past-main查看main函数之前的,有的默认开启了
__libc_start_call_main 用来做准备工作?
细看崩溃指令 来到崩溃的那一行汇编来看看什么情况,这里指向的含义是 尝试执行这条指令,但是失败了
vpcmpeqb指令
[rdi]是引用内存, 此时的rdi的值是0xffffffffffffe7db,我们来看一下内存的情况,用info inferiors命令,查看进程,然后从proc里看内存(pwndbg可以直接vmmap)
从这里看地址就能看出来,是无效地址, 用户空间的大小早超了(所以说每个细节都需要关注)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 pwndbg> info inferiors Num Description Connection Executable * 1 process 1269337 1 (native) /home/ubuntu/youlan/a.out pwndbg> !cat /proc/1269337/maps 555555554000-555555555000 r--p 00000000 fc:02 822041 /home/ubuntu/youlan/a.out 555555555000-555555556000 r-xp 00001000 fc:02 822041 /home/ubuntu/youlan/a.out 555555556000-555555557000 r--p 00002000 fc:02 822041 /home/ubuntu/youlan/a.out 555555557000-555555558000 r--p 00002000 fc:02 822041 /home/ubuntu/youlan/a.out 555555558000-555555559000 rw-p 00003000 fc:02 822041 /home/ubuntu/youlan/a.out 555555559000-55555557a000 rw-p 00000000 00:00 0 [heap] 7ffff7d84000-7ffff7d87000 rw-p 00000000 00:00 0 7ffff7d87000-7ffff7daf000 r--p 00000000 fc:02 29791 /usr/lib/x86_64-linux-gnu/libc.so.6 7ffff7daf000-7ffff7f44000 r-xp 00028000 fc:02 29791 /usr/lib/x86_64-linux-gnu/libc.so.6 7ffff7f44000-7ffff7f9c000 r--p 001bd000 fc:02 29791 /usr/lib/x86_64-linux-gnu/libc.so.6 7ffff7f9c000-7ffff7fa0000 r--p 00214000 fc:02 29791 /usr/lib/x86_64-linux-gnu/libc.so.6 7ffff7fa0000-7ffff7fa2000 rw-p 00218000 fc:02 29791 /usr/lib/x86_64-linux-gnu/libc.so.6 7ffff7fa2000-7ffff7faf000 rw-p 00000000 00:00 0 7ffff7fbb000-7ffff7fbd000 rw-p 00000000 00:00 0 7ffff7fbd000-7ffff7fc1000 r--p 00000000 00:00 0 [vvar] 7ffff7fc1000-7ffff7fc3000 r-xp 00000000 00:00 0 [vdso] 7ffff7fc3000-7ffff7fc5000 r--p 00000000 fc:02 2337 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 7ffff7fc5000-7ffff7fef000 r-xp 00002000 fc:02 2337 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 7ffff7fef000-7ffff7ffa000 r--p 0002c000 fc:02 2337 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 7ffff7ffb000-7ffff7ffd000 r--p 00037000 fc:02 2337 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 7ffff7ffd000-7ffff7fff000 rw-p 00039000 fc:02 2337 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack] ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]
何人传来无效指针? 库函数出问题概率很低,所以找自己写的函数. 我们知道问题出在name变量上
frame 3 、然后用 p name 、info locals 都能看到是无效的
info frame 看函数的栈帧信息
既然是name错了 它哪里来的? get_name ,
get_name调试 设置get_name的断点进行调试
返回值看rax(通常通用寄存器第一个存返回值),rax在返回的时候是没问题的,那哪里出问题了。。此时事情就很奇怪了
返回后执行ni, 单步一下,再看值,就错了
答案揭晓 问题就出在这一条指令上,它改变了值的大小
cdqe: 扩展指令,使用eax的最高位拓展rax高32位的所有位
x86下和arm不太一样, sxtw这条指令有问题(arm下
这是编译器故意产生的指令,和没有include这个函数有关系 ,再深入的..就先放放
问题与知识补充
include进来就没问题了
#include “parser.c”
但是有个新的warning
32位下编译没问题
为什么会有一个nop指令
nop插桩
如何跑arm系统 macbook直接可以
x86怎么装?:看样子基本上离不开qemu
云服务器买
https://blog.csdn.net/chenxiangneu/article/details/78955462
设置符号服务器?
失败了如何再执行? info signal
info handle
handle SIGSEGV nopass (不给应用程序
再跑一遍又会收到这个信号
smd指令。。。
用户空间 内核空间大小