0x00

总结记录一下_IO_FILE的基础知识
参考博客
linux IO_FILE 利用_io list all结构体-CSDN博客
【IO_FILE】源码详解 | Loora1N’s Blog | 鹭雨
【PWN】iofile | 狼组安全团队公开知识库

0x01 _IO_FILE结构

FILE 在 Linux 系统的标准 IO 库中是用于描述文件的结构,称为文件流。 FILE 结构在程序执行 fopen 等函数时会进行创建,并分配在堆中。我们常定义一个指向 FILE 结构的指针来接收这个返回值。FILE 结构定义在 libio.h 中,如下所示

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
struct _IO_FILE {
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags

/* The following pointers correspond to the C++ streambuf protocol. */
/* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
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;
#if 0
int _blksize;
#else
int _flags2;
#endif
_IO_off_t _old_offset; /* This used to be _offset but it's too small. */

#define __HAVE_COLUMN /* temporary */
/* 1+column number of pbase(); 0 is unknown. */
unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];

/* char* _save_gptr; char* _save_egptr; */

_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};
struct _IO_FILE_complete
{
struct _IO_FILE _file;
#endif
#if defined _G_IO_IO_FILE_VERSION && _G_IO_IO_FILE_VERSION == 0x20001
_IO_off64_t _offset;
# if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
/* Wide character stream stuff. */
struct _IO_codecvt *_codecvt;
struct _IO_wide_data *_wide_data;
struct _IO_FILE *_freeres_list;
void *_freeres_buf;
# else
void *__pad1;
void *__pad2;
void *__pad3;
void *__pad4;

size_t __pad5;
int _mode;
/* Make sure we don't get into trouble again. */
char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
#endif
};

其中的第一个成员_flags是一个非常重要且关键的值。其高两个字节是_IO_MAGIC(一个宏,其值为0xFBAD0000),用于检查_IO_file的合法性。glibc中存在的检查如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define _IO_MAGIC 0xFBAD0000
#define _IO_MAGIC_MASK 0xFFFF0000
/*glibc中一个用于检查的宏如下*/
#define CHECK_FILE(FILE, RET) do {
if ((FILE) == NULL ||
((FILE)->_flags & _IO_MAGIC_MASK) != _IO_MAGIC) {
__set_errno (EINVAL);
return RET;
}
} while (0)
/*glibc 在遍历 _IO_list_all (全局 FILE 链表)进行 flush/unbuffer 操作时,通常使用如下方式过滤不合法的文件*/
for (fp = (FILE *) _IO_list_all; fp; fp = fp->_chain) {
if (((fp->_flags & _IO_MAGIC_MASK) != _IO_MAGIC) …)
continue;

}

_flags的低二字节,也就是低16bit是一个状态码,用于控制控制 FILE 的读写状态、缓存、put/get 模式、错误状态等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#define _IO_MAGIC 0xFBAD0000           /* Magic number 文件结构体的魔数,用于标识文件结构体的有效性 */
#define _OLD_STDIO_MAGIC 0xFABC0000 /* Emulate old stdio 模拟旧的标准输入输出库(stdio)行为的魔数 */
#define _IO_MAGIC_MASK 0xFFFF0000 /* Magic mask 魔数掩码,用于从 _flags 变量中提取魔数部分 */
#define _IO_USER_BUF 1 /* User owns buffer; don't delete it on close. 用户拥有缓冲区,不在关闭时删除缓冲区 */
#define _IO_UNBUFFERED 2 /* Unbuffered 无缓冲模式,直接进行I/O操作,不使用缓冲区 */
#define _IO_NO_READS 4 /* Reading not allowed 不允许读取操作 */
#define _IO_NO_WRITES 8 /* Writing not allowed 不允许写入操作 */
#define _IO_EOF_SEEN 0x10 /* EOF seen 已经到达文件结尾(EOF) */
#define _IO_ERR_SEEN 0x20 /* Error seen 已经发生错误 */
#define _IO_DELETE_DONT_CLOSE 0x40 /* Don't call close(_fileno) on cleanup. 不关闭文件描述符 _fileno,在清理时不调用 close 函数 */
#define _IO_LINKED 0x80 /* Set if linked (using _chain) to streambuf::_list_all. 链接到一个链表(使用 _chain 指针),用于 streambuf::_list_all */
#define _IO_IN_BACKUP 0x100 /* In backup 处于备份模式 */
#define _IO_LINE_BUF 0x200 /* Line buffered 行缓冲模式,在输出新行时刷新缓冲区 */
#define _IO_TIED_PUT_GET 0x400 /* Set if put and get pointer logically tied. 在输出和输入指针逻辑上绑定时设置 */
#define _IO_CURRENTLY_PUTTING 0x800 /* Currently putting 当前正在执行 put 操作 */
#define _IO_IS_APPENDING 0x1000 /* Is appending 处于附加模式(在文件末尾追加内容) */
#define _IO_IS_FILEBUF 0x2000 /* Is file buffer 是一个文件缓冲区 */
#define _IO_BAD_SEEN 0x4000 /* Bad seen 遇到错误(bad flag set) */
#define _IO_USER_LOCK 0x8000 /* User lock 用户锁定,防止其他线程访问 */

_IO_read_ptr正在使用的input缓冲区的input地址
_IO_read_end input缓冲区的结束地址
_IO_read_base input缓冲区的基址
_IO_write_base output缓冲区的基址
_IO_write_ptr 指向还没输出的那个字节
_IO_write_end output缓冲区的结束地址
_IO_buf_base input和output缓冲区的基址
_IO_buf_end input和output缓冲区的结束地址
_chain_IO_FILE *类型,存放着一个单链表,用于串联所有的file stream(其实就是_IO_FILE结构体),表头通过_IO_list_all指针访问,注意_IO_list_all便是_IO_FILE_plus *类型的。结构如下所示

_fileno 与文件相关的文件描述符(例如stdin为0,stdout为1,stderr为2)
_vtable_offset 存放虚表(virtual table)的偏移
_offset 存放当前文件的偏移
至于vtable,为函数指针结构体,存放着各种 IO 相关的函数的指针,可以在另一个结构体_IO_FILE_plus中看到。

1
2
3
4
5
struct _IO_FILE_plus
{
_IO_FILE file;
const struct _IO_jump_t *vtable;
};

_IO_jump_t定义如下

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
struct _IO_jump_t
{
JUMP_FIELD(size_t, __dummy);
JUMP_FIELD(size_t, __dummy2);
JUMP_FIELD(_IO_finish_t, __finish);
JUMP_FIELD(_IO_overflow_t, __overflow);
JUMP_FIELD(_IO_underflow_t, __underflow);
JUMP_FIELD(_IO_underflow_t, __uflow);
JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
/* showmany */
JUMP_FIELD(_IO_xsputn_t, __xsputn);
JUMP_FIELD(_IO_xsgetn_t, __xsgetn);
JUMP_FIELD(_IO_seekoff_t, __seekoff);
JUMP_FIELD(_IO_seekpos_t, __seekpos);
JUMP_FIELD(_IO_setbuf_t, __setbuf);
JUMP_FIELD(_IO_sync_t, __sync);
JUMP_FIELD(_IO_doallocate_t, __doallocate);
JUMP_FIELD(_IO_read_t, __read);
JUMP_FIELD(_IO_write_t, __write);
JUMP_FIELD(_IO_seek_t, __seek);
JUMP_FIELD(_IO_close_t, __close);
JUMP_FIELD(_IO_stat_t, __stat);
JUMP_FIELD(_IO_showmanyc_t, __showmanyc);
JUMP_FIELD(_IO_imbue_t, __imbue);
#if 0
get_column;
set_column;
#endif
};

IO函数便会调用这个表中的函数,以fopen,fread,fwrite,fclose为例
fopen函数是在分配空间,建立FILE结构体,未调用vtable中的函数。
fread函数中调用的vtable函数有:
* _IO_sgetn函数调用了vtable的_IO_file_xsgetn
* _IO_doallocbuf函数调用了vtable的_IO_file_doallocate以初始化输入缓冲区。
* vtable中的_IO_file_doallocate调用了vtable中的__GI__IO_file_stat以获取文件信息。
* __underflow函数调用了vtable中的_IO_new_file_underflow实现文件数据读取。
* vtable中的_IO_new_file_underflow调用了vtable__GI__IO_file_read最终去执行系统调用read。
fwrite 函数调用的vtable函数有:
* _IO_fwrite函数调用了vtable的_IO_new_file_xsputn
* _IO_new_file_xsputn函数调用了vtable中的_IO_new_file_overflow实现缓冲区的建立以及刷新缓冲区。
* vtable中的_IO_new_file_overflow函数调用了vtable的_IO_file_doallocate以初始化输入缓冲区。
* vtable中的_IO_file_doallocate调用了vtable中的__GI__IO_file_stat以获取文件信息。
* new_do_write中的_IO_SYSWRITE调用了vtable_IO_new_file_write最终去执行系统调用write。
fclose函数调用的vtable函数有:
* 在清空缓冲区的_IO_do_write函数中会调用vtable中的函数。
* 关闭文件描述符_IO_SYSCLOSE函数为vtable中的__close函数。
* _IO_FINISH函数为vtable中的__finish函数。

一个程序初始情况下有三个_IO_FILE,分别为 _IO_2_1_stderr__IO_2_1_stdout__IO_2_1_stdin_IO_list_all初始默认指向_IO_2_1_stderr_
并且存在 3 个全局指针 stdinstdoutstderr 分别指向 _IO_2_1_stdin__IO_2_1_stdout__IO_2_1_stderr_ 三个结构体
结构如下所示

程序在进行文件IO操作时候,便会创建_IO_FILE_plus结构体,并使用头插法接入_IO_list_all链表中
下面就分析一下fopenfreadfwritefclose四个函数,来学习一下_IO_FILE_plus的作用机制(因为笔者本地没有下载glibc的源码,自己看源码的时候都是从其他地方看,因为懒得再从其他地方贴源码过来,所以下面调试都是硬看汇编的
再放上结构体内的偏移对应

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
amd64:

0x0:'_flags',
0x8:'_IO_read_ptr',
0x10:'_IO_read_end',
0x18:'_IO_read_base',
0x20:'_IO_write_base',
0x28:'_IO_write_ptr',
0x30:'_IO_write_end',
0x38:'_IO_buf_base',
0x40:'_IO_buf_end',
0x48:'_IO_save_base',
0x50:'_IO_backup_base',
0x58:'_IO_save_end',
0x60:'_markers',
0x68:'_chain',
0x70:'_fileno',
0x74:'_flags2',
0x78:'_old_offset',
0x80:'_cur_column',
0x82:'_vtable_offset',
0x83:'_shortbuf',
0x88:'_lock',
0x90:'_offset',
0x98:'_codecvt',
0xa0:'_wide_data',
0xa8:'_freeres_list',
0xb0:'_freeres_buf',
0xb8:'__pad5',
0xc0:'_mode',
0xc4:'_unused2',
0xd8:'vtable'

0x02 fopen函数

写个demo来调试一下(glibc2.35)

1
2
3
4
5
6
7
8
9
#include<stdio.h>
#include<stdlib.h>

int main(){

FILE*fp = fopen("test.txt","wb");
char *ptr = malloc(0x20);
return 0;
}

在fopen下个断点跟进fopen函数看看
发现fopen函数实际上是_IO_new_fopen
其汇编如下

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
pwndbg> disass
Dump of assembler code for function _IO_new_fopen:
=> 0x00007ffff7c7f630 <+0>: endbr64
0x00007ffff7c7f634 <+4>: push r13
0x00007ffff7c7f636 <+6>: mov r13,rsi
0x00007ffff7c7f639 <+9>: push r12
0x00007ffff7c7f63b <+11>: push rbp
0x00007ffff7c7f63c <+12>: mov rbp,rdi
0x00007ffff7c7f63f <+15>: mov edi,0x1d8
0x00007ffff7c7f644 <+20>: push rbx
0x00007ffff7c7f645 <+21>: sub rsp,0x8
0x00007ffff7c7f649 <+25>: call 0x7ffff7c28380 <malloc@plt>
0x00007ffff7c7f64e <+30>: test rax,rax
0x00007ffff7c7f651 <+33>: je 0x7ffff7c7f721 <_IO_new_fopen+241>
0x00007ffff7c7f657 <+39>: mov rbx,rax
0x00007ffff7c7f65a <+42>: lea rax,[rax+0xe0]
0x00007ffff7c7f661 <+49>: xor edx,edx
0x00007ffff7c7f663 <+51>: xor esi,esi
0x00007ffff7c7f665 <+53>: mov QWORD PTR [rbx+0x88],rax
0x00007ffff7c7f66c <+60>: lea rcx,[rbx+0xf0]
0x00007ffff7c7f673 <+67>: mov rdi,rbx
0x00007ffff7c7f676 <+70>: mov r12,rbx
0x00007ffff7c7f679 <+73>: lea r8,[rip+0x197a40] # 0x7ffff7e170c0 <_IO_wfile_jumps>
0x00007ffff7c7f680 <+80>: call 0x7ffff7c8e650 <_IO_no_init>
0x00007ffff7c7f685 <+85>: lea rax,[rip+0x197f74] # 0x7ffff7e17600 <_IO_file_jumps>
0x00007ffff7c7f68c <+92>: mov rdi,rbx
0x00007ffff7c7f68f <+95>: mov QWORD PTR [rbx+0xd8],rax
0x00007ffff7c7f696 <+102>: call 0x7ffff7c8be10 <_IO_new_file_init_internal>
0x00007ffff7c7f69b <+107>: mov ecx,0x1
0x00007ffff7c7f6a0 <+112>: mov rdx,r13
0x00007ffff7c7f6a3 <+115>: mov rsi,rbp
0x00007ffff7c7f6a6 <+118>: mov rdi,rbx
0x00007ffff7c7f6a9 <+121>: call 0x7ffff7c8c180 <_IO_new_file_fopen>
0x00007ffff7c7f6ae <+126>: test rax,rax
0x00007ffff7c7f6b1 <+129>: je 0x7ffff7c7f700 <_IO_new_fopen+208>
0x00007ffff7c7f6b3 <+131>: test BYTE PTR [rbx+0x74],0x1
0x00007ffff7c7f6b7 <+135>: je 0x7ffff7c7f6ed <_IO_new_fopen+189>
0x00007ffff7c7f6b9 <+137>: test BYTE PTR [rbx],0x8
0x00007ffff7c7f6bc <+140>: je 0x7ffff7c7f6ed <_IO_new_fopen+189>
0x00007ffff7c7f6be <+142>: mov ecx,DWORD PTR [rbx+0xc0]
0x00007ffff7c7f6c4 <+148>: lea rdx,[rip+0x197875] # 0x7ffff7e16f40 <_IO_wfile_jumps_maybe_mmap>
0x00007ffff7c7f6cb <+155>: lea rax,[rip+0x197dae] # 0x7ffff7e17480 <_IO_file_jumps_maybe_mmap>
0x00007ffff7c7f6d2 <+162>: test ecx,ecx
0x00007ffff7c7f6d4 <+164>: cmovg rax,rdx
0x00007ffff7c7f6d8 <+168>: mov QWORD PTR [rbx+0xd8],rax
0x00007ffff7c7f6df <+175>: mov rax,QWORD PTR [rbx+0xa0]
0x00007ffff7c7f6e6 <+182>: mov QWORD PTR [rax+0xe0],rdx
0x00007ffff7c7f6ed <+189>: add rsp,0x8
0x00007ffff7c7f6f1 <+193>: mov rax,r12
0x00007ffff7c7f6f4 <+196>: pop rbx
0x00007ffff7c7f6f5 <+197>: pop rbp
0x00007ffff7c7f6f6 <+198>: pop r12
0x00007ffff7c7f6f8 <+200>: pop r13
0x00007ffff7c7f6fa <+202>: ret
0x00007ffff7c7f6fb <+203>: nop DWORD PTR [rax+rax*1+0x0]
0x00007ffff7c7f700 <+208>: mov rdi,rbx
0x00007ffff7c7f703 <+211>: xor r12d,r12d
0x00007ffff7c7f706 <+214>: call 0x7ffff7c8d2b0 <__GI__IO_un_link>
0x00007ffff7c7f70b <+219>: mov rdi,rbx
0x00007ffff7c7f70e <+222>: call 0x7ffff7c28370 <free@plt>
0x00007ffff7c7f713 <+227>: add rsp,0x8
0x00007ffff7c7f717 <+231>: mov rax,r12
0x00007ffff7c7f71a <+234>: pop rbx
0x00007ffff7c7f71b <+235>: pop rbp
0x00007ffff7c7f71c <+236>: pop r12
0x00007ffff7c7f71e <+238>: pop r13
0x00007ffff7c7f720 <+240>: ret
0x00007ffff7c7f721 <+241>: xor r12d,r12d
0x00007ffff7c7f724 <+244>: jmp 0x7ffff7c7f6ed <_IO_new_fopen+189>
End of assembler dump.

看到

1
0x7ffff7c7f649 <fopen64+25>    call   malloc@plt                  <malloc@plt>

先调用malloc来分配_IO_FILE_plus的内存,这也告诉我们_IO_FILE_plus是分配在堆上的,此时有了一个chunk位于0x555555559290

1
2
3
4
5
6
7
8
9
10
11
12
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x555555559000
Size: 0x290 (with flag bits: 0x291)

Allocated chunk | PREV_INUSE
Addr: 0x555555559290
Size: 0x1e0 (with flag bits: 0x1e1)

Top chunk | PREV_INUSE
Addr: 0x555555559470
Size: 0x20b90 (with flag bits: 0x20b91)

然后到

1
0x00007ffff7c7f680 <+80>:    call   0x7ffff7c8e650 <_IO_no_init>

调用_IO_no_init函数,其又调用_IO_old_init函数
_IO_no_init函数结束后,我们查看这个chunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 pwndbg> x/40gx 0x5555555592a0
0x5555555592a0: 0x00000000fbad0000 0x0000000000000000
0x5555555592b0: 0x0000000000000000 0x0000000000000000
0x5555555592c0: 0x0000000000000000 0x0000000000000000
0x5555555592d0: 0x0000000000000000 0x0000000000000000
0x5555555592e0: 0x0000000000000000 0x0000000000000000
0x5555555592f0: 0x0000000000000000 0x0000000000000000
0x555555559300: 0x0000000000000000 0x0000000000000000
0x555555559310: 0x0000000000000000 0x0000000000000000
0x555555559320: 0x0000000000000000 0x0000555555559380
0x555555559330: 0x0000000000000000 0x0000000000000000
0x555555559340: 0x0000555555559390 0x0000000000000000
0x555555559350: 0x0000000000000000 0x0000000000000000
0x555555559360: 0x0000000000000000 0x0000000000000000
0x555555559370: 0x0000000000000000 0x0000000000000000
0x555555559380: 0x0000000000000000 0x0000000000000000
0x555555559390: 0x0000000000000000 0x0000000000000000
0x5555555593a0: 0x0000000000000000 0x0000000000000000
0x5555555593b0: 0x0000000000000000 0x0000000000000000
0x5555555593c0: 0x0000000000000000 0x0000000000000000
0x5555555593d0: 0x0000000000000000 0x0000000000000000

可以看到完成了部分的初始化,例如_flags字段设置为了0xfbad0000
接着继续回到_IO_new_fopen继续初始化

1
2
0x7ffff7c7f68c <fopen64+92>         mov    rdi, rbx                        RDI => 0x5555555592a0 ◂— 0xfbad0000
0x7ffff7c7f68f <fopen64+95> mov qword ptr [rbx + 0xd8], rax [0x555555559378] <= 0x7ffff7e17600 (_IO_file_jumps) ◂— 0

可以看到设置了vtable指针的值到_IO_file_jumps

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
pwndbg> p *(struct _IO_FILE_plus*) 0x5555555592a0
$1 = {
file = {
_flags = -72548352,
_IO_read_ptr = 0x0,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_IO_write_end = 0x0,
_IO_buf_base = 0x0,
_IO_buf_end = 0x0,
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x0,
_fileno = 0,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x555555559380,
_offset = 0,
_codecvt = 0x0,
_wide_data = 0x555555559390,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = 0,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7e17600 <_IO_file_jumps>
}

然后调用_IO_new_file_init_internal函数

1
0x7ffff7c7f696 <fopen64+102>        call   _IO_new_file_init_internal  <_IO_new_file_init_internal>

先继续设置_IO_FILE_plus部分字段的值

1
2
3
4
0x7ffff7c8be14 <_IO_new_file_init_internal+4>     or     dword ptr [rdi], 0x240c                        [0x5555555592a0] <= 0xfbad240c (0xfbad0000 | 0x240c)
0x7ffff7c8be1a <_IO_new_file_init_internal+10> push rbx
0x7ffff7c8be1b <_IO_new_file_init_internal+11> mov rbx, rdi RBX => 0x5555555592a0 ◂— 0xfbad240c
0x7ffff7c8be1e <_IO_new_file_init_internal+14> mov qword ptr [rdi + 0x90], 0xffffffffffffffff [0x555555559330] <= 0xfffffffffffffff
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
pwndbg> p *(struct _IO_FILE_plus*) 0x5555555592a0
$2 = {
file = {
_flags = -72539124,
_IO_read_ptr = 0x0,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_IO_write_end = 0x0,
_IO_buf_base = 0x0,
_IO_buf_end = 0x0,
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x0,
_fileno = 0,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x555555559380,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x555555559390,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = 0,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7e17600 <_IO_file_jumps>
}

可以看到先设置了_flags字段的状态位(低两字节)为0x240c,同时设置_offset字段为-1
然后调用_IO_link_in函数

1
0x7ffff7c8be29 <_IO_new_file_init_internal+25>    call   _IO_link_in                 <_IO_link_in>

我们看此时_IO_list_all还指向_IO_2_1_stderr_

1
2
pwndbg> p _IO_list_all
$3 = (struct _IO_FILE_plus *) 0x7ffff7e1b6a0 <_IO_2_1_stderr_>

_IO_link_in函数先设置了_flags的状态位为0x248c,如下

1
2
0x7ffff7c8d2f2 <_IO_link_in+34>    mov    ebp, edx                        EBP => 0xfbad240c
0x7ffff7c8d2f4 <_IO_link_in+36> and ebp, 0x80 EBP => 0 (0xfbad240c & 0x80)

然后又进行一些操作(懒得放了),接着调用了__libc_cleanup_push_defer进行一些清理

1
0x7ffff7c8d349 <_IO_link_in+121>    call   __libc_cleanup_push_defer   <__libc_cleanup_push_defer>

后续又是一些操作,然后调用__libc_cleanup_pop_restore

1
0x7ffff7c8d469 <_IO_link_in+409>    call   __libc_cleanup_pop_restore  <__libc_cleanup_pop_restore>

然后又是一些操作,回到_IO_new_file_init_internal函数,这时我们看创建的_IO_FILE_plus的状态

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
pwndbg> p *(struct _IO_FILE_plus*) 0x5555555592a0
$5 = {
file = {
_flags = -72538996,
_IO_read_ptr = 0x0,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_IO_write_end = 0x0,
_IO_buf_base = 0x0,
_IO_buf_end = 0x0,
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7e1b6a0 <_IO_2_1_stderr_>,
_fileno = 0,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x555555559380,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x555555559390,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = 0,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7e17600 <_IO_file_jumps>
}

发现其_chain域指向了_IO_2_1_stderr_,我们再看_IO_list_all指针

1
2
pwndbg> p _IO_list_all
$6 = (struct _IO_FILE_plus *) 0x5555555592a0

正指向我们创建的_IO_FILE_plus结构体,所以_IO_link_in正是把我们创建的_IO_FILE_plus结构体加入_IO_list_all全局链表中
然后_IO_new_file_init_internal_fileno域设置为-1,接着回到_IO_new_fopen

1
2
3
0x7ffff7c8be2e <_IO_new_file_init_internal+30>    mov    dword ptr [rbx + 0x70], 0xffffffff             [0x555555559310] <= 0xffffffff
0x7ffff7c8be35 <_IO_new_file_init_internal+37> pop rbx RBX => 0x5555555592a0
0x7ffff7c8be36 <_IO_new_file_init_internal+38> ret <fopen64+107>
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
pwndbg> p *(struct _IO_FILE_plus*) 0x5555555592a0
$7 = {
file = {
_flags = -72538996,
_IO_read_ptr = 0x0,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_IO_write_end = 0x0,
_IO_buf_base = 0x0,
_IO_buf_end = 0x0,
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7e1b6a0 <_IO_2_1_stderr_>,
_fileno = -1,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x555555559380,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x555555559390,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = 0,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7e17600 <_IO_file_jumps>
}

然后调用_IO_file_fopen函数

1
0x7ffff7c7f6a9 <fopen64+121>                      call   _IO_file_fopen              <_IO_file_fopen>

其进行一些操作后调用open64函数,真正打开文件

1
2
3
4
► 0x7ffff7c8c0b1 <_IO_file_open+33>    call   open64                      <open64>
file: 0x555555556007 ◂— 'test.txt'
oflag: 0x241
vararg: 0x1b6

open64返回_IO_file_fopen后,_IO_file_fopen进行一些操作,设置了_fileno域(文件描述符)

1
2
3
4
5
6
7
8
9
10
11
0x7ffff7c8c0b9 <_IO_file_open+41>    test   r12d, r12d     3 & 3     EFLAGS => 0x206 [ cf PF af zf sf IF df of ac ]
0x7ffff7c8c0bc <_IO_file_open+44> ✘ js _IO_file_open+192 <_IO_file_open+192>

0x7ffff7c8c0c2 <_IO_file_open+50> mov eax, dword ptr [rbx] EAX, [0x5555555592a0] => 0xfbad248c
0x7ffff7c8c0c4 <_IO_file_open+52> mov edx, ebp EDX => 4
0x7ffff7c8c0c6 <_IO_file_open+54> and ebp, 0x1004 EBP => 4 (0x4 & 0x1004)
0x7ffff7c8c0cc <_IO_file_open+60> mov dword ptr [rbx + 0x70], r12d [0x555555559310] <= 3
0x7ffff7c8c0d0 <_IO_file_open+64> and edx, 0x100c EDX => 4 (0x4 & 0x100c)
0x7ffff7c8c0d6 <_IO_file_open+70> and eax, 0xffffeff3 EAX => 0xfbad2480 (0xfbad248c & 0xffffeff3)
0x7ffff7c8c0db <_IO_file_open+75> or eax, edx EAX => 0xfbad2484 (0xfbad2480 | 0x4)
0x7ffff7c8c0dd <_IO_file_open+77> mov dword ptr [rbx], eax [0x5555555592a0] <= 0xfbad2484
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
pwndbg> p *(struct _IO_FILE_plus*) 0x5555555592a0
$11 = {
file = {
_flags = -72539004,
_IO_read_ptr = 0x0,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_IO_write_end = 0x0,
_IO_buf_base = 0x0,
_IO_buf_end = 0x0,
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7e1b6a0 <_IO_2_1_stderr_>,
_fileno = 3,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x555555559380,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x555555559390,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = 0,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7e17600 <_IO_file_jumps>
}

可见_fileno已经被设置为3,_IO_file_fopen函数还设置了缓冲区等
后续再次调用_IO_link_in,应该是确保结构体状态的更新

1
2
3
4
5
0x7ffff7c8c0df <_IO_file_open+79>     cmp    ebp, 0x1004                      0x4 - 0x1004     EFLAGS => 0x287 [ CF PF af zf SF IF df of ac ]
0x7ffff7c8c0e5 <_IO_file_open+85> ✔ jne _IO_file_open+144 <_IO_file_open+144>

0x7ffff7c8c120 <_IO_file_open+144> mov rdi, rbx RDI => 0x5555555592a0 ◂— 0xfbad2484
0x7ffff7c8c123 <_IO_file_open+147> call _IO_link_in <_IO_link_in>

后续调用了一些字符串解析函数,最后回到_IO_new_fopen函数,最后回到main函数结束

1
2
3
4
5
6
7
8
9
10
0x7ffff7c7f6b3 <fopen64+131>    test   byte ptr [rbx + 0x74], 1     0 & 1     EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c7f6b7 <fopen64+135> ✔ je fopen64+189 <fopen64+189>

0x7ffff7c7f6ed <fopen64+189> add rsp, 8 RSP => 0x7fffffffdca8 (0x7fffffffdca0 + 0x8)
0x7ffff7c7f6f1 <fopen64+193> mov rax, r12 RAX => 0x5555555592a0 ◂— 0xfbad2484
0x7ffff7c7f6f4 <fopen64+196> pop rbx RBX => 0
0x7ffff7c7f6f5 <fopen64+197> pop rbp RBP => 0x7fffffffdce0
0x7ffff7c7f6f6 <fopen64+198> pop r12 R12 => 0x7fffffffddf8
0x7ffff7c7f6f8 <fopen64+200> pop r13 R13 => 0x555555555169 (main)
0x7ffff7c7f6fa <fopen64+202> ret <main+37>

看最终返回的_IO_FILE_plus结构体

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
pwndbg> p *(struct _IO_FILE_plus*) 0x5555555592a0
$15 = {
file = {
_flags = -72539004,
_IO_read_ptr = 0x0,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_IO_write_end = 0x0,
_IO_buf_base = 0x0,
_IO_buf_end = 0x0,
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7e1b6a0 <_IO_2_1_stderr_>,
_fileno = 3,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x555555559380,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x555555559390,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = 0,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7e17600 <_IO_file_jumps>
}

0x03 fread函数

继续用fopen使用的demo,加一个fread函数即可(glibc2.35)

1
2
3
4
5
6
7
8
9
10
#include<stdio.h>
#include<stdlib.h>

int main(){

FILE*fp = fopen("test.txt","rb");
char *ptr = malloc(0x20);
fread(ptr, 1, 20, fp);
return 0;
}

在fread下个断点,查看其汇编代码如下

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
pwndbg> disass
Dump of assembler code for function __GI__IO_fread:
Address range 0x7ffff7c7fb30 to 0x7ffff7c7fc2e:
=> 0x00007ffff7c7fb30 <+0>: endbr64
0x00007ffff7c7fb34 <+4>: push r15
0x00007ffff7c7fb36 <+6>: push r14
0x00007ffff7c7fb38 <+8>: push r13
0x00007ffff7c7fb3a <+10>: push r12
0x00007ffff7c7fb3c <+12>: mov r12,rsi
0x00007ffff7c7fb3f <+15>: push rbp
0x00007ffff7c7fb40 <+16>: imul r12,rdx
0x00007ffff7c7fb44 <+20>: push rbx
0x00007ffff7c7fb45 <+21>: sub rsp,0x18
0x00007ffff7c7fb49 <+25>: test r12,r12
0x00007ffff7c7fb4c <+28>: je 0x7ffff7c7fbe1 <__GI__IO_fread+177>
0x00007ffff7c7fb52 <+34>: mov eax,DWORD PTR [rcx]
0x00007ffff7c7fb54 <+36>: mov r14,rdi
0x00007ffff7c7fb57 <+39>: mov rbp,rsi
0x00007ffff7c7fb5a <+42>: mov r13,rdx
0x00007ffff7c7fb5d <+45>: mov rbx,rcx
0x00007ffff7c7fb60 <+48>: and eax,0x8000
0x00007ffff7c7fb65 <+53>: jne 0x7ffff7c7fb9b <__GI__IO_fread+107>
0x00007ffff7c7fb67 <+55>: mov r15,QWORD PTR fs:0x10
0x00007ffff7c7fb70 <+64>: mov rdi,QWORD PTR [rcx+0x88]
0x00007ffff7c7fb77 <+71>: cmp QWORD PTR [rdi+0x8],r15
0x00007ffff7c7fb7b <+75>: je 0x7ffff7c7fb97 <__GI__IO_fread+103>
0x00007ffff7c7fb7d <+77>: mov edx,0x1
0x00007ffff7c7fb82 <+82>: lock cmpxchg DWORD PTR [rdi],edx
0x00007ffff7c7fb86 <+86>: jne 0x7ffff7c7fc18 <__GI__IO_fread+232>
0x00007ffff7c7fb8c <+92>: mov rdi,QWORD PTR [rbx+0x88]
0x00007ffff7c7fb93 <+99>: mov QWORD PTR [rdi+0x8],r15
0x00007ffff7c7fb97 <+103>: add DWORD PTR [rdi+0x4],0x1
0x00007ffff7c7fb9b <+107>: mov rdx,r12
0x00007ffff7c7fb9e <+110>: mov rsi,r14
0x00007ffff7c7fba1 <+113>: mov rdi,rbx
0x00007ffff7c7fba4 <+116>: call 0x7ffff7c8dfd0 <__GI__IO_sgetn>
0x00007ffff7c7fba9 <+121>: test DWORD PTR [rbx],0x8000
0x00007ffff7c7fbaf <+127>: jne 0x7ffff7c7fbd4 <__GI__IO_fread+164>
0x00007ffff7c7fbb1 <+129>: mov rdi,QWORD PTR [rbx+0x88]
0x00007ffff7c7fbb8 <+136>: mov ecx,DWORD PTR [rdi+0x4]
0x00007ffff7c7fbbb <+139>: lea edx,[rcx-0x1]
0x00007ffff7c7fbbe <+142>: mov DWORD PTR [rdi+0x4],edx
0x00007ffff7c7fbc1 <+145>: test edx,edx
0x00007ffff7c7fbc3 <+147>: jne 0x7ffff7c7fbd4 <__GI__IO_fread+164>
0x00007ffff7c7fbc5 <+149>: mov QWORD PTR [rdi+0x8],0x0
0x00007ffff7c7fbcd <+157>: xchg DWORD PTR [rdi],edx
0x00007ffff7c7fbcf <+159>: cmp edx,0x1
0x00007ffff7c7fbd2 <+162>: jg 0x7ffff7c7fc00 <__GI__IO_fread+208>
0x00007ffff7c7fbd4 <+164>: cmp r12,rax
0x00007ffff7c7fbd7 <+167>: je 0x7ffff7c7fbf8 <__GI__IO_fread+200>
0x00007ffff7c7fbd9 <+169>: xor edx,edx
0x00007ffff7c7fbdb <+171>: div rbp
0x00007ffff7c7fbde <+174>: mov r12,rax
0x00007ffff7c7fbe1 <+177>: add rsp,0x18
0x00007ffff7c7fbe5 <+181>: mov rax,r12
0x00007ffff7c7fbe8 <+184>: pop rbx
0x00007ffff7c7fbe9 <+185>: pop rbp
0x00007ffff7c7fbea <+186>: pop r12
0x00007ffff7c7fbec <+188>: pop r13
0x00007ffff7c7fbee <+190>: pop r14
0x00007ffff7c7fbf0 <+192>: pop r15
0x00007ffff7c7fbf2 <+194>: ret
0x00007ffff7c7fbf3 <+195>: nop DWORD PTR [rax+rax*1+0x0]
0x00007ffff7c7fbf8 <+200>: mov r12,r13
0x00007ffff7c7fbfb <+203>: jmp 0x7ffff7c7fbe1 <__GI__IO_fread+177>
0x00007ffff7c7fbfd <+205>: nop DWORD PTR [rax]
0x00007ffff7c7fc00 <+208>: mov QWORD PTR [rsp+0x8],rax
0x00007ffff7c7fc05 <+213>: call 0x7ffff7c91300 <__GI___lll_lock_wake_private>
0x00007ffff7c7fc0a <+218>: mov rax,QWORD PTR [rsp+0x8]
0x00007ffff7c7fc0f <+223>: jmp 0x7ffff7c7fbd4 <__GI__IO_fread+164>
0x00007ffff7c7fc11 <+225>: nop DWORD PTR [rax+0x0]
0x00007ffff7c7fc18 <+232>: call 0x7ffff7c91230 <__GI___lll_lock_wait_private>
0x00007ffff7c7fc1d <+237>: jmp 0x7ffff7c7fb8c <__GI__IO_fread+92>
0x00007ffff7c7fc22 <+242>: endbr64
0x00007ffff7c7fc26 <+246>: mov rbp,rax
0x00007ffff7c7fc29 <+249>: jmp 0x7ffff7c2910f <__GI__IO_fread.cold>
Address range 0x7ffff7c2910f to 0x7ffff7c29145:
0x00007ffff7c2910f <-354849>: test DWORD PTR [rbx],0x8000
0x00007ffff7c29115 <-354843>: jne 0x7ffff7c2913d <__GI__IO_fread-354803>
0x00007ffff7c29117 <-354841>: mov rdi,QWORD PTR [rbx+0x88]
0x00007ffff7c2911e <-354834>: mov eax,DWORD PTR [rdi+0x4]
0x00007ffff7c29121 <-354831>: sub eax,0x1
0x00007ffff7c29124 <-354828>: mov DWORD PTR [rdi+0x4],eax
0x00007ffff7c29127 <-354825>: jne 0x7ffff7c2913d <__GI__IO_fread-354803>
0x00007ffff7c29129 <-354823>: mov QWORD PTR [rdi+0x8],0x0
0x00007ffff7c29131 <-354815>: xchg DWORD PTR [rdi],eax
0x00007ffff7c29133 <-354813>: sub eax,0x1
0x00007ffff7c29136 <-354810>: jle 0x7ffff7c2913d <__GI__IO_fread-354803>
0x00007ffff7c29138 <-354808>: call 0x7ffff7c91300 <__GI___lll_lock_wake_private>
0x00007ffff7c2913d <-354803>: mov rdi,rbp
0x00007ffff7c29140 <-354800>: call 0x7ffff7c2a120 <_Unwind_Resume>
End of assembler dump.

可以看到fread函数最起初实际上是_IO_fread函数,先计算需要读取的字节数

1
2
3
4
5
6
7
8
9
10
11
0x7ffff7c7fb34 <fread+4>     push   r15
0x7ffff7c7fb36 <fread+6> push r14
0x7ffff7c7fb38 <fread+8> push r13
0x7ffff7c7fb3a <fread+10> push r12
0x7ffff7c7fb3c <fread+12> mov r12, rsi R12 => 1
0x7ffff7c7fb3f <fread+15> push rbp
0x7ffff7c7fb40 <fread+16> imul r12, rdx
0x7ffff7c7fb44 <fread+20> push rbx
0x7ffff7c7fb45 <fread+21> sub rsp, 0x18 RSP => 0x7fffffffdca0 (0x7fffffffdcb8 - 0x18)
0x7ffff7c7fb49 <fread+25> test r12, r12 0x14 & 0x14 EFLAGS => 0x206 [ cf PF af zf sf IF df of ac ]
0x7ffff7c7fb4c <fread+28> ✘ je fread+177 <fread+177>

看到imul r12, rdx计算得到了需要读取的字节数(0x14正是demo中需要读取的字节数20),如果不为0则继续,为0则直接返回
接下来是针对_flags域进行了一些有关锁的操作,然后调用_IO_sgetn函数

1
2
3
4
0x7ffff7c7fb9b <fread+107>    mov    rdx, r12     RDX => 0x14
0x7ffff7c7fb9e <fread+110> mov rsi, r14 RSI => 0x555555559480 ◂— 0
0x7ffff7c7fba1 <fread+113> mov rdi, rbx RDI => 0x5555555592a0 ◂— 0xfbad2484
0x7ffff7c7fba4 <fread+116> call _IO_sgetn <_IO_sgetn>

先放上_IO_sgetn的汇编

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
pwndbg> disass
Dump of assembler code for function __GI__IO_sgetn:
0x00007ffff7c8dfd0 <+0>: endbr64
0x00007ffff7c8dfd4 <+4>: push rbx
0x00007ffff7c8dfd5 <+5>: lea rcx,[rip+0x188a24] # 0x7ffff7e16a00 <_IO_helper_jumps>
0x00007ffff7c8dfdc <+12>: lea rax,[rip+0x189785] # 0x7ffff7e17768
0x00007ffff7c8dfe3 <+19>: sub rax,rcx
0x00007ffff7c8dfe6 <+22>: sub rsp,0x20
0x00007ffff7c8dfea <+26>: mov rbx,QWORD PTR [rdi+0xd8]
0x00007ffff7c8dff1 <+33>: mov r8,rbx
0x00007ffff7c8dff4 <+36>: sub r8,rcx
0x00007ffff7c8dff7 <+39>: cmp rax,r8
0x00007ffff7c8dffa <+42>: jbe 0x7ffff7c8e010 <__GI__IO_sgetn+64>
=> 0x00007ffff7c8dffc <+44>: mov rax,QWORD PTR [rbx+0x40]
0x00007ffff7c8e000 <+48>: add rsp,0x20
0x00007ffff7c8e004 <+52>: pop rbx
0x00007ffff7c8e005 <+53>: jmp rax
0x00007ffff7c8e007 <+55>: nop WORD PTR [rax+rax*1+0x0]
0x00007ffff7c8e010 <+64>: mov QWORD PTR [rsp+0x18],rdx
0x00007ffff7c8e015 <+69>: mov QWORD PTR [rsp+0x10],rsi
0x00007ffff7c8e01a <+74>: mov QWORD PTR [rsp+0x8],rdi
0x00007ffff7c8e01f <+79>: call 0x7ffff7c89ef0 <_IO_vtable_check>
0x00007ffff7c8e024 <+84>: mov rax,QWORD PTR [rbx+0x40]
0x00007ffff7c8e028 <+88>: mov rdx,QWORD PTR [rsp+0x18]
0x00007ffff7c8e02d <+93>: mov rsi,QWORD PTR [rsp+0x10]
0x00007ffff7c8e032 <+98>: mov rdi,QWORD PTR [rsp+0x8]
0x00007ffff7c8e037 <+103>: add rsp,0x20
0x00007ffff7c8e03b <+107>: pop rbx
0x00007ffff7c8e03c <+108>: jmp rax
End of assembler dump.

回到调试,_IO_sgetn先进行安全检测

1
2
3
4
5
6
7
8
9
10
0x7ffff7c8dfd4 <_IO_sgetn+4>     push   rbx
► 0x7ffff7c8dfd5 <_IO_sgetn+5> lea rcx, [rip + 0x188a24] RCX => 0x7ffff7e16a00 (_IO_helper_jumps) ◂— 0
0x7ffff7c8dfdc <_IO_sgetn+12> lea rax, [rip + 0x189785] RAX => 0x7ffff7e17768 ◂— 0
0x7ffff7c8dfe3 <_IO_sgetn+19> sub rax, rcx RAX => 0xd68 (0x7ffff7e17768 - 0x7ffff7e16a00)
0x7ffff7c8dfe6 <_IO_sgetn+22> sub rsp, 0x20 RSP => 0x7fffffffdc70 (0x7fffffffdc90 - 0x20)
0x7ffff7c8dfea <_IO_sgetn+26> mov rbx, qword ptr [rdi + 0xd8] RBX, [0x555555559378] => 0x7ffff7e17600 (_IO_file_jumps) ◂— 0
0x7ffff7c8dff1 <_IO_sgetn+33> mov r8, rbx R8 => 0x7ffff7e17600 (_IO_file_jumps) ◂— 0
0x7ffff7c8dff4 <_IO_sgetn+36> sub r8, rcx R8 => 0xc00 (0x7ffff7e17600 - 0x7ffff7e16a00)
0x7ffff7c8dff7 <_IO_sgetn+39> cmp rax, r8 0xd68 - 0xc00 EFLAGS => 0x202 [ cf pf af zf sf IF df of ac ]
0x7ffff7c8dffa <_IO_sgetn+42> ✘ jbe _IO_sgetn+64 <_IO_sgetn+64>

可以看到先计算了_IO_helper_jumps 到某个地址的偏移,又判断了vtable(注意看mov rbx, qword ptr [rdi + 0xd8],0xd8正是vtable_IO_FILE_plus中的偏移)是否在 _IO_helper_jumps 范围之外
<_IO_sgetn+64>处正是_IO_vtable_check函数

1
2
3
4
0x00007ffff7c8e010 <+64>:    mov    QWORD PTR [rsp+0x18],rdx
0x00007ffff7c8e015 <+69>: mov QWORD PTR [rsp+0x10],rsi
0x00007ffff7c8e01a <+74>: mov QWORD PTR [rsp+0x8],rdi
0x00007ffff7c8e01f <+79>: call 0x7ffff7c89ef0 <_IO_vtable_check>

jbe跳转,表示如果 rax <= r8,则跳转,即vtable在不在指定范围内,调用_IO_vtable_check来进一步检查(见另一篇博客
后续调用vtable中的_IO_file_xsgetn函数

1
2
3
4
0x7ffff7c8dffc <_IO_sgetn+44>              mov    rax, qword ptr [rbx + 0x40]     RAX, [_IO_file_jumps+64] => 0x7ffff7c8b2b0 (__GI__IO_file_xsgetn) ◂— endbr64
0x7ffff7c8e000 <_IO_sgetn+48> add rsp, 0x20 RSP => 0x7fffffffdc90 (0x7fffffffdc70 + 0x20)
0x7ffff7c8e004 <_IO_sgetn+52> pop rbx RBX => 0x5555555592a0
0x7ffff7c8e005 <_IO_sgetn+53> jmp rax <__GI__IO_file_xsgetn>

可以看到rbx存储的是vtable地址,rbx + 0x40正是虚表中_IO_file_xsgetn函数
放上_IO_file_xsgetn的汇编

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
pwndbg> disass
Dump of assembler code for function __GI__IO_file_xsgetn:
=> 0x00007ffff7c8b2b0 <+0>: endbr64
0x00007ffff7c8b2b4 <+4>: push r15
0x00007ffff7c8b2b6 <+6>: push r14
0x00007ffff7c8b2b8 <+8>: push r13
0x00007ffff7c8b2ba <+10>: mov r13,rsi
0x00007ffff7c8b2bd <+13>: push r12
0x00007ffff7c8b2bf <+15>: push rbp
0x00007ffff7c8b2c0 <+16>: push rbx
0x00007ffff7c8b2c1 <+17>: mov rbx,rdi
0x00007ffff7c8b2c4 <+20>: sub rsp,0x18
0x00007ffff7c8b2c8 <+24>: cmp QWORD PTR [rdi+0x38],0x0
0x00007ffff7c8b2cd <+29>: mov QWORD PTR [rsp],rdx
0x00007ffff7c8b2d1 <+33>: je 0x7ffff7c8b4a8 <__GI__IO_file_xsgetn+504>
0x00007ffff7c8b2d7 <+39>: mov rax,QWORD PTR [rsp]
0x00007ffff7c8b2db <+43>: lea r15,[rip+0x18b71e] # 0x7ffff7e16a00 <_IO_helper_jumps>
0x00007ffff7c8b2e2 <+50>: lea r14,[rip+0x18c47f] # 0x7ffff7e17768
0x00007ffff7c8b2e9 <+57>: sub r14,r15
0x00007ffff7c8b2ec <+60>: mov r12,rax
0x00007ffff7c8b2ef <+63>: test rax,rax
0x00007ffff7c8b2f2 <+66>: je 0x7ffff7c8b3c4 <__GI__IO_file_xsgetn+276>
0x00007ffff7c8b2f8 <+72>: nop DWORD PTR [rax+rax*1+0x0]
0x00007ffff7c8b300 <+80>: mov rsi,QWORD PTR [rbx+0x8]
0x00007ffff7c8b304 <+84>: mov rbp,QWORD PTR [rbx+0x10]
0x00007ffff7c8b308 <+88>: sub rbp,rsi
0x00007ffff7c8b30b <+91>: cmp rbp,r12
0x00007ffff7c8b30e <+94>: jae 0x7ffff7c8b418 <__GI__IO_file_xsgetn+360>
0x00007ffff7c8b314 <+100>: test rbp,rbp
0x00007ffff7c8b317 <+103>: jne 0x7ffff7c8b3d8 <__GI__IO_file_xsgetn+296>
0x00007ffff7c8b31d <+109>: test DWORD PTR [rbx],0x100
0x00007ffff7c8b323 <+115>: jne 0x7ffff7c8b3f9 <__GI__IO_file_xsgetn+329>
0x00007ffff7c8b329 <+121>: mov rcx,QWORD PTR [rbx+0x38]
0x00007ffff7c8b32d <+125>: test rcx,rcx
0x00007ffff7c8b330 <+128>: je 0x7ffff7c8b3d0 <__GI__IO_file_xsgetn+288>
0x00007ffff7c8b336 <+134>: mov rsi,QWORD PTR [rbx+0x40]
0x00007ffff7c8b33a <+138>: sub rsi,rcx
0x00007ffff7c8b33d <+141>: cmp rsi,r12
0x00007ffff7c8b340 <+144>: ja 0x7ffff7c8b430 <__GI__IO_file_xsgetn+384>
0x00007ffff7c8b346 <+150>: cmp rsi,0x7f
0x00007ffff7c8b34a <+154>: jbe 0x7ffff7c8b3d0 <__GI__IO_file_xsgetn+288>
0x00007ffff7c8b350 <+160>: mov rax,r12
0x00007ffff7c8b353 <+163>: xor edx,edx
0x00007ffff7c8b355 <+165>: div rsi
0x00007ffff7c8b358 <+168>: mov rdi,rdx
0x00007ffff7c8b35b <+171>: mov rdx,r12
0x00007ffff7c8b35e <+174>: sub rdx,rdi
0x00007ffff7c8b361 <+177>: mov rbp,QWORD PTR [rbx+0xd8]
0x00007ffff7c8b368 <+184>: movq xmm0,rcx
0x00007ffff7c8b36d <+189>: punpcklqdq xmm0,xmm0
0x00007ffff7c8b371 <+193>: mov rax,rbp
0x00007ffff7c8b374 <+196>: movups XMMWORD PTR [rbx+0x8],xmm0
0x00007ffff7c8b378 <+200>: sub rax,r15
0x00007ffff7c8b37b <+203>: movups XMMWORD PTR [rbx+0x18],xmm0
0x00007ffff7c8b37f <+207>: movups XMMWORD PTR [rbx+0x28],xmm0
0x00007ffff7c8b383 <+211>: cmp r14,rax
0x00007ffff7c8b386 <+214>: jbe 0x7ffff7c8b460 <__GI__IO_file_xsgetn+432>
0x00007ffff7c8b38c <+220>: mov rsi,r13
0x00007ffff7c8b38f <+223>: mov rdi,rbx
0x00007ffff7c8b392 <+226>: call QWORD PTR [rbp+0x70]
0x00007ffff7c8b395 <+229>: test rax,rax
0x00007ffff7c8b398 <+232>: jle 0x7ffff7c8b488 <__GI__IO_file_xsgetn+472>
0x00007ffff7c8b39e <+238>: mov rdx,QWORD PTR [rbx+0x90]
0x00007ffff7c8b3a5 <+245>: add r13,rax
0x00007ffff7c8b3a8 <+248>: sub r12,rax
0x00007ffff7c8b3ab <+251>: cmp rdx,0xffffffffffffffff
0x00007ffff7c8b3af <+255>: je 0x7ffff7c8b3bb <__GI__IO_file_xsgetn+267>
0x00007ffff7c8b3b1 <+257>: add rdx,rax
0x00007ffff7c8b3b4 <+260>: mov QWORD PTR [rbx+0x90],rdx
0x00007ffff7c8b3bb <+267>: test r12,r12
0x00007ffff7c8b3be <+270>: jne 0x7ffff7c8b300 <__GI__IO_file_xsgetn+80>
0x00007ffff7c8b3c4 <+276>: mov r13,QWORD PTR [rsp]
0x00007ffff7c8b3c8 <+280>: jmp 0x7ffff7c8b448 <__GI__IO_file_xsgetn+408>
0x00007ffff7c8b3ca <+282>: nop WORD PTR [rax+rax*1+0x0]
0x00007ffff7c8b3d0 <+288>: mov rdx,r12
0x00007ffff7c8b3d3 <+291>: jmp 0x7ffff7c8b361 <__GI__IO_file_xsgetn+177>
0x00007ffff7c8b3d5 <+293>: nop DWORD PTR [rax]
0x00007ffff7c8b3d8 <+296>: mov rdi,r13
0x00007ffff7c8b3db <+299>: mov rdx,rbp
0x00007ffff7c8b3de <+302>: sub r12,rbp
0x00007ffff7c8b3e1 <+305>: call 0x7ffff7c283e0 <*ABS*+0xa97d0@plt>
0x00007ffff7c8b3e6 <+310>: add QWORD PTR [rbx+0x8],rbp
0x00007ffff7c8b3ea <+314>: mov r13,rax
0x00007ffff7c8b3ed <+317>: test DWORD PTR [rbx],0x100
0x00007ffff7c8b3f3 <+323>: je 0x7ffff7c8b329 <__GI__IO_file_xsgetn+121>
0x00007ffff7c8b3f9 <+329>: mov rdi,rbx
0x00007ffff7c8b3fc <+332>: call 0x7ffff7c8d6b0 <_IO_switch_to_main_get_area>
0x00007ffff7c8b401 <+337>: mov rsi,QWORD PTR [rbx+0x8]
0x00007ffff7c8b405 <+341>: mov rbp,QWORD PTR [rbx+0x10]
0x00007ffff7c8b409 <+345>: sub rbp,rsi
0x00007ffff7c8b40c <+348>: cmp rbp,r12
0x00007ffff7c8b40f <+351>: jb 0x7ffff7c8b314 <__GI__IO_file_xsgetn+100>
0x00007ffff7c8b415 <+357>: nop DWORD PTR [rax]
0x00007ffff7c8b418 <+360>: mov rdi,r13
0x00007ffff7c8b41b <+363>: mov rdx,r12
0x00007ffff7c8b41e <+366>: call 0x7ffff7c28620 <*ABS*+0xa9c10@plt>
0x00007ffff7c8b423 <+371>: add QWORD PTR [rbx+0x8],r12
0x00007ffff7c8b427 <+375>: mov r13,QWORD PTR [rsp]
0x00007ffff7c8b42b <+379>: jmp 0x7ffff7c8b448 <__GI__IO_file_xsgetn+408>
0x00007ffff7c8b42d <+381>: nop DWORD PTR [rax]
0x00007ffff7c8b430 <+384>: mov rdi,rbx
0x00007ffff7c8b433 <+387>: call 0x7ffff7c8d870 <__GI___underflow>
0x00007ffff7c8b438 <+392>: cmp eax,0xffffffff
0x00007ffff7c8b43b <+395>: jne 0x7ffff7c8b300 <__GI__IO_file_xsgetn+80>
0x00007ffff7c8b441 <+401>: mov r13,QWORD PTR [rsp]
0x00007ffff7c8b445 <+405>: sub r13,r12
0x00007ffff7c8b448 <+408>: add rsp,0x18
0x00007ffff7c8b44c <+412>: mov rax,r13
0x00007ffff7c8b44f <+415>: pop rbx
0x00007ffff7c8b450 <+416>: pop rbp
0x00007ffff7c8b451 <+417>: pop r12
0x00007ffff7c8b453 <+419>: pop r13
0x00007ffff7c8b455 <+421>: pop r14
0x00007ffff7c8b457 <+423>: pop r15
0x00007ffff7c8b459 <+425>: ret
0x00007ffff7c8b45a <+426>: nop WORD PTR [rax+rax*1+0x0]
0x00007ffff7c8b460 <+432>: mov QWORD PTR [rsp+0x8],rdx
0x00007ffff7c8b465 <+437>: call 0x7ffff7c89ef0 <_IO_vtable_check>
0x00007ffff7c8b46a <+442>: mov rdx,QWORD PTR [rsp+0x8]
0x00007ffff7c8b46f <+447>: mov rsi,r13
0x00007ffff7c8b472 <+450>: mov rdi,rbx
0x00007ffff7c8b475 <+453>: call QWORD PTR [rbp+0x70]
0x00007ffff7c8b478 <+456>: test rax,rax
0x00007ffff7c8b47b <+459>: jg 0x7ffff7c8b39e <__GI__IO_file_xsgetn+238>
0x00007ffff7c8b481 <+465>: nop DWORD PTR [rax+0x0]
0x00007ffff7c8b488 <+472>: mov edx,DWORD PTR [rbx]
0x00007ffff7c8b48a <+474>: mov r13,QWORD PTR [rsp]
0x00007ffff7c8b48e <+478>: mov ecx,edx
0x00007ffff7c8b490 <+480>: sub r13,r12
0x00007ffff7c8b493 <+483>: or edx,0x10
0x00007ffff7c8b496 <+486>: or ecx,0x20
0x00007ffff7c8b499 <+489>: test rax,rax
0x00007ffff7c8b49c <+492>: cmovne edx,ecx
0x00007ffff7c8b49f <+495>: mov DWORD PTR [rbx],edx
0x00007ffff7c8b4a1 <+497>: jmp 0x7ffff7c8b448 <__GI__IO_file_xsgetn+408>
0x00007ffff7c8b4a3 <+499>: nop DWORD PTR [rax+rax*1+0x0]
0x00007ffff7c8b4a8 <+504>: mov rdi,QWORD PTR [rdi+0x48]
0x00007ffff7c8b4ac <+508>: test rdi,rdi
0x00007ffff7c8b4af <+511>: je 0x7ffff7c8b4bc <__GI__IO_file_xsgetn+524>
0x00007ffff7c8b4b1 <+513>: call 0x7ffff7c28370 <free@plt>
0x00007ffff7c8b4b6 <+518>: and DWORD PTR [rbx],0xfffffeff
0x00007ffff7c8b4bc <+524>: mov rdi,rbx
0x00007ffff7c8b4bf <+527>: call 0x7ffff7c8dc90 <__GI__IO_doallocbuf>
0x00007ffff7c8b4c4 <+532>: jmp 0x7ffff7c8b2d7 <__GI__IO_file_xsgetn+39>
End of assembler dump.

_IO_file_xsgetn函数是fread的核心,其大致流程整体是一个循环,先检查读缓冲区状态(fp->_IO_read_end - fp->_IO_read_ptr),大于0则从fp->_IO_read_ptr读取数据到目标,并更新读指针,如果读缓冲区为空则会调用__underflow函数再读取(第一次读写前_IO_file_xsgetn会先调用_IO_doallocbuf分配缓冲区)
回到调试,_IO_file_xsgetn先保存寄存器和读取字节数,这一部分就懒得放了(上面代码可以看到),然后检查 _IO_buf_base ([rdi + 0x38]处)是否为0,为0则说明无缓冲区,调用_IO_doallocbuf分配缓冲区

1
2
3
4
5
6
7
8
9
10
11
0x7ffff7c8b2c4 <__GI__IO_file_xsgetn+20>     sub    rsp, 0x18                     RSP => 0x7fffffffdc50 (0x7fffffffdc68 - 0x18)
0x7ffff7c8b2c8 <__GI__IO_file_xsgetn+24> cmp qword ptr [rdi + 0x38], 0 0 - 0 EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c8b2cd <__GI__IO_file_xsgetn+29> mov qword ptr [rsp], rdx [0x7fffffffdc50] <= 0x14
0x7ffff7c8b2d1 <__GI__IO_file_xsgetn+33> ✔ je __GI__IO_file_xsgetn+504 <__GI__IO_file_xsgetn+504>

0x7ffff7c8b4a8 <__GI__IO_file_xsgetn+504> mov rdi, qword ptr [rdi + 0x48] RDI, [0x5555555592e8] => 0
0x7ffff7c8b4ac <__GI__IO_file_xsgetn+508> test rdi, rdi 0 & 0 EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c8b4af <__GI__IO_file_xsgetn+511> ✔ je __GI__IO_file_xsgetn+524 <__GI__IO_file_xsgetn+524>

0x7ffff7c8b4bc <__GI__IO_file_xsgetn+524> mov rdi, rbx RDI => 0x5555555592a0 ◂— 0xfbad2484
0x7ffff7c8b4bf <__GI__IO_file_xsgetn+527> call _IO_doallocbuf <_IO_doallocbuf>

_IO_doallocbuf函数还会调用vtable->_IO_file_doallocate函数(会进行安全检测),最终实现分配 _IO_buf_base ~ _IO_buf_end 之间的缓冲区(调用malloc分配堆上内存)

1
2
3
4
5
6
7
8
9
10
0x7ffff7c8dcb7 <_IO_doallocbuf+39>    mov    rbp, qword ptr [rbx + 0xd8]     RBP, [0x555555559378] => 0x7ffff7e17600 (_IO_file_jumps) ◂— 0
0x7ffff7c8dcbe <_IO_doallocbuf+46> lea rdx, [rip + 0x188d3b] RDX => 0x7ffff7e16a00 (_IO_helper_jumps) ◂— 0
0x7ffff7c8dcc5 <_IO_doallocbuf+53> lea rax, [rip + 0x189a9c] RAX => 0x7ffff7e17768 ◂— 0
0x7ffff7c8dccc <_IO_doallocbuf+60> sub rax, rdx RAX => 0xd68 (0x7ffff7e17768 - 0x7ffff7e16a00)
0x7ffff7c8dccf <_IO_doallocbuf+63> mov rcx, rbp RCX => 0x7ffff7e17600 (_IO_file_jumps) ◂— 0
0x7ffff7c8dcd2 <_IO_doallocbuf+66> sub rcx, rdx RCX => 0xc00 (0x7ffff7e17600 - 0x7ffff7e16a00)
0x7ffff7c8dcd5 <_IO_doallocbuf+69> cmp rax, rcx 0xd68 - 0xc00 EFLAGS => 0x202 [ cf pf af zf sf IF df of ac ]
0x7ffff7c8dcd8 <_IO_doallocbuf+72>: jbe 0x7ffff7c8dd40 <__GI__IO_doallocbuf+176>

0x7ffff7c8dd40 <_IO_doallocbuf+176>: call 0x7ffff7c89ef0 <_IO_vtable_check>

安全检查如上

1
2
0x7ffff7c8dcda <_IO_doallocbuf+74>     mov    rdi, rbx     RDI => 0x5555555592a0 ◂— 0xfbad2484
0x7ffff7c8dcdd <_IO_doallocbuf+77> call qword ptr [rbp + 0x68] <_IO_file_doallocate>

具体代码就不放了,其还会调用_IO_file_stat函数再调用fstat64函数来保存文件状态,然后调用_IO_setb函数来设置fp->_IO_buf_base = buf,fp->_IO_buf_end = ebuf,并设置 _IO_save_base, _IO_backup_base ,fp->_IO_save_end指针为0
我们看最后_IO_doallocbuf结束后结构体的状态

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
pwndbg> p *(struct _IO_FILE_plus*) 0x5555555592a0
$5 = {
file = {
_flags = -72539004,
_IO_read_ptr = 0x0,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_IO_write_end = 0x0,
_IO_buf_base = 0x5555555594b0 "",
_IO_buf_end = 0x55555555a4b0 "",
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7e1b6a0 <_IO_2_1_stderr_>,
_fileno = 3,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x555555559380,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x555555559390,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = 0,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7e17600 <_IO_file_jumps>
}

可以看到_IO_buf_base以及_IO_buf_end已经被正确设置了
我们再查看堆,发现确实分配了0x1000大小的缓冲区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x555555559000
Size: 0x290 (with flag bits: 0x291)

Allocated chunk | PREV_INUSE
Addr: 0x555555559290
Size: 0x1e0 (with flag bits: 0x1e1)

Allocated chunk | PREV_INUSE
Addr: 0x555555559470
Size: 0x30 (with flag bits: 0x31)

Allocated chunk | PREV_INUSE
Addr: 0x5555555594a0
Size: 0x1010 (with flag bits: 0x1011)

Top chunk | PREV_INUSE
Addr: 0x55555555a4b0
Size: 0x1fb50 (with flag bits: 0x1fb51)

之后_IO_file_xsgetn检查读缓冲区,发现fp->_IO_read_ptr== fp->_IO_read_end,读缓冲区为空,则调用__underflow函数

1
2
0x7ffff7c8b430 <__GI__IO_file_xsgetn+384>    mov    rdi, rbx     RDI => 0x5555555592a0 ◂— 0xfbad2484
0x7ffff7c8b433 <__GI__IO_file_xsgetn+387> call __underflow <__underflow>

__underflow函数正是调用了vtable->_IO_file_underflow函数([_IO_file_jumps+32]便是)

1
2
3
4
5
6
0x7ffff7c8d8f1 <__underflow+129>          mov    rax, qword ptr [rbx + 0x20]     RAX, [_IO_file_jumps+32] => 0x7ffff7c8cab0 (_IO_file_underflow) ◂— endbr64
0x7ffff7c8d8f5 <__underflow+133> add rsp, 8 RSP => 0x7fffffffdc38 (0x7fffffffdc30 + 0x8)
0x7ffff7c8d8f9 <__underflow+137> mov rdi, rbp RDI => 0x5555555592a0 ◂— 0xfbad2484
0x7ffff7c8d8fc <__underflow+140> pop rbx RBX => 0x5555555592a0
0x7ffff7c8d8fd <__underflow+141> pop rbp RBP => 0
0x7ffff7c8d8fe <__underflow+142> jmp rax <_IO_file_underflow>

放上vtable->_IO_file_underflow的汇编

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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
pwndbg> disass
Dump of assembler code for function _IO_new_file_underflow:
Address range 0x7ffff7c8cab0 to 0x7ffff7c8cdb9:
=> 0x00007ffff7c8cab0 <+0>: endbr64
0x00007ffff7c8cab4 <+4>: mov eax,DWORD PTR [rdi]
0x00007ffff7c8cab6 <+6>: test al,0x10
0x00007ffff7c8cab8 <+8>: jne 0x7ffff7c8cd88 <_IO_new_file_underflow+728>
0x00007ffff7c8cabe <+14>: push r15
0x00007ffff7c8cac0 <+16>: push r14
0x00007ffff7c8cac2 <+18>: push r13
0x00007ffff7c8cac4 <+20>: push r12
0x00007ffff7c8cac6 <+22>: push rbp
0x00007ffff7c8cac7 <+23>: push rbx
0x00007ffff7c8cac8 <+24>: mov rbx,rdi
0x00007ffff7c8cacb <+27>: sub rsp,0x18
0x00007ffff7c8cacf <+31>: test al,0x4
0x00007ffff7c8cad1 <+33>: jne 0x7ffff7c8cd90 <_IO_new_file_underflow+736>
0x00007ffff7c8cad7 <+39>: mov rdx,QWORD PTR [rdi+0x8]
0x00007ffff7c8cadb <+43>: cmp rdx,QWORD PTR [rdi+0x10]
0x00007ffff7c8cadf <+47>: jb 0x7ffff7c8cc70 <_IO_new_file_underflow+448>
0x00007ffff7c8cae5 <+53>: cmp QWORD PTR [rdi+0x38],0x0
0x00007ffff7c8caea <+58>: je 0x7ffff7c8ccc0 <_IO_new_file_underflow+528>
0x00007ffff7c8caf0 <+64>: test eax,0x202
0x00007ffff7c8caf5 <+69>: je 0x7ffff7c8cbd2 <_IO_new_file_underflow+290>
0x00007ffff7c8cafb <+75>: mov rax,QWORD PTR [rip+0x18d336] # 0x7ffff7e19e38
0x00007ffff7c8cb02 <+82>: mov r12,QWORD PTR [rax]
0x00007ffff7c8cb05 <+85>: mov edx,DWORD PTR [r12]
0x00007ffff7c8cb09 <+89>: mov eax,edx
0x00007ffff7c8cb0b <+91>: and eax,0x8000
0x00007ffff7c8cb10 <+96>: jne 0x7ffff7c8cbc0 <_IO_new_file_underflow+272>
0x00007ffff7c8cb16 <+102>: mov rdi,QWORD PTR [r12+0x88]
0x00007ffff7c8cb1e <+110>: mov rbp,QWORD PTR fs:0x10
0x00007ffff7c8cb27 <+119>: cmp QWORD PTR [rdi+0x8],rbp
0x00007ffff7c8cb2b <+123>: je 0x7ffff7c8ccf0 <_IO_new_file_underflow+576>
0x00007ffff7c8cb31 <+129>: mov edx,0x1
0x00007ffff7c8cb36 <+134>: lock cmpxchg DWORD PTR [rdi],edx
0x00007ffff7c8cb3a <+138>: jne 0x7ffff7c8cd78 <_IO_new_file_underflow+712>
0x00007ffff7c8cb40 <+144>: mov rax,QWORD PTR [rip+0x18d2f1] # 0x7ffff7e19e38
0x00007ffff7c8cb47 <+151>: mov rdi,QWORD PTR [r12+0x88]
0x00007ffff7c8cb4f <+159>: mov r8,QWORD PTR [rax]
0x00007ffff7c8cb52 <+162>: mov QWORD PTR [rdi+0x8],rbp
0x00007ffff7c8cb56 <+166>: mov edx,DWORD PTR [r8]
0x00007ffff7c8cb59 <+169>: and edx,0x288
0x00007ffff7c8cb5f <+175>: add DWORD PTR [rdi+0x4],0x1
0x00007ffff7c8cb63 <+179>: cmp edx,0x280
0x00007ffff7c8cb69 <+185>: je 0x7ffff7c8cd03 <_IO_new_file_underflow+595>
0x00007ffff7c8cb6f <+191>: lea r15,[rip+0x18abf2] # 0x7ffff7e17768
0x00007ffff7c8cb76 <+198>: lea rbp,[rip+0x189e83] # 0x7ffff7e16a00 <_IO_helper_jumps>
0x00007ffff7c8cb7d <+205>: mov r13,r15
0x00007ffff7c8cb80 <+208>: mov r14,rbp
0x00007ffff7c8cb83 <+211>: sub r13,rbp
0x00007ffff7c8cb86 <+214>: test DWORD PTR [r12],0x8000
0x00007ffff7c8cb8e <+222>: jne 0x7ffff7c8cbe0 <_IO_new_file_underflow+304>
0x00007ffff7c8cb90 <+224>: mov rdi,QWORD PTR [r12+0x88]
0x00007ffff7c8cb98 <+232>: mov eax,DWORD PTR [rdi+0x4]
0x00007ffff7c8cb9b <+235>: sub eax,0x1
0x00007ffff7c8cb9e <+238>: mov DWORD PTR [rdi+0x4],eax
0x00007ffff7c8cba1 <+241>: jne 0x7ffff7c8cbe0 <_IO_new_file_underflow+304>
0x00007ffff7c8cba3 <+243>: mov QWORD PTR [rdi+0x8],0x0
0x00007ffff7c8cbab <+251>: xchg DWORD PTR [rdi],eax
0x00007ffff7c8cbad <+253>: cmp eax,0x1
0x00007ffff7c8cbb0 <+256>: jle 0x7ffff7c8cbe0 <_IO_new_file_underflow+304>
0x00007ffff7c8cbb2 <+258>: call 0x7ffff7c91300 <__GI___lll_lock_wake_private>
0x00007ffff7c8cbb7 <+263>: jmp 0x7ffff7c8cbed <_IO_new_file_underflow+317>
0x00007ffff7c8cbb9 <+265>: nop DWORD PTR [rax+0x0]
0x00007ffff7c8cbc0 <+272>: and edx,0x288
0x00007ffff7c8cbc6 <+278>: cmp edx,0x280
0x00007ffff7c8cbcc <+284>: je 0x7ffff7c8cd00 <_IO_new_file_underflow+592>
0x00007ffff7c8cbd2 <+290>: lea r15,[rip+0x18ab8f] # 0x7ffff7e17768
0x00007ffff7c8cbd9 <+297>: lea rbp,[rip+0x189e20] # 0x7ffff7e16a00 <_IO_helper_jumps>
0x00007ffff7c8cbe0 <+304>: sub r15,rbp
0x00007ffff7c8cbe3 <+307>: lea r14,[rip+0x189e16] # 0x7ffff7e16a00 <_IO_helper_jumps>
0x00007ffff7c8cbea <+314>: mov r13,r15
0x00007ffff7c8cbed <+317>: mov rdi,rbx
0x00007ffff7c8cbf0 <+320>: call 0x7ffff7c8d720 <__GI__IO_switch_to_get_mode>
0x00007ffff7c8cbf5 <+325>: movdqu xmm0,XMMWORD PTR [rbx+0x38]
0x00007ffff7c8cbfa <+330>: mov rbp,QWORD PTR [rbx+0xd8]
0x00007ffff7c8cc01 <+337>: movdqa xmm1,xmm0
0x00007ffff7c8cc05 <+341>: mov rax,rbp
0x00007ffff7c8cc08 <+344>: movq rsi,xmm0
0x00007ffff7c8cc0d <+349>: punpcklqdq xmm1,xmm0
0x00007ffff7c8cc11 <+353>: sub rax,r14
0x00007ffff7c8cc14 <+356>: movups XMMWORD PTR [rbx+0x8],xmm1
0x00007ffff7c8cc18 <+360>: movups XMMWORD PTR [rbx+0x18],xmm1
0x00007ffff7c8cc1c <+364>: movups XMMWORD PTR [rbx+0x28],xmm1
0x00007ffff7c8cc20 <+368>: cmp rax,r13
0x00007ffff7c8cc23 <+371>: jae 0x7ffff7c8cd48 <_IO_new_file_underflow+664>
0x00007ffff7c8cc29 <+377>: mov rdx,QWORD PTR [rbx+0x40]
0x00007ffff7c8cc2d <+381>: mov rdi,rbx
0x00007ffff7c8cc30 <+384>: sub rdx,rsi
0x00007ffff7c8cc33 <+387>: call QWORD PTR [rbp+0x70]
0x00007ffff7c8cc36 <+390>: test rax,rax
0x00007ffff7c8cc39 <+393>: jle 0x7ffff7c8cc88 <_IO_new_file_underflow+472>
0x00007ffff7c8cc3b <+395>: mov rdx,QWORD PTR [rbx+0x90]
0x00007ffff7c8cc42 <+402>: add QWORD PTR [rbx+0x10],rax
0x00007ffff7c8cc46 <+406>: cmp rdx,0xffffffffffffffff
0x00007ffff7c8cc4a <+410>: je 0x7ffff7c8cc56 <_IO_new_file_underflow+422>
0x00007ffff7c8cc4c <+412>: add rdx,rax
0x00007ffff7c8cc4f <+415>: mov QWORD PTR [rbx+0x90],rdx
0x00007ffff7c8cc56 <+422>: mov rax,QWORD PTR [rbx+0x8]
0x00007ffff7c8cc5a <+426>: movzx eax,BYTE PTR [rax]
0x00007ffff7c8cc5d <+429>: add rsp,0x18
0x00007ffff7c8cc61 <+433>: pop rbx
0x00007ffff7c8cc62 <+434>: pop rbp
0x00007ffff7c8cc63 <+435>: pop r12
0x00007ffff7c8cc65 <+437>: pop r13
0x00007ffff7c8cc67 <+439>: pop r14
0x00007ffff7c8cc69 <+441>: pop r15
0x00007ffff7c8cc6b <+443>: ret
0x00007ffff7c8cc6c <+444>: nop DWORD PTR [rax+0x0]
0x00007ffff7c8cc70 <+448>: movzx eax,BYTE PTR [rdx]
0x00007ffff7c8cc73 <+451>: add rsp,0x18
0x00007ffff7c8cc77 <+455>: pop rbx
0x00007ffff7c8cc78 <+456>: pop rbp
0x00007ffff7c8cc79 <+457>: pop r12
0x00007ffff7c8cc7b <+459>: pop r13
0x00007ffff7c8cc7d <+461>: pop r14
0x00007ffff7c8cc7f <+463>: pop r15
0x00007ffff7c8cc81 <+465>: ret
0x00007ffff7c8cc82 <+466>: nop WORD PTR [rax+rax*1+0x0]
0x00007ffff7c8cc88 <+472>: mov QWORD PTR [rbx+0x90],0xffffffffffffffff
0x00007ffff7c8cc93 <+483>: mov edx,DWORD PTR [rbx]
0x00007ffff7c8cc95 <+485>: mov ecx,edx
0x00007ffff7c8cc97 <+487>: or edx,0x20
0x00007ffff7c8cc9a <+490>: or ecx,0x10
0x00007ffff7c8cc9d <+493>: test rax,rax
0x00007ffff7c8cca0 <+496>: mov eax,0xffffffff
0x00007ffff7c8cca5 <+501>: cmove edx,ecx
0x00007ffff7c8cca8 <+504>: mov DWORD PTR [rbx],edx
0x00007ffff7c8ccaa <+506>: add rsp,0x18
0x00007ffff7c8ccae <+510>: pop rbx
0x00007ffff7c8ccaf <+511>: pop rbp
0x00007ffff7c8ccb0 <+512>: pop r12
0x00007ffff7c8ccb2 <+514>: pop r13
0x00007ffff7c8ccb4 <+516>: pop r14
0x00007ffff7c8ccb6 <+518>: pop r15
0x00007ffff7c8ccb8 <+520>: ret
0x00007ffff7c8ccb9 <+521>: nop DWORD PTR [rax+0x0]
0x00007ffff7c8ccc0 <+528>: mov rdi,QWORD PTR [rdi+0x48]
0x00007ffff7c8ccc4 <+532>: test rdi,rdi
0x00007ffff7c8ccc7 <+535>: je 0x7ffff7c8ccd4 <_IO_new_file_underflow+548>
0x00007ffff7c8ccc9 <+537>: call 0x7ffff7c28370 <free@plt>
0x00007ffff7c8ccce <+542>: and DWORD PTR [rbx],0xfffffeff
0x00007ffff7c8ccd4 <+548>: mov rdi,rbx
0x00007ffff7c8ccd7 <+551>: call 0x7ffff7c8dc90 <__GI__IO_doallocbuf>
0x00007ffff7c8ccdc <+556>: mov eax,DWORD PTR [rbx]
0x00007ffff7c8ccde <+558>: test eax,0x202
0x00007ffff7c8cce3 <+563>: jne 0x7ffff7c8cafb <_IO_new_file_underflow+75>
0x00007ffff7c8cce9 <+569>: jmp 0x7ffff7c8cbd2 <_IO_new_file_underflow+290>
0x00007ffff7c8ccee <+574>: xchg ax,ax
0x00007ffff7c8ccf0 <+576>: mov r8,r12
0x00007ffff7c8ccf3 <+579>: jmp 0x7ffff7c8cb59 <_IO_new_file_underflow+169>
0x00007ffff7c8ccf8 <+584>: nop DWORD PTR [rax+rax*1+0x0]
0x00007ffff7c8cd00 <+592>: mov r8,r12
0x00007ffff7c8cd03 <+595>: mov rax,QWORD PTR [r8+0xd8]
0x00007ffff7c8cd0a <+602>: lea r15,[rip+0x18aa57] # 0x7ffff7e17768
0x00007ffff7c8cd11 <+609>: lea rbp,[rip+0x189ce8] # 0x7ffff7e16a00 <_IO_helper_jumps>
0x00007ffff7c8cd18 <+616>: mov r13,r15
0x00007ffff7c8cd1b <+619>: mov r14,rbp
0x00007ffff7c8cd1e <+622>: mov QWORD PTR [rsp+0x8],rax
0x00007ffff7c8cd23 <+627>: sub r13,rbp
0x00007ffff7c8cd26 <+630>: sub rax,rbp
0x00007ffff7c8cd29 <+633>: cmp r13,rax
0x00007ffff7c8cd2c <+636>: jbe 0x7ffff7c8cd60 <_IO_new_file_underflow+688>
0x00007ffff7c8cd2e <+638>: mov rax,QWORD PTR [rsp+0x8]
0x00007ffff7c8cd33 <+643>: mov esi,0xffffffff
0x00007ffff7c8cd38 <+648>: mov rdi,r8
0x00007ffff7c8cd3b <+651>: call QWORD PTR [rax+0x18]
0x00007ffff7c8cd3e <+654>: jmp 0x7ffff7c8cb86 <_IO_new_file_underflow+214>
0x00007ffff7c8cd43 <+659>: nop DWORD PTR [rax+rax*1+0x0]
0x00007ffff7c8cd48 <+664>: call 0x7ffff7c89ef0 <_IO_vtable_check>
0x00007ffff7c8cd4d <+669>: mov rsi,QWORD PTR [rbx+0x38]
0x00007ffff7c8cd51 <+673>: jmp 0x7ffff7c8cc29 <_IO_new_file_underflow+377>
0x00007ffff7c8cd56 <+678>: cs nop WORD PTR [rax+rax*1+0x0]
0x00007ffff7c8cd60 <+688>: call 0x7ffff7c89ef0 <_IO_vtable_check>
0x00007ffff7c8cd65 <+693>: mov rax,QWORD PTR [rip+0x18d0cc] # 0x7ffff7e19e38
0x00007ffff7c8cd6c <+700>: mov r8,QWORD PTR [rax]
0x00007ffff7c8cd6f <+703>: jmp 0x7ffff7c8cd2e <_IO_new_file_underflow+638>
0x00007ffff7c8cd71 <+705>: nop DWORD PTR [rax+0x0]
0x00007ffff7c8cd78 <+712>: call 0x7ffff7c91230 <__GI___lll_lock_wait_private>
0x00007ffff7c8cd7d <+717>: jmp 0x7ffff7c8cb40 <_IO_new_file_underflow+144>
0x00007ffff7c8cd82 <+722>: nop WORD PTR [rax+rax*1+0x0]
0x00007ffff7c8cd88 <+728>: mov eax,0xffffffff
0x00007ffff7c8cd8d <+733>: ret
0x00007ffff7c8cd8e <+734>: xchg ax,ax
0x00007ffff7c8cd90 <+736>: or eax,0x20
0x00007ffff7c8cd93 <+739>: mov DWORD PTR [rdi],eax
0x00007ffff7c8cd95 <+741>: mov rax,QWORD PTR [rip+0x18d074] # 0x7ffff7e19e10
0x00007ffff7c8cd9c <+748>: mov DWORD PTR fs:[rax],0x9
0x00007ffff7c8cda3 <+755>: mov eax,0xffffffff
0x00007ffff7c8cda8 <+760>: jmp 0x7ffff7c8cc5d <_IO_new_file_underflow+429>
0x00007ffff7c8cdad <+765>: endbr64
0x00007ffff7c8cdb1 <+769>: mov rbp,rax
0x00007ffff7c8cdb4 <+772>: jmp 0x7ffff7c2984e <_IO_new_file_underflow.cold>
Address range 0x7ffff7c2984e to 0x7ffff7c29887:
0x00007ffff7c2984e <-406114>: test DWORD PTR [r12],0x8000
0x00007ffff7c29856 <-406106>: jne 0x7ffff7c2987f <_IO_new_file_underflow-406065>
0x00007ffff7c29858 <-406104>: mov rdi,QWORD PTR [r12+0x88]
0x00007ffff7c29860 <-406096>: mov eax,DWORD PTR [rdi+0x4]
0x00007ffff7c29863 <-406093>: sub eax,0x1
0x00007ffff7c29866 <-406090>: mov DWORD PTR [rdi+0x4],eax
0x00007ffff7c29869 <-406087>: jne 0x7ffff7c2987f <_IO_new_file_underflow-406065>
0x00007ffff7c2986b <-406085>: mov QWORD PTR [rdi+0x8],0x0
0x00007ffff7c29873 <-406077>: xchg DWORD PTR [rdi],eax
0x00007ffff7c29875 <-406075>: sub eax,0x1
0x00007ffff7c29878 <-406072>: jle 0x7ffff7c2987f <_IO_new_file_underflow-406065>
0x00007ffff7c2987a <-406070>: call 0x7ffff7c91300 <__GI___lll_lock_wake_private>
0x00007ffff7c2987f <-406065>: mov rdi,rbp
0x00007ffff7c29882 <-406062>: call 0x7ffff7c2a120 <_Unwind_Resume>
End of assembler dump.

vtable->_IO_file_underflow函数主体是先检查缓冲区,如果fp->_IO_buf_base == NULL则调用_IO_doallocbuf来分配缓冲区,然后系统调用read读取数据到缓冲区,然后根据需要读取的数据调整读写指针
发现这里_IO_file_underflow还会调用_IO_switch_to_get_mode 函数来切换文件模式为读(_mode=-1)并清空对应指针防止干扰

1
2
0x7ffff7c8cbed <_IO_file_underflow+317>    mov    rdi, rbx                  RDI => 0x5555555592a0 ◂— 0xfbad2488
0x7ffff7c8cbf0 <_IO_file_underflow+320> call _IO_switch_to_get_mode <_IO_switch_to_get_mode>

后续回到_IO_file_underflow函数,设置读写指针,如下所示,都设置为了fp->_IO_buf_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
26
27
28
29
30
31
32
33
34
35
pwndbg> p *(struct _IO_FILE_plus*) 0x5555555592a0
$3 = {
file = {
_flags = -72539000,
_IO_read_ptr = 0x5555555594b0 "",
_IO_read_end = 0x5555555594b0 "",
_IO_read_base = 0x5555555594b0 "",
_IO_write_base = 0x5555555594b0 "",
_IO_write_ptr = 0x5555555594b0 "",
_IO_write_end = 0x5555555594b0 "",
_IO_buf_base = 0x5555555594b0 "",
_IO_buf_end = 0x55555555a4b0 "",
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7e1b6a0 <_IO_2_1_stderr_>,
_fileno = 3,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x555555559380,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x555555559390,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7e17600 <_IO_file_jumps>
}

然后_IO_file_underflow调用vtable->_IO_file_read(位于_IO_file_jumps+0x70)

1
2
3
4
0x7ffff7c8cc29 <_IO_file_underflow+377>    mov    rdx, qword ptr [rbx + 0x40]        RDX, [0x5555555592e0] => 0x55555555a4b0 ◂— 0
0x7ffff7c8cc2d <_IO_file_underflow+381> mov rdi, rbx RDI => 0x5555555592a0 ◂— 0xfbad2488
0x7ffff7c8cc30 <_IO_file_underflow+384> sub rdx, rsi RDX => 0x1000 (0x55555555a4b0 - 0x5555555594b0)
0x7ffff7c8cc33 <_IO_file_underflow+387> call qword ptr [rbp + 0x70] <_IO_file_read>

然后再系统调用read函数,将文件数据读入fp->_IO_buf_base(最多读0x1000,即_IO_buf_end - _IO_buf_base

1
2
3
4
5
6
7
8
9
10
11
12
0x7ffff7c8b93c <_IO_file_read+12>    mov    edi, r8d                        EDI => 3
0x7ffff7c8b93f <_IO_file_read+15> ✘ jne _IO_file_read+32 <_IO_file_read+32>

0x7ffff7c8b941 <_IO_file_read+17> jmp read <read>

0x7ffff7d147d0 <read> endbr64
0x7ffff7d147d4 <read+4> mov eax, dword ptr fs:[0x18] EAX, [0x7ffff7fb1758] => 0
0x7ffff7d147dc <read+12> test eax, eax 0 & 0 EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7d147de <read+14> ✘ jne read+32 <read+32>

0x7ffff7d147e0 <read+16> syscall <SYS_read>
───────────────────────────────────────────────────────────

可以看到缓冲区读入的’a’

1
2
3
4
5
6
pwndbg> x/10gx 0x5555555594a0
0x5555555594a0: 0x0000000000000000 0x0000000000001011
0x5555555594b0: 0x6161616161616161 0x6161616161616161
0x5555555594c0: 0x6161616161616161 0x6161616161616161
0x5555555594d0: 0x6161616161616161 0x6161616161616161
0x5555555594e0: 0x00000a6161616161 0x0000000000000000

最后_IO_file_underflow执行完毕,结构体状态如下

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
pwndbg> p *(struct _IO_FILE_plus*) 0x5555555592a0
$3 = {
file = {
_flags = -72539000,
_IO_read_ptr = 0x5555555594b0 'a' <repeats 53 times>, "\n",
_IO_read_end = 0x5555555594e6 "",
_IO_read_base = 0x5555555594b0 'a' <repeats 53 times>, "\n",
_IO_write_base = 0x5555555594b0 'a' <repeats 53 times>, "\n",
_IO_write_ptr = 0x5555555594b0 'a' <repeats 53 times>, "\n",
_IO_write_end = 0x5555555594b0 'a' <repeats 53 times>, "\n",
_IO_buf_base = 0x5555555594b0 'a' <repeats 53 times>, "\n",
_IO_buf_end = 0x55555555a4b0 "",
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7e1b6a0 <_IO_2_1_stderr_>,
_fileno = 3,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x555555559380,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x555555559390,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7e17600 <_IO_file_jumps>
}

_IO_read_end已调整至读缓冲区结束位置
然后回到_IO_file_xsgetn的循环,将缓冲区中的数据读入目标地址,可以粗略看做

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
while (n > 0)
{
size_t avail = fp->_IO_read_end - fp->_IO_read_ptr;
if (avail > 0)
{
size_t to_copy = MIN(avail, n);
memcpy(s, fp->_IO_read_ptr, to_copy);
fp->_IO_read_ptr += to_copy;
s += to_copy;
n -= to_copy;
already += to_copy;
}
else
{
// 缓冲区已空,调用 refill 函数
if (__underflow(fp) == EOF)
break;
}
}

最后fread结束后可以看到数据(20个’a’)已经读入我们的目标地址

1
2
3
4
pwndbg> x/6gx 0x555555559470
0x555555559470: 0x00007ffff7e170c0 0x0000000000000031
0x555555559480: 0x6161616161616161 0x6161616161616161
0x555555559490: 0x0000000061616161 0x0000000000000000

0x04 fwrite函数

还是同一个demo,改成fwrite函数即可,其实fwrite的流程和fread是很类似的

1
2
3
4
5
6
7
8
9
10
11
#include<stdio.h>
#include<stdlib.h>

int main(){

FILE*fp = fopen("test.txt","wb");
char *ptr = malloc(0x20);
memcpy(ptr,"wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww",0x20);
fwrite(ptr, 1, 0x20, fp);
return 0;
}

在fwrite下个断点,发现是_IO_fwrite函数,贴上汇编

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
pwndbg> disass
Dump of assembler code for function __GI__IO_fwrite:
Address range 0x7ffff7c7ff20 to 0x7ffff7c800a6:
=> 0x00007ffff7c7ff20 <+0>: endbr64
0x00007ffff7c7ff24 <+4>: push r15
0x00007ffff7c7ff26 <+6>: push r14
0x00007ffff7c7ff28 <+8>: push r13
0x00007ffff7c7ff2a <+10>: push r12
0x00007ffff7c7ff2c <+12>: mov r12,rsi
0x00007ffff7c7ff2f <+15>: push rbp
0x00007ffff7c7ff30 <+16>: imul r12,rdx
0x00007ffff7c7ff34 <+20>: push rbx
0x00007ffff7c7ff35 <+21>: sub rsp,0x8
0x00007ffff7c7ff39 <+25>: test r12,r12
0x00007ffff7c7ff3c <+28>: je 0x7ffff7c80015 <__GI__IO_fwrite+245>
0x00007ffff7c7ff42 <+34>: mov eax,DWORD PTR [rcx]
0x00007ffff7c7ff44 <+36>: mov r14,rdi
0x00007ffff7c7ff47 <+39>: mov r13,rsi
0x00007ffff7c7ff4a <+42>: mov rbp,rdx
0x00007ffff7c7ff4d <+45>: mov rbx,rcx
0x00007ffff7c7ff50 <+48>: and eax,0x8000
0x00007ffff7c7ff55 <+53>: jne 0x7ffff7c7ff8b <__GI__IO_fwrite+107>
0x00007ffff7c7ff57 <+55>: mov r15,QWORD PTR fs:0x10
0x00007ffff7c7ff60 <+64>: mov rdi,QWORD PTR [rcx+0x88]
0x00007ffff7c7ff67 <+71>: cmp QWORD PTR [rdi+0x8],r15
0x00007ffff7c7ff6b <+75>: je 0x7ffff7c7ff87 <__GI__IO_fwrite+103>
0x00007ffff7c7ff6d <+77>: mov edx,0x1
0x00007ffff7c7ff72 <+82>: lock cmpxchg DWORD PTR [rdi],edx
0x00007ffff7c7ff76 <+86>: jne 0x7ffff7c80090 <__GI__IO_fwrite+368>
0x00007ffff7c7ff7c <+92>: mov rdi,QWORD PTR [rbx+0x88]
0x00007ffff7c7ff83 <+99>: mov QWORD PTR [rdi+0x8],r15
0x00007ffff7c7ff87 <+103>: add DWORD PTR [rdi+0x4],0x1
0x00007ffff7c7ff8b <+107>: mov eax,DWORD PTR [rbx+0xc0]
0x00007ffff7c7ff91 <+113>: test eax,eax
0x00007ffff7c7ff93 <+115>: jne 0x7ffff7c80030 <__GI__IO_fwrite+272>
0x00007ffff7c7ff99 <+121>: mov DWORD PTR [rbx+0xc0],0xffffffff
0x00007ffff7c7ffa3 <+131>: mov r15,QWORD PTR [rbx+0xd8]
0x00007ffff7c7ffaa <+138>: lea rdx,[rip+0x196a4f] # 0x7ffff7e16a00 <_IO_helper_jumps>
0x00007ffff7c7ffb1 <+145>: lea rax,[rip+0x1977b0] # 0x7ffff7e17768
0x00007ffff7c7ffb8 <+152>: sub rax,rdx
0x00007ffff7c7ffbb <+155>: mov rcx,r15
0x00007ffff7c7ffbe <+158>: sub rcx,rdx
0x00007ffff7c7ffc1 <+161>: cmp rax,rcx
0x00007ffff7c7ffc4 <+164>: jbe 0x7ffff7c80080 <__GI__IO_fwrite+352>
0x00007ffff7c7ffca <+170>: mov rdx,r12
0x00007ffff7c7ffcd <+173>: mov rsi,r14
0x00007ffff7c7ffd0 <+176>: mov rdi,rbx
0x00007ffff7c7ffd3 <+179>: call QWORD PTR [r15+0x38]
0x00007ffff7c7ffd7 <+183>: cmp rax,0xffffffffffffffff
0x00007ffff7c7ffdb <+187>: mov r14,rax
0x00007ffff7c7ffde <+190>: sete r15b
0x00007ffff7c7ffe2 <+194>: test DWORD PTR [rbx],0x8000
0x00007ffff7c7ffe8 <+200>: jne 0x7ffff7c80008 <__GI__IO_fwrite+232>
0x00007ffff7c7ffea <+202>: mov rdi,QWORD PTR [rbx+0x88]
0x00007ffff7c7fff1 <+209>: sub DWORD PTR [rdi+0x4],0x1
0x00007ffff7c7fff5 <+213>: jne 0x7ffff7c80008 <__GI__IO_fwrite+232>
0x00007ffff7c7fff7 <+215>: mov QWORD PTR [rdi+0x8],0x0
0x00007ffff7c7ffff <+223>: xor eax,eax
0x00007ffff7c80001 <+225>: xchg DWORD PTR [rdi],eax
0x00007ffff7c80003 <+227>: cmp eax,0x1
0x00007ffff7c80006 <+230>: jg 0x7ffff7c80068 <__GI__IO_fwrite+328>
0x00007ffff7c80008 <+232>: cmp r12,r14
0x00007ffff7c8000b <+235>: je 0x7ffff7c80012 <__GI__IO_fwrite+242>
0x00007ffff7c8000d <+237>: test r15b,r15b
0x00007ffff7c80010 <+240>: je 0x7ffff7c8006f <__GI__IO_fwrite+335>
0x00007ffff7c80012 <+242>: mov r12,rbp
0x00007ffff7c80015 <+245>: add rsp,0x8
0x00007ffff7c80019 <+249>: mov rax,r12
0x00007ffff7c8001c <+252>: pop rbx
0x00007ffff7c8001d <+253>: pop rbp
0x00007ffff7c8001e <+254>: pop r12
0x00007ffff7c80020 <+256>: pop r13
0x00007ffff7c80022 <+258>: pop r14
0x00007ffff7c80024 <+260>: pop r15
0x00007ffff7c80026 <+262>: ret
0x00007ffff7c80027 <+263>: nop WORD PTR [rax+rax*1+0x0]
0x00007ffff7c80030 <+272>: cmp eax,0xffffffff
0x00007ffff7c80033 <+275>: je 0x7ffff7c7ffa3 <__GI__IO_fwrite+131>
0x00007ffff7c80039 <+281>: test DWORD PTR [rbx],0x8000
0x00007ffff7c8003f <+287>: jne 0x7ffff7c80060 <__GI__IO_fwrite+320>
0x00007ffff7c80041 <+289>: mov rdi,QWORD PTR [rbx+0x88]
0x00007ffff7c80048 <+296>: sub DWORD PTR [rdi+0x4],0x1
0x00007ffff7c8004c <+300>: jne 0x7ffff7c80060 <__GI__IO_fwrite+320>
0x00007ffff7c8004e <+302>: xor r15d,r15d
0x00007ffff7c80051 <+305>: xor r14d,r14d
0x00007ffff7c80054 <+308>: jmp 0x7ffff7c7fff7 <__GI__IO_fwrite+215>
0x00007ffff7c80056 <+310>: cs nop WORD PTR [rax+rax*1+0x0]
0x00007ffff7c80060 <+320>: xor r12d,r12d
0x00007ffff7c80063 <+323>: jmp 0x7ffff7c80015 <__GI__IO_fwrite+245>
0x00007ffff7c80065 <+325>: nop DWORD PTR [rax]
0x00007ffff7c80068 <+328>: call 0x7ffff7c91300 <__GI___lll_lock_wake_private>
0x00007ffff7c8006d <+333>: jmp 0x7ffff7c80008 <__GI__IO_fwrite+232>
0x00007ffff7c8006f <+335>: mov rax,r14
0x00007ffff7c80072 <+338>: xor edx,edx
0x00007ffff7c80074 <+340>: div r13
0x00007ffff7c80077 <+343>: mov r12,rax
0x00007ffff7c8007a <+346>: jmp 0x7ffff7c80015 <__GI__IO_fwrite+245>
0x00007ffff7c8007c <+348>: nop DWORD PTR [rax+0x0]
0x00007ffff7c80080 <+352>: call 0x7ffff7c89ef0 <_IO_vtable_check>
0x00007ffff7c80085 <+357>: jmp 0x7ffff7c7ffca <__GI__IO_fwrite+170>
0x00007ffff7c8008a <+362>: nop WORD PTR [rax+rax*1+0x0]
0x00007ffff7c80090 <+368>: call 0x7ffff7c91230 <__GI___lll_lock_wait_private>
0x00007ffff7c80095 <+373>: jmp 0x7ffff7c7ff7c <__GI__IO_fwrite+92>
0x00007ffff7c8009a <+378>: endbr64
0x00007ffff7c8009e <+382>: mov rbp,rax
0x00007ffff7c800a1 <+385>: jmp 0x7ffff7c291b1 <__GI__IO_fwrite.cold>
Address range 0x7ffff7c291b1 to 0x7ffff7c291e7:
0x00007ffff7c291b1 <-355695>: test DWORD PTR [rbx],0x8000
0x00007ffff7c291b7 <-355689>: jne 0x7ffff7c291df <__GI__IO_fwrite-355649>
0x00007ffff7c291b9 <-355687>: mov rdi,QWORD PTR [rbx+0x88]
0x00007ffff7c291c0 <-355680>: mov eax,DWORD PTR [rdi+0x4]
0x00007ffff7c291c3 <-355677>: sub eax,0x1
0x00007ffff7c291c6 <-355674>: mov DWORD PTR [rdi+0x4],eax
0x00007ffff7c291c9 <-355671>: jne 0x7ffff7c291df <__GI__IO_fwrite-355649>
0x00007ffff7c291cb <-355669>: mov QWORD PTR [rdi+0x8],0x0
0x00007ffff7c291d3 <-355661>: xchg DWORD PTR [rdi],eax
0x00007ffff7c291d5 <-355659>: sub eax,0x1
0x00007ffff7c291d8 <-355656>: jle 0x7ffff7c291df <__GI__IO_fwrite-355649>
0x00007ffff7c291da <-355654>: call 0x7ffff7c91300 <__GI___lll_lock_wake_private>
0x00007ffff7c291df <-355649>: mov rdi,rbp
0x00007ffff7c291e2 <-355646>: call 0x7ffff7c2a120 <_Unwind_Resume>
End of assembler dump.

回到调试,也是和fread的开始类似,先计算要写入的字节数(为0则直接返回),再判断一下_flags的状态进行一些有关锁的操作

1
2
3
4
5
6
7
8
9
10
11
0x7ffff7c7ff24 <fwrite+4>     push   r15
0x7ffff7c7ff26 <fwrite+6> push r14
0x7ffff7c7ff28 <fwrite+8> push r13
0x7ffff7c7ff2a <fwrite+10> push r12
0x7ffff7c7ff2c <fwrite+12> mov r12, rsi R12 => 1
0x7ffff7c7ff2f <fwrite+15> push rbp
0x7ffff7c7ff30 <fwrite+16> imul r12, rdx
0x7ffff7c7ff34 <fwrite+20> push rbx
0x7ffff7c7ff35 <fwrite+21> sub rsp, 8 RSP => 0x7fffffffdc90 (0x7fffffffdc98 - 0x8)
0x7ffff7c7ff39 <fwrite+25> test r12, r12 0x20 & 0x20 EFLAGS => 0x202 [ cf pf af zf sf IF df of ac ]
0x7ffff7c7ff3c <fwrite+28> ✘ je fwrite+245 <fwrite+245>

可见判断了写入字节数test r12, r12是否为0,为零则跳转到<fwrite+245>,正是返回的指令

1
2
3
4
5
6
7
8
9
0x00007ffff7c80015 <+245>:   add    rsp,0x8
0x00007ffff7c80019 <+249>: mov rax,r12
0x00007ffff7c8001c <+252>: pop rbx
0x00007ffff7c8001d <+253>: pop rbp
0x00007ffff7c8001e <+254>: pop r12
0x00007ffff7c80020 <+256>: pop r13
0x00007ffff7c80022 <+258>: pop r14
0x00007ffff7c80024 <+260>: pop r15
0x00007ffff7c80026 <+262>: ret

然后就是关于_IO_FILE线程锁的操作,笔者对这块知识不熟悉,就先不放了
再后续就是检查_mode字段以及vtable操作

1
2
3
0x7ffff7c7ff8b <fwrite+107>    mov    eax, dword ptr [rbx + 0xc0]     EAX, [0x555555559360] => 0
0x7ffff7c7ff91 <fwrite+113> test eax, eax 0 & 0 EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c7ff93 <fwrite+115> ✘ jne fwrite+272 <fwrite+272>

这里检查[rbx + 0xc0]也就是_IO_FILE_plus偏移0xc0处,也就是_mode处是否为0,不为0则跳转,我们看跳转处

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
pwndbg> p *_IO_list_all
$1 = {
file = {
_flags = -72539000,
_IO_read_ptr = 0x0,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_IO_write_end = 0x0,
_IO_buf_base = 0x0,
_IO_buf_end = 0x0,
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7e1b6a0 <_IO_2_1_stderr_>,
_fileno = 3,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x555555559380,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x555555559390,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = 0,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7e17600 <_IO_file_jumps>
}

我们这里_mode为0,不会跳转,但还是看看跳转过去的判断

1
2
0x00007ffff7c80030 <+272>:   cmp    eax,0xffffffff
0x00007ffff7c80033 <+275>: je 0x7ffff7c7ffa3 <__GI__IO_fwrite+131>

_mode为-1则跳转回来

1
2
3
4
5
6
7
8
0x7ffff7c7ff99 <fwrite+121>    mov    dword ptr [rbx + 0xc0], 0xffffffff     [0x555555559360] <= 0xffffffff
0x7ffff7c7ffa3 <fwrite+131> mov r15, qword ptr [rbx + 0xd8] R15, [0x555555559378] => 0x7ffff7e17600 (_IO_file_jumps) ◂— 0
0x7ffff7c7ffaa <fwrite+138> lea rdx, [rip + 0x196a4f] RDX => 0x7ffff7e16a00 (_IO_helper_jumps) ◂— 0
0x7ffff7c7ffb1 <fwrite+145> lea rax, [rip + 0x1977b0] RAX => 0x7ffff7e17768 ◂— 0
0x7ffff7c7ffb8 <fwrite+152> sub rax, rdx RAX => 0xd68 (0x7ffff7e17768 - 0x7ffff7e16a00)
0x7ffff7c7ffbb <fwrite+155> mov rcx, r15 RCX => 0x7ffff7e17600 (_IO_file_jumps) ◂— 0
0x7ffff7c7ffbe <fwrite+158> sub rcx, rdx RCX => 0xc00 (0x7ffff7e17600 - 0x7ffff7e16a00)
0x7ffff7c7ffc1 <fwrite+161> cmp rax, rcx 0xd68 - 0xc00 EFLAGS => 0x202 [ cf pf af zf sf IF df of ac ]

可以看到跳转回来处(我们这里其实不会跳转,因为_mode已经是0)后续就开始进行vtable的安全检查,注意

1
0x7ffff7c7ff99 <fwrite+121>    mov    dword ptr [rbx + 0xc0], 0xffffffff     [0x555555559360] <= 0xffffffff

_mode设置为-1,我们验证一下

1
2
3
4
pwndbg> p _IO_list_all
$3 = (struct _IO_FILE_plus *) 0x5555555592a0
pwndbg> x 0x5555555592a0+0xc0
0x555555559360: 0xffffffff

看到_mode已经被设置为了-1
后续对vtable的检查和fread一样,先判断是否在指定范围内

1
2
3
4
5
6
7
8
0x7ffff7c7ffa3 <fwrite+131>    mov    r15, qword ptr [rbx + 0xd8]            R15, [0x555555559378] => 0x7ffff7e17600 (_IO_file_jumps) ◂— 0
0x7ffff7c7ffaa <fwrite+138> lea rdx, [rip + 0x196a4f] RDX => 0x7ffff7e16a00 (_IO_helper_jumps) ◂— 0
0x7ffff7c7ffb1 <fwrite+145> lea rax, [rip + 0x1977b0] RAX => 0x7ffff7e17768 ◂— 0
0x7ffff7c7ffb8 <fwrite+152> sub rax, rdx RAX => 0xd68 (0x7ffff7e17768 - 0x7ffff7e16a00)
0x7ffff7c7ffbb <fwrite+155> mov rcx, r15 RCX => 0x7ffff7e17600 (_IO_file_jumps) ◂— 0
0x7ffff7c7ffbe <fwrite+158> sub rcx, rdx RCX => 0xc00 (0x7ffff7e17600 - 0x7ffff7e16a00)
0x7ffff7c7ffc1 <fwrite+161> cmp rax, rcx 0xd68 - 0xc00 EFLAGS => 0x202 [ cf pf af zf sf IF df of ac ]
0x7ffff7c7ffc4 <ferite+164>: jbe 0x7ffff7c80080 <__GI__IO_fwrite+352>

不在指定范围内则调用_IO_vtable_check函数来进一步检查

1
0x00007ffff7c80080 <+352>:   call   0x7ffff7c89ef0 <_IO_vtable_check>

这里不再赘述,后续调用vtable->_IO_file_xsputn函数

1
2
3
4
0x7ffff7c7ffca <fwrite+170>    mov    rdx, r12     RDX => 0x20
0x7ffff7c7ffcd <fwrite+173> mov rsi, r14 RSI => 0x555555559480 ◂— 0
0x7ffff7c7ffd0 <fwrite+176> mov rdi, rbx RDI => 0x5555555592a0 ◂— 0xfbad2488
0x7ffff7c7ffd3 <fwrite+179> call qword ptr [r15 + 0x38] <_IO_file_xsputn>

可以看到这里r15正指向vtable

1
r15            0x7ffff7e17600

[r15 + 0x38]处正是vtable中偏移0x38的_IO_file_xsputn
我们还是贴上汇编

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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
pwndbg> disass
Dump of assembler code for function _IO_new_file_xsputn:
=> 0x00007ffff7c8b600 <+0>: endbr64
0x00007ffff7c8b604 <+4>: push r15
0x00007ffff7c8b606 <+6>: push r14
0x00007ffff7c8b608 <+8>: push r13
0x00007ffff7c8b60a <+10>: push r12
0x00007ffff7c8b60c <+12>: xor r12d,r12d
0x00007ffff7c8b60f <+15>: push rbp
0x00007ffff7c8b610 <+16>: push rbx
0x00007ffff7c8b611 <+17>: sub rsp,0x18
0x00007ffff7c8b615 <+21>: test rdx,rdx
0x00007ffff7c8b618 <+24>: je 0x7ffff7c8b66e <_IO_new_file_xsputn+110>
0x00007ffff7c8b61a <+26>: mov rbp,rdi
0x00007ffff7c8b61d <+29>: mov r13,rsi
0x00007ffff7c8b620 <+32>: mov rdi,QWORD PTR [rdi+0x28]
0x00007ffff7c8b624 <+36>: mov rbx,rdx
0x00007ffff7c8b627 <+39>: mov eax,DWORD PTR [rbp+0x0]
0x00007ffff7c8b62a <+42>: and eax,0xa00
0x00007ffff7c8b62f <+47>: cmp eax,0xa00
0x00007ffff7c8b634 <+52>: je 0x7ffff7c8b680 <_IO_new_file_xsputn+128>
0x00007ffff7c8b636 <+54>: mov r12,QWORD PTR [rbp+0x30]
0x00007ffff7c8b63a <+58>: cmp r12,rdi
0x00007ffff7c8b63d <+61>: jbe 0x7ffff7c8b6a0 <_IO_new_file_xsputn+160>
0x00007ffff7c8b63f <+63>: sub r12,rdi
0x00007ffff7c8b642 <+66>: xor r14d,r14d
0x00007ffff7c8b645 <+69>: test r12,r12
0x00007ffff7c8b648 <+72>: je 0x7ffff7c8b6a0 <_IO_new_file_xsputn+160>
0x00007ffff7c8b64a <+74>: cmp r12,rbx
0x00007ffff7c8b64d <+77>: mov rsi,r13
0x00007ffff7c8b650 <+80>: mov r15,rbx
0x00007ffff7c8b653 <+83>: cmova r12,rbx
0x00007ffff7c8b657 <+87>: mov rdx,r12
0x00007ffff7c8b65a <+90>: add r13,r12
0x00007ffff7c8b65d <+93>: sub r15,r12
0x00007ffff7c8b660 <+96>: call 0x7ffff7c283e0 <*ABS*+0xa97d0@plt>
0x00007ffff7c8b665 <+101>: mov QWORD PTR [rbp+0x28],rax
0x00007ffff7c8b669 <+105>: add r14,r15
0x00007ffff7c8b66c <+108>: jne 0x7ffff7c8b6a3 <_IO_new_file_xsputn+163>
0x00007ffff7c8b66e <+110>: add rsp,0x18
0x00007ffff7c8b672 <+114>: mov rax,r12
0x00007ffff7c8b675 <+117>: pop rbx
0x00007ffff7c8b676 <+118>: pop rbp
0x00007ffff7c8b677 <+119>: pop r12
0x00007ffff7c8b679 <+121>: pop r13
0x00007ffff7c8b67b <+123>: pop r14
0x00007ffff7c8b67d <+125>: pop r15
0x00007ffff7c8b67f <+127>: ret
0x00007ffff7c8b680 <+128>: mov r12,QWORD PTR [rbp+0x40]
0x00007ffff7c8b684 <+132>: lea rax,[rsi+rdx*1]
0x00007ffff7c8b688 <+136>: sub r12,rdi
0x00007ffff7c8b68b <+139>: cmp rdx,r12
0x00007ffff7c8b68e <+142>: jbe 0x7ffff7c8b73d <_IO_new_file_xsputn+317>
0x00007ffff7c8b694 <+148>: xor r14d,r14d
0x00007ffff7c8b697 <+151>: test r12,r12
0x00007ffff7c8b69a <+154>: jne 0x7ffff7c8b64a <_IO_new_file_xsputn+74>
0x00007ffff7c8b69c <+156>: nop DWORD PTR [rax+0x0]
0x00007ffff7c8b6a0 <+160>: mov r15,rbx
0x00007ffff7c8b6a3 <+163>: mov rax,QWORD PTR [rbp+0xd8]
0x00007ffff7c8b6aa <+170>: lea r14,[rip+0x18b34f] # 0x7ffff7e16a00 <_IO_helper_jumps>
0x00007ffff7c8b6b1 <+177>: lea r12,[rip+0x18c0b0] # 0x7ffff7e17768
0x00007ffff7c8b6b8 <+184>: sub r12,r14
0x00007ffff7c8b6bb <+187>: mov rdx,rax
0x00007ffff7c8b6be <+190>: sub rdx,r14
0x00007ffff7c8b6c1 <+193>: cmp r12,rdx
0x00007ffff7c8b6c4 <+196>: jbe 0x7ffff7c8b880 <_IO_new_file_xsputn+640>
0x00007ffff7c8b6ca <+202>: mov esi,0xffffffff
0x00007ffff7c8b6cf <+207>: mov rdi,rbp
0x00007ffff7c8b6d2 <+210>: call QWORD PTR [rax+0x18]
0x00007ffff7c8b6d5 <+213>: cmp eax,0xffffffff
0x00007ffff7c8b6d8 <+216>: je 0x7ffff7c8b830 <_IO_new_file_xsputn+560>
0x00007ffff7c8b6de <+222>: mov rcx,QWORD PTR [rbp+0x40]
0x00007ffff7c8b6e2 <+226>: sub rcx,QWORD PTR [rbp+0x38]
0x00007ffff7c8b6e6 <+230>: mov r8,r15
0x00007ffff7c8b6e9 <+233>: cmp rcx,0x7f
0x00007ffff7c8b6ed <+237>: jbe 0x7ffff7c8b6fa <_IO_new_file_xsputn+250>
0x00007ffff7c8b6ef <+239>: mov rax,r15
0x00007ffff7c8b6f2 <+242>: xor edx,edx
0x00007ffff7c8b6f4 <+244>: div rcx
0x00007ffff7c8b6f7 <+247>: sub r8,rdx
0x00007ffff7c8b6fa <+250>: test r8,r8
0x00007ffff7c8b6fd <+253>: jne 0x7ffff7c8b750 <_IO_new_file_xsputn+336>
0x00007ffff7c8b6ff <+255>: mov r12,rbx
0x00007ffff7c8b702 <+258>: test r15,r15
0x00007ffff7c8b705 <+261>: je 0x7ffff7c8b66e <_IO_new_file_xsputn+110>
0x00007ffff7c8b70b <+267>: lea rsi,[r13+r8*1+0x0]
0x00007ffff7c8b710 <+272>: mov rdx,r15
0x00007ffff7c8b713 <+275>: mov rdi,rbp
0x00007ffff7c8b716 <+278>: call 0x7ffff7c8ddc0 <__GI__IO_default_xsputn>
0x00007ffff7c8b71b <+283>: sub rax,r15
0x00007ffff7c8b71e <+286>: add r12,rax
0x00007ffff7c8b721 <+289>: jmp 0x7ffff7c8b66e <_IO_new_file_xsputn+110>
0x00007ffff7c8b726 <+294>: cs nop WORD PTR [rax+rax*1+0x0]
0x00007ffff7c8b730 <+304>: sub rax,0x1
0x00007ffff7c8b734 <+308>: cmp BYTE PTR [rax],0xa
0x00007ffff7c8b737 <+311>: je 0x7ffff7c8b850 <_IO_new_file_xsputn+592>
0x00007ffff7c8b73d <+317>: cmp r13,rax
0x00007ffff7c8b740 <+320>: jb 0x7ffff7c8b730 <_IO_new_file_xsputn+304>
0x00007ffff7c8b742 <+322>: xor r14d,r14d
0x00007ffff7c8b745 <+325>: jmp 0x7ffff7c8b64a <_IO_new_file_xsputn+74>
0x00007ffff7c8b74a <+330>: nop WORD PTR [rax+rax*1+0x0]
0x00007ffff7c8b750 <+336>: mov rax,QWORD PTR [rbp+0xd8]
0x00007ffff7c8b757 <+343>: test DWORD PTR [rbp+0x0],0x1000
0x00007ffff7c8b75e <+350>: jne 0x7ffff7c8b870 <_IO_new_file_xsputn+624>
0x00007ffff7c8b764 <+356>: mov rdx,QWORD PTR [rbp+0x10]
0x00007ffff7c8b768 <+360>: mov rsi,QWORD PTR [rbp+0x20]
0x00007ffff7c8b76c <+364>: cmp rdx,rsi
0x00007ffff7c8b76f <+367>: je 0x7ffff7c8b7b1 <_IO_new_file_xsputn+433>
0x00007ffff7c8b771 <+369>: mov rcx,rax
0x00007ffff7c8b774 <+372>: sub rcx,r14
0x00007ffff7c8b777 <+375>: cmp r12,rcx
0x00007ffff7c8b77a <+378>: jbe 0x7ffff7c8b8fd <_IO_new_file_xsputn+765>
0x00007ffff7c8b780 <+384>: sub rsi,rdx
0x00007ffff7c8b783 <+387>: mov QWORD PTR [rsp],r8
0x00007ffff7c8b787 <+391>: mov edx,0x1
0x00007ffff7c8b78c <+396>: mov rdi,rbp
0x00007ffff7c8b78f <+399>: call QWORD PTR [rax+0x80]
0x00007ffff7c8b795 <+405>: cmp rax,0xffffffffffffffff
0x00007ffff7c8b799 <+409>: je 0x7ffff7c8b8aa <_IO_new_file_xsputn+682>
0x00007ffff7c8b79f <+415>: mov QWORD PTR [rbp+0x90],rax
0x00007ffff7c8b7a6 <+422>: mov r8,QWORD PTR [rsp]
0x00007ffff7c8b7aa <+426>: mov rax,QWORD PTR [rbp+0xd8]
0x00007ffff7c8b7b1 <+433>: mov rdx,rax
0x00007ffff7c8b7b4 <+436>: sub rdx,r14
0x00007ffff7c8b7b7 <+439>: cmp r12,rdx
0x00007ffff7c8b7ba <+442>: jbe 0x7ffff7c8b8e1 <_IO_new_file_xsputn+737>
0x00007ffff7c8b7c0 <+448>: mov QWORD PTR [rsp],r8
0x00007ffff7c8b7c4 <+452>: mov rdx,r8
0x00007ffff7c8b7c7 <+455>: mov rsi,r13
0x00007ffff7c8b7ca <+458>: mov rdi,rbp
0x00007ffff7c8b7cd <+461>: call QWORD PTR [rax+0x78]
0x00007ffff7c8b7d0 <+464>: mov r8,QWORD PTR [rsp]
0x00007ffff7c8b7d4 <+468>: mov r14,rax
0x00007ffff7c8b7d7 <+471>: movzx eax,WORD PTR [rbp+0x80]
0x00007ffff7c8b7de <+478>: test r14,r14
0x00007ffff7c8b7e1 <+481>: je 0x7ffff7c8b7ec <_IO_new_file_xsputn+492>
0x00007ffff7c8b7e3 <+483>: test ax,ax
0x00007ffff7c8b7e6 <+486>: jne 0x7ffff7c8b8c0 <_IO_new_file_xsputn+704>
0x00007ffff7c8b7ec <+492>: mov rax,QWORD PTR [rbp+0x38]
0x00007ffff7c8b7f0 <+496>: mov edx,DWORD PTR [rbp+0xc0]
0x00007ffff7c8b7f6 <+502>: movq xmm0,rax
0x00007ffff7c8b7fb <+507>: mov QWORD PTR [rbp+0x28],rax
0x00007ffff7c8b7ff <+511>: punpcklqdq xmm0,xmm0
0x00007ffff7c8b803 <+515>: movups XMMWORD PTR [rbp+0x8],xmm0
0x00007ffff7c8b807 <+519>: movups XMMWORD PTR [rbp+0x18],xmm0
0x00007ffff7c8b80b <+523>: test edx,edx
0x00007ffff7c8b80d <+525>: jle 0x7ffff7c8b898 <_IO_new_file_xsputn+664>
0x00007ffff7c8b813 <+531>: mov rax,QWORD PTR [rbp+0x40]
0x00007ffff7c8b817 <+535>: mov QWORD PTR [rbp+0x30],rax
0x00007ffff7c8b81b <+539>: cmp r14,r8
0x00007ffff7c8b81e <+542>: jb 0x7ffff7c8b8ad <_IO_new_file_xsputn+685>
0x00007ffff7c8b824 <+548>: sub r15,r14
0x00007ffff7c8b827 <+551>: jmp 0x7ffff7c8b6ff <_IO_new_file_xsputn+255>
0x00007ffff7c8b82c <+556>: nop DWORD PTR [rax+0x0]
0x00007ffff7c8b830 <+560>: sub rbx,r15
0x00007ffff7c8b833 <+563>: mov rax,0xffffffffffffffff
0x00007ffff7c8b83a <+570>: test r15,r15
0x00007ffff7c8b83d <+573>: mov r12,rbx
0x00007ffff7c8b840 <+576>: cmove r12,rax
0x00007ffff7c8b844 <+580>: jmp 0x7ffff7c8b66e <_IO_new_file_xsputn+110>
0x00007ffff7c8b849 <+585>: nop DWORD PTR [rax+0x0]
0x00007ffff7c8b850 <+592>: sub rax,r13
0x00007ffff7c8b853 <+595>: add rax,0x1
0x00007ffff7c8b857 <+599>: mov r12,rax
0x00007ffff7c8b85a <+602>: jne 0x7ffff7c8b921 <_IO_new_file_xsputn+801>
0x00007ffff7c8b860 <+608>: mov r15,rbx
0x00007ffff7c8b863 <+611>: mov r14d,0x1
0x00007ffff7c8b869 <+617>: jmp 0x7ffff7c8b669 <_IO_new_file_xsputn+105>
0x00007ffff7c8b86e <+622>: xchg ax,ax
0x00007ffff7c8b870 <+624>: mov QWORD PTR [rbp+0x90],0xffffffffffffffff
0x00007ffff7c8b87b <+635>: jmp 0x7ffff7c8b7b1 <_IO_new_file_xsputn+433>
0x00007ffff7c8b880 <+640>: mov QWORD PTR [rsp],rax
0x00007ffff7c8b884 <+644>: call 0x7ffff7c89ef0 <_IO_vtable_check>
0x00007ffff7c8b889 <+649>: mov rax,QWORD PTR [rsp]
0x00007ffff7c8b88d <+653>: jmp 0x7ffff7c8b6ca <_IO_new_file_xsputn+202>
0x00007ffff7c8b892 <+658>: nop WORD PTR [rax+rax*1+0x0]
0x00007ffff7c8b898 <+664>: test DWORD PTR [rbp+0x0],0x202
0x00007ffff7c8b89f <+671>: jne 0x7ffff7c8b817 <_IO_new_file_xsputn+535>
0x00007ffff7c8b8a5 <+677>: jmp 0x7ffff7c8b813 <_IO_new_file_xsputn+531>
0x00007ffff7c8b8aa <+682>: xor r14d,r14d
0x00007ffff7c8b8ad <+685>: sub rbx,r15
0x00007ffff7c8b8b0 <+688>: mov r12,rbx
0x00007ffff7c8b8b3 <+691>: add r12,r14
0x00007ffff7c8b8b6 <+694>: jmp 0x7ffff7c8b66e <_IO_new_file_xsputn+110>
0x00007ffff7c8b8bb <+699>: nop DWORD PTR [rax+rax*1+0x0]
0x00007ffff7c8b8c0 <+704>: lea edi,[rax-0x1]
0x00007ffff7c8b8c3 <+707>: mov edx,r14d
0x00007ffff7c8b8c6 <+710>: mov rsi,r13
0x00007ffff7c8b8c9 <+713>: call 0x7ffff7c8e8b0 <__GI__IO_adjust_column>
0x00007ffff7c8b8ce <+718>: mov r8,QWORD PTR [rsp]
0x00007ffff7c8b8d2 <+722>: add eax,0x1
0x00007ffff7c8b8d5 <+725>: mov WORD PTR [rbp+0x80],ax
0x00007ffff7c8b8dc <+732>: jmp 0x7ffff7c8b7ec <_IO_new_file_xsputn+492>
0x00007ffff7c8b8e1 <+737>: mov QWORD PTR [rsp+0x8],rax
0x00007ffff7c8b8e6 <+742>: mov QWORD PTR [rsp],r8
0x00007ffff7c8b8ea <+746>: call 0x7ffff7c89ef0 <_IO_vtable_check>
0x00007ffff7c8b8ef <+751>: mov rax,QWORD PTR [rsp+0x8]
0x00007ffff7c8b8f4 <+756>: mov r8,QWORD PTR [rsp]
0x00007ffff7c8b8f8 <+760>: jmp 0x7ffff7c8b7c0 <_IO_new_file_xsputn+448>
0x00007ffff7c8b8fd <+765>: mov QWORD PTR [rsp+0x8],rax
0x00007ffff7c8b902 <+770>: mov QWORD PTR [rsp],r8
0x00007ffff7c8b906 <+774>: call 0x7ffff7c89ef0 <_IO_vtable_check>
0x00007ffff7c8b90b <+779>: mov rsi,QWORD PTR [rbp+0x20]
0x00007ffff7c8b90f <+783>: mov rdx,QWORD PTR [rbp+0x10]
0x00007ffff7c8b913 <+787>: mov rax,QWORD PTR [rsp+0x8]
0x00007ffff7c8b918 <+792>: mov r8,QWORD PTR [rsp]
0x00007ffff7c8b91c <+796>: jmp 0x7ffff7c8b780 <_IO_new_file_xsputn+384>
0x00007ffff7c8b921 <+801>: mov r14d,0x1
0x00007ffff7c8b927 <+807>: jmp 0x7ffff7c8b64a <_IO_new_file_xsputn+74>
End of assembler dump.

回到调试,还是先判断了写入字节数是否为0,为0直接返回

1
2
3
4
5
6
7
8
9
10
0x7ffff7c8b604 <_IO_file_xsputn+4>     push   r15
0x7ffff7c8b606 <_IO_file_xsputn+6> push r14
0x7ffff7c8b608 <_IO_file_xsputn+8> push r13
0x7ffff7c8b60a <_IO_file_xsputn+10> push r12
0x7ffff7c8b60c <_IO_file_xsputn+12> xor r12d, r12d R12D => 0
0x7ffff7c8b60f <_IO_file_xsputn+15> push rbp
0x7ffff7c8b610 <_IO_file_xsputn+16> push rbx
0x7ffff7c8b611 <_IO_file_xsputn+17> sub rsp, 0x18 RSP => 0x7fffffffdc40 (0x7fffffffdc58 - 0x18)
0x7ffff7c8b615 <_IO_file_xsputn+21> test rdx, rdx 0x20 & 0x20 EFLAGS => 0x202 [ cf pf af zf sf IF df of ac ]
0x7ffff7c8b618 <_IO_file_xsputn+24> ✘ je _IO_file_xsputn+110 <_IO_file_xsputn+110>
1
2
3
4
5
6
7
8
9
0x00007ffff7c8b66e <+110>:   add    rsp,0x18
0x00007ffff7c8b672 <+114>: mov rax,r12
0x00007ffff7c8b675 <+117>: pop rbx
0x00007ffff7c8b676 <+118>: pop rbp
0x00007ffff7c8b677 <+119>: pop r12
0x00007ffff7c8b679 <+121>: pop r13
0x00007ffff7c8b67b <+123>: pop r14
0x00007ffff7c8b67d <+125>: pop r15
0x00007ffff7c8b67f <+127>: ret

继续往后

1
2
3
4
5
6
0x7ffff7c8b620 <_IO_file_xsputn+32>     mov    rdi, qword ptr [rdi + 0x28]     RDI, [0x5555555592c8] => 0
0x7ffff7c8b624 <_IO_file_xsputn+36> mov rbx, rdx RBX => 0x20
0x7ffff7c8b627 <_IO_file_xsputn+39> mov eax, dword ptr [rbp] EAX, [0x5555555592a0] => 0xfbad2488
0x7ffff7c8b62a <_IO_file_xsputn+42> and eax, 0xa00 EAX => 0 (0xfbad2488 & 0xa00)
0x7ffff7c8b62f <_IO_file_xsputn+47> cmp eax, 0xa00 0x0 - 0xa00 EFLAGS => 0x287 [ CF PF af zf SF IF df of ac ]
0x7ffff7c8b634 <_IO_file_xsputn+52> ✘ je _IO_file_xsputn+128 <_IO_file_xsputn+128>

[rdi+0x28]_IO_FILE_plus偏移0x28处的 _IO_write_ptr,将其保存,然后检查 _flags & 0xa00 == 0xa00,这是判断是否为 _IO_LINE_BUF_IO_UNBUFFERED(判断缓冲模式),若是,则跳到另一分支处理,先不管,往下走

1
2
3
0x7ffff7c8b636 <_IO_file_xsputn+54>     mov    r12, qword ptr [rbp + 0x30]     R12, [0x5555555592d0] => 0
0x7ffff7c8b63a <_IO_file_xsputn+58> cmp r12, rdi 0 - 0 EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c8b63d <_IO_file_xsputn+61> ✔ jbe _IO_file_xsputn+160 <_IO_file_xsputn+160>

这里[rbp+0x30]_IO_FILE_plus偏移0x30处的 _IO_write_end,如果_IO_write_ptr>= _IO_write_end,则写缓冲区不足,跳转

1
2
3
4
5
6
7
8
9
10
11
12
0x7ffff7c8b6a0 <_IO_file_xsputn+160>    mov    r15, rbx                        R15 => 0x20
0x7ffff7c8b6a3 <_IO_file_xsputn+163> mov rax, qword ptr [rbp + 0xd8] RAX, [0x555555559378] => 0x7ffff7e17600 (_IO_file_jumps) ◂— 0
0x7ffff7c8b6aa <_IO_file_xsputn+170> lea r14, [rip + 0x18b34f] R14 => 0x7ffff7e16a00 (_IO_helper_jumps) ◂— 0
0x7ffff7c8b6b1 <_IO_file_xsputn+177> lea r12, [rip + 0x18c0b0] R12 => 0x7ffff7e17768 ◂— 0
0x7ffff7c8b6b8 <_IO_file_xsputn+184> sub r12, r14 R12 => 0xd68 (0x7ffff7e17768 - 0x7ffff7e16a00)
0x7ffff7c8b6bb <_IO_file_xsputn+187> mov rdx, rax RDX => 0x7ffff7e17600 (_IO_file_jumps) ◂— 0
0x7ffff7c8b6be <_IO_file_xsputn+190> sub rdx, r14 RDX => 0xc00 (0x7ffff7e17600 - 0x7ffff7e16a00)
0x7ffff7c8b6c1 <_IO_file_xsputn+193> cmp r12, rdx 0xd68 - 0xc00 EFLAGS => 0x202 [ cf pf af zf sf IF df of ac ]
0x7ffff7c8b6c4 <_IO_file_xsputn+196> ✘ jbe _IO_file_xsputn+640 <_IO_file_xsputn+640>

0x00007ffff7c8b880 <+640>: mov QWORD PTR [rsp],rax
0x00007ffff7c8b884 <+644>: call 0x7ffff7c89ef0 <_IO_vtable_check>

这里是vtable的安全检查,不再赘述,往下继续,调用vtable->_IO_file_overflow函数(vtable偏移0x18处)

1
2
3
0x7ffff7c8b6ca <_IO_file_xsputn+202>    mov    esi, 0xffffffff     ESI => 0xffffffff
0x7ffff7c8b6cf <_IO_file_xsputn+207> mov rdi, rbp RDI => 0x5555555592a0 ◂— 0xfbad2488
0x7ffff7c8b6d2 <_IO_file_xsputn+210> call qword ptr [rax + 0x18] <_IO_file_overflow>

先贴上其汇编

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
pwndbg> disass
Dump of assembler code for function _IO_new_file_overflow:
=> 0x00007ffff7c8cdc0 <+0>: endbr64
0x00007ffff7c8cdc4 <+4>: push r12
0x00007ffff7c8cdc6 <+6>: push rbp
0x00007ffff7c8cdc7 <+7>: mov rbp,rdi
0x00007ffff7c8cdca <+10>: push rbx
0x00007ffff7c8cdcb <+11>: mov eax,DWORD PTR [rdi]
0x00007ffff7c8cdcd <+13>: test al,0x8
0x00007ffff7c8cdcf <+15>: jne 0x7ffff7c8cfc0 <_IO_new_file_overflow+512>
0x00007ffff7c8cdd5 <+21>: mov ebx,esi
0x00007ffff7c8cdd7 <+23>: mov rsi,QWORD PTR [rdi+0x20]
0x00007ffff7c8cddb <+27>: test ah,0x8
0x00007ffff7c8cdde <+30>: je 0x7ffff7c8ce30 <_IO_new_file_overflow+112>
0x00007ffff7c8cde0 <+32>: test rsi,rsi
0x00007ffff7c8cde3 <+35>: je 0x7ffff7c8cf58 <_IO_new_file_overflow+408>
0x00007ffff7c8cde9 <+41>: mov rdx,QWORD PTR [rdi+0x28]
0x00007ffff7c8cded <+45>: cmp ebx,0xffffffff
0x00007ffff7c8cdf0 <+48>: je 0x7ffff7c8ce9c <_IO_new_file_overflow+220>
0x00007ffff7c8cdf6 <+54>: cmp QWORD PTR [rbp+0x40],rdx
0x00007ffff7c8cdfa <+58>: je 0x7ffff7c8cee0 <_IO_new_file_overflow+288>
0x00007ffff7c8ce00 <+64>: lea rax,[rdx+0x1]
0x00007ffff7c8ce04 <+68>: mov QWORD PTR [rbp+0x28],rax
0x00007ffff7c8ce08 <+72>: mov BYTE PTR [rdx],bl
0x00007ffff7c8ce0a <+74>: mov eax,DWORD PTR [rbp+0x0]
0x00007ffff7c8ce0d <+77>: test al,0x2
0x00007ffff7c8ce0f <+79>: jne 0x7ffff7c8ceb0 <_IO_new_file_overflow+240>
0x00007ffff7c8ce15 <+85>: test ah,0x2
0x00007ffff7c8ce18 <+88>: je 0x7ffff7c8ce23 <_IO_new_file_overflow+99>
0x00007ffff7c8ce1a <+90>: cmp ebx,0xa
0x00007ffff7c8ce1d <+93>: je 0x7ffff7c8ceb0 <_IO_new_file_overflow+240>
0x00007ffff7c8ce23 <+99>: movzx eax,bl
0x00007ffff7c8ce26 <+102>: pop rbx
0x00007ffff7c8ce27 <+103>: pop rbp
0x00007ffff7c8ce28 <+104>: pop r12
0x00007ffff7c8ce2a <+106>: ret
0x00007ffff7c8ce2b <+107>: nop DWORD PTR [rax+rax*1+0x0]
0x00007ffff7c8ce30 <+112>: test rsi,rsi
0x00007ffff7c8ce33 <+115>: je 0x7ffff7c8cf58 <_IO_new_file_overflow+408>
0x00007ffff7c8ce39 <+121>: mov rdx,QWORD PTR [rdi+0x8]
0x00007ffff7c8ce3d <+125>: test ah,0x1
0x00007ffff7c8ce40 <+128>: jne 0x7ffff7c8cf80 <_IO_new_file_overflow+448>
0x00007ffff7c8ce46 <+134>: mov rsi,QWORD PTR [rbp+0x40]
0x00007ffff7c8ce4a <+138>: cmp rsi,rdx
0x00007ffff7c8ce4d <+141>: je 0x7ffff7c8cf20 <_IO_new_file_overflow+352>
0x00007ffff7c8ce53 <+147>: mov rcx,QWORD PTR [rbp+0x10]
0x00007ffff7c8ce57 <+151>: mov QWORD PTR [rbp+0x8],rcx
0x00007ffff7c8ce5b <+155>: mov QWORD PTR [rbp+0x18],rcx
0x00007ffff7c8ce5f <+159>: mov ecx,eax
0x00007ffff7c8ce61 <+161>: or ch,0x8
0x00007ffff7c8ce64 <+164>: mov QWORD PTR [rbp+0x30],rsi
0x00007ffff7c8ce68 <+168>: mov rsi,rdx
0x00007ffff7c8ce6b <+171>: mov DWORD PTR [rbp+0x0],ecx
0x00007ffff7c8ce6e <+174>: mov ecx,DWORD PTR [rbp+0xc0]
0x00007ffff7c8ce74 <+180>: mov QWORD PTR [rbp+0x28],rdx
0x00007ffff7c8ce78 <+184>: mov QWORD PTR [rbp+0x20],rdx
0x00007ffff7c8ce7c <+188>: test ecx,ecx
0x00007ffff7c8ce7e <+190>: jg 0x7ffff7c8cded <_IO_new_file_overflow+45>
0x00007ffff7c8ce84 <+196>: test eax,0x202
0x00007ffff7c8ce89 <+201>: je 0x7ffff7c8cded <_IO_new_file_overflow+45>
0x00007ffff7c8ce8f <+207>: mov QWORD PTR [rbp+0x30],rdx
0x00007ffff7c8ce93 <+211>: cmp ebx,0xffffffff
0x00007ffff7c8ce96 <+214>: jne 0x7ffff7c8cdf6 <_IO_new_file_overflow+54>
0x00007ffff7c8ce9c <+220>: pop rbx
0x00007ffff7c8ce9d <+221>: mov rdi,rbp
0x00007ffff7c8cea0 <+224>: sub rdx,rsi
0x00007ffff7c8cea3 <+227>: pop rbp
0x00007ffff7c8cea4 <+228>: pop r12
0x00007ffff7c8cea6 <+230>: jmp 0x7ffff7c8c930 <_IO_new_do_write>
0x00007ffff7c8ceab <+235>: nop DWORD PTR [rax+rax*1+0x0]
0x00007ffff7c8ceb0 <+240>: mov rsi,QWORD PTR [rbp+0x20]
0x00007ffff7c8ceb4 <+244>: mov rdx,QWORD PTR [rbp+0x28]
0x00007ffff7c8ceb8 <+248>: mov rdi,rbp
0x00007ffff7c8cebb <+251>: sub rdx,rsi
0x00007ffff7c8cebe <+254>: call 0x7ffff7c8c930 <_IO_new_do_write>
0x00007ffff7c8cec3 <+259>: cmp eax,0xffffffff
0x00007ffff7c8cec6 <+262>: jne 0x7ffff7c8ce23 <_IO_new_file_overflow+99>
0x00007ffff7c8cecc <+268>: mov eax,0xffffffff
0x00007ffff7c8ced1 <+273>: jmp 0x7ffff7c8ce26 <_IO_new_file_overflow+102>
0x00007ffff7c8ced6 <+278>: cs nop WORD PTR [rax+rax*1+0x0]
0x00007ffff7c8cee0 <+288>: mov eax,DWORD PTR [rbp+0xc0]
0x00007ffff7c8cee6 <+294>: test eax,eax
0x00007ffff7c8cee8 <+296>: jle 0x7ffff7c8cf40 <_IO_new_file_overflow+384>
0x00007ffff7c8ceea <+298>: mov rax,QWORD PTR [rbp+0xa0]
0x00007ffff7c8cef1 <+305>: mov rdi,rbp
0x00007ffff7c8cef4 <+308>: mov rsi,QWORD PTR [rax+0x18]
0x00007ffff7c8cef8 <+312>: mov rdx,QWORD PTR [rax+0x20]
0x00007ffff7c8cefc <+316>: sub rdx,rsi
0x00007ffff7c8ceff <+319>: sar rdx,0x2
0x00007ffff7c8cf03 <+323>: call 0x7ffff7c861a0 <__GI__IO_wdo_write>
0x00007ffff7c8cf08 <+328>: cmp eax,0xffffffff
0x00007ffff7c8cf0b <+331>: je 0x7ffff7c8cecc <_IO_new_file_overflow+268>
0x00007ffff7c8cf0d <+333>: mov rdx,QWORD PTR [rbp+0x28]
0x00007ffff7c8cf11 <+337>: jmp 0x7ffff7c8ce00 <_IO_new_file_overflow+64>
0x00007ffff7c8cf16 <+342>: cs nop WORD PTR [rax+rax*1+0x0]
0x00007ffff7c8cf20 <+352>: mov rdx,QWORD PTR [rbp+0x38]
0x00007ffff7c8cf24 <+356>: movq xmm0,rdx
0x00007ffff7c8cf29 <+361>: mov rcx,rdx
0x00007ffff7c8cf2c <+364>: punpcklqdq xmm0,xmm0
0x00007ffff7c8cf30 <+368>: movups XMMWORD PTR [rbp+0x8],xmm0
0x00007ffff7c8cf34 <+372>: jmp 0x7ffff7c8ce57 <_IO_new_file_overflow+151>
0x00007ffff7c8cf39 <+377>: nop DWORD PTR [rax+0x0]
0x00007ffff7c8cf40 <+384>: sub rdx,rsi
0x00007ffff7c8cf43 <+387>: mov rdi,rbp
0x00007ffff7c8cf46 <+390>: call 0x7ffff7c8c930 <_IO_new_do_write>
0x00007ffff7c8cf4b <+395>: cmp eax,0xffffffff
0x00007ffff7c8cf4e <+398>: jne 0x7ffff7c8cf0d <_IO_new_file_overflow+333>
0x00007ffff7c8cf50 <+400>: jmp 0x7ffff7c8cecc <_IO_new_file_overflow+268>
0x00007ffff7c8cf55 <+405>: nop DWORD PTR [rax]
0x00007ffff7c8cf58 <+408>: mov rdi,rbp
0x00007ffff7c8cf5b <+411>: call 0x7ffff7c8dc90 <__GI__IO_doallocbuf>
0x00007ffff7c8cf60 <+416>: mov rdx,QWORD PTR [rbp+0x38]
0x00007ffff7c8cf64 <+420>: mov eax,DWORD PTR [rbp+0x0]
0x00007ffff7c8cf67 <+423>: movq xmm0,rdx
0x00007ffff7c8cf6c <+428>: mov QWORD PTR [rbp+0x18],rdx
0x00007ffff7c8cf70 <+432>: punpcklqdq xmm0,xmm0
0x00007ffff7c8cf74 <+436>: movups XMMWORD PTR [rbp+0x8],xmm0
0x00007ffff7c8cf78 <+440>: jmp 0x7ffff7c8ce3d <_IO_new_file_overflow+125>
0x00007ffff7c8cf7d <+445>: nop DWORD PTR [rax]
0x00007ffff7c8cf80 <+448>: mov r12,QWORD PTR [rbp+0x10]
0x00007ffff7c8cf84 <+452>: mov rdi,rbp
0x00007ffff7c8cf87 <+455>: sub r12,rdx
0x00007ffff7c8cf8a <+458>: call 0x7ffff7c8d7c0 <__GI__IO_free_backup_area>
0x00007ffff7c8cf8f <+463>: mov rdx,QWORD PTR [rbp+0x18]
0x00007ffff7c8cf93 <+467>: mov rsi,r12
0x00007ffff7c8cf96 <+470>: neg rsi
0x00007ffff7c8cf99 <+473>: mov rcx,rdx
0x00007ffff7c8cf9c <+476>: sub rcx,QWORD PTR [rbp+0x38]
0x00007ffff7c8cfa0 <+480>: mov rax,rcx
0x00007ffff7c8cfa3 <+483>: neg rax
0x00007ffff7c8cfa6 <+486>: cmp rcx,r12
0x00007ffff7c8cfa9 <+489>: cmova rax,rsi
0x00007ffff7c8cfad <+493>: add rdx,rax
0x00007ffff7c8cfb0 <+496>: mov eax,DWORD PTR [rbp+0x0]
0x00007ffff7c8cfb3 <+499>: jmp 0x7ffff7c8ce46 <_IO_new_file_overflow+134>
0x00007ffff7c8cfb8 <+504>: nop DWORD PTR [rax+rax*1+0x0]
0x00007ffff7c8cfc0 <+512>: or eax,0x20
0x00007ffff7c8cfc3 <+515>: mov DWORD PTR [rdi],eax
0x00007ffff7c8cfc5 <+517>: mov rax,QWORD PTR [rip+0x18ce44] # 0x7ffff7e19e10
0x00007ffff7c8cfcc <+524>: mov DWORD PTR fs:[rax],0x9
0x00007ffff7c8cfd3 <+531>: mov eax,0xffffffff
0x00007ffff7c8cfd8 <+536>: jmp 0x7ffff7c8ce26 <_IO_new_file_overflow+102>
End of assembler dump.

回到调试

1
2
3
4
5
6
7
8
0x7ffff7c8cdc0 <_IO_file_overflow>        endbr64
0x7ffff7c8cdc4 <_IO_file_overflow+4> push r12
0x7ffff7c8cdc6 <_IO_file_overflow+6> push rbp
0x7ffff7c8cdc7 <_IO_file_overflow+7> mov rbp, rdi RBP => 0x5555555592a0 ◂— 0xfbad2488
0x7ffff7c8cdca <_IO_file_overflow+10> push rbx
0x7ffff7c8cdcb <_IO_file_overflow+11> mov eax, dword ptr [rdi] EAX, [0x5555555592a0] => 0xfbad2488
0x7ffff7c8cdcd <_IO_file_overflow+13> test al, 8 0x88 & 0x8 EFLAGS => 0x202 [ cf pf af zf sf IF df of ac ]
0x7ffff7c8cdcf <_IO_file_overflow+15> ✘ jne _IO_file_overflow+512 <_IO_file_overflow+512>

这里先保存栈帧,然后检查_flags,判断文件是否可写,(笔者这里demo写的时候忘记改文件打开模式了,所以不可写,直接返回了,改一下后重新快进到这一步)不可写则返回(<_IO_file_overflow+512>后续执行就返回了),可写则继续

1
2
3
4
5
6
7
8
9
10
0x7ffff7c8cdd5 <_IO_file_overflow+21>     mov    ebx, esi                        EBX => 0xffffffff
0x7ffff7c8cdd7 <_IO_file_overflow+23> mov rsi, qword ptr [rdi + 0x20] RSI, [0x5555555592c0] => 0
0x7ffff7c8cddb <_IO_file_overflow+27> test ah, 8 0x24 & 0x8 EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c8cdde <_IO_file_overflow+30> ✔ je _IO_file_overflow+112 <_IO_file_overflow+112>

0x7ffff7c8ce30 <_IO_file_overflow+112> test rsi, rsi 0 & 0 EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c8ce33 <_IO_file_overflow+115> ✔ je _IO_file_overflow+408 <_IO_file_overflow+408>

0x7ffff7c8cf58 <_IO_file_overflow+408> mov rdi, rbp RDI => 0x5555555592a0 ◂— 0xfbad2484
0x7ffff7c8cf5b <_IO_file_overflow+411> call _IO_doallocbuf <_IO_doallocbuf>

这里先判断_flags状态,还未准备好写入,然后判断[rdi + 0x20]_IO_write_base是否为空,为空则调用_IO_doallocbuf来分配缓冲区
这里_IO_doallocbuf的流程在fread就分析过了,不再赘述,直接看返回后结构体的状态

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
pwndbg> p *_IO_list_all
$1 = {
file = {
_flags = -72539004,
_IO_read_ptr = 0x0,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_IO_write_end = 0x0,
_IO_buf_base = 0x5555555594b0 "",
_IO_buf_end = 0x55555555a4b0 "",
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7e1b6a0 <_IO_2_1_stderr_>,
_fileno = 3,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x555555559380,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x555555559390,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7e17600 <_IO_file_jumps>
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x555555559000
Size: 0x290 (with flag bits: 0x291)

Allocated chunk | PREV_INUSE
Addr: 0x555555559290
Size: 0x1e0 (with flag bits: 0x1e1)

Allocated chunk | PREV_INUSE
Addr: 0x555555559470
Size: 0x30 (with flag bits: 0x31)

Allocated chunk | PREV_INUSE
Addr: 0x5555555594a0
Size: 0x1010 (with flag bits: 0x1011)

Top chunk | PREV_INUSE
Addr: 0x55555555a4b0
Size: 0x1fb50 (with flag bits: 0x1fb51)

分配好了大小0x1000的chunk作为缓冲区,并设置好了_IO_buf_base_IO_buf_end指针
然后设置了read三元组(_IO_read_base,_IO_read_ptr,_IO_read_end)为_IO_buf_base

1
2
3
4
5
6
7
0x7ffff7c8cf60 <_IO_file_overflow+416>    mov    rdx, qword ptr [rbp + 0x38]     RDX, [0x5555555592d8] => 0x5555555594b0 ◂— 0
0x7ffff7c8cf64 <_IO_file_overflow+420> mov eax, dword ptr [rbp] EAX, [0x5555555592a0] => 0xfbad2484
0x7ffff7c8cf67 <_IO_file_overflow+423> movq xmm0, rdx XMM0 => 0x5555555594b0 ◂— 0
0x7ffff7c8cf6c <_IO_file_overflow+428> mov qword ptr [rbp + 0x18], rdx [0x5555555592b8] <= 0x5555555594b0 ◂— 0
0x7ffff7c8cf70 <_IO_file_overflow+432> punpcklqdq xmm0, xmm0
0x7ffff7c8cf74 <_IO_file_overflow+436> movups xmmword ptr [rbp + 8], xmm0
0x7ffff7c8cf78 <_IO_file_overflow+440> jmp _IO_file_overflow+125 <_IO_file_overflow+125>
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
pwndbg> p *_IO_list_all
$2 = {
file = {
_flags = -72539004,
_IO_read_ptr = 0x5555555594b0 "",
_IO_read_end = 0x5555555594b0 "",
_IO_read_base = 0x5555555594b0 "",
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_IO_write_end = 0x0,
_IO_buf_base = 0x5555555594b0 "",
_IO_buf_end = 0x55555555a4b0 "",
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7e1b6a0 <_IO_2_1_stderr_>,
_fileno = 3,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x555555559380,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x555555559390,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7e17600 <_IO_file_jumps>
}

可以看到都设置为了_IO_buf_base
后续再判断_flags

1
2
0x7ffff7c8ce3d <_IO_file_overflow+125>    test   ah, 1                           0x24 & 0x1     EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c8ce40 <_IO_file_overflow+128> ✘ jne _IO_file_overflow+448 <_IO_file_overflow+448>

如果是用户提供的缓冲区则跳转到_IO_file_overflow+448

1
2
3
4
0x00007ffff7c8cf80 <+448>:   mov    r12,QWORD PTR [rbp+0x10]
0x00007ffff7c8cf84 <+452>: mov rdi,rbp
0x00007ffff7c8cf87 <+455>: sub r12,rdx
0x00007ffff7c8cf8a <+458>: call 0x7ffff7c8d7c0 <__GI__IO_free_backup_area>

这里是调用函数清理backup区域。显然这里的缓冲区并不是用户提供的,而是malloc分配的,所以不会跳转
后续则是

1
2
3
0x7ffff7c8ce46 <_IO_file_overflow+134>    mov    rsi, qword ptr [rbp + 0x40]     RSI, [0x5555555592e0] => 0x55555555a4b0 ◂— 0
0x7ffff7c8ce4a <_IO_file_overflow+138> cmp rsi, rdx 0x55555555a4b0 - 0x5555555594b0 EFLAGS => 0x206 [ cf PF af zf sf IF df of ac ]
0x7ffff7c8ce4d <_IO_file_overflow+141> ✘ je _IO_file_overflow+352 <_IO_file_overflow+352>

验证_IO_buf_end是否等于_IO_buf_base,确保缓冲区的分配
接着往下走

1
2
3
4
5
6
7
0x7ffff7c8ce53 <_IO_file_overflow+147>    mov    rcx, qword ptr [rbp + 0x10]     RCX, [0x5555555592b0] => 0x5555555594b0 ◂— 0
0x7ffff7c8ce57 <_IO_file_overflow+151> mov qword ptr [rbp + 8], rcx [0x5555555592a8] <= 0x5555555594b0 ◂— 0
0x7ffff7c8ce5b <_IO_file_overflow+155> mov qword ptr [rbp + 0x18], rcx [0x5555555592b8] <= 0x5555555594b0 ◂— 0
0x7ffff7c8ce5f <_IO_file_overflow+159> mov ecx, eax ECX => 0xfbad2484
0x7ffff7c8ce61 <_IO_file_overflow+161> or ch, 8 CH => 44 (0x24 | 0x8)
0x7ffff7c8ce64 <_IO_file_overflow+164> mov qword ptr [rbp + 0x30], rsi [0x5555555592d0] <= 0x55555555a4b0 ◂— 0
0x7ffff7c8ce68 <_IO_file_overflow+168> mov rsi, rdx RSI => 0x5555555594b0 ◂— 0

这里rcx[rbp + 0x10]也就是_IO_read_end,将其赋给_IO_read_ptr_IO_read_base. rsi_IO_buf_end,这里也就是把_IO_write_end设置成了_IO_buf_endrdx_IO_buf_base,再往下

1
2
3
4
0x7ffff7c8ce6b <_IO_file_overflow+171>:   mov    DWORD PTR [rbp+0x0],ecx
0x7ffff7c8ce6e <_IO_file_overflow+174>: mov ecx,DWORD PTR [rbp+0xc0]
0x7ffff7c8ce74 <_IO_file_overflow+180>: mov QWORD PTR [rbp+0x28],rdx
0x7ffff7c8ce78 <_IO_file_overflow+184> mov qword ptr [rbp + 0x20], rdx [0x5555555592c0] <= 0x5555555594b0 ◂— 0

这里设置_flags,然后保存_mode到ecx([rbp+0xc0]即偏移0xc0是_mode域),然后将_IO_write_base_IO_write_ptr设置为_IO_buf_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
26
27
28
29
30
31
32
33
34
35
pwndbg> p *_IO_list_all
$4 = {
file = {
_flags = -72536956,
_IO_read_ptr = 0x5555555594b0 "",
_IO_read_end = 0x5555555594b0 "",
_IO_read_base = 0x5555555594b0 "",
_IO_write_base = 0x5555555594b0 "",
_IO_write_ptr = 0x5555555594b0 "",
_IO_write_end = 0x55555555a4b0 "",
_IO_buf_base = 0x5555555594b0 "",
_IO_buf_end = 0x55555555a4b0 "",
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7e1b6a0 <_IO_2_1_stderr_>,
_fileno = 3,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x555555559380,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x555555559390,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7e17600 <_IO_file_jumps>
}

可以看到确实设置好了
接着往下走

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0x7ffff7c8ce7c <_IO_file_overflow+188>    test   ecx, ecx                        0xffffffff & 0xffffffff     EFLAGS => 0x286 [ cf PF af zf SF IF df of ac ]
0x7ffff7c8ce7e <_IO_file_overflow+190> ✘ jg _IO_file_overflow+45 <_IO_file_overflow+45>

0x7ffff7c8ce84 <_IO_file_overflow+196> test eax, 0x202 0xfbad2484 & 0x202 EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c8ce89 <_IO_file_overflow+201> ✔ je _IO_file_overflow+45 <_IO_file_overflow+45>

0x7ffff7c8cded <_IO_file_overflow+45> cmp ebx, -1 0xffffffff - -0x1 EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c8cdf0 <_IO_file_overflow+48> ✔ je _IO_file_overflow+220 <_IO_file_overflow+220>

0x7ffff7c8ce9c <_IO_file_overflow+220> pop rbx RBX => 32
0x7ffff7c8ce9d <_IO_file_overflow+221> mov rdi, rbp RDI => 0x5555555592a0 ◂— 0xfbad2c84
0x7ffff7c8cea0 <_IO_file_overflow+224> sub rdx, rsi RDX => 0 (0x5555555594b0 - 0x5555555594b0)
0x7ffff7c8cea3 <_IO_file_overflow+227> pop rbp RBP => 0x5555555592a0
0x7ffff7c8cea4 <_IO_file_overflow+228> pop r12 R12 => 0xd68
0x7ffff7c8cea6 <_IO_file_overflow+230> jmp _IO_do_write <_IO_do_write>

先检查ecx也就是_mode是否大于0(是否被设置为了某种读写模式),这里我们_mode是-1,所以不跳转,然后再判断_flags的状态(0x200 = _IO_NO_WRITES0x2 = _IO_CURRENTLY_PUTTING,je判断,意味着当前不是正在输出,也没禁止写入),跳转到<_IO_file_overflow+45>,判断确定可以写入之后,调用_IO_do_write函数来系统调用write输出fp->_IO_write_ptrfp->_IO_write_base之间的内容,输出完后刷新缓冲区指针

1
2
3
4
5
6
0x7ffff7c8c930 <_IO_do_write>             endbr64
0x7ffff7c8c934 <_IO_do_write+4> xor eax, eax EAX => 0
0x7ffff7c8c936 <_IO_do_write+6> test rdx, rdx 0 & 0 EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c8c939 <_IO_do_write+9> ✘ jne _IO_do_write+16 <_IO_do_write+16>

0x7ffff7c8c93b <_IO_do_write+11> ret <_IO_file_xsputn+213>

我们此时_IO_write_ptr == _IO_write_base,不需要输出,缓冲区也不需要刷新,故而_IO_do_write提前返回,再接着往下

1
2
3
4
5
0x7ffff7c8b6de <_IO_file_xsputn+222>    mov    rcx, qword ptr [rbp + 0x40]     RCX, [0x5555555592e0] => 0x55555555a4b0 ◂— 0
0x7ffff7c8b6e2 <_IO_file_xsputn+226> sub rcx, qword ptr [rbp + 0x38] RCX => 0x1000 (0x55555555a4b0 - 0x5555555594b0)
0x7ffff7c8b6e6 <_IO_file_xsputn+230> mov r8, r15 R8 => 0x20
0x7ffff7c8b6e9 <_IO_file_xsputn+233> cmp rcx, 0x7f 0x1000 - 0x7f EFLAGS => 0x216 [ cf PF AF zf sf IF df of ac ]
0x7ffff7c8b6ed <_IO_file_xsputn+237> ✘ jbe _IO_file_xsputn+250 <_IO_file_xsputn+250>

然后计算_IO_buf_end - _IO_buf_base,其大于0x7f,继续往下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0x7ffff7c8b6ef <_IO_file_xsputn+239>    mov    rax, r15      RAX => 0x20
0x7ffff7c8b6f2 <_IO_file_xsputn+242> xor edx, edx EDX => 0
0x7ffff7c8b6f4 <_IO_file_xsputn+244> div rcx
0x7ffff7c8b6f7 <_IO_file_xsputn+247>: sub r8,rdx
0x7ffff7c8b6fa <_IO_file_xsputn+250> test r8, r8 0 & 0 EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c8b6fd <_IO_file_xsputn+253> ✘ jne _IO_file_xsputn+336 <_IO_file_xsputn+336>

0x7ffff7c8b6ff <_IO_file_xsputn+255> mov r12, rbx R12 => 0x20
0x7ffff7c8b702 <_IO_file_xsputn+258> test r15, r15 0x20 & 0x20 EFLAGS => 0x202 [ cf pf af zf sf IF df of ac ]
0x7ffff7c8b705 <_IO_file_xsputn+261> ✘ je _IO_file_xsputn+110 <_IO_file_xsputn+110>

0x7ffff7c8b70b <_IO_file_xsputn+267> lea rsi, [r13 + r8] RSI => 0x555555559480 ◂— 'wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww'
0x7ffff7c8b710 <_IO_file_xsputn+272> mov rdx, r15 RDX => 0x20
0x7ffff7c8b713 <_IO_file_xsputn+275> mov rdi, rbp RDI => 0x5555555592a0 ◂— 0xfbad2c84
0x7ffff7c8b716 <_IO_file_xsputn+278> call _IO_default_xsputn <_IO_default_xsputn>

这里div rcx,也就是将0x20除以0x1000的余数0x20存储到了rdx,经过一系列判断后调用_IO_default_xsputn函数

1
2
0x7ffff7c8ddc4 <_IO_default_xsputn+4>     test   rdx, rdx     0x20 & 0x20     EFLAGS => 0x202 [ cf pf af zf sf IF df of ac ]
0x7ffff7c8ddc7 <_IO_default_xsputn+7> ✘ je _IO_default_xsputn+480 <_IO_default_xsputn+480>

仍然是检查写入量不为0,后续一些列判断后调用了__mempcpy_avx_unaligned_erms函数(mempcpy函数的优化实现)

1
► 0x7ffff7da0780 <__mempcpy_avx_unaligned_erms>       endbr64

将数据写入缓冲区中

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
pwndbg> p *_IO_list_all
$2 = {
file = {
_flags = -72536956,
_IO_read_ptr = 0x5555555594b0 'w' <repeats 32 times>,
_IO_read_end = 0x5555555594b0 'w' <repeats 32 times>,
_IO_read_base = 0x5555555594b0 'w' <repeats 32 times>,
_IO_write_base = 0x5555555594b0 'w' <repeats 32 times>,
_IO_write_ptr = 0x5555555594d0 "",
_IO_write_end = 0x55555555a4b0 "",
_IO_buf_base = 0x5555555594b0 'w' <repeats 32 times>,
_IO_buf_end = 0x55555555a4b0 "",
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7e1b6a0 <_IO_2_1_stderr_>,
_fileno = 3,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x555555559380,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x555555559390,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7e17600 <_IO_file_jumps>
}

可以看到_IO_write_ptr正好是移动了0x20,再看缓冲区

1
2
3
4
5
pwndbg> x/8gx 0x5555555594a0
0x5555555594a0: 0x0000000000000000 0x0000000000001011
0x5555555594b0: 0x7777777777777777 0x7777777777777777
0x5555555594c0: 0x7777777777777777 0x7777777777777777
0x5555555594d0: 0x0000000000000000 0x0000000000000000

可以看到数据已经读入
程序结束后,我们打开测试文件看到数据已经写入到了文件中
笔者就产生了疑惑:这里并没有系统调用write函数,数据只是进入了缓冲区,是如何写入文件的呢?不急,在本博客0x06部分再分析。
我们先总结一下fwrite的流程:
首先判断输出缓冲区还有多少剩余,如果有剩余则将目标输出数据拷贝到输出缓冲区,如果输出缓冲区没有剩余(输出缓冲区未建立也是没有剩余)或输出缓冲区不够则调用_IO_OVERFLOW建立输出缓冲区或刷新输出缓冲区。输出缓冲区刷新后判断剩余的目标输出数据是否超过块的size(0x1000),如果超过块的size,则不通过输出缓冲区直接以块为单位,使用sys_write输出大块的目标数据到目标文件。余下或者小于块size的数据(我们这里0x20的数据便是这一行列)则调用_IO_default_xsputn将数据拷贝到输出缓冲区

0x05 fclose函数

还是这个demo,加个fclose函数

1
2
3
4
5
6
7
8
9
10
11
12
#include<stdio.h>
#include<stdlib.h>

int main(){

FILE*fp = fopen("test.txt","wb");
char *ptr = malloc(0x20);
memcpy(ptr,"wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww",0x20);
fwrite(ptr, 1, 0x20, fp);
fclose(fp);
return 0;
}

在fclose下个断点,开始调试

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
pwndbg> disass
Dump of assembler code for function _IO_new_fclose:
Address range 0x7ffff7c7ec70 to 0x7ffff7c7ee46:
=> 0x00007ffff7c7ec70 <+0>: endbr64
0x00007ffff7c7ec74 <+4>: push r12
0x00007ffff7c7ec76 <+6>: push rbp
0x00007ffff7c7ec77 <+7>: mov rbp,rdi
0x00007ffff7c7ec7a <+10>: push rbx
0x00007ffff7c7ec7b <+11>: mov eax,DWORD PTR [rdi]
0x00007ffff7c7ec7d <+13>: test ah,0x20
0x00007ffff7c7ec80 <+16>: jne 0x7ffff7c7ed30 <_IO_new_fclose+192>
0x00007ffff7c7ec86 <+22>: mov edx,eax
0x00007ffff7c7ec88 <+24>: and edx,0x8000
0x00007ffff7c7ec8e <+30>: je 0x7ffff7c7ed3d <_IO_new_fclose+205>
0x00007ffff7c7ec94 <+36>: shl eax,0x1a
0x00007ffff7c7ec97 <+39>: sar eax,0x1f
0x00007ffff7c7ec9a <+42>: mov r12d,eax
0x00007ffff7c7ec9d <+45>: test edx,edx
0x00007ffff7c7ec9f <+47>: jne 0x7ffff7c7ecb2 <_IO_new_fclose+66>
0x00007ffff7c7eca1 <+49>: mov rdi,QWORD PTR [rbp+0x88]
0x00007ffff7c7eca8 <+56>: sub DWORD PTR [rdi+0x4],0x1
0x00007ffff7c7ecac <+60>: je 0x7ffff7c7eda0 <_IO_new_fclose+304>
0x00007ffff7c7ecb2 <+66>: mov rbx,QWORD PTR [rbp+0xd8]
0x00007ffff7c7ecb9 <+73>: lea rdx,[rip+0x197d40] # 0x7ffff7e16a00 <_IO_helper_jumps>
0x00007ffff7c7ecc0 <+80>: lea rax,[rip+0x198aa1] # 0x7ffff7e17768
0x00007ffff7c7ecc7 <+87>: sub rax,rdx
0x00007ffff7c7ecca <+90>: mov rcx,rbx
0x00007ffff7c7eccd <+93>: sub rcx,rdx
0x00007ffff7c7ecd0 <+96>: cmp rax,rcx
0x00007ffff7c7ecd3 <+99>: jbe 0x7ffff7c7ee10 <_IO_new_fclose+416>
0x00007ffff7c7ecd9 <+105>: xor esi,esi
0x00007ffff7c7ecdb <+107>: mov rdi,rbp
0x00007ffff7c7ecde <+110>: call QWORD PTR [rbx+0x10]
0x00007ffff7c7ece1 <+113>: mov eax,DWORD PTR [rbp+0xc0]
0x00007ffff7c7ece7 <+119>: test eax,eax
0x00007ffff7c7ece9 <+121>: jg 0x7ffff7c7edc0 <_IO_new_fclose+336>
0x00007ffff7c7ecef <+127>: cmp QWORD PTR [rbp+0x48],0x0
0x00007ffff7c7ecf4 <+132>: je 0x7ffff7c7ecfe <_IO_new_fclose+142>
0x00007ffff7c7ecf6 <+134>: mov rdi,rbp
0x00007ffff7c7ecf9 <+137>: call 0x7ffff7c8d7c0 <__GI__IO_free_backup_area>
0x00007ffff7c7ecfe <+142>: cmp rbp,QWORD PTR [rip+0x19b273] # 0x7ffff7e19f78
0x00007ffff7c7ed05 <+149>: sete al
0x00007ffff7c7ed08 <+152>: cmp rbp,QWORD PTR [rip+0x19b1e1] # 0x7ffff7e19ef0
0x00007ffff7c7ed0f <+159>: sete dl
0x00007ffff7c7ed12 <+162>: or al,dl
0x00007ffff7c7ed14 <+164>: jne 0x7ffff7c7ed27 <_IO_new_fclose+183>
0x00007ffff7c7ed16 <+166>: cmp rbp,QWORD PTR [rip+0x19b16b] # 0x7ffff7e19e88
0x00007ffff7c7ed1d <+173>: je 0x7ffff7c7ed27 <_IO_new_fclose+183>
0x00007ffff7c7ed1f <+175>: mov rdi,rbp
0x00007ffff7c7ed22 <+178>: call 0x7ffff7c28370 <free@plt>
0x00007ffff7c7ed27 <+183>: mov eax,r12d
0x00007ffff7c7ed2a <+186>: pop rbx
0x00007ffff7c7ed2b <+187>: pop rbp
0x00007ffff7c7ed2c <+188>: pop r12
0x00007ffff7c7ed2e <+190>: ret
0x00007ffff7c7ed2f <+191>: nop
0x00007ffff7c7ed30 <+192>: call 0x7ffff7c8d2b0 <__GI__IO_un_link>
0x00007ffff7c7ed35 <+197>: mov eax,DWORD PTR [rbp+0x0]
0x00007ffff7c7ed38 <+200>: test ah,0x80
0x00007ffff7c7ed3b <+203>: jne 0x7ffff7c7ed76 <_IO_new_fclose+262>
0x00007ffff7c7ed3d <+205>: mov rbx,QWORD PTR fs:0x10
0x00007ffff7c7ed46 <+214>: mov rdi,QWORD PTR [rbp+0x88]
0x00007ffff7c7ed4d <+221>: cmp QWORD PTR [rdi+0x8],rbx
0x00007ffff7c7ed51 <+225>: je 0x7ffff7c7ed72 <_IO_new_fclose+258>
0x00007ffff7c7ed53 <+227>: xor eax,eax
0x00007ffff7c7ed55 <+229>: mov edx,0x1
0x00007ffff7c7ed5a <+234>: lock cmpxchg DWORD PTR [rdi],edx
0x00007ffff7c7ed5e <+238>: jne 0x7ffff7c7ee30 <_IO_new_fclose+448>
0x00007ffff7c7ed64 <+244>: mov rdi,QWORD PTR [rbp+0x88]
0x00007ffff7c7ed6b <+251>: mov eax,DWORD PTR [rbp+0x0]
0x00007ffff7c7ed6e <+254>: mov QWORD PTR [rdi+0x8],rbx
0x00007ffff7c7ed72 <+258>: add DWORD PTR [rdi+0x4],0x1
0x00007ffff7c7ed76 <+262>: mov edx,eax
0x00007ffff7c7ed78 <+264>: and edx,0x8000
0x00007ffff7c7ed7e <+270>: test ah,0x20
0x00007ffff7c7ed81 <+273>: je 0x7ffff7c7ec94 <_IO_new_fclose+36>
0x00007ffff7c7ed87 <+279>: mov rdi,rbp
0x00007ffff7c7ed8a <+282>: call 0x7ffff7c8be90 <_IO_new_file_close_it>
0x00007ffff7c7ed8f <+287>: mov edx,DWORD PTR [rbp+0x0]
0x00007ffff7c7ed92 <+290>: mov r12d,eax
0x00007ffff7c7ed95 <+293>: and edx,0x8000
0x00007ffff7c7ed9b <+299>: jmp 0x7ffff7c7ec9d <_IO_new_fclose+45>
0x00007ffff7c7eda0 <+304>: mov QWORD PTR [rdi+0x8],0x0
0x00007ffff7c7eda8 <+312>: xchg DWORD PTR [rdi],edx
0x00007ffff7c7edaa <+314>: cmp edx,0x1
0x00007ffff7c7edad <+317>: jle 0x7ffff7c7ecb2 <_IO_new_fclose+66>
0x00007ffff7c7edb3 <+323>: call 0x7ffff7c91300 <__GI___lll_lock_wake_private>
0x00007ffff7c7edb8 <+328>: jmp 0x7ffff7c7ecb2 <_IO_new_fclose+66>
0x00007ffff7c7edbd <+333>: nop DWORD PTR [rax]
0x00007ffff7c7edc0 <+336>: mov rbx,QWORD PTR [rbp+0x98]
0x00007ffff7c7edc7 <+343>: xor eax,eax
0x00007ffff7c7edc9 <+345>: mov edx,0x1
0x00007ffff7c7edce <+350>: lock cmpxchg DWORD PTR [rip+0x19cc5a],edx # 0x7ffff7e1ba30 <__gconv_lock>
0x00007ffff7c7edd6 <+358>: jne 0x7ffff7c7ee20 <_IO_new_fclose+432>
0x00007ffff7c7edd8 <+360>: mov rdi,QWORD PTR [rbx]
0x00007ffff7c7eddb <+363>: call 0x7ffff7c2b990 <__gconv_release_step>
0x00007ffff7c7ede0 <+368>: mov rdi,QWORD PTR [rbx+0x38]
0x00007ffff7c7ede4 <+372>: call 0x7ffff7c2b990 <__gconv_release_step>
0x00007ffff7c7ede9 <+377>: xor eax,eax
0x00007ffff7c7edeb <+379>: xchg DWORD PTR [rip+0x19cc3f],eax # 0x7ffff7e1ba30 <__gconv_lock>
0x00007ffff7c7edf1 <+385>: cmp eax,0x1
0x00007ffff7c7edf4 <+388>: jle 0x7ffff7c7ecfe <_IO_new_fclose+142>
0x00007ffff7c7edfa <+394>: lea rdi,[rip+0x19cc2f] # 0x7ffff7e1ba30 <__gconv_lock>
0x00007ffff7c7ee01 <+401>: call 0x7ffff7c91300 <__GI___lll_lock_wake_private>
0x00007ffff7c7ee06 <+406>: jmp 0x7ffff7c7ecfe <_IO_new_fclose+142>
0x00007ffff7c7ee0b <+411>: nop DWORD PTR [rax+rax*1+0x0]
0x00007ffff7c7ee10 <+416>: call 0x7ffff7c89ef0 <_IO_vtable_check>
0x00007ffff7c7ee15 <+421>: jmp 0x7ffff7c7ecd9 <_IO_new_fclose+105>
0x00007ffff7c7ee1a <+426>: nop WORD PTR [rax+rax*1+0x0]
0x00007ffff7c7ee20 <+432>: lea rdi,[rip+0x19cc09] # 0x7ffff7e1ba30 <__gconv_lock>
0x00007ffff7c7ee27 <+439>: call 0x7ffff7c91230 <__GI___lll_lock_wait_private>
0x00007ffff7c7ee2c <+444>: jmp 0x7ffff7c7edd8 <_IO_new_fclose+360>
0x00007ffff7c7ee2e <+446>: xchg ax,ax
0x00007ffff7c7ee30 <+448>: call 0x7ffff7c91230 <__GI___lll_lock_wait_private>
0x00007ffff7c7ee35 <+453>: jmp 0x7ffff7c7ed64 <_IO_new_fclose+244>
0x00007ffff7c7ee3a <+458>: endbr64
0x00007ffff7c7ee3e <+462>: mov r12,rax
0x00007ffff7c7ee41 <+465>: jmp 0x7ffff7c28fff <_IO_new_fclose.cold>
Address range 0x7ffff7c28fff to 0x7ffff7c29036:
0x00007ffff7c28fff <-351345>: test DWORD PTR [rbp+0x0],0x8000
0x00007ffff7c29006 <-351338>: jne 0x7ffff7c2902e <_IO_new_fclose-351298>
0x00007ffff7c29008 <-351336>: mov rdi,QWORD PTR [rbp+0x88]
0x00007ffff7c2900f <-351329>: mov eax,DWORD PTR [rdi+0x4]
0x00007ffff7c29012 <-351326>: sub eax,0x1
0x00007ffff7c29015 <-351323>: mov DWORD PTR [rdi+0x4],eax
0x00007ffff7c29018 <-351320>: jne 0x7ffff7c2902e <_IO_new_fclose-351298>
0x00007ffff7c2901a <-351318>: mov QWORD PTR [rdi+0x8],0x0
0x00007ffff7c29022 <-351310>: xchg DWORD PTR [rdi],eax
0x00007ffff7c29024 <-351308>: sub eax,0x1
0x00007ffff7c29027 <-351305>: jle 0x7ffff7c2902e <_IO_new_fclose-351298>
0x00007ffff7c29029 <-351303>: call 0x7ffff7c91300 <__GI___lll_lock_wake_private>
0x00007ffff7c2902e <-351298>: mov rdi,r12
0x00007ffff7c29031 <-351295>: call 0x7ffff7c2a120 <_Unwind_Resume>
End of assembler dump.

_IO_new_fclose函数,往下走

1
2
3
4
5
6
7
8
9
0x7ffff7c7ec74 <fclose+4>      push   r12
0x7ffff7c7ec76 <fclose+6> push rbp
0x7ffff7c7ec77 <fclose+7> mov rbp, rdi RBP => 0x5555555592a0 ◂— 0xfbad2c84
0x7ffff7c7ec7a <fclose+10> push rbx
0x7ffff7c7ec7b <fclose+11> mov eax, dword ptr [rdi] EAX, [0x5555555592a0] => 0xfbad2c84
0x7ffff7c7ec7d <fclose+13> test ah, 0x20 0x2c & 0x20 EFLAGS => 0x202 [ cf pf af zf sf IF df of ac ]
0x7ffff7c7ec80 <fclose+16> ✔ jne fclose+192 <fclose+192>

0x7ffff7c7ed30 <fclose+192> call _IO_un_link <_IO_un_link>

test ah, 0x20:检测_flags相应位,判断文件流是否需要链表管理(_IO_IS_FILEBUF 标志),满足条件调用_IO_un_link函数

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
pwndbg> disass
Dump of assembler code for function __GI__IO_un_link:
=> 0x00007ffff7c8d070 <+0>: push r13
0x00007ffff7c8d072 <+2>: push r12
0x00007ffff7c8d074 <+4>: lea r12,[rip+0x18f9d5] # 0x7ffff7e1ca50 <list_all_lock>
0x00007ffff7c8d07b <+11>: push rbp
0x00007ffff7c8d07c <+12>: push rbx
0x00007ffff7c8d07d <+13>: mov rbx,rdi
0x00007ffff7c8d080 <+16>: sub rsp,0x38
0x00007ffff7c8d084 <+20>: mov rax,QWORD PTR fs:0x28
0x00007ffff7c8d08d <+29>: mov QWORD PTR [rsp+0x28],rax
0x00007ffff7c8d092 <+34>: xor eax,eax
0x00007ffff7c8d094 <+36>: mov rbp,rsp
0x00007ffff7c8d097 <+39>: lea rax,[rip+0xffffffffffffff42] # 0x7ffff7c8cfe0 <flush_cleanup>
0x00007ffff7c8d09e <+46>: mov QWORD PTR [rsp+0x8],0x0
0x00007ffff7c8d0a7 <+55>: mov rdi,rbp
0x00007ffff7c8d0aa <+58>: mov QWORD PTR [rsp],rax
0x00007ffff7c8d0ae <+62>: call 0x7ffff7c911c0 <__GI___libc_cleanup_push_defer>
0x00007ffff7c8d0b3 <+67>: mov r13,QWORD PTR fs:0x10
0x00007ffff7c8d0bc <+76>: cmp r13,QWORD PTR [rip+0x18f995] # 0x7ffff7e1ca58 <list_all_lock+8>
0x00007ffff7c8d0c3 <+83>: je 0x7ffff7c8d0e1 <__GI__IO_un_link+113>
0x00007ffff7c8d0c5 <+85>: xor eax,eax
0x00007ffff7c8d0c7 <+87>: mov edx,0x1
0x00007ffff7c8d0cc <+92>: lock cmpxchg DWORD PTR [rip+0x18f97c],edx # 0x7ffff7e1ca50 <list_all_lock>
0x00007ffff7c8d0d4 <+100>: jne 0x7ffff7c8d270 <__GI__IO_un_link+512>
0x00007ffff7c8d0da <+106>: mov QWORD PTR [rip+0x18f977],r13 # 0x7ffff7e1ca58 <list_all_lock+8>
0x00007ffff7c8d0e1 <+113>: mov edx,DWORD PTR [rip+0x18f96d] # 0x7ffff7e1ca54 <list_all_lock+4>
0x00007ffff7c8d0e7 <+119>: mov esi,DWORD PTR [rbx]
0x00007ffff7c8d0e9 <+121>: mov QWORD PTR [rip+0x18f950],rbx # 0x7ffff7e1ca40 <run_fp>
0x00007ffff7c8d0f0 <+128>: lea eax,[rdx+0x1]
0x00007ffff7c8d0f3 <+131>: mov DWORD PTR [rip+0x18f95b],eax # 0x7ffff7e1ca54 <list_all_lock+4>
0x00007ffff7c8d0f9 <+137>: mov eax,esi
0x00007ffff7c8d0fb <+139>: and eax,0x8000
0x00007ffff7c8d100 <+144>: jne 0x7ffff7c8d1f8 <__GI__IO_un_link+392>
0x00007ffff7c8d106 <+150>: mov r13,QWORD PTR fs:0x10
0x00007ffff7c8d10f <+159>: mov rdi,QWORD PTR [rbx+0x88]
0x00007ffff7c8d116 <+166>: cmp r13,QWORD PTR [rdi+0x8]
0x00007ffff7c8d11a <+170>: je 0x7ffff7c8d230 <__GI__IO_un_link+448>
0x00007ffff7c8d120 <+176>: mov edx,0x1
0x00007ffff7c8d125 <+181>: lock cmpxchg DWORD PTR [rdi],edx
0x00007ffff7c8d129 <+185>: jne 0x7ffff7c8d297 <__GI__IO_un_link+551>
0x00007ffff7c8d12f <+191>: mov esi,DWORD PTR [rbx]
0x00007ffff7c8d131 <+193>: mov rdi,QWORD PTR [rbx+0x88]
0x00007ffff7c8d138 <+200>: mov rcx,QWORD PTR [rip+0x18e541] # 0x7ffff7e1b680 <_IO_list_all>
0x00007ffff7c8d13f <+207>: mov eax,esi
0x00007ffff7c8d141 <+209>: add DWORD PTR [rdi+0x4],0x1
0x00007ffff7c8d145 <+213>: mov QWORD PTR [rdi+0x8],r13
0x00007ffff7c8d149 <+217>: and eax,0x8000
0x00007ffff7c8d14e <+222>: test rcx,rcx
0x00007ffff7c8d151 <+225>: je 0x7ffff7c8d167 <__GI__IO_un_link+247>
0x00007ffff7c8d153 <+227>: mov rdx,QWORD PTR [rcx+0x68]
0x00007ffff7c8d157 <+231>: cmp rbx,rcx
0x00007ffff7c8d15a <+234>: jne 0x7ffff7c8d21c <__GI__IO_un_link+428>
0x00007ffff7c8d160 <+240>: mov QWORD PTR [rip+0x18e519],rdx # 0x7ffff7e1b680 <_IO_list_all>
0x00007ffff7c8d167 <+247>: and sil,0x7f
0x00007ffff7c8d16b <+251>: mov DWORD PTR [rbx],esi
0x00007ffff7c8d16d <+253>: test eax,eax
0x00007ffff7c8d16f <+255>: jne 0x7ffff7c8d196 <__GI__IO_un_link+294>
0x00007ffff7c8d171 <+257>: mov rdi,QWORD PTR [rbx+0x88]
0x00007ffff7c8d178 <+264>: mov eax,DWORD PTR [rdi+0x4]
0x00007ffff7c8d17b <+267>: sub eax,0x1
0x00007ffff7c8d17e <+270>: mov DWORD PTR [rdi+0x4],eax
0x00007ffff7c8d181 <+273>: jne 0x7ffff7c8d196 <__GI__IO_un_link+294>
0x00007ffff7c8d183 <+275>: mov QWORD PTR [rdi+0x8],0x0
0x00007ffff7c8d18b <+283>: xchg DWORD PTR [rdi],eax
0x00007ffff7c8d18d <+285>: cmp eax,0x1
0x00007ffff7c8d190 <+288>: jg 0x7ffff7c8d28d <__GI__IO_un_link+541>
0x00007ffff7c8d196 <+294>: mov eax,DWORD PTR [rip+0x18f8b8] # 0x7ffff7e1ca54 <list_all_lock+4>
0x00007ffff7c8d19c <+300>: lea edx,[rax-0x1]
0x00007ffff7c8d19f <+303>: mov QWORD PTR [rip+0x18f896],0x0 # 0x7ffff7e1ca40 <run_fp>
0x00007ffff7c8d1aa <+314>: mov DWORD PTR [rip+0x18f8a4],edx # 0x7ffff7e1ca54 <list_all_lock+4>
0x00007ffff7c8d1b0 <+320>: test edx,edx
0x00007ffff7c8d1b2 <+322>: jne 0x7ffff7c8d1ce <__GI__IO_un_link+350>
0x00007ffff7c8d1b4 <+324>: mov QWORD PTR [rip+0x18f899],0x0 # 0x7ffff7e1ca58 <list_all_lock+8>
0x00007ffff7c8d1bf <+335>: xchg DWORD PTR [rip+0x18f88b],edx # 0x7ffff7e1ca50 <list_all_lock>
0x00007ffff7c8d1c5 <+341>: cmp edx,0x1
0x00007ffff7c8d1c8 <+344>: jg 0x7ffff7c8d280 <__GI__IO_un_link+528>
0x00007ffff7c8d1ce <+350>: mov rdi,rbp
0x00007ffff7c8d1d1 <+353>: call 0x7ffff7c91200 <__GI___libc_cleanup_pop_restore>
0x00007ffff7c8d1d6 <+358>: mov rax,QWORD PTR [rsp+0x28]
0x00007ffff7c8d1db <+363>: sub rax,QWORD PTR fs:0x28
0x00007ffff7c8d1e4 <+372>: jne 0x7ffff7c8d2a1 <__GI__IO_un_link+561>
0x00007ffff7c8d1ea <+378>: add rsp,0x38
0x00007ffff7c8d1ee <+382>: pop rbx
0x00007ffff7c8d1ef <+383>: pop rbp
0x00007ffff7c8d1f0 <+384>: pop r12
0x00007ffff7c8d1f2 <+386>: pop r13
0x00007ffff7c8d1f4 <+388>: ret
0x00007ffff7c8d1f5 <+389>: nop DWORD PTR [rax]
0x00007ffff7c8d1f8 <+392>: mov rcx,QWORD PTR [rip+0x18e481] # 0x7ffff7e1b680 <_IO_list_all>
0x00007ffff7c8d1ff <+399>: test rcx,rcx
0x00007ffff7c8d202 <+402>: jne 0x7ffff7c8d153 <__GI__IO_un_link+227>
0x00007ffff7c8d208 <+408>: and sil,0x7f
0x00007ffff7c8d20c <+412>: mov DWORD PTR [rbx],esi
0x00007ffff7c8d20e <+414>: jmp 0x7ffff7c8d19f <__GI__IO_un_link+303>
0x00007ffff7c8d210 <+416>: cmp rbx,rdx
0x00007ffff7c8d213 <+419>: je 0x7ffff7c8d250 <__GI__IO_un_link+480>
0x00007ffff7c8d215 <+421>: mov rcx,rdx
0x00007ffff7c8d218 <+424>: mov rdx,QWORD PTR [rdx+0x68]
0x00007ffff7c8d21c <+428>: test rdx,rdx
0x00007ffff7c8d21f <+431>: jne 0x7ffff7c8d210 <__GI__IO_un_link+416>
0x00007ffff7c8d221 <+433>: jmp 0x7ffff7c8d167 <__GI__IO_un_link+247>
0x00007ffff7c8d226 <+438>: cs nop WORD PTR [rax+rax*1+0x0]
0x00007ffff7c8d230 <+448>: mov rcx,QWORD PTR [rip+0x18e449] # 0x7ffff7e1b680 <_IO_list_all>
0x00007ffff7c8d237 <+455>: add DWORD PTR [rdi+0x4],0x1
0x00007ffff7c8d23b <+459>: test rcx,rcx
0x00007ffff7c8d23e <+462>: jne 0x7ffff7c8d153 <__GI__IO_un_link+227>
0x00007ffff7c8d244 <+468>: and sil,0x7f
0x00007ffff7c8d248 <+472>: mov DWORD PTR [rbx],esi
0x00007ffff7c8d24a <+474>: jmp 0x7ffff7c8d178 <__GI__IO_un_link+264>
0x00007ffff7c8d24f <+479>: nop
0x00007ffff7c8d250 <+480>: mov rdx,QWORD PTR [rbx+0x68]
0x00007ffff7c8d254 <+484>: and sil,0x7f
0x00007ffff7c8d258 <+488>: add rcx,0x68
0x00007ffff7c8d25c <+492>: mov QWORD PTR [rcx],rdx
0x00007ffff7c8d25f <+495>: mov DWORD PTR [rbx],esi
0x00007ffff7c8d261 <+497>: test eax,eax
0x00007ffff7c8d263 <+499>: je 0x7ffff7c8d171 <__GI__IO_un_link+257>
0x00007ffff7c8d269 <+505>: jmp 0x7ffff7c8d196 <__GI__IO_un_link+294>
0x00007ffff7c8d26e <+510>: xchg ax,ax
0x00007ffff7c8d270 <+512>: mov rdi,r12
0x00007ffff7c8d273 <+515>: call 0x7ffff7c91230 <__GI___lll_lock_wait_private>
0x00007ffff7c8d278 <+520>: jmp 0x7ffff7c8d0da <__GI__IO_un_link+106>
0x00007ffff7c8d27d <+525>: nop DWORD PTR [rax]
0x00007ffff7c8d280 <+528>: mov rdi,r12
0x00007ffff7c8d283 <+531>: call 0x7ffff7c91300 <__GI___lll_lock_wake_private>
0x00007ffff7c8d288 <+536>: jmp 0x7ffff7c8d1ce <__GI__IO_un_link+350>
0x00007ffff7c8d28d <+541>: call 0x7ffff7c91300 <__GI___lll_lock_wake_private>
0x00007ffff7c8d292 <+546>: jmp 0x7ffff7c8d196 <__GI__IO_un_link+294>
0x00007ffff7c8d297 <+551>: call 0x7ffff7c91230 <__GI___lll_lock_wait_private>
0x00007ffff7c8d29c <+556>: jmp 0x7ffff7c8d12f <__GI__IO_un_link+191>
0x00007ffff7c8d2a1 <+561>: call 0x7ffff7d36550 <__stack_chk_fail>
End of assembler dump.

往下走
进行了一些有关线程锁的操作,然后再维护_IO_list_all链表:检查标志位是否包含_IO_LINKED标志,该标志的定义是#define _IO_LINKED 0x80,表示该结构体是否被链接到了_IO_list_all链表中。
如果没有_IO_LINKED标志(不在_IO_list_all链表中)或者_IO_list_all链表为空,则直接返回。
否则的话即表示结构体为_IO_list_all链表中某个节点,所要做的就是将这个节点取下来,接下来就是单链表的删除节点的操作,首先判断是不是_IO_list_all链表头,如果是的话直接将_IO_list_all指向_IO_list_all->file._chain就好了,如果不是链表头则遍历链表,找到该结构体,再将其取下。
最后返回之前设置_flags~_IO_LINKED表示该结构体不在_IO_list_all链表中.我们看现在_IO_list_all

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
pwndbg> p *_IO_list_all
$2 = {
file = {
_flags = -72540026,
_IO_read_ptr = 0x0,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_IO_write_end = 0x0,
_IO_buf_base = 0x0,
_IO_buf_end = 0x0,
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7e1b780 <_IO_2_1_stdout_>,
_fileno = 2,
_flags2 = 0,
_old_offset = -1,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x7ffff7e1ca60 <_IO_stdfile_2_lock>,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x7ffff7e1a8a0 <_IO_wide_data_2>,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = 0,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7e17600 <_IO_file_jumps>
}
pwndbg> p _IO_list_all
$3 = (struct _IO_FILE_plus *) 0x7ffff7e1b6a0 <_IO_2_1_stderr_>

已经变回了_IO_2_1_stderr_
我们再看我们要关闭的结构体

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
pwndbg> p *(struct _IO_FILE_plus*) 0x5555555592a0
$4 = {
file = {
_flags = -72537084,
_IO_read_ptr = 0x5555555594b0 'w' <repeats 32 times>,
_IO_read_end = 0x5555555594b0 'w' <repeats 32 times>,
_IO_read_base = 0x5555555594b0 'w' <repeats 32 times>,
_IO_write_base = 0x5555555594b0 'w' <repeats 32 times>,
_IO_write_ptr = 0x5555555594d0 "",
_IO_write_end = 0x55555555a4b0 "",
_IO_buf_base = 0x5555555594b0 'w' <repeats 32 times>,
_IO_buf_end = 0x55555555a4b0 "",
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7e1b6a0 <_IO_2_1_stderr_>,
_fileno = 3,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x555555559380,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x555555559390,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7e17600 <_IO_file_jumps>
}

_chain域仍然指向_IO_2_1_stderr_,只是将_IO_list_all指针指向了_IO_2_1_stderr_
再往下走,接着经过一些检查后就是调用_IO_file_close_it函数

1
2
0x7ffff7c7ed87 <fclose+279>    mov    rdi, rbp        RDI => 0x5555555592a0 ◂— 0xfbad2c04
0x7ffff7c7ed8a <fclose+282> call _IO_file_close_it <_IO_file_close_it>
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
pwndbg> disass
Dump of assembler code for function _IO_new_file_close_it:
=> 0x00007ffff7c8be90 <+0>: endbr64
0x00007ffff7c8be94 <+4>: push r12
0x00007ffff7c8be96 <+6>: push rbp
0x00007ffff7c8be97 <+7>: push rbx
0x00007ffff7c8be98 <+8>: mov r12d,DWORD PTR [rdi+0x70]
0x00007ffff7c8be9c <+12>: cmp r12d,0xffffffff
0x00007ffff7c8bea0 <+16>: je 0x7ffff7c8bf8c <_IO_new_file_close_it+252>
0x00007ffff7c8bea6 <+22>: mov eax,DWORD PTR [rdi]
0x00007ffff7c8bea8 <+24>: mov rbx,rdi
0x00007ffff7c8beab <+27>: xor ebp,ebp
0x00007ffff7c8bead <+29>: and eax,0x808
0x00007ffff7c8beb2 <+34>: cmp eax,0x800
0x00007ffff7c8beb7 <+39>: je 0x7ffff7c8bf98 <_IO_new_file_close_it+264>
0x00007ffff7c8bebd <+45>: mov rdi,rbx
0x00007ffff7c8bec0 <+48>: xor r12d,r12d
0x00007ffff7c8bec3 <+51>: call 0x7ffff7c8f230 <__GI__IO_unsave_markers>
0x00007ffff7c8bec8 <+56>: test BYTE PTR [rbx+0x74],0x20
0x00007ffff7c8becc <+60>: jne 0x7ffff7c8bf03 <_IO_new_file_close_it+115>
0x00007ffff7c8bece <+62>: mov r12,QWORD PTR [rbx+0xd8]
0x00007ffff7c8bed5 <+69>: lea rdx,[rip+0x18ab24] # 0x7ffff7e16a00 <_IO_helper_jumps>
0x00007ffff7c8bedc <+76>: lea rax,[rip+0x18b885] # 0x7ffff7e17768
0x00007ffff7c8bee3 <+83>: sub rax,rdx
0x00007ffff7c8bee6 <+86>: mov rcx,r12
0x00007ffff7c8bee9 <+89>: sub rcx,rdx
0x00007ffff7c8beec <+92>: cmp rax,rcx
0x00007ffff7c8beef <+95>: jbe 0x7ffff7c8bfe0 <_IO_new_file_close_it+336>
0x00007ffff7c8bef5 <+101>: mov rdi,rbx
0x00007ffff7c8bef8 <+104>: call QWORD PTR [r12+0x88]
0x00007ffff7c8bf00 <+112>: mov r12d,eax
0x00007ffff7c8bf03 <+115>: mov eax,DWORD PTR [rbx+0xc0]
0x00007ffff7c8bf09 <+121>: test eax,eax
0x00007ffff7c8bf0b <+123>: jle 0x7ffff7c8bf47 <_IO_new_file_close_it+183>
0x00007ffff7c8bf0d <+125>: mov rax,QWORD PTR [rbx+0xa0]
0x00007ffff7c8bf14 <+132>: cmp QWORD PTR [rax+0x40],0x0
0x00007ffff7c8bf19 <+137>: je 0x7ffff7c8bf23 <_IO_new_file_close_it+147>
0x00007ffff7c8bf1b <+139>: mov rdi,rbx
0x00007ffff7c8bf1e <+142>: call 0x7ffff7c84340 <__GI__IO_free_wbackup_area>
0x00007ffff7c8bf23 <+147>: xor ecx,ecx
0x00007ffff7c8bf25 <+149>: xor edx,edx
0x00007ffff7c8bf27 <+151>: xor esi,esi
0x00007ffff7c8bf29 <+153>: mov rdi,rbx
0x00007ffff7c8bf2c <+156>: call 0x7ffff7c83590 <__GI__IO_wsetb>
0x00007ffff7c8bf31 <+161>: mov rax,QWORD PTR [rbx+0xa0]
0x00007ffff7c8bf38 <+168>: pxor xmm0,xmm0
0x00007ffff7c8bf3c <+172>: movups XMMWORD PTR [rax],xmm0
0x00007ffff7c8bf3f <+175>: movups XMMWORD PTR [rax+0x10],xmm0
0x00007ffff7c8bf43 <+179>: movups XMMWORD PTR [rax+0x20],xmm0
0x00007ffff7c8bf47 <+183>: xor ecx,ecx
0x00007ffff7c8bf49 <+185>: xor edx,edx
0x00007ffff7c8bf4b <+187>: xor esi,esi
0x00007ffff7c8bf4d <+189>: mov rdi,rbx
0x00007ffff7c8bf50 <+192>: call 0x7ffff7c8dc20 <__GI__IO_setb>
0x00007ffff7c8bf55 <+197>: pxor xmm0,xmm0
0x00007ffff7c8bf59 <+201>: mov rdi,rbx
0x00007ffff7c8bf5c <+204>: movups XMMWORD PTR [rbx+0x8],xmm0
0x00007ffff7c8bf60 <+208>: movups XMMWORD PTR [rbx+0x18],xmm0
0x00007ffff7c8bf64 <+212>: movups XMMWORD PTR [rbx+0x28],xmm0
0x00007ffff7c8bf68 <+216>: call 0x7ffff7c8d2b0 <__GI__IO_un_link>
0x00007ffff7c8bf6d <+221>: test r12d,r12d
0x00007ffff7c8bf70 <+224>: mov DWORD PTR [rbx],0xfbad240c
0x00007ffff7c8bf76 <+230>: mov DWORD PTR [rbx+0x70],0xffffffff
0x00007ffff7c8bf7d <+237>: cmove r12d,ebp
0x00007ffff7c8bf81 <+241>: mov QWORD PTR [rbx+0x90],0xffffffffffffffff
0x00007ffff7c8bf8c <+252>: mov eax,r12d
0x00007ffff7c8bf8f <+255>: pop rbx
0x00007ffff7c8bf90 <+256>: pop rbp
0x00007ffff7c8bf91 <+257>: pop r12
0x00007ffff7c8bf93 <+259>: ret
0x00007ffff7c8bf94 <+260>: nop DWORD PTR [rax+0x0]
0x00007ffff7c8bf98 <+264>: mov edx,DWORD PTR [rdi+0xc0]
0x00007ffff7c8bf9e <+270>: test edx,edx
0x00007ffff7c8bfa0 <+272>: jle 0x7ffff7c8bfc8 <_IO_new_file_close_it+312>
0x00007ffff7c8bfa2 <+274>: mov rax,QWORD PTR [rdi+0xa0]
0x00007ffff7c8bfa9 <+281>: mov rsi,QWORD PTR [rax+0x18]
0x00007ffff7c8bfad <+285>: mov rdx,QWORD PTR [rax+0x20]
0x00007ffff7c8bfb1 <+289>: sub rdx,rsi
0x00007ffff7c8bfb4 <+292>: sar rdx,0x2
0x00007ffff7c8bfb8 <+296>: call 0x7ffff7c861a0 <__GI__IO_wdo_write>
0x00007ffff7c8bfbd <+301>: mov ebp,eax
0x00007ffff7c8bfbf <+303>: jmp 0x7ffff7c8bebd <_IO_new_file_close_it+45>
0x00007ffff7c8bfc4 <+308>: nop DWORD PTR [rax+0x0]
0x00007ffff7c8bfc8 <+312>: mov rsi,QWORD PTR [rdi+0x20]
0x00007ffff7c8bfcc <+316>: mov rdx,QWORD PTR [rdi+0x28]
0x00007ffff7c8bfd0 <+320>: sub rdx,rsi
0x00007ffff7c8bfd3 <+323>: call 0x7ffff7c8c930 <_IO_new_do_write>
0x00007ffff7c8bfd8 <+328>: mov ebp,eax
0x00007ffff7c8bfda <+330>: jmp 0x7ffff7c8bebd <_IO_new_file_close_it+45>
0x00007ffff7c8bfdf <+335>: nop
0x00007ffff7c8bfe0 <+336>: call 0x7ffff7c89ef0 <_IO_vtable_check>
0x00007ffff7c8bfe5 <+341>: jmp 0x7ffff7c8bef5 <_IO_new_file_close_it+101>
End of assembler dump.

我们接着走

1
2
3
4
5
6
7
0x7ffff7c8be90 <_IO_file_close_it>       endbr64
0x7ffff7c8be94 <_IO_file_close_it+4> push r12
0x7ffff7c8be96 <_IO_file_close_it+6> push rbp
0x7ffff7c8be97 <_IO_file_close_it+7> push rbx
0x7ffff7c8be98 <_IO_file_close_it+8> mov r12d, dword ptr [rdi + 0x70] R12D, [0x555555559310] => 3
0x7ffff7c8be9c <_IO_file_close_it+12> cmp r12d, -1 3 - -1 EFLAGS => 0x213 [ CF pf AF zf sf IF df of ac ]
0x7ffff7c8bea0 <_IO_file_close_it+16> ✘ je _IO_file_close_it+252 <_IO_file_close_it+252>

这里是判断文件处于打开状态,直接比较_fileno域,确保不是-1
否则直接返回

1
2
3
4
5
0x00007ffff7c8bf8c <+252>:   mov    eax,r12d
0x00007ffff7c8bf8f <+255>: pop rbx
0x00007ffff7c8bf90 <+256>: pop rbp
0x00007ffff7c8bf91 <+257>: pop r12
0x00007ffff7c8bf93 <+259>: ret

接着判断是不是输出缓冲区

1
2
3
4
5
6
0x7ffff7c8bea6 <_IO_file_close_it+22>     mov    eax, dword ptr [rdi]             EAX, [0x5555555592a0] => 0xfbad2c04
0x7ffff7c8bea8 <_IO_file_close_it+24> mov rbx, rdi RBX => 0x5555555592a0 ◂— 0xfbad2c04
0x7ffff7c8beab <_IO_file_close_it+27> xor ebp, ebp EBP => 0
0x7ffff7c8bead <_IO_file_close_it+29> and eax, 0x808 EAX => 0x800 (0xfbad2c04 & 0x808)
0x7ffff7c8beb2 <_IO_file_close_it+34> cmp eax, 0x800 0x800 - 0x800 EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c8beb7 <_IO_file_close_it+39> ✔ je _IO_file_close_it+264 <_IO_file_close_it+264>

如果是的话(我们这里是),则调用_IO_do_flush刷新此时的输出缓冲区,_IO_do_flush也是一个宏定义

1
2
3
4
5
6
7
8
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
# define _IO_do_flush(_f) \
((_f)->_mode <= 0 \
? _IO_do_write(_f, (_f)->_IO_write_base, \
(_f)->_IO_write_ptr-(_f)->_IO_write_base) \
: _IO_wdo_write(_f, (_f)->_wide_data->_IO_write_base, \
((_f)->_wide_data->_IO_write_ptr \
- (_f)->_wide_data->_IO_write_base)))

可以看到它对应的是调用_IO_do_write函数去输出此时的输出缓冲区

1
2
3
4
5
6
7
8
0x7ffff7c8bf98 <_IO_file_close_it+264>    mov    edx, dword ptr [rdi + 0xc0]     EDX, [0x555555559360] => 0xffffffff
0x7ffff7c8bf9e <_IO_file_close_it+270> test edx, edx 0xffffffff & 0xffffffff EFLAGS => 0x286 [ cf PF af zf SF IF df of ac ]
0x7ffff7c8bfa0 <_IO_file_close_it+272> ✔ jle _IO_file_close_it+312 <_IO_file_close_it+312>

0x7ffff7c8bfc8 <_IO_file_close_it+312> mov rsi, qword ptr [rdi + 0x20] RSI, [0x5555555592c0] => 0x5555555594b0 ◂— 'wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww'
0x7ffff7c8bfcc <_IO_file_close_it+316> mov rdx, qword ptr [rdi + 0x28] RDX, [0x5555555592c8] => 0x5555555594d0 ◂— 0
0x7ffff7c8bfd0 <_IO_file_close_it+320> sub rdx, rsi RDX => 32 (0x5555555594d0 - 0x5555555594b0)
0x00007ffff7c8bfd3 <+323>: call 0x7ffff7c8c930 <_IO_new_do_write>

放上反汇编结果方便分析

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
pwndbg> disass
Dump of assembler code for function _IO_new_do_write:
0x00007ffff7c8c930 <+0>: endbr64
0x00007ffff7c8c934 <+4>: xor eax,eax
0x00007ffff7c8c936 <+6>: test rdx,rdx
0x00007ffff7c8c939 <+9>: jne 0x7ffff7c8c940 <_IO_new_do_write+16>
0x00007ffff7c8c93b <+11>: ret
0x00007ffff7c8c93c <+12>: nop DWORD PTR [rax+0x0]
0x00007ffff7c8c940 <+16>: push r15
0x00007ffff7c8c942 <+18>: lea r15,[rip+0x18a0b7] # 0x7ffff7e16a00 <_IO_helper_jumps>
0x00007ffff7c8c949 <+25>: push r14
0x00007ffff7c8c94b <+27>: push r13
0x00007ffff7c8c94d <+29>: lea r13,[rip+0x18ae14] # 0x7ffff7e17768
0x00007ffff7c8c954 <+36>: push r12
0x00007ffff7c8c956 <+38>: sub r13,r15
0x00007ffff7c8c959 <+41>: mov r12,rsi
0x00007ffff7c8c95c <+44>: push rbp
0x00007ffff7c8c95d <+45>: mov rbp,rdx
0x00007ffff7c8c960 <+48>: push rbx
0x00007ffff7c8c961 <+49>: mov rbx,rdi
0x00007ffff7c8c964 <+52>: sub rsp,0x8
0x00007ffff7c8c968 <+56>: mov r14,QWORD PTR [rdi+0xd8]
0x00007ffff7c8c96f <+63>: test DWORD PTR [rdi],0x1000
0x00007ffff7c8c975 <+69>: jne 0x7ffff7c8ca40 <_IO_new_do_write+272>
0x00007ffff7c8c97b <+75>: mov rax,QWORD PTR [rdi+0x10]
0x00007ffff7c8c97f <+79>: mov rsi,QWORD PTR [rdi+0x20]
0x00007ffff7c8c983 <+83>: cmp rax,rsi
0x00007ffff7c8c986 <+86>: je 0x7ffff7c8c9c5 <_IO_new_do_write+149>
0x00007ffff7c8c988 <+88>: mov rdx,r14
0x00007ffff7c8c98b <+91>: sub rdx,r15
0x00007ffff7c8c98e <+94>: cmp r13,rdx
0x00007ffff7c8c991 <+97>: jbe 0x7ffff7c8ca90 <_IO_new_do_write+352>
0x00007ffff7c8c997 <+103>: mov edx,0x1
0x00007ffff7c8c99c <+108>: sub rsi,rax
0x00007ffff7c8c99f <+111>: mov rdi,rbx
0x00007ffff7c8c9a2 <+114>: call QWORD PTR [r14+0x80]
0x00007ffff7c8c9a9 <+121>: mov rdx,rax
0x00007ffff7c8c9ac <+124>: mov eax,0xffffffff
0x00007ffff7c8c9b1 <+129>: cmp rdx,0xffffffffffffffff
0x00007ffff7c8c9b5 <+133>: je 0x7ffff7c8ca2a <_IO_new_do_write+250>
0x00007ffff7c8c9b7 <+135>: mov QWORD PTR [rbx+0x90],rdx
0x00007ffff7c8c9be <+142>: mov r14,QWORD PTR [rbx+0xd8]
0x00007ffff7c8c9c5 <+149>: mov rax,r14
0x00007ffff7c8c9c8 <+152>: sub rax,r15
0x00007ffff7c8c9cb <+155>: cmp rax,r13
0x00007ffff7c8c9ce <+158>: jae 0x7ffff7c8ca80 <_IO_new_do_write+336>
0x00007ffff7c8c9d4 <+164>: mov rdx,rbp
0x00007ffff7c8c9d7 <+167>: mov rsi,r12
0x00007ffff7c8c9da <+170>: mov rdi,rbx
0x00007ffff7c8c9dd <+173>: call QWORD PTR [r14+0x78]
0x00007ffff7c8c9e1 <+177>: mov r13,rax
0x00007ffff7c8c9e4 <+180>: movzx eax,WORD PTR [rbx+0x80]
0x00007ffff7c8c9eb <+187>: test r13,r13
0x00007ffff7c8c9ee <+190>: je 0x7ffff7c8c9f5 <_IO_new_do_write+197>
0x00007ffff7c8c9f0 <+192>: test ax,ax
0x00007ffff7c8c9f3 <+195>: jne 0x7ffff7c8ca60 <_IO_new_do_write+304>
0x00007ffff7c8c9f5 <+197>: mov rax,QWORD PTR [rbx+0x38]
0x00007ffff7c8c9f9 <+201>: mov edx,DWORD PTR [rbx+0xc0]
0x00007ffff7c8c9ff <+207>: movq xmm0,rax
0x00007ffff7c8ca04 <+212>: mov QWORD PTR [rbx+0x28],rax
0x00007ffff7c8ca08 <+216>: punpcklqdq xmm0,xmm0
0x00007ffff7c8ca0c <+220>: movups XMMWORD PTR [rbx+0x8],xmm0
0x00007ffff7c8ca10 <+224>: movups XMMWORD PTR [rbx+0x18],xmm0
0x00007ffff7c8ca14 <+228>: test edx,edx
0x00007ffff7c8ca16 <+230>: jle 0x7ffff7c8ca50 <_IO_new_do_write+288>
0x00007ffff7c8ca18 <+232>: mov rax,QWORD PTR [rbx+0x40]
0x00007ffff7c8ca1c <+236>: mov QWORD PTR [rbx+0x30],rax
0x00007ffff7c8ca20 <+240>: xor eax,eax
0x00007ffff7c8ca22 <+242>: cmp rbp,r13
0x00007ffff7c8ca25 <+245>: setne al
0x00007ffff7c8ca28 <+248>: neg eax
0x00007ffff7c8ca2a <+250>: add rsp,0x8
0x00007ffff7c8ca2e <+254>: pop rbx
0x00007ffff7c8ca2f <+255>: pop rbp
0x00007ffff7c8ca30 <+256>: pop r12
0x00007ffff7c8ca32 <+258>: pop r13
0x00007ffff7c8ca34 <+260>: pop r14
0x00007ffff7c8ca36 <+262>: pop r15
0x00007ffff7c8ca38 <+264>: ret
0x00007ffff7c8ca39 <+265>: nop DWORD PTR [rax+0x0]
0x00007ffff7c8ca40 <+272>: mov QWORD PTR [rdi+0x90],0xffffffffffffffff
0x00007ffff7c8ca4b <+283>: jmp 0x7ffff7c8c9c5 <_IO_new_do_write+149>
0x00007ffff7c8ca50 <+288>: test DWORD PTR [rbx],0x202
0x00007ffff7c8ca56 <+294>: jne 0x7ffff7c8ca1c <_IO_new_do_write+236>
0x00007ffff7c8ca58 <+296>: jmp 0x7ffff7c8ca18 <_IO_new_do_write+232>
0x00007ffff7c8ca5a <+298>: nop WORD PTR [rax+rax*1+0x0]
0x00007ffff7c8ca60 <+304>: lea edi,[rax-0x1]
0x00007ffff7c8ca63 <+307>: mov edx,r13d
0x00007ffff7c8ca66 <+310>: mov rsi,r12
0x00007ffff7c8ca69 <+313>: call 0x7ffff7c8e8b0 <__GI__IO_adjust_column>
0x00007ffff7c8ca6e <+318>: add eax,0x1
0x00007ffff7c8ca71 <+321>: mov WORD PTR [rbx+0x80],ax
0x00007ffff7c8ca78 <+328>: jmp 0x7ffff7c8c9f5 <_IO_new_do_write+197>
0x00007ffff7c8ca7d <+333>: nop DWORD PTR [rax]
0x00007ffff7c8ca80 <+336>: call 0x7ffff7c89ef0 <_IO_vtable_check>
0x00007ffff7c8ca85 <+341>: jmp 0x7ffff7c8c9d4 <_IO_new_do_write+164>
0x00007ffff7c8ca8a <+346>: nop WORD PTR [rax+rax*1+0x0]
0x00007ffff7c8ca90 <+352>: call 0x7ffff7c89ef0 <_IO_vtable_check>
0x00007ffff7c8ca95 <+357>: mov rsi,QWORD PTR [rbx+0x20]
0x00007ffff7c8ca99 <+361>: mov rax,QWORD PTR [rbx+0x10]
0x00007ffff7c8ca9d <+365>: jmp 0x7ffff7c8c997 <_IO_new_do_write+103>
End of assembler dump.

这个函数先判断输出缓冲区有无数据需要输出(这一部分在fwrite函数分析过,但那时不需要输出)

1
2
3
4
0x7ffff7c8c930 <_IO_do_write>       endbr64
0x7ffff7c8c934 <_IO_do_write+4> xor eax, eax EAX => 0
0x7ffff7c8c936 <_IO_do_write+6> test rdx, rdx 0x20 & 0x20 EFLAGS => 0x202 [ cf pf af zf sf IF df of ac ]
0x7ffff7c8c939 <_IO_do_write+9> ✔ jne _IO_do_write+16 <_IO_do_write+16>

我们这里刚调用过fwrite函数,所以缓冲区是需要刷新的,所以继续往下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0x7ffff7c8c940 <_IO_do_write+16>    push   r15
0x7ffff7c8c942 <_IO_do_write+18> lea r15, [rip + 0x18a0b7] R15 => 0x7ffff7e16a00 (_IO_helper_jumps) ◂— 0
0x7ffff7c8c949 <_IO_do_write+25> push r14
0x7ffff7c8c94b <_IO_do_write+27> push r13
0x7ffff7c8c94d <_IO_do_write+29> lea r13, [rip + 0x18ae14] R13 => 0x7ffff7e17768 ◂— 0
0x7ffff7c8c954 <_IO_do_write+36> push r12
0x7ffff7c8c956 <_IO_do_write+38> sub r13, r15 R13 => 0xd68 (0x7ffff7e17768 - 0x7ffff7e16a00)
0x7ffff7c8c959 <_IO_do_write+41> mov r12, rsi R12 => 0x5555555594b0 ◂— 'wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww'
0x7ffff7c8c95c <_IO_do_write+44> push rbp
0x7ffff7c8c95d <_IO_do_write+45> mov rbp, rdx RBP => 0x20
0x7ffff7c8c960 <_IO_do_write+48> push rbx
0x7ffff7c8c961 <_IO_do_write+49> mov rbx, rdi RBX => 0x5555555592a0 ◂— 0xfbad2c04
0x7ffff7c8c964 <_IO_do_write+52> sub rsp, 8 RSP => 0x7fffffffdc00 (0x7fffffffdc08 - 0x8)
0x7ffff7c8c968 <_IO_do_write+56> mov r14, qword ptr [rdi + 0xd8] R14, [0x555555559378] => 0x7ffff7e17600 (_IO_file_jumps) ◂— 0
0x7ffff7c8c96f <_IO_do_write+63> test dword ptr [rdi], 0x1000 0xfbad2c04 & 0x1000 EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c8c975 <_IO_do_write+69> ✘ jne _IO_do_write+272 <_IO_do_write+272>

这里是vtable的安全检查,不再赘述,继续往下

1
2
3
4
5
6
7
8
9
0x7ffff7c8c97b <_IO_do_write+75>     mov    rax, qword ptr [rdi + 0x10]     RAX, [0x5555555592b0] => 0x5555555594b0 ◂— 'wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww'
0x7ffff7c8c97f <_IO_do_write+79> mov rsi, qword ptr [rdi + 0x20] RSI, [0x5555555592c0] => 0x5555555594b0 ◂— 'wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww'
0x7ffff7c8c983 <_IO_do_write+83> cmp rax, rsi 0x5555555594b0 - 0x5555555594b0 EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c8c986 <_IO_do_write+86> ✔ je _IO_do_write+149 <_IO_do_write+149>

0x7ffff7c8c9c5 <_IO_do_write+149> mov rax, r14 RAX => 0x7ffff7e17600 (_IO_file_jumps) ◂— 0
0x7ffff7c8c9c8 <_IO_do_write+152> sub rax, r15 RAX => 0xc00 (0x7ffff7e17600 - 0x7ffff7e16a00)
0x7ffff7c8c9cb <_IO_do_write+155> cmp rax, r13 0xc00 - 0xd68 EFLAGS => 0x293 [ CF pf AF zf SF IF df of ac ]
0x7ffff7c8c9ce <_IO_do_write+158> ✘ jae _IO_do_write+336 <_IO_do_write+336>

往下

1
2
3
4
0x7ffff7c8c9d4 <_IO_do_write+164>    mov    rdx, rbp     RDX => 0x20
0x7ffff7c8c9d7 <_IO_do_write+167> mov rsi, r12 RSI => 0x5555555594b0 ◂— 'wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww'
0x7ffff7c8c9da <_IO_do_write+170> mov rdi, rbx RDI => 0x5555555592a0 ◂— 0xfbad2c04
0x7ffff7c8c9dd <_IO_do_write+173> call qword ptr [r14 + 0x78] <_IO_file_write>

这里调用了vtable->_IO_file_write来输出缓冲区数据以刷新缓冲区(我们这里fp->_IO_write_basefp->_IO_write_ptr间是有数据的)
跟进

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
pwndbg> disass
Dump of assembler code for function _IO_new_file_write:
=> 0x00007ffff7c8aec0 <+0>: endbr64
0x00007ffff7c8aec4 <+4>: push r13
0x00007ffff7c8aec6 <+6>: mov r13,rdi
0x00007ffff7c8aec9 <+9>: push r12
0x00007ffff7c8aecb <+11>: push rbp
0x00007ffff7c8aecc <+12>: push rbx
0x00007ffff7c8aecd <+13>: sub rsp,0x8
0x00007ffff7c8aed1 <+17>: test rdx,rdx
0x00007ffff7c8aed4 <+20>: jle 0x7ffff7c8af58 <_IO_new_file_write+152>
0x00007ffff7c8aeda <+26>: mov rbp,rsi
0x00007ffff7c8aedd <+29>: mov r12,rdx
0x00007ffff7c8aee0 <+32>: mov rbx,rdx
0x00007ffff7c8aee3 <+35>: jmp 0x7ffff7c8aefd <_IO_new_file_write+61>
0x00007ffff7c8aee5 <+37>: nop DWORD PTR [rax]
0x00007ffff7c8aee8 <+40>: call 0x7ffff7d14870 <__GI___libc_write>
0x00007ffff7c8aeed <+45>: test rax,rax
0x00007ffff7c8aef0 <+48>: js 0x7ffff7c8af20 <_IO_new_file_write+96>
0x00007ffff7c8aef2 <+50>: sub rbx,rax
0x00007ffff7c8aef5 <+53>: add rbp,rax
0x00007ffff7c8aef8 <+56>: test rbx,rbx
0x00007ffff7c8aefb <+59>: jle 0x7ffff7c8af50 <_IO_new_file_write+144>
0x00007ffff7c8aefd <+61>: mov edi,DWORD PTR [r13+0x70]
0x00007ffff7c8af01 <+65>: mov rdx,rbx
0x00007ffff7c8af04 <+68>: mov rsi,rbp
0x00007ffff7c8af07 <+71>: test BYTE PTR [r13+0x74],0x2
0x00007ffff7c8af0c <+76>: je 0x7ffff7c8aee8 <_IO_new_file_write+40>
0x00007ffff7c8af0e <+78>: call 0x7ffff7d19b90 <__GI___write_nocancel>
0x00007ffff7c8af13 <+83>: test rax,rax
0x00007ffff7c8af16 <+86>: jns 0x7ffff7c8aef2 <_IO_new_file_write+50>
0x00007ffff7c8af18 <+88>: nop DWORD PTR [rax+rax*1+0x0]
0x00007ffff7c8af20 <+96>: or DWORD PTR [r13+0x0],0x20
0x00007ffff7c8af25 <+101>: mov rax,r12
0x00007ffff7c8af28 <+104>: sub rax,rbx
0x00007ffff7c8af2b <+107>: mov rdx,QWORD PTR [r13+0x90]
0x00007ffff7c8af32 <+114>: test rdx,rdx
0x00007ffff7c8af35 <+117>: js 0x7ffff7c8af41 <_IO_new_file_write+129>
0x00007ffff7c8af37 <+119>: add rdx,rax
0x00007ffff7c8af3a <+122>: mov QWORD PTR [r13+0x90],rdx
0x00007ffff7c8af41 <+129>: add rsp,0x8
0x00007ffff7c8af45 <+133>: pop rbx
0x00007ffff7c8af46 <+134>: pop rbp
0x00007ffff7c8af47 <+135>: pop r12
0x00007ffff7c8af49 <+137>: pop r13
0x00007ffff7c8af4b <+139>: ret
0x00007ffff7c8af4c <+140>: nop DWORD PTR [rax+0x0]
0x00007ffff7c8af50 <+144>: mov rax,r12
0x00007ffff7c8af53 <+147>: sub rax,rbx
0x00007ffff7c8af56 <+150>: jmp 0x7ffff7c8af2b <_IO_new_file_write+107>
0x00007ffff7c8af58 <+152>: xor eax,eax
0x00007ffff7c8af5a <+154>: jmp 0x7ffff7c8af2b <_IO_new_file_write+107>
End of assembler dump.

仍然是判断需要输出的数据长度是否为0

1
2
3
4
5
6
7
8
0x7ffff7c8aec4 <_IO_file_write+4>     push   r13
0x7ffff7c8aec6 <_IO_file_write+6> mov r13, rdi R13 => 0x5555555592a0 ◂— 0xfbad2c04
0x7ffff7c8aec9 <_IO_file_write+9> push r12
0x7ffff7c8aecb <_IO_file_write+11> push rbp
0x7ffff7c8aecc <_IO_file_write+12> push rbx
0x7ffff7c8aecd <_IO_file_write+13> sub rsp, 8 RSP => 0x7fffffffdbd0 (0x7fffffffdbd8 - 0x8)
0x7ffff7c8aed1 <_IO_file_write+17> test rdx, rdx 0x20 & 0x20 EFLAGS => 0x202 [ cf pf af zf sf IF df of ac ]
0x7ffff7c8aed4 <_IO_file_write+20> ✘ jle _IO_file_write+152 <_IO_file_write+152>

为0则直接返回,往下走

1
2
3
4
5
6
7
8
9
10
11
0x7ffff7c8aeda <_IO_file_write+26>    mov    rbp, rsi     RBP => 0x5555555594b0 ◂— 'wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww'
0x7ffff7c8aedd <_IO_file_write+29> mov r12, rdx R12 => 0x20
0x7ffff7c8aee3 <_IO_file_write+35>: jmp 0x7ffff7c8aefd <_IO_new_file_write+61>

0x7ffff7c8aefd <_IO_file_write+61> mov edi, dword ptr [r13 + 0x70] EDI, [0x555555559310] => 3
0x7ffff7c8af01 <_IO_file_write+65> mov rdx, rbx RDX => 0x20
0x7ffff7c8af04 <_IO_file_write+68> mov rsi, rbp RSI => 0x5555555594b0 ◂— 'wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww'
0x7ffff7c8af07 <_IO_file_write+71> test byte ptr [r13 + 0x74], 2 0 & 2 EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c8af0c <_IO_file_write+76> ✔ je _IO_file_write+40 <_IO_file_write+40>

0x7ffff7c8aee8 <_IO_file_write+40> call write <write>

后续就是系统调用write来将输出缓冲区的数据输出到目标文件

1
2
3
4
5
6
7
0x7ffff7d14870 <write>       endbr64
0x7ffff7d14874 <write+4> mov eax, dword ptr fs:[0x18] EAX, [0x7ffff7fb1758] => 0
0x7ffff7d1487c <write+12> test eax, eax 0 & 0 EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7d1487e <write+14> ✘ jne write+32 <write+32>

0x7ffff7d14880 <write+16> mov eax, 1 EAX => 1
0x7ffff7d14885 <write+21> syscall <SYS_write>

后续回到_IO_do_write

1
2
3
4
5
6
7
8
9
10
11
12
0x7ffff7c8c9f5 <_IO_do_write+197>    mov    rax, qword ptr [rbx + 0x38]     RAX, [0x5555555592d8] => 0x5555555594b0 ◂— 'wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww'
0x7ffff7c8c9f9 <_IO_do_write+201> mov edx, dword ptr [rbx + 0xc0] EDX, [0x555555559360] => 0xffffffff
0x7ffff7c8c9ff <_IO_do_write+207> movq xmm0, rax XMM0 => 0x5555555594b0 ◂— 'wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww'
0x7ffff7c8ca04 <_IO_do_write+212> mov qword ptr [rbx + 0x28], rax [0x5555555592c8] <= 0x5555555594b0 ◂— 'wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww'
0x7ffff7c8ca08 <_IO_do_write+216> punpcklqdq xmm0, xmm0
0x7ffff7c8ca0c <_IO_do_write+220> movups xmmword ptr [rbx + 8], xmm0
0x7ffff7c8ca10 <_IO_do_write+224> movups xmmword ptr [rbx + 0x18], xmm0
0x7ffff7c8ca14 <_IO_do_write+228> test edx, edx 0xffffffff & 0xffffffff EFLAGS => 0x286 [ cf PF af zf SF IF df of ac ]
0x7ffff7c8ca16 <_IO_do_write+230> ✔ jle _IO_do_write+288 <_IO_do_write+288>

0x7ffff7c8ca50 <_IO_do_write+288> test dword ptr [rbx], 0x202 0xfbad2c04 & 0x202 EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c8ca56 <_IO_do_write+294> ✘ jne _IO_do_write+236 <_IO_do_write+236>

看到刷新了write指针

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
pwndbg> p *(struct _IO_FILE_plus*) 0x5555555592a0
$7 = {
file = {
_flags = -72537084,
_IO_read_ptr = 0x5555555594b0 'w' <repeats 32 times>,
_IO_read_end = 0x5555555594b0 'w' <repeats 32 times>,
_IO_read_base = 0x5555555594b0 'w' <repeats 32 times>,
_IO_write_base = 0x5555555594b0 'w' <repeats 32 times>,
_IO_write_ptr = 0x5555555594b0 'w' <repeats 32 times>,
_IO_write_end = 0x55555555a4b0 "",
_IO_buf_base = 0x5555555594b0 'w' <repeats 32 times>,
_IO_buf_end = 0x55555555a4b0 "",
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7e1b6a0 <_IO_2_1_stderr_>,
_fileno = 3,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x555555559380,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x555555559390,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7e17600 <_IO_file_jumps>
}

再往后

1
2
3
4
5
6
7
8
0x7ffff7c8bece <_IO_file_close_it+62>    mov    r12, qword ptr [rbx + 0xd8]     R12, [0x555555559378] => 0x7ffff7e17600 (_IO_file_jumps) ◂— 0
0x7ffff7c8bed5 <_IO_file_close_it+69> lea rdx, [rip + 0x18ab24] RDX => 0x7ffff7e16a00 (_IO_helper_jumps) ◂— 0
0x7ffff7c8bedc <_IO_file_close_it+76> lea rax, [rip + 0x18b885] RAX => 0x7ffff7e17768 ◂— 0
0x7ffff7c8bee3 <_IO_file_close_it+83> sub rax, rdx RAX => 0xd68 (0x7ffff7e17768 - 0x7ffff7e16a00)
0x7ffff7c8bee6 <_IO_file_close_it+86> mov rcx, r12 RCX => 0x7ffff7e17600 (_IO_file_jumps) ◂— 0
0x7ffff7c8bee9 <_IO_file_close_it+89> sub rcx, rdx RCX => 0xc00 (0x7ffff7e17600 - 0x7ffff7e16a00)
0x7ffff7c8beec <_IO_file_close_it+92> cmp rax, rcx 0xd68 - 0xc00 EFLAGS => 0x202 [ cf pf af zf sf IF df of ac ]
0x00007ffff7c8beef <+95>: jbe 0x7ffff7c8bfe0 <_IO_new_file_close_it+336>

仍然是vtable的安全检查机制,<_IO_new_file_close_it+336>处就是

1
0x00007ffff7c8bfe0 <+336>:   call   0x7ffff7c89ef0 <_IO_vtable_check>

不再赘述,后续调用vtable->_IO_file_close函数

1
2
0x7ffff7c8bef5 <_IO_file_close_it+101>    mov    rdi, rbx     RDI => 0x5555555592a0 ◂— 0xfbad2c04
0x7ffff7c8bef8 <_IO_file_close_it+104> call qword ptr [r12 + 0x88] <_IO_file_close>

调用__close_nocancel函数

1
2
3
4
5
6
7
0x7ffff7c8a590 <_IO_file_close>         endbr64
0x7ffff7c8a594 <_IO_file_close+4> mov edi, dword ptr [rdi + 0x70] EDI, [0x555555559310] => 3
0x7ffff7c8a597 <_IO_file_close+7> jmp __close_nocancel <__close_nocancel>

0x7ffff7d19860 <__close_nocancel> endbr64
0x7ffff7d19864 <__close_nocancel+4> mov eax, 3 EAX => 3
0x7ffff7d19869 <__close_nocancel+9> syscall <SYS_close>

__close_nocancel直接系统调用close函数来关闭文件描述符
后续回到_IO_file_close_it,调用_IO_setb函数

1
2
3
4
5
0x7ffff7c8bf47 <_IO_file_close_it+183>    xor    ecx, ecx     ECX => 0
0x7ffff7c8bf49 <_IO_file_close_it+185> xor edx, edx EDX => 0
0x7ffff7c8bf4b <_IO_file_close_it+187> xor esi, esi ESI => 0
0x7ffff7c8bf4d <_IO_file_close_it+189> mov rdi, rbx RDI => 0x5555555592a0 ◂— 0xfbad2c04
0x7ffff7c8bf50 <_IO_file_close_it+192> call _IO_setb <_IO_setb>

这里_IO_setb还调用free函数释放了缓冲区

1
2
0x7ffff7c8dc74 <_IO_setb+84>    call   free@plt                    <free@plt>
ptr: 0x5555555594b0 ◂— 'wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww'

_IO_setb结束后可以看到

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
pwndbg> p *(struct _IO_FILE_plus*) 0x5555555592a0
$10 = {
file = {
_flags = -72537083,
_IO_read_ptr = 0x5555555594b0 'w' <repeats 32 times>,
_IO_read_end = 0x5555555594b0 'w' <repeats 32 times>,
_IO_read_base = 0x5555555594b0 'w' <repeats 32 times>,
_IO_write_base = 0x5555555594b0 'w' <repeats 32 times>,
_IO_write_ptr = 0x5555555594b0 'w' <repeats 32 times>,
_IO_write_end = 0x55555555a4b0 "",
_IO_buf_base = 0x0,
_IO_buf_end = 0x0,
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7e1b6a0 <_IO_2_1_stderr_>,
_fileno = 3,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x555555559380,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x555555559390,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7e17600 <_IO_file_jumps>
}

_IO_buf_base,_IO_buf_end都已经清空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x555555559000
Size: 0x290 (with flag bits: 0x291)

Allocated chunk | PREV_INUSE
Addr: 0x555555559290
Size: 0x1e0 (with flag bits: 0x1e1)

Allocated chunk | PREV_INUSE
Addr: 0x555555559470
Size: 0x30 (with flag bits: 0x31)

Top chunk | PREV_INUSE
Addr: 0x5555555594a0
Size: 0x20b60 (with flag bits: 0x20b61)

对应的缓冲区chunk也被释放(这里被topchunk合并)
然后刷新读写指针
将指针全部清空了(xmmword是16字节单元)

1
2
3
4
5
0x7ffff7c8bf55 <_IO_file_close_it+197>    pxor   xmm0, xmm0
0x7ffff7c8bf59 <_IO_file_close_it+201> mov rdi, rbx RDI => 0x5555555592a0 ◂— 0xfbad2c05
0x7ffff7c8bf5c <_IO_file_close_it+204> movups xmmword ptr [rbx + 8], xmm0
0x7ffff7c8bf60 <_IO_file_close_it+208> movups xmmword ptr [rbx + 0x18], xmm0
0x7ffff7c8bf64 <_IO_file_close_it+212> movups xmmword ptr [rbx + 0x28], xmm0

后续再次调用_IO_un_link确保从_IO_list_all中脱离

1
2
0x7ffff7c8bf68 <_IO_file_close_it+216>    call   _IO_un_link                 <_IO_un_link>
rdi: 0x5555555592a0 ◂— 0xfbad2c05

再往下

1
2
3
4
5
0x7ffff7c8bf6d <_IO_file_close_it+221>    test   r12d, r12d                                     0 & 0     EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c8bf70 <_IO_file_close_it+224> mov dword ptr [rbx], 0xfbad240c [0x5555555592a0] <= 0xfbad240c
0x7ffff7c8bf76 <_IO_file_close_it+230> mov dword ptr [rbx + 0x70], 0xffffffff [0x555555559310] <= 0xffffffff
0x7ffff7c8bf7d <_IO_file_close_it+237> ✔ cmove r12d, ebp
0x7ffff7c8bf81 <_IO_file_close_it+241> mov qword ptr [rbx + 0x90], 0xffffffffffffffff [0x555555559330] <= 0xffffffffffffffff

设置_flags,_fileno_offset

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
pwndbg> p *(struct _IO_FILE_plus*) 0x5555555592a0
$11 = {
file = {
_flags = -72539124,
_IO_read_ptr = 0x0,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_IO_write_end = 0x0,
_IO_buf_base = 0x0,
_IO_buf_end = 0x0,
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7e1b6a0 <_IO_2_1_stderr_>,
_fileno = -1,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x555555559380,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x555555559390,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7e17600 <_IO_file_jumps>
}

可以看到指针全部清空, _offset_fileno都被设置为-1
最后回到_IO_new_fclose函数,再对vtable安全检查后,调用vtable->_IO_file_finish函数

1
2
3
0x7ffff7c7ecd9 <fclose+105>    xor    esi, esi     ESI => 0
0x7ffff7c7ecdb <fclose+107> mov rdi, rbp RDI => 0x5555555592a0 ◂— 0xfbad240c
0x7ffff7c7ecde <fclose+110> call qword ptr [rbx + 0x10] <_IO_file_finish>

函数首先检查了文件描述符是否打开,其已经被设置为-1,所以不会进入该流程。如果文件打开的话则会调用_IO_do_flush_IO_SYSCLOSE刷新缓冲区以及关闭文件。

1
2
0x7ffff7c8bffd <_IO_file_finish+13>     cmp    dword ptr [rdi + 0x70], -1     0xffffffff - -0x1     EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c8c001 <_IO_file_finish+17> ✔ je _IO_file_finish+106 <_IO_file_finish+106>

接着调用_IO_default_finish确认缓冲区确实被释放,以及结构体从_IO_list_all中取了下来,并设置指针

1
2
3
4
5
6
0x7ffff7c8c05a <_IO_file_finish+106>    add    rsp, 8       RSP => 0x7fffffffdc48 (0x7fffffffdc40 + 0x8)
0x7ffff7c8c05e <_IO_file_finish+110> mov rdi, rbp RDI => 0x5555555592a0 ◂— 0xfbad240c
0x7ffff7c8c061 <_IO_file_finish+113> xor esi, esi ESI => 0
0x7ffff7c8c063 <_IO_file_finish+115> pop rbx RBX => 0x7ffff7e17600 (_IO_file_jumps)
0x7ffff7c8c064 <_IO_file_finish+116> pop rbp RBP => 0x5555555592a0
0x7ffff7c8c065 <_IO_file_finish+117> jmp _IO_default_finish <_IO_default_finish>

可以看到判断[rdi + 0x38]也就是_IO_buf_base是否为0,确认缓冲区确实被释放

1
2
3
4
5
0x7ffff7c8e734 <_IO_default_finish+4>     push   rbp
0x7ffff7c8e735 <_IO_default_finish+5> mov rbp, rdi RBP => 0x5555555592a0 ◂— 0xfbad240c
0x7ffff7c8e738 <_IO_default_finish+8> mov rdi, qword ptr [rdi + 0x38] RDI, [0x5555555592d8] => 0
0x7ffff7c8e73c <_IO_default_finish+12> test rdi, rdi 0 & 0 EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c8e73f <_IO_default_finish+15> ✔ je _IO_default_finish+23 <_IO_default_finish+23>

接着又检查了很多域

1
2
3
4
5
6
7
8
9
10
0x7ffff7c8e747 <_IO_default_finish+23>    mov    rax, qword ptr [rbp + 0x60]     RAX, [0x555555559300] => 0
0x7ffff7c8e74b <_IO_default_finish+27> test rax, rax 0 & 0 EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c8e74e <_IO_default_finish+30> ✔ je _IO_default_finish+48 <_IO_default_finish+48>

0x7ffff7c8e760 <_IO_default_finish+48> mov rdi, qword ptr [rbp + 0x48] RDI, [0x5555555592e8] => 0
0x7ffff7c8e764 <_IO_default_finish+52> test rdi, rdi 0 & 0 EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c8e767 <_IO_default_finish+55> ✔ je _IO_default_finish+70 <_IO_default_finish+70>

0x7ffff7c8e776 <_IO_default_finish+70> test byte ptr [rbp], 0x80 0xc & 0x80 EFLAGS => 0x246 [ cf PF af ZF sf IF df of ac ]
0x7ffff7c8e77a <_IO_default_finish+74> ✘ jne _IO_default_finish+96 <_IO_default_finish+96>

检查完后回到_IO_new_fclose,再经过一下检查判断后调用free来释放结构体的chunk
free完后fclose函数全部结束

1
2
0x7ffff7c7ed1f <fclose+175>    mov    rdi, rbp     RDI => 0x5555555592a0 ◂— 0xfbad240c
0x7ffff7c7ed22 <fclose+178> call free@plt <free@plt>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x555555559000
Size: 0x290 (with flag bits: 0x291)

Free chunk (tcachebins) | PREV_INUSE
Addr: 0x555555559290
Size: 0x1e0 (with flag bits: 0x1e1)
fd: 0x555555559

Allocated chunk | PREV_INUSE
Addr: 0x555555559470
Size: 0x30 (with flag bits: 0x31)

Top chunk | PREV_INUSE
Addr: 0x5555555594a0
Size: 0x20b60 (with flag bits: 0x20b61)

已经被free掉了,我们仍然试着查看结构体信息

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
pwndbg> p *(struct _IO_FILE_plus*) 0x5555555592a0
$12 = {
file = {
_flags = 1431655769,
_IO_read_ptr = 0x3649288fbc281a3 <error: Cannot access memory at address 0x3649288fbc281a3>,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_IO_write_end = 0x0,
_IO_buf_base = 0x0,
_IO_buf_end = 0x0,
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7e1b6a0 <_IO_2_1_stderr_>,
_fileno = -1,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x555555559380,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x555555559390,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7e17600 <_IO_file_jumps>
}

看到直到fclose结束,vatble指针和_chain指针仍然保留着,同时也有一些其他域的信息仍然留存比如_wide_data
而这里的_flags0x55555559,其实是tcachebin中这个chunk经过safe-linking后的fd

0x06 IO缓冲机制

我们会发现调试fwrite时候只写入了_IO_FILE的缓冲区,而没有直接写入文件,这是glibc的IO缓冲机制,因为系统调用read或者write的开销很大(需要从用户态切换到内核态),所以依赖缓冲区来优化
在调试fwrite时,我们发现_IO_do_write会系统调用write输出fp->_IO_write_ptrfp->_IO_write_base之间的内容,这是在缓冲区剩余空间不足或者第一次初始化分配缓冲区时候,_IO_file_overflow调用_IO_do_write做的
我们调试的时候是第一次初始化分配缓冲区,所以没有系统调用write,如果是因为缓冲区剩余空间不足时调用_IO_file_overflow,则会系统调用write输出fp->_IO_write_ptrfp->_IO_write_base之间的内容,相当于刷新了缓冲区,将数据真正写入文件中
而在调试fread时候,我们发现数据被系统调用read直接从文件读出来了,这也是因为第一次初始化分配缓冲区后,缓冲区无数据,调用_IO_file_underflow函数刷新缓冲区,系统调用read来读出了数据
综上我们发现,刷新缓冲区后,数据才真正读入或者输出,那么什么时候刷新缓冲区呢?见下图