一、恶意代码检测技术简述
二、恶意代码技术理论及实现
2.1 恶意代码注入技术
2.2 注入模块(dll)显示
三、文件加密技术
3.1 键盘记录技术
3.2 屏幕截取技术
四、恶意代码检测技术原理论述
4.1 MD5检测技术
4.2 特征码检测技术
4.3 导入表分析技术(启发式扫描)
4.4 沙盒模拟技术
原创 FreeBuf
一、恶意代码检测技术简述
恶意代码(MALWARE)检测技术目前主流的有以下几种:特征码扫描技术
、基于签名的扫描技术
、启发式扫描技术
、沙盒模拟技术
、导入表分析技术
、以及云查杀技术
。
恶意代码检测大体分为静态分析和动态分析。
静态分析中最为经典的就是特征码扫描技术(Signature scanning),特征码扫描技术是恶意代码检测的基石,其通过检测二进制文件中是否含有恶意代码特征值来判断文件是否存在威胁,特征码扫描技术依赖于海量已知的恶意代码特征,这些特征绝大部分都是人工逆向提取出来含有一定意义的16进制码,也可能是由程序自动提取得到的特征码。
基于恶意代码签名的检测技术、导入表分析技术也属于静态分析技术,但是基于恶意代码签名的检测技术并没有对文件进行分析,只是对文件进行了签名计算。
动态分析的代表就是启发式扫描(HEURISTIC scanning technique),启发式扫描也依赖于特征库,但与特征码静态扫描不同的是,启发式扫描会实时检测系统中存在的进程,当进程作出一些敏感行为
时(如修改注册表
、格式化磁盘
、长时间大量读写文件
、隐藏文件
、添加启动项
、调用未导出的系统函数
等等)为该进程累积权值,不同的行为对应了不同的权值,当一个进程权值达到设定的阈值时即可以判定该程序存在恶意行为。
启发式扫描的这种动态分析的方式可以检测未知的恶意程序,并且还可以排除二进制文件加壳而导致的漏报。虽然启发式扫描有这些好处,但是启发式扫描检测进程是基于程序运行之上的,也就是说当一个未知安全性的程序运行后,在其生命周期内实时的对于其行为检测,当恶意程序通过某种方式绕过了启发式检测后,该恶意程序的危险行为会直接作用于系统,造成威胁。
所以在此基础上出现了沙盒分析方法,沙盒分析法也是一种动态分析方法,其将待检测文件置于了一个模拟的、可控的虚拟环境中运行,通过分析其行为以及函数调用来对其威胁性打分,得益于这种代码仿真技术,弥补了启发式扫描的不足,减小了不必要的损失。
以上的种种检测技术丰富了与恶意代码的对抗手段
,同时也提供了对层出不穷的新病毒的检测能力
以及应急响应能力
。其中大多数检测技术都依赖于一个庞大的特征库
,里面包含了已知恶意代码的各种特征,例如:16进制特征
、病毒文件的签名特征
、病毒行为特征
等等。由于特征信息对计算机病毒检测有着举足轻重的作用,所以特征选取的好坏直接决定了病毒扫描的性能。
特征码扫描技术具有一定的通杀性(当然,这取决于特征码选取的好坏),即通过广泛存在于恶意文件中的特征码进行扫描,可以实现一对多的扫描能力(即一条特征码可以匹配多种恶意文件)。
由于特征签名的的唯一性可对一样本实现100%的查杀率,但由于特征签名具有的唯一性,仅能实现一对一(即一个签名值仅能匹配一个恶意文件),这种方法虽然不可能出现误报的情况,但所需的病毒库体量会过于庞大,且只要恶意代码作者重新编译文件或改变任何一个字节,都会使得该签名值无法匹配,但是由于签名计算实现十分简单快速,因此基于特征签名的检测技术适合对突发的一种恶意代码进行应急响应,前提是恶意代码不会动态更新其本体。
导入表分析技术(启发式扫描的一种方式),通过为不同的API赋予不同的权值,一个程序通常会调用多个API,当对一个程序的总和权值累积到阈值时即可判定为恶意程序,这也就说明了导入表分析技术(启发式扫描技术)无法识别病毒的具体类型或名称,同时启发式扫描的误报率也是很高的,但这种识别方式也对防范未知恶意程序有很大贡献。
总结一下,各扫描技术特点如下表:
二、恶意代码技术理论及实现
2.1 恶意代码注入技术
恶意代码注入是指将恶意代码附加在正常运行中的程序上,以实现对系统或正常程序的破坏、修改等作用。
一个windows程序通常会加载多个动态连接库(dll)文件。例如,一个名为nika.exe的进程中导入了kernel32,gdi32等动态链接库(dll),而恶意代码注入就是将恶意模块加载于正常程序所调用的动态链接库后,以此来执行恶意行为。当对nika.exe注入了一个名为hacker的恶意动态连接库后,我们发现,恶意程序将一个名为hacker.dll的动态连接库放入了nika.exe所链接的库文件中,那么此时hacker.dll中的恶意代码是附加在nika.exe上运行的。
恶意代码注入技术分为几种:线程注入、HOOK注入、APC注入等。
线程注入通过创建远程线程的方式加载恶意动态链接库,通常使用以下函数实现:
OpenProcess(打开进程)
VirtualAllocEx(修改内存空间状态)
WriteProcessMemory(写入内存至空间)
LoadLibrary(加载动态链接库)
CreateRemoteThread(远程创建线程)
一个简单的注入技术(远程线程注入技术)流程如下:
1、调用OpenProcess打开目标进程(得到目标进程句柄)
2、调用VirtualAllocEx在目标进程中申请一块内存(用于写动态链接库)
3、调用WriteProcessMemory将Dll路径写入远程内存空间内
4、调用GetProcAddress获取 LoadLibrary在Kernel32中的地址(方便后续远程调用LoadLibrary加载动态链接库)
5、调用CreateRemoteThread创建一个远程线程,用于调用LoadLibrary加载动态链接库
6、关闭目标句柄
接下来我们尝试实现一个简单的注入程序:
第一步
:实现一个简单的程序(输出Hallo World),作为被注入程序。
代码如下:
#include <stdio.h>
int main()
{
printf("Hallo World!\n");
getchar();
return 0;
}
代码中的getchar函数是防止程序输出字符串后直接退出,从而无法观察到实验现象而使用的。
第二步
:新建一个项目(动态链接库项目),用以下代码实现简单弹窗:
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
HANDLE hHandle = GetCurrentProcess();
DWORD pressID = GetProcessId(hHandle);
char temp[100] = { 0 };
sprintf(temp, "I am Form pressID = %d", pressID);
MessageBoxA(0, temp, "Success!", MB_OK);
break;
}
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
DLL_PROCESS_ATTACH表示dll加载成功,case DLL_PROCESS_ATTACH下的代码获取了当前进程PID并且通过 MessageBoxA弹出窗口。这可以更加方便我们理解注入技术。
第三步
:再新建一个项目用于远程注入动态链接库,代码如下:
#include <stdio.h>
#include <Windows.h>
int main()
{
printf("请输入进程ID:");
DWORD processID = 0;
scanf("%d", &(DWORD)processID);
char dllPath[MAX_PATH];
memset(dllPath, 0x00, sizeof(dllPath));
printf("请输入注入模块路径(绝对路径):");
scanf("%s", dllPath);
LPVOID pathSize = lstrlen(dllPath) + 1;
HANDLE hHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID);
if (hHandle == NULL)
{
printf("远程进程打开失败!\n");
getchar();
return -1;
}
LPVOID NewMemoryAddress = VirtualAllocEx(hHandle, NULL, pathSize, MEM_COMMIT, PAGE_READWRITE);
if (NewMemoryAddress == NULL)
{
printf("远程进程空间创建失败!\n");
getchar();
return -1;
}
SIZE_T real_size = 0;
if (WriteProcessMemory(hHandle, NewMemoryAddress, dllPath, pathSize, &real_size) == FALSE)
{
printf("远程进程空间写入失败!\n");
getchar();
return -1;
}
printf("写入目标进程空间成功[写入 %zu 字节]\n", real_size);
FARPROC LoadFun = GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
if (LoadFun == NULL)
{
printf("获取LoadLibrary函数地址失败!\n");
getchar();
return -1;
}
HANDLE rHandle = CreateRemoteThread(hHandle, NULL, 0, (LPTHREAD_START_ROUTINE)LoadFun, NewMemoryAddress, 0, NULL);
if (rHandle == NULL)
{
printf("远程调用LoadLibrary注入失败!\n");
getchar();
return -1;
}
CloseHandle(hHandle);
printf("注入成功!\n");
getchar();
return 0;
}
其中 processID是目标进程的进程ID,而 dllPath是第二步中生成的Dll文件的路径(注意需要使用DLL的绝对路径)
以上我们就实现了一个简单的远程线程注入程序,运行结果如下:
注入后目标进程成功弹出窗口并显示进程PID
2.2 注入模块(dll)显示
使用ProcessExplorer工具查看进程PID及加载的模块:
三、文件加密技术
使用文件加密技术是勒索病毒常见的行为特征之一,勒索病毒通过加密计算机内重要文件实施勒索行为,部分勒索病毒只会加密特定后缀的文件(如.pdf .txt .doc等等,文档文件类型居多)以实现快速加密勒索。
常见的加密方法大致分为以下几类:
-
对称加密( symmetric encryption algorithm ):对称加密只有一个密钥,分别由加密方和解密方保管,其加密算法是固定的,密钥始终不变,由加密方通过密钥加密后的数据交给解密方,解密方使用与加密方相同的密钥经过相同的算法即可解密密文。这种加密算法的优点是加密速度快、复杂度效、方便易用,但是其安全性低,由于密钥加密方通过生成的密钥加密明文后需要连同加密密钥一起发送给解密方才能解密密文,而这个传输过程中可能会被截断或篡改,导致保密性和安全性收到影响。常见的对称加密算法有AES、DES、RC等算法。
-
非对称加密( public key encryption ):非对称加密由两个密钥,分别是公钥和私钥,公钥可以被任何人浏览,而私钥通常由加密方保管,用公钥加密的数据只能使用私钥解密,使用私钥加密的数据只能通过公钥解密。加密方通过接收方生成的公钥加密,并将密文发送至解密方,解密方使用对应的私钥即可解密,私钥是保密的,而公钥是公开的,即使公钥传输时被截获,由于不知道私钥也就无法解密密文。所以在非对称加密中只需要保护好密钥即可,并且传输过程中没有传输私钥,私钥始终保存在解密方。非对称加密的优点是:安全性高、密文不易修改和解密。缺点:加密算法复杂,加密解密耗时高。常见的非对称加密算法有:RSA、Elgamal等算法。
以上就是两大类的加密类型,下面介绍一下文件加密模式:
静态加密:对文件加密后得到一份加密文件,当需要使用时在手动使用解密算法对加密文件进行解密,解密后即可正常访问文件。
动态加密:对文件进行动态加密,实时检测访问文件用户合法性,对于合法用户在访问文件时进行实时解密,在用户关闭文件时重新加密,无需手动解密加密文件,动态加密能够做到加解密全过程透明无感,所以动态加密技术也成为透明加密。
对于文件加密,通常流程为:读取文件流 ⏩ 逐字节使用加密算法加密(对称或非对称算法) ⏩ 得到加密文件。解密流程同上。
3.1 键盘记录技术
键盘记录技术也是木马病毒常用的窃取(如账号,密码等)信息的手段之一,通过实时检测用户的按键信息,截取其中有用的部分,即用户账号密码的输入,从而实现木马盗号等威胁行为。在Windows下我们可以使用GetRawInputData函数获取用户输入。流程如下:
注册原始输入设备(RegisterRawInputDevices) → 获取原始输入数据(GetInputRawData) → 记录键盘数据至文件
其中需要一张ASCLL码对应键位信息表,这需要自定义,以实现按键对应ASCLL码的查询与记录。
3.2 屏幕截取技术
屏幕截取技术同样是木马远控等程序常有的功能。通过截取目标机器屏幕,可以使远控端查看目标当前所处状态及操作。截取屏幕可以使GetDC来实现,GetDc可以进行绘图。
代码如下:
#include <stdio.h>
#include <Windows.h>
#include <atlimage.h>
int PrintScreen()
{
HWND hWnd;
HDC hdc;
HBITMAP bmp;
HGDIOBJ bmp_t;
HDC mdc;
unsigned int width = 0;
unsigned int hight = 0;
hWnd = GetDesktopWindow();
hdc = GetDC(hWnd);
mdc = CreateCompatibleDC(hdc);
width = GetSystemMetrics(SM_CXSCREEN);
hight = GetSystemMetrics(SM_CYSCREEN);
bmp = CreateCompatibleBitmap(hdc, width, hight);
bmp_t = SelectObject(mdc, bmp);
BitBlt(mdc, NULL, NULL, width, hight, hdc, NULL, NULL, SRCCOPY);
CImage image;
image.Attach(bmp);
image.Save("ScreenPrint.jpg");
return 0;
}
int main(void)
{
PrintScreen();
}
四、恶意代码检测技术原理论述
4.1 MD5检测技术
MD5检测技术是指通过MD5算法对恶意文件生成一串长度固定,且唯一的MD5值,通过大量样本的计算,将多个恶意文件的MD5值保存至文件便成为了MD5病毒库,在检测一个新程序是否是恶意文件时我们只需要对这个文件用相同的MD5算法进行运算,得到的MD5值进入数据库内查询,如果匹配到了,那么我们可以判断,这一定是一个恶意文件。
MD5由于计算快速简便且不会发生误报的现象,大部分恶意代码检测引擎都有对文件MD5的检测。但由于MD5的唯一性,哪怕恶意文件中1个字节发生了改变,MD5也会随之发生改变。这样就出现了另一种优化的MD5检测方法,通过查找PE文件的.text节(即代码段),对其进行MD5值运算,这样得出的MD5值相较先前更好一点。当然了,现在也有一种模糊哈希的计算方法,通过对恶意文件分片计算哈希值可以对比出两个文件的相似度。这种算法更适合对恶意文件进行检测。接下来我们通过一个图片加深对MD5检测技术的了解:
上图简单表明了一个基础的MD5检测病毒的示例,通过计算待测文件MD5与病毒库内保存的恶意文件MD5值比对,当MD5值A与MD5值B相同时则匹配到恶意文件,反之则为正常文件。
4.2 特征码检测技术
接下来我们来了解特征码检测技术,特征码检测技术是一种静态检测方式,特征码检测技术通过在二进制文件中查找指定的特征码(通常是十六进制)进行比对分析来判定程序是否存在威胁。
特征码( Signature ):特征码通常为十六进制码,特征码可以是二进制文件中特定的字符串(如熊猫烧香病毒中的“xboy”字符串,转换为十六进制特征码0x78 0x62 0x6f 0x79),还可以是一段汇编指令或其他特征。特征码不再是简单的一对一(即一条特征码对应一个病毒)而可以做到一条特征码可以匹配多个病毒(即具有通杀性)。
特征码匹配 :特征码匹配是特征码检测技术中的一部分,我们通常将特征码匹配的算法成为特征码扫描引擎。
特征码匹配技术有以下几种方式:
1、基于特征码偏移量+特征码长度的匹配方式:这种匹配方式通过所得特征码位于恶意文件内的偏移地址加上特征码的长度进行定位匹配,这种方式的好处是扫描快,缺点是这种匹配方式通杀性较低,对于存在特征码但偏移地址不同的恶意文件无法扫描出正确结果。下图解释了该种特征码匹配技术扫描流程:
2、基于全文匹配的方式:该特征码匹配方式使用了全文匹配算法,通过KMP或AC自动机或BF暴力匹配等算法在整个待测文件的二进制码中匹配特征,只要成功匹配到,则可以判定该文件为恶意文件。下图解释了该种特征码扫描流程:
4.3 导入表分析技术(启发式扫描)
导入表分析技术,是启发式扫描的一个子目,也属于静态分析方法,通过对Windows下PE文件的函数导入导出表进行分析,可以为该程序威胁程度进行赋分,当分数达到一定阈值时可以判断该程序可能为恶意程序。导入表分析技术也依赖于一个函数库,这个库中记录不同API函数的敏感程度,并为该API函数赋分,在扫描程序时当匹配到该程序调用了库中记载的API函数时查找库中该函数对应的权重并累加到该文件的可疑总分中。该检测方式优点是可以检测未知的威胁文件;缺点是误报率较高。
下图简述了导入表分析技术的流程:
当然这只是一个最简单的方式,实际情况复杂的多,一般通过导入表组合排列或函数调用流程来做更精确的检测。
4.4 沙盒模拟技术
沙盒模拟技术,是一种动态分析方式,通过虚拟出未知文件的运行环境并在该虚拟环境中(与物理机隔离)运行该未知程序并实时监控该未知程序操作来判断该程序的威胁性。由于该虚拟环境与物理主机完全隔离,所以即使该程序存在威胁操作也不会影响到真实的物理主机。