分享

内核编程之SSDTHook(3)Hook NtCreateSection监控所有可执行模块加载

 黑犬黑犬_924 2017-11-21

在上两篇博文中,我介绍了SSDTHook的原理,并给出了一个实例--通过Hook NtOpenProcess来实现进程保护

这次我们玩个更好玩的,拦截所有可执行模块的载人!杀软都有这种功能,当一个程序运行时,他能得到具体路径,检查是否安全后,还可以通知用户询问是否允许运行。

这么有趣的功能,要如何实现呢,其实出了hook也有办法,但是我们现在研究的是hook,就hook ssdt吧,那么问题来了,要hook那个系统服务函数呢?在用户模式创建进程,大家一下就会想到CreateProcess,然后想到CreateProcessAsUser等,正好SSDT里有一个NtCreateProcess,很多人想当然就想到要hook这个(其实我当时也是),其实我们有一个捷径,我们可以hook这个函数:NtCreateSection(NT 5.x,2000-XP)或NtCreateUserProcess(NT 6.x,NT 10.x,Vista-W10)当一个可执行模块载人时,系统会先调用NtCreateFile来打开文件,创建文件对象,然后调用NtCreateSection,再调用NtCreateProcess,然后再完成一些其他工作,比如创建线程,通知Win32子系统,因此我们可以Hook这个系统服务函数来拦截可执行模块的载人,但在Vista以后,系统不会再调用NtCreateSection,而是整个过程统统交给了NtCreateUserProcess来处理,但是这不以为着在Vista+对NtCreateSection做hook一点价值都没有,其实,DLL的载人内存也需要经过这一个过程,因此我们可以通过对这个函数做hook实现对DLL载人的监控和拦截,再退一步说,如果我们想在XP上监控和拦截进程的创建,就要hook NtCreateSection。

我们首先来看看NtCreateSection的原型:

  1. NTSTATUS ZwCreateSection(  

  2.   _Out_    PHANDLE            SectionHandle,  

  3.   _In_     ACCESS_MASK        DesiredAccess,  

  4.   _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,  

  5.   _In_opt_ PLARGE_INTEGER     MaximumSize,  

  6.   _In_     ULONG              SectionPageProtection,  

  7.   _In_     ULONG              AllocationAttributes,  

  8.   _In_opt_ HANDLE             FileHandle  

  9. );  

Windows加载所有的可执行模块在参数 SectionPageProtection 和 AllocationAttributes 与普通的映射文件有所区别,具有以下特点:

  1. (AllocationAttributes == SEC_IMAGE) && (SectionPageProtection & PAGE_EXECUTE)  

SSDTHook和原理和实现细节都在上两篇博文(http://blog.csdn.net/zuishikonghuan/article/details/50717936http://blog.csdn.net/zuishikonghuan/article/details/50765464)中详细说,我们就详细说说怎么得到要创建的进程的路径吧。

首先我们需要得到文件对象的指针,由于我们在NtCreateSection里可以得到文件句柄,所以可以用ObReferenceObjectByHandle得到文件对象的指针,然后要得到文件路径有两种方法,第一种方法比较复杂:FILE_OBJECT中有一个成员FileName,是不包含卷名称的文件路径,然后,通过FILE_OBJECT里有一个成员DeviceObject,然后通过ObQueryNameString、RtlUnicodeStringToAnsiString、RtlVolumeDeviceToDosName得到卷名称,第二种方法就很简单了,直接调用IoQueryFileDosDeviceName就行了。本例中我们用第二种方法,因此不解释了,各位直接看代码吧。

另外还需要说明的一点是,我们在内核中得到的路径是以“\??\”开头的,这是为何呢?还记得之前驱动开发的博文中创建设备和符号连接吗,磁盘设备上有卷设备,而X盘中的X:就是卷设备的符号连接,符号连接在内核下是以“\??\”开头的,用户模式下是以“\\.\”开头的。

另外,可能还会有人问怎么在得到路径后通知应用程序,以便应用程序检查是否安全以及通知用户呢?可以在应用程序中用一个线程一直读我们创建的设备,驱动派遣函数中阻塞I/O请求(可以用Event,KeWaitForSingleObject等,在后续的驱动开发博文中讲),然后在hook里面激活事件,然后应用程序读取得到路径,线程通信,再次读取,驱动程序再次阻塞I/O请求……和轮询相比,消耗的系统资源很少,而且非常简单。

好了,该说的都已经说了,不该说的也说了,最后上代码,注释也比较丰富,还不明白就看代码吧:

  1. #include <Ntifs.h>  

  2. #include <ntddk.h>  

  3. extern "C" VOID DriverUnload(PDRIVER_OBJECT pDriverObject);  

  4. extern "C" NTSTATUS DefDispatchRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp);  

  5. extern "C" NTSTATUS IoctlDispatchRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp);  

  6. #define IOCTL1 CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)  

  7. #define SEC_IMAGE 0x1000000  

  8. typedef struct _DEVICE_EXTENSION {  

  9.     UNICODE_STRING SymLinkName; //我们定义的设备扩展里只有一个符号链接名成员  

  10. } DEVICE_EXTENSION, *PDEVICE_EXTENSION;  

  11. //我们将 NtCreateSection hook 到自己的函数  

  12. NTSTATUS NTAPI MyNtCreateSection(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,  

  13.     PLARGE_INTEGER MaximumSize, ULONG SectionPageProtection, ULONG AllocationAttributes, HANDLE FileHandle);  

  14. //KeServiceDescriptorTable 中我们感兴趣的结构  

  15. typedef struct _KESERVICE_DESCRIPTOR_TABLE  

  16. {  

  17.     PULONG ServiceTableBase;  

  18.     PULONG ServiceCounterTableBase;  

  19.     ULONG NumberOfServices;  

  20.     PUCHAR ParamTableBase;  

  21. }KESERVICE_DESCRIPTOR_TABLE, *PKESERVICE_DESCRIPTOR_TABLE;  

  22. //ntoskrnl.exe (ntoskrnl.lib) 导出的 KeServiceDescriptorTable  

  23. extern "C" extern PKESERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;  

  24. //关闭页面保护  

  25. void PageProtectClose()  

  26. {  

  27.     __asm{  

  28.         cli;  

  29.         mov eax, cr0;  

  30.         and eax, not 10000h;  

  31.         mov cr0, eax;  

  32.     }  

  33. }  

  34. //启用页面保护  

  35. void PageProtectOpen()  

  36. {  

  37.     __asm{  

  38.         mov eax, cr0;  

  39.         or eax, 10000h;  

  40.         mov cr0, eax;  

  41.         sti;  

  42.     }  

  43. }  

  44. //根据 ZwXXXX的地址 获取服务函数在 SSDT 中所对应的服务的索引号  

  45. #define SYSTEMCALL_INDEX(ServiceFunction) (*(PULONG)((PUCHAR)ServiceFunction + 1))  

  46. ULONG oldNtCreateSection;//之前的NtCreateSection  

  47. ULONG ProtectProcessID = 0;  

  48. #pragma code_seg("INIT")  

  49. extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath)  

  50. {  

  51.     DbgPrint("DriverEntry\r\n");  

  52.     pDriverObject->DriverUnload = DriverUnload;//注册驱动卸载函数  

  53.     //注册派遣函数  

  54.     pDriverObject->MajorFunction[IRP_MJ_CREATE] = DefDispatchRoutine;  

  55.     pDriverObject->MajorFunction[IRP_MJ_CLOSE] = DefDispatchRoutine;  

  56.     pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IoctlDispatchRoutine;  

  57.     NTSTATUS status;  

  58.     PDEVICE_OBJECT pDevObj;  

  59.     PDEVICE_EXTENSION pDevExt;  

  60.     //创建设备名称的字符串  

  61.     UNICODE_STRING devName;  

  62.     RtlInitUnicodeString(&devName, L"\\Device\\MySSDTHookDevice");  

  63.     //创建设备  

  64.     status = IoCreateDevice(pDriverObject, sizeof(DEVICE_EXTENSION), &devName, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevObj);  

  65.     if (!NT_SUCCESS(status))  

  66.         return status;  

  67.     pDevObj->Flags |= DO_BUFFERED_IO;//将设备设置为缓冲设备  

  68.     pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;//得到设备扩展  

  69.     //创建符号链接  

  70.     UNICODE_STRING symLinkName;  

  71.     RtlInitUnicodeString(&symLinkName, L"\\??\\MySSDTHookDevice_link");  

  72.     pDevExt->SymLinkName = symLinkName;  

  73.     status = IoCreateSymbolicLink(&symLinkName, &devName);  

  74.     if (!NT_SUCCESS(status))  

  75.     {  

  76.         IoDeleteDevice(pDevObj);  

  77.         return status;  

  78.     }  

  79.     //Hook SSDT  

  80.     PageProtectClose();  

  81.     //得到原来的地址,记录在 oldNtCreateSection  

  82.     oldNtCreateSection = KeServiceDescriptorTable->ServiceTableBase[SYSTEMCALL_INDEX(ZwCreateSection)];  

  83.     //修改SSDT中 NtCreateSection 的地址,使其指向 MyNtCreateSection  

  84.     KeServiceDescriptorTable->ServiceTableBase[SYSTEMCALL_INDEX(ZwCreateSection)] = (ULONG)&MyNtCreateSection;  

  85.     DbgPrint("Old Addr:0x%X\r\n", oldNtCreateSection);  

  86.     PageProtectOpen();  

  87.     return STATUS_SUCCESS;  

  88. }  

  89. DRIVER_UNLOAD DriverUnload;  

  90. extern "C" VOID DriverUnload(PDRIVER_OBJECT pDriverObject)  

  91. {  

  92.     PageProtectClose();  

  93.     //修改SSDT中 NtCreateSection 的地址,使其指向 oldNtCreateSection  

  94.     //也就是在驱动卸载时恢复原来的地址  

  95.     KeServiceDescriptorTable->ServiceTableBase[SYSTEMCALL_INDEX(ZwCreateSection)] = oldNtCreateSection;  

  96.     PageProtectOpen();  

  97.     PDEVICE_OBJECT pDevObj;  

  98.     pDevObj = pDriverObject->DeviceObject;  

  99.     PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;//得到设备扩展  

  100.     //删除符号链接  

  101.     UNICODE_STRING pLinkName = pDevExt->SymLinkName;  

  102.     IoDeleteSymbolicLink(&pLinkName);  

  103.     //删除设备  

  104.     IoDeleteDevice(pDevObj);  

  105. }  

  106. extern "C" NTSTATUS DefDispatchRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp)  

  107. {  

  108.     pIrp->IoStatus.Status = STATUS_SUCCESS;  

  109.     pIrp->IoStatus.Information = 0;  

  110.     IoCompleteRequest(pIrp, IO_NO_INCREMENT);  

  111.     return STATUS_SUCCESS;  

  112. }  

  113. extern "C" NTSTATUS IoctlDispatchRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp)  

  114. {  

  115.     NTSTATUS status = STATUS_SUCCESS;  

  116.     //得到I/O堆栈的当前这一层,也就是IO_STACK_LOCATION结构的指针  

  117.     PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);  

  118.     ULONG in_size = stack->Parameters.DeviceIoControl.InputBufferLength;//得到输入缓冲区的大小  

  119.     ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;//得到控制码  

  120.     PVOID buffer = pIrp->AssociatedIrp.SystemBuffer;//得到缓冲区指针  

  121.     switch (code)  

  122.     {  

  123.     case IOCTL1:  

  124.         DbgPrint("Get ioctl code 1\r\n");  

  125.         break;  

  126.     default:  

  127.         status = STATUS_INVALID_VARIANT;  

  128.         //如果是没有处理的IRP,则返回STATUS_INVALID_VARIANT,这意味着用户模式的I/O函数失败,但并不会设置GetLastError  

  129.     }  

  130.     // 完成IRP  

  131.     pIrp->IoStatus.Status = status;//设置IRP完成状态,会设置用户模式下的GetLastError  

  132.     pIrp->IoStatus.Information = 0;//设置操作的字节  

  133.     IoCompleteRequest(pIrp, IO_NO_INCREMENT);//完成IRP,不增加优先级  

  134.     return status;  

  135. }  

  136. NTSTATUS NTAPI MyNtCreateSection(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,  

  137.     PLARGE_INTEGER MaximumSize, ULONG SectionPageProtection, ULONG AllocationAttributes, HANDLE FileHandle){  

  138.     if ((AllocationAttributes == SEC_IMAGE) && (SectionPageProtection & PAGE_EXECUTE)){  

  139.         if (FileHandle){  

  140.             PFILE_OBJECT FileObject;  

  141.             NTSTATUS status;  

  142.             if ((status = ObReferenceObjectByHandle(FileHandle, 0, NULL, KernelMode, (PVOID*)&FileObject, NULL)) == STATUS_SUCCESS){  

  143.                 POBJECT_NAME_INFORMATION FilePath;  

  144.                 if ((status = IoQueryFileDosDeviceName(FileObject, &FilePath)) == STATUS_SUCCESS){  

  145.                     DbgPrint("FilePath: %ws\r\n", FilePath->Name.Buffer);  

  146.                     ExFreePool(FilePath);// IoQueryFileDosDeviceName 获取的 OBJECT_NAME_INFORMATION 需要手动释放  

  147.                 }  

  148.                 else DbgPrint("E: IoQueryFileDosDeviceName failed with code 0x%X\r\n", status);  

  149.                 ObDereferenceObject(FileObject);//使获取到的 FileObject 引用计数减1  

  150.             }  

  151.             else DbgPrint("E: ObReferenceObjectByHandle failed with code 0x%X\r\n", status);  

  152.         }  

  153.         else DbgPrint("E: FileHandle is NULL.\r\n");  

  154.     }  

  155.     //定义一个函数指针 _NtCreateSection, 根据 oldNtCreateSection 记录的真实函数的地址进行 Call  

  156.     //也就是说其他进程直接交还给系统的 NtCreateSection 处理  

  157.     typedef NTSTATUS(NTAPI * _NtCreateSection)(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,  

  158.         PLARGE_INTEGER MaximumSize, ULONG SectionPageProtection, ULONG AllocationAttributes, HANDLE FileHandle);  

  159.     _NtCreateSection _oldNtCreateSection = (_NtCreateSection)oldNtCreateSection;  

  160.     return _oldNtCreateSection(SectionHandle, DesiredAccess, ObjectAttributes, MaximumSize, SectionPageProtection, AllocationAttributes, FileHandle);  

  161. }  

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多