概述

“熊猫烧香”,是一款拥有自动传播、自动感染硬盘能力和强大的破坏能力的病毒,它不但能感染系统中exe,com,pif,src,html,asp等文件,它还能终止大量的反病毒软件进程并且会删除扩展名为gho的文件(该类文件是一系统备份工具“GHOST”的备份文件,删除后会使用户的系统备份文件丢失。被感染的用户系统中所有.exe可执行文件全部被改成熊猫举着三根香的模样。

运行被感染的程序

提取样本

使用PCHunter32查看进程

右键-定位到进程文件

1
2
3
4
5
6
文件: C:\Windows\System32\drivers\spo0lsv.exe
大小: 30001 bytes
修改时间: 2018年7月14日, 8:40:21
MD5: 512301C535C88255C9A252FDF70B7A03
SHA1: CA3A1070CFF311C0BA40AB60A8FE3266CFEFE870
CRC32: E334747C

把定位的病毒样本提取出来

病毒样本提取(spo0lsv.exe)出来后,放到虚拟机中做进一步分析

手工查杀

结束进程

(回到PCHunter32界面右击-结束进程)

查看启动项

删除改启动项

右击-删除(启动信息和文件)

行为分析

获取样本之后,在虚拟机中,使用监控工具监视样本的运行

等待病毒执行的差不多后,分别查看病毒行为

​ 1.文件操作,主要查看文件创建、修改、删除等操作

​ 2.注册表操作,主要看注册表设置、创建等操作

​ 3.进程操作,主要看创建进程、写入内存等操作

​ 4.网络操作,主要看网络链接地址、IP信息等

监控病毒运行

这里的监控使用火绒剑

将病毒(熊猫烧香)拖拽到火绒剑中

弹出以下窗口,在动作选项中的子选项框全部勾选,以便后续分别筛选分析

点击确定

事件过多,正好用到火绒剑的筛选功能

左上角过滤-动作过滤

执行监控

文件监控

在动作中去掉其它选项,在文件监控的子选项勾选、、文件修改、、文件写入

注:熊猫烧香感染的基本原理基本上是修改了文件

然后发现所有的web文件都加入了这么一条链接

注册表监控

动作过滤-选择设置注册表项

进程监控

不太清楚具体对进程做了什么,所以在动作过滤-进程监控直接勾选

可以通过双击来查看详情

枚举进程基本上是为了查看当前进程中是否有杀软

网络监控

由于并不清楚他会对网络做什么操作,所以直接勾选网络监控

火绒剑的网络监控好像不是很强

使用WSExplorer查看病毒网络行为

行为总结

火绒剑的动作过滤中的,行为监控选项过滤出恶意的行为

点开来查看详情

通过这个界面基本可以看出这个病毒干了些什么

分析监控日志后,能够分析出的恶意行为:

1.自我复制样本到C盘(C://Windows/driver)

2.在每一个目录下创建Desktop_.ini文件

3.在C盘根目录创建autorun.inf文件(里面指定了自启动文件为根目录下的setup.exe样本)

4.对程序目录下的exe文件进行感染,图标变为熊猫烧香

5.设置注册表启动项为C:/Windows/driver/spo0lsv.exe

6.设置注册表键值为隐藏

逆向分析

脱壳

通过PEID工具发现熊猫烧香这个病毒是带壳的,就需要先脱壳了

拖入OD中开始单步

填充IAT

真实IAT

找到OEP

F7进入

确认IAT是否正常

很明显的IAT表中参杂着0x7FFFFFFF,需要把IAT表中0x7FFFFFFF更改成0即可,不然脱壳后修复IAT的时候会影响寻址

大概更改了十来个

Dump

回到OEP处dump

然后点击脱壳,取个名称保存

修复IAT

之后会产生一个新文件(就是已脱壳的熊猫烧香)

IDA&OD分析

使用IDA的签名工具可以更好的识别库代码

  1. 将Dump修复后的文件使用IDA打开

  2. 在IDA中使用Shift + F5 打开签名窗口

  3. 在签名窗口Ctrl + F搜索Delphi

  4. 找到Delphi对应的特征库,应用用后可以识别更多是库函数

使用PEID查看(脱完壳的熊猫烧香)深度扫描

发现是Delphi编写的

使用IDA打开

添加签名

Shift + F5 —> 右键 —-> Apply new signature…

Ctrl + F搜索

到OEP处,使用快捷键F5,查看伪代码

OD打开

通过OD找到第一个函数 (405250)进行动静结构分析

在第一个函数(405250)下一个断点,直接运行到这查看

单步

跳转到了函数(40891C)

F7进入

打开IDA来查看一下(40819C)这个函数

函数40819C

找到关键代码

逐一分析

函数4053AC

函数405FC4

(可以看出,这个函数是在遍历到指定的进程然后杀死)

那就,就近原则,选择JE 40845E(更改标志位,让其跳转到49845E)

函数 40D18C

一路单步后,到了一个神奇的地方

使用IDA查看40D81C函数

分析到这,一个个线程进去看一下

函数 49A5B0

线程1 40A48C

这个里面就获取了所有盘符

OD进入函数 407F00

一路单步往下走(盯住堆栈和寄存器)

image-20210102102615059

继续单步

走出循环,瞅一眼程序是否改变

而且大小也变了

病毒大小 + 源文件大小

项当于是往后面追加的

手动恢复感染文件

用010Editor把0x00000 - 0x1F000这一段给删了(把后面附加的感染特征一并删除)

函数 40BACC

进入这个函数查看这个函数里面的线程干了什么

函数 40D088

进入40D088,看到六个计时器

分析每个计时器对应的函数分别是什么作用

函数 40CEE4

函数 40D040

函数 40D048

函数 407430

函数 40CC4C

函数 407C28

这个代码已经看了太多了,就是下载并执行

文件感染

函数基本都分析网络,创建文件Desktop_ini,试试在写入文件相关API下个断点,碰碰运气(CreateFile,OpenFIle,CopyFile)

然后就看到了一堆,走都走不出去,无奈删除断点

编写专杀工具

信息

1
2
3
4
5
6
7
8
结束进程			 “spo0lsv.exe”
删除病毒文件 "C:\\Windows\\System32\\drivers\\spo0lsv.exe"
修改注册表 “Software\\Microsoft\\Windows\\CurrentVersion\\Run\\”
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced\\Folder\\Hidden\\SHOWALL\\"
删除文件 “Desktop_.ini”
”setup.exe“
恢复文件 删除文件头 - 0x1F000
删除含有"www.ac86.cn/66/index.htm"链接的这一行

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
#include <Windows.h>
#include <atlstr.h>
#include <tlhelp32.h>
#include <shlwapi.h>
#include <vector>

using std::vector;


//获取进程ID
DWORD GetProcessPid(CString csProcessName)
{
PROCESSENTRY32 pStc;
pStc.dwSize = sizeof(pStc);
HANDLE nSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

BOOL bRet = Process32First(nSnapShot, &pStc);
while (bRet)
{
if (csProcessName == CString(pStc.szExeFile))
{
return pStc.th32ProcessID;
}
bRet = Process32Next(nSnapShot, &pStc);
}
return 0;
}
//获取驱动器
vector<CStringA> GetDriverList()
{
vector<CStringA>nDiverList;

TCHAR nDrive[MAX_PATH];

GetLogicalDriveStrings(100, (LPWSTR)nDrive);

TCHAR* pName = nDrive;
//指针指向的地方不为零(表示有盘符)
while (*pName != 0) {
//添加到vector中
nDiverList.push_back(pName);
printf("%S\n", pName);
//指向下一个盘符
pName += _tcslen(pName) + 1;
}

return nDiverList;//返回vector
}

//恢复文件
void RecoveryFile(CStringA nFilePath, CStringA nFileName)
{

int nFileType = 0;

CStringA csFileType;
csFileType = PathFindExtensionA(nFileName);
csFileType.MakeUpper();

//通过分析得知exe,scr,pif,com,htm,html,asp,php,jsp,aspx文件属于被感染类型
if (csFileType == ".EXE" || csFileType == ".SCR" || csFileType == ".PIF" || csFileType == ".COM")
nFileType = 1;
else if (csFileType == ".HTML" || csFileType == ".HTM" || csFileType == ".ASP" || csFileType == ".ASPX" || csFileType == ".JSP" || csFileType == ".PHP")
nFileType = 2;//网络文件
else return;//不属于这些文件类型就没必要接下去的判断了


FILE* pFile = nullptr;
char* cBuff = nullptr;
int nFileSize = 0;
fopen_s(&pFile, nFilePath, "rb");//读文件
fseek(pFile, 0, SEEK_END);//将文件指针指向结尾
nFileSize = ftell(pFile);//返回文件的偏移量(文件大小)
rewind(pFile); //重新指向文件的开头
cBuff = new char[nFileSize] {};
fread(cBuff, nFileSize, 1, pFile);
fclose(pFile);


if (nFileType == 1)
{
int i = 0;
//感染特征,在文件的最后加上 .文件名.文件类型.文件类型.原文件大小.
//文件类型.原文件大小.一边情况下不会超过12个字节
for (i = 0; i < 13; i++)
{
//感染特征倒数第二个.的十六进制是0x02
if (*(cBuff + nFileSize - 13 + i) == (char)0x02)
break;
}

//特征的最后一个点的16进制是0x01
if (i == 13 || (*(cBuff + nFileSize - 1) != (char)0x01))
{
delete[]cBuff;
return;
}

char cTemp[13]{};
int nExeSize = 0;
memcpy_s(cTemp, 13, cBuff + nFileSize - 13 + i + 1, 13 - i - 2);

sscanf_s(cTemp, "%d", &nExeSize);

if (nExeSize <= 0)
{
delete[]cBuff;
return;
}

printf("被感染文件类型:%s\t被感染文件大小:%d\n", csFileType, nExeSize);

char* NewFile = new char[nExeSize] {};

memcpy_s(NewFile, nExeSize, cBuff + 0x1F000, nExeSize);

if (!DeleteFileA(nFilePath))
{
printf("修复%s失败!%s\n", csFileType, nFilePath);
delete[]NewFile;
delete[]cBuff;
return;
}

fopen_s(&pFile, nFilePath, "wb");
fwrite(NewFile, nExeSize, 1, pFile);
fclose(pFile);

delete[]NewFile;

printf("修复%s成功!%s\n", csFileType, nFilePath);
}
else if (nFileType == 2)
{
CStringA nFileCode(cBuff);
if (nFileCode.Find("www.ac86.cn/66/index.htm") != -1)
{
int nHtmlSize = nFileSize - 76;
char* NewFile = new char[nHtmlSize] {};

memcpy_s(NewFile, nHtmlSize, cBuff, nHtmlSize);

fopen_s(&pFile, nFilePath, "wb");
fwrite(NewFile, nHtmlSize, 1, pFile);
fclose(pFile);


delete[]NewFile;
printf("修复%s成功!%s\n", csFileType, nFilePath);
}
}
delete[]cBuff;
cBuff = nullptr;
}

//遍历文件
void FindFile(CStringA nDir)
{
WIN32_FIND_DATAA nFileData = { 0 };
HANDLE hFind = INVALID_HANDLE_VALUE;
hFind = FindFirstFileA(nDir + L"\\*", &nFileData);
if (hFind == INVALID_HANDLE_VALUE) return;

do {//排除两个隐藏路径(当前目录,上级目录)
if (CStringA(nFileData.cFileName) == "." || CStringA(nFileData.cFileName) == "..")
{
continue;
}
//删除Desktop_.ini文件
if (SetFileAttributesA(nDir + "\\Desktop_.ini", FILE_ATTRIBUTE_NORMAL))
{
printf("%s\\Desktop.ini\n",nDir);
DeleteFileA(nDir + "\\Desktop_.ini");
printf("删除文件成功:%s\n", nDir + "\\Desktop_.ini");
}

if (nFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
FindFile(nDir + "\\" + nFileData.cFileName);//目录拼接,递归查找
else
{
//恢复文件
RecoveryFile(nDir + "\\" + nFileData.cFileName, nFileData.cFileName);
}
//查找下一个文件
} while (FindNextFileA(hFind, &nFileData));


}
//主逻辑
int main()
{
//结束进程(熊猫烧香)
DWORD ID = GetProcessPid("spo0lsv.exe");
if (ID != 0)
{
DWORD Flag = TerminateProcess(OpenProcess(PROCESS_ALL_ACCESS, FALSE, ID),0);
if (Flag == 0)
{
printf("结束熊猫烧香进程失败\n");

}
}
else
{
printf("未找到熊猫烧香进程\n");
}
//删除熊猫烧香文件
SetFileAttributesA("C:\\Windows\\System32\\drivers\\spo0lsv.exe", FILE_ATTRIBUTE_NORMAL); //去除属性
DWORD Flag = DeleteFileA("C:\\Windows\\System32\\drivers\\spo0lsv.exe");
if (Flag == 0)
{
printf("未未找到熊猫烧香文件\n");

}
//修改注册表 删除启动项svcshare
HKEY hKey = NULL;
int nError = RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run\\", &hKey);
if (nError != ERROR_SUCCESS)printf("打开注册表失败(启动项)");
RegDeleteValueA(hKey, "svcshare");
RegCloseKey(hKey);
printf("已清理启动项\n");
//修改注册表
nError = RegOpenKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced\\Folder\\Hidden\\SHOWALL\\", &hKey);
if (nError != ERROR_SUCCESS)printf("打开注册表失败(隐藏)");
DWORD Val = 1;
RegSetValueExA(hKey, "CheckedValue", 0, REG_DWORD, (CONST BYTE*) & nVal, sizeof(DWORD));//0改为1 ,取消隐藏
RegCloseKey(hKey);
printf("已清理隐藏\n");

vector<CStringA>nDiverList;

nDiverList = GetDriverList();//返回驱动器列表(以盘符组成的数组)

for (auto& val : nDiverList)
{
SetFileAttributesA(val + "\\autorun.inf", FILE_ATTRIBUTE_NORMAL);
DeleteFileA(val + "\\autorun.inf");

SetFileAttributesA(val + "\\setup.exe", FILE_ATTRIBUTE_NORMAL);
DeleteFileA(val + "\\setup.exe");
//传入盘符遍历文件
FindFile(val);
};


system("pause");
return 0;
}