160

add中有两次输入并调用了edit函数,它检查的是自己的股那里chunk是否会被改变

但是add是先malloc(user),后malloc(管理)所以要改变其它chunk一点会影响管理
所以可以用consolidation去malloc一个大的chunk,而让管理chunk在后面
exp1
1 | from pwn import * |
exp2
或者在malloc(0x20)之后free让函数从bin链中取出
1 | from pwn import * |
161
add
只能控制大小,不能控制内容

edit
可以堆溢出,而且有of by one


思路
让一个chunk包含一个free的unsortedbin的fd泄露libc
然后把malloc_hook改为onegadgat
先malloc1和0,用0改1的size位,然后malloc2(unsorted_bin),之后在unsorted_bin中伪造chunk绕过后续free和malloc改变size_list时malloc的检验
malloc之后会把2的真实size清零,所以接着要为其赋值,让它free之后可以进入正确的bin链
最后再malloc一个隔开并free掉2就可以show了
1 | add(0x38) |

拿到libc_base之后就可以构造了,因为malloc没有输入参数,所以想直接给他onegadget
想要malloc控制到malloc_hook附近,看到有0x7f
所以malloc一个0x60大小的chunk,此时会切分我们的2的unsorted_bin,可以用chunk1来改它的fd
1 | add(0x60) # 2 |


同时为了满足onegadget的条件
在realloc_hook处放onegadget,然后在malloc_hook处放realloc的相关 push
1 | one = libc_base + 0xf1147 |



__realloc_hook 是 realloc() 入口的“可选前置回调函数指针”。
具体逻辑大致是这样(伪代码):
1 | void *realloc(void *p, size_t n) { |
所以关系是:
__realloc_hook == NULL
→realloc()直接走正常的__libc_realloc实现。__realloc_hook != NULL
→realloc()会先通过函数指针调用__realloc_hook指向的地址(把p、n、caller 等传进去),由 hook 决定返回什么。
“hook 值是 onegadget”时,就意味着:只要程序触发了 realloc(),就会间接跳到 onegadget 地址执行(因为 realloc() 会 call [__realloc_hook])。
calloc走malloc_hook
因为在 libc 里,
malloc和calloc本来就不是两套完全独立的分配器。它们最终都会复用同一套底层堆分配逻辑(glibc 里就是_int_malloc这类内部函数),区别主要在于:
malloc(n):只负责“给你一块可用内存”,不保证清零calloc(m, n):负责“分配m*n字节”,并且 保证返回的内存内容全为 0所以实现上最自然的做法就是:
calloc先算出总大小m*n,并做溢出检查- 然后调用底层分配逻辑(内部等价于“走 malloc 的分配流程”)拿到一块内存
- 再把这块内存清零(
memset/clear_memory),最后返回换句话说:calloc “走 malloc 的方法”是因为它复用同一套堆分配代码,只是在分配完之后多做一步“清零”。
exp
1 | from pwn import * |
162