本博文由CSDN博主zuishikonghuan所作,版权归zuishikonghuan所有,转载请注明出处:http://blog.csdn.net/zuishikonghuan/article/details/50924829

在上两篇博文中,我介绍了SSDTHook的原理,并给出了一个实例--通过Hook NtOpenProcess来实现进程保护:http://blog.csdn.net/zuishikonghuan/article/details/50765464

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

这么有趣的功能,要如何实现呢,其实出了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的原型:

NTSTATUS ZwCreateSection(_Out_    PHANDLE            SectionHandle,_In_     ACCESS_MASK        DesiredAccess,_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,_In_opt_ PLARGE_INTEGER     MaximumSize,_In_     ULONG              SectionPageProtection,_In_     ULONG              AllocationAttributes,_In_opt_ HANDLE             FileHandle
);

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

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

SSDTHook和原理和实现细节都在上两篇博文(http://blog.csdn.net/zuishikonghuan/article/details/50717936 和http://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请求……和轮询相比,消耗的系统资源很少,而且非常简单。

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

#include <Ntifs.h>
#include <ntddk.h>
extern "C" VOID DriverUnload(PDRIVER_OBJECT pDriverObject);
extern "C" NTSTATUS DefDispatchRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp);
extern "C" NTSTATUS IoctlDispatchRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp);#define IOCTL1 CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define SEC_IMAGE 0x1000000typedef struct _DEVICE_EXTENSION {UNICODE_STRING SymLinkName;    //我们定义的设备扩展里只有一个符号链接名成员
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;//我们将 NtCreateSection hook 到自己的函数
NTSTATUS NTAPI MyNtCreateSection(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,PLARGE_INTEGER MaximumSize, ULONG SectionPageProtection, ULONG AllocationAttributes, HANDLE FileHandle);//KeServiceDescriptorTable 中我们感兴趣的结构
typedef struct _KESERVICE_DESCRIPTOR_TABLE
{PULONG ServiceTableBase;PULONG ServiceCounterTableBase;ULONG NumberOfServices;PUCHAR ParamTableBase;
}KESERVICE_DESCRIPTOR_TABLE, *PKESERVICE_DESCRIPTOR_TABLE;//ntoskrnl.exe (ntoskrnl.lib) 导出的 KeServiceDescriptorTable
extern "C" extern PKESERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;//关闭页面保护
void PageProtectClose()
{__asm{cli;mov eax, cr0;and eax, not 10000h;mov cr0, eax;}
}//启用页面保护
void PageProtectOpen()
{__asm{mov eax, cr0;or eax, 10000h;mov cr0, eax;sti;}
}//根据 ZwXXXX的地址 获取服务函数在 SSDT 中所对应的服务的索引号
#define SYSTEMCALL_INDEX(ServiceFunction) (*(PULONG)((PUCHAR)ServiceFunction + 1))ULONG oldNtCreateSection;//之前的NtCreateSection
ULONG ProtectProcessID = 0;#pragma code_seg("INIT")
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath)
{DbgPrint("DriverEntry\r\n");pDriverObject->DriverUnload = DriverUnload;//注册驱动卸载函数//注册派遣函数pDriverObject->MajorFunction[IRP_MJ_CREATE] = DefDispatchRoutine;pDriverObject->MajorFunction[IRP_MJ_CLOSE] = DefDispatchRoutine;pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IoctlDispatchRoutine;NTSTATUS status;PDEVICE_OBJECT pDevObj;PDEVICE_EXTENSION pDevExt;//创建设备名称的字符串UNICODE_STRING devName;RtlInitUnicodeString(&devName, L"\\Device\\MySSDTHookDevice");//创建设备status = IoCreateDevice(pDriverObject, sizeof(DEVICE_EXTENSION), &devName, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevObj);if (!NT_SUCCESS(status))return status;pDevObj->Flags |= DO_BUFFERED_IO;//将设备设置为缓冲设备pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;//得到设备扩展//创建符号链接UNICODE_STRING symLinkName;RtlInitUnicodeString(&symLinkName, L"\\??\\MySSDTHookDevice_link");pDevExt->SymLinkName = symLinkName;status = IoCreateSymbolicLink(&symLinkName, &devName);if (!NT_SUCCESS(status)){IoDeleteDevice(pDevObj);return status;}//Hook SSDTPageProtectClose();//得到原来的地址,记录在 oldNtCreateSectionoldNtCreateSection = KeServiceDescriptorTable->ServiceTableBase[SYSTEMCALL_INDEX(ZwCreateSection)];//修改SSDT中 NtCreateSection 的地址,使其指向 MyNtCreateSectionKeServiceDescriptorTable->ServiceTableBase[SYSTEMCALL_INDEX(ZwCreateSection)] = (ULONG)&MyNtCreateSection;DbgPrint("Old Addr:0x%X\r\n", oldNtCreateSection);PageProtectOpen();return STATUS_SUCCESS;
}DRIVER_UNLOAD DriverUnload;
extern "C" VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{PageProtectClose();//修改SSDT中 NtCreateSection 的地址,使其指向 oldNtCreateSection//也就是在驱动卸载时恢复原来的地址KeServiceDescriptorTable->ServiceTableBase[SYSTEMCALL_INDEX(ZwCreateSection)] = oldNtCreateSection;PageProtectOpen();PDEVICE_OBJECT pDevObj;pDevObj = pDriverObject->DeviceObject;PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;//得到设备扩展//删除符号链接UNICODE_STRING pLinkName = pDevExt->SymLinkName;IoDeleteSymbolicLink(&pLinkName);//删除设备IoDeleteDevice(pDevObj);
}extern "C" NTSTATUS DefDispatchRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{pIrp->IoStatus.Status = STATUS_SUCCESS;pIrp->IoStatus.Information = 0;IoCompleteRequest(pIrp, IO_NO_INCREMENT);return STATUS_SUCCESS;
}extern "C" NTSTATUS IoctlDispatchRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{NTSTATUS status = STATUS_SUCCESS;//得到I/O堆栈的当前这一层,也就是IO_STACK_LOCATION结构的指针PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);ULONG in_size = stack->Parameters.DeviceIoControl.InputBufferLength;//得到输入缓冲区的大小ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;//得到控制码PVOID buffer = pIrp->AssociatedIrp.SystemBuffer;//得到缓冲区指针switch (code){case IOCTL1:DbgPrint("Get ioctl code 1\r\n");break;default:status = STATUS_INVALID_VARIANT;//如果是没有处理的IRP,则返回STATUS_INVALID_VARIANT,这意味着用户模式的I/O函数失败,但并不会设置GetLastError}// 完成IRPpIrp->IoStatus.Status = status;//设置IRP完成状态,会设置用户模式下的GetLastErrorpIrp->IoStatus.Information = 0;//设置操作的字节IoCompleteRequest(pIrp, IO_NO_INCREMENT);//完成IRP,不增加优先级return status;
}NTSTATUS NTAPI MyNtCreateSection(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,PLARGE_INTEGER MaximumSize, ULONG SectionPageProtection, ULONG AllocationAttributes, HANDLE FileHandle){if ((AllocationAttributes == SEC_IMAGE) && (SectionPageProtection & PAGE_EXECUTE)){if (FileHandle){PFILE_OBJECT FileObject;NTSTATUS status;if ((status = ObReferenceObjectByHandle(FileHandle, 0, NULL, KernelMode, (PVOID*)&FileObject, NULL)) == STATUS_SUCCESS){POBJECT_NAME_INFORMATION FilePath;if ((status = IoQueryFileDosDeviceName(FileObject, &FilePath)) == STATUS_SUCCESS){DbgPrint("FilePath: %ws\r\n", FilePath->Name.Buffer);ExFreePool(FilePath);// IoQueryFileDosDeviceName 获取的 OBJECT_NAME_INFORMATION 需要手动释放}else DbgPrint("E: IoQueryFileDosDeviceName failed with code 0x%X\r\n", status);ObDereferenceObject(FileObject);//使获取到的 FileObject 引用计数减1}else DbgPrint("E: ObReferenceObjectByHandle failed with code 0x%X\r\n", status);}else DbgPrint("E: FileHandle is NULL.\r\n");}//定义一个函数指针 _NtCreateSection, 根据 oldNtCreateSection 记录的真实函数的地址进行 Call//也就是说其他进程直接交还给系统的 NtCreateSection 处理typedef NTSTATUS(NTAPI * _NtCreateSection)(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,PLARGE_INTEGER MaximumSize, ULONG SectionPageProtection, ULONG AllocationAttributes, HANDLE FileHandle);_NtCreateSection _oldNtCreateSection = (_NtCreateSection)oldNtCreateSection;return _oldNtCreateSection(SectionHandle, DesiredAccess, ObjectAttributes, MaximumSize, SectionPageProtection, AllocationAttributes, FileHandle);
}

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

  1. 内核编程之Hello_kernel

    前言 通过hello_kernel编程达到以下目的: 内核编程框架了解 模块参数调用 模块间函数调用 内核模块相关关的命令使用 环境准备 内核源码树准备,有两种方法: 方法1:从kernel.org下 ...

  2. Win64 驱动内核编程-13.回调监控模块加载

    回调监控模块加载 模块加载包括用户层模块(.DLL)和内核模块(.SYS)的加载.传统方法要监控这两者加在必须 HOOK 好几个函数,比如 NtCreateSection 和 NtLoadDriver ...

  3. 【梅哥的Ring0湿润插入教程】重磅第三课:Ring0下的PE Loader及重加载内核秒杀一切内核级钩子(上篇)...

    [梅哥的Ring0湿润插入教程] Email:mlkui@163.com 转载请注明出处,谢绝喷子记者等,如引起各类不适请自觉滚J8蛋! 第三课:Ring0下PE Loader及重加载内核绕过一切内核 ...

  4. thymeleaf加载不了js引用_web前端教程之js中的模块化一

    web前端教程之js中的模块化一:我们知道最常见的模块化方案有CommonJS.AMD.CMD.ES6,AMD规范一般用于浏览器,异步的,因为模块加载是异步的,js解释是同步的,所以有时候导致依赖还没 ...

  5. 【Android 插件化】Hook 插件化框架总结 ( 插件包管理 | Hook Activity 启动流程 | Hook 插件包资源加载 ) ★★★

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  6. 【Android 插件化】Hook 插件化框架 ( 从源码角度分析加载资源流程 | Hook 点选择 | 资源冲突解决方案 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  7. 【Android 插件化】Hook 插件化框架 ( 加载插件包资源 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  8. Linux驱动之内核加载模块过程分析

    Linux内核支持动态的加载模块运行:比如insmod first_drv.ko,这样就可以将模块加载到内核所在空间供应用程序调用.现在简单描述下insmod first_drv.ko的过程 1.in ...

  9. Linux内核源码树建立加载hello模块

    在加载模块之前,书中说要先建立内核源码树,那么,如何建立内核源码树呢? 首先,要先知道你的OS的内核版本,用uname -r可以查得到 在/url/src/目录下可以看到对应的版本目录 如果没有可以用 ...

最新文章

  1. Eclipse的编码
  2. Mac OS X 中快速访问系统根目录的四种方法
  3. 这几个冷门却实用的 Python 库,我爱了!
  4. 假设有python程序文件_《Python程序设计》题库
  5. 数据结构 5-0 树与二叉树总结
  6. python3_configparser模块详解
  7. Hibernate一级缓存、二级缓存以及查询缓存的关系
  8. C# MD5加密与解密
  9. tcp程序——回声客户端
  10. 自动驾驶 11-4: 优化状态估计 Optimizing State Estimation
  11. asp.net大文件分块上传视频教程
  12. 1867. 最大数量高于平均水平的订单
  13. 记一次 关于Android studio 编译报错compileDebugJavaWithJavac FAILED
  14. marked.js读取markdown文件,图片实现点击放大
  15. 视频教程-沐风老师Scratch3.0快速入门视频课程-其他
  16. 蓝牙技术|蓝牙Mesh在照明网络上的应用
  17. linux 安装 jdk8u222
  18. GCC中文手册(中)zz
  19. 心理学导论 --- 2. 大脑是啥样的
  20. 最简单DIY基于ESP32CAM的物联网相机系统②(在JAVAWEB服务器实现图片查看器)

热门文章

  1. 问题1004:不存在的泳池
  2. linux命令seq,Linux 中seq 命令的用法
  3. 2021年中国味精市场供需及主要企业经营情况分析[图]
  4. vmware未能启动虚拟机
  5. web 响应式_建立响应式Web简历
  6. SuperMap iMobile for Android开发入门(iMobile 10i + AS 3.6)
  7. 针对报错 ValueError: When using data tensors as input to a model, you should specify the `steps_per_epoc
  8. 计算机科学与技术社会实践活动,计算机科学与技术学院社会实践团队寻根之旅...
  9. 4745g linux驱动
  10. 这款图片剪裁开源项目,简直逆天了