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; #define _IO_file_flags _flags char * _IO_read_ptr; char * _IO_read_end; char * _IO_read_base; char * _IO_write_base; char * _IO_write_ptr; char * _IO_write_end; char * _IO_buf_base; char * _IO_buf_end; char *_IO_save_base; char *_IO_backup_base; char *_IO_save_end; struct _IO_marker *_markers ; struct _IO_FILE *_chain ; int _fileno; #if 0 int _blksize; #else int _flags2; #endif _IO_off_t _old_offset; #define __HAVE_COLUMN unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1 ]; _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 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; 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 #define CHECK_FILE(FILE, RET) do { if ((FILE) == NULL || ((FILE)->_flags & _IO_MAGIC_MASK) != _IO_MAGIC) { __set_errno (EINVAL); return RET; } } while (0 ) 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 #define _OLD_STDIO_MAGIC 0xFABC0000 #define _IO_MAGIC_MASK 0xFFFF0000 #define _IO_USER_BUF 1 #define _IO_UNBUFFERED 2 #define _IO_NO_READS 4 #define _IO_NO_WRITES 8 #define _IO_EOF_SEEN 0x10 #define _IO_ERR_SEEN 0x20 #define _IO_DELETE_DONT_CLOSE 0x40 #define _IO_LINKED 0x80 #define _IO_IN_BACKUP 0x100 #define _IO_LINE_BUF 0x200 #define _IO_TIED_PUT_GET 0x400 #define _IO_CURRENTLY_PUTTING 0x800 #define _IO_IS_APPENDING 0x1000 #define _IO_IS_FILEBUF 0x2000 #define _IO_BAD_SEEN 0x4000 #define _IO_USER_LOCK 0x8000
_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); 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 个全局指针 stdin
,stdout
,stderr
分别指向 _IO_2_1_stdin_
,_IO_2_1_stdout_
,_IO_2_1_stderr_
三个结构体 结构如下所示
程序在进行文件IO操作时候,便会创建_IO_FILE_plus
结构体,并使用头插法接入_IO_list_all
链表中 下面就分析一下fopen
,fread
,fwrite
,fclose
四个函数,来学习一下_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 { 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
[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_end
,rdx
是_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_WRITES
,0x2
= _IO_CURRENTLY_PUTTING
,je判断,意味着当前不是正在输出,也没禁止写入),跳转到<_IO_file_overflow+45>
,判断确定可以写入之后,调用_IO_do_write
函数来系统调用write
输出fp->_IO_write_ptr
到fp->_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_base
与fp->_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
来释放结构体的chunkfree
完后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
而这里的_flags
为0x55555559
,其实是tcachebin
中这个chunk经过safe-linking
后的fd
值
0x06 IO缓冲机制 我们会发现调试fwrite
时候只写入了_IO_FILE
的缓冲区,而没有直接写入文件,这是glibc的IO缓冲机制,因为系统调用read
或者write
的开销很大(需要从用户态切换到内核态),所以依赖缓冲区来优化 在调试fwrite
时,我们发现_IO_do_write
会系统调用write
输出fp->_IO_write_ptr
到fp->_IO_write_base
之间的内容,这是在缓冲区剩余空间不足或者第一次初始化分配缓冲区时候,_IO_file_overflow
调用_IO_do_write
做的 我们调试的时候是第一次初始化分配缓冲区,所以没有系统调用write
,如果是因为缓冲区剩余空间不足时调用_IO_file_overflow
,则会系统调用write
输出fp->_IO_write_ptr
到fp->_IO_write_base
之间的内容,相当于刷新了缓冲区,将数据真正写入文件中 而在调试fread
时候,我们发现数据被系统调用read
直接从文件读出来了,这也是因为第一次初始化分配缓冲区后,缓冲区无数据,调用_IO_file_underflow
函数刷新缓冲区,系统调用read
来读出了数据 综上我们发现,刷新缓冲区后,数据才真正读入或者输出,那么什么时候刷新缓冲区呢?见下图