学逆向论坛

找回密码
立即注册

只需一步,快速开始

发新帖

2万

积分

41

好友

1171

主题
发表于 2020-9-2 09:47:23 | 查看: 2243| 回复: 0

相关题目:

unlink
unlink 用来将一个双向链表(只存储空闲的 chunk)中的一个元素取出来,可能在以下地方使用
  • malloc
  • 从恰好大小合适的 large bin 中获取 chunk。
  • 这里需要注意的是 fastbin 与 small bin 就没有使用 unlink,这就是为什么漏洞会经常出现在它们这里的原因。
  • 依次遍历处理 unsorted bin 时也没有使用 unlink 。
  • 从比请求的 chunk 所在的 bin 大的 bin 中取 chunk。
  • free
  • 后向合并,合并物理相邻低地址空闲 chunk。
  • 前向合并,合并物理相邻高地址空闲 chunk(除了 top chunk)。
  • malloc_consolidate
  • 后向合并,合并物理相邻低地址空闲 chunk。
  • 前向合并,合并物理相邻高地址空闲 chunk(除了 top chunk)。
  • realloc
  • 前向扩展,合并物理相邻高地址空闲 chunk(除了 top chunk)。
由于 unlink 使用非常频繁,所以 unlink 被实现为了一个宏,如下
/* Take a chunk off a bin list */
  // unlink p
  #define unlink(AV, P, BK, FD) {                                            \
  // 由于 P 已经在双向链表中,所以有两个地方记录其大小,所以检查一下其大小是否一致。
  if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))      \
  malloc_printerr ("corrupted size vs. prev_size");               \
  FD = P->fd;                                                                      \
  BK = P->bk;                                                                      \
  // 防止攻击者简单篡改空闲的 chunk 的 fd 与 bk 来实现任意写的效果。
  if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                      \
  malloc_printerr (check_action, "corrupted double-linked list", P, AV);  \
  else {                                                                      \
  FD->bk = BK;                                                              \
  BK->fd = FD;                                                              \
  // 下面主要考虑 P 对应的 nextsize 双向链表的修改
  if (!in_smallbin_range (chunksize_nomask (P))                              \
  // 如果P->fd_nextsize为 NULL,表明 P 未插入到 nextsize 链表中。
  // 那么其实也就没有必要对 nextsize 字段进行修改了。
  // 这里没有去判断 bk_nextsize 字段,可能会出问题。
  && __builtin_expect (P->fd_nextsize != NULL, 0)) {                      \
  // 类似于小的 chunk 的检查思路
  if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0)              \
  || __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0))    \
  malloc_printerr (check_action,                                      \
  "corrupted double-linked list (not small)",    \
  P, AV);                                              \
  // 这里说明 P 已经在 nextsize 链表中了。
  // 如果 FD 没有在 nextsize 链表中
  if (FD->fd_nextsize == NULL) {                                      \
  // 如果 nextsize 串起来的双链表只有 P 本身,那就直接拿走 P
  // 令 FD 为 nextsize 串起来的
  if (P->fd_nextsize == P)                                      \
  FD->fd_nextsize = FD->bk_nextsize = FD;                      \
  else {                                                              \
  // 否则我们需要将 FD 插入到 nextsize 形成的双链表中
  FD->fd_nextsize = P->fd_nextsize;                              \
  FD->bk_nextsize = P->bk_nextsize;                              \
  P->fd_nextsize->bk_nextsize = FD;                              \
  P->bk_nextsize->fd_nextsize = FD;                              \
  }                                                              \
  } else {                                                              \
  // 如果在的话,直接拿走即可
  P->fd_nextsize->bk_nextsize = P->bk_nextsize;                      \
  P->bk_nextsize->fd_nextsize = P->fd_nextsize;                      \
  }                                                                      \
  }                                                                      \
  }                                                                              \
  }

unlink&malloc_printerr

unlink&malloc_printerr
就是类似于双向链表删除一个节点
可以看出, P 最后的 fd 和 bk 指针并没有发生变化,但是当我们去遍历整个双向链表时,已经遍历不到对应的链表了。这一点没有变化还是很有用处的,因为我们有时候可以使用这个方法来泄漏地址
  • libc 地址
  • P 位于双向链表头部,bk 泄漏
  • P 位于双向链表尾部,fd 泄漏
  • 双向链表只包含一个空闲 chunk 时,P 位于双向链表中,fd 和 bk 均可以泄漏
  • 泄漏堆地址,双向链表包含多个空闲 chunk
  • P 位于双向链表头部,fd 泄漏
  • P 位于双向链表中,fd 和 bk 均可以泄漏
  • P 位于双向链表尾部,bk 泄漏
同时,无论是对于 fd,bk 还是 fd_nextsize ,bk_nextsize,程序都会检测 fd 和 bk 是否满足对应的要求:
// fd bk
  if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                      \
  malloc_printerr (check_action, "corrupted double-linked list", P, AV);  \
  // next_size related
  if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0)              \
  || __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0))    \
  malloc_printerr (check_action,                                      \
  "corrupted double-linked list (not small)",    \
  P, AV);

简单来说就是该节点的前一个结点的后一个节点是否是它本身,该节点的后一个节点的前一个节点是否是它本身
注意:堆的第一个 chunk 所记录的 prev_inuse 位默认为 1。
malloc_printerr
在 glibc malloc 时检测到错误的时候,会调用 malloc_printerr 函数:
static void malloc_printerr(const char *str) {
  __libc_message(do_abort, "%s\n", str);
  __builtin_unreachable();
  }

主要会调用 __libc_message 来执行abort 函数,如下
  if ((action & do_abort)) {
  if ((action & do_backtrace))
  BEFORE_ABORT(do_abort, written, fd);
  /* Kill the application.  */
  abort();
  }

在abort 函数里,在 glibc 还是 2.23 版本时,会 fflush stream。
  /* Flush all streams.  We cannot close them now because the user
  might have registered a handler for SIGABRT.  */
  if (stage == 1)
  {
  ++stage;
  fflush (NULL);
  }



温馨提示:
1.如果您喜欢这篇帖子,请给作者点赞评分,点赞会增加帖子的热度,评分会给作者加学币。(评分不会扣掉您的积分,系统每天都会重置您的评分额度)。
2.回复帖子不仅是对作者的认可,还可以获得学币奖励,请尊重他人的劳动成果,拒绝做伸手党!
3.发广告、灌水回复等违规行为一经发现直接禁言,如果本帖内容涉嫌违规,请点击论坛底部的举报反馈按钮,也可以在【投诉建议】板块发帖举报。
论坛交流群:672619046

小黑屋|手机版|站务邮箱|学逆向论坛 ( 粤ICP备2021023307号 )|网站地图

GMT+8, 2024-12-22 19:22 , Processed in 0.192403 second(s), 38 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表