Hook与IoCallDriver与IofCallDriver

来源:互联网 发布:js 超出显示省略号 编辑:程序博客网 时间:2024/04/27 20:50

用途之一:从内核层保护文件不被删除


IoCallDriver:一个内核例程,过滤所有的系统请求

NTSTATUS IoCallDriver(IN PDEVICE_OBJECT  DeviceObject,    IN OUT PIRP  Irp    );

IofCallDriver:在IoCallDriver中调用的一个例程,几乎所有的内核驱动都调用了IofCallDriver

NTSTATUS FASTCALL IofCallDriver(IN PDEVICE_OBJECT DeviceObject,    IN OUT PIRP Irp){if(pIofCallDriver != NULL) {// This routine will either jump immediately to IovCallDriver or// IoPerfCallDriver.return pIofCallDriver(DeviceObject, Irp, _ReturnAddress());}return IopfCallDriver(DeviceObject, Irp);}

几乎所有的系统调用都是如此,进入之后,立刻出现一个jmp,跳到真实的调用处,这就形成了一个系统调用跳转表,只要修改这个地址,就能构成一个hook,现在安全软件能够很容易的检测到它。


实现hook的代码:

// 首先定义一个函数指针类型typdef NTSTATUS FASTCALL(*PMY_IOFCALLDRIVER_FP)(IN PDEVICE_OBJECT,IN OUT PIRP);// 以下函数用函数newIofCallDriver去代替现有的IofCallDriver,同时返回旧的IofCallDriver所跳转的实际地址,如果失败,则返回空PMY_IOFCALLDRIVER_FP MyHookIofCallDriverXP(IN PMY_IOFCALLDRIVER_FP newIofCallDriver,       IN BOOLEAN hookOrUnhook){UNICODE_STRING functionName;PBYTE address;Static PMY_IOFCALLDRIVER_FP oldIofCallDriverBody = NULL;RtlInitUnicodeString(&functionName,L"IofCallDriver");// 得到IofCallDriver的入口地址Address = MmGetSysemRoutineAddress (&functionName);If(hookOrUnhook){// 获得入口地址后,加2字节的地址就是旧的,IofCallDriver 的执行体的地址oldIofCallDriverBody = ( PMY_IOFCALLDRIVER_FP )( *(PLONG)(address + 2);InterlockedExchange((PLONG) (address +2), newIoCallDriver);return oldIofCallDriverBody;}else{// 复原if(oldIofCallDriverBody == NULL){InterlockedExchange ((PLONG)(address + 2),oldIofCallDriverBody);return oldIofCallDriverBody;}}}


"缓冲"方法(METHOD_BUFFERED)
备注:在下面的讨论中,"输入"表示数据从用户模式的应用程序到驱动程序,"输出"表示数据从驱动程序到应用程序。

对于读取请求,I/O 管理器分配一个与用户模式的缓冲区大小相同的系统缓冲区。IRP 中的 SystemBuffer 字段包含系统地址。UserBuffer 字段包含初始的用户缓冲区地址。当完成请求时,I/O 管理器将驱动程序已经提供的数据从系统缓冲区复制到用户缓冲区。对于写入请求,会分配一个系统缓冲区并将 SystemBuffer 设置为地址。用户缓冲区的内容会被复制到系统缓冲区,但是不设置 UserBuffer。对于 IOCTL 请求,会分配一个容量大小足以包含输入缓冲区或输出缓冲区的系统缓冲区,并将 SystemBuffer 设置为分配的缓冲区地址。输入缓冲区中的数据复制到系统缓冲区。UserBuffer 字段设置为用户模式输出缓冲区地址。内核模式驱动程序应当只使用系统缓冲区,且不应使用 UserBuffer 中存储的地址。

对于 IOCTL,驱动程序应当从系统缓冲区获取输入并将输出写入到系统缓冲区。当完成请求时,I/O 系统将输出数据从系统缓冲区复制到用户缓冲区。

"直接"方法(METHOD_IN/OUT_DIRECT)
对于读取和写入请求,用户模式缓冲区会被锁定,并且会创建一个内存描述符列表 (MDL)。MDL 地址会存储在 IRP 的 MdlAddress 字段中。SystemBuffer 和 UserBuffer 均没有任何含义。但是,驱动程序不应当更改这些字段的值。

对于 IOCTL 请求,如果在 METHOD_IN_DIRECT 和 METHOD_OUT_DIRECT 中同时有一个输出缓冲区,则分配一个系统缓冲区(SystemBuffer 又有了地址)并将输入数据复制到其中。如果有一个输出缓冲区,且它被锁定,则会创建 MDL 并设置 MdlAddress。UserBuffer 字段没有任何含义。

"两者都不"方法(METHOD_NEITHER)
对于读取和写入请求,UserBuffer 字段被设置为指向初始的用户缓冲区。不执行任何其他操作。SystemAddress 和 MdlAddress 没有任何含义。对于 IOCTL 请求,I/O 管理器将 UserBuffer 设置为初始的用户输出缓冲区,而且,它将当前 I/O 栈位置的 Parameters.DeviceIoControl.Type3InputBuffer 设置为用户输入缓冲区。利用该 I/O 方法,由驱动程序来确定如何处理缓冲区:分配系统缓冲区或创建 MDL。

通常,驱动程序在访问用户数据时不应当将 UserBuffer 字段用作地址,即使当用户缓冲区被锁定时也是如此。这是由于在调用驱动程序时,在系统中可能看不到调用用户的地址空间。(对于该规则的一个例外是,在最高层驱动程序将 IRP 向下传递到较低层的驱动程序之前,它可能需要使用 UserBuffer 来复制数据。)如果使用"直接"或"两者都不"方法,在创建 MDL 之后,驱动程序可以使用 MmGetSystemAddressForMdl 函数来获取有效的系统地址以访问用户缓冲区。


在驱动层,依传输类型的不同,输入缓冲区的位置亦不同,见下表:
传输类型                               位置
METHOD_IN_DIRECT                irp->AssociatedIrp.SystemBuffer
METHOD_OUT_DIRECT             irp->AssociatedIrp.SystemBuffer
METHOD_BUFFERED                 irp->AssociatedIrp.SystemBuffer
METHOD_NEITHER                   irpStack->Parameters.DeviceIoControl.Type3InputBuffer

在驱动层,依传输类型的不同,输出缓冲区的位置亦不同,见下表:
传输类型                              位置
METHOD_IN_DIRECT                irp->MdlAddress
METHOD_OUT_DIRECT             irp->MdlAddress
METHOD_BUFFERED                 irp->AssociatedIrp.SystemBuffer
METHOD_NEITHER                    irp->UserBuffer

所以只要确定了传输方式后,就可以根据各自的位置来读取和写入数据,从而实现应用层和驱动的通信。

原创粉丝点击