要学习基于IO_FILE的堆利用就得了解它的本质,以下会介绍几个主要的IO函数,结合源码和动态调试去学习。 调试环境搭建可参考环境从零开始配置pwn环境:从零开始配置pwn环境:优化pwn虚拟机配置支持libc等指令-CSDN博客
1.在开始上源码之前,还是将fwrite的总体流程先描述一遍,好让大家有个大概的概念。
1.1 fwrite函数的总体流程图如下。
1.2 fwrite的主要实现在_IO_new_file_xsputn中,整体流程包含四个部分。
-
首先判断输出缓冲区还有多少剩余,如果有剩余则将目标输出数据拷贝到输出缓冲区。
-
如果输出缓冲区没有剩余(输出缓冲区未建立也是没有剩余)或输出缓冲区不够则调用_IO_OVERFLOW建立输出缓冲区或刷新输出缓冲区。
-
输出缓冲区刷新后判断剩余的目标输出数据是否超过块的size,如果超过块的size,则不通过输出缓冲区直接以块为单位,使用sys_write输出目标数据。
-
如果按块输出数据后还剩一点数据则调用_IO_default_xsputn将数据拷贝到输出缓冲区。
1.3 接着介绍一下其中涉及的几个IO_FILE结构体的指针。
指针 | 描述 |
---|---|
_IO_buf_base | 输入输出缓冲区基地址 |
_IO_buf_end | 输入输出缓冲区结束地址 |
_IO_write_base | 输出缓冲区基地址 |
_IO_write_ptr | 输入缓冲区当前地址 |
_IO_write_end | 输出缓冲区结束地址 |
其中_IO_buf_base和_IO_buf_end是缓冲区建立函数_IO_doallocbuf(上小结详细描述过)会在里面建立输入输出缓冲区,并把基地址保存在_IO_buf_base中,结束地址保存在_IO_buf_end中。在建立输入输出缓冲区后,如果缓冲区作为输出缓冲区使用,会将基址给_IO_write_base,结束地址给_IO_write_end,同时_IO_write_ptr表示为已经使用的地址。即_IO_write_base到_IO_write_ptr之间的空间是已经使用的缓冲区,_IO_write_ptr到_IO_write_end之间为剩余的输出缓冲区。
1.4 fwrite函数的原型
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
# ptr-- 这是指向要被写入的元素数组的指针。
# size-- 这是要被写入的每个元素的大小,以字节为单位。
# nmemb-- 这是元素的个数,每个元素的大小为 size 字节。
# stream-- 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输出流。
1.5 首先是编写一个简单的调用fwrite函数的C程序
#include<stdio.h>
#include<stdlib.h>
int main(){FILE* fp = fopen("test","wb");char *ptr = malloc(0x20);fwrite(ptr, 1, 0x20, fp);return 0;
}
2.调试fopen程序
2.1 获得可执行程序
gcc -g fwrite.c -o fwrite
2.2 编译完成之后,使用gdb进行调试,在fwrite处下断点。看到程序首先断在_IO_fwrite处。
下载相关程序并复制到相应目录https://github.com/balexios/glibc2.23/blob/master/libio/iofwrite.c
pwndbg> n
7 fwrite(ptr, 1, 0x20, fp);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────RAX 0x602240 ◂— 0x0RBX 0x0RCX 0x7ffff7dd1b20 (main_arena) ◂— 0x100000000RDX 0x602240 ◂— 0x0RDI 0x0RSI 0x602260 ◂— 0x0R8 0x4R9 0x1R10 0x4a1R11 0x7ffff7a91130 (malloc) ◂— push rbpR12 0x4004c0 (_start) ◂— xor ebp, ebpR13 0x7fffffffe6a0 ◂— 0x1R14 0x0R15 0x0RBP 0x7fffffffe5c0 —▸ 0x400610 (__libc_csu_init) ◂— push r15RSP 0x7fffffffe5b0 —▸ 0x602010 ◂— 0xfbad2484RIP 0x4005df (main+41) ◂— mov rdx, qword ptr [rbp - 0x10]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────0x4005c8 <main+18> call fopen@plt <0x400490>0x4005cd <main+23> mov qword ptr [rbp - 0x10], rax0x4005d1 <main+27> mov edi, 0x200x4005d6 <main+32> call malloc@plt <0x400480>0x4005db <main+37> mov qword ptr [rbp - 8], rax► 0x4005df <main+41> mov rdx, qword ptr [rbp - 0x10]0x4005e3 <main+45> mov rax, qword ptr [rbp - 8]0x4005e7 <main+49> mov rcx, rdx0x4005ea <main+52> mov edx, 0x200x4005ef <main+57> mov esi, 10x4005f4 <main+62> mov rdi, rax
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/wolf/iofile/fwrite.c2 #include<stdlib.h>3 int main(){4 5 FILE* fp = fopen("test","wb");6 char *ptr = malloc(0x20);► 7 fwrite(ptr, 1, 0x20, fp);8 return 0;9 }
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe5b0 —▸ 0x602010 ◂— 0xfbad2484
01:0008│ 0x7fffffffe5b8 —▸ 0x602240 ◂— 0x0
02:0010│ rbp 0x7fffffffe5c0 —▸ 0x400610 (__libc_csu_init) ◂— push r15
03:0018│ 0x7fffffffe5c8 —▸ 0x7ffff7a2d830 (__libc_start_main+240) ◂— mov edi, eax
04:0020│ 0x7fffffffe5d0 —▸ 0x7fffffffe6a8 —▸ 0x7fffffffe8db ◂— '/ctf/work/wolf/iofile/fwrite'
... ↓
06:0030│ 0x7fffffffe5e0 ◂— 0x1f7b99608
07:0038│ 0x7fffffffe5e8 —▸ 0x4005b6 (main) ◂— push rbp
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────► f 0 4005df main+41f 1 7ffff7a2d830 __libc_start_main+240
pwndbg> s
__GI__IO_fwrite (buf=0x602240, size=1, count=32, fp=0x602010) at iofwrite.c:31
warning: Source file is more recent than executable.
31 {
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────RAX 0x602240 ◂— 0x0RBX 0x0RCX 0x602010 ◂— 0xfbad2484RDX 0x20RDI 0x602240 ◂— 0x0RSI 0x1R8 0x4R9 0x1R10 0x62cR11 0x7ffff7a7b6e0 (fwrite) ◂— push r14R12 0x4004c0 (_start) ◂— xor ebp, ebpR13 0x7fffffffe6a0 ◂— 0x1R14 0x0R15 0x0RBP 0x7fffffffe5c0 —▸ 0x400610 (__libc_csu_init) ◂— push r15RSP 0x7fffffffe5a8 —▸ 0x4005fc (main+70) ◂— mov eax, 0RIP 0x7ffff7a7b6e0 (fwrite) ◂— push r14
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────► 0x7ffff7a7b6e0 <fwrite> push r140x7ffff7a7b6e2 <fwrite+2> push r130x7ffff7a7b6e4 <fwrite+4> push r120x7ffff7a7b6e6 <fwrite+6> push rbp0x7ffff7a7b6e7 <fwrite+7> push rbx0x7ffff7a7b6e8 <fwrite+8> mov rbx, rsi0x7ffff7a7b6eb <fwrite+11> imul rbx, rdx0x7ffff7a7b6ef <fwrite+15> test rbx, rbx0x7ffff7a7b6f2 <fwrite+18> je fwrite+264 <0x7ffff7a7b7e8>0x7ffff7a7b6f8 <fwrite+24> mov eax, dword ptr [rcx]0x7ffff7a7b6fa <fwrite+26> mov r12, rdi
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/wolf/iofile/iofwrite.c26 27 #include "libioP.h"28 29 _IO_size_t30 _IO_fwrite (const void *buf, _IO_size_t size, _IO_size_t count, _IO_FILE *fp)► 31 {32 _IO_size_t request = size * count;33 _IO_size_t written = 0;34 CHECK_FILE (fp, 0);35 if (request == 0)36 return 0;
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe5a8 —▸ 0x4005fc (main+70) ◂— mov eax, 0
01:0008│ 0x7fffffffe5b0 —▸ 0x602010 ◂— 0xfbad2484
02:0010│ 0x7fffffffe5b8 —▸ 0x602240 ◂— 0x0
03:0018│ rbp 0x7fffffffe5c0 —▸ 0x400610 (__libc_csu_init) ◂— push r15
04:0020│ 0x7fffffffe5c8 —▸ 0x7ffff7a2d830 (__libc_start_main+240) ◂— mov edi, eax
05:0028│ 0x7fffffffe5d0 —▸ 0x7fffffffe6a8 —▸ 0x7fffffffe8db ◂— '/ctf/work/wolf/iofile/fwrite'
... ↓
07:0038│ 0x7fffffffe5e0 ◂— 0x1f7b99608
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────► f 0 7ffff7a7b6e0 fwritef 1 4005fc main+70f 2 7ffff7a2d830 __libc_start_main+240
pwndbg>
2.3 在开始调试之前,还是先把传入的IO_FILE的fp值看一下,如下图所示。
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 = 0x0, _IO_buf_end = 0x0, _IO_save_base = 0x0, _IO_backup_base = 0x0, _IO_save_end = 0x0, _markers = 0x0, _chain = 0x7ffff7dd2540 <_IO_2_1_stderr_>, _fileno = 3, _flags2 = 0, _old_offset = 0, _cur_column = 0, _vtable_offset = 0 '\000', _shortbuf = "", _lock = 0x6020f0, _offset = -1, _codecvt = 0x0, _wide_data = 0x602100, _freeres_list = 0x0, _freeres_buf = 0x0, __pad5 = 0, _mode = 0, _unused2 = '\000' <repeats 19 times>}, vtable = 0x7ffff7dd06e0 <_IO_file_jumps>
}
pwndbg>
2.4 此时vtable中的内容如下图。
pwndbg> p *_IO_list_all->vtable
$2 = {__dummy = 0, __dummy2 = 0, __finish = 0x7ffff7a869c0 <_IO_new_file_finish>, __overflow = 0x7ffff7a87730 <_IO_new_file_overflow>, __underflow = 0x7ffff7a874a0 <_IO_new_file_underflow>, __uflow = 0x7ffff7a88600 <__GI__IO_default_uflow>, __pbackfail = 0x7ffff7a89980 <__GI__IO_default_pbackfail>, __xsputn = 0x7ffff7a861e0 <_IO_new_file_xsputn>, __xsgetn = 0x7ffff7a85ec0 <__GI__IO_file_xsgetn>, __seekoff = 0x7ffff7a854c0 <_IO_new_file_seekoff>, __seekpos = 0x7ffff7a88a00 <_IO_default_seekpos>, __setbuf = 0x7ffff7a85430 <_IO_new_file_setbuf>, __sync = 0x7ffff7a85370 <_IO_new_file_sync>, __doallocate = 0x7ffff7a7a180 <__GI__IO_file_doallocate>, __read = 0x7ffff7a861a0 <__GI__IO_file_read>, __write = 0x7ffff7a85b70 <_IO_new_file_write>, __seek = 0x7ffff7a85970 <__GI__IO_file_seek>, __close = 0x7ffff7a85340 <__GI__IO_file_close>, __stat = 0x7ffff7a85b60 <__GI__IO_file_stat>, __showmanyc = 0x7ffff7a89af0 <_IO_default_showmanyc>, __imbue = 0x7ffff7a89b00 <_IO_default_imbue>
}
pwndbg>
2.5 从图中可以看出刚经过fopen初始化,输入输出缓冲区没有建立,此时所有指针都为空。_IO_fwrite函数在文件/libio/iofwrite.c中。
_IO_size_t
_IO_fwrite (const void *buf, _IO_size_t size, _IO_size_t count, _IO_FILE *fp)
{_IO_size_t request = size * count;...if (_IO_vtable_offset (fp) != 0 || _IO_fwide (fp, -1) == -1)written = _IO_sputn (fp, (const char *) buf, request);...
}
libc_hidden_def (_IO_fwrite)
2.6 没有做过多操作就调用了_IOsputn函数,该函数是vtable中的__xsputn(_IO_new_file_xsputn)在文件/libio/fileops.c中。源码分析从四个部分进行,其中下面每部分代码都是_IO_new_file_xsputn函数中的源码。
第一部分所包含的代码如下。
_IO_size_t
_IO_new_file_xsputn (_IO_FILE *f, const void *data, _IO_size_t n)
{ _IO_size_t count = 0;
...# 判断输出缓冲区还有多少空间else if (f->_IO_write_end > f->_IO_write_ptr)count = f->_IO_write_end - f->_IO_write_ptr; /* Space available. */# 如果输出缓冲区有空间,则先把数据拷贝至输出缓冲区if (count > 0){if (count > to_do)count = to_do;...memcpy (f->_IO_write_ptr, s, count);f->_IO_write_ptr += count;# 计算是否还有目标输出数据剩余s += count;to_do -= count;
主要功能就是判断输出缓冲区还有多少空间,其中像示例程序所示的f->_IO_write_end以及f->_IO_write_ptr均为0,此时的输出缓冲区为0。
另一部分则是如果输出缓冲区仍有剩余空间的话,则将目标输出数据拷贝至输出缓冲区,并计算在输出缓冲区填满后,是否仍然剩余目标输出数据。
第二部分代码如下。
# 如果还有目标数据剩余,此时则表明输出缓冲区未建立或输出缓冲区已经满了if (to_do + must_flush > 0){_IO_size_t block_size, do_write;# 函数实现清空输出缓冲区或建立缓冲区的功能if (_IO_OVERFLOW (f, EOF) == EOF)return to_do == 0 ? EOF : n - to_do;# 检查输出数据是否是大块block_size = f->_IO_buf_end - f->_IO_buf_base;do_write = to_do - (block_size >= 128 ? to_do % block_size : 0);
2.7 经过了上一步骤,如果还有目标输出数据,表明输出缓冲区未建立或输出缓冲区已经满了,此时调用_IO_OVERFLOW函数。该函数功能主要是实现刷新输出缓冲区或建立缓冲区,它就是vtable中的__overlfow(_IO_new_file_overflow),文件在/libio/fileops.c中。
pwndbg> s
1331 if (_IO_OVERFLOW (f, EOF) == EOF)
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────RAX 0x0RBX 0x602010 ◂— 0xfbad2484RCX 0x602010 ◂— 0xfbad2484RDX 0x0RDI 0x0RSI 0x602240 ◂— 0x0R8 0x6020f0 ◂— 0x100000001R9 0x602010 ◂— 0xfbad2484R10 0x1R11 0x7ffff7a7b6e0 (fwrite) ◂— push r14R12 0x20R13 0x602240 ◂— 0x0R14 0x1R15 0x0RBP 0x602010 ◂— 0xfbad2484RSP 0x7fffffffe540 ◂— 0x1RIP 0x7ffff7a86278 (_IO_file_xsputn+152) ◂— mov rax, qword ptr [rbx + 0xd8]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────► 0x7ffff7a86278 <_IO_file_xsputn+152> mov rax, qword ptr [rbx + 0xd8]0x7ffff7a8627f <_IO_file_xsputn+159> mov esi, 0xffffffff0x7ffff7a86284 <_IO_file_xsputn+164> mov rdi, rbx0x7ffff7a86287 <_IO_file_xsputn+167> mov rbp, r120x7ffff7a8628a <_IO_file_xsputn+170> call qword ptr [rax + 0x18]0x7ffff7a8628d <_IO_file_xsputn+173> cmp eax, -10x7ffff7a86290 <_IO_file_xsputn+176> je _IO_file_xsputn+123 <0x7ffff7a8625b>0x7ffff7a86292 <_IO_file_xsputn+178> mov rcx, qword ptr [rbx + 0x40]0x7ffff7a86296 <_IO_file_xsputn+182> sub rcx, qword ptr [rbx + 0x38]0x7ffff7a8629a <_IO_file_xsputn+186> xor edx, edx0x7ffff7a8629c <_IO_file_xsputn+188> cmp rcx, 0x7f
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/wolf/iofile/fileops.c1326 }1327 if (to_do + must_flush > 0)1328 {1329 _IO_size_t block_size, do_write;1330 /* Next flush the (full) buffer. */► 1331 if (_IO_OVERFLOW (f, EOF) == EOF)1332 /* If nothing else has to be written we must not signal the1333 caller that everything has been written. */1334 return to_do == 0 ? EOF : n - to_do;1335 1336 /* Try to maintain alignment: write a whole number of blocks. */
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe540 ◂— 0x1
01:0008│ 0x7fffffffe548 ◂— 0x20 /* ' ' */
02:0010│ 0x7fffffffe550 —▸ 0x602010 ◂— 0xfbad2484
03:0018│ 0x7fffffffe558 —▸ 0x602240 ◂— 0x0
04:0020│ 0x7fffffffe560 ◂— 0x20 /* ' ' */
05:0028│ 0x7fffffffe568 ◂— 0x1
06:0030│ 0x7fffffffe570 ◂— 0x0
07:0038│ 0x7fffffffe578 —▸ 0x7ffff7a7b7bb (fwrite+219) ◂— cmp rax, -1
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────► f 0 7ffff7a86278 _IO_file_xsputn+152f 1 7ffff7a7b7bb fwrite+219f 2 4005fc main+70f 3 7ffff7a2d830 __libc_start_main+240
pwndbg>
int
_IO_new_file_overflow (_IO_FILE *f, int ch)
{# 判断标志位是否包含_IO_NO_WRITESif (f->_flags & _IO_NO_WRITES) /* SET ERROR */{f->_flags |= _IO_ERR_SEEN;__set_errno (EBADF);return EOF;}# 判断输出缓冲区是否为空if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_IO_write_base == NULL){/* Allocate a buffer if needed. */if (f->_IO_write_base == NULL){# 分配输出缓冲区_IO_doallocbuf (f);_IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base);}# 初始化指针if (f->_IO_read_ptr == f->_IO_buf_end)f->_IO_read_end = f->_IO_read_ptr = f->_IO_buf_base;f->_IO_write_ptr = f->_IO_read_ptr;f->_IO_write_base = f->_IO_write_ptr;f->_IO_write_end = f->_IO_buf_end;f->_IO_read_base = f->_IO_read_ptr = f->_IO_read_end;f->_flags |= _IO_CURRENTLY_PUTTING;if (f->_mode <= 0 && f->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))f->_IO_write_end = f->_IO_write_ptr;}# 输出输出缓冲区 if (ch == EOF)return _IO_do_write (f, f->_IO_write_base,f->_IO_write_ptr - f->_IO_write_base);if (f->_IO_write_ptr == f->_IO_buf_end ) /* Buffer is really full */if (_IO_do_flush (f) == EOF) ## return EOF;*f->_IO_write_ptr++ = ch;if ((f->_flags & _IO_UNBUFFERED)|| ((f->_flags & _IO_LINE_BUF) && ch == '\n'))if (_IO_do_write (f, f->_IO_write_base,f->_IO_write_ptr - f->_IO_write_base) == EOF)return EOF;return (unsigned char) ch;
}
libc_hidden_ver (_IO_new_file_overflow, _IO_file_overflow)
__overflow函数首先检测_IO_FILE的flags是否包含_IO_NO_WRITES标志位,如果包含的话直接返回。
接着判断f->_IO_write_base是否为空,如果为空表明输出缓冲区尚未建立,就调用_IO_doallocbuf函数去分配输出缓冲区,_IO_doallocbuf函数源码在上小节fread中已经分析过了,就不继续跟进分析了,总结下它功能就是分配输出输出缓冲区并将指针_IO_buf_base和_IO_buf_end赋值。
在执行_IO_doallocbuf分配完空间后调用_IO_setg宏,该宏的定义如下,它将输出相关的缓冲区指针赋值为_IO_buf_base指针。
#define _IO_setg(fp, eb, g, eg) ((fp)->_IO_read_base = (eb),
(fp)->_IO_read_ptr = (g), (fp)->_IO_read_end = (eg))
2.8 经过上面这些步骤,此时IO_FILE的指针如下图所示。可以看到_IO_buf_base和_IO_buf_end被赋值了,且输出相关缓冲区指针被赋值为_IO_buf_base。
pwndbg> n
_IO_new_file_overflow (f=0x602010, ch=-1) at fileops.c:821
821 _IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────RAX 0x1RBX 0x602010 ◂— 0xfbad2484RCX 0x1RDX 0xfbad2484RDI 0x0RSI 0x602270 ◂— 0x0R8 0x6020f0 ◂— 0x100000001R9 0x602010 ◂— 0xfbad2484R10 0x1R11 0x346R12 0x20R13 0x602240 ◂— 0x0R14 0x1R15 0x0RBP 0xffffffffRSP 0x7fffffffe520 —▸ 0x602010 ◂— 0xfbad2484RIP 0x7ffff7a878f8 (_IO_file_overflow+456) ◂— mov rdx, qword ptr [rbx + 0x38]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────0x7ffff7a878f0 <_IO_file_overflow+448> mov rdi, rbx0x7ffff7a878f3 <_IO_file_overflow+451> call _IO_doallocbuf <0x7ffff7a88560>► 0x7ffff7a878f8 <_IO_file_overflow+456> mov rdx, qword ptr [rbx + 0x38]0x7ffff7a878fc <_IO_file_overflow+460> mov ecx, dword ptr [rbx]0x7ffff7a878fe <_IO_file_overflow+462> mov qword ptr [rbx + 0x18], rdx0x7ffff7a87902 <_IO_file_overflow+466> mov qword ptr [rbx + 8], rdx0x7ffff7a87906 <_IO_file_overflow+470> mov qword ptr [rbx + 0x10], rdx0x7ffff7a8790a <_IO_file_overflow+474> jmp _IO_file_overflow+119 <0x7ffff7a877a7>↓0x7ffff7a877a7 <_IO_file_overflow+119> test ch, 10x7ffff7a877aa <_IO_file_overflow+122> jne _IO_file_overflow+256 <0x7ffff7a87830>0x7ffff7a877b0 <_IO_file_overflow+128> mov rax, qword ptr [rbx + 0x40]
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/wolf/iofile/fileops.c816 {817 /* Allocate a buffer if needed. */818 if (f->_IO_write_base == NULL)819 {820 _IO_doallocbuf (f);► 821 _IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base);822 }823 /* Otherwise must be currently reading.824 If _IO_read_ptr (and hence also _IO_read_end) is at the buffer end,825 logically slide the buffer forwards one block (by setting the826 read pointers to all point at the beginning of the block). This
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe520 —▸ 0x602010 ◂— 0xfbad2484
01:0008│ 0x7fffffffe528 ◂— 0x20 /* ' ' */
... ↓
03:0018│ 0x7fffffffe538 —▸ 0x7ffff7a8628d (_IO_file_xsputn+173) ◂— cmp eax, -1
04:0020│ 0x7fffffffe540 ◂— 0x1
05:0028│ 0x7fffffffe548 ◂— 0x20 /* ' ' */
06:0030│ 0x7fffffffe550 —▸ 0x602010 ◂— 0xfbad2484
07:0038│ 0x7fffffffe558 —▸ 0x602240 ◂— 0x0
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────► f 0 7ffff7a878f8 _IO_file_overflow+456f 1 7ffff7a8628d _IO_file_xsputn+173f 2 7ffff7a7b7bb fwrite+219f 3 4005fc main+70f 4 7ffff7a2d830 __libc_start_main+240
pwndbg> n
830 if (__glibc_unlikely (_IO_in_backup (f)))
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────RAX 0x1RBX 0x602010 ◂— 0xfbad2484RCX 0xfbad2484RDX 0x602270 ◂— 0x0RDI 0x0RSI 0x602270 ◂— 0x0R8 0x6020f0 ◂— 0x100000001R9 0x602010 ◂— 0xfbad2484R10 0x1R11 0x346R12 0x20R13 0x602240 ◂— 0x0R14 0x1R15 0x0RBP 0xffffffffRSP 0x7fffffffe520 —▸ 0x602010 ◂— 0xfbad2484RIP 0x7ffff7a877a7 (_IO_file_overflow+119) ◂— test ch, 1
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────0x7ffff7a878fc <_IO_file_overflow+460> mov ecx, dword ptr [rbx]0x7ffff7a878fe <_IO_file_overflow+462> mov qword ptr [rbx + 0x18], rdx0x7ffff7a87902 <_IO_file_overflow+466> mov qword ptr [rbx + 8], rdx0x7ffff7a87906 <_IO_file_overflow+470> mov qword ptr [rbx + 0x10], rdx0x7ffff7a8790a <_IO_file_overflow+474> jmp _IO_file_overflow+119 <0x7ffff7a877a7>↓► 0x7ffff7a877a7 <_IO_file_overflow+119> test ch, 10x7ffff7a877aa <_IO_file_overflow+122> jne _IO_file_overflow+256 <0x7ffff7a87830>0x7ffff7a877b0 <_IO_file_overflow+128> mov rax, qword ptr [rbx + 0x40]0x7ffff7a877b4 <_IO_file_overflow+132> cmp rax, rdx0x7ffff7a877b7 <_IO_file_overflow+135> je _IO_file_overflow+400 <0x7ffff7a878c0>0x7ffff7a877bd <_IO_file_overflow+141> mov rsi, qword ptr [rbx + 0x10]
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/wolf/iofile/fileops.c825 logically slide the buffer forwards one block (by setting the826 read pointers to all point at the beginning of the block). This827 makes room for subsequent output.828 Otherwise, set the read pointers to _IO_read_end (leaving that829 alone, so it can continue to correspond to the external position). */► 830 if (__glibc_unlikely (_IO_in_backup (f)))831 {832 size_t nbackup = f->_IO_read_end - f->_IO_read_ptr;833 _IO_free_backup_area (f);834 f->_IO_read_base -= MIN (nbackup,835 f->_IO_read_base - f->_IO_buf_base);
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe520 —▸ 0x602010 ◂— 0xfbad2484
01:0008│ 0x7fffffffe528 ◂— 0x20 /* ' ' */
... ↓
03:0018│ 0x7fffffffe538 —▸ 0x7ffff7a8628d (_IO_file_xsputn+173) ◂— cmp eax, -1
04:0020│ 0x7fffffffe540 ◂— 0x1
05:0028│ 0x7fffffffe548 ◂— 0x20 /* ' ' */
06:0030│ 0x7fffffffe550 —▸ 0x602010 ◂— 0xfbad2484
07:0038│ 0x7fffffffe558 —▸ 0x602240 ◂— 0x0
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────► f 0 7ffff7a877a7 _IO_file_overflow+119f 1 7ffff7a8628d _IO_file_xsputn+173f 2 7ffff7a7b7bb fwrite+219f 3 4005fc main+70f 4 7ffff7a2d830 __libc_start_main+240
pwndbg> p *_IO_list_all
$6 = {file = {_flags = -72539004, _IO_read_ptr = 0x602270 "", _IO_read_end = 0x602270 "", _IO_read_base = 0x602270 "", _IO_write_base = 0x0, _IO_write_ptr = 0x0, _IO_write_end = 0x0, _IO_buf_base = 0x602270 "", _IO_buf_end = 0x603270 "", _IO_save_base = 0x0, _IO_backup_base = 0x0, _IO_save_end = 0x0, _markers = 0x0, _chain = 0x7ffff7dd2540 <_IO_2_1_stderr_>, _fileno = 3, _flags2 = 0, _old_offset = 0, _cur_column = 0, _vtable_offset = 0 '\000', _shortbuf = "", _lock = 0x6020f0, _offset = -1, _codecvt = 0x0, _wide_data = 0x602100, _freeres_list = 0x0, _freeres_buf = 0x0, __pad5 = 0, _mode = -1, _unused2 = '\000' <repeats 19 times>}, vtable = 0x7ffff7dd06e0 <_IO_file_jumps>
}
pwndbg>
pwndbg> p *_IO_list_all
$6 = {
file = {
_flags = -72539004,
_IO_read_ptr = 0x602270 "",
_IO_read_end = 0x602270 "",
_IO_read_base = 0x602270 "",
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_IO_write_end = 0x0,
_IO_buf_base = 0x602270 "",
_IO_buf_end = 0x603270 "",
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7dd2540 <_IO_2_1_stderr_>,
_fileno = 3,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x6020f0,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x602100,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7dd06e0 <_IO_file_jumps>
}
pwndbg>
然后代码初始化其他相关指针,最主要的就是将f->_IO_write_base以及f->_IO_write_ptr设置成f->_IO_read_ptr指针;将f->_IO_write_end赋值为f->_IO_buf_end指针。
2.9 接着就执行_IO_do_write来调用系统调用write输出输出缓冲区,输出的内容为f->_IO_write_ptr到f->_IO_write_base之间的内容。跟进去该函数,函数在/libio/fileops.c中。
int
_IO_new_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do)
{return (to_do == 0|| (_IO_size_t) new_do_write (fp, data, to_do) == to_do) ? 0 : EOF;
}
libc_hidden_ver (_IO_new_do_write, _IO_do_write)
2.10 该函数调用了new_do_write,跟进去,函数在/libio/fileops.c中。
static
_IO_size_t
new_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do)
{_IO_size_t count;...# 额外判断else if (fp->_IO_read_end != fp->_IO_write_base){_IO_off64_t new_pos= _IO_SYSSEEK (fp, fp->_IO_write_base - fp->_IO_read_end, 1);if (new_pos == _IO_pos_BAD)return 0;fp->_offset = new_pos;}# 调用函数输出输出缓冲区count = _IO_SYSWRITE (fp, data, to_do);...# 刷新设置缓冲区指针_IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_buf_base;fp->_IO_write_end = (fp->_mode <= 0&& (fp->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))? fp->_IO_buf_base : fp->_IO_buf_end);return count;
}
这里有一个判断,判断fp->_IO_read_end是否等于fp->_IO_write_base,如果不等的话,调用_IO_SYSSEEK去调整文件偏移。
2.11 接着就调用_IO_SYSWRITE函数,该函数时vtable中的__write(_IO_new_file_write)函数,也就是最终执行系统调用的地方,跟进去看,文件在/libio/fileops.c中。
_IO_ssize_t
_IO_new_file_write (_IO_FILE *f, const void *data, _IO_ssize_t n)
{_IO_ssize_t to_do = n;while (to_do > 0){# 系统调用write输出_IO_ssize_t count = (__builtin_expect (f->_flags2& _IO_FLAGS2_NOTCANCEL, 0)? write_not_cancel (f->_fileno, data, to_do): write (f->_fileno, data, to_do));... return n;
}
执行完_IO_SYSWRITE函数后,回到new_do_write函数,刷新设置缓冲区指针并返回。
经历了缓冲区建立以及刷新缓冲区,程序返回到_IO_new_file_xsputn函数中,进入到以下代码块。
# 检查输出数据是否是大块block_size = f->_IO_buf_end - f->_IO_buf_base;do_write = to_do - (block_size >= 128 ? to_do % block_size : 0);if (do_write){# 如果是大块的话则不使用输出缓冲区而直接输出。count = new_do_write (f, s, do_write);to_do -= count;if (count < do_write)return n - to_do;}
2.12 运行到此处,此时已经经过了_IO_OVERFLOW函数(对输出缓冲区进行了初始化或刷新),也就是说此时的IO_FILE缓冲区指针的状态是处于刷新的初始化状态,输出缓冲区中也没用数据。
上面这部分代码检查剩余目标输出数据大小,如果超过输出缓冲区f->_IO_write_end – f->_IO_write_base的大小,则为了提高效率,不在使用输出缓冲区,而是以块(4kb)为基本单位直接将缓冲区调用new_do_write输出。new_do_write函数在上面已经跟过了,就是输出,并刷新指针设置。
由于示例程序只输出0x20大小的数据,而它的输出缓冲区大小为0x1000,因此不会进入这部分代码。
在以大块为基本单位把数据直接输出后可能还剩余小数据,IO采用的策略是将剩余目标输出数据放入到输出缓冲区里面,相关源码如下。
# 剩余的数据拷贝至输出缓冲区if (to_do)to_do -= _IO_default_xsputn (f, s+do_write, to_do);
2.13 程序调用_IO_default_xsputn函数对剩下的s + do_write数据进行操作,跟进去该函数,在/libio/genops.c中。
_IO_size_t
_IO_default_xsputn (_IO_FILE *f, const void *data, _IO_size_t n)
{const char *s = (char *) data;_IO_size_t more = n;if (more <= 0)return 0;for (;;){/* Space available. */if (f->_IO_write_ptr < f->_IO_write_end){_IO_size_t count = f->_IO_write_end - f->_IO_write_ptr;if (count > more)count = more;if (count > 20){# 输出长度大于20,则调用memcpy拷贝memcpy (f->_IO_write_ptr, s, count);f->_IO_write_ptr += count;
#endifs += count;}else if (count){# 小于20则直接赋值char *p = f->_IO_write_ptr;_IO_ssize_t i;for (i = count; --i >= 0; )*p++ = *s++;f->_IO_write_ptr = p;}more -= count;}# 如果输出缓冲区为空,则调用_IO_OVERFLOW直接输出。if (more == 0 || _IO_OVERFLOW (f, (unsigned char) *s++) == EOF)break;more--;}return n - more;
}
libc_hidden_def (_IO_default_xsputn)
可以看到函数最主要的作用就是将剩余的目标输出数据拷贝到输出缓冲区里。为了性能优化,当长度大于20时,使用memcpy拷贝,当长度小于20时,使用for循环赋值拷贝。如果输出缓冲区为空,则调用_IO_OVERFLOW进行输出。
2.14 根据源码可知,示例程序最终会进入_IO_default_xsputn中,并且把数据拷贝到输出缓冲区里,执行完成后,看到IO_FILE结构体的数据如下。
pwndbg> n
459 return n - more;
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────RAX 0x602290 ◂— 0x0RBX 0x20RCX 0x0RDX 0x0RDI 0x602290 ◂— 0x0RSI 0x602260 ◂— 0x0R8 0x0R9 0x0R10 0x0R11 0x346R12 0x602010 ◂— 0xfbad2c84R13 0x20R14 0x602260 ◂— 0x0R15 0x0RBP 0x0RSP 0x7fffffffe510 —▸ 0x602010 ◂— 0xfbad2c84RIP 0x7ffff7a886d8 (_IO_default_xsputn+168) ◂— mov rax, r13
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────0x7ffff7a886d5 <_IO_default_xsputn+165> nop dword ptr [rax]► 0x7ffff7a886d8 <_IO_default_xsputn+168> mov rax, r130x7ffff7a886db <_IO_default_xsputn+171> pop rbx0x7ffff7a886dc <_IO_default_xsputn+172> sub rax, rbp0x7ffff7a886df <_IO_default_xsputn+175> pop rbp0x7ffff7a886e0 <_IO_default_xsputn+176> pop r120x7ffff7a886e2 <_IO_default_xsputn+178> pop r130x7ffff7a886e4 <_IO_default_xsputn+180> pop r140x7ffff7a886e6 <_IO_default_xsputn+182> ret ↓0x7ffff7a862c7 <_IO_file_xsputn+231> sub rbp, rax0x7ffff7a862ca <_IO_file_xsputn+234> jmp _IO_file_xsputn+123 <0x7ffff7a8625b>
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/wolf/iofile/genops.c454 }455 if (more == 0 || _IO_OVERFLOW (f, (unsigned char) *s++) == EOF)456 break;457 more--;458 }► 459 return n - more;460 }461 libc_hidden_def (_IO_default_xsputn)462 463 _IO_size_t464 _IO_sgetn (_IO_FILE *fp, void *data, _IO_size_t n)
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe510 —▸ 0x602010 ◂— 0xfbad2c84
01:0008│ 0x7fffffffe518 ◂— 0x20 /* ' ' */
... ↓
03:0018│ 0x7fffffffe528 —▸ 0x602240 ◂— 0x0
04:0020│ 0x7fffffffe530 ◂— 0x0
05:0028│ 0x7fffffffe538 —▸ 0x7ffff7a862c7 (_IO_file_xsputn+231) ◂— sub rbp, rax
06:0030│ 0x7fffffffe540 ◂— 0x1
07:0038│ 0x7fffffffe548 ◂— 0x20 /* ' ' */
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────► f 0 7ffff7a886d8 _IO_default_xsputn+168f 1 7ffff7a862c7 _IO_file_xsputn+231f 2 7ffff7a7b7bb fwrite+219f 3 4005fc main+70f 4 7ffff7a2d830 __libc_start_main+240
pwndbg> n
460 }
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────RAX 0x20RBX 0x20RCX 0x0RDX 0x0RDI 0x602290 ◂— 0x0RSI 0x602260 ◂— 0x0R8 0x0R9 0x0R10 0x0R11 0x346R12 0x602010 ◂— 0xfbad2c84R13 0x20R14 0x602260 ◂— 0x0R15 0x0RBP 0x0RSP 0x7fffffffe510 —▸ 0x602010 ◂— 0xfbad2c84RIP 0x7ffff7a886db (_IO_default_xsputn+171) ◂— pop rbx
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────0x7ffff7a886d5 <_IO_default_xsputn+165> nop dword ptr [rax]0x7ffff7a886d8 <_IO_default_xsputn+168> mov rax, r13► 0x7ffff7a886db <_IO_default_xsputn+171> pop rbx0x7ffff7a886dc <_IO_default_xsputn+172> sub rax, rbp0x7ffff7a886df <_IO_default_xsputn+175> pop rbp0x7ffff7a886e0 <_IO_default_xsputn+176> pop r120x7ffff7a886e2 <_IO_default_xsputn+178> pop r130x7ffff7a886e4 <_IO_default_xsputn+180> pop r140x7ffff7a886e6 <_IO_default_xsputn+182> ret ↓0x7ffff7a862c7 <_IO_file_xsputn+231> sub rbp, rax0x7ffff7a862ca <_IO_file_xsputn+234> jmp _IO_file_xsputn+123 <0x7ffff7a8625b>
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/wolf/iofile/genops.c455 if (more == 0 || _IO_OVERFLOW (f, (unsigned char) *s++) == EOF)456 break;457 more--;458 }459 return n - more;► 460 }461 libc_hidden_def (_IO_default_xsputn)462 463 _IO_size_t464 _IO_sgetn (_IO_FILE *fp, void *data, _IO_size_t n)465 {
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe510 —▸ 0x602010 ◂— 0xfbad2c84
01:0008│ 0x7fffffffe518 ◂— 0x20 /* ' ' */
... ↓
03:0018│ 0x7fffffffe528 —▸ 0x602240 ◂— 0x0
04:0020│ 0x7fffffffe530 ◂— 0x0
05:0028│ 0x7fffffffe538 —▸ 0x7ffff7a862c7 (_IO_file_xsputn+231) ◂— sub rbp, rax
06:0030│ 0x7fffffffe540 ◂— 0x1
07:0038│ 0x7fffffffe548 ◂— 0x20 /* ' ' */
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────► f 0 7ffff7a886db _IO_default_xsputn+171f 1 7ffff7a862c7 _IO_file_xsputn+231f 2 7ffff7a7b7bb fwrite+219f 3 4005fc main+70f 4 7ffff7a2d830 __libc_start_main+240
pwndbg> n
459 return n - more;
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────RAX 0x20RBX 0x602010 ◂— 0xfbad2c84RCX 0x0RDX 0x0RDI 0x602290 ◂— 0x0RSI 0x602260 ◂— 0x0R8 0x0R9 0x0R10 0x0R11 0x346R12 0x602010 ◂— 0xfbad2c84R13 0x20R14 0x602260 ◂— 0x0R15 0x0RBP 0x0RSP 0x7fffffffe518 ◂— 0x20 /* ' ' */RIP 0x7ffff7a886dc (_IO_default_xsputn+172) ◂— sub rax, rbp
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────0x7ffff7a886d5 <_IO_default_xsputn+165> nop dword ptr [rax]0x7ffff7a886d8 <_IO_default_xsputn+168> mov rax, r130x7ffff7a886db <_IO_default_xsputn+171> pop rbx► 0x7ffff7a886dc <_IO_default_xsputn+172> sub rax, rbp0x7ffff7a886df <_IO_default_xsputn+175> pop rbp0x7ffff7a886e0 <_IO_default_xsputn+176> pop r120x7ffff7a886e2 <_IO_default_xsputn+178> pop r130x7ffff7a886e4 <_IO_default_xsputn+180> pop r140x7ffff7a886e6 <_IO_default_xsputn+182> ret ↓0x7ffff7a862c7 <_IO_file_xsputn+231> sub rbp, rax0x7ffff7a862ca <_IO_file_xsputn+234> jmp _IO_file_xsputn+123 <0x7ffff7a8625b>
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/wolf/iofile/genops.c454 }455 if (more == 0 || _IO_OVERFLOW (f, (unsigned char) *s++) == EOF)456 break;457 more--;458 }► 459 return n - more;460 }461 libc_hidden_def (_IO_default_xsputn)462 463 _IO_size_t464 _IO_sgetn (_IO_FILE *fp, void *data, _IO_size_t n)
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe518 ◂— 0x20 /* ' ' */
... ↓
02:0010│ 0x7fffffffe528 —▸ 0x602240 ◂— 0x0
03:0018│ 0x7fffffffe530 ◂— 0x0
04:0020│ 0x7fffffffe538 —▸ 0x7ffff7a862c7 (_IO_file_xsputn+231) ◂— sub rbp, rax
05:0028│ 0x7fffffffe540 ◂— 0x1
06:0030│ 0x7fffffffe548 ◂— 0x20 /* ' ' */
07:0038│ 0x7fffffffe550 —▸ 0x602010 ◂— 0xfbad2c84
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────► f 0 7ffff7a886dc _IO_default_xsputn+172f 1 7ffff7a862c7 _IO_file_xsputn+231f 2 7ffff7a7b7bb fwrite+219f 3 4005fc main+70f 4 7ffff7a2d830 __libc_start_main+240
pwndbg> n
460 }
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────RAX 0x20RBX 0x602010 ◂— 0xfbad2c84RCX 0x0RDX 0x0RDI 0x602290 ◂— 0x0RSI 0x602260 ◂— 0x0R8 0x0R9 0x0R10 0x0R11 0x346R12 0x602010 ◂— 0xfbad2c84R13 0x20R14 0x602260 ◂— 0x0R15 0x0RBP 0x0RSP 0x7fffffffe518 ◂— 0x20 /* ' ' */RIP 0x7ffff7a886df (_IO_default_xsputn+175) ◂— pop rbp
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────0x7ffff7a886d5 <_IO_default_xsputn+165> nop dword ptr [rax]0x7ffff7a886d8 <_IO_default_xsputn+168> mov rax, r130x7ffff7a886db <_IO_default_xsputn+171> pop rbx0x7ffff7a886dc <_IO_default_xsputn+172> sub rax, rbp► 0x7ffff7a886df <_IO_default_xsputn+175> pop rbp0x7ffff7a886e0 <_IO_default_xsputn+176> pop r120x7ffff7a886e2 <_IO_default_xsputn+178> pop r130x7ffff7a886e4 <_IO_default_xsputn+180> pop r140x7ffff7a886e6 <_IO_default_xsputn+182> ret ↓0x7ffff7a862c7 <_IO_file_xsputn+231> sub rbp, rax0x7ffff7a862ca <_IO_file_xsputn+234> jmp _IO_file_xsputn+123 <0x7ffff7a8625b>
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/wolf/iofile/genops.c455 if (more == 0 || _IO_OVERFLOW (f, (unsigned char) *s++) == EOF)456 break;457 more--;458 }459 return n - more;► 460 }461 libc_hidden_def (_IO_default_xsputn)462 463 _IO_size_t464 _IO_sgetn (_IO_FILE *fp, void *data, _IO_size_t n)465 {
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe518 ◂— 0x20 /* ' ' */
... ↓
02:0010│ 0x7fffffffe528 —▸ 0x602240 ◂— 0x0
03:0018│ 0x7fffffffe530 ◂— 0x0
04:0020│ 0x7fffffffe538 —▸ 0x7ffff7a862c7 (_IO_file_xsputn+231) ◂— sub rbp, rax
05:0028│ 0x7fffffffe540 ◂— 0x1
06:0030│ 0x7fffffffe548 ◂— 0x20 /* ' ' */
07:0038│ 0x7fffffffe550 —▸ 0x602010 ◂— 0xfbad2c84
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────► f 0 7ffff7a886df _IO_default_xsputn+175f 1 7ffff7a862c7 _IO_file_xsputn+231f 2 7ffff7a7b7bb fwrite+219f 3 4005fc main+70f 4 7ffff7a2d830 __libc_start_main+240
pwndbg> n
_IO_new_file_xsputn (f=0x602010, data=<optimized out>, n=32) at fileops.c:1354
1354 return n - to_do;
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────RAX 0x20RBX 0x602010 ◂— 0xfbad2c84RCX 0x0RDX 0x0RDI 0x602290 ◂— 0x0RSI 0x602260 ◂— 0x0R8 0x0R9 0x0R10 0x0R11 0x346R12 0x20R13 0x602240 ◂— 0x0R14 0x0R15 0x0RBP 0x0RSP 0x7fffffffe540 ◂— 0x1RIP 0x7ffff7a8625b (_IO_file_xsputn+123) ◂— mov rax, r12
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────0x7ffff7a862bc <_IO_file_xsputn+220> mov rdx, rbp0x7ffff7a862bf <_IO_file_xsputn+223> mov rdi, rbx0x7ffff7a862c2 <_IO_file_xsputn+226> call _IO_default_xsputn <0x7ffff7a88630>0x7ffff7a862c7 <_IO_file_xsputn+231> sub rbp, rax0x7ffff7a862ca <_IO_file_xsputn+234> jmp _IO_file_xsputn+123 <0x7ffff7a8625b>↓► 0x7ffff7a8625b <_IO_file_xsputn+123> mov rax, r120x7ffff7a8625e <_IO_file_xsputn+126> sub rax, rbp0x7ffff7a86261 <_IO_file_xsputn+129> add rsp, 80x7ffff7a86265 <_IO_file_xsputn+133> pop rbx0x7ffff7a86266 <_IO_file_xsputn+134> pop rbp0x7ffff7a86267 <_IO_file_xsputn+135> pop r12
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/wolf/iofile/fileops.c1349 buffer, but it's somewhat messier for line-buffered files,1350 so we let _IO_default_xsputn handle the general case. */1351 if (to_do)1352 to_do -= _IO_default_xsputn (f, s+do_write, to_do);1353 }► 1354 return n - to_do;1355 }1356 libc_hidden_ver (_IO_new_file_xsputn, _IO_file_xsputn)1357 1358 _IO_size_t1359 _IO_file_xsgetn (_IO_FILE *fp, void *data, _IO_size_t n)
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe540 ◂— 0x1
01:0008│ 0x7fffffffe548 ◂— 0x20 /* ' ' */
02:0010│ 0x7fffffffe550 —▸ 0x602010 ◂— 0xfbad2c84
03:0018│ 0x7fffffffe558 —▸ 0x602240 ◂— 0x0
04:0020│ 0x7fffffffe560 ◂— 0x20 /* ' ' */
05:0028│ 0x7fffffffe568 ◂— 0x1
06:0030│ 0x7fffffffe570 ◂— 0x0
07:0038│ 0x7fffffffe578 —▸ 0x7ffff7a7b7bb (fwrite+219) ◂— cmp rax, -1
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────► f 0 7ffff7a8625b _IO_file_xsputn+123f 1 7ffff7a7b7bb fwrite+219f 2 4005fc main+70f 3 7ffff7a2d830 __libc_start_main+240
pwndbg> n
1355 }
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────RAX 0x20RBX 0x602010 ◂— 0xfbad2c84RCX 0x0RDX 0x0RDI 0x602290 ◂— 0x0RSI 0x602260 ◂— 0x0R8 0x0R9 0x0R10 0x0R11 0x346R12 0x20R13 0x602240 ◂— 0x0R14 0x0R15 0x0RBP 0x0RSP 0x7fffffffe540 ◂— 0x1RIP 0x7ffff7a86261 (_IO_file_xsputn+129) ◂— add rsp, 8
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────0x7ffff7a862c2 <_IO_file_xsputn+226> call _IO_default_xsputn <0x7ffff7a88630>0x7ffff7a862c7 <_IO_file_xsputn+231> sub rbp, rax0x7ffff7a862ca <_IO_file_xsputn+234> jmp _IO_file_xsputn+123 <0x7ffff7a8625b>↓0x7ffff7a8625b <_IO_file_xsputn+123> mov rax, r120x7ffff7a8625e <_IO_file_xsputn+126> sub rax, rbp► 0x7ffff7a86261 <_IO_file_xsputn+129> add rsp, 80x7ffff7a86265 <_IO_file_xsputn+133> pop rbx0x7ffff7a86266 <_IO_file_xsputn+134> pop rbp0x7ffff7a86267 <_IO_file_xsputn+135> pop r120x7ffff7a86269 <_IO_file_xsputn+137> pop r130x7ffff7a8626b <_IO_file_xsputn+139> pop r14
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/wolf/iofile/fileops.c1350 so we let _IO_default_xsputn handle the general case. */1351 if (to_do)1352 to_do -= _IO_default_xsputn (f, s+do_write, to_do);1353 }1354 return n - to_do;► 1355 }1356 libc_hidden_ver (_IO_new_file_xsputn, _IO_file_xsputn)1357 1358 _IO_size_t1359 _IO_file_xsgetn (_IO_FILE *fp, void *data, _IO_size_t n)1360 {
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe540 ◂— 0x1
01:0008│ 0x7fffffffe548 ◂— 0x20 /* ' ' */
02:0010│ 0x7fffffffe550 —▸ 0x602010 ◂— 0xfbad2c84
03:0018│ 0x7fffffffe558 —▸ 0x602240 ◂— 0x0
04:0020│ 0x7fffffffe560 ◂— 0x20 /* ' ' */
05:0028│ 0x7fffffffe568 ◂— 0x1
06:0030│ 0x7fffffffe570 ◂— 0x0
07:0038│ 0x7fffffffe578 —▸ 0x7ffff7a7b7bb (fwrite+219) ◂— cmp rax, -1
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────► f 0 7ffff7a86261 _IO_file_xsputn+129f 1 7ffff7a7b7bb fwrite+219f 2 4005fc main+70f 3 7ffff7a2d830 __libc_start_main+240
pwndbg> n
__GI__IO_fwrite (buf=0x602240, size=1, count=32, fp=0x602010) at iofwrite.c:37
37 _IO_acquire_lock (fp);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────RAX 0x20RBX 0x20RCX 0x0RDX 0x0RDI 0x602290 ◂— 0x0RSI 0x602200 ◂— 0x0R8 0x20R9 0x0R10 0x0R11 0x346R12 0x602240 ◂— 0x0R13 0x20R14 0x1R15 0x0RBP 0x602010 ◂— 0xfbad2c84RSP 0x7fffffffe580 ◂— 0x0RIP 0x7ffff7a7b7c6 (fwrite+230) ◂— test dword ptr [rbp], 0x8000
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────0x7ffff7a7b7b5 <fwrite+213> mov rdi, rbp0x7ffff7a7b7b8 <fwrite+216> call qword ptr [rax + 0x38]0x7ffff7a7b7bb <fwrite+219> cmp rax, -10x7ffff7a7b7bf <fwrite+223> mov r8, rax0x7ffff7a7b7c2 <fwrite+226> sete sil► 0x7ffff7a7b7c6 <fwrite+230> test dword ptr [rbp], 0x80000x7ffff7a7b7cd <fwrite+237> je fwrite+280 <0x7ffff7a7b7f8>↓0x7ffff7a7b7f8 <fwrite+280> mov rdx, qword ptr [rbp + 0x88]0x7ffff7a7b7ff <fwrite+287> sub dword ptr [rdx + 4], 10x7ffff7a7b803 <fwrite+291> jne fwrite+239 <0x7ffff7a7b7cf>0x7ffff7a7b805 <fwrite+293> mov qword ptr [rdx + 8], 0
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/wolf/iofile/iofwrite.c34 CHECK_FILE (fp, 0);35 if (request == 0)36 return 0;37 _IO_acquire_lock (fp);38 if (_IO_vtable_offset (fp) != 0 || _IO_fwide (fp, -1) == -1)► 39 written = _IO_sputn (fp, (const char *) buf, request);40 _IO_release_lock (fp);41 /* We have written all of the input in case the return value indicates42 this or EOF is returned. The latter is a special case where we43 simply did not manage to flush the buffer. But the data is in the44 buffer and therefore written as far as fwrite is concerned. */
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe580 ◂— 0x0
01:0008│ 0x7fffffffe588 —▸ 0x7fffffffe5c0 —▸ 0x400610 (__libc_csu_init) ◂— push r15
02:0010│ 0x7fffffffe590 —▸ 0x4004c0 (_start) ◂— xor ebp, ebp
03:0018│ 0x7fffffffe598 —▸ 0x7fffffffe6a0 ◂— 0x1
04:0020│ 0x7fffffffe5a0 ◂— 0x0
05:0028│ 0x7fffffffe5a8 —▸ 0x4005fc (main+70) ◂— mov eax, 0
06:0030│ 0x7fffffffe5b0 —▸ 0x602010 ◂— 0xfbad2c84
07:0038│ 0x7fffffffe5b8 —▸ 0x602240 ◂— 0x0
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────► f 0 7ffff7a7b7c6 fwrite+230f 1 4005fc main+70f 2 7ffff7a2d830 __libc_start_main+240
pwndbg> p *_IO_list_all
$7 = {file = {_flags = -72536956, _IO_read_ptr = 0x602270 "", _IO_read_end = 0x602270 "", _IO_read_base = 0x602270 "", _IO_write_base = 0x602270 "", _IO_write_ptr = 0x602290 "", _IO_write_end = 0x603270 "", _IO_buf_base = 0x602270 "", _IO_buf_end = 0x603270 "", _IO_save_base = 0x0, _IO_backup_base = 0x0, _IO_save_end = 0x0, _markers = 0x0, _chain = 0x7ffff7dd2540 <_IO_2_1_stderr_>, _fileno = 3, _flags2 = 0, _old_offset = 0, _cur_column = 0, _vtable_offset = 0 '\000', _shortbuf = "", _lock = 0x6020f0, _offset = -1, _codecvt = 0x0, _wide_data = 0x602100, _freeres_list = 0x0, _freeres_buf = 0x0, __pad5 = 0, _mode = -1, _unused2 = '\000' <repeats 19 times>}, vtable = 0x7ffff7dd06e0 <_IO_file_jumps>
}
pwndbg>
可以看到此时的_IO_write_base为0x602270,而_IO_write_ptr为0x602290,大小正好是0x20。至此,源码分析结束。
3.参考材料
https://wiki.wgpsec.org/knowledge/ctf/iofile.html