IO FILE之fopen详解

阅读量    9775 |   稿费 300

分享到: QQ空间 新浪微博 微信 QQ facebook twitter

?

前言

最近打算详细整理下IO FILE相关的笔记,不少地方都是知道个大概,因此这次打算从源码出发,把IO FILE相关的东西都过一遍。

思路大致是fopenfwrite以及fread之类的IO函数的源码分析,再到libc2.24对vtable检查之前的利用方式,再到vtable检查的分析以及相应的对抗方式。

第一篇fopen详解,主要是基于源码的分析,源码的动态调试建议大家使用带调试符号的glibc,再次给大家推荐pwn_debug,可以很方便的安装带调试符号的glibc,使用debug模式即可。

?

源码分析

首先编写一个简单的调用fopen函数的c程序。

#include<stdio.h>

int main(){

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

编译出来之后使用pwn_debug的debug模式开启程序,或者指定带调试符号的glibc,我这里使用的glibc版本是2.23。接下来开始分析。

gdb跟进去fopen函数,可以看到fopen实际上是 _IO_new_fopen函数,该函数在/libio/iofopen.c文件中,可以看到它调用的是__fopen_internal

_IO_FILE *
_IO_new_fopen (const char *filename, const char *mode)
{
  return __fopen_internal (filename, mode, 1);
}

跟进去__fopen_internal中,关键源码如下:

_IO_FILE *
__fopen_internal (const char *filename, const char *mode, int is32)
{
  struct locked_FILE
  {
    struct _IO_FILE_plus fp;
#ifdef _IO_MTSAFE_IO
    _IO_lock_t lock;
#endif
    struct _IO_wide_data wd;
  } *new_f = (struct locked_FILE *) malloc (sizeof (struct locked_FILE));  ## step 1 分配内存

...

  _IO_no_init (&new_f->fp.file, 0, 0, &new_f->wd, &_IO_wfile_jumps); ## step 2 null初始化结构体数据 
...

  _IO_JUMPS (&new_f->fp) = &_IO_file_jumps; ## 设置vtable为_IO_file_jumps
  _IO_file_init (&new_f->fp); ## step 3 将file结构体链接进去_IO_list_all

...
  # step 4 打开文件
  if (_IO_file_fopen ((_IO_FILE *) new_f, filename, mode, is32) != NULL)
    return __fopen_maybe_mmap (&new_f->fp.file);

}

整个__fopen_internal函数包含四个部分:

  1. malloc分配内存空间。
  2. _IO_no_init 对file结构体进行null初始化。
  3. _IO_file_init将结构体链接进_IO_list_all链表。
  4. _IO_file_fopen执行系统调用打开文件。

下面详细分析跟进去每个子函数进行分析。

malloc分配内存空间

可以看到首先调用malloc函数分配了一个struct locked_FILE大小的结构体,这个结构体比函数刚开始的地方定义,在64位系统中为0x230,该结构体包含三个_IO_FILE_plus_IO_lock_t_IO_wide_data,其中_IO_FILE_plus为使用的IO FILE的结构体。执行完malloc后内存状态如下:

_IO_no_init 对file结构体进行null初始化

在分配完空间后,接着就调用_IO_no_init函数去null初始化结构体,跟进去该函数,函数在/libio/genops.c中:

void
_IO_old_init (_IO_FILE *fp, int flags)
{
  fp->_flags = _IO_MAGIC|flags;
  fp->_flags2 = 0;
  fp->_IO_buf_base = NULL;
  fp->_IO_buf_end = NULL;
  fp->_IO_read_base = NULL;
  fp->_IO_read_ptr = NULL;
  fp->_IO_read_end = NULL;
  fp->_IO_write_base = NULL;
  fp->_IO_write_ptr = NULL;
  fp->_IO_write_end = NULL;
  fp->_chain = NULL; /* Not necessary. */

  fp->_IO_save_base = NULL;
  fp->_IO_backup_base = NULL;
  fp->_IO_save_end = NULL;
  fp->_markers = NULL;
  fp->_cur_column = 0;
...
  fp->_vtable_offset = 0;
...
}
void
_IO_no_init (_IO_FILE *fp, int flags, int orientation,
         struct _IO_wide_data *wd, const struct _IO_jump_t *jmp)
{
  _IO_old_init (fp, flags);
  fp->_mode = orientation;
  ...
      ## 初始化fp的_wide_data字段
      fp->_wide_data = wd;
      fp->_wide_data->_IO_buf_base = NULL;
      fp->_wide_data->_IO_buf_end = NULL;
      fp->_wide_data->_IO_read_base = NULL;
      fp->_wide_data->_IO_read_ptr = NULL;
      fp->_wide_data->_IO_read_end = NULL;
      fp->_wide_data->_IO_write_base = NULL;
      fp->_wide_data->_IO_write_ptr = NULL;
      fp->_wide_data->_IO_write_end = NULL;
      fp->_wide_data->_IO_save_base = NULL;
      fp->_wide_data->_IO_backup_base = NULL;
      fp->_wide_data->_IO_save_end = NULL;

      fp->_wide_data->_wide_vtable = jmp;
    ...
  fp->_freeres_list = NULL;
}

可以看到函数最主要的功能是初始化locked_FILE里面的_IO_FILE_plus结构体,基本上将所有的值都初始化为null以及默认值,同时将_wide_data字段赋值并初始化。初始化结束后,FILE结构体如下:

_IO_file_init将结构体链接进_IO_list_all

在执行完_IO_no_init函数后,回到__fopen_internal函数,函数将_IO_FILE_plus结构体的vtable设置成了_IO_file_jumps,然后调用_IO_file_init_IO_FILE_plus结构体链接进入_IO_list_all链表,跟进去函数,函数在/libio/fileops.c中:

void
_IO_new_file_init (struct _IO_FILE_plus *fp)
{

  fp->file._offset = _IO_pos_BAD;
  fp->file._IO_file_flags |= CLOSED_FILEBUF_FLAGS;
  ## 调用_IO_link_in和设置_fileno
  _IO_link_in (fp);
  fp->file._fileno = -1;
}
libc_hidden_ver (_IO_new_file_init, _IO_file_init)

看到这个函数的主体就是调用了_IO_link_in函数,跟进去,函数在/libio/genops.c中:

void
_IO_link_in (struct _IO_FILE_plus *fp)
{
  ## 检查flag的标志位是否是_IO_LINKED
  if ((fp->file._flags & _IO_LINKED) == 0)
    {
      ## 设置_IO_LINKED标志位
      fp->file._flags |= _IO_LINKED;
      ...
      fp->file._chain = (_IO_FILE *) _IO_list_all;
      _IO_list_all = fp;
      ++_IO_list_all_stamp;
      ...
    }
}
libc_hidden_def (_IO_link_in)

之前一直都知道FILE结构体是通过_IO_list_all的单链表进行管理的,这里_IO_link_in函数的功能是检查FILE结构体是否包含_IO_LINKED标志,如果不包含则表示这个结构体没有链接进入_IO_list_all,则再后面把它链接进入_IO_list_all链表,同时设置FILE结构体的_chain字段为之前的链表的值,否则直接返回。

所以_IO_file_init主要功能是将FILE结构体链接进入_IO_list_all链表,在没执行_IO_file_init函数前_IO_list_all指向的是stderr结构体:

执行完后可以看到_IO_list_all指向的是申请出来的结构体:

同时此时FILE结构体的_chain字段指向了之前的stderr结构体:

_IO_file_fopen打开文件句柄

将FILE结构体链接到_IO_list_all链表后,程序返回到__fopen_internal中,接下来就调用_IO_new_file_fopen函数,跟进去该函数,函数在libio/fileops.c文件中:

_IO_FILE *
_IO_new_file_fopen (_IO_FILE *fp, const char *filename, const char *mode,
            int is32not64)
{

  ...
  ## 检查文件是否以打开,打开则返回
  if (_IO_file_is_open (fp))
    return 0;
  ## 设置文件打开模式
  switch (*mode)
    {
    case 'r':
      omode = O_RDONLY;
      read_write = _IO_NO_WRITES;
      break;
      ...    
     }
  ...
  ## 调用_IO_file_open函数
  result = _IO_file_open (fp, filename, omode|oflags, oprot, read_write,
              is32not64);
  ...
}
libc_hidden_ver (_IO_new_file_fopen, _IO_file_fopen)

函数先检查文件描述符是否打开,然后设置文件打开的模式,最后调用_IO_file_open函数,跟进去_IO_file_open函数,该函数在/libio/fileops.c里面:

_IO_FILE *
_IO_file_open (_IO_FILE *fp, const char *filename, int posix_mode, int prot,
           int read_write, int is32not64)
{
  int fdesc;

  ...
  # 调用系统函数open打开文件    
  fdesc = open (filename, posix_mode | (is32not64 ? 0 : O_LARGEFILE), prot);
  ...
  # 将文件描述符设置到FILE结构体的相应字段_fileno里
  fp->_fileno = fdesc;
  ...
  #再次调用_IO_link_in
  _IO_link_in ((struct _IO_FILE_plus *) fp);
  return fp;
}
libc_hidden_def (_IO_file_open)

函数的主要功能就是执行系统调用open打开文件,并将文件描述符赋值给FILE结构体的_fileno字段,最后再次调用_IO_link_in函数,确保该结构体被链接进入_IO_list_all链表。

执行完_IO_new_file_fopen函数后,FILE结构体为:

该函数执行完后,程序返回FILE结构体指针,分析结束

?

小结

看完代码后,可以将fopen整体的流程可以归纳为:

  1. malloc分配内存空间。
  2. _IO_no_init 对file结构体进行null初始化。
  3. _IO_file_init将结构体链接进_IO_list_all链表。
  4. _IO_file_fopen执行系统调用打开文件。

整个流程还是比较简单的,fopen返回之后_IO_list_all链表指向返回的FILE结构体,且FILE结构体的_chain字段指向之前的结构体(没有其他额外打开文件的话,将是指向stderr),同时其他的字段大多都是默认的null值,vtable存储的是__GI__IO_file_jumps函数表,截图如下。

香港最快现场开奖记录-香港最快现场开奖结果-香港最快直播开奖现场
m.2121buy.com m.304496092.com m.52chendumeishi.com m.52qqcz.com m.7568872.com m.99vv340.com m.agag70.com m.anpinglansai.com m.asshishicai.com m.baoshidamall.com m.bjoso.com m.bocdima.com m.bxxyzl.com m.cdjutaiedu.com m.cl1899.com m.cpexe.com m.cztxhg666.com m.diejuji66.com m.dmgjzx.com m.dphhw.com m.elitecld.com m.esexfree.com m.ezupdos.com m.fanfan-zone.com m.fengwoyidong.com m.getfault.com m.hcxiangjiao.com m.hf556.com m.hongfushangmao.com m.huibobet.com m.hyhatch.com m.hzybqh.com m.ibroyeur.com m.jiangyaqing.com m.ksuca.com m.liangdianjiaoyu.com m.lovegwu.com m.mayihuobang.com m.miaoxiangshike.com m.miiqin.com m.modelslimming.com m.msjyhs.com m.mysoushou.com m.myteenssex.com m.nanahey.com m.o48d.com m.o8jw.com m.pzsyr.com m.qgbrs.com m.rbrbt.com m.rfynq.com m.rqwdt.com m.rxbled.com m.rxdmn.com m.sct-ys.com m.sdyxtjy.com m.spark-eo.com m.spyware-pcrisk.com m.sxlrr.com m.syhrzg.com m.tadeyijia.com m.tengjingkj.com m.tiantianoa.com m.tykjr.com m.uxf7.com m.weakbug.com m.wgppd.com m.xajyz.net m.xiqidai.com m.yczszw.com m.youxiaad.com m.yue8888.com m.yzkths.com m.zqbf137.com m.zqssc05.com m.zsbxp.com m.zspxk.com m.zsxscj.com www.88366666.com www.942dn.com www.lottimes.com www.opc32.com www.ss-tm.com www.tiaopigui.com www.xmyinhe.com www.mfkof.com www.upsbbs.com www.gzesmb.com www.niqzone.com www.m-instyLe.cn m.1yguz.com m.2huoo.net m.51bpbpz.net m.53zy.net m.5ecn.net m.91youhui.net m.adospados.com m.afty.net m.afzn.net m.baobeiqianbao.net m.bestmeet.net m.bjbna.net m.bjkjwj.net m.cg365sc.net m.cnyntq.net m.congdash.net m.coybcs.net m.csoug.net m.dijier.net m.djhsc.net m.donghuiwan.com m.dsi-ag.com m.dzshgwx.net m.eamoy.net m.ecopote.com m.ehuiche.net m.eiconf.net m.eimeeting.net m.ez-ns.com m.freshkpop.net m.gaoxiaoduanzi.net m.glxiduobao.com m.guiqin.net m.guogai.net m.gydushu.com m.haokan5.net m.hltjq.net m.hshardcover.com m.huangjinnc.com m.huangzhe0910.com m.i517.net m.icauto.net m.ijiawei.net m.iyaorao.net m.iyuwei.net m.izxs.net m.jlskjk.com m.jnrstc.com m.jnxins.com m.joinchampions.com m.jsywym.net m.juezhe.net m.julisports.net m.kardon023.com m.keiee.net m.kkdb168.com m.ksb120.net m.kzbiz.net m.laziw.com m.lcoder.net m.lefuxx.com m.live-cam4sex.com m.louboutinb.com m.ly568.net m.matengfei.net m.mblabc.net m.mcmcmc.net m.meitwo.net m.miyueing.com m.mjrgb.com m.mmd47.com m.mms39.com m.nanjingwx.com m.naodongxueyuan.com m.ncwywk.com m.neimengguly.com m.newcityvr.com m.newhnwg.com m.ngzrb.com m.nianlai.net m.niuhuotong.com m.nj142.com m.nmgmmw.com m.nnwsn.com m.nongtaifruit.com m.npymh.com m.nqbcs.com m.nwezq.com m.oe2pq.com m.p950r.com m.panoad.net m.papasj.com m.pcfordlm.com m.pinkypunk.com m.pojox.net m.popo365.net m.portalfan.net m.qhy360.com m.qinghuayingyu.com m.qingjiankeji.com m.qirongzhongchou.net m.qiutianzichan.com m.quanminkuaiduobao.com m.quansiweizhiyuan.com m.readc.net m.rpocr.net m.sdell.net m.sdhzz.net m.senrijin.com m.shangzewangluo.com m.shanmeme.com m.shanrenzixunjituan.com m.shengwh.com m.shengzhiqi.net m.shfongwei.com m.shiguangji888.com m.shiyihui.net m.shop666.net m.shoppniac.com m.shyarun.com m.softsweet.net m.sxsgky.net m.szifresh.com m.tahuyu.net m.taiyear.net m.taowogo.com m.taxionghaizi.com m.thirftyfun.com m.tiqianme.com m.tjcbmy.com m.tjynet.com m.tyc1413191.com m.v-liandong.com m.v-quick.net m.vrwar.net m.walyy.net m.we30.net m.weixd.net m.wenancn.net m.wetrussian.com m.wuaimei.net m.wuyutong.net m.wzj0759.com m.xianyoujie.com m.xiaolangli.com m.xiaolieai.com m.xiawangshipin.com m.xiyuesh.com m.xuanf.net m.xyflower.net m.xzcit.net m.yifoobao.net m.yjhzttjq.com m.yjk1997.com m.ylmov.net m.yongyijinfu.com m.yueguoji.net m.zhansen8.com m.zhaokuandai.com m.zhc-nj.com m.zhongruiedu.com m.zivdoll.com m.zjjyjn.com m.zmkaolu.com m.zmtbs.com m.zmysjh.com m.zzfreemaker.com
分享到: QQ空间 新浪微博 微信 QQ facebook twitter
|推荐阅读
|发表评论
|评论列表
加载更多