反病毒攻防研究第012篇:利用Inline HOOK实现主动防御

一、前言

之前文章中所讨论的恶意程序的应对方法,都是十分被动的,即只有当恶意程序被执行后,才考虑补救措施。这样,我们就会一直处于后手状态,而如果说病毒的危害性极大,那么即便我们完美地修复了诸如注册表项,服务项等敏感位置,并且删除了病毒本身,但是它依旧可能已经破坏了系统中非常重要的文件,造成了不可逆的损伤。因此这篇文章就来简单讨论一下利用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()函数,以防止病毒对注册表的写入。而由本篇文章的分析又可以发现,我们所学的知识往往是一环扣一环的,只有把之前的知识学好了,打下了坚实的基础,才能不断探索更加高深的领域。以前的知识,我也会在未来的文章中多次提及并运用,所以也希望读者能够勤于动手,切忌眼高手低。

时间: 2024-08-08 05:36:54

反病毒攻防研究第012篇:利用Inline HOOK实现主动防御的相关文章

反病毒攻防研究第015篇:病毒感染标志的添加

一.前言 对于感染型病毒而言,如果对同一个目标文件多次进行感染,有可能导致目标文件损坏,使得无法执行.所以病毒程序往往会在第一次感染时对目标文件写进一个感染标志,这样在第二次遇到该文件时,首先判断一下该文件中是否包含有感染标志,如果有,则不再感染,如果没有感染标志则进行感染(关于文件的感染,可参见<反病毒攻防研究第004篇:利用缝隙实现代码的植入>和<反病毒攻防研究第005篇:添加节区实现代码的植入>).所谓的感染标志其实就是在PE文件中无关紧要的位置写入的一个字符串,所以感染标志

反病毒攻防研究第009篇:DLL注入(上)——DLL文件的编写

一.前言 我之前所编写的用于模拟计算机病毒的对话框程序都是exe文件,所以运行时必将会产生一个进程,产生进程就非常容易被发现.而为了不被发现,可以选择将对话框程序创建为DLL文件.这种文件会加载到已有进程的地址空间中,这样就不会再次创建出进程,隐蔽性相对较好,DLL注入也是恶意程序总会使用的手段.这次我带算用几篇文章的篇幅来论述DLL注入的问题,而这篇文章就首先来讨论一下如何把我之前的对话框程序改写为DLL文件. 二.编写对话框DLL程序 这里我依旧使用VC++6.0,创建一个简单的Win32

反病毒攻防研究第004篇:利用缝隙实现代码的植入

一.前言 现在很多网站都提供各式各样软件的下载,这就为黑客提供了植入病毒木马的良机.黑客可以将自己的恶意程序植入到正常的程序中,之后发布到网站上,这样当用户下载并运行了植入病毒的程序后,计算机就会中毒,而且病毒可能会接着感染计算机中的其他程序,甚至通过网络或者U盘,使得传播面积不断扩大.而本篇文章就来剖析病毒感染的实现原理,首先需要搜索正常程序中的缝隙用于"病毒"(用对话框模拟)的植入,之后感染目标程序以实现"病毒"的启动.当然,讨论完这些,我依旧会分析如何应对这种

反病毒攻防研究第010篇:DLL注入(中)——DLL注入与卸载器的编写

一.前言 我在上一篇文章中所讨论的DLL利用方法,对于DLL文件本身来说是十分被动的,它需要等待程序的调用才可以发挥作用.而这次我打算主动出击,编写DLL注入与卸载器,这样就可以主动地对进程进行注入的操作了,从而更好地模拟现实中恶意代码的行为. 二.DLL注入的原理 如果想让DLL文件强制注入某个进程,那么就需要通过创建远程线程来实现.这里需要注意的是,所谓的"远程线程",并不是跨计算机的,而是跨进程的.举例来说,进程A在进程B中创建一个线程,这就叫做远程线程.从根本上说,DLL注入技

反病毒攻防研究第011篇:DLL注入(下)——无DLL的注入

一.前言 一般来说,想要将自己编写的程序注入到其它进程中,是必须要使用DLL文件的,这种方法已经在上一篇文章中讨论过了.但是事实上,可以不依赖于DLL文件来实现注入的.只不过这种方法不具有通用性,没有DLL注入那样灵活,因为它需要把代码写入"注入程序"中,一旦想要注入的内容发生了变化,就需要重写整个"注入程序".而不像DLL注入那样,只要修改DLL程序即可.即便如此,无DLL进行注入的方式,也是一种值得讨论的方法. 二.无DLL注入的基本原理 在注入与卸载方面,无论

反病毒攻防研究第007篇:简单木马分析与防范part1

一.前言 病毒与木马技术发展到今天,由于二者总是相辅相成,你中有我,我中有你,所以它们之间的界限往往已经不再那么明显,相互之间往往都会采用对方的一些技术以达到自己的目的,所以现在很多时候也就将二者直接统称为"恶意代码".这次我打算用两篇文章的篇幅来讨论病毒与简单的木马相互结合的分析与防范方法.本篇也就是第一篇,讨论的是利用只有服务器端的木马程序实现"病毒"的启动.而在下一篇中,我会讨论既有服务器端又有客户端的木马程序与"病毒"相结合的分析与防范.

反病毒攻防研究第005篇:添加节区实现代码的植入

一.前言 上一篇文章所讨论的利用缝隙实现代码的植入有一个很大的问题,就是我们想要植入的代码的长度不能够比缝隙大,否则需要把自身的代码截成几个部分,再分别插入不同的缝隙中.而这次所讨论的方法是增加一个节区,这个节区完全可以达到私人订制的效果,其大小完全由我们自己来决定,这样的话,即便是代码较长,也不用担心.而这种方式最大的缺陷就是不利于恶意代码自身的隐藏,因此在现实中可能并不常用.其实,我在这里讨论节区的添加,是为了以后更加深入的讨论打下基础,因为在加壳以及免杀技术中,经常会对PE文件添加节区.这

反病毒攻防研究第008篇:简单木马分析与防范part2

一.前言 一般来说,木马是既有客户端也有服务器端的.上次讨论的不过是一种特殊情况,毕竟不是人人都懂得DOS命令,因此现在木马的客户端也都是做成非常直观的界面形式,方便操作.本篇文章会从客户端与服务器端两个方面进行讨论,与上次的讨论不同的是,这次我会直接把用来模拟病毒的对话框程序放入服务器端,这样只要成功连接,那么就可以通过由客户端所发出的命令来让服务器端直接执行对话框程序.用这种思想,可以给服务器端增加很多功能,但是在这里仅仅讨论对话框的打开. 二.服务器端的实现 这里所讨论的木马依旧是命令行下

网络攻防研究第001篇:尝试暴力破解某高校研究生管理系统学生密码

前言 如果你是在校大学生,而且还对网络攻防比较感兴趣的话,相信你最开始尝试渗透的莫过于所在院校的学生管理系统.因为一般来说这样的系统往往比较薄弱,拿来练手那是再合适不过的了.作为本系列的第一篇文章,我将会利用暴力破解的方式,尝试对某高校的研究生管理系统的学生密码进行破解.由于这个管理系统的网站属于该高校的内网资源,外网是无法访问的,因此大家就不要尝试按照文中的内容来对文中出现的网址进行访问了.利用本文所论述的暴力破解思想,可以帮助大家更好地认识我们的网络,也有助于了解目标网站是否安全.那么在这里