0
点赞
收藏
分享

微信扫一扫

malloc源码分析(3)--heap的初始化

心存浪漫 2022-03-13 阅读 81
linux

文章目录

测试代码

其实代码很简单,但重要的是我们去看看这个到底是怎么分配出来的

#include<malloc.h>
int main(){
        void *p=malloc(0x10);
        void *p2=malloc(0x20);
        free(p);
        free(p2);
        p=NULL;
        p2=NULL;
        return 0;
}

最开始

可以看到最开始系统帮我们做了二进制文件,so等的映射,这个时候栈已经有了,但还没有堆
在这里插入图片描述
随着我们第一个malloc的执行,开始了堆的分配

第一次调用malloc

最开始进来其实他就尝试去获取__malloc_hook并判断是否为空,如果不为空就去调用(这也产生了使用__malloc_hook去get shell)
在这里插入图片描述
atomic_forced_read看他的函数名就可以知道,是原子级别的操作,与我们实际的堆分配关系不大(实际上他是防止其他线程的干扰)
可以看到最开始__malloc_hook是有值的,并且就是malloc_hook_ini的值
在这里插入图片描述

所以会去调用malloc_hook_ini,参数就是bytes我们malloc的大小,RETURN_ADDRESS(0),他是获取发生调用的位置,这里就是我们main函数的调用地址
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

malloc_hook_ini

可以看到这里手动帮我们清空了__malloc_hook,所以之后如果我们自己不手动修改__malloc_hook,系统不会再去调用hook操作
在这里插入图片描述

ptmalloc_init

看到函数名里的p就可以知道这个是处理多线程用的,其实没有影响我们的初始化

static void
ptmalloc_init (void)
{
#第一次进来时为-1,这个可以参考我讲的常量和全局变量
  if (__malloc_initialized >= 0)
    return;

  __malloc_initialized = 0;
#ifdef SHARED
  /* In case this libc copy is in a non-default namespace, never use brk.
     Likewise if dlopened from statically linked program.  */
  Dl_info di;
  struct link_map *l;

#这里我判断为否,所以没有进去
  if (_dl_open_hook != NULL
      || (_dl_addr (ptmalloc_init, &di, &l, NULL) != 0
          && l->l_ns != LM_ID_BASE))
    __morecore = __failing_morecore;
#endif

  thread_arena = &main_arena;
  thread_atfork (ptmalloc_lock_all, ptmalloc_unlock_all, ptmalloc_unlock_all2);
  const char *s = NULL;
  #这里的_envrion指向的时SHELL=/bin/sh
  if (__glibc_likely (_environ != NULL))
    {
      char **runp = _environ;
      char *envline;

#这里我没进去
      while (__builtin_expect ((envline = next_env_entry (&runp)) != NULL,
                               0))
        {
          size_t len = strcspn (envline, "=");

          if (envline[len] != '=')
            /* This is a "MALLOC_" variable at the end of the string
               without a '=' character.  Ignore it since otherwise we
               will access invalid memory below.  */
            continue;

          switch (len)
            {
            case 6:
              if (memcmp (envline, "CHECK_", 6) == 0)
                s = &envline[7];
              break;
            case 8:
              if (!__builtin_expect (__libc_enable_secure, 0))
                {
                  if (memcmp (envline, "TOP_PAD_", 8) == 0)
                    __libc_mallopt (M_TOP_PAD, atoi (&envline[9]));
                  else if (memcmp (envline, "PERTURB_", 8) == 0)
                    __libc_mallopt (M_PERTURB, atoi (&envline[9]));
                }
              break;
            case 9:
              if (!__builtin_expect (__libc_enable_secure, 0))
                {
                  if (memcmp (envline, "MMAP_MAX_", 9) == 0)
                    __libc_mallopt (M_MMAP_MAX, atoi (&envline[10]));
                  else if (memcmp (envline, "ARENA_MAX", 9) == 0)
                    __libc_mallopt (M_ARENA_MAX, atoi (&envline[10]));
                }
              break;
            case 10:
              if (!__builtin_expect (__libc_enable_secure, 0))
                {
                  if (memcmp (envline, "ARENA_TEST", 10) == 0)
                    __libc_mallopt (M_ARENA_TEST, atoi (&envline[11]));
                }
              break;
            case 15:
              if (!__builtin_expect (__libc_enable_secure, 0))
                {
                  if (memcmp (envline, "TRIM_THRESHOLD_", 15) == 0)
                    __libc_mallopt (M_TRIM_THRESHOLD, atoi (&envline[16]));
                  else if (memcmp (envline, "MMAP_THRESHOLD_", 15) == 0)
                    __libc_mallopt (M_MMAP_THRESHOLD, atoi (&envline[16]));
                }
              break;
            default:
              break;
            }
        }
    }
    #这里我也没进去,s=NULL
  if (s && s[0])
    {
      __libc_mallopt (M_CHECK_ACTION, (int) (s[0] - '0'));
      if (check_action != 0)
        __malloc_check_init ();
    }
    #这里又去读取了个hook函数的位置并且如果不为0尝试去调用
  void (*hook) (void) = atomic_forced_read (__malloc_initialize_hook);
  if (hook != NULL)
    (*hook)();
  __malloc_initialized = 1;
}

这里其实整体没什么好说的,就是想提一点
这里有个调用__malloc_initialize_hook
如果可以修改__malloc_initialize_hook并且把__malloc_initialized修改成负数,我们也可以调用到我们的hook,但其实一般来说没必要了很好

总结

总的来说这个函数没什么太大的用处,因为他没有去怎么影响我们malloc的过程,可以忽略

当结束ptmalloc后,相当于我们重新调用一次malloc(size)
所以第一遍初始化的时候相当于只要就是执行了一个ptmalloc_int,然后把__malloc_hook制空

第二次调用malloc

由于malloc_hook清空了,所以不会去调用
在这里插入图片描述
这里相当于时去把我们main_arena的地址放到ar_ptr里面
在这里插入图片描述
在这里插入图片描述
接下来就是比较重要的__int_malloc

__int_malloc

首先做个简单的校验,在我们常见方法提到过,只要bytes不大于(unsinge long) -0x40就可以,然后nb就保存堆管理实际需要给我们分配的大小,因为还要包括header
在这里插入图片描述
这里我们时0x10,所以nb应该是0x20
在这里插入图片描述
接下来就是一个判断,包括代码也这么说了,av也就是main-arena的地址不太可能为空,所以这个代码就跳过
在这里插入图片描述
这里时第一次判断大小,这里面global_max_fast这个时候应该还是0
在这里插入图片描述

在这里插入图片描述
所以当然不通过
在这里插入图片描述
这里是满足的,因为只要<0x400就成立,所以我们进来
可以看到里面主要就是一个找到对应的index,这个操作本质上就是/16,因为small_bin大小是等差数列,公差为16
bin_at就可以通过这个索引找到对应的双向链表的位置,同时由于bin_at的实现,相当于我们把这个虽然只是用来保存地址的双向链表也当成一个bin,他们会主动指向假想的头

这个唯一的好处就是我们之后在做增删的话,就不需要判断这个头是不是我们main_arena的,因为所有的结构都是一样的,也是利于后面代码的编写
在这里插入图片描述

在这里插入图片描述
这个时候由于还没有初始化各种bin,所以里面的bins都是null
在这里插入图片描述
之后有个这个判断,last(bin)实际上就是获得bin->bk,这里由于没初始化,所以就是0
在这里插入图片描述
这里判断一下,发现好像确实没有初始化,就开始初始化
在这里插入图片描述

malloc_consolidate

刚进来可以看见他做了一个这样的判断,其实这个函数在其他情况也会出现,一个简单的例子就是
当需要0x310的chunk现在有0x20,和0x300,这个时候我们可以free fastchunk来满足需求,不然会浪费
所以他这个判断的目的就是当没有初始化的时候gobal_max_fast就是为0,初始化了肯定不是0了,所以这个也就是一种判没判断初始化的条件,但这种判断方法不算是充要条件,感觉或许可以利用
在这里插入图片描述
这里因为不成立就进入了else环节,就开始做一些初始化工作
在这里插入图片描述

malloc_init_state

一进来就是如下,正如注释所言,建立循环双向链表
在这里插入图片描述
这里面bins共有254/2=127对循环双向列表结构,而我们这里是从1开始,其实是因为第0对有特殊的用途,这个我们暂时不说
在这里插入图片描述

#define bin_at(m, i) \
  (mbinptr) (((char *) &((m)->bins[((i) - 1) * 2]))			      \
             - offsetof (struct malloc_chunk, fd))

再复习一下bin_at函数,现在暂时还不清楚这个相当于是最小的i就是1嘛,因为如果是0就是负数,所以他这个刚好符合了我们的习惯(长时间使用c,一直习惯0是第一个),他这个刚刚好,1就是第一个,所以我们index为1的链表确确实实他就是第1对
这里我们第一对就是88嘛,但我们的bin其实是指向它前面
在这里插入图片描述

在这里插入图片描述
这是它最后的初始化效果,可以看到它这个真的很像一个chunk,只不过fd和bk都指向本身
在这里插入图片描述
熟悉双向链表的同学熟悉,我们这个相当于是初始化的第一个双向链表成员,我们可以直接通过bin->fd==bin来判断现在链表是否为空,同时他这个对后续的增加也很有帮助
比如说我们要添加一个,只需要
bin->fd=new_bins
bin->fd->bk=bin
就感觉其实很方便

然后这个一共做了127次
看看初始化完了的结果
在这里插入图片描述
可以看到每个都是指向自己的头部
在这里插入图片描述
后面就是判断你是不是main_arena,我们当然是
在这里插入图片描述
然后开始设置一些属性,可以看到这个时候才真正给global_max_fast设值
说出来你也不信,set_max_fast居然会给main_arena的top赋值???
在这里插入图片描述
下面一步相当于给main_arena做了一个FASTCHUNKS的标签,这个之后遇见了再说有什么用处

initial_top

在这里插入图片描述
unsorted_chunks其实就是bin_at(m,1)
这里其实获取的就是我们刚才看的第一块
而他的bk就是指向自己,所以这个判断是false

在这里插入图片描述
这里的nb就是我们要的大小,明显这个不满足,因为只要<0x400就都是in_smallbin_range

差点忘记这个idx是哪来的,就是我们之前在__int_malloc里面设置了
在这里插入图片描述
在这里插入图片描述
这里++相当于找到比我们大一号的块的双向链表
idx2block就是>>5位这里就直接变成0了
然后找到对应的bin_map,我暂时也不知道binmap有什么作用
idx2bit就是((1<<idx)&31),这里就是8
这里bit确实大于map,我们进来
在这里插入图片描述
这里具体的binmap代表什么我还不是很了解,但这里很明显因为我们现在没有空余的内容,所以只能去使用top chunk

这里就是我也不知道莫名其妙av->top就有值了,就之前我在set_max_fast之前没值,不知道为什么之后就有值了
在这里插入图片描述
因为这里的top chunk我们还没有初始化嘛,所以它就指向了自己,然后size相当于就是reminder指针也是0
在这里插入图片描述
这里size是0,所以当然不满足
在这里插入图片描述
进入这个判断
在这里插入图片描述
have_fastchunks(M) (((M)->flags & FASTCHUNKS_BIT) == 0)
还记得我们之前设置的这个位嘛,这个位为0应该表示我还有空闲的fast chunks,为1表示没有,所以我们这里进不去
接下来只能进入else
在这里插入图片描述

sysmalloc

一上来就是判断,这个判断很关键,涉及到后面的一个泄露libc地址
在这里插入图片描述
我们先看看那个threshold是多少
在这里插入图片描述
也就是0x20000,这就是malloc源码里面为什么会提到128kb的原因,只有>=0x20000,才会尝试去用mmap
而且后面的判断确实为true
在这里插入图片描述
不过我们这里确实没要求那么多,所以这个判断失败
在这里插入图片描述
在这里插入图片描述
这里是main_arena所以进了else
在这里插入图片描述
这里就是0x2000+0x20+0x20,尝试页对齐,因为pagesize是 0x1000嘛
在这里插入图片描述
所以我们这里就对齐到0x21000
往上有人说top chunk大小就是最开始给你分配0x21000,其实我们跟了之后发现不是很对,我们现在比如说要malloc(0x1000)
在这里插入图片描述
在这里插入图片描述
这里就会对齐到0x22000
在这里插入图片描述
所以说topchunk的大小跟你第一个malloc的块有关,但它基本大小是0x20020
在这里插入图片描述
今天先到这里了吧

举报

相关推荐

0 条评论