熊猫烧香病毒的查杀和修复
熊猫烧香是蠕虫病毒的变种,Delphi语言编写,本文研究的病毒版本中,它拥有自我复制,本地感染和局域网感染的能力,甚至感染网页代码。感染寄生于被感染文件,执行被感染文件时会分离出病毒程序并执行。病毒运行时存在关闭安全软件类进程,关闭并删除安全软件相关服务,启动项,删除GHO系统备份文件,更换软件图标等动作。
病毒名称:(Nimaya)熊猫烧香
所属家族:Virus.Win32.Lamer.gx(卡巴斯基)
MD5值:B8F8E75C9E77743A61BBEA9CCBCFFD5D
SHA1值:188FC8FC580C0EA4BF8A8900A3D36471823C8923
SHA256: 0c15096fb3bc30800f7af002c25953162b799391300a62b8507fe8e4f6532768
SSDeep:
3072:apAja0pSLwYqK6hVZ7N4bdq4a53YKCOTpc:a2ja0pShqK65ZOq4QYK1m
CRC32:E63D45D3
病毒执行流程:
image.png
自动查杀实现:
#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;
}
实现效果:
image.png
实现功能有:提升权限查杀病毒进程,遍历盘符删除病毒文件或自动运行脚本,以及删除病毒的注册表启动项和修改回来设置的文件隐藏选项。
修复工具:
该熊猫烧香病毒感染exe等非网页文件时,只是复制了一份以被感染文件命名的病毒文件,在其后面追加被感染文件的内容,后面再加上感染标志。而网页文件只是简单的在其原文件的末尾添加了一段脚本代码。
由此我们也容易使用010Editor工具进行手工还原文件。这里我尝试编写一个恢复单个被感染exe文件的小例子。例如如下图恢复被感染程序Exam1.exe,RecoverFiles.exe是编写的修复工具。如果想实现全盘修复,需要自己遍历盘符和文件夹各文件。修复工具效果如下:
image.png
实现代码如下:
#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");
}