目录
0.实验要求
1.病毒行为分析
1.1 火绒剑监控
1.1.1 对docx进行的操作
1.1.2 创建了readme.txt
1.1.3 创建了自删除脚本、执行并删除
1.1.4 补充
1.2 IDA逆向
1.2.1 sub_1470
1.2.2 sub_401320
1.2.3 sub_4017D0
1.2.4 sub_4019B0
1.3 Stirling对比加密前和加密后文件
2.特征码提取以及“查杀”
2.1 特征码选取
2.2 代码实现
2.3 小结
3.恢复文件
3.1 原理以及代码实现
3.2 小结
4. Extension
5. 总结
1、样本分析只要说明主要行为即可。提示:sub_401320主要用于文件加密操作;sub_401470主要用于查找docx文件;sub_4017D0主要用于创建自删除脚本文件;sub_4019B0主要用于创建readme说明文件。
2、专杀工具需要能够识别本次的样本文件,可采用课堂上说过的两种取特征码的方法(也可以采用你自己的方法)进行样本识别与查杀。
3、专杀工具需要有文件恢复功能,需要采用逆运算的方式解密文件,同时还需要修改文件后缀,改回docx的扩展名。专杀工具采用的编程语言不限。
4、本次实验需要提交实验报告以及专杀程序的源代码。
初始状态:
首先打开火绒剑,设置一下过滤规则,运行我们的病毒执行文件
可以看到监控到了病毒如下的行为
这里只分析一个病毒对ScapeGoat01.docx的操作,对02、03、04、05的操作也是一样的,不一一赘述
首先病毒“FILE_readdir”,即使不知道这个状态是什么意思也可以大概猜到,病毒是做了一个“read current directory”的操作,获取了当前路径
接着他"FILE_open"、"FILE_rename"了ScapeGoat01.docx,即重命名了这个文件
(emmm腾讯安全管家和wps做了什么不太清楚,不过不影响我们的分析,不详细追究了)
接着这个病毒"FILE_open"了一个后缀为crc32的文件,根据之后的ida逆向分析可以知道这就是被更名了的ScapreGoat1.docx
(emmm其实猜一猜也可以猜到)
然后病毒对这个文件"FILE_write",即对他做了写操作,至于他写了什么,是加了还是减了还是改了,现在还不知道
最后就"FILE_modified"了,modified大概是"修正了的、变更了的"的意思,所以到这一步病毒完成了对一个docx的文件的操作
这里补充一下:
用PEid分析一下我们的病毒程序
可以知道他无壳,且编程语言是C++
那么C++的文件读写操作基本是这样的
要对文件进行读写操作,首先要调用CreateFile函数打开或者创建文件
HANDLE CreateFile(
LPCTSTR,lpFileName, //指向文件名的指针
DWORD dwDesiredAccess, //访问模式(读/写)
DWORD dwShareMode, //共享模式
LPSECURITY_ATTRIBUTES lpSecurityAttributes,//指向安全属性的指针
DWORD dwCreationDisposition, //如何让创建
DWORD dwFlagAndAttributes, //文件属性
HANDLE hTemplateFile //用于复制文件句柄
);
然后用ReadFile、WriteFile函数对其操作
BOOL ReadFile(
HANDLE fFile, //文件句柄
LPCVOID lpBuffer, //数据缓存区指针
DWORD nNumberOfBytesToRead, //所要写的字节数
LPDWORD lpNumberOfBytesRead, //用于保存实际写入字节数的存储区的指针
LPOVERLAPPED lpOverlapped //OVERLAPPED结构体指针
)
BOOL WriteFile(
HANDLE fFile, //文件句柄
LPCVOID lpBuffer, //数据缓存区指针
DWORD nNumberOfBytesToWrite, //所要写的字节数
LPDWORD lpNumberOfBytesWritten,//用于保存实际写入字节数的存储区的指针
LPOVERLAPPED lpOverlapped //OVERLAPPED结构体指针
)
我的理解是,C++要对一个文件进行操作的时候,首先必须调用CreateFile函数获得一个文件句柄,接着再将这个句柄传入读或写的函数中进行操作
这也是为什么刚刚火绒剑监视的窗口中有许多的FILE_open操作
(遇到文件操作和句柄操作就开始头疼,C++没学好,相见泪茫茫)
将Virus.exe拖入32位的IDA
找到函数入口
跟一下_main_0
如下
跟火绒剑看到的一样,首先就是调用一个GetCurrentDirectoryA()的函数
然后call了三个用户函数
分别点进去看一下
恰好就是实验要求里面的提示
sub_401470、sub_4019B0、sub_4017D0
查找docx文件、创建自删除脚本、创建readme.txt
至于最关键的sub_401320加密的函数,现在没看到(但是等等会看到的)
(emmm为什么老师都给了hint了还要这么麻烦从入口一个一个点
我想的是真正分析病毒的话别人总不会告诉你哪个函数做什么哪个函数做什么的吧
所以就一点一点从入口开始
不过这个virus.exe也还好,没有做什么复杂的操作,一下子就找到了)
首先是sub_1470----查找docx文件的函数
emmm这个函数里面跳转的地方有点多,所以直接F5大法,查看一下伪代码
这里我们其实只需要注意
第一个红框的v6 --- " *.* "星点星其实很好理解,即遍历的格式 --- 任何文件名.任何后缀名
第一个红框保存到的句柄
第三个红框将文件名保存到一个String中
第四个红框大概是一些过滤规则,如果不符合就继续寻找文件(这里不太清楚、但是觉得可说可不说,不影响大局)
第五个红框的PathFindExtensionA(cFileName),读一读函数名即可知道,是获取了这个文件的后缀名,再与“.docx”比较
第六个红框即重命名了文件,加了.crc32的后缀
最后一个sub_401023了v9,而v9通过刚刚的分析就知道,是那一个被重命名的文件
总结一下
遍历当前文件夹所有文件 →→ 获取后缀名 →→ 是.docx的话,将他重命名 →→ sub_401023这个文件
跟一下sub_401023
其实恰好就是我们刚刚没有找到的对文件进行加密的sub_401320函数
接着分析加密函数
刚刚已经说了,对文件进行操作必须先调用CreateFile()函数获取句柄,
然后这个函数调用了SetFilePointer函数
DWORD SetFilePointer(
HANDLE hFile,
LONG lDistanceToMove,
PLONG lpDistanceToMoveHigh,
DWORD dwMoveMethod
);
SetFilePointer - 在一个文件中设置新的读取位置。
SetFilePointer - 返回值 Long,返回一个新位置,它采用从文件起始处开始算起的一个字节偏移量。
设置了一个新的读取位置,而这个新的位置是什么,现在还不得而知
最最重要的是,红框里的"nNumberOfBytesToRead",值为16
有经验的大侠大概一眼就知道是什么意思了,但是我没有经验,还是得借助F5大法,不过等等再F5吧
现在还是先跟进loc_4013C9看看
这里选中的xor操作就是最重要的一步了,将cl与1进行了一个异或运算
这个时候F5看一下
看到了,“nNumberOfBytesToRead”,即从指针开始处读取16位,将他们分别与1进行异或运算,将结果保存到文件中,即实现了对文件的修改
事实上呢,这个值有可能是黑客的随便的一个很长key,没有这个key就无法实现逆向运算破解
而这里为了方便我们的学习,仅仅只是设了一个跟1做异或运算
那么加密的部分至此结束
创建了一个文件名为delself.cmd的脚本文件
手动的跟一下这个函数的跳转的话,可以发现最后是调用了WinExec函数,执行了这个脚本
创建了一个readme.txt的文件
向这个文件中写入了“This is a ransomware”
最后的自删除脚本和创建说明函数较为简单
将加密前和加密后的文件拖入Stirling,比较一下,并将比较结果高亮显示
发现,除了前16位之外,其余是一样的,所以我们知道了,刚刚的SetFilePointer其实指定的值就是0,从头开始读取16位,分别与1进行异或运算完成加密
应该用Hex Editor Neo或者其他软件也是可以完成比较的,但这里没有试过,就不说了
这里其实很尴尬,刚刚说了,C++没学好,相见泪茫茫,于是在网上寻找资料的时候,恰好找到了老师博客上的资料
这里贴上来作为参考
病毒木马查杀实战第019篇:病毒特征码查杀之编程实现
不过当然是没有直接Ctrl+C Ctrl+V的,稍微做了一点修改
先说说找特征码,说到找特征码就不得不说,上课的时候在忙着打查杀代码和恢复文件的代码,没怎么听到
所以这里只能尴尬的用一个比较简单的方法,如有错误还望指正
查看一下Virus.exe的字符集
找到了"This is a ransomware"
“一般程序不会有这样的字段”
所以这里我将他提取出来
先双击跟进去
找到他的位置位于00420064
用Hex Editor Neo看一下
实际为00020064
修改的代码如下,注释在代码中
#include "pch.h"
#include
#include
#include
using namespace std;
#define NAMELEN 20
#define SIGNLEN 32
typedef struct SIGN
{
char virusName[NAMELEN];//病毒名
LONG fileOffset;//偏移量
BYTE virusSign[SIGNLEN + 1];//特征码内容
}_SIGN,*PSIGN;
SIGN sign =
{
"virus.exe",//病毒名
0x020064,//偏移量
//This is ransomware的十六进制代码
"\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x72\x61\x6e\x73\x6f\x6d" \
"\x77\x61\x72\x65\x2e\x00\x00\x00\x00\x00\x00\x00\x72\x65\x61\x64"
};
//查找特征码
BOOL CheckSign(char* FilePath)
{
DWORD dwSigNum = 0;
DWORD dwNum = 0;
BYTE buffer[SIGNLEN + 1];
HANDLE hFile = NULL;
hFile = CreateFile(FilePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
// 将待检测程序的文件指针指向特征码的偏移位置
SetFilePointer(hFile, sign.fileOffset, NULL, FILE_BEGIN);
// 读取目标程序指定偏移位置的特征码
ReadFile(hFile, buffer, sizeof(buffer), &dwNum, NULL);
// 特征码的比对
if (memcmp(sign.virusSign, buffer, SIGNLEN) == 0)
{
printf("发现病毒程序:%s\n", FilePath);
CloseHandle(hFile);
return TRUE;
}
CloseHandle(hFile);
return FALSE;
}
int main()
{
WIN32_FIND_DATA stFindFile;
HANDLE hFindFile;
char *szFilter = (char *)"*.exe"; // 保存过滤条件
char szFindFile[MAX_PATH]; // 保存欲检测的程序的路径
char szSearch[MAX_PATH]; // 保存完整筛选路径
int ret = 0; // 搜索的返回值
char directory[256];//保存目录值
//获取当前目录
GetCurrentDirectory(256,directory);
lstrcpy(szFindFile,directory);//存入路径中
lstrcat(szFindFile, "\\");// 添加'\'
lstrcpy(szSearch, directory);//存入路径中
lstrcat(szSearch,"\\");//添加'\'
lstrcat(szSearch, szFilter);//添加过滤条件
hFindFile = FindFirstFile(szSearch, &stFindFile);
if (hFindFile != INVALID_HANDLE_VALUE)
{
do
{
// 组成完整的待检测程序的路径
lstrcat(szFindFile, stFindFile.cFileName);
// 利用特征码检测目标程序是不是病毒程序
if (!CheckSign(szFindFile))
{
printf("%s不是病毒程序\n", szFindFile);
}
// 重置文件路径
// 这里不重置szFindFile里的值,在接下来调用组成完整路径和检测函数的话会出错
lstrcpy(szFindFile, directory);
lstrcat(szFindFile, "\\");
ret = FindNextFile(hFindFile, &stFindFile);
} while (ret != 0);
}
else
{
cout<<"Wrong"<
实际效果:
将代码生成的可执行文件拖入病毒存在的文件夹中并运行
成功找到病毒文件
这里只设置了对可执行文件所在目录的搜索,并且没有对程序实现删除操作,其实删除挺简单的,但实际上病毒还可能设计到很多的注册表操作,注册表操作现在还没有学,虽然这个病毒没有对注册表进行操作,但是还是先不要献丑了,这里偷偷懒
(一直在摸鱼)
在上一篇博客中说了
网络安全入门实验02拓展:不如我们从头来过---一个有趣但是并不完美的异或加密
异或运算是一个双向运算,即对加密后文件,取前16位,再做一次异或运算,即可得到原内容
简单来说就是做一个与1的逆运算即可
代码及注释如下
// MyRecover.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include
#include
#include
#include
using namespace std;
bool RecoverFile(char* FilePath)
{
HANDLE hFile =
CreateFile(FilePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
//获取文件大小
DWORD filesize = GetFileSize(hFile, NULL);
//最后一位为'/0'
char* buffer = new char[filesize + 1];
memset(buffer, 0, filesize + 1);
DWORD readsize = 0;
ReadFile(hFile, buffer, filesize, &readsize, NULL);
buffer[filesize] = 0;
CloseHandle(hFile);
//异或运算,实则为加密后的逆运算
for (int i = 0; i < 16; i++)
{
buffer[i] = buffer[i] ^ 1;
}
//写文件句柄
HANDLE hWriteFile;
//保存要写的文件名
char hWriteFileName[MAX_PATH] = {};
//去掉文件的后缀名
for (int i = 0; i < strlen(FilePath); i++)
{
if (FilePath[i] != '\.')
{
hWriteFileName[i] = FilePath[i];
}
else
{
break;
}
}
//添加后缀名为.docx
lstrcat(hWriteFileName, ".docx");
//写文件句柄
hWriteFile = CreateFile(_T(hWriteFileName),
GENERIC_WRITE | GENERIC_READ,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hWriteFile == INVALID_HANDLE_VALUE)
{
CloseHandle(hWriteFile);
return false;
}
DWORD writesize = 0;
//写文件
WriteFile(hWriteFile, buffer, filesize, &writesize, NULL);
delete[]buffer;
CloseHandle(hWriteFile);//关闭句柄
return true;
}
int main()
{
WIN32_FIND_DATA stFindFile;
HANDLE hFindFile;
char *szFilter = (char *)"*.crc32"; // 保存过滤条件
char szRecoverFile[MAX_PATH]; // 保存欲恢复的程序的路径
char szSearch[MAX_PATH]; // 保存完整筛选路径
int ret = 0; // 搜索的返回值
char directory[256];//保存目录值
//获取当前目录
GetCurrentDirectory(256, directory);
lstrcpy(szRecoverFile, directory);//存入路径中
lstrcat(szRecoverFile, "\\");// 添加'\'
lstrcpy(szSearch, directory);//存入路径中
lstrcat(szSearch, "\\");//添加'\'
lstrcat(szSearch, szFilter);//添加过滤条件
hFindFile = FindFirstFile(szSearch, &stFindFile);
if (hFindFile != INVALID_HANDLE_VALUE)
{
do
{
// 组成完整的程序的路径
lstrcat(szRecoverFile, stFindFile.cFileName);
// 将后缀为.crc32的文件恢复为.docx
if (!RecoverFile(szRecoverFile))
{
printf("%s 恢复成功\n", szRecoverFile);
}
// 重置文件路径
// 这里不重置szRecoverFile里的值,在接下来调用组成完整路径和恢复函数的话会出错
lstrcpy(szRecoverFile, directory);
lstrcat(szRecoverFile, "\\");
ret = FindNextFile(hFindFile, &stFindFile);
} while (ret != 0);
}
else
{
cout << "Wrong" << endl;
}
FindClose(hFindFile);
system("pause");
return 0;
}
实际效果:
将代码生成的可执行文件拖入被加密的docx所在的文件夹中并运行
运行前:
运行:
运行后:
将查杀和恢复分了开来,并且没有删除原来的crc32文件,算是不足
emmm,看到这里,你肯定也发现了,实验是做完了,但是!
病毒在加密文件的时候好像把文件名也给改了呀
怎么逆向分析的时候没分析到呢?
而且!你这个恢复,也没把文件名恢复啊!
emmm,这个事情是这样的
细心的你肯定也发现了
在这张图,也就是sub_401470查找docx文件的函数中,在do-while循环里面,除了说继续查找文件的sub_101014,和加密的sub_401023之外,还有一个
v7 = (LPSTR)sub_40100A(&String2);
v7跟一个“已经与'\'拼接了的v9”拼接
很好猜的,v9是路径,v7自然就是文件名了
那么文件名哪来的,肯定就是sub_40100A这个函数来的
点进去看看
再进去看看
再进去看看
再进去看看
到这里就emmm,好了打扰了
这是一个crc32的算法,看到算法头就开始大了,有兴趣的话可以自己百度一下、Google一下
大概就是将一个什么值与0xEDB88320做了一个很复杂运算(反之我是看不懂)
我认为,病毒就是用这个算法生成的值,重命名了文件
把文件名恢复...打扰了
上周的作业感觉没什么好写的,就没写一周
这一周的作业,emmm,还有完善的空间吧,但是为了赶着把文章发出去,就没有对代码进行完善了
附上Virus.exe的IDA Database文件
链接:百度云
提取码:deof