1. 程式人生 > >堆溢出學習筆記(linux)

堆溢出學習筆記(linux)

inf 利用 allow 兩個指針 lin 向上 獲取 col 一次

本文主要是linux下堆的數據結構及堆調試、堆溢出利用的一些基礎知識

首先,linux下堆的數據結構如下

/*
  This struct declaration is misleading (but accurate and necessary).
  It declares a "view" into memory allowing access to necessary
  fields at known offsets from a given base. See explanation below.
*/
struct malloc_chunk {

  INTERNAL_SIZE_T      prev_size;  /* Size of previous chunk (if free).  */
  INTERNAL_SIZE_T      size;       /* Size in bytes, including overhead. */

  struct malloc_chunk* fd;         /* double links -- used only if free. */
  struct malloc_chunk* bk;

  /* Only used for large blocks: pointer to next larger size.  */
  struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
  struct malloc_chunk* bk_nextsize;
};


  • prev_size, 如果該 chunk 的物理相鄰的前一地址chunk(兩個指針的地址差值為前一chunk大小)是空閑的話,那該字段記錄的是前一個 chunk 的大小(包括 chunk 頭)。否則,該字段可以用來存儲物理相鄰的前一個chunk 的數據。這裏的前一 chunk 指的是較低地址的 chunk
  • size ,該 chunk 的大小,大小必須是 2 * SIZE_SZ 的整數倍。如果申請的內存大小不是 2 * SIZE_SZ 的整數倍,會被轉換滿足大小的最小的 2 * SIZE_SZ 的倍數。32 位系統中,SIZE_SZ 是 4;64 位系統中,SIZE_SZ 是 8。 該字段的低三個比特位對 chunk 的大小沒有影響,它們從高到低分別表示
    • NON_MAIN_ARENA,記錄當前 chunk 是否不屬於主線程,1表示不屬於,0表示屬於。
    • IS_MAPPED,記錄當前 chunk 是否是由 mmap 分配的。
    • PREV_INUSE,記錄前一個 chunk 塊是否被分配。一般來說,堆中第一個被分配的內存塊的 size 字段的P位都會被設置為1,以便於防止訪問前面的非法內存。當一個 chunk 的 size 的 P 位為 0 時,我們能通過 prev_size 字段來獲取上一個 chunk 的大小以及地址。這也方便進行空閑chunk之間的合並。
  • fd,bk。 chunk 處於分配狀態時,從 fd 字段開始是用戶的數據。chunk 空閑時,會被添加到對應的空閑管理鏈表中,其字段的含義如下
    • fd 指向下一個(非物理相鄰)空閑的 chunk
    • bk 指向上一個(非物理相鄰)空閑的 chunk
    • 通過 fd 和 bk 可以將空閑的 chunk 塊加入到空閑的 chunk 塊鏈表進行統一管理
  • fd_nextsize, bk_nextsize,也是只有 chunk 空閑的時候才使用,不過其用於較大的 chunk(large chunk)。
    • fd_nextsize 指向前一個與當前 chunk 大小不同的第一個空閑塊,不包含 bin 的頭指針。
    • bk_nextsize 指向後一個與當前 chunk 大小不同的第一個空閑塊,不包含 bin 的頭指針。
    • 一般空閑的 large chunk 在 fd 的遍歷順序中,按照由大到小的順序排列。這樣做可以避免在尋找合適chunk 時挨個遍歷。

以上內容摘自CTF-WIKI https://ctf-wiki.github.io/ctf-wiki/pwn/heap/heap_structure/#top-chunk

給出一個簡單堆溢出的例子

#include <stdio.h>

int main(void) 
{
  char *chunk;
  chunk=malloc(24);
  puts("Get input:");
  gets(chunk);
  return 0;
}

gcc -no-pie -p example1 example1.c編譯程序

objdump -d example1查看main函數地址,然後gdb在main函數起始位置下斷點。

技術分享圖片

當執行到0x400589時查看rax內容即為malloc分配堆的起始地址。

技術分享圖片

執行完0x405a5時 x/10x 0x602250用‘A‘*100覆蓋堆查看堆溢出情況(Gdb指令查看手冊https://darkdust.net/files/GDB%20Cheat%20Sheet.pdf)

技術分享圖片

這裏查看0x602250的原因是INTERNAL_SIZE_T默認和size_t一致,32位系統下size_t 4字節,64位系統下size_t 8字節。malloc返回的堆地址指針實際是 struct malloc_chunk* fd的fd,所以64位系統下查看堆首需要用返回的堆地址減去16字節的堆頭。

我們在重新看一下堆覆蓋之前的堆內容

技術分享圖片

size部分的最後三個字節分別表示特定含義(見上數據結構),用戶真正可用的堆地址是0X602260-0X60226F,共32字節。申請24字節分配32字節的原因是32位系統8字節對齊,64位16位對齊。

0X602270是top chunk的內容,top chunk是在第一次執行malloc時heap 會被分為兩塊,一塊給用戶,剩下的那塊就是 top chunk。top chunk就是當前堆的物理地址最高chunk,這個chunk不屬於任何bin。

未完待續。。。

堆溢出學習筆記(linux)