CVE-2018-10538 初探wavpack测试crashes样本分析

CVE-2018-10538

  前期通过CVE-2018-10538复现环境,可以发现CVE issue问题描述为:An issue was discovered in WavPack 5.1.0 and earlier for WAV input. Out-of-bounds writes can occur because ParseRiffHeaderConfig in riff.c does not validate the sizes of unknown chunks before attempting memory allocation, related to a lack of integer-overflow protection within a bytes_to_copy calculation and subsequent malloc call, leading to insufficient memory allocation.

  最近使用afl对wavpack进行了一次模糊测试,跑出来了15个crashes,针对其中test09.wav样本进行一下分析,对应CVE-2018-10538。如有错误请各位大佬多多指教。

1 关于崩溃样本文件

  crash09-关于id_000009,sig_11,src_001266,time_25197388,op_havoc,rep_16,出自afl fuzz的结果,系统栈帧崩溃时的backtrace如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
gdb-peda$ bt
#0 __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:416
#1 0x00007ffff7a2438e in __GI__IO_file_xsgetn (fp=0x555555675580, data=<optimized out>, n=0x95959596) at fileops.c:1304
#2 0x00007ffff7a17f13 in __GI__IO_fread (buf=0x0, size=size@entry=0x1, count=count@entry=0x95959596, fp=fp@entry=0x555555675580) at iofread.c:38
#3 0x00005555555b4cd6 in fread (__stream=0x555555675580, __n=0x95959596, __size=0x1, __ptr=<optimized out>) at /usr/include/x86_64-linux-gnu/bits/stdio2.h:297
#4 DoReadFile (hFile=hFile@entry=0x555555675580, lpBuffer=lpBuffer@entry=0x0, nNumberOfBytesToRead=nNumberOfBytesToRead@entry=0x95959596,
lpNumberOfBytesRead=lpNumberOfBytesRead@entry=0x7fffffffba0c) at utils.c:618
#5 0x000055555559c086 in ParseRiffHeaderConfig (infile=0x555555675580, infilename=0x5555556752c0 "test09.wav", fourcc=<optimized out>, wpc=0x555555675300,
config=0x7fffffffbc60) at riff.c:296
#6 0x0000555555599386 in pack_file (infilename=0x5555556752c0 "test09.wav", outfilename=0x5555556752e0 "test09.wv", out2filename=0x0, config=<optimized out>)
at wavpack.c:1776
#7 0x000055555555af58 in main (argc=<optimized out>, argc@entry=0x3, argv=<optimized out>, argv@entry=0x7fffffffe338) at wavpack.c:1272
#8 0x00007ffff79b90b3 in __libc_start_main (main=0x555555557600 <main>, argc=0x3, argv=0x7fffffffe338, init=<optimized out>, fini=<optimized out>,
rtld_fini=<optimized out>, stack_end=0x7fffffffe328) at ../csu/libc-start.c:308
#9 0x000055555556d8ee in _start ()

  其中,#3,#4分别表示在fread和utils.c 618行函数DoReadFile出现错误,从具体调用关系上看,#4在调用#3过程中,需要调用fread函数,fread函数的标准释义是:

size_t fread( void restrict buffer, size_t size, size_t count, FILE restrict stream );

  其中restrict buffer表示要拷贝数据,从steam中读取size*count个字节数据到buffer中,在执行文件中发现 实际参数值为buf=0x0, size=size@entry=0x1, count=count@entry=0x95959596, fp=fp@entry=0x555555675580。一般而言,系统函数库中的函数一般不会出错的,出错的问题在于我们调用时使用了不正确的参数或者地址,导致系统出错。跟着bt的结果,我们一步步发掘系统报段错误的原因:


2 分析崩溃原因

  崩溃前最后一个自写的函数就是DoReadFile,函数调用参数分别如下
(buf=0x0, size=size@entry=0x1, count=count@entry=0x95959596, fp=fp@entry=0x555555675580)

1
2
3
4
5
6
7
8
Breakpoint 1, DoReadFile (hFile=hFile@entry=0x555555675580, 
lpBuffer=lpBuffer@entry=0x7fffffffbb4c,
nNumberOfBytesToRead=nNumberOfBytesToRead@entry=0x8,
lpNumberOfBytesRead=lpNumberOfBytesRead@entry=0x7fffffffbb1c) at utils.c:620

0x000055555559b3fa in ParseRiffHeaderConfig (infile=0x555555675580,
infilename=0x455641570003877c <error: Cannot access memory at address 0x455641570003877c>, fourcc=<optimized out>, wpc=0x555555675300, config=0x7fffffffbd70) at riff.c:75
75 if ((!DoReadFile (infile, ((char *) &riff_chunk_header) + 4, sizeof (RiffChunkHeader) - 4, &bcount) ||

2.1 分析Core dump文件

core转储文件可以用于发现具体错误点,core dump文件需要提前设置,这里不再过多赘述。从分析core dump文件开始,发现报错是出现了glibc文件之中。

2.2 分析出错代码位置

1
2
3
Core was generated by `./cli/wavpack -y test09.wav'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:416

出错的代码部分,fileops.c:1304行,glibc版本为2.3.1,在线链接是:https://elixir.bootlin.com/glibc/glibc-2.31/source/libio/fileops.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21


while (want > 0)
{

have = fp->_IO_read_end - fp->_IO_read_ptr;
if (want <= have) ## 第二部分,输入缓冲区里已经有足够的字符,则直接把缓冲区里的字符给目标buff
{
memcpy (s, fp->_IO_read_ptr, want);
fp->_IO_read_ptr += want;
want = 0;
}
else
{
if (have > 0) ## 第三部分,输入缓冲区里有部分字符,但是没有达到fread的size需求,先把已有的拷贝至目标buff
{
memcpy (s, fp->_IO_read_ptr, have); <<<============程序运行到这一步报错,fileops.c:1304
s += have;
want -= have;
fp->_IO_read_ptr += have;
}

2.3 拷贝内存时报错

  在memcpy函数拷贝数据出现越界,第1个参数*s为数据data,拷贝的目标地址,第二个fp参数,拷贝的源地址,fp->_IO_read_ptr是FILE结构体的一部分,偏移为8,表示读取的起始地址。have表示传入数据长度。在IO_FILE结构体中,fp也是一个IO_FILE结构体,遵从IO_FILE标准结构。

代码报错fileops.c:1304

  程序运行逻辑中,have表示实际能够存储的内存大小,want表示当前传入的参数,报错上下文也就是want = 0x95959596。此时,have < want,因此,程序进入if (have > 0)对应的逻辑块,程序直接如上图所示的代码行,直接从_IO_read_ptr标记的内存处拷贝have个大小的,该部分可能并不会引起溢出

  可以看到file类型的fp函数IO_read_ptr地址偏移为8,通过gdb-peda看到fp第二个地址为_IO_read_ptr=0x555555676258,最大读指针地址_IO_read_end=0x55555555676940,其中可读取空间包括0x940-0x258=0x6E8个地址空间。memcpy如果执行复制n=0x95959596(大约相当于2G内存)后,_IO_read_ptr(0x555555676258)+ n(0x95959596)= 0x5555EAFCF7EE > 程序所在内存空间,导致覆盖了0x0x555555676258~0x55555EAFCF7EE范围的数据,这一覆盖范围远远大于映射的内存区域,导致出现段错误,但这个只是我们的猜想。

还原整个调试过程,如下所示:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
------------------------------------------------------------------------------]
Legend: code, data, rodata, value
289 char *buff = malloc (bytes_to_copy); # <-----------------申请2G内存区域,申请失败返回0,导致没有申请成功
gdb-peda$ p bytes_to_copy
$4 = 0x95959596
gdb-peda$ n
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x7fffffffbc7c --> 0x8
RCX: 0x9595
RDX: 0x95959595
RSI: 0x959595
RDI: 0x0
RBP: 0x555555675580 --> 0xfbad2488
RSP: 0x7fffffffbc40 --> 0x0
RIP: 0x55555559bfcb (<ParseRiffHeaderConfig+3515>: test r8d,r8d)
R8 : 0x0
R9 : 0x95959595
R10: 0x2
R11: 0x7ffff793a900 --> 0x9595959595959595
R12: 0x7fffffffbed0 --> 0x0
R13: 0x555555675300 --> 0x0
R14: 0x95959596
R15: 0x7fffffffbca0 --> 0x9595959595959595
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x55555559bfbc <ParseRiffHeaderConfig+3500>: mov r8d,DWORD PTR [rip+0xd84fd] # 0x5555556744c0 <debug_logging_mode>
0x55555559bfc3 <ParseRiffHeaderConfig+3507>: mov r9d,DWORD PTR [rsp+0x28]
0x55555559bfc8 <ParseRiffHeaderConfig+3512>: mov rdi,rax
=> 0x55555559bfcb <ParseRiffHeaderConfig+3515>: test r8d,r8d
0x55555559bfce <ParseRiffHeaderConfig+3518>: je 0x55555559c035 <ParseRiffHeaderConfig+3621>
0x55555559bfd0 <ParseRiffHeaderConfig+3520>: lea rsp,[rsp-0x98]
0x55555559bfd8 <ParseRiffHeaderConfig+3528>: mov QWORD PTR [rsp],rdx
0x55555559bfdc <ParseRiffHeaderConfig+3532>: mov QWORD PTR [rsp+0x8],rcx
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffbc40 --> 0x0
0008| 0x7fffffffbc48 --> 0x5555556752c0 ("test09.wav")
0016| 0x7fffffffbc50 --> 0x68b81
0024| 0x7fffffffbc58 --> 0x7fffffffbcc0 --> 0x0
0032| 0x7fffffffbc60 --> 0x1
0040| 0x7fffffffbc68 --> 0x555595959595
0048| 0x7fffffffbc70 --> 0x3
0056| 0x7fffffffbc78 --> 0x800000000
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
291 if (debug_logging_mode)
gdb-peda$ n
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x7fffffffbc7c --> 0x8
RCX: 0x9595
RDX: 0x95959595
RSI: 0x959595
RDI: 0x0
RBP: 0x555555675580 --> 0xfbad2488
RSP: 0x7fffffffbc40 --> 0x0
RIP: 0x55555559c035 (<ParseRiffHeaderConfig+3621>: nop DWORD PTR [rax])
R8 : 0x0
R9 : 0x95959595
R10: 0x2
R11: 0x7ffff793a900 --> 0x9595959595959595
R12: 0x7fffffffbed0 --> 0x0
R13: 0x555555675300 --> 0x0
R14: 0x95959596
R15: 0x7fffffffbca0 --> 0x9595959595959595
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x55555559c029 <ParseRiffHeaderConfig+3609>: xor eax,eax
0x55555559c02b <ParseRiffHeaderConfig+3611>: call 0x5555555b47d0 <error_line>
0x55555559c030 <ParseRiffHeaderConfig+3616>: mov rdi,QWORD PTR [rsp+0x28]
=> 0x55555559c035 <ParseRiffHeaderConfig+3621>: nop DWORD PTR [rax]
0x55555559c038 <ParseRiffHeaderConfig+3624>: lea rsp,[rsp-0x98]
0x55555559c040 <ParseRiffHeaderConfig+3632>: mov QWORD PTR [rsp],rdx
0x55555559c044 <ParseRiffHeaderConfig+3636>: mov QWORD PTR [rsp+0x8],rcx
0x55555559c049 <ParseRiffHeaderConfig+3641>: mov QWORD PTR [rsp+0x10],rax
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffbc40 --> 0x0
0008| 0x7fffffffbc48 --> 0x5555556752c0 ("test09.wav")
0016| 0x7fffffffbc50 --> 0x68b81
0024| 0x7fffffffbc58 --> 0x7fffffffbcc0 --> 0x0
0032| 0x7fffffffbc60 --> 0x1
0040| 0x7fffffffbc68 --> 0x555595959595
0048| 0x7fffffffbc70 --> 0x3
0056| 0x7fffffffbc78 --> 0x800000000
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
296 if (!DoReadFile (infile, buff, bytes_to_copy, &bcount) ||
gdb-peda$ p buff
$5 = <optimized out>
gdb-peda$ p bytes_to_copy # <-----------------进入到读取FIle文件到memecpy的源目的地址中,由于申请的地址为空,则出现错误,无法写入到指定内存区域
$6 = 0x95959596
gdb-peda$ vmmap
Start End Perm Name
0x0000555555554000 0x0000555555557000 r--p /home/pwn/aflsmart/WavPack/cli/wavpack
0x0000555555557000 0x0000555555665000 r-xp /home/pwn/aflsmart/WavPack/cli/wavpack
0x0000555555665000 0x0000555555673000 r--p /home/pwn/aflsmart/WavPack/cli/wavpack
0x0000555555673000 0x0000555555674000 r--p /home/pwn/aflsmart/WavPack/cli/wavpack
0x0000555555674000 0x0000555555675000 rw-p /home/pwn/aflsmart/WavPack/cli/wavpack
0x0000555555675000 0x0000555555696000 rw-p [heap]
0x00007ffff78f8000 0x00007ffff793b000 rw-p mapped
0x00007ffff796a000 0x00007ffff796c000 rw-p mapped
0x00007ffff796c000 0x00007ffff7972000 r--p /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
0x00007ffff7972000 0x00007ffff7983000 r-xp /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
0x00007ffff7983000 0x00007ffff7989000 r--p /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
0x00007ffff7989000 0x00007ffff798a000 r--p /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
0x00007ffff798a000 0x00007ffff798b000 rw-p /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
0x00007ffff798b000 0x00007ffff798f000 rw-p mapped
0x00007ffff798f000 0x00007ffff7990000 r--p /usr/lib/x86_64-linux-gnu/libdl-2.31.so
0x00007ffff7990000 0x00007ffff7992000 r-xp /usr/lib/x86_64-linux-gnu/libdl-2.31.so
0x00007ffff7992000 0x00007ffff7993000 r--p /usr/lib/x86_64-linux-gnu/libdl-2.31.so
0x00007ffff7993000 0x00007ffff7994000 r--p /usr/lib/x86_64-linux-gnu/libdl-2.31.so
0x00007ffff7994000 0x00007ffff7995000 rw-p /usr/lib/x86_64-linux-gnu/libdl-2.31.so
0x00007ffff7995000 0x00007ffff79b7000 r--p /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x00007ffff79b7000 0x00007ffff7b2f000 r-xp /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x00007ffff7b2f000 0x00007ffff7b7d000 r--p /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x00007ffff7b7d000 0x00007ffff7b81000 r--p /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x00007ffff7b81000 0x00007ffff7b83000 rw-p /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x00007ffff7b83000 0x00007ffff7b87000 rw-p mapped
0x00007ffff7b87000 0x00007ffff7bff000 r--p /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1
0x00007ffff7bff000 0x00007ffff7d9a000 r-xp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1
0x00007ffff7d9a000 0x00007ffff7e2b000 r--p /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1
0x00007ffff7e2b000 0x00007ffff7e57000 r--p /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1
0x00007ffff7e57000 0x00007ffff7e59000 rw-p /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1
0x00007ffff7e59000 0x00007ffff7e5d000 rw-p mapped
0x00007ffff7e5d000 0x00007ffff7e6a000 r--p /usr/lib/x86_64-linux-gnu/libm-2.31.so
0x00007ffff7e6a000 0x00007ffff7f11000 r-xp /usr/lib/x86_64-linux-gnu/libm-2.31.so
0x00007ffff7f11000 0x00007ffff7faa000 r--p /usr/lib/x86_64-linux-gnu/libm-2.31.so
0x00007ffff7faa000 0x00007ffff7fab000 r--p /usr/lib/x86_64-linux-gnu/libm-2.31.so
0x00007ffff7fab000 0x00007ffff7fac000 rw-p /usr/lib/x86_64-linux-gnu/libm-2.31.so
0x00007ffff7fac000 0x00007ffff7fae000 rw-p mapped
0x00007ffff7fc9000 0x00007ffff7fcd000 r--p [vvar]
0x00007ffff7fcd000 0x00007ffff7fcf000 r-xp [vdso]
0x00007ffff7fcf000 0x00007ffff7fd0000 r--p /usr/lib/x86_64-linux-gnu/ld-2.31.so
0x00007ffff7fd0000 0x00007ffff7ff3000 r-xp /usr/lib/x86_64-linux-gnu/ld-2.31.so
0x00007ffff7ff3000 0x00007ffff7ffb000 r--p /usr/lib/x86_64-linux-gnu/ld-2.31.so
0x00007ffff7ffc000 0x00007ffff7ffd000 r--p /usr/lib/x86_64-linux-gnu/ld-2.31.so
0x00007ffff7ffd000 0x00007ffff7ffe000 rw-p /usr/lib/x86_64-linux-gnu/ld-2.31.so
0x00007ffff7ffe000 0x00007ffff7fff000 rw-p mapped
0x00007ffffffde000 0x00007ffffffff000 rw-p [stack]
0xffffffffff600000 0xffffffffff601000 --xp [vsyscall]

gdb-peda$
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x000055555559c05a in ParseRiffHeaderConfig (infile=0x555555675580, infilename=0x9595 <error: Cannot access memory at address 0x9595>,
fourcc=<optimized out>, wpc=0x555555675300, config=0x7fffffffbed0) at riff.c:296
296 if (!DoReadFile (infile, buff, bytes_to_copy, &bcount) ||

```。
通过调试,关键点在286行,如下代码段

```bash
int bytes_to_copy = (chunk_header.ckSize + 1) & ~1L; # <<<<<<代码出错的关键点 288行
char *buff = malloc (bytes_to_copy); # <<<<<<代码出错的关键点

if (debug_logging_mode)
error_line ("extra unknown chunk \"%c%c%c%c\" of %d bytes",
chunk_header.ckID [0], chunk_header.ckID [1], chunk_header.ckID [2],
chunk_header.ckID [3], chunk_header.ckSize);

if (!DoReadFile (infile, buff, bytes_to_copy, &bcount) ||
bcount != bytes_to_copy ||
(!(config->qmode & QMODE_NO_STORE_WRAPPER) &&
!WavpackAddWrapper (wpc, buff, bytes_to_copy))) {
error_line ("%s", WavpackGetErrorMessage (wpc));
free (buff);
return WAVPACK_SOFT_ERROR;
}

2.4 总结

  出错核心在malloc函数,在申请内存过程中,申请bytes_to_copy=0x95959596(通过实际计算大约是2G内存),系统在执行中无法满足就返回0,表示堆上申请内存失败。在后续的DoReadFile函数中,依然直接读取0x0处内存地址的数据,导致系统报错,出现段错误。

3 发现的4个问题

  1. 一个程序运行可以申请的最大内存应该满足多少?
  2. 为什么在调试中,部分不属于申请较大内存的操作也同样报错,这个内存申请的阀值是多少?
  3. wavpack转储文件的格式是什么样的?为什么申请内存是0x95959596,而不是其他值?
  4. 其他所欠缺的知识点,包括IO_FILE文件结构、fread函数操作、x64架构下寄存器传参的具体实现?

针对上述问题,计划再出几个博客记录,所以在这里不做过多赘述。

4.1 IO_FILE文件结构

参考知识:_IO_FILE结构体可以看到,IO_read_ptr在地址偏移8字节位置,IO_read_end在偏移16字节的位置,一般情况下IO_read_ptr和IO_read_base保持一致。

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
struct _IO_FILE
{
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */ // 0偏移,8字节

/* The following pointers correspond to the C++ streambuf protocol. */ //8偏移,8字节
char *_IO_read_ptr; /* Current read pointer */
char *_IO_read_end; /* End of get area. */
char *_IO_read_base; /* Start of putback+get area. */
char *_IO_write_base; /* Start of put area. */
char *_IO_write_ptr; /* Current put pointer. */
char *_IO_write_end; /* End of put area. */
char *_IO_buf_base; /* Start of reserve area. */
char *_IO_buf_end; /* End of reserve area. */

/* The following fields are used to support backing up and undo. */
char *_IO_save_base; /* Pointer to start of non-current get area. */
char *_IO_backup_base; /* Pointer to first valid character of backup area */
char *_IO_save_end; /* Pointer to end of non-current get area. */

struct _IO_marker *_markers;

struct _IO_FILE *_chain;

int _fileno;
int _flags2;

查看FILE结构体中各字段的具体数值,遵从小端模式:

1
2
3
4
5
gdb-peda$ x/32w 0x555555675580
0x555555675580: 0xfbad2488 0x00000000 0x55676258 0x00005555
0x555555675590: 0x55676940 0x00005555 0x55675940 0x00005555
0x5555556755a0: 0x55675940 0x00005555 0x55675940 0x00005555
0x5555556755b0: 0x55675940 0x00005555 0x55675940 0x00005555

vmmap查看当前系统能够使用的内存区域,如下方系统显示,不管是超出内存区复制数据抑或覆盖的数据超过现内存边界,都会产生段错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
gdb-peda$ vmmap
Start End Perm Name
0x0000555555554000 0x0000555555557000 r--p /home/pwn/aflsmart/WavPack/cli/wavpack
0x0000555555557000 0x0000555555665000 r-xp /home/pwn/aflsmart/WavPack/cli/wavpack
0x0000555555665000 0x0000555555673000 r--p /home/pwn/aflsmart/WavPack/cli/wavpack
0x0000555555673000 0x0000555555674000 r--p /home/pwn/aflsmart/WavPack/cli/wavpack
0x0000555555674000 0x0000555555675000 rw-p /home/pwn/aflsmart/WavPack/cli/wavpack

0x00007ffff7993000 0x00007ffff7994000 r--p /usr/lib/x86_64-linux-gnu/libdl-2.31.so
0x00007ffff7994000 0x00007ffff7995000 rw-p /usr/lib/x86_64-linux-gnu/libdl-2.31.so
0x00007ffff7995000 0x00007ffff79b7000 r--p /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x00007ffff79b7000 0x00007ffff7b2f000 r-xp /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x00007ffff7b2f000 0x00007ffff7b7d000 r--p /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x00007ffff7b7d000 0x00007ffff7b81000 r--p /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x00007ffff7b81000 0x00007ffff7b83000 rw-p /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x00007ffff7b83000 0x00007ffff7b87000 rw-p mapped

4.2 调试中寄存器细节问题

1
2
3
4
   0x7ffff7b2077f <__memmove_avx_unaligned_erms+399>:	lea    rcx,[rdi+rdx*1-0x20]
0x7ffff7b20784 <__memmove_avx_unaligned_erms+404>: mov r8,rdi //r8=
0x7ffff7b20787 <__memmove_avx_unaligned_erms+407>: and r8,0x1f
=> 0x7ffff7b2078b <__memmove_avx_unaligned_erms+411>: sub r8,0x20

R8在执行sub命令前为0x00,因此减去0x20导致异常,R8变为0xffffffffffffffe0

由于sub R8,0x20,R8=0后,导致 R8:0xffffffffffffffe0,后续对寄存器操作均使用R8寄存器进行计算。

1
2
3
=> 0x7ffff7a24380 <__GI__IO_file_xsgetn+272>:	mov    rdi,r13
0x7ffff7a24383 <__GI__IO_file_xsgetn+275>: mov rdx,rbp
0x7ffff7a24386 <__GI__IO_file_xsgetn+278>: sub r12,rbp

在执行到这一行代码时,rbp、r13、r12进行计算,需要使用

(1)vmovdqu 指令的具体作用,导致这个问题出现的主要原因,以及如何避免这种问题。

4.3 寄存器部分功能描述

1.对寄存器掌握不深,对x64环境下各个寄存器的功能不熟悉。

第64位 第32位 第16位 第8位 一般作用
63 31 15 7 0
%rax %eax %ax %al 返回值
%rbx %ebx %bx %bl 被调用者保存
%rcx %ecx %cx %cl 第四个参数
%rdx %edx %dx %dl 第三个参数
%rsi %esi %si %sil 第二个参数
%rdi %edi %di %dil 第一个参数
%rbp %ebp %bp %bpl 被调用者保存
%rsp %esp %sp %spl 栈指针
%r8 %r8d %r8w %r8b 第五个参数
%r9 %r9d %r9w %r9b 第六个参数
%r10 %r10d %r10w %r10b 调用者保存
%r11 %r11d %r11w %r11b 调用者保存
%r12 %r12d %r12w %r12b 被调用者保存
%r13 %r13d %r13w %r13b 被调用者保存
%r14 %r14d %r14w %r14b 被调用者保存
%r15 %r15d %r15w %r15b 被调用者保存

其中,字节级操作可以访问最低的字节,16位操作可以访问最低的2个字 节32位操作可以访问最低的4个字节,而64位操作可以访问整个寄存器

课后温习

  1. fread函数的详细实现讲解 https://ray-cp.github.io/archivers/IO_FILE_fread_analysis
  2. glibc-2.3.1下fileops.c的实现 https://elixir.bootlin.com/glibc/glibc-2.31/source/libio/fileops.c