前言

我们这次会依据上次的内容,编程实现一个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层主动防御之编程实现相关推荐

  1. 病毒木马查杀实战第026篇:“白加黑”恶意程序研究(上)

    前言 众所周知,传统的恶意程序都是由单一文件构成的.从而实现某一种或者几种恶意功能. 而这类的恶意程序为了避免被发现以及被查杀,往往会採用五花八门的自我隐藏技术以及免杀技术,病毒程序的作者非常多时候也 ...

  2. 病毒木马查杀实战第009篇:QQ盗号木马之手动查杀

    前言 之前在<病毒木马查杀第002篇:熊猫烧香之手动查杀>中,我在不借助任何工具的情况下,基本实现了对于"熊猫烧香"病毒的查杀.但是毕竟"熊猫烧香" ...

  3. 病毒木马查杀实战第010篇:QQ盗号木马之十六进制代码分析

    前言 按照我的个人习惯,在运用诸如IDA Pro与OllyDBG对病毒进行逆向分析之前,我都会利用一些自动化的工具,通过静态或动态的分析方法(参见<病毒木马查杀第008篇:熊猫烧香之病毒查杀总结 ...

  4. 病毒木马查杀实战第001篇:基本查杀理论与实验环境配置

    前言 <病毒木马查杀>系列以真实的病毒木马(或统称为恶意程序)为研究对象,通过现有的技术手段对其分析,总结出它的恶意行为,进而制定出相应的应对方法(如编写专杀工具),对其彻底查杀.当然,本 ...

  5. 病毒木马查杀实战第002篇:熊猫烧香之手动查杀

    前言 作为本系列研究的开始,我选择"熊猫烧香"这个病毒为研究对象.之所以选择这一款病毒,主要是因为它具有一定的代表性.一方面它当时造成了极大的影响,使得无论是不是计算机从业人员,都 ...

  6. 病毒木马查杀实战第018篇:病毒特征码查杀之基本原理

    前言 在本系列的导论中,我曾经在"病毒查杀方法"中简单讲解过特征码查杀这种方式.而我也在对于实际病毒的专杀工具编写中,使用过CRC32算法来对目标程序进行指纹匹配,从而进行病毒判定 ...

  7. 病毒木马查杀实战第019篇:病毒特征码查杀之编程实现

    前言 上次我们已经简介过了病毒特征码提取的基本方法,那么这次我们就通过编程来实现对于病毒的特征码查杀. 定义特征码存储结构 为了简单起见.这次我们使用的是setup.exe以及unpacked.exe ...

  8. 病毒木马查杀实战第004篇:熊猫烧香之专杀工具的编写

    前言 如果是非感染型的病毒,完成行为分析之后,就可以开始编写专杀工具了.当然对于我们这次研究的对象--"熊猫烧香"来说,其实通过之前的行为分析,我们并没有得出它的所有恶意行为,毕竟 ...

  9. 病毒木马查杀实战第020篇:Ring3层主动防御之基本原理

    前言 如果说我们的计算机中安装有杀毒软件,那么当我们有意或无意地下载了一个恶意程序后,杀软一般都会弹出一个对话框提示我们,下载的程序很可能是恶意程序,建议删除之类的,或者杀软就不提示,直接删除了:或者 ...

最新文章

  1. java srs 推流_srs安装与ffmpeg推流
  2. sap data service安装方法
  3. linux下zip2
  4. 编译安装openresty+mysql+php7
  5. SQLite_Android
  6. 别乎略安身立命的基础本领
  7. 【系】微信小程序云开发实战坚果商城-前端之分类实现
  8. shiro中的过滤器
  9. 如何在电脑上开启2个微信(如何进行应用分身)
  10. PLSQL7配置免安装Oracle客户端
  11. SDUT—Python程序设计实验五(列表与元组)
  12. 大数据(3i)Sqoop安装和操作
  13. 前端开发app程序 用什么模拟器测试安卓_【移动端测试】安卓模拟器简介和adb使用...
  14. 如何判断是否是ssd硬盘?win10查看固态硬盘的方法
  15. VLC保存网络流视频
  16. Unity3D 多种播放音效的方式
  17. Soul源码解析(16)-Soul网关熔断插件使用及源码解读
  18. 面试:MySQL篇,详尽知识点总结
  19. 如此美妙,Python 处理CSV、JSON和XML数据的方法真简便
  20. 使用Joern处理大量文件,生成PDG速度很慢的解决方法

热门文章

  1. 怎样给孩子取一个好名字?搜狗“有名堂”大数据支招
  2. 一个关于未来十年的预言
  3. html怎么给视频添加边框,要求给视频做一个边框 怎么样图片边框覆盖视频?
  4. 【编译原理】高级语言及其语法描述
  5. 4.24随笔(美蜜代码漏洞以及Java9)
  6. Word里公式和文字不对齐的解决方法
  7. DIY界的新宠儿:一款“改装神器”如何开拓全球智能家居市场
  8. 工厂管理信息系统-数据库课程设计
  9. 【详细解读】计算机操作系统知识点总览
  10. BDB 入门篇 第5章 Saving and Retrieving Objects 保存和检索对象