标签:命名 book creat desc ant byte 其他 wro out
首先checksec查看程序的保护机制,可以看到除了canary其他保护都开启了。
其次运行程序,先观察一下程序大致流程,方便后面的代码分析。
这里我们可以看到,这是一个菜单题,总共有5个功能,分别是增加book、删除book、修改book的description、输出book的详细信息以及修改作者名字。
然后用ida64打开对应的二进制文件,通过前面的分析,把主函数调用的各个函数重新命名一下, 方便我们记忆。
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
struct _IO_FILE *v3; // rdi
__int64 savedregs; // [rsp+20h] [rbp+0h]
setvbuf(stdout, 0LL, 2, 0LL);
v3 = stdin;
setvbuf(stdin, 0LL, 1, 0LL);
sub_A77(v3, 0LL);
change_name();
while ( menu(v3) != 6 )
{
switch ( &savedregs )
{
case 1u:
create();
break;
case 2u:
delete();
break;
case 3u:
edit();
break;
case 4u:
show();
break;
case 5u:
change_name();
break;
default:
v3 = "Wrong option";
puts("Wrong option");
break;
}
}
puts("Thanks to use our library software");
return 0LL;
}
我们一个一个函数分析,首先看create函数,这个函数用来创建一个book,每一个book共包含了id变量、name指针、des指针、size变量。
这里笔者只取了部分代码:
if ( struct_ptr )
{
*(struct_ptr + 6) = size;
*(off_202010 + id) = struct_ptr;
*(struct_ptr + 2) = des_ptr;
*(struct_ptr + 1) = name_ptr;
*struct_ptr = ++unk_202024;
return 0LL;
}
注意:这里的off_202010
应该是一个结构体指针数组,off_202010+1
就是数组第二个元素的地址,*(off_202010+1)
就是数组第二个元素的值。
然后根据上面的代码,可以发现这个数组里面存储的都是结构体指针,然后每个结构体指针指向一个结构体,结构体有对应的id、name_ptr、des_ptr、size。
再来看delete函数,它用来删除指定id的book实例,代码如下:
signed __int64 delete()
{
int v1; // [rsp+8h] [rbp-8h]
int i; // [rsp+Ch] [rbp-4h]
i = 0;
printf("Enter the book id you want to delete: ");
__isoc99_scanf("%d", &v1);
if ( v1 > 0 )
{
for ( i = 0; i <= 19 && (!*(off_202010 + i) || **(off_202010 + i) != v1); ++i )
;
if ( i != 20 )
{
free(*(*(off_202010 + i) + 8LL));
free(*(*(off_202010 + i) + 16LL));
free(*(off_202010 + i));
*(off_202010 + i) = 0LL;
return 0LL;
}
printf("Can‘t find selected book!", &v1);
}
else
{
printf("Wrong id", &v1);
}
return 1LL;
}
根据上面的代码,我们发现delete功能会先遍历数组元素,查看指定id的元素是否可以删除,可以的话就依次free掉name_ptr、des_ptr和id,然后把数组对应位置清零。
再来看edit函数,这个函数用来修改指定book的description:
printf("Enter the book id you want to edit: ");
__isoc99_scanf("%d", &v1);
if ( v1 > 0 )
{
for ( i = 0; i <= 19 && (!*(off_202010 + i) || **(off_202010 + i) != v1); ++i )
;
if ( i == 20 )
{
printf("Can‘t find selected book!", &v1);
}
else
{
printf("Enter new book description: ", &v1);
if ( !my_gets(*(*(off_202010 + i) + 16LL), *(*(off_202010 + i) + 24LL) - 1) )
return 0LL;
printf("Unable to read new description");
}
}
我们发现edit是通过一个作者自己定义的my_gets函数(这里是笔者自己改的名字)来获取用户的输入并且写入指定的缓冲区,比如这里就是写入*(*(off_202010 + i) + 16LL)
,根据前面的分析也就是写入第i本书的description部分,大小为*(*(off_202010 + i) + 24LL) - 1
,也就是description_size-1。
在分析下一个函数之前,我们先来看一下这个自定义的my_gets函数:
signed __int64 __fastcall my_gets(_BYTE *ptr, int size)
{
int i; // [rsp+14h] [rbp-Ch]
_BYTE *buf; // [rsp+18h] [rbp-8h]
if ( size <= 0 )
return 0LL;
buf = ptr;
for ( i = 0; ; ++i )
{
if ( read(0, buf, 1uLL) != 1 )
return 1LL;
if ( *buf == ‘\n‘ )
break;
++buf;
if ( i == size )
break;
}
*buf = 0;
return 0LL;
}
注意:仔细分析一下这里的代码,就会发现倒数第3行的*buf = 0;
会造成空字节溢出,当用户输入字符数≥size时,跳出for循环,这时buf指针由于在循环中执行过++buf
,那么buf=0`就会在缓冲区溢出1字节的地方置零。
再来看show函数,这个函数主要是把每个book结构体的内容输出出来:
if ( v0 )
{
printf("ID: %d\n", **(off_202010 + i));
printf("Name: %s\n", *(*(off_202010 + i) + 8LL));
printf("Description: %s\n", *(*(off_202010 + i) + 16LL));
LODWORD(v0) = printf("Author: %s\n", off_202018);
}
这里可以比较清晰地看到id、name、description以及author_name分别在什么位置。
最后是change_name函数,这是调用了my_gets来修改author_name的函数,实现比较简单:
signed __int64 change_name()
{
printf("Enter author name: ");
if ( !my_gets(off_202018, 32) )
return 0LL;
printf("fail to read author_name", 32LL);
return 1LL;
}
到此为止整个程序的流程就大致分析完毕了。
首先,我们验证一下在my_gets函数中发现的可能存在的单空字节溢出的问题,稍微注意看每个调用my_gets函数的地方就会发现,只有在调用change_name的时候,my_gets的第二个参数(也就是size)是没有“-1”的,这也就是说change_name的时候我们可以把buf填满,然后就会溢出一个0字节。
Talk is cheap,我们先看把author_name填满0x20字节,并且创建2个book时,内存的分布情况:
set_author_name(‘A‘*0x20)
create(0x20,‘AAAA‘,0x1000,‘BBBB‘)
create(0x20,‘CCCC‘,0x21000,‘DDDD‘)
这里可能会有疑问:不是说会溢出一个空字节吗?这里没有看到的原因是我们先调用change_name,的确溢出了一个空字节;但是我们又调用了create创建了book1,book1_ptr把\x00这个字节给覆盖掉了而已。
不信的话,我们在创建book之后再调用一次change_name,观察内存情况:
set_author_name(‘A‘*0x20)
create(0x20,‘AAAA‘,0x1000,‘BBBB‘)
create(0x20,‘CCCC‘,0x21000,‘DDDD‘)
change(‘A‘*0x20)
如此一来,就验证了change_name这个函数是存在off-by-one漏洞的。
标签:命名 book creat desc ant byte 其他 wro out
原文地址:https://www.cnblogs.com/sketchpl4ne/p/14407128.html