病毒木马查杀实战第017篇:U盘病毒之专杀工具的编写

       本系列教程版权归“i春秋”所有,转载请标明出处。

本文配套视频教程,请访问“i春秋”(www.ichunqiu.com)。

前言

经过前几次的讨论,我们对于这次的U盘病毒已经有了一定的了解,那么这次我们就依据病毒的行为特征,来编写针对于这次U盘病毒的专杀工具。

专杀工具功能说明

因为这次是一个U盘病毒,所以我打算把这次的专杀工具换一种形式实现。不再像前几次那样需要被动运行,而是当我们的专杀工具执行后,一旦有U盘插入,就能主动检测U盘内容,如果发现病毒,就将其删除掉,之后检查系统中是否也存在病毒,如果有,也一并清理干净。我们这次的专杀工具需要实现以下几个功能:

       1、专杀工具开启后,需要时刻监测是否有U盘插入,如果有,则启动检测机制。

这里可以使用OnDeviceChange()这个消息响应函数,从而对当前系统中新添加的设备进行实时监控,并且通过对这个函数的参数具体内容的判断,就可以实现U盘的监控。

       2、查找U盘中是否存在autorun.inf文件,如果有,则解析该文件中的“open”语句后的内容,获取自启动病毒程序的名称。

解析autorun.inf文件的内容,可以使用GetPrivateProfileString()这个函数,通过它,先找到“AutoRun”区段,之后找到该区段下“open”后面的内容,就能够获取自启动的病毒程序的名称。

       3、删除autorun.inf文件以及它所要打开的程序(Recycle.exe)。

首先应当计算Recycle.exe的CRC32指纹特征码,保存为全局变量,用于之后病毒程序的匹配。由于病毒创建出来的程序具有系统以及隐藏属性,因此在删除之前,应当先将病毒程序的文件属性设置为NORMAL(普通),这可以通过SetFileAttributes()函数实现。之后再利用DeleteFile()将病毒程序删除。

       4、全盘搜索U盘中的exe程序,进行CRC32指纹匹配,删除所有病毒程序,并恢复被隐藏的文件夹。

依据上面计算出的病毒程序的CRC32值,就可以对U盘根目录盘中的所有exe文件进行匹配。毕竟病毒程序本体是没有变化的,只不过是换了一个名称而已。另外,病毒还会将与病毒同名文件夹进行隐藏,由于我们并没有分析过病毒是如何选取这些文件夹的(其实也没必要分析),因此我们只能通过病毒名称来查找被隐藏的文件夹,之后将其属性设置为NORMAL,那么文件夹也就恢复了。

       5、解析本地系统的注册表启动项,找到病毒程序的名称以及隐藏位置。

在删除本地系统中的病毒程序之前,我们需要先结束掉病毒进程。由于病毒的名称是依据计算机的特征计算出来的,而我们并没有逆向分析它的算法(其实也没有必要),所以我们并不知道病毒程序在不同的计算机中的进程名称是什么,也就不能直接结束病毒进程。但是这里可以使用一种取巧的方法,那就是病毒会将自身加入到注册表启动项中,因此我们只要解析注册表启动项,就能够获取病毒名称以及隐藏位置。经过我在不同的计算机中的测试,可以知道病毒名称是由六个字符组成的,那么我们只要查找启动项中的名称为六个字符,并且它所启动的程序能够匹配之前计算的CRC32指纹特征,那么我们也就找到了病毒的名称以及隐藏位置。

       6、依据上面所找到的病毒名称,结束掉当前正在运行的病毒进程,并删除病毒启动项以及病毒本体。

有了病毒名称,就可以在当前进程中搜索,找到之后,就可以结束掉病毒进程,之后再删除病毒本体。

       7、将病毒创建的九个动态链接库文件删除,它们位于临时文件夹中的“E_N4”中。

其实这里即便是不删除这几个动态连库文件,也是可以的,但是为了进行彻底的查杀,我在这里还是需要将它们删除,但是这里需要说明的是,一般来说,文件夹是不能够直接删除的,需要先将文件夹中的内容清空,才能移除文件夹。

纵观以上七项内容可以发现,前四项主要是针对于U盘的修复,而后面三项则是针对于本地系统的修复。接下来我会利用MFC来实现这些功能。

界面设计

这里新建一个“MFC AppWizard(exe)”工程,工程名称设定为“UVirusKiller”,然后创建一个“基本对话框”,再单击“完成”,接下来使用一个“编辑框”以及两个“按钮”控件完成界面设计:

图1 界面设计

接下来对“编辑框”进行如下设定:

图2

并将其ID改为IDC_LIST。之后为“安全打开U盘”按钮控件创建一个类型为“Control”,名为“m_SafeOpen”的变量:

图3

至此,界面设计结束。其实大家完全可以依据自己的喜好来设计自己的程序界面。

编写用于检测U盘盘符的代码

由于程序是在MFC下进行开发的,因此可以使用OnDeviceChange()这个消息响应函数。那么我们的第一步就是先在文件UVirusKillerDlg.cpp中添加消息映射:

图4 添加消息映射代码

接下来在头文件UVirusKillerDlg.h中的protected下,添加消息响应函数的定义:

图5 添加消息响应函数定义

之后在文件UVirusKillerDlg.cpp中添加获取盘符的代码:

void CUVirusKillerDlg::GetDriverName(DWORD dwData)
{
        PDEV_BROADCAST_HDR pDevHdr = (PDEV_BROADCAST_HDR)dwData;
        //如果设备类型为DBT_DEVTYP_VOLUME,则把当前结构体转换为
        //DBT_DEVTYP_VOLUME类型的结构体
        if ( pDevHdr->dbch_devicetype == DBT_DEVTYP_VOLUME )
        {
                //结构体转换
                PDEV_BROADCAST_VOLUME pDevVolume = (PDEV_BROADCAST_VOLUME)pDevHdr;
                //如果pDevVolume->dbcv_flags为0表示为可移动磁盘
                if ( pDevVolume->dbcv_flags == 0 )
                {
                        //通过将pDevVolume->dbcv_unitmask移位来判断逻辑盘符,
                        //第0位表示A盘,第1位表示B盘,依此类推。
                        DWORD dwUnitmask = pDevVolume->dbcv_unitmask;
                        //最多循环移动26位,因为至多有26位
                        for (i = 0; i < 26; ++i)
                        {
                                //因为新插入的可移动设备一定会是最后一个盘符的后一个,
                                //所以这里寻找dwUnitmask中的最低位数值为0x1的位。
                                if ( dwUnitmask & 0x1)
                                {
                                        //找到则跳出循环
                                        break;
                                }
                                //没找到则继续移位寻找
                                dwUnitmask = dwUnitmask >> 1;
                        }
                        //如果循环完26位依旧没找到,则返回
                        if ( i >= 26 )
                        {
                                return ;
                        }
                        //格式操作转化为字符串
                        DriverName.Format("%c:", i + ‘A‘);
                }
        }
}

最后在头文件UVirusKillerDlg.h中的public下添加:

图6

需要说明的是,由于程序中使用了DBT_DEVTYP_VOLUME这样以DBT_开头的宏,所以一定要在UVirusKillerDlg.cpp中包含头文件“Dbt.h”。再定义一个字符型全局变量i用于保存可移动磁盘的盘符。并且定义一个CString类型的csTxt用于保存提示内容。至此,判断盘符的程序已经完成。编写完以上程序后,接下来我们还需完善OnDeviceChange()函数。

检测U盘中的可疑文件

这里不再进行讲解,我已经在代码中添加了足够的注释:

BOOL CUVirusKillerDlg::OnDeviceChange( UINT nEventType, // An event type.
								       DWORD dwData     // The address of a structure that
									                    // contains event-specific data.
								     )
{
    // The system broadcasts the DBT_DEVICEARRIVAL device event when
	// a device or piece of media has been inserted and becomes available.
	if ( nEventType == DBT_DEVICEARRIVAL )
    {
        // 在未对U盘进行查杀之前,令“安全打开U盘”不可用
		m_SafeOpen.EnableWindow(FALSE);
		SetDlgItemText(IDC_LIST, csTxt);
		//获取盘符名称
		GetDriverName(dwData);
        //显示可移动磁盘的盘符
		CString TmpFile;
		TmpFile.Format("检测到可移动磁盘为:%c\r\n", i + ‘A‘);
		csTxt += TmpFile;
		SetDlgItemText(IDC_LIST, csTxt);
        // 如果成功获取盘符,则继续执行
        if ( DriverName != "" )
        {
			// 创建CString类型的File,令其保存autorun.inf的完整路径
            CString File = DriverName;
            File += "\\autorun.inf";
            // 用于保存由autorun.inf所启动的程序名
            char szBuff[MAX_PATH] = { 0 };
            // 判断可移动磁盘中的autorun.inf文件是否存在
            if ( GetFileAttributes(File.GetBuffer(0)) == -1 )
            {
                csTxt += "在可移动磁盘中没有检测到autorun.inf\r\n";
				SetDlgItemText(IDC_LIST, csTxt);
				// 如果当前U盘中不存在autorun.inf,则令“安全打开U盘”按钮可用
				m_SafeOpen.EnableWindow(TRUE);
				return FALSE;
            }

			csTxt += "在可移动磁盘中检测到autorun.inf\r\n";
            csTxt += "正在解析autorun.inf的启动内容\r\n";
			SetDlgItemText(IDC_LIST, csTxt);
            // 获取AutoRun.inf文件中open后面的内容,即所要自动打开的可疑文件
            GetPrivateProfileString(
                "AutoRun",        //The name of the section containing the key name.
                "open",           //The name of the key whose associated string is to be retrieved.
                NULL,             //A default string.
                szBuff,           //A pointer to the buffer that receives the retrieved string.
                MAX_PATH,        //The size of the buffer pointed to by the lpReturnedString
//parameter, in characters.
                File.GetBuffer(0)       //The name of the initialization file.
            );
            // DelFile保存由autorun.inf启动的程序的路径
			CString DelFile = DriverName;
			DelFile += ‘\\‘;
            DelFile += szBuff;

            csTxt += "由autorun.inf启动的程序为:";
			csTxt += DelFile;
			csTxt += "\r\n正在计算病毒程序的哈希值...\r\n";
            SetDlgItemText(IDC_LIST, csTxt);
            // 获取病毒程序的CRC32值
			VirusCRC32 = CalcCRC32(DelFile);
            // 删除可移动磁盘中的autorun.inf以及由之启动的文件,需要首先将病毒程序的属性调整为NORMAL
			csTxt += "正在删除可移动磁盘中的autorun.inf以及由之启动的程序...\r\n";			

			SetFileAttributes(File, FILE_ATTRIBUTE_NORMAL);
			// 删除autorun.inf
			BOOL bRet = DeleteFile(File);
			csTxt += File;
			if (bRet)
			{
                csTxt += _T("病毒程序被删除!\r\n");
			}
            else
			{
                csTxt += _T("病毒程序无法删除!\r\n");
			}				

			SetFileAttributes(DelFile, FILE_ATTRIBUTE_NORMAL);
			// 删除由autorun.inf启动的病毒程序
			bRet = DeleteFile(DelFile);
			csTxt += DelFile;
			if (bRet)
			{
                csTxt += _T("病毒程序被删除!\r\n");
			}
            else
			{
                csTxt += _T("病毒程序无法删除!\r\n");
			}

			SearchAndDeleteVirus(DriverName);
			csTxt += _T("U盘修复完毕,您现在可以安全地打开U盘或修复本地系统!\r\n");
			SetDlgItemText(IDC_LIST, csTxt);
            //令"安全打开U盘"按钮可用
			m_SafeOpen.EnableWindow(TRUE);
        }
    }
    //The system broadcasts the DBT_DEVICEREMOVECOMPLETE device event
	//when a device or piece of media has been physically removed.
	else if ( nEventType == DBT_DEVICEREMOVECOMPLETE )
    {
        //当U盘被拔出时,令"安全打开U盘"按钮不可用
		m_SafeOpen.EnableWindow(FALSE);
    }

    return TRUE;
}

编写“修复本地系统”按钮事件代码

void CUVirusKillerDlg::OnBtnRepair()
{
	// TODO: Add your control notification handler code here
	csTxt += _T("开始检测本地系统...\r\n正在检查注册表启动项...\r\n");
	char RegName[]="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\\";
	DWORD dwType = 0;
    DWORD dwBufferSize = MAXBYTE;
    DWORD dwKeySize = MAXBYTE;
	char szValueName[MAXBYTE] = { 0 };
    char szValueKey[MAXBYTE] = { 0 };

	//打开注册表启动项
	HKEY hKey = NULL;
    LONG lRetRun = RegOpenKey(HKEY_LOCAL_MACHINE, RegName, &hKey);
	if ( lRetRun != ERROR_SUCCESS )
    {
        AfxMessageBox("注册表启动项打开失败!");
		return ;
    }

	int i = 0;	

	while ( TRUE )
	{
	    // 枚举键项
		lRetRun = RegEnumValue(hKey, i, szValueName, &dwBufferSize, NULL, &dwType, (unsigned char *)szValueKey, &dwKeySize);
        if ( lRetRun == ERROR_NO_MORE_ITEMS )
        {
            break;
        }
        // 如果键项为6个字符,并且键值指向的程序的CRC32指纹与病毒指纹匹配,则关闭病毒进程以及删除键项与病毒本体
		if ( lstrlen(szValueName) == 6 &&  CalcCRC32(szValueKey) == VirusCRC32 )
		{
		    BOOL bRet = FALSE;
            DWORD dwPid = 0;
            // 删除病毒的注册表启动项
			RegDeleteValue(hKey, szValueName);
	        csTxt += _T("注册表启动项清理完毕!\r\n");

			strcat(szValueName, ".EXE");
			bRet = FindTargetProcess(szValueName, &dwPid);
			if(TRUE)
			{
			    csTxt += _T("检查系统内存...\r\n");
                csTxt += _T("系统中存在病毒进程:");
				csTxt += szValueName;
                csTxt += _T("准备进行查杀...\r\n");
                SetDlgItemText(IDC_LIST,csTxt);

				// 提升权限
	            bRet = EnableDebugPrivilege(SE_DEBUG_NAME);
				if (bRet == FALSE)
				{
                    csTxt += _T("提升权限失败\r\n");
				}
                else
				{
                    csTxt += _T("提升权限成功!\r\n");
				}
	            SetDlgItemText(IDC_LIST,csTxt);

				// 打开并尝试结束病毒进程
		        HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwPid);
                if (hProcess == INVALID_HANDLE_VALUE)
				{
                    csTxt += _T("无法结束病毒进程\r\n");
                    return ;
				}
		        bRet = TerminateProcess(hProcess,0);
                if (bRet == FALSE)
				{
                    csTxt += _T("无法结束病毒进程\r\n");
                    return ;
				}
                csTxt += _T("病毒进程已经结束\r\n");
                SetDlgItemText(IDC_LIST,csTxt);
                CloseHandle(hProcess);
			}
			break;
		}		

		//清空缓冲区
		ZeroMemory(szValueName,MAXBYTE);
        ZeroMemory(szValueKey, MAXBYTE);

		i++;
	}
	RegCloseKey(hKey);

	// 删除自启动的病毒程序
	csTxt += szValueKey;
	// 获取病毒程序所在文件夹的路径
	szValueKey[lstrlen(szValueKey)-11] = ‘\0‘;
	SetFileAttributes(szValueKey, FILE_ATTRIBUTE_NORMAL);
	BOOL bRet = DeleteDirectory(szValueKey);
    if (bRet)
	{
        csTxt += _T("病毒程序被删除!\r\n");
	}
    else
	{
        csTxt += _T("病毒程序无法删除!\r\n");
	}
    // 获取保存有病毒DLL文件的文件夹路径
	char tempPath[100];
	DWORD dwSize = 100;
	GetTempPath(dwSize, tempPath);
	lstrcat(tempPath, "\E_N4");
    SetFileAttributes(tempPath, FILE_ATTRIBUTE_NORMAL);
	// 删除病毒文件夹
	bRet = DeleteDirectory(tempPath);
	csTxt += tempPath;
    if (bRet)
	{
        csTxt += _T("病毒文件夹被删除!\r\n");
	}
    else
	{
        csTxt += _T("病毒文件夹无法删除!\r\n");
	}
    SetDlgItemText(IDC_LIST,csTxt);
}

至此,所有主要的程序编写完毕,接下来就可以进行专杀工具的测试了。

专杀工具的测试

这里我们需要让本地系统还有U盘保持中毒的状态,先拔出U盘,然后启动专杀工具,使其开始进行监控,之后插入U盘,专杀工具就会自动开始查杀:

图7

此时如果点击“安全打开U盘”,就会打开U盘,可以发现此时U盘中已经没有了病毒程序,并且被隐藏的文件夹也都显示出来了。如果点击“修复本地系统”,那么专杀工具会首先结束掉病毒进程,之后再删除系统中的病毒程序,从而对系统进行彻底的清理。

小结

这次的专杀工具的代码稍长,但其实也并不难,都是一些基础的API函数的调用,这其中还使用了很多之前“熊猫烧香专杀工具”的函数代码,说明我们应当善于利用之前所编写过的程序。并且应当善于分析病毒特征,比如如何判断病毒名称以及被隐藏的文件夹等,如果要逆向分析这些算法,那么过程无疑是艰辛的。至此,针对于这次的U盘病毒的讲解就到这里,希望大家能够举一反三,有所收获。

时间: 2024-08-05 15:57:07

病毒木马查杀实战第017篇:U盘病毒之专杀工具的编写的相关文章

病毒木马查杀实战第022篇:txt病毒研究

       本系列教程版权归"i春秋"所有,转载请标明出处. 本文配套视频教程,请访问"i春秋"(www.ichunqiu.com). 前言 反病毒爱好者们很喜欢讨论的一个问题就是,如今什么样的病毒才算得上是主流,或者说什么样的病毒才是厉害的病毒呢?我们之前的课程所讲解的都是Ring3层的病毒,所以有些朋友可能会认为,那么Ring0层的病毒其实才是最厉害的,也是病毒发展的主流:或者有朋友可能认为,采取了五花八门的隐藏技术的病毒才是最难对付的.诚然,大家的观点都很有

病毒木马查杀实战第026篇:“白加黑”恶意程序研究(上)

前言 众所周知,传统的恶意程序都是由单一文件构成的,从而实现某一种或者几种恶意功能.而这类的恶意程序为了避免被发现以及被查杀,往往会采用五花八门的自我隐藏技术以及免杀技术,病毒程序的作者很多时候也是脑洞大开,为了对抗杀软的查杀也是无所不用其极.我们每天所处理的恶意文件里面,反查杀手段运用得最好的就是脚本木马,关于这类程序,我在之前的<病毒木马查杀实战第025篇:JS下载者脚本木马的分析与防御>这篇博文中也做过简单的论述.可是,不论恶意程序如何进化,杀软厂商总有各种各样的方法来应对现有的以及未知

病毒木马查杀实战第018篇:病毒特征码查杀之基本原理

本系列教程版权归"i春秋"所有,转载请标明出处.        本文配套视频教程,请访问"i春秋"(www.ichunqiu.com). 前言 在本系列的导论中,我曾经在"病毒查杀方法"中简单讲解过特征码查杀这种方式.而我也在对于实际病毒的专杀工具编写中,使用过CRC32算法来对目标程序进行指纹匹配,从而进行病毒判定.一般来说,类似于MD5以及CRC32这样的算法,在病毒大规模爆发时是可以提高查杀效率的,但是传统的更为常用的方法是采用以静态分析文

病毒木马查杀第011篇:QQ盗号木马之专杀工具的编写

一.前言 由于我已经在<病毒木马查杀第004篇:熊猫烧香之专杀工具的编写>中编写了一个比较通用的专杀工具的框架,而这个框架对于本病毒来说,经过简单修改也是基本适用的,所以本文就不讨论那些重叠的知识,只针对这个病毒特有的方面来讨论专杀工具的编写,然后将其进行组合,就是完整的针对于本病毒的专杀工具了. 二.原理讨论 对于本病毒而言,其最大的特色就在于使用了进程守护技术.病毒运行后,同时有三个病毒进程存在,关闭其中的任何一个,由于还有两个病毒进程的存在,那么被关闭的又会被重新开启.要解决这个问题,不

病毒木马查杀第009篇:QQ盗号木马之手动查杀

一.前言 之前在<病毒木马查杀第002篇:熊猫烧香之手动查杀>中,我在不借助任何工具的情况下,基本实现了对于"熊猫烧香"病毒的查杀.但是毕竟"熊猫烧香"是一款比较简单的病毒,它并没有采取一些特别强的自我保护技术,所以我们完全可以"徒手"解决.但是这次研究的恶意程序就没那么简单,它采取了进程保护的技术,使得我们不能够使用常规手法对其实现查杀.所以这次我引入了两个工具--icesword与autoruns,以达到查杀的目的. 二.病毒的基

病毒木马查杀第008篇:熊猫烧香之病毒查杀总结

一.前言 之前用了六篇文章的篇幅,分别从手动查杀.行为分析.专杀工具的编写以及逆向分析等方面,对"熊猫烧香"病毒的查杀方式做了讨论.相信大家已经从中获取了自己想要的知识,希望大家在阅读完这几篇文章后,能够有一种"病毒也不过如此"的感觉,更希望这些文章能够为有志于在未来参与到反病毒工作的朋友,打下坚实的理论基础.以下就是我在这几篇文章的分析中所总结出来的一些知识点,分为静态分析与动态分析两个方面进行讨论,并加入了一些延伸知识,为大家查漏补缺. 二.病毒的静态分析 静态

病毒木马查杀实战第020篇:Ring3层主动防御之基本原理

本系列教程版权归"i春秋"所有,转载请标明出处.        本文配套视频教程,请访问"i春秋"(www.ichunqiu.com). 前言 如果说我们的计算机中安装有杀毒软件,那么当我们有意或无意地下载了一个恶意程序后,杀软一般都会弹出一个对话框提示我们,下载的程序很可能是恶意程序,建议删除之类的,或者杀软就不提示,直接删除了:或者当我们运行了某一个程序,包含有可疑操作,比如创建开机启动项,那么杀软一般也会对此进行提醒:或者当我们在计算机中插入U盘,杀软往往也会

病毒木马查杀实战第019篇:病毒特征码查杀之编程实现

       本系列教程版权归"i春秋"所有,转载请标明出处.        本文配套视频教程,请访问"i春秋"(www.ichunqiu.com). 前言 上次我们已经简单介绍过了病毒特征码提取的基本方法,那么这次我们就通过编程来实现对于病毒的特征码查杀. 定义特征码存储结构 为了简单起见,这次我们使用的是setup.exe以及unpacked.exe这两个病毒样本.经过上次的分析,我们对setup.exe样本的特征码提取如下: \x2a\x2a\x2a\xce\

病毒木马查杀实战第015篇:U盘病毒之脱壳研究

本系列教程版权归"i春秋"所有,转载请标明出处.        本文配套视频教程,请访问"i春秋"(www.ichunqiu.com). 前言 由于我们的最终目标是编写出针对于这次的U盘病毒的专杀工具,而通过上次的分析我们知道,病毒有可能在不同的计算机中会以不同的名称进行显示,如果真是如此,那么就有必要在此分析出病毒的命名规律等特征,然后再进行查杀. 对病毒样本进行脱壳 按照常规,首先是对病毒进行查壳的工作,这里我所使用的是"小生我怕怕"版的PE