前言

​ 之前知道老师是因为《软件调试》这本书,非常厉害,但是因为主要是windows的一直还没读(因为主要学习Linux,不过最近越发感觉不能局限于一个系统),最近看见一个大佬在朋友圈转发这个课,才发现张老师原来是有自己的公司和培训,看了下来太牛了,立马报名了。

​ (老师b站也有号, 官网nanocode.cn,) 还有其他很多优秀的课程,看来要好好买一波了..这才是真正有价值的知识付费!!

背景

​ 老师将实际案例中的一个问题抽象出了一个很简单的demo, 用于搜索argv[0]的名字, 其实也就是当前可执行程序的名字,本身看着这个代码是没有什么问题的(不,有问题,没有include parser.c)

parser.c

1
2
3
4
5
6
7
8
9
// parser.c
#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

段错误,访问了不该访问的

image-20231024145754493

调试分析

回溯粗看

​ 朱熹的不远复:“不远复”,出自《易经》“不远之复,以修身也”。和孔子的吾日三省吾身有异曲同工之妙,人要不断反思自己,反省走的路.

​ bt命令回溯 调用栈 , 库函数一般都经过很多修改和测试、一般没问题,所以更多的还是自己写的代码的问题

image-20231024150010518

​ 可以减少bt的显示,如 bt -frame-info short-location 不带源代码位置

​ frame 3查看自己写的函数里的栈帧的情况, list查看源代码, 这样就能够看到上下文, disass查看汇编

image-20231024150656532

​ 所以能够看到问题是在 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 用来做准备工作?

细看崩溃指令

​ 来到崩溃的那一行汇编来看看什么情况,这里指向的含义是 尝试执行这条指令,但是失败了

image-20231024151819672

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 都能看到是无效的

Snipaste_2023-10-26_17-16-56

​ info frame 看函数的栈帧信息

​ 既然是name错了 它哪里来的? get_name

get_name调试

​ 设置get_name的断点进行调试

​ 返回值看rax(通常通用寄存器第一个存返回值),rax在返回的时候是没问题的,那哪里出问题了。。此时事情就很奇怪了

Snipaste_2023-10-26_17-38-09

返回后执行ni, 单步一下,再看值,就错了

Snipaste_2023-10-26_17-35-28

答案揭晓

​ 问题就出在这一条指令上,它改变了值的大小

​ cdqe: 扩展指令,使用eax的最高位拓展rax高32位的所有位

​ x86下和arm不太一样, sxtw这条指令有问题(arm下

这是编译器故意产生的指令,和没有include这个函数有关系,再深入的..就先放放

问题与知识补充

  1. 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指令。。。

用户空间 内核空间大小