驱动层与应用层的事件同步(主动防御原理浅析)

来源:互联网 发布:扎克伯格老婆知乎 编辑:程序博客网 时间:2024/05/21 11:30

在电脑上,大家都装有各种各样的防护软件,也习惯了防护软件弹出的各种提示框, 很多人会好奇,这些软件是如何在危险程序运行前就拦截到关键行为,并弹出提示框的呢?今天,我在这里简单讲解一下实现的原理,即驱动层与应用层的事件同步。下面切入正题。


要实现驱动层与应用层的事件同步, 主要经历下几个步骤:

1、在应用层使用CreateEvent创建事件(自动重置类型或手动重置类型);

2、将事件句柄发送到驱动程序中。

3、应用层使用WaitForSingleObject或WaitForMultipleObjects等待事件。

4、驱动层拦截到关键事件,使用KeSetEvent将事件设置为有信号。

5、应用层等待到事件信号,向驱动层发送控制消息,获取信息。

6、弹出消息提示用户。

7、设置事件为无信号,等待下一次通知。


下面,我用一个监测系统进程创建的简单例子, 简单说明一下实现的步骤。


首先,先从应用层开始。

打开VisualStudio, 创建一个基于对话框的MFC应用程序, 在界面上添加两个按钮,如图:




添加一个全局的事件对象句柄:

HANDLE g_hKernelEvent = NULL;


在对话框的OnInitDialog()方法中,创建手动重置的事件对象。

//创建手动重置的事件g_hKernelEvent = CreateEvent(NULL, TRUE, FALSE, NULL);


为启动按钮添加处理代码:

void CKernelHanleDemoDlg::OnBnClickedBtnStart(){    DWORD dwRet;    //将创建的事件句柄传入驱动程序    BOOL bRet = IOControl(IOCTL_START, &g_hKernelEvent, sizeof(g_hKernelEvent), NULL, 1024, &dwRet);    //正在运行    g_bIsRunning = TRUE;    //创建监听线程    HANDLE hThread = CreateThread(NULL, 0, KernelHandleThread, NULL, 0, NULL);    CloseHandle(hThread);    Sleep(1);}

为停止按钮添加处理代码:

void CKernelHanleDemoDlg::OnBnClickedBtnStop(){    g_bIsRunning = FALSE;    DWORD dwRet;    BOOL bRet = IOControl(IOCTL_STOP, NULL, 0, NULL, NULL, &dwRet);}

启动和停止按钮的功能,就是向驱动发送控制代码,实现对事件通知的控制。

代码中的IOControl函数和KernelHandleThread线程函数, 如下:

BOOL IOControl(DWORD Ctl_code, LPVOID InputBuffer, DWORD nInBufferSize, LPVOID OutputBuffer, DWORD nOutBufferSize, LPDWORD dwRet){    BOOL bRet = FALSE;    //打开驱动的符号链接    HANDLE hDevice = CreateFile(L"\\\\.\\KernelHandle",GENERIC_READ|GENERIC_WRITE,0,        NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);    if (INVALID_HANDLE_VALUE == hDevice)    {        MessageBox(NULL,L"Failed To Open Device!",NULL,MB_OK);        return FALSE;    }    bRet = DeviceIoControl(hDevice,Ctl_code,InputBuffer,nInBufferSize,OutputBuffer,nOutBufferSize,dwRet,NULL);    CloseHandle(hDevice);    return bRet;}//监测线程DWORD WINAPI KernelHandleThread(LPVOID lpParameter){    DWORD dwRet;    while(g_bIsRunning)    {        //等待同步事件信号        WaitForSingleObject(g_hKernelEvent, INFINITE);        char szBuffer[20] = {0};        WCHAR szMsgBuffer[100] = {0};        int nProcessId = 0;        //等待完成,向驱动发送请求,获取PID        IOControl(IOCTL_GET_DATA, NULL, 0, szBuffer, 20, &dwRet);        //转换成PID        nProcessId = atoi(szBuffer);        wsprintf(szMsgBuffer, L"有新进程创建, 进程ID:%d", nProcessId);        //弹出消息        MessageBox(NULL,szMsgBuffer, L"驱动消息", MB_OK|MB_ICONINFORMATION);        //设置同步事件为无信号,等待下一次通知        ResetEvent(g_hKernelEvent);    }    return 0;}

至此,应用层主要做的工作已经完成,即:传入事件句柄, 等待驱动通知。


下面,进行驱动层的代码部分。

驱动部分,我使用WDK 7600.16385.1 + VisualDDK开发, 请各位看官按照自己的开发环境进行改动。


首先, 添加两个全局变量, 用于保存事件句柄信息:

//同步事件对象PRKEVENT g_pEventObject = NULL;//句柄信息OBJECT_HANDLE_INFORMATION g_ObjectHandleInfo;


在DriverEntry中, 添加驱动控制分发函数:

DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KernelHandleDefaultHandler;

然后添加驱动控制的处理代码:

NTSTATUS KernelHandleDefaultHandler(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){    NTSTATUS status = STATUS_SUCCESS;    ULONG ulReturn = 0;    PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);    //驱动控制代码    ULONG ulCtrlCode = stack->Parameters.DeviceIoControl.IoControlCode;    //输入输出缓冲区    PVOID InputBuffer = (PVOID)Irp->AssociatedIrp.SystemBuffer;    PVOID OutputBuffer = (PVOID)Irp->AssociatedIrp.SystemBuffer;    //输入输出缓冲区大小    ULONG ulInputBufferSize = stack->Parameters.DeviceIoControl.InputBufferLength;    ULONG ulOutputBufferSize = stack->Parameters.DeviceIoControl.OutputBufferLength;    switch(ulCtrlCode)    {    case IOCTL_START:        {            //设置同步事件            if (InputBuffer == NULL || ulInputBufferSize < sizeof(HANDLE))            {                KdPrint(("Set Event Error~!\n"));                break;            }            //取得句柄对象            HANDLE hEvent = *(HANDLE*)InputBuffer;            status = ObReferenceObjectByHandle(hEvent, GENERIC_ALL, NULL, KernelMode, (PVOID*)&g_pEventObject, &g_ObjectHandleInfo);            KdPrint(("g_pEventObject = 0x%X\n", g_pEventObject));            //设置进程创建通知函数            if (!g_bIsNotifyRoutineSetted)            {                PsSetCreateProcessNotifyRoutine(CreateProcessNotifyFunction, FALSE);                g_bIsNotifyRoutineSetted = TRUE;            }                        break;        }    case IOCTL_STOP:        {            if (g_bIsNotifyRoutineSetted)            {                //移除进程创建通知函数                PsSetCreateProcessNotifyRoutine(CreateProcessNotifyFunction, TRUE);                g_bIsNotifyRoutineSetted = FALSE;            }             //释放对象引用            if (g_pEventObject != NULL)            {                ObDereferenceObject(g_pEventObject);                g_pEventObject = NULL;            }            break;        }    case IOCTL_GET_DATA:        {            int nLength = strlen(g_szPIDInfo);            if (OutputBuffer == NULL && ulOutputBufferSize < nLength)            {                KdPrint(("OutputBufferSize is too small ~!\n"));                break;            }                        //复制进程PID到输出缓冲区            RtlCopyBytes((PCHAR)OutputBuffer, g_szPIDInfo, nLength+1);            break;        }    default:        break;    }Irp->IoStatus.Status = STATUS_SUCCESS;Irp->IoStatus.Information = ulOutputBufferSize;IoCompleteRequest(Irp, IO_NO_INCREMENT);return Irp->IoStatus.Status;}

在驱动中,拦截进程创建, 一般使用Hook NtCreateProcess来实现,但在我们的这个例子里,只需要获取进程创建的进程ID, 那么,有一个更加简单的方法,即使用PsSetCreateProcessNotifyRoutine函数, 可以方便的获取进程的创建信息。

函数原型如下:

NTSTATUS PsSetCreateProcessNotifyRoutine(  _In_  PCREATE_PROCESS_NOTIFY_ROUTINE NotifyRoutine,  _In_  BOOLEAN Remove);


其中,NotifyRoutine指定了处理进程创建通知的函数, Remove为False时,代表添加通知函数, 为TRUE时,代表移除通知函数。

NotifyRoutine是一个函数指针,原型为:

VOID(*PCREATE_PROCESS_NOTIFY_ROUTINE) (    IN HANDLE  ParentId,    IN HANDLE  ProcessId,    IN BOOLEAN  Create    );

无需解释过多,Create为True时,代表进程创建,为False时,代表进程退出,因此,我们的处理函数,就写成如下面这样:

//PID信息CHAR g_szPIDInfo[20];VOID CreateProcessNotifyFunction(IN HANDLE  hParentId, IN HANDLE  hProcessId, IN BOOLEAN  bCreate ){    //如果是进程创建    if (bCreate)    {        //格式化字符串        RtlZeroMemory(g_szPIDInfo, 20);        RtlStringCchPrintfA(g_szPIDInfo, 20, "%d", (int)hProcessId);        //设置事件为有信号,通知应用层        KeSetEvent(g_pEventObject, 0, FALSE);    }}

这样, 当有进程创建时, 就会将创建的进程的PID发送到缓冲区,应用层接收事件信号后,就会读取缓冲区中的PID内容,将弹出消息提示了, 如图:



文章中的代码, 讲解了主要的代码部分,省略了部分代码, 完整的代码,我已上传到网盘。

链接: http://pan.baidu.com/s/1bn9zxHP 密码: 9t7j

解压密码:000000


注:

本文代码的开发环境为:

Visual Studio 2010 + WDK 7600.16385.1 + VisualDDK,  编译成功后,请手动加载驱动。


本帖为原创,转帖请说明出处,谢谢合作。

本帖地址:http://blog.csdn.net/sonsie007/article/details/38356961



0 0
原创粉丝点击