网络安全课程作业-RE
根据 crackme,overflow,overflow2,overflow3 的二进制程序,复现授课中的利用过程, 并将详细步骤整理(重要步骤的截图要清晰)到报告中;
Crackme.c
先用codeblock编写crackme.c代码。用release版本编译,然后IDA32位打开编译好的exe文件。
先实验一下代码。
F5反编译主函数。发现主函数逻辑比较好认得。
打开汇编目录,找到关键跳转。只需将jnz改成jz,便成功。
Ctrl + Alt + K 修改此处代码。
Edit -> Program File -> Apply patches to input files. 点击OK。
完成了对程序的修改,再打开试一下。成功。
Overflow1.c
写出代码,用debug版本编译。
用OD打开,找到主函数,下好断点。
先输入qqqqqqq。进入verify_password函数。观察其栈帧变化。
先运行完strcmp函数,发现其返回值放在EBP – 0XC 位置,跟踪其位置。此时为1。
再运行完strcpy函数,其将复制的字符串放在EBP-0X14位置。看到其并未覆盖EBP-0XC。
所以程序会直接返回1,输出错误
现在输入qqqqqqqq。发现EBP-0XC的位置为1
运行完strcpy函数,发现EBP-C的位置已经被’/0’的00所替换。所以返回0。输出正确。
然后输入 11111111。发现此时EBP-0XC的位置是0xFFFFFFFF。
执行完strcpy函数。发现EBP-0XC位置被填充,但是未被填充完。
所以函数返回值仍不是0.
输出仍错误。
Overflow2
先写代码。用debug版本编译。然后用IDA打开。
找到了关键跳转位置,是在 0x0040140A 的地方。
然后打开OD,观察栈针。
经过观察,并且根据上个实验,很快就找到了相应的位置。根据代码,我们要从ebp-0x14的地方开始填字符串,填到ebp + 0x4的地方,并且将关键跳转位置覆盖。
这其中有七个字节,所以我们选择3组12345678 + 跳转位置。
这里有个小问题,就是0A是换行符,所以保存成HEX没法读进去。这里采用的解决方法是在程序的无用的地方开辟一个跳转。让程序先return到跳转处,再跳转到成功位置。
选择的位置是 0x401337 。所以填入 401337。 完成输入。
完成后开始试验。
进入函数。
经过strcpy。成功修改返回地址。
成功实现栈溢出
Overflow3
首先输入代码。用debug版本编译。
用OD进行动态调试,进入关键函数。发现在函数结束会多个栈检查函数。这里影响到栈溢出。就直接nop掉。
观察栈帧,是从EBP-30处开始填入字符串,我们这里构造字符串,一直溢到EBP+4处。多余部分用nop(90)代替。
需要找到MessageBoxA的函数地址。用下面代码可以找到其函数地址。
#include <stdio.h>
#include <windows.h>
typedef void (*FuncPointer)(LPTSTR); // 函数指针
int main()
{
HINSTANCE LibHandle;
FuncPointer GetAddr;
// 加载成功后返回库模块的句柄
LibHandle = LoadLibrary("user32");
printf("user32 LibHandle = 0x%X\n", LibHandle);
// 返回动态链接库(DLL)中的输出库函数地址
GetAddr=(FuncPointer)GetProcAddress(LibHandle,"MessageBoxA");
printf("MessageBoxA = 0x%X\n", GetAddr);
return 0;
}
构造出汇编代码。这里用visual studio写汇编代码,用微软的_as函数可以直接在C语言中嵌入我们要的汇编代码。编译出exe,然后用OD看他的机器码。
OD打开后找到关键代码,然后手动拿出其机器码。
用010editor将机器码敲入password.txt文件。将多余部分用90填充。
然后用OD调试。首先查看函数的栈针。用EBP作为基址方便我们查询。
运行程序,发现函数返回地址已经被修改。
F8一直到RET,发现成功跳转到我们之前写入的机器码处。
运行代码。发现成功弹窗了。
进一步。按照上面的方法,重新构造。加上调用exit()函数的代码。
先找到exit()函数的入口地址。
1. #include <stdio.h>
2. #include <windows.h>
3.
4. typedef void (*FuncPointer)(LPTSTR); // 函数指针
5.
6. int main()
7. {
8. HINSTANCE LibHandle;
9. FuncPointer GetAddr;
10. // 加载成功后返回库模块的句柄
11. LibHandle = LoadLibrary("kernel32");
12. printf("kernel32 LibHandle = 0x%X\n", LibHandle);
13. // 返回动态链接库(DLL)中的输出库函数地址
14. GetAddr=(FuncPointer)GetProcAddress(LibHandle,"ExitProcess");
15. printf("ExitProcess = 0x%X\n", GetAddr);
16. return 0;
17. }
编写汇编代码。同样按照上面方法重复操作。
在OD中打开,找到机器码。
敲入password文件中。最后敲入要返回的栈的地址,也就是我们开始数据溢出的位置。这里是0x0012FAF0。
进入程序,先把检查栈底的保护函数nop掉。
运行程序,成功溢出到想要的位置。
运行到第一个call eax,调用messageBoxA函数。成功弹窗。
点击确定,继续F8单步走。
发现可以调用exit()函数。继续F8。发现程序没有报错,同时程序正常推出了,实验完成。