学习二进制不容易,大家要忍得住寂寞,耐得住孤独,进入正题.
总体思路:
输入更多的数据,溢出,修改main的返回值地址,使其返回到correct中,从而获取shell.
大家可能会有疑问:为什么不修改auth函数的返回值地址,跳到correct中.这是因为长度不够,我们需要输入16个字节的长度,才能覆盖返回值地址,但是程序只给了我们12个字节的长度.
main函数
int main(int argc, const char **argv, const char **envp)
{
int v4; // [sp+18h] [bp-28h]@1
int16 v5; // [sp+1Eh] [bp-22h]@1
unsigned int v6; // [sp+3Ch] [bp-4h]@1
memset(&v5, 0, 30);
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
printf("Authenticate : ");
_isoc99_scanf("%30s", &v5);
memset(&input, 0, 12);
v4 = 0;
v6 = Base64Decode(&v5, &v4); //base64解码,v5是输入的内容,v4是解码后的内容,v6是解码后的长度
if ( v6 > 12 )
{
puts("Wrong Length");
}
else
{
memcpy(&input, v4, v6);
if ( auth(v6) == 1 )
correct();
}
return 0;
}
auth函数
bool auth(int a1)
{
char v2; // [sp+14h] [bp-14h]@1
char *s2; // [sp+1Ch] [bp-Ch]@1
int v4; // [sp+20h] [bp-8h]@1
memcpy(&v4, &input, a1); //将缓冲区中a1个长度的字符赋值到v4中,
s2 = (char *)calc_md5(&v2, 12);
printf("hash : %s\n", (char)s2);
return strcmp("f87cd601aa7fedca99018a8be88eda34", s2) == 0;
}
从上述逻辑中看出,若想获得shell,必须让auth返回值为1.可是每次输入,得出的结果是不一样的.
memcpy提供了一般内存的复制(例如字符数组、整型、结构体、类等)。即memcpy对于需要复制的内容没有限制,这是条件1.
main函数中还有
if ( v6 > 12 )
{
puts("Wrong Length");
}
我们输入内容,解码后长度要<=12,这是条件二.
我们构造的12个字符长度的内容如下:
- 前四个字符随便取
- 中间四个字符为:\x84\x92\x04\x08.因为0x08049284是调用system函数的代码地址,有图为证
- 最后四个字符为:\x40\xeb\x11\x08.地址0x0811eb40是input的地址.简单点说就是你输入的内容在缓冲区的地址,memcpy函数将缓冲区的内容复制到变量的地址中去.
0x0811eb40将ebp覆盖掉,当atuh函数结束后,此时的ebp为0x0811eb40.
当main函数结束时,由于esp=ebp+4,esp的内容存的是函数返回值地址.
so,esp=0x0811eb40+4=0x0811eb44.而我们输入的内容在缓冲区中的地址是0x0811eb40,0x0811eb44—->0x08049284.于是跳转到correct中.