首页
社区
课程
招聘
[原创]驱动程序学习笔记(一)(二)(三)(四)(五)
发表于: 2012-3-18 23:25 79068

[原创]驱动程序学习笔记(一)(二)(三)(四)(五)

2012-3-18 23:25
79068
收藏
免费 6
支持
分享
最新回复 (90)
雪    币: 2559
活跃值: (176)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
76
楼主的这份WINDBG命令很好,我等小菜受益匪浅。
2014-3-29 09:45
0
雪    币: 40
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
77
谢楼主分享
2014-4-15 00:04
0
雪    币: 2
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
79
好东西  顶一下
2014-11-14 22:13
0
雪    币: 201
活跃值: (183)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
80
非常好,谢谢楼主分享。
2014-12-3 09:32
0
雪    币: 79
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
81
thanks a lot!!
2014-12-22 13:29
0
雪    币: 188
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
82
多谢楼主分享经验总结。
2014-12-22 15:58
0
雪    币: 207
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
83
LZ很牛叉~~~~~~~~~
2014-12-22 16:32
0
雪    币: 34
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
84
有个问题,问什么加载驱动,我的杀毒软件就会有提示呢?怎么不让它提示,在程序里应该写什么代码呢
2015-2-7 19:39
0
雪    币: 121
活跃值: (44)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
85
支持~~~希望能坚持学下去~~
2015-2-14 22:34
0
雪    币: 163
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
86
Mark
2015-2-17 23:29
0
雪    币: 81
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
87
mark  留着学习
2015-5-19 17:45
0
雪    币: 33
活跃值: (244)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
88
非常感谢,最喜欢看这类总结的笔记!!!!!
2015-6-6 00:13
0
雪    币: 39
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
89
好帖,mark一下。。
2015-6-7 23:49
0
雪    币: 346
活跃值: (25)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
90
驱动小菜路过!
2016-4-13 16:46
0
雪    币: 1604
活跃值: (640)
能力值: ( LV13,RANK:460 )
在线值:
发帖
回帖
粉丝
91
这里顺便帮 LZ 补充几点:

一,DRIVER_OBJECT 结构中的最后一个成员 MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1] ,是一个函数指针数组,长度为 IRP_MJ_MAXIMUM_FUNCTION + 1,该数组本质上是一个调用表,由28个函数(分发例程)的地址组成,驱动程序应该在 DriverEntry() 中初始化这些指针,以便处理与其对应类型的 IRP(即 I/O 请求包。 这些分发例程都接收一个指向 IRP 地址的指针作为实参传入)。

在描述 Windows 驱动程序模型的 wdm.h 头文件中可以看到,它将 IRP_MJ_MAXIMUM_FUNCTION 符号常量定义为16进制的 0x1b,因此这个数组能够存储 0x1b+1 = 0x1c = 28 个函数指针(第一个函数指针从 0x00 开始编号)
从内核调试器的输出也可以看到,这个数组由28个32 位指针(Ptr32)构成。

二,关于下面这段输出:
kd> !object \driver\i8042prt
Object: 823397c0  Type: (823ed040) Driver
    ObjectHeader: 823397a8 (old version)
    HandleCount: 0  PointerCount: 5
    Directory Object: e1014838  Name: i8042prt

其中,Object: 823397c0 是驱动对象 i8042prt 的对象起始地址,在32位windows 7上,对象头大小固定为24字节,并且统一由执行体组件的对象管理器分配,而对象体则由对象类型指出的特定执行体组件分配;你可以将对象体地址减去对象头地址(823397c0-823397a8=0x18 ),也就是10进制的24字节,这就是32位的对象头大小。

三,关于下面这段输出:
kd> !devstack 822d7310
  !DevObj   !DrvObj       !DevExt   ObjectName
  8212a030  \Driver\Kbdclass   8212a0e8  KeyboardClass0
  8212ceb8  \Driver\DKbFltr    8212cf70  
> 822d7310  \Driver\i8042prt   822d73c8  
  822dc888  \Driver\ACPI       8239bea0  00000076

其中,最左侧的 !DevObj 列下方的4个地址形成设备栈,!DrvObj 列下方则分别是创建这些设备对象的驱动对象,!DevExt 则是这些设备对象的设备扩展结构的起始地址, nt!_DEVICE_OBJECT
结构中有一个成员叫做DeviceExtension,它是一个Void指针,存储设备扩展结构的起始地址。
注意区分:nt!_DEVICE_OBJECT 中还有一个叫做 DeviceObjectExtension的成员,它是一个
_DEVOBJ_EXTENSION 对象指针,换言之,!DevExt 列中的地址是通过读取DeviceExtension
字段得到的,而非读取DeviceObjectExtension字段得到;ObjectName 列则是设备对象的名字(这个名字位于由对象管理器维护的内核全局名字空间中,在用户空间中不可见,除非驱动程序为它创建的设备对象名字再创建一个用户空间可见的符号链接
在上面这个例子中,\Driver\ACPI 是最底层的驱动对象,它创建一个设备对象(822dc888)有一个名字叫做00000076;
第二层的驱动对象为\Driver\i8042prt,它创建一个设备对象(822d7310)没有名字,该设备对象附加到\Driver\ACPI创建的设备对象822dc888之上;
第三层的驱动对象为\Driver\DKbFltr ,它创建一个设备对象(8212ceb8)附加到\Driver\i8042prt创建的822d7310设备对象之上。
由此可知,一个驱动可以创建一个设备对象并且把它附加到其他驱动创建的设备对象之上。
(通过IoAttachDevice*() 例程)
假设现在一个 IRP 由 I/O 管理器创建并自顶向下传递,那么首先将由设备栈中最顶层的设备对象
8212a030 处理该 IRP,然后依序向下传递,最后由设备对象 822dc888 处理。通过这种设备附加机制就能够监控或修改发给最终设备的I/O请求。
注意,一个驱动可以创建多个设备对象:nt!_DRIVER_OBJECT的DeviceObject成员指向链表中的第一个设备对象, nt!_DEVICE_OBJECT的NextDevice成员指向链表中下一个设备对象;最后一个设备对象的NextDevice成员总是为 NULL,因此这类似于一个单向链表(严格意义的单项链表需要每个表项都内嵌一个 nt!_SINGLE_LIST_ENTRY 结构,显然我们在驱动对象或设备对象中没有发现这个成员)。它们仅仅是通过指针引用链接在一起,并没有实质上的关联,
另一方面,由不同驱动创建并通过IoAttachDevice*() 附加到设备栈中的设备对象之间则是紧密相关的:它们都处理同一个IRP,有着实际的数据通信过程。
切勿混淆这两者,可以把一种称为设备对象;第二种称为设备对象,以助于区分。
2016-6-6 00:42
0
雪    币: 1604
活跃值: (640)
能力值: ( LV13,RANK:460 )
在线值:
发帖
回帖
粉丝
92
LZ 给出的上面这段IDA反编译的驱动的卸载例程在逻辑上而言有点小瑕疵,可以修正如下:

1。由于设备链表中的最后一个设备对象的NextDevice始终为NULL,可以通过它作为循环退出
的条件,确保遍历并删除了所有链中的设备对象,使用如下的for循环更简洁:

for(pNextObj = pDriverObject->DeviceObject; pNextObj != NULL;  pNextObj = pNextObj->NextDevice) 
{
       .....//删除设备对象的符号链接
     IoDeleteDevice(pNextObj);

}


2。没有必要读取设备对象的设备扩展子结构,运行这段代码会增加多余的CPU时钟周期开销,因此可以注释掉这一段:
v1 = pNextObj->DeviceExtension;


3。通过 IoDeleteSymbolicLink() 删除设备对象的Ring3层可见的符号链接之前,要求传入的
UNICODE_STRING结构(该结构包含符号链接字符串)必须先初始化,否则IoDeleteSymbolicLink() 根本无法查找到符号链接名并删除,可惜伪代码中没有初始化 pLinkName(一个 UNICODE_STRING 实例)。一般而言,应该使用 RtlInitUnicodeString()
来初始化一个 UNICODE_STRING 对象,比如:

const WCHAR DeviceLinkBuffer[] = L"\\DosDevices\\myDeviceSymbolLink";

UNICODE_STRING unicodeString;
RtlInitUnicodeString(&unicodeString, DeviceLinkBuffer);
IoDeleteSymbolicLink(&unicodeString);


注意,宽字符数组 DeviceLinkBuffer 被 RtlInitUnicodeString() 用于初始化unicodeString,
然后IoDeleteSymbolicLink() 就能够通过unicodeString删除设备对象的符号链接。其中:
DeviceLinkBuffer  必须定义成全局量,也就是在 DriverEntry() 之外定义;
“DosDevices”是 “\??”的别名,两者都是在用户空间中访问对象管理器维护的全局名字空间的接口,假如在符号链接字符串中少了其中之一,都无法访问到实际引用的设备对象。
很明显,你还需要确保删除的符号链接与创建时使用的是同一个,为此也可以把 unicodeString 定义成全局变量。

最后,如果驱动创建了多个设备对象,并且为每一个都创建符号链接,为了逐个删除这些设备对象以及它们各自的符号链接,可以增加一个内层的循环,或者其它等效的逻辑。
2016-6-6 17:54
0
游客
登录 | 注册 方可回帖
返回
//