首页
社区
课程
招聘
初识IO_FILE Exploitation
发表于: 2025-12-10 15:15 2771

初识IO_FILE Exploitation

2025-12-10 15:15
2771

参考链接:

3c6K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6U0N6r3k6Q4x3X3c8%4K9h3E0A6i4K6u0W2L8%4u0Y4i4K6u0r3M7s2N6F1i4K6u0r3L8r3W2F1N6i4S2Q4x3V1k6#2M7$3g2J5i4K6u0V1L8h3!0V1k6g2)9J5c8X3W2G2i4K6u0V1k6X3W2D9k6g2)9J5c8X3W2F1N6s2u0G2k6s2g2U0N6r3W2G2L8W2)9J5c8R3`.`.

cddK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6U0N6r3k6Q4x3X3c8%4K9h3E0A6i4K6u0W2L8%4u0Y4i4K6u0r3M7s2N6F1i4K6u0r3L8r3W2F1N6i4S2Q4x3V1k6#2M7$3g2J5i4K6u0V1L8h3!0V1k6g2)9J5c8X3W2G2i4K6u0V1k6X3W2D9k6g2)9J5c8X3k6S2K9$3g2Q4x3X3c8$3N6r3q4T1L8r3g2Q4x3X3c8W2P5s2m8D9L8$3W2@1i4K6u0r3

记一次跟着ctiwiki学习IO_FILE Exploitation的过程,我会在有些地方调试看看,加深理解。大部分内容都是来自ctfwiki。

我这里是为了链接libc2.23版本的,方便后面调试。

下载有调试符号和没调试符号的libc

这里的the_end程序其实就是后面2018 HCTF the_end题目的源程序

查看libc文件的调试链接

更改链接为有调试符号的

修改前的效果

修改之后的效果

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

FILE 结构定义在 libio.h 中,如下所示

进程中的 FILE 结构会通过_chain 域彼此连接形成一个链表,链表头部用全局变量_IO_list_all 表示,通过这个值我们可以遍历所有的 FILE 结构。

在标准 I/O 库中,每个程序启动时有三个文件流是自动打开的:stdin、stdout、stderr。因此在初始状态下,_IO_list_all 指向了一个有这些文件流构成的链表,但是需要注意的是这三个文件流位于 libc.so 的数据段。而我们使用 fopen 创建的文件流是分配在堆内存上的。

我们可以在 libc.so 中找到 stdin\stdout\stderr 等符号,这些符号是指向 FILE 结构的指针,真正结构的符号是

p _IO_list_all可以看到_IO_list_all是指向_IO_2_1_stderr_,而且地址位于libc.so上。注意结构体是_IO_FILE_plus,后面会说到

p _IO_2_1_stderr_再看_IO_2_1_stderr_的结构,可以看到_chain指向了_IO_2_1_stdout_

p _IO_2_1_stdout_再看_IO_2_1_stderr_的结构,可以看到_chain指向了_IO_2_1_stdin_

p _IO_2_1_stdin_看_IO_2_1_stdin_的结构,可以看到_chain是指向0x0,为空。

从这四个图可以看到链表指向是_IO_list_all --> IO_2_1_stderr --> IO_2_1_stdout --> IO_2_1_stdin

在初始状态下,链表中只有这三个文件流。

但是事实上_IO_FILE 结构外包裹着另一种结构_IO_FILE_plus,其中包含了一个重要的指针 vtable 指向了一系列函数指针。

在 libc2.23 版本下,32 位的 vtable 偏移为 0x94,64 位偏移为 0xd8

从上面三张图可以看到_IO_2_1_stderr_,IO_2_1_stdout,_IO_2_1_stdin_都是这种结构,vtable指向的地址都是一样的。

也可以看到64位下偏移是0xd8

vtable 是 IO_jump_t 类型的指针,IO_jump_t 中保存了一些函数指针,在后面我们会看到在一系列标准 IO 函数中会调用这些函数指针

p *(struct _IO_jump_t *)0x7ffff7dd06e0查看vtable的结构,以 __GI_为前缀的符号通常是 glibc 内部用于绑定到实际实现函数的别名,其真正的函数名是去掉__GI_的

先下个源码wget 70aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6X3N6s2m8Q4x3X3g2Y4L8Y4g2Q4x3X3g2G2M7X3N6Q4x3V1k6Y4L8Y4g2Q4x3V1k6Y4L8r3W2T1j5#2)9J5c8X3N6D9K9h3u0U0i4K6u0V1x3W2)9J5k6e0t1K6i4K6u0W2N6r3q4J5i4K6u0W2P5s2Z5`.,方便后面看fread这些函数

tar -xvf glibc-2.23.tar.xz

fread 是标准 IO 库函数,作用是从文件流中读数据,函数原型如下

fread 函数的各个参数作用如下:

cat glibc-2.23/libio/iofread.c,fread 的代码位于/libio/iofread.c 中,函数名为_IO_fread,可以看到这个函数会接着调用_IO_sgetn函数。

_IO_sgetn函数定义在libio/genops.c这个文件内,grep -C 3 "_IO_sgetn" glibc-2.23/libio/genops.c,可以看到在_IO_sgetn 函数中会直接返回_IO_XSGETN

grep -C 3 "_IO_XSGETN" glibc-2.23/libio/libioP.h_IO_XSGETN是一个在 glibc 的 IO 系统内部用于实现多态调用的宏,它的定义位于头文件 libio/libioP.h中,AI是这样解释的:这里的 JUMP2是另一个宏,负责最终的函数跳转。它的作用是:根据传入的 FILE 结构体 (**FP****) 找到其对应的虚函数表 (vtable),然后从表中取出 ****__xsgetn**函数指针并调用它,同时将参数 FP, DATA, N传递过去

也就是说fread最后会调用vtable表中的__xsgetn函数指针,最后调用了__GI__IO_file_xsgetn。这个以 __GI_为前缀的符号通常是 glibc 内部用于绑定到实际实现函数的别名,其真正的函数定义就是 _IO_file_xsgetn

grep -A 60 "_IO_file_xsgetn" glibc-2.23/libio/fileops.c这个函数内还调用了vtable中的__underflow指针

类似地最终会调用系统接口 read 函数

fread --> _IO_sgetn --> _IO_XSGETN --> __xsgetn --> __IO_file_xsgetn --> __underflow --> _IO_new_file_underflow --> _IO_SYSREAD

fwrite 同样是标准 IO 库函数,作用是向文件流写入数据,函数原型如下

fwrite 函数的各个参数作用如下:

<font style="color:rgba(0, 0, 0, 0.87);">cat glibc-2.23/libio/iofwrite.c</font>fwrite 的代码位于/libio/iofwrite.c 中,函数名为_IO_fwrite,可以看到这个函数会接着调用_IO_sputn函数。

grep "_IO_sputn" glibc-2.23/libio/libioP.h,而_IO_sputn是一个宏,它的定义位于 glibc 源码的 libio/libioP.h 头文件中,AI解释如下:这个宏的核心作用是作为函数调用的路由,它会根据传入的 FILE结构体找到对应的虚函数表(vtable),并调用其中的 __xsputn函数指针 。

grep -C 3 "_IO_XSPUTN" glibc-2.35/libio/libioP.h

grep -C 90 "_IO_new_file_xsputn" glibc-2.23/libio/fileops.c在_IO_new_file_xsputn函数内还可以发现调用了_IO_OVERFLOW函数

grep -C 3 "_IO_OVERFLOW" glibc-2.35/libio/libioP.h会跳转到__overflow指针

根据上文得到的结果,会调用_IO_new_file_overflow函数

grep -C 30 "_IO_new_file_overflow" glibc-2.35/libio/fileops.c调用了_IO_do_write

类似地最终会调用系统接口 write 函数

fwrite --> _IO_sputn --> _IO_XSPUTN --> __xsputn --> _IO_new_file_xsputn --> _IO_OVERFLOW --> __overflow --> _IO_new_file_overflow --> _IO_do_write --> _IO_new_do_write --> new_do_write --> _IO_SYSWRITE

fopen 在标准 IO 库中用于打开文件,函数原型如下

在 fopen 内部会创建 FILE 结构并进行一些初始化操作,下面来看一下这个过程

cat glibc-2.23/libio/iofopen.c,这个函数在libio/iofopen.c文件内,函数名为_IO_new_fopen,该函数直接调用__fopen_internal

首先在 fopen 对应的函数__fopen_internal 内部会调用 malloc 函数,分配 FILE 结构的空间。因此我们可以获知 FILE 结构是存储在堆上的

之后会为创建的 FILE 初始化 vtable,并调用_IO_file_init 进一步初始化操作。

在_IO_file_init 函数的初始化操作中,会调用_IO_link_in 把新分配的 FILE 链入_IO_list_all 为起始的 FILE 链表中

grep -A 60 "_IO_new_file_init" glibc-2.23/libio/fileops.c这里用了另一个函数名表示初始化函数

grep -A 60 "_IO_link_in" glibc-2.23/libio/genops.c

之后__fopen_internal 函数会调用_IO_file_fopen 函数打开目标文件,_IO_file_fopen 会根据用户传入的打开模式进行打开操作,总之最后会调用到系统接口 open 函数,这里不再深入。

总结一下 fopen 的操作是

fopen --> _IO_new_fopen --> __fopen_internal --> _IO_file_fopen --> .....

fclose 是标准 IO 库中用于关闭已打开文件的函数,其作用与 fopen 相反。

功能:关闭一个文件流,使用 fclose 就可以把缓冲区内最后剩余的数据输出到磁盘文件中,并释放文件指针和有关的缓冲区

cat glibc-2.23/libio/iofclose.cfclose在libio/iofclose.c文件中的函数名是_IO_new_fclose

fclose 首先会调用_IO_unlink_it 将指定的 FILE 从_chain 链表中脱链

之后会调用_IO_file_close_it 函数,_IO_file_close_it 会调用系统接口 close 关闭文件

最后调用 vtable 中的_IO_FINISH,其对应的是_IO_file_finish 函数,其中会调用 free 函数释放之前分配的 FILE 结构

printf 和 puts 是常用的输出函数,在 printf 的参数是以'\n'结束的纯字符串时,printf 会被优化为 puts 函数并去除换行符。

puts 在源码中实现的函数是_IO_puts,这个函数的操作与 fwrite 的流程大致相同,函数内部同样会调用 vtable 中的_IO_sputn,结果会执行_IO_new_file_xsputn,最后会调用到系统接口 write 函数。

printf 的调用栈回溯如下,同样是通过_IO_file_xsputn 实现

前面我们介绍了 Linux 中文件流的特性(FILE),我们可以得知 Linux 中的一些常见的 IO 操作函数都需要经过 FILE 结构进行处理。尤其是_IO_FILE_plus 结构中存在 vtable,一些函数会取出 vtable 中的指针进行调用。

因此伪造 vtable 劫持程序流程的中心思想就是针对_IO_FILE_plus 的 vtable 动手脚,通过把 vtable 指向我们控制的内存,并在其中布置函数指针来实现。

因此 vtable 劫持分为两种,一种是直接改写 vtable 中的函数指针,通过任意地址写就可以实现。另一种是覆盖 vtable 的指针指向我们控制的内存,然后在其中布置函数指针。

这里演示了修改 vtable 中的指针,首先需要知道_IO_FILE_plus 位于哪里,对于 fopen 的情况下是位于堆内存,对于 stdin\stdout\stderr 是位于 libc.so 中。

根据 vtable 在_IO_FILE_plus 的偏移得到 vtable 的地址,在 64 位系统下偏移是 0xd8。之后需要搞清楚欲劫持的 IO 函数会调用 vtable 中的哪个函数。 printf 会调用 vtable 中的 xsputn,并且 xsputn 的是 vtable 中第八项之后就可以写入这个指针进行劫持。

并且在 xsputn 等 vtable 函数进行调用时,传入的第一个参数其实是对应的_IO_FILE_plus 地址。比如这例子调用 printf,传递给 vtable 的第一个参数就是_IO_2_1_stdout_的地址。

类似这种,第一个参数都是fp,fp实际上就是_IO_FILE_plus的地址。

利用这点可以实现给劫持的 vtable 函数传參,比如以下这个代码,memcopy(fp,"sh",3);就是把file结构前三个字节改成了sh\0,然后劫持xsputn就可以实现system(sh)了

但是在目前 libc2.23 版本下,位于 libc 数据段的 vtable 是不可以进行写入的。

不过,通过在可控的内存中伪造 vtable 的方法依然可以实现利用。

我们首先分配一款内存来存放伪造的 vtable,之后修改_IO_FILE_plus 的 vtable 指针指向这块内存。因为 vtable 中的指针我们放置的是 system 函数的地址,因此需要传递参数 "/bin/sh" 或 "sh"。

因为 vtable 中的函数调用时会把对应的_IO_FILE_plus 指针作为第一个参数传递,因此这里我们把 "sh" 写入_IO_FILE_plus 头部。之后对 fwrite 的调用就会经过我们伪造的 vtable 执行 system("sh")。

同样,如果程序中不存在 fopen 等函数创建的_IO_FILE 时,也可以选择 stdin\stdout\stderr 等位于 libc.so 中的_IO_FILE,这些流在 printf\scanf 等函数中就会被使用到。在 libc2.23 之前,这些 vtable 是可以写入并且不存在其他检测的。

可以看到main函数里开头就打印了sleep函数的地址,我们可以根据这个地址找到libc版本是libc6_2.23-0ubuntu10_amd64

此外还有个for循环可以任意地址写入总共5个字节
思路:
● 利用的是在程序调用 exit 后,会遍历 _IO_list_all ,调用 IO_2_1_stdout 下的 vtable 中 _setbuf 函数。
● 可以先修改两个字节在当前 vtable 附近伪造一个 fake_vtable ,然后使用 3 个字节修改 fake_vtable 中 _setbuf 的内容为 one_gadget。

我们先查看_IO_2_1_stdout_在libc文件中的偏移,为0x3c5620

再根据vtable跟FILE结构的偏移是0xd8,可以算出vtable的地址是:0x3c56f8。

从这里可以看到__setbuf是第12个元素,偏移是11个0x8字节,也就是0x58

查看虚表地址附近的内容,我们需要找到一个fake_vtable地址,该地址的0x58偏移是一个libc地址,我们可以把该地址改成one_gadget。也就是fake_vtable_addr + 0x58 = libc_base + onegadget

我们可以把0x7ffff7dd26b8当成fake_vtable地址,然后在0x58偏移的位置伪造onegadget的地址

one_gadget ./libc6_2.23-0ubuntu10_amd64/lib/x86_64-linux-gnu/libc-2.23.so有三个gadget,到时候三个都试一下,看哪个可以

计算一下偏移

调试的时候看到已经把目标地址改成one_gadget地址了

也成功跳转到了onegadget,不知道为什么就是拿不到shell, Got EOF了

报了这个错误,AI说是onegadget的条件不满足,三个gadget都试过了也没法,看别人的wp好像也是本地打不通。。。没辙了

wget 749K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6J5j5i4N6Q4x3X3g2Y4K9i4c8Z5N6h3u0#2M7$3g2J5j5$3!0F1N6r3g2F1N6q4)9J5k6h3y4G2L8g2)9J5c8X3y4@1k6W2)9J5k6s2N6A6K9$3W2Q4x3V1k6U0N6r3k6Q4x3X3c8U0K9r3q4D9L8r3g2F1k6$3g2K6i4K6u0r3M7X3g2X3M7#2)9J5c8X3S2W2j5h3c8K6i4K6u0r3L8h3q4K6N6r3g2J5i4K6u0r3M7s2N6F1i4K6u0r3L8r3W2F1N6i4S2Q4x3V1k6#2M7$3g2J5i4K6u0V1L8h3!0V1k6g2)9J5c8X3W2G2i4K6u0V1k6X3W2D9k6g2)9J5c8U0t1H3x3e0S2Q4y4h3k6Z5j5%4c8X3i4K6g2X3N6r3S2W2i4K6g2X3k6h3&6V1i4K6u0r3N6r3S2W2i4K6g2X3k6h3&6V1
wget 75aK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3I4S2N6h3&6U0K9s2m8S2k6r3I4A6j5Y4u0S2M7X3W2S2L8W2)9J5k6h3&6W2N6q4)9J5c8U0x3#2x3K6f1J5x3K6M7H3z5g2)9J5c8X3I4A6j5X3x3$3i4K6u0V1k6r3u0Y4i4K6g2X3x3W2)9J5k6e0t1K6i4K6u0V1x3s2g2T1N6h3&6@1N6e0p5H3i4K6g2X3j5h3#2V1y4U0c8Q4x3X3g2V1k6h3t1`.
dpkg -x libc6-dbg_2.23-0ubuntu10_amd64.deb libc6-dbg_2.23-0ubuntu10_amd64
wget 8cfK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6D9j5i4g2F1j5$3S2H3j5h3c8D9K9h3u0J5j5i4u0A6j5h3&6Q4x3X3g2F1k6i4c8Q4x3V1j5K6y4e0x3#2x3U0x3%4x3U0W2Q4x3V1k6D9K9h3u0U0y4W2)9#2k6U0u0Q4x3X3f1J5x3#2)9J5k6o6m8#2j5Y4g2F1N6s2f1I4x3q4)9#2k6X3q4E0k6o6j5@1i4K6u0W2k6r3g2T1
dpkg -x libc6_2.23-0ubuntu10_amd64.deb libc6_2.23-0ubuntu10_amd64
wget 647K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6J5j5i4N6Q4x3X3g2Y4K9i4c8Z5N6h3u0#2M7$3g2J5j5$3!0F1N6r3g2F1N6q4)9J5k6h3y4G2L8g2)9J5c8X3y4@1k6W2)9J5k6s2N6A6K9$3W2Q4x3V1k6U0N6r3k6Q4x3X3c8U0K9r3q4D9L8r3g2F1k6$3g2K6i4K6u0r3M7X3g2X3M7#2)9J5c8X3S2W2j5h3c8K6i4K6u0r3L8h3q4K6N6r3g2J5i4K6u0r3M7s2N6F1i4K6u0r3L8r3W2F1N6i4S2Q4x3V1k6#2M7$3g2J5i4K6u0V1L8h3!0V1k6g2)9J5c8X3W2G2i4K6u0V1k6X3W2D9k6g2)9J5c8U0t1H3x3e0S2Q4y4h3k6Z5j5%4c8X3i4K6g2X3N6r3S2W2i4K6g2X3k6h3&6V1i4K6u0r3N6r3S2W2i4K6g2X3k6h3&6V1
wget 4c2K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3I4S2N6h3&6U0K9s2m8S2k6r3I4A6j5Y4u0S2M7X3W2S2L8W2)9J5k6h3&6W2N6q4)9J5c8U0x3#2x3K6f1J5x3K6M7H3z5g2)9J5c8X3I4A6j5X3x3$3i4K6u0V1k6r3u0Y4i4K6g2X3x3W2)9J5k6e0t1K6i4K6u0V1x3s2g2T1N6h3&6@1N6e0p5H3i4K6g2X3j5h3#2V1y4U0c8Q4x3X3g2V1k6h3t1`.
dpkg -x libc6-dbg_2.23-0ubuntu10_amd64.deb libc6-dbg_2.23-0ubuntu10_amd64
wget 5eaK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6D9j5i4g2F1j5$3S2H3j5h3c8D9K9h3u0J5j5i4u0A6j5h3&6Q4x3X3g2F1k6i4c8Q4x3V1j5K6y4e0x3#2x3U0x3%4x3U0W2Q4x3V1k6D9K9h3u0U0y4W2)9#2k6U0u0Q4x3X3f1J5x3#2)9J5k6o6m8#2j5Y4g2F1N6s2f1I4x3q4)9#2k6X3q4E0k6o6j5@1i4K6u0W2k6r3g2T1
dpkg -x libc6_2.23-0ubuntu10_amd64.deb libc6_2.23-0ubuntu10_amd64
patchelf --set-interpreter ./libc6_2.23-0ubuntu10_amd64/lib/x86_64-linux-gnu/ld-2.23.so ./the_end
patchelf --replace-needed libc.so.6 ./libc6_2.23-0ubuntu10_amd64/lib/x86_64-linux-gnu/libc-2.23.so ./the_end
patchelf --set-interpreter ./libc6_2.23-0ubuntu10_amd64/lib/x86_64-linux-gnu/ld-2.23.so ./the_end
patchelf --replace-needed libc.so.6 ./libc6_2.23-0ubuntu10_amd64/lib/x86_64-linux-gnu/libc-2.23.so ./the_end
readelf -x .gnu_debuglink ./libc6_2.23-0ubuntu10_amd64/lib/x86_64-linux-gnu/libc-2.23.so
readelf -x .gnu_debuglink ./libc6_2.23-0ubuntu10_amd64/lib/x86_64-linux-gnu/libc-2.23.so
objcopy -R .gnu_debuglink ./libc6_2.23-0ubuntu10_amd64/lib/x86_64-linux-gnu/libc-2.23.so
cp libc6-dbg_2.23-0ubuntu10_amd64/usr/lib/debug/lib/x86_64-linux-gnu/libc-2.23.so ./libc6_2.23-0ubuntu10_amd64/lib/x86_64-linux-gnu/libc2.23
objcopy --add-gnu-debuglink=./libc6_2.23-0ubuntu10_amd64/lib/x86_64-linux-gnu/libc2.23 ./libc6_2.23-0ubuntu10_amd64/lib/x86_64-linux-gnu/libc-2.23.so
objcopy -R .gnu_debuglink ./libc6_2.23-0ubuntu10_amd64/lib/x86_64-linux-gnu/libc-2.23.so
cp libc6-dbg_2.23-0ubuntu10_amd64/usr/lib/debug/lib/x86_64-linux-gnu/libc-2.23.so ./libc6_2.23-0ubuntu10_amd64/lib/x86_64-linux-gnu/libc2.23
objcopy --add-gnu-debuglink=./libc6_2.23-0ubuntu10_amd64/lib/x86_64-linux-gnu/libc2.23 ./libc6_2.23-0ubuntu10_amd64/lib/x86_64-linux-gnu/libc-2.23.so
struct _IO_FILE {
  int _flags;       /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
 
  /* The following pointers correspond to the C++ streambuf protocol. */
  /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
  char* _IO_read_ptr;   /* Current read pointer */
  char* _IO_read_end;   /* End of get area. */
  char* _IO_read_base;  /* Start of putback+get area. */
  char* _IO_write_base; /* Start of put area. */
  char* _IO_write_ptr;  /* Current put pointer. */
  char* _IO_write_end;  /* End of put area. */
  char* _IO_buf_base;   /* Start of reserve area. */
  char* _IO_buf_end;    /* End of reserve area. */
  /* The following fields are used to support backing up and undo. */
  char *_IO_save_base; /* Pointer to start of non-current get area. */
  char *_IO_backup_base;  /* Pointer to first valid character of backup area */
  char *_IO_save_end; /* Pointer to end of non-current get area. */
 
  struct _IO_marker *_markers;
 
  struct _IO_FILE *_chain;
 
  int _fileno;
#if 0
  int _blksize;
#else
  int _flags2;
#endif
  _IO_off_t _old_offset; /* This used to be _offset but it's too small.  */
 
#define __HAVE_COLUMN /* temporary */
  /* 1+column number of pbase(); 0 is unknown. */
  unsigned short _cur_column;
  signed char _vtable_offset;
  char _shortbuf[1];
 
  /*  char* _save_gptr;  char* _save_egptr; */
 
  _IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};
struct _IO_FILE_complete
{
  struct _IO_FILE _file;
#endif
#if defined _G_IO_IO_FILE_VERSION && _G_IO_IO_FILE_VERSION == 0x20001
  _IO_off64_t _offset;
# if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
  /* Wide character stream stuff.  */
  struct _IO_codecvt *_codecvt;
  struct _IO_wide_data *_wide_data;
  struct _IO_FILE *_freeres_list;
  void *_freeres_buf;
# else
  void *__pad1;
  void *__pad2;
  void *__pad3;
  void *__pad4;
 
  size_t __pad5;
  int _mode;
  /* Make sure we don't get into trouble again.  */
  char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
#endif
};
struct _IO_FILE {
  int _flags;       /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
 
  /* The following pointers correspond to the C++ streambuf protocol. */
  /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
  char* _IO_read_ptr;   /* Current read pointer */
  char* _IO_read_end;   /* End of get area. */
  char* _IO_read_base;  /* Start of putback+get area. */
  char* _IO_write_base; /* Start of put area. */
  char* _IO_write_ptr;  /* Current put pointer. */
  char* _IO_write_end;  /* End of put area. */
  char* _IO_buf_base;   /* Start of reserve area. */
  char* _IO_buf_end;    /* End of reserve area. */
  /* The following fields are used to support backing up and undo. */
  char *_IO_save_base; /* Pointer to start of non-current get area. */
  char *_IO_backup_base;  /* Pointer to first valid character of backup area */
  char *_IO_save_end; /* Pointer to end of non-current get area. */
 
  struct _IO_marker *_markers;
 
  struct _IO_FILE *_chain;
 
  int _fileno;
#if 0
  int _blksize;
#else
  int _flags2;
#endif
  _IO_off_t _old_offset; /* This used to be _offset but it's too small.  */
 
#define __HAVE_COLUMN /* temporary */
  /* 1+column number of pbase(); 0 is unknown. */
  unsigned short _cur_column;
  signed char _vtable_offset;
  char _shortbuf[1];
 
  /*  char* _save_gptr;  char* _save_egptr; */
 
  _IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};
struct _IO_FILE_complete
{
  struct _IO_FILE _file;
#endif
#if defined _G_IO_IO_FILE_VERSION && _G_IO_IO_FILE_VERSION == 0x20001
  _IO_off64_t _offset;
# if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
  /* Wide character stream stuff.  */
  struct _IO_codecvt *_codecvt;
  struct _IO_wide_data *_wide_data;
  struct _IO_FILE *_freeres_list;
  void *_freeres_buf;
# else
  void *__pad1;
  void *__pad2;
  void *__pad3;
  void *__pad4;
 
  size_t __pad5;
  int _mode;
  /* Make sure we don't get into trouble again.  */
  char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
#endif
};
_IO_2_1_stderr_
_IO_2_1_stdout_
_IO_2_1_stdin_
_IO_2_1_stderr_
_IO_2_1_stdout_
_IO_2_1_stdin_
struct _IO_FILE_plus
{
    _IO_FILE    file;
    IO_jump_t   *vtable;
}
struct _IO_FILE_plus
{
    _IO_FILE    file;
    IO_jump_t   *vtable;
}
void * funcs[] = {
   1 NULL, // "extra word"
   2 NULL, // DUMMY
   3 exit, // finish
   4 NULL, // overflow
   5 NULL, // underflow
   6 NULL, // uflow
   7 NULL, // pbackfail
    
   8 NULL, // xsputn  #printf
   9 NULL, // xsgetn
   10 NULL, // seekoff
   11 NULL, // seekpos
   12 NULL, // setbuf
   13 NULL, // sync
   14 NULL, // doallocate
   15 NULL, // read
   16 NULL, // write
   17 NULL, // seek
   18 pwn,  // close
   19 NULL, // stat
   20 NULL, // showmanyc
   21 NULL, // imbue
};
void * funcs[] = {
   1 NULL, // "extra word"

[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回