您现在的位置: 华盟网 >> Hack >> 病毒知识 >> 正文

[组图]反病毒攻防研究:利用Inline HOOK实现主动防御

2015/3/17 作者:彩儿 来源: 本站整理
导读 77169.com小编引导:一、前言        之前文章中所讨论的歹意顺序的应对办法,都是非常主动的,即只要当歹意顺序被执行后,才思索弥补措施。这样,我们就会不断处于先手形态,而假如说病毒的危害性

一、前言

        之前文章中所讨论的恶意程序的应对方法,都是十分被动的,即只有当恶意程序被执行后,才考虑补救措施。这样,我们就会一直处于后手状态,而如果说病毒的危害性极大,那么即便我们完美地修复了诸如注册表项,服务项等敏感位置,并且删除了病毒本身,但是它依旧可能已经破坏了系统中非常重要的文件,造成了不可逆的损伤。因此这篇文章就来简单讨论一下利用Inline HOOK技术实现主动防御,在病毒执行前,就主动将危险函数劫持,如同一道防火墙,保护我们计算机的安全。

二、Inline HOOK原理

        我们平时所使用的API函数都保存在操作系统提供的DLL文件中,当程序需要使用某个API函数时,在程序运行后,程序会隐式地将API函数所在的DLL加载到内存中。这样,程序就会像调用自己的函数一样调用API。依据这个原理,假设我们要对系统中的某个API函数进行HOOK,那么首先就需要在指定进程的内存中找到该函数的地址,然后修改该函数首地址的代码为“jmp MyProc”指令,这里的“MyProc”就是我们自己编写的欲执行的函数。这样一来,当指定的进程想要正常调用该函数时,就会直接跳到我们自己所编写的函数中去执行,这样就完成了Inline HOOK。将流程归纳如下:
        (1)构造跳转指令,即jmp MyProc。
        (2)在内存中找到欲HOOK函数的地址(这可以通过使用GetProcAddress()函数实现),然后保存欲HOOK位置处的前5个字节(跳转指令需占用5个字节)。
        (3)将构造的跳转指令写入需要HOOK的位置处,这样当被HOOK的位置执行时,就会跳转到我们的函数执行。
        (4)如果要执行原来的函数,那么需要取消HOOK,还原第(2)步中保存的5个字节。
        (5)执行原来的流程。
        那么我们接下来的编程就依照这个流程来进行。


三、封装InlineHOOK类

        为了方便起见,对于HOOK技术的编程我这里采用面向对象的思想。用C++封装一个Inline HOOK类,这样以后的编程也可以使用它。
        一般来说,封装的类都有两个文件,一个是类的头文件,另一个是类的实现文件。类名一般都是以字母“C”为开头,表示“Class Name”,因此这里的类名为“CInlineHOOK”,头文件我们起名为“CInlineHOOK.h”,那么类的实现文件命名为“CInlineHOOK.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;   }  

        以上就是整个Inline HOOK的封装,代码非常简单,这里不再赘述。以后的研究文章中,还会用到这个类。因为利用它可以很容易地实现对函数的HOOK。

 

四、对“病毒”所用到的函数进行HOOK

        这里我们结合我之前所写的文章《反病毒攻防研究第010篇:DLL注入(中)——DLL注入与卸载器的编写》进行讨论。在那篇文章中,我使用DLL注入器将模拟病毒程序的DLL文件注入到了记事本进程中,这样当DLL成功注入后,用于模拟病毒的对话框程序就自动运行了。而在这篇文章中,我同样也是要编写一个DLL文件,用这个DLL文件来劫持“病毒”程序。这里是假设MessageBoxA()就是一个危险函数,也是我们所要劫持的目标。首先将本篇文章所生成的DLL注入,然后再注入“病毒”DLL进行验证。

        同样需要建立一个简单的Win32 Dynamic Link Library项目,然后添加如下代码:


// HookMessageBoxA.cpp : Defines the entry point for the DLL application.   //  
   #include "stdafx.h"  
#include "InlineHOOK.h"     
CInlineHOOK MsgHOOK;     
// 我们实现的HOOK函数   int  
WINAPI   MyMessageBoxA(  
        HWND hWnd,           LPCSTR lpText,  
        LPCSTR lpCaption,           UINT uType  
)   {  
        //先卸载Inline HOOK,以显示用户提示对话框           MsgHOOK.UnHOOK();  
        if ( MessageBox(NULL,"检测到可疑程序正在启动,是否拦截?", "提示", MB_YESNO) == IDNO )           {  
                //用户选择了不进行拦截                   MessageBoxA(hWnd,lpText,lpCaption,uType);  
                MessageBox(NULL, "疑似恶意程序未被拦截", "提示", MB_OK);                   MsgHOOK.ReHOOK();  
        }            else  
        {                   //用户选择了进行拦截  
                MessageBox(NULL, "疑似恶意程序拦截成功", "提示", MB_OK);                   MsgHOOK.ReHOOK();  
        }     
        return 0;   }  
   BOOL APIENTRY DllMain( HANDLE hModule,   
                       DWORD  ul_reason_for_call,                           LPVOID lpReserved  
                      )   {  
        switch ( ul_reason_for_call )           {  
         //当DLL被某进程加载时DllMain被调用           case DLL_PROCESS_ATTACH:  
        {                   MsgHOOK.HOOK("user32.dll",  
                             "MessageBoxA",                                (PROC)MyMessageBoxA);  
                break;           }  
        //当DLL被某进程卸载时DllMain被调用           case DLL_PROCESS_DETACH:  
        {                   MsgHOOK.UnHOOK();  
                break;           }  
        }              return TRUE;  
}  

        然后将我们之前封装的类的两个文件添加到工程中,如下图所示:

\图1 添加类文件到工程中

        编译生成DLL文件,就可以进行测试了。

 

五、程序的测试

         首先打开记事本程序,获取其进程ID,然后利用《反病毒攻防研究第010篇:DLL注入(中)——DLL注入与卸载器的编写》中所编写出来的DLL注入工具,将上述DLL文件注入到记事本进程中。然后再注入“病毒”程序,如下图所示:

\

图2 成功拦截可疑程序

        可见我们的Inline HOOK已经成功将MessageBox()函数拦截下来了,此时点击“是”:

\

图3 提示恶意程序拦截成功

        程序会提示说拦截成功,而用于模拟病毒的对话框也并未弹出。此时再查看一下记事本进程中的模块信息:

\

 

图4 查看记事本进程的模块信息

        可见我们所注入的两个DLL文件都在记事本进程中,但是HackedDll.dll已经失效,对我们的系统已经不会产生威胁了。如果在图2中所示的对话框选择了“否”,表明我们不进行拦截,则会如下图所示:

\

图5 放弃拦截

        此时用于模拟病毒的对话框程序弹出,如果这个是真的病毒的话,那么我们的计算机就已经被感染了。点击“确定”:

\

图6 提示恶意程序未被拦截

        至此,我们的Inline HOOK程序的目的达到,说明是可行的。

 

六、小结

        其实,HOOK的应用范围非常广泛,本篇文章所举的例子不过是冰山一角。灵活利用这种思想,在安全领域可以达成很多的目的。比如杀毒软件就会用HOOK技术钩住一些API函数,如钩住RegSetValueEx()函数,以防止病毒对注册表的写入。而由本篇文章的分析又可以发现,我们所学的知识往往是一环扣一环的,只有把之前的知识学好了,打下了坚实的基础,才能不断探索更加高深的领域。以前的知识,我也会在未来的文章中多次提及并运用,所以也希望读者能够勤于动手,切忌眼高手低。​​

 

                  微信群名称:华盟黑白之道二群     华盟-黑白之道⑦QQ群: 9430885

  • 上一篇Hack:

  • 下一篇Hack:
  • 网友评论
      验证码
     

    关注

    分享

    0

    讨论

    2

    猜你喜欢

    论坛最新贴