病毒木马查杀实战第021篇:Ring3层主动防御之编程实现

本文阅读 8 分钟

       我们这次会依据上次的内容,编程实现一个Ring3层的简单的主动防御软件。整个程序使用MFC实现,程序开始监控时,会将DLL程序注入到explorer.exe进程中,这样每当有新的进程创建,程序首先会进行特征码匹配,从而判断目标程序是否为病毒程序,如果是,则进行拦截,反之不拦截。停止监控时,再卸载掉DLL程序。以下就是程序各个部分的代码实现。

 

       对于这次所使用的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];    // 构造的跳转指令的代码
};
#include "stdafx.h"
#include "InlineHook.h"

CInlineHook::CInlineHook()
{
        // 对成员变量的初始化
        m_pfnOrig = NULL;
        ZeroMemory(m_bOldBytes, 5);
        ZeroMemory(m_bNewBytes, 5);
}

CInlineHook::~CInlineHook()
{
        // 取消Hook
        UnHook();
}

//挂钩函数,参数依次为模块名称、函数名称以及自定义的钩子函数
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的Opcode
                m_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。

 

       这里需要创建一个简单的Win32 DynamicLink Library项目:

img

图1

       并把上面编写的“InlineHook.h”和“InlineHook.cpp”加入该项目中:

img

图2

// HookCreateProcess.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "InlineHook.h"
#include "windows.h"

#define NAMELEN 20
#define SIGNLEN 32

typedef 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实现,界面中只有两个按钮:

img

图3

       然后为这两个按钮分别添加两个变量:

img

图4

       我们希望在程序运行时,“开启监控”按钮是可用状态,而“关闭监控”是不可用的状态,因此需要在BOOL CSimpleHIPSDlg::OnInitDialog()中添加如下代码:

img

图5

 

void CSimpleHIPSDlg::OnButtonOn() 
{
    // TODO: Add your control notification handler code here
    BOOL  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进程,则注入DLL
    if(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 memory
                                         NULL,            // desired starting address
                                         nDllLen,         // size of region to allocate
                                         MEM_COMMIT,      // type of allocation
                                         PAGE_READWRITE); // type of access protection
        if(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 process
                    NULL,                             // SD
                    0,                                // initial stack size
                    (LPTHREAD_START_ROUTINE)pFunAddr, // thread function
                    pDllAddr,                         // thread argument
                    0,                                // creation option
                    NULL);                            // thread identifier
        if (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。

 

“关闭监控”按钮代码的编写

void CSimpleHIPSDlg::OnButtonOff() 
{
    // TODO: Add your control notification handler code here
    BOOL  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 )
    {
        // 查找所注入的DLL
        if ( 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程序放置在同一目录中,运行主动防御程序,此时“关闭监控”按钮是不可用的状态:

img

图6

       然后我们可以利用ProcessExplorer来协助我们进行观察。点击“开始监控”,可以发现在explorer.exe进程中,多出了一个名为HookCreateProcess.dll的文件,说明我们的注入是成功的,而且“开启监控”按钮也处于了不可用的状态:

img

图7

       此时可以尝试一下运行setup.exe以及unpacked.exe这两个病毒程序:

img

图8

       我们的主动防御系统都能够成功地将病毒程序拦截,而正常程序则会被主动放行,说明我们的程序达到了预期的目的。而点击“关闭监控”,通过Process Explorer可知,DLL文件已经被卸载掉了,也就说明,我们的程序很好地完成了相应的功能。

 

本文为互联网自动采集或经作者授权后发布,本文观点不代表立场,若侵权下架请联系我们删帖处理!文章出自:https://jiangye.blog.csdn.net/article/details/51198642?spm=1001.2014.3001.5502
-- 展开阅读全文 --
KillDefender 的 Beacon 对象文件 PoC 实现
« 上一篇 02-09
Web安全—逻辑越权漏洞(BAC)
下一篇 » 03-13

发表评论

成为第一个评论的人

热门文章

标签TAG

最近回复