堆的一些基本思想

malloc,mmap

  • 当想要创建的堆过大时,malloc会调用mmap来创建堆
  • 这个堆的标识为2
  • mmap有prev_size和size,但是没有fd和bk,因为不会进bin
  • mmap创建的堆不会在heap那个空间里,而是在glibc中找一块位置进行创建(一般是在heap下面)
  • 并且对于这个的free也不一样,会使用系统调用来进行free
  • 释放它时size需要与页对齐
  • 并且释放后的堆块将不能读写

堆的结构,bin,tcache

这个就不说了,打多了之后都知道了。

有个注意点,创建堆时产生的指针是指向空白位置的,而bin中得到指针是指向大小的。

tcache的特性

  • 在glibc2.26之后引入了tcache技术
  • tache是个单链表,并且最大值默认是7
  • 在2.27高版本或者2.29版本中加入了key指针来防止double free
  • 在从fast bin或者small bin中取chunk事,会尽可能把剩余的chunk放入tcache bin

神奇的操作

  • unsorted bin在存进去第一个的时候会把它们的指针设为main_arena,而这个地址相对于libc的基址是不会变的

malloc_hook的劫持

  • malloc_hook一般用来打one_gadget,因为它的参数是创建chunk的大小

free_hook的劫持

  • 与malloc_hook不同的是,free_hook的参数是堆内容的地址,所以可以直接将它变成system函数运行。

IO_FILE

  • 在fopen的时候会创建所谓文件流的东西,它会分配在堆中,并以链表的形式串联起来。
  • 技巧:pwndbg的p /x命令可以查询一些存储的值

所有的file结构都是这样一个结构体:

struct _IO_FILE_plus
{
    _IO_FILE    file;
    _IO_jump_t   *vtable;
}

可以直接pwndbg调试的。抄一下别人博客的源码(侵权立删!):

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
};

而vtable指针里面又有很多跳转函数:

void * funcs[] = {
   1 NULL, // "extra word"
   2 NULL, // DUMMY
   3 exit, // finish                <_IO_new_file_finish>
   4 NULL, // overflow                <_IO_new_file_overflow>
   5 NULL, // underflow                <_IO_new_file_underflow>
   6 NULL, // uflow                  <__GI__IO_default_uflow>
   7 NULL, // pbackfail                 <__GI__IO_default_pbackfail>
   8 NULL, // xsputn  #printf        <_IO_new_file_xsputn>
   9 NULL, // xsgetn               <__GI__IO_file_xsgetn>
   10 NULL, // seekoff                <_IO_new_file_seekoff>
   11 NULL, // seekpos               <_IO_default_seekpos>
   12 NULL, // setbuf                <_IO_new_file_setbuf>
   13 NULL, // sync                  <_IO_new_file_sync>
   14 NULL, // doallocate               <__GI__IO_file_doallocate>
   15 NULL, // read                 <__GI__IO_file_read>
   16 NULL, // write                <_IO_new_file_write>
   17 NULL, // seek                   <__GI__IO_file_seek>
   18 pwn,  // close                   <__GI__IO_file_close>
   19 NULL, // stat                       <__GI__IO_file_stat>
   20 NULL, // showmanyc           <_IO_default_showmanyc>
   21 NULL, // imbue              <_IO_default_imbue>
};

神奇的是,puts函数居然也会调用这里面的xsputn,overflow