前言

经过前几次的讨论,我们对于这次的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盘病毒的讲解就到这里,希望大家能够举一反三,有所收获。

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

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

    前言 反病毒爱好者们非常喜欢讨论的一个问题就是,现在什么样的病毒才算得上是主流,或者说什么样的病毒才是厉害的病毒呢?我们之前的课程所解说的都是Ring3层的病毒.所以有些朋友可能会觉得.那么Ring0 ...

  2. 病毒木马查杀实战第024篇:MBR病毒之编程解析引导区

    前言 通过之前的学习,相信大家已经对磁盘的引导区有了充分的认识.但是我们之前的学习都是利用现成的工具来对引导区进行解析的,而对于一名反病毒工程师而言,不单单需要有扎实的逆向分析功底,同时也需要有很强的 ...

  3. 病毒木马查杀实战第023篇:MBR病毒之引导区的解析

    前言 引导型病毒指寄生在磁盘引导区或主引导区的计算机病毒.这种病毒利用系统引导时,不对主引导区的内容正确与否进行判别的缺点,在引导系统的过程中入侵系统,驻留内存,监视系统运行,伺机传染和破坏.按照引导 ...

  4. 病毒木马查杀实战第025篇:JS下载者脚本木马的分析与防御

    前言 这次我与大家分享的是我所总结的关于JS下载者脚本木马的分析与防御技术.之所以要选择这样的一个题目,是因为在日常的病毒分析工作中,每天都会遇到这类病毒样本,少则几个,多则几十个(当然了,更多的样本 ...

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

    前言 由于我已经在<病毒木马查杀第004篇:熊猫烧香之专杀工具的编写>中编写了一个比较通用的专杀工具的框架,而这个框架对于本病毒来说,经过简单修改也是基本适用的,所以本文就不讨论那些重叠的 ...

  6. 病毒木马查杀实战第010篇:QQ盗号木马之十六进制代码分析

    前言 按照我的个人习惯,在运用诸如IDA Pro与OllyDBG对病毒进行逆向分析之前,我都会利用一些自动化的工具,通过静态或动态的分析方法(参见<病毒木马查杀第008篇:熊猫烧香之病毒查杀总结 ...

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

    前言 之前在<病毒木马查杀第002篇:熊猫烧香之手动查杀>中,我在不借助任何工具的情况下,基本实现了对于"熊猫烧香"病毒的查杀.但是毕竟"熊猫烧香" ...

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

    前言 假设说我们的计算机中安装有杀毒软件,那么当我们有意或无意地下载了一个恶意程序后.杀软一般都会弹出一个对话框提示我们,下载的程序非常可能是恶意程序,建议删除之类的.或者杀软就不提示.直接删除了:或 ...

  9. 病毒木马查杀实战第012篇:QQ盗号木马之逆向分析

    前言 在本系列的文章中,对每一个病毒分析的最后一个部分,若无特殊情况,我都会采用逆向分析的手段来为读者彻底剖析目标病毒.但是之前的"熊猫烧香"病毒,我用了三篇文章的篇幅(每篇250 ...

随机推荐

  1. PAT-1148(Werewolf )思维+数学问题

    Werewolf PAT-1148 题目的要点是不管n规模多大,始终只有两个狼人 说谎的是一个狼人和一个好人 紧紧抓住这两点进行实现和分析 #include <iostream> #inc ...

  2. HashMap之tableSizeFor方法图解

    目录 普通人的简单粗暴方式 示例代码 问题 大神的实现 移位的思想 全过程示意图 初始值 右移一位+或运算 右移二位+或运算 右移四位+或运算 右移八位+或运算 右移十六位+或运算 结果+1 初始容量 ...

  3. JavaWeb随笔整理

    JavaWeb随笔整理 为方便阅读,故整理了相关学习笔记 前端相关 HTML CSS JavaScript BootStrap 数据库相关 MySQL基础 MySQL表的约束和数据库设计 MySQL多 ...

  4. windows10 缺失 msvcp140.dll 解决办法

    1.问题描述 我更新完windows10 驱动后,出现计算机缺失msvcp140.dll文件,虚机和QQ都无法启动 2.解决办法 查找大量文章,最终发现通过重新安装 Visual Studio 201 ...

  5. POJ_2752 Seek the Name, Seek the Fame 【KMP】

    一.题目 POJ2752 二.分析 比较明显的KMP运用. 但是这题不是只找一个,仔细看题后可以发现相当于是在找到最大的满足条件的后缀后,再在这个后缀里面找满足条件的后缀. 可以不断的运用KMP得出答 ...

  6. mysql最权威的总结

    1.数据库操作 create database person charset utf8; -- 创建数据库show DATABASES; -- 查看数据库drop database person; - ...

  7. 力扣 - 92. 反转链表II

    目录 题目 思路1(迭代) 代码 复杂度分析 思路2(递归) 代码 复杂度分析 题目 92. 反转链表 II 思路1(迭代) 将反转链表分成3个部分:前一段未反转的部分.待反转链表部分.后一段未反转部 ...

  8. Nacos 2.0 正式发布,性能提升了 10 倍!!

    前不久,在3月20号,Nacos 2.0.0 正式发布了!我简单看了下官方的介绍,可能nacos未来逐渐会成为各大公司作为服务治理和配置中心的主要中间件. Nacos 简介:一个更易于构建云原生应用的 ...

  9. Hadoop学习笔记—Yarn

    目录 一些基本知识 ResourceManager 的恢复 Resource Manager的HA YARN Node Labels YARN Node Attributes Web Applicat ...

  10. 史上最全jdk新特性总结,涵盖jdk8到jdk15!

    前言 在本文中,我将描述自第8版以来Java最重要且对开发人员友好的功能.为什么会有这样的主意?在Web上,您可以找到许多文章,其中包含每种Java版本的新功能列表.但是,由于缺少文章,因此无法简要概 ...