安全类工具制作第007篇:行为监控工具的开发

一、前言

现今的杀毒软件都会带有“行为监控”的功能。该功能可以在可疑进程被创建时,或者注册表敏感位置被写入时等情况下,给予用户以提示,让用户来选择是否对相应的可疑操作进行拦截,从而达到主动防御的目的。这样就可以避免传统杀软依靠特征码查杀病毒的滞后性。行为监控工具可以在恶意程序还未正式产生危害时,就将其扼杀。

在制作病毒专杀工具的时候,往往也会使用行为监控工具。比如由微软推出的大名鼎鼎的Process Monitor就是这类工具的典范。通过监控在虚拟机中运行的病毒的种种行为,来采取相应的对策,从而编写出专杀工具(基于真实病毒的专杀工具制作的整套流程,我会在本系列以后的文章中详细讲述)。而我这次所编写的就是能够实现类似于Process Monitor的监控程序。当然,我这里更主要的是程序原理的论述,文章中实际编写出来的程序自然无法与Process
Monitor相提并论(因为Process Monitor一方面应该是钩取了一些我们并不常见的函数,另一方面它应该是运用了内核级的一些技术,这些会在我未来的文章中进行讨论)。当然我相信读者会对其不断完善,从而私人订制出一套自己的安全工具。

二、行为监控的原理

任何程序功能的实现,必然需要调用相应的API函数,因此,想要监测程序的行为,就需要对其调用的API函数进行钩取并分析。我之前的文章中主要讨论过两类HOOK技术,一个是Inline HOOK(详见《反病毒攻防研究第012篇:利用Inline HOOK实现主动防御》),另一个是IAT
HOOK技术(详见《逆向工程第004篇:令计算器程序显示汉字(下)》)。通过这两种HOOK技术,就能够对程序中的API函数实现拦截的操作。行为监控的原理就是对敏感API函数进行钩取,之后可以由用户选择是否进行拦截,这样就能够预防恶意程序损害我们的系统。

具体到本程序,为了讨论的简单起见,我这里仅仅钩取用于进程创建的CreateProcess()这个API函数(大家可以依据本章所讲述的原理自行丰富程序的功能)。由于使用了HOOK,那么就必然要涉及DLL的编写。而为了能在所有基于消息的进程中注入我们的DLL,就必须使用Windows钩子。

需要说明的是,我在《反病毒攻防研究第012篇:利用InlineHOOK实现主动防御》中所讲述的方法仅仅针对于一个程序进行防御,但是这次我打算针对系统中的所有CreateProcess()函数进行钩取,这就需要使用Windows下全局钩取的方法。Windows提供的钩子非常多,其中有一种类型的钩子非常实用,那就是WH_GETMESSAGE这个类型的钩子。它可以很方便地将DLL文件注入到所有基于消息机制的程序中,从而实现DLL所编写的功能。

经过上述分析就已经明确了,本程序的实现,需要编写一个DLL程序用于注入,再编写一个EXE程序用于前台的控制。

三、DLL程序的编写

这里需要创建一个简单的Win32Dynamic Link Library项目,并把之前所编写的“CInlineHOOK.h”和“CInlineHOOK.cpp”加入该项目中(详见《反病毒攻防研究第012篇:利用Inline HOOK实现主动防御》),然后添加如下代码:

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

#include "stdafx.h"
#include "InlineHOOK.h"
#include <stdio.h>

#pragma data_seg(".shared")
HHOOK g_hHook = NULL;
HWND  g_ExeHwnd = NULL;
#pragma data_seg()

#pragma comment (linker, ".shared, RWS")

extern "C" __declspec(dllexport) void SetHookOn(HWND hWnd);
extern "C" __declspec(dllexport) void SetHookOff();

CInlineHOOK CreateProcessWHook;

HINSTANCE g_hInst = NULL;
//定义一些常量用来标识类型,可依据程序的完善而增加这部分
#define PM_CREATEPROCESS  0x00000001L
//定义一个结构体,将该结构体的信息发送给用于加载DLL的EXE文件,并让EXE给出提示
typedef struct _PM_INFO
{
    WCHAR wProcessName[0x200];
    DWORD dwPMClass;
}PM_INFO, *PPM_INFO;

BOOL
WINAPI
MyCreateProcessW(
               __in_opt    LPCWSTR lpApplicationName,
               __inout_opt LPWSTR lpCommandLine,
               __in_opt    LPSECURITY_ATTRIBUTES lpProcessAttributes,
               __in_opt    LPSECURITY_ATTRIBUTES lpThreadAttributes,
               __in        BOOL bInheritHandles,
               __in        DWORD dwCreationFlags,
               __in_opt    LPVOID lpEnvironment,
               __in_opt    LPCWSTR lpCurrentDirectory,
               __in        LPSTARTUPINFOW lpStartupInfo,
               __out       LPPROCESS_INFORMATION lpProcessInformation
    )
{
    PM_INFO sz = { 0 };
    if ( wcslen(lpCommandLine) != 0 )
    {
        wcscpy(sz.wProcessName, lpCommandLine);
    }
    else
    {
        wcscpy(sz.wProcessName, lpApplicationName);
    }

    sz.dwPMClass = PM_CREATEPROCESS;

    COPYDATASTRUCT cds = { NULL, sizeof(PM_INFO), (void *)&sz };
    BOOL bRet = FALSE;
	// 注意FindWindow的第二个参数应填写程序主窗口的"Caption"
	// 这里的SendMessage()函数用来发送一个WM_COPYDATA消息,将结构体
	// 传给了加载DLL的EXE程序,使EXE程序把相应的信息显示给用户。
    if ( SendMessage(FindWindow(NULL, "EasyPM"), WM_COPYDATA, GetCurrentProcessId(), (LPARAM)&cds) != -1 )
    {
        CreateProcessWHook.UnHOOK();
        bRet = CreateProcessW(lpApplicationName, lpCommandLine,
                    lpProcessAttributes, lpThreadAttributes,
                    bInheritHandles, dwCreationFlags,
                    lpEnvironment, lpCurrentDirectory,
                    lpStartupInfo, lpProcessInformation);
        CreateProcessWHook.ReHOOK();
    }

    return bRet;
}

BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
    switch ( ul_reason_for_call )
    {
    case DLL_PROCESS_ATTACH:
        {
            g_hInst = (HINSTANCE)hModule;
            CreateProcessWHook.HOOK("kernel32.dll", "CreateProcessW", (PROC)MyCreateProcessW);
            break;
        }
    case DLL_PROCESS_DETACH:
        {
            CreateProcessWHook.UnHOOK();
            if ( g_hHook != NULL )
            {
                SetHookOff();
            }
            break;
        }
    }

    return TRUE;
}

// 钩子函数
LRESULT CALLBACK GetMsgProc(
                            int code,       // hook code
                            WPARAM wParam,  // removal option
                            LPARAM lParam   // message
                            )
{
    return CallNextHookEx(g_hHook, code, wParam, lParam);
}

void SetHookOn(HWND hWnd)
{
    g_ExeHwnd = hWnd;
    // 安装WH_GETMESSAGE钩子用于监视被投递到消息队列中的消息
	SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hInst, 0);
}

void SetHookOff()
{
    // 卸载钩子
	UnhookWindowsHookEx(g_hHook);
    g_hHook = NULL;
}

以上就是全部DLL程序的代码,并不复杂,我已经添加了相应的注释,大家可以在此基础上添加其它API函数的代码,以便更加全面地监控我们的系统。

四、程序界面的制作

本程序需要一个“List Control”和三个“Button”控件:

图1 程序界面的制作

然后为“List Control”控件添加一个名为“m_PMReports”的变量,为“启动”和“停止”按钮分别添加名为“m_BtnOn”和“m_BtnOff”的变量。并添加如下初始化代码:

typedef void (*SETHOOKON)(HWND);
typedef void (*SETHOOKOFF)();

void CEasyPMDlg::InitPMReports()
{
    m_PMReports.SetExtendedStyle(m_PMReports.GetExtendedStyle()
                            | LVS_EX_GRIDLINES);

    m_PMReports.InsertColumn(0, "序号");
    m_PMReports.InsertColumn(1, "时间");
    m_PMReports.InsertColumn(2, "进程");
    m_PMReports.InsertColumn(3, "类型");

    m_PMReports.SetColumnWidth(0, 40);
    m_PMReports.SetColumnWidth(1, 160);
    m_PMReports.SetColumnWidth(2, 200);
    m_PMReports.SetColumnWidth(3, 200);
}

void CEasyPMDlg::OnBtnOn()
{
    // TODO: Add your control notification handler code here
    m_hInst = LoadLibrary("EasyPM.dll");
    SETHOOKON SetHookOn = (SETHOOKON)GetProcAddress(m_hInst, "SetHookOn");

    SetHookOn(GetSafeHwnd());
    FreeLibrary(m_hInst);
    m_BtnOn.EnableWindow(FALSE);
    m_BtnOff.EnableWindow(TRUE);
}

void CEasyPMDlg::OnBtnOff()
{
    // TODO: Add your control notification handler code here
    m_hInst = GetModuleHandle("EasyPM.dll");
    SETHOOKOFF SetHookOff = (SETHOOKOFF)GetProcAddress(m_hInst, "SetHookOff");
    SetHookOff();
    CloseHandle(m_hInst);
    FreeLibrary(m_hInst);
    m_BtnOn.EnableWindow(TRUE);
    m_BtnOff.EnableWindow(FALSE);
}

void CEasyPMDlg::OnBtnClear()
{
    // TODO: Add your control notification handler code here
    m_PMReports.DeleteAllItems();
}

最后需要在相应的位置进行声明,这里不再赘述。

五、主程序的编写

主程序其实就是主要用于接收DLL的消息,代码如下:

#define PM_CREATEPROCESS  0x00000001L

typedef struct _PM_INFO
{
    WCHAR wProcessName[0x200];
    DWORD dwPMClass;
}PM_INFO, *PPM_INFO;

BOOL CEasyPMDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
    // TODO: Add your message handler code here and/or call default
    WCHAR sz[2048];
    PPM_INFO pPMInfo = (PPM_INFO)pCopyDataStruct->lpData;
    wcscpy(sz,pPMInfo->wProcessName);

    int nNum = m_PMReports.GetItemCount();
    CString Str;
    //显示序号
    Str.Format("%d", nNum);
    m_PMReports.InsertItem(nNum, Str);
    //获取系统时间
    SYSTEMTIME StTime;
    GetLocalTime(&StTime);
    Str.Format("%04d/%02d/%02d %02d:%02d:%02d",
            StTime.wYear,
            StTime.wMonth,
            StTime.wDay,
            StTime.wHour,
            StTime.wMonth,
            StTime.wSecond);
    //显示时间
    m_PMReports.SetItemText(nNum, 1, Str);
    //显示进程名称,注意由于这里是宽字符,所以要用大写的S
    Str.Format("%S", sz);
    m_PMReports.SetItemText(nNum, 2, Str);
    //显示类型,可以依据钩取函数的增加而完善case语句
    switch ( pPMInfo->dwPMClass )
    {
    case PM_CREATEPROCESS:
        {
            Str = "进程创建(CreateProcess)";
            break;
        }
    }
    m_PMReports.SetItemText(nNum, 3, Str);

    return CDialog::OnCopyData(pWnd, pCopyDataStruct);
}

这部分代码就是对WM_COPYDATA消息的一个响应,这些代码基本就是对界面进行了操作。因为使用的消息映射,所以还需在相应的位置进行声明,首先是在BEGIN_MESSAGE_MAP(CEasyPMDlg, CDialog)下添加:

ON_WM_COPYDATA()

最后在头文件“EasyPMDlg.h”中的“//{{AFX_MSG(CEasyPMDlg)”下添加:

afx_msg BOOL OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct);

以上就是全部代码,完成后就可以编译链接了,运行时不要忘了将之前编写的“EasyPM.dll”放在和本EXE程序同一目录下。

六、程序的测试

这里我先运行本程序,单击界面中的“启动”按钮,然后依次打开“记事本”、“画图”和“计算器”程序,如下图所示:

图2 监控启动的程序

可见,我们的程序已经成功监控了所有打开的程序。这里需要说明的是,第4个程序是我用于截图的QQ截图工具。

七、小结

我们的代码并不能监控到所有的进程,而且我们的程序也过于简单,很容易被恶意程序所突破。不过这个程序仅仅是一个雏形,我在此希望能够起到抛砖引玉的作用。本程序还可以加入进程拦截的功能,有兴趣的读者可以自行完善。

时间: 2024-12-19 06:27:11

安全类工具制作第007篇:行为监控工具的开发的相关文章

安全类工具制作第001篇:制作AutoRun.inf免疫程序

一.前言 我曾经在<反病毒攻防研究第006篇:利用WinRAR与AutoRun.inf实现自启动>那篇文章中讨论过AutoRun.inf的原理与应对方法,作为对这个问题的收尾,我打算在此讨论一下如何利用MFC开发一个简单的免疫程序.尽管如今我们应该已经不会再受到AutoRun.inf的肆虐,但是这其中包含的很多思想依旧值得探讨.需要强调的是,这篇文章的重点在于预防,也就是在还没有受到AutoRun.inf的威胁时该如何给自己的计算机打预防针.如果计算机中了AutoRun.inf的破坏,可参考反

安全类工具制作第006篇:服务管理器

一.前言 服务是一种在操作系统启动的时候就会启动的进程.在操作系统启动时会有两种程序随着系统启动,一种是普通的Win32程序,另一种则是驱动程序.正是基于服务的这种特性,恶意程序往往也会将自身伪装成正常的服务来实现自启动.因此在反病毒的过程中,还是很有必要对服务项进行查看并管理的. 服务管理器的开发原理与之前所讨论的注册表管理器和进程管理器是类似的,主要也是枚举服务并将其显示在"List Control"控件中.而对于服务的管理,是通过服务相关的API函数来实现的.有了本系列之前几篇文

安全类工具制作第005篇:进程管理器(下)

一.前言 这次的程序是为了完好上一次所编写的进程管理器.使得当我们选中某一个进程的时候.能够查看其DLL文件,而且能够对可疑的模块进行卸载操作.这样就能够有效对抗DLL的恶意注入. 二.界面制作 这个界面是要依托于上一篇文章中制作的界面,须要单击上次界面中的"查看DLL"button来启动. 在上次的工作区中,找到VC6中菜单条的"Insert"选项.在其下拉菜单中选择"Resource-".在弹出的界面中选择"Dialog"

安全类工具制作第002篇:U盘防火墙的制作

一.前言 我在上一篇文章中讨论了如何制作AutoRun.inf免疫程序,虽然这个免疫程序可以对所有的盘符有效,但是其实主要还是针对于U盘来进行防护的.由于目前新版的操作系统已经基本不支持AutoRun.inf,因此一般来说我们无需特别地关注这个问题.作为对U盘防御研究的收尾,这次我所讨论的是制作一个U盘防火墙.通过这个防火墙,当检测到有U盘插入时,则会产生提示,并且自动检查U盘中是否有AutoRun.inf文件,进行解析再进行删除操作,通过这个防火墙就可以安全地打开U盘. 其实现在很多杀毒软件都

安全类工具制作第004篇:进程管理器(上)

一.前言 进程是计算机中执行的程序,是向操作系统申请资源的基本单位.我们执行一个程序.那么就会对应地创建一个甚至多个进程,关闭程序时.进程也就结束了.查看进程最经常使用的手段是按下Ctrl+Shift+Delete打开Windows自带的任务管理器,或者使用老牌强力软件"冰刃".又或者是使用由微软推出的更为强大的Process Monitor,都能基本得到同样的效果.不同的是.强大的进程查看软件能够查看到系统的隐藏进程,而一般的仅仅能查看应用层的进程.而我在这两篇文章中所讨论的就是怎样

使用MBROSTool 工具制作本地硬盘F3救急模式的方法总结

前面写了一篇使用MBROSTool 工具制作本地硬盘多启动盘的方法总结.里面就是可以把一些系统安装到硬盘上面方便使用,比如安装PE到硬盘,不过启动的时候会先进入多UDm菜单,然后选择[启动本地系统]后才会进入本地的系统, 有的人不喜欢这种,或者只希望用PE,那么可以使用F3救急模式,也就是默认就没有变化,开机直接进入本地系统,只有按下F3的时候才进入PE系统,我之前也网上找过很多方法,不过都很麻烦,也很危险,曾经误操作导致分区表错误,丢失掉硬盘上的所有数据,下面就来用MBT实现这个功能. 首先F

制作U-boot ---AT91SAM9260篇

制作U-boot ---AT91SAM9260篇 0.制作交叉编译链请看<ARM交叉编译工具制作V2> ---------------------------------------------------转载请注明:xiaobin_hlj80---- 1.烧写boot-uboot 查看AT91SAM9260芯片文档,关于内存部分 P19– 6221s.pdf(http://www.atmel.com/zh/cn/devices/SAM9260.aspx?tab=documents) 起始地址

使用OpenSSL工具制作X.509证书的方法及其注意事项总结

如何使用OpenSSL工具生成根证书与应用证书 本文由CSDN-蚍蜉撼青松 [主页:http://blog.csdn.net/howeverpf]原创,转载请注明出处! 一.步骤简记 // 生成顶级CA的公钥证书和私钥文件,有效期10年(RSA 1024bits,默认) openssl req -new -x509 -days 3650 -keyout CARoot1024.key -out CARoot1024.crt // 为顶级CA的私钥文件去除保护口令 openssl rsa -in C

ArcMap自定义脚本工具制作

原文 ArcMap自定义脚本工具制作 在制图的前期,一般需要做一些数据的整理,如图层合并.裁剪等工作.虽然在ArcMap中也有提供对应的工具,但使用起来需要点技巧.如批量裁剪,虽然可以实现,但出来的结果会重命名为[图层名]_clip,这样对于配置好整个区域的mxd,需要单独裁剪部分范围数据非常不友好,以为后续工作意味着需要单独给裁剪出来的数据重命名,删除掉后缀_clip才可以统一设置数据源. 解决这个问题其实可以很快速的编写python脚本去实现.但脚本对于一般人来说比较麻烦,每次使用都需要调整