病毒木马查杀实战第021篇:Ring3层主动防御之编程实现
前言
我们这次会依据上次的内容,编程实现一个Ring3层的简单的主动防御软件。整个程序使用MFC实现,程序开始监控时,会将DLL程序注入到explorer.exe进程中,这样每当有新的进程创建,程序首先会进行特征码匹配,从而判断目标程序是否为病毒程序,如果是,则进行拦截,反之不拦截。停止监控时,再卸载掉DLL程序。以下就是程序各个部分的代码实现。
封装InlineHook类
对于这次所使用的Hook技术,我打算采取面向对象的方法,用C++封装一个Inline Hook类,便于以后的使用。一般来说,封装的类都有两个文件,一个是类的头文件,另一个是类的实现文件。类名一般都是以字母“C”为开头,表示“Class Name”,因此这里的类名为“CInlineHook”,头文件我起名为“InlineHook.h”,那么类的实现文件就可以命名为“InlineHook.cpp”。首先是类的头文件代码:
#include <windows.h>class CInlineHook
{
public:CInlineHook(); // 构造函数,用于初始化~CInlineHook(); // 析构函数,用户程序结束后资源的释放// Hook函数BOOL Hook(LPSTR pszModuleName, LPSTR pszFuncName, PROC pfnHookFunc);// 取消Hook函数void UnHook();// 重新进行Hook函数BOOL ReHook();private:PROC m_pfnOrig; // 自定义的函数的地址BYTE m_bOldBytes[5]; // 原始函数入口代码BYTE m_bNewBytes[5]; // 构造的跳转指令的代码
};
头文件中主要是声明一些需要使用的函数与变量,代码中已给出了相应的注释。接下来是类的实现文件(InlineHook.cpp)的代码:
#include "stdafx.h"
#include "InlineHook.h"CInlineHook::CInlineHook()
{// 对成员变量的初始化m_pfnOrig = NULL;ZeroMemory(m_bOldBytes, 5);ZeroMemory(m_bNewBytes, 5);
}CInlineHook::~CInlineHook()
{// 取消HookUnHook();
}//挂钩函数,参数依次为模块名称、函数名称以及自定义的钩子函数
BOOL CInlineHook::Hook(LPSTR pszModuleName, LPSTR pszFuncName, PROC pfnHookFunc)
{BOOL bRet = FALSE;// 获取指定模块中函数的地址m_pfnOrig = (PROC)GetProcAddress(GetModuleHandle(pszModuleName), pszFuncName);if ( m_pfnOrig != NULL ){// 保存该地址处前5个字节的内容DWORD dwNum = 0;ReadProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bOldBytes, 5, &dwNum);// 构造JMP指令,"\xe9"为jmp的Opcodem_bNewBytes[0] = '\xe9'; // pfnHookFunc是Hook后的地址,m_pfnOrig是原来的地址,5是指令长度*(DWORD *)(m_bNewBytes + 1) = (DWORD)pfnHookFunc - (DWORD)m_pfnOrig - 5;// 将构造好的地址写入该地址处WriteProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bNewBytes, 5, &dwNum);bRet = TRUE;}return bRet;
}//取消函数的挂钩
void CInlineHook::UnHook()
{if ( m_pfnOrig != 0 ){DWORD dwNum = 0;WriteProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bOldBytes, 5, &dwNum);}
}//重新对函数进行挂钩
BOOL CInlineHook::ReHook()
{ BOOL bRet = FALSE; if ( m_pfnOrig != 0 ) { DWORD dwNum = 0; WriteProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bNewBytes, 5, &dwNum); bRet = TRUE; } return bRet;
}
以上就是整个InlineHook的封装,代码非常简单,这里不再赘述,利用它可以很容易地实现对函数的Hook。
编写DLL程序
这里需要创建一个简单的Win32 DynamicLink Library项目:
图1
并把上面编写的“InlineHook.h”和“InlineHook.cpp”加入该项目中:
图2
然后在源文件中新建一个名为HookCreateProcess.cpp的文件,添加如下代码:
// HookCreateProcess.cpp : Defines the entry point for the DLL application.
//#include "stdafx.h"
#include "InlineHook.h"
#include "windows.h"#define NAMELEN 20
#define SIGNLEN 32typedef struct SIGN
{char szVirusName[NAMELEN];LONG lFileOffset;BYTE bVirusSign[SIGNLEN+1];
}_SIGN, *PSIGN;// 病毒程序的特征码
SIGN Sign[2] =
{{// setup.exe"setup.exe",0x0C040,"\x2a\x2a\x2a\xce\xe4\x2a\xba\xba\x2a\xc4\xd0\x2a\xc9\xfa\x2a\xb8"\"\xd0\x2a\xc8\xbe\x2a\xcf\xc2\x2a\xd4\xd8\x2a\xd5\xdf\x2a\x2a\x2a"},{// unpacked.exe"unpacked.exe",0x1920,"\x13\x8b\x45\xf0\xe8\x00\x00\x00\x00\x81\x04\x24\xd7\x86\x00\x00"\"\xff\xd0\xeb\x11\x6a\x10\x68\x30\x80\x40\x00\xff\x75\xfc\x53\xff"}
};// 特征码检测函数
BOOL CheckSig(LPCWSTR FilePath)
{DWORD dwSigNum = 0;DWORD dwNum = 0;BYTE buffer[SIGNLEN+1];int i;HANDLE hFile = NULL;hFile = CreateFileW(FilePath,GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);for(i=0; i <= 1; i++){// 将待检测程序的文件指针指向特征码的偏移位置SetFilePointer(hFile, Sign[i].lFileOffset, NULL, FILE_BEGIN);// 读取目标程序指定偏移位置的特征码ReadFile(hFile, buffer, sizeof(buffer), &dwNum, NULL);// 特征码的比对if(memcmp(Sign[i].bVirusSign, buffer, SIGNLEN) == 0){CloseHandle(hFile);return TRUE;}}CloseHandle(hFile);return FALSE;
}// 创建一个名为CreateProcessHook的CInlineHook类
CInlineHook CreateProcessHook;// 我们实现的Hook函数
BOOL
WINAPI
MyCreateProcessW(LPCWSTR lpApplicationName,LPWSTR lpCommandLine,LPSECURITY_ATTRIBUTES lpProcessAttributes,LPSECURITY_ATTRIBUTES lpThreadAttributes,BOOL bInheritHandles,DWORD dwCreationFlags,LPVOID lpEnvironment,LPCWSTR lpCurrentDirectory,LPSTARTUPINFOW lpStartupInfo,LPPROCESS_INFORMATION lpProcessInformation)
{BOOL bRet = FALSE;if ( !CheckSig(lpApplicationName) ){// 如果经过特征码匹配,目标程序不是病毒,则卸载钩子,执行程序,再安装钩子 CreateProcessHook.UnHook();bRet = CreateProcessW(lpApplicationName,lpCommandLine,lpProcessAttributes,lpThreadAttributes,bInheritHandles,dwCreationFlags,lpEnvironment,lpCurrentDirectory,lpStartupInfo,lpProcessInformation);CreateProcessHook.ReHook();} else{// 如果经过特征码匹配,目标程序是病毒,则进行拦截MessageBox(NULL, "您启动的程序是病毒,已经被拦截!", "重要提示", MB_OK);}return bRet;
}BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{switch ( ul_reason_for_call ){case DLL_PROCESS_ATTACH:{// Hook CreateProcessW()函数CreateProcessHook.Hook("kernel32.dll","CreateProcessW",(PROC)MyCreateProcessW);break;}case DLL_PROCESS_DETACH:{CreateProcessHook.UnHook();break;}}return TRUE;
}
上述程序在编译运行后,就会生成我们所需要的DLL文件。其原理是钩取成功后,每次遇到CreateProcess()函数,都会解析它的第一个参数,获取所要启动的程序完整路径,然后利用之前讲过的特征码的匹配方式进行匹配,以判定目标程序是否安全。如果遇到病毒程序,则进行拦截,使其无法运行,正常程序则放行。
程序界面的制作
程序使用MFC实现,界面中只有两个按钮:
图3
然后为这两个按钮分别添加两个变量:
图4
我们希望在程序运行时,“开启监控”按钮是可用状态,而“关闭监控”是不可用的状态,因此需要在BOOL CSimpleHIPSDlg::OnInitDialog()中添加如下代码:
图5
“开启监控”按钮代码的编写
“开启监控”按钮的功能是首先需要获取欲注入的DLL程序的完整路径,也就是在当前目录中。之后需要查找当前进程中是否存在explorer.exe进程,并获取其PID值,之后就可以利用该PID值进行DLL的注入了。而DLL注入的流程,已在上次讲过。完整代码如下:
void CSimpleHIPSDlg::OnButtonOn()
{// TODO: Add your control notification handler code hereBOOL bRet = FALSE;DWORD dwPid = 0;// 获取欲注入的DLL文件的完整路径char *szDllName = getcwd(NULL, 0);strcat(szDllName, "\\HookCreateProcess.dll");// 查找explorer.exe进程,获取其PID值bRet = FindTargetProcess("explorer.exe", &dwPid);// 如果找到explorer.exe进程,则注入DLLif(bRet == TRUE){// 利用PID值,获取进程的句柄HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);if(hProcess == NULL){AfxMessageBox("进程打开失败!");return;}// 长度为DLL名称的长度加上字符终止符int nDllLen = strlen(szDllName) + sizeof(char);// 申请内存空间PVOID pDllAddr = VirtualAllocEx( hProcess, // process to allocate memoryNULL, // desired starting addressnDllLen, // size of region to allocateMEM_COMMIT, // type of allocationPAGE_READWRITE); // type of access protectionif(pDllAddr == NULL){AfxMessageBox("申请内存区域失败!");CloseHandle(hProcess);return;}DWORD dwWriteNum = 0;if (!WriteProcessMemory(hProcess, pDllAddr, szDllName, nDllLen, &dwWriteNum)){AfxMessageBox("进程写入失败!");// 失败就释放原先申请的内存区域,撤销内存页的提交状态VirtualFreeEx(hProcess, pDllAddr, nDllLen, MEM_DECOMMIT);return;}// 获取LoadLibraryA的地址FARPROC pFunAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");// 创建远程线程HANDLE hThread = CreateRemoteThread(hProcess, // handle to processNULL, // SD0, // initial stack size(LPTHREAD_START_ROUTINE)pFunAddr, // thread functionpDllAddr, // thread argument0, // creation optionNULL); // thread identifierif (hThread == NULL){AfxMessageBox("创建远程线程失败!");// 释放原先申请的内存区域,撤销内存页的提交状态VirtualFreeEx(hProcess, pDllAddr, nDllLen, MEM_DECOMMIT);return;}AfxMessageBox("监控成功开启!");m_BtnOn.EnableWindow(FALSE);m_BtnOff.EnableWindow(TRUE);// 等待线程退出WaitForSingleObject(hThread, INFINITE);// 释放原先申请的内存区域 撤销内存页的提交状态VirtualFreeEx(hProcess, pDllAddr, nDllLen, MEM_DECOMMIT);//关闭句柄CloseHandle(hThread);CloseHandle(hProcess);}// 如果未找到explorer.exe进程,则进行提示else{AfxMessageBox("未找到explorer.exe进程,监控失败!");return;}
}
上述程序中使用了查找指定进程的函数FindTargetProcess(),它与之前所讲的“熊猫烧香专杀工具”中的代码是一致的,这里不再赘述。因为程序使用了getcwd()函数获取当前路径,所以需要添加头文件direct.h,而为了实现进程的遍历,又需要包含头文件Tlhelp32.h。
“关闭监控”按钮代码的编写
“关闭监控”按钮的功能是查找explorer.exe进程中是否含有我们所注入的HookCreateProcess.dll文件,如果有,则将其卸载掉。为了保险起见,还需要先进行提升权限的操作。提升权限的代码与之前所讲的“熊猫烧香专杀工具”中的代码是一致的,这里不再赘述。完整的代码如下:
void CSimpleHIPSDlg::OnButtonOff()
{// TODO: Add your control notification handler code hereBOOL flag = FALSE;DWORD dwPid = 0;char *szDllName = "HookCreateProcess.dll";// 提升权限EnableDebugPrivilege(SE_DEBUG_NAME);// 查找explorer.exe进程,获取其PID值FindTargetProcess("explorer.exe", &dwPid);// 获取系统运行模块的列表HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPid);MODULEENTRY32 Me32 = { 0 };Me32.dwSize = sizeof(MODULEENTRY32);// 检索与进程相关联的第一个模块的信息BOOL bRet = Module32First(hSnap, &Me32);while ( bRet ){// 查找所注入的DLLif ( strcmp(Me32.szModule, szDllName) == 0 ){flag = TRUE;break;}//检索下一个模块信息bRet = Module32Next(hSnap, &Me32);}if (flag == FALSE){AfxMessageBox("找不到相应的模块!");return;}CloseHandle(hSnap);HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);if ( hProcess == NULL ){AfxMessageBox("进程打开失败!");return ;}FARPROC pFunAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "FreeLibrary");HANDLE hThread = CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)pFunAddr,Me32.hModule,0,NULL);if (hThread == NULL){AfxMessageBox("创建远程线程失败!");return;}AfxMessageBox("监控成功关闭!");m_BtnOn.EnableWindow(TRUE);m_BtnOff.EnableWindow(FALSE);//等待线程退出WaitForSingleObject(hThread, INFINITE);CloseHandle(hThread);CloseHandle(hProcess);
}
至此,所有程序编写完毕。
主动防御程序的测试
我们将主动防御程序与准备注入的DLL程序放置在同一目录中,运行主动防御程序,此时“关闭监控”按钮是不可用的状态:
图6
然后我们可以利用ProcessExplorer来协助我们进行观察。点击“开始监控”,可以发现在explorer.exe进程中,多出了一个名为HookCreateProcess.dll的文件,说明我们的注入是成功的,而且“开启监控”按钮也处于了不可用的状态:
图7
此时可以尝试一下运行setup.exe以及unpacked.exe这两个病毒程序:
图8
我们的主动防御系统都能够成功地将病毒程序拦截,而正常程序则会被主动放行,说明我们的程序达到了预期的目的。而点击“关闭监控”,通过Process Explorer可知,DLL文件已经被卸载掉了,也就说明,我们的程序很好地完成了相应的功能。
小结
我们这次所讨论的主动防御程序还是比较简陋的,也只能够防范特征库中所包含的病毒,而对于未知病毒则无能为力了,这就是利用特征库查毒的局限性。其实大家可以在我们的代码的基础上进行功能的完善,通过对各种各样的API函数的钩取,来保护我们的系统免受侵害。也希望这次的程序能够起到抛砖引玉的作用,使大家有所收获。
病毒木马查杀实战第021篇:Ring3层主动防御之编程实现相关推荐
- 病毒木马查杀实战第026篇:“白加黑”恶意程序研究(上)
前言 众所周知,传统的恶意程序都是由单一文件构成的.从而实现某一种或者几种恶意功能. 而这类的恶意程序为了避免被发现以及被查杀,往往会採用五花八门的自我隐藏技术以及免杀技术,病毒程序的作者非常多时候也 ...
- 病毒木马查杀实战第009篇:QQ盗号木马之手动查杀
前言 之前在<病毒木马查杀第002篇:熊猫烧香之手动查杀>中,我在不借助任何工具的情况下,基本实现了对于"熊猫烧香"病毒的查杀.但是毕竟"熊猫烧香" ...
- 病毒木马查杀实战第010篇:QQ盗号木马之十六进制代码分析
前言 按照我的个人习惯,在运用诸如IDA Pro与OllyDBG对病毒进行逆向分析之前,我都会利用一些自动化的工具,通过静态或动态的分析方法(参见<病毒木马查杀第008篇:熊猫烧香之病毒查杀总结 ...
- 病毒木马查杀实战第001篇:基本查杀理论与实验环境配置
前言 <病毒木马查杀>系列以真实的病毒木马(或统称为恶意程序)为研究对象,通过现有的技术手段对其分析,总结出它的恶意行为,进而制定出相应的应对方法(如编写专杀工具),对其彻底查杀.当然,本 ...
- 病毒木马查杀实战第002篇:熊猫烧香之手动查杀
前言 作为本系列研究的开始,我选择"熊猫烧香"这个病毒为研究对象.之所以选择这一款病毒,主要是因为它具有一定的代表性.一方面它当时造成了极大的影响,使得无论是不是计算机从业人员,都 ...
- 病毒木马查杀实战第018篇:病毒特征码查杀之基本原理
前言 在本系列的导论中,我曾经在"病毒查杀方法"中简单讲解过特征码查杀这种方式.而我也在对于实际病毒的专杀工具编写中,使用过CRC32算法来对目标程序进行指纹匹配,从而进行病毒判定 ...
- 病毒木马查杀实战第019篇:病毒特征码查杀之编程实现
前言 上次我们已经简介过了病毒特征码提取的基本方法,那么这次我们就通过编程来实现对于病毒的特征码查杀. 定义特征码存储结构 为了简单起见.这次我们使用的是setup.exe以及unpacked.exe ...
- 病毒木马查杀实战第004篇:熊猫烧香之专杀工具的编写
前言 如果是非感染型的病毒,完成行为分析之后,就可以开始编写专杀工具了.当然对于我们这次研究的对象--"熊猫烧香"来说,其实通过之前的行为分析,我们并没有得出它的所有恶意行为,毕竟 ...
- 病毒木马查杀实战第020篇:Ring3层主动防御之基本原理
前言 如果说我们的计算机中安装有杀毒软件,那么当我们有意或无意地下载了一个恶意程序后,杀软一般都会弹出一个对话框提示我们,下载的程序很可能是恶意程序,建议删除之类的,或者杀软就不提示,直接删除了:或者 ...
最新文章
- java srs 推流_srs安装与ffmpeg推流
- sap data service安装方法
- linux下zip2
- 编译安装openresty+mysql+php7
- SQLite_Android
- 别乎略安身立命的基础本领
- 【系】微信小程序云开发实战坚果商城-前端之分类实现
- shiro中的过滤器
- 如何在电脑上开启2个微信(如何进行应用分身)
- PLSQL7配置免安装Oracle客户端
- SDUT—Python程序设计实验五(列表与元组)
- 大数据(3i)Sqoop安装和操作
- 前端开发app程序 用什么模拟器测试安卓_【移动端测试】安卓模拟器简介和adb使用...
- 如何判断是否是ssd硬盘?win10查看固态硬盘的方法
- VLC保存网络流视频
- Unity3D 多种播放音效的方式
- Soul源码解析(16)-Soul网关熔断插件使用及源码解读
- 面试:MySQL篇,详尽知识点总结
- 如此美妙,Python 处理CSV、JSON和XML数据的方法真简便
- 使用Joern处理大量文件,生成PDG速度很慢的解决方法
热门文章
- 怎样给孩子取一个好名字?搜狗“有名堂”大数据支招
- 一个关于未来十年的预言
- html怎么给视频添加边框,要求给视频做一个边框 怎么样图片边框覆盖视频?
- 【编译原理】高级语言及其语法描述
- 4.24随笔(美蜜代码漏洞以及Java9)
- Word里公式和文字不对齐的解决方法
- DIY界的新宠儿:一款“改装神器”如何开拓全球智能家居市场
- 工厂管理信息系统-数据库课程设计
- 【详细解读】计算机操作系统知识点总览
- BDB 入门篇 第5章 Saving and Retrieving Objects 保存和检索对象