《DDK学习笔记》1---入门

1.驱动程序的结构:
1.1 一个入口点(DriverEntry):用于创建设备对象及符号连接,以及其它初使化操作,如分配池内存等.
1.2 一个出口(DriverUnload):删除符号连接与设备对象,并释放已经分配的各种资源,如池内存等
1.3 几个DispatchHandler:用于响应Ring3程序的请求及其它驱动事件,并做相关处理

2.内存管理
2.1 分配系统池内存(ExAllocatePool):它有点像C中malloc,只不过存在分页和紧急选项
2.2 释放系统池内存(ExFreePool):它有点像C中的free
2.3 注意:Ring3中所有堆内存都是可分页的,实质上是因为Ring3程序的中断请求级非常低,当它访问分页内存时IO管理器有机会处理页错误中断,从而调入需要的内存页,但在驱动程序中我们可以随时调整中断请求级以减少驱动程序运行的时间,这样当驱动程序的当前中断请求级高于DISPATCH_LEVEL时,IO管理器没有机会处理页错误,导致内存访问异常(此时一般出现蓝屏)
2.4 如果需要频繁分配和释放相同大小的内存,可以使用后备列表来管理系统堆内存以提高性能

3.输出输出
3.1
IRP_MJ_DEVICE_CONTROL,响应系统的DeviceIocontrol
IRP_MJ_READ,响应系统的ReadFileEx
IRP_MJ_WRITE,响应系统的WriteFileEx

3.2访问输入输出参数:
3.2.1 Buffered方式:使用IoGetCurrentIrpStackLocation得到调用者堆栈区域指针IrpStack(PIO_STACK_LOCATION类型)
 Irp->AssociatedIrp.SystemBuffer;输出输出缓冲
 IrpStack->Parameters.DeviceIoControl.InputBufferLength;输入缓冲长度
 IrpStack->Parameters.DeviceIoControl.OutputBufferLength;输出缓冲长度
 IrpStack->Parameters.DeviceIoControl.IoControlCode;设备控制代码
 如果需要输出参数,在填写完SystemBuffer后要设置IRP的IoStatus成员的Information指示输出数据的长度.
3.2.2 Direct方式:MmGetSystemAddressForMdlSafe映射IRP->MdlAddress地址到内核空间
 MmGetMdlByteCount,得到MDL大小,以字节为单位.通常应该在IRQL<=DISPATCH_LEVEL的情况下使用MDL,那么如何输出呢?和Buffered方式一样从一个地方读,处理完再写到同一个地方吗?(问题1)
3.2.3 Neither方式:这个方式比恐怖,除非确定驱动不是分层的并且运行在PASSIVE_LEVEL级,一般不使用这种方式.比如写一个简单的Dump核心数据结构的驱动,该驱动只由我们的一个程序控制,那么可以直接把用户模式的地址传给驱动使用

注意:《Programming the Microsoft Windows Driver Model》一书说到"决不(或几乎从不)直接引用用户模式的内存地址"


4.安装KMD
4.1 需要Administrator权限,调用OpenSCManager打服务管理器,用CreateService以SERVICE_KERNEL_DRIVER类型将驱动安装为服务

5.启动KMD
5.1 可以在CreateService时指定SERVICE_AUTO_START自动加载,或是指定SERVICE_DEMAND_START安装类型再调用StartService手工加载

6.停止与卸载KMD
6.1 可以使用ControlService指定SERVICE_CONTROL_STOP停止驱动,然后可以使用DeleteService卸载驱动.最后CloseServiceHandle

7.Ring3访问KMD
7.1 需要Administrator权限,CreateFile打开设备对象
7.2 DeviceIoControl,与驱动进行交互操作,可以使用各种自定义的数据结构进行输入输出.
7.3 ReadFile,读驱动数据
7.4 Writefile,写数据给驱动
7.5 CloseHandle,关闭设备对象

8.过滤/挂钩IRP请求

8.1挂钩某个IRP处理函数:
 a,调用IoGetDeviceObjectPointer返回的设备对象(PDEVICE_OBJECT)
 b,由设备对象得到驱动对象PDEVICE_OBJECT->DriverObject(PDRIVER_OBJECT),这里是否应该调用ObReferenceObjectByPointer函数增加驱动对象的引用计数,以防止该驱动程序在我们的驱动程序前被卸载呢?(问题2)
 c,再由驱动对象得到中断请求派遣函数表(PDRIVER_OBJECT->MajorFunction)
 d,保存PDRIVER_OBJECT->MajorFunction[IRP_MJ_XXXXXXX]值(原中断请求派遣函数地址)
 e,修改PDRIVER_OBJECT->MajorFunction[IRP_MJ_XXXXXXX]值(让它指向我们自定义的函数),使用锁总线前缀lock的xchg指令进行赋值操作(让代码多线程和多处理器安全)
 f,结束处理同System Service Hook

8.2过滤某个设备的IRP请求:
 a,初使化IRP请求派遣函数表MajorFunction,将它们全都指向一个派遣函数DispatchAny
 b,再为MajorFunction指定几个我们感兴趣的IRP请求派遣函数
 c,得到设备对象指针:IoGetDeviceObjectPointer
 d,将设备加到设备堆栈上:IoAttachDeviceToDeviceStack,并保存下层设备对象,以供IoCallDriver时使用.
 e,在DispatchAny中将所有IRP传给下层驱动:IoSkipCurrentIrpStackLocation,IoCallDriver
 f,在指定的几个请求派遣函数中对IRP进行处理,如果有必要,可以将IRP传给下层驱动.


思考:
 a,知道了FileMon使用过滤型驱动原理后,发现它的确和我写的File监控驱动一样没有办法监视到USB和PGP盘的操作,因为都在程序中硬编码了卷标名.在驱动中我挂接了所有的ZwXXXFile操作,然后使用ZwQueryObject得到句柄对应的对象属性,再得到卷标,最后匹配某个盘符(符号链接)与该卷标对应,从而得到文件全路径名.


 b,确定一个派遣函数在哪个驱动中:
  1.使用ZwQuerySystemInformation得到Module Lists
  2.使用8.1的方法得到某个设备派遣函数地址,比较函数地址是否在某个Driver Module的地址空间范围内.


 c,挂接/Device/Tcp的IRP_MJ_DEVICE_CONTROL函数隐藏端口
  《Subverting the Windows Kernel》一书中使用了该方法,的确比较Cool(有点像我在Ring3挂COM接口的方法,都是通过修改某个FunctionTable中的项目来实现),但是这是可以被b方法发现,如果用b方法查询某个函数地址没有存在对应的sys地址空间中,那么可以肯定rootkit也Hook了ZwQuerySystemInformation使得我们得到了不真实的Module Lists.

想到一个方法可以针C对这样的完整性检测进行攻击,首先分配一块不可分页内存,在其中写入一些花指令和时间差反跟踪代码,然后再jmp到我们的函数中,最后让/Device/Tcp的IRP_MJ_DEVICE_CONTROL的函数指向这块内存.(可行吗?问题3)

初学驱动开发,感觉对386保护模式以及DDK还需要进一步了解,否则很多操作系统核心方面的东东无法深入了解,所以说基础很重要,Rootkit或是Kernel Hacking只是这些基础技术的特殊应用.这两个星期中感觉自己学到了不少东西,得到了Eva无私帮助,无数次的问他都给我一一详细的解答,否则我没有办法进步的这么快,虽然只是刚入门但至少可以看懂《Subverting the Windows Kernel》和《Windows Internals》中的大部分内容,在这里要特别感谢Eva和那些开放核心技术资料的人,你们才是真正的Hacker!

参考资料:
《MSDN 2001》
《The Undocumented Functions Microsoft Windows NT/2000》
《Windows NT(2000) Native API Reference》
《Four-F KMD教程》,罗云彬和刘松翻译
《Programming the Microsoft Windows Driver Model》
《Undocumented Windows NT》
《Undocumented Windows 2000 Secrets》
《Subverting the Windows Kernel》
《Windows Internals,4th》
《Developing Your Own Unix-Like OS On IBM PC》

你可能感兴趣的:(数据结构,windows,Microsoft,Module,service,DDK)