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

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

2012-3-18 23:25
79067

(二)(三)(四) (五)见15楼,16楼,38楼,39楼,56楼
看看现在的网络,到处都是rootkit,bootkit,搞得乌烟瘴气的,再看看现在的看雪,已经不是我等小菜能涉猎的地方了,每天别人讨论的我都看不懂,但是又对这些感兴趣,好吧,我承认我落伍了,再不学连鸡肋都比我跑的快了。犹豫了好久,还是弄本寒江独钓看吧,先从最基本的学起。学习过程重在积累知识,运用知识,这些我从中学到的一并都记录下来,并且加入自己的理解,不对的地方还请大牛们指正鄙视,顺便向sudami,MJ0011等大牛学习。

1:具体怎么安装DDK啊,VMware调试DDK之类的,没什么好说的,就那样。
2:现在主要学习NT式驱动结构,WDM和这差不多,驱动入口函数DriverEntry,虽然名字可以自己定义,但不建议修
改,NTSTATUS DriverEntry(PDRIVER_OBJECT driver,PUNICODE_STRING reg_path),该函数由system进程负责加载,
其两个参数,一个是驱动对象,另一个是驱动注册表的字符串,这两个结构在内核中都很重要,并且由系统传入。

3:驱动对象:代表着一个驱动程序,windows通过该结构来找出其对应的驱动有什么功能,比如windows中有很多组件
都有自己的DRIVER_OBJECT,如硬件驱动,类驱动disk,NTFS文件系统等,通过WinObj可以很形象的列出系统中存在
的驱动和设备,在WinDbg中,输入命令dt _DRIVER_OBJECT可以看清楚该结构成员。
kd> dt _DRIVER_OBJECT
nt!_DRIVER_OBJECT
   +0x000 Type             : Int2B--结构类型
  +0x002 Size             : Int2B--结构大小
  +0x004 DeviceObject     : Ptr32 _DEVICE_OBJECT--驱动所创建的设备对象,此对象是条设备链的第一个对象
  +0x008 Flags            : Uint4B
   +0x00c DriverStart      : Ptr32 Void--此驱动模块在内核空间中起始地址
  +0x010 DriverSize       : Uint4B--驱动对象的大小
  +0x014 DriverSection    : Ptr32 Void
   +0x018 DriverExtension  : Ptr32 _DRIVER_EXTENSION
   +0x01c DriverName       : _UNICODE_STRING--驱动名称
  +0x024 HardwareDatabase : Ptr32 _UNICODE_STRING--
   +0x028 FastIoDispatch   : Ptr32 _FAST_IO_DISPATCH--快速I/O分发例程
  +0x02c DriverInit       : Ptr32     long 
   +0x030 DriverStartIo    : Ptr32     void 
   +0x034 DriverUnload     : Ptr32     void --对应驱动卸载例程
  +0x038 MajorFunction    : [28] Ptr32     long --主功能号,对应普通的分发函数。

4:UNICODE_STRING,与ring3下字符串不同,内核用的全部都是Unicode字符编码,为的是保障内核的安全和稳定性
避免产生缓冲区溢出等问题。下面看一下这个结构:
kd> dt _UNICODE_STRING
ntdll!_UNICODE_STRING
   +0x000 Length           : Uint2B--字符串字节长度
  +0x002 MaximumLength    : Uint2B--字符串总的字节大小
  +0x004 Buffer           : Ptr32 Uint2B--字符串缓冲区

5:DriverEntry的返回状态是NTSTATUS,他是一个错误码,使用NT_SUCCESS可以判断返回是否成功。

6:驱动就相当于是一个模块,而驱动创建产生的设备则有点像GUI编程中的窗口,那么窗口是可以接收消息的,驱动
中设备接收的消息就是IRP,在驱动编程中叫做请求,其实都一样,分为

IRP_MJ_READ,IRP_MJ_WRITE,IRP_MJ_DEVICE_CONTROL等等,对应着读请求,写请求,控制请求等。一个驱动对象可以
创建许多个设备对象,这些设备对象连接成链。
kd> dt _DEVICE_OBJECT
ntdll!_DEVICE_OBJECT
   +0x000 Type             : Int2B
   +0x002 Size             : Uint2B
   +0x004 ReferenceCount   : Int4B--引用计数值 
  +0x008 DriverObject     : Ptr32 _DRIVER_OBJECT--设备对象所属的驱动对象
  +0x00c NextDevice       : Ptr32 _DEVICE_OBJECT--指向设备链中下一个设备
  +0x010 AttachedDevice   : Ptr32 _DEVICE_OBJECT--附加设备
  +0x014 CurrentIrp       : Ptr32 _IRP--当前IRP栈,使用StartIO例程,此域指向当前IRP对象
   +0x018 Timer            : Ptr32 _IO_TIMER--设备对象的定时器 
   +0x01c Flags            : Uint4B-- 设备对象标志,以DO_作为前缀的一组常量
   +0x020 Characteristics  : Uint4B--设备的特征,以FILE_作为前缀的一组常量
   +0x024 Vpb              : Ptr32 _VPB--指向设备的卷参数块(Volumn Parameter Block)
   +0x028 DeviceExtension  : Ptr32 Void--指向设备对象的扩展部分
   +0x02c DeviceType       : Uint4B--设备类型
   +0x030 StackSize        : Char--设备栈的大小
   +0x034 Queue            : __unnamed
     +0x05c AlignmentRequirement : Uint4B--缓冲区的对齐要求,其值等于对齐边界减一
   +0x060 DeviceQueue      : -- 设备队列,存放针对该设备的I/O请求
   +0x074 Dpc              : _KDPC
     +0x094 ActiveThreadCount : Uint4B-- 用于文件系统:使用此设备对象的线程数 
   +0x098 SecurityDescriptor : Ptr32 Void-- 设备的安全描述符
   +0x09c DeviceLock       : _KEVENT-- 设备锁
   +0x0ac SectorSize       : Uint2B--扇区大小 
   +0x0ae Spare1           : Uint2B
     +0x0b0 DeviceObjectExtension : Ptr32 _DEVOBJ_EXTENSION--指向扩展部分
   +0x0b4 Reserved         : Ptr32 Void--保留的
刚刚学,这里面有的东西我也不太熟悉,有些也是通过查资料得来的。好吧,看了这么多,内容极其枯燥,来个
实例观察一下。键盘大家最熟悉不过了,系统中只要有驱动文件被载入内存,就会产生一个驱动对象。i8042prt 是

完成 ps/2 键盘驱动主
要功能的驱动程序,下面来观察一下这个i8042prt的情况

kd> !drvobj \driver\i8042prt
Driver object (823397c0) is for:
 \Driver\i8042prt
Driver Extension List: (id , addr)

Device Object list:--设备列表
8233ac88  822d7310  --说明了这个驱动创建了两个设备对象
下面来看看这个驱动对象的具体内容
kd> !object \driver\i8042prt
Object: 823397c0  Type: (823ed040) Driver
    ObjectHeader: 823397a8 (old version)
    HandleCount: 0  PointerCount: 5
    Directory Object: e1014838  Name: i8042prt
该对象的起始地址位于823397c0,好吧,查看DRIVER_OBJECT的内容
试了及好几次,命令!strct老是找不出对象结构体,不知什么原因,哪位大侠能告知小弟啊???
遂用dt命令将就着看吧
kd> dt _DRIVER_OBJECT 823397c0
ntdll!_DRIVER_OBJECT
   +0x000 Type             : 0n4
   +0x002 Size             : 0n168
   +0x004 DeviceObject     : 0x8233ac88 _DEVICE_OBJECT--设备链中的第一个设备
  +0x008 Flags            : 0x12
   +0x00c DriverStart      : 0xf86e4000 Void
   +0x010 DriverSize       : 0xb900
   +0x014 DriverSection    : 0x822d6078 Void
   +0x018 DriverExtension  : 0x82339868 _DRIVER_EXTENSION
   +0x01c DriverName       : _UNICODE_STRING "\Driver\i8042prt"
   +0x024 HardwareDatabase : 0x806727e0 _UNICODE_STRING 

"\REGISTRY\MACHINE\HARDWARE\DESCRIPTION\SYSTEM"
   +0x028 FastIoDispatch   : (null) 
   +0x02c DriverInit       : 0xf86ed285     long  i8042prt!GsDriverEntry+0
   +0x030 DriverStartIo    : 0xf86e4910     void  i8042prt!I8xStartIo+0
   +0x034 DriverUnload     : 0xf86eaeb6     void  i8042prt!I8xUnload+0
   +0x038 MajorFunction    : [28] 0xf86e7aa6     long  i8042prt!I8xCreate+0
第一个设备对象已经获得了,下面使用!devobj命令看看其具体信息
kd> !devobj 8233ac88
Device object (8233ac88) is for:
  \Driver\i8042prt DriverObject 823397c0
Current Irp 00000000 RefCount 0 Type 00000027 Flags 00002004
DevExt 8233ad40 DevObjExt 8233afd0 
ExtensionFlags (0000000000)  
AttachedDevice (Upper) 81eafba0*** ERROR: Module load completed but symbols could not be loaded for 
vmmouse.sys
 \Driver\vmmouse--看起来有点像跟鼠标有关
AttachedTo (Lower) 822dc770 \Driver\ACPI
Device queue is not busy.
再来通过DEVICE_OBJECT找到下一个设备
kd> dt _DEVICE_OBJECT 8233ac88
ntdll!_DEVICE_OBJECT
   +0x000 Type             : 0n3
   +0x002 Size             : 0x348
   +0x004 ReferenceCount   : 0n0
   +0x008 DriverObject     : 0x823397c0 _DRIVER_OBJECT--所属的驱动对象正好是上面的那个i8042prt
   +0x00c NextDevice       : 0x822d7310 _DEVICE_OBJECT--下一个设备的地址
  +0x010 AttachedDevice   : 0x81eafba0 _DEVICE_OBJECT
   +0x014 CurrentIrp       : (null) 
   +0x018 Timer            : (null) 
   +0x01c Flags            : 0x2004
   +0x020 Characteristics  : 0
   +0x024 Vpb              : (null) 
   +0x028 DeviceExtension  : 0x8233ad40 Void
   +0x02c DeviceType       : 0x27
   +0x030 StackSize        : 5 ''
   +0x034 Queue            : __unnamed
   +0x05c AlignmentRequirement : 0
   +0x060 DeviceQueue      : _KDEVICE_QUEUE
   +0x074 Dpc              : _KDPC
   +0x094 ActiveThreadCount : 0
   +0x098 SecurityDescriptor : (null) 
   +0x09c DeviceLock       : _KEVENT
   +0x0ac SectorSize       : 0
   +0x0ae Spare1           : 1
   +0x0b0 DeviceObjectExtension : 0x8233afd0 _DEVOBJ_EXTENSION
   +0x0b4 Reserved         : (null) 
先看看下一个设备干嘛的吧
kd> !devobj 822d7310
Device object (822d7310) is for:
  \Driver\i8042prt DriverObject 823397c0
Current Irp 00000000 RefCount 0 Type 00000027 Flags 00002004
DevExt 822d73c8 DevObjExt 822d7658 
ExtensionFlags (0000000000)  
AttachedDevice (Upper) 8212ceb8*** ERROR: Module load completed but symbols could not be loaded for 

DKbFltr.sys--这里名字没有显示出来,不知哪里设置问题,不过看这个驱动好像是个过滤驱动
 \Driver\DKbFltr
AttachedTo (Lower) 822dc888 \Driver\ACPI
Device queue is not busy.

看看具体信息
kd> dt _DEVICE_OBJECT 822d7310
ntdll!_DEVICE_OBJECT
   +0x000 Type             : 0n3
   +0x002 Size             : 0x348
   +0x004 ReferenceCount   : 0n0
   +0x008 DriverObject     : 0x823397c0 _DRIVER_OBJECT--所属驱动仍然是i8042prt
   +0x00c NextDevice       : (null) --设备链已经结束了
   +0x010 AttachedDevice   : 0x8212ceb8 _DEVICE_OBJECT
   +0x014 CurrentIrp       : (null) 
   +0x018 Timer            : (null) 
   +0x01c Flags            : 0x2004
   +0x020 Characteristics  : 0
   +0x024 Vpb              : (null) 
   +0x028 DeviceExtension  : 0x822d73c8 Void
   +0x02c DeviceType       : 0x27
   +0x030 StackSize        : 5 ''
   +0x034 Queue            : __unnamed
   +0x05c AlignmentRequirement : 0
   +0x060 DeviceQueue      : _KDEVICE_QUEUE
   +0x074 Dpc              : _KDPC
   +0x094 ActiveThreadCount : 0
   +0x098 SecurityDescriptor : (null) 
   +0x09c DeviceLock       : _KEVENT
   +0x0ac SectorSize       : 0
   +0x0ae Spare1           : 1
   +0x0b0 DeviceObjectExtension : 0x822d7658 _DEVOBJ_EXTENSION
   +0x0b4 Reserved         : (null) 
从这里我们知道了,这个驱动程序i8042prt创建了两个设备,这两个设备互相形成链式结构,这个链表以NULL表示
结束,驱动对象通过 +04 struct _DEVICE_OBJECT *DeviceObject 可以找到这个链。设备对象通过 +0c struct 

_DEVICE_OBJECT *NextDevice 链在一起。设备对象通过 +08 struct _DRIVER_OBJECT *DriverObject 可以找到创建

它的驱动的驱动对象。
要想获得更多的信息,还要了解一下设备栈的概念,一个物理设备的驱动任务,通常由几个驱动程序一层一层的共同

完成。每层一个设备对象,他们联系在一起,组成一个设备栈。一个设备栈上的设备对象,从上自下,联系在一起。

从下自上,也联系在一起。还以上面的那个键盘驱动为例,继续探索下设备栈的相关东西。
/*************************  设备栈****************  ***********************************************/
使用命令!devstack可以观看设备栈中的内容
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
!DevNode 82130ca8 :
  DeviceInst is "ACPI\PNP0303\4&cd4a22c&0"
  ServiceName is "i8042prt"
其中只有两个驱动对象有名字\Driver\Kbdclass ,\Driver\ACPI
/************************************************************************************/

我们从上往下依次看
第一个:
kd> !devobj 8212a030
Device object (8212a030) is for:
 KeyboardClass0 \Driver\Kbdclass DriverObject 81ebf858
Current Irp 00000000 RefCount 0 Type 0000000b Flags 00002044
Dacl e13b5f84 DevExt 8212a0e8 DevObjExt 8212a1c8 
ExtensionFlags (0000000000)  
AttachedTo (Lower) 8212ceb8 \Driver\DKbFltr
Device queue is not busy.
设备KeyboardClass0属于驱动\Driver\Kbdclass

kd> dt _DEVICE_OBJECT 8212a030
ntdll!_DEVICE_OBJECT
   +0x000 Type             : 0n3
   +0x002 Size             : 0x198
   +0x004 ReferenceCount   : 0n0
   +0x008 DriverObject     : 0x81ebf858 _DRIVER_OBJECT
   +0x00c NextDevice       : (null) --
   +0x010 AttachedDevice   : (null) --上面没有其他设备对象
   +0x014 CurrentIrp       : (null) 
   +0x018 Timer            : (null) 
   +0x01c Flags            : 0x2044
   +0x020 Characteristics  : 0
   +0x024 Vpb              : (null) 
   +0x028 DeviceExtension  : 0x8212a0e8 Void
   +0x02c DeviceType       : 0xb
   +0x030 StackSize        : 7 ''
   +0x034 Queue            : __unnamed
   +0x05c AlignmentRequirement : 0
   +0x060 DeviceQueue      : _KDEVICE_QUEUE
   +0x074 Dpc              : _KDPC
   +0x094 ActiveThreadCount : 0
   +0x098 SecurityDescriptor : 0xe13b5f70 Void
   +0x09c DeviceLock       : _KEVENT
   +0x0ac SectorSize       : 0
   +0x0ae Spare1           : 0
   +0x0b0 DeviceObjectExtension : 0x8212a1c8 _DEVOBJ_EXTENSION
   +0x0b4 Reserved         : (null) 
通过设备扩展可以看到下面一层的设备
kd> dt _DEVICE_EXTENSION 8212a1c8
ACPI!_DEVICE_EXTENSION
   +0x000 Flags            : 0x8212a030`0000000d
   +0x000 UFlags           : __unnamed
   +0x008 Signature        : 0x10
   +0x00c DebugFlags       : 0
   +0x010 DispatchTable    : (null) 
   +0x014 WorkContext      : WORK_QUEUE_CONTEXT
   +0x014 Fdo              : _FDO_DEVICE_EXTENSION
   +0x014 Filter           : _FILTER_DEVICE_EXTENSION
   +0x014 Pdo              : _PDO_DEVICE_EXTENSION
   +0x058 WorkQueue        : EXTENSION_WORKER
   +0x058 Button           : BUTTON_EXTENSION
   +0x058 Thermal          : THERMAL_EXTENSION
   +0x058 LinkNode         : LINK_NODE_EXTENSION
   +0x058 Dock             : DOCK_EXTENSION
   +0x058 Processor        : _PROCESSOR_DEVICE_EXTENSION
   +0x088 DeviceState      : 0 ( Stopped )
   +0x08c PreviousState    : 0 ( Stopped )
   +0x090 PowerInfo        : _ACPI_POWER_INFO
   +0x10c DeviceID         : (null) 
   +0x10c Address          : 0
   +0x110 InstanceID       : (null) 
   +0x114 ResourceList     : (null) 
   +0x118 PnpResourceList  : (null) 
   +0x11c OutstandingIrpCount : 0n0
   +0x120 ReferenceCount   : 0n0
   +0x124 HibernatePathCount : 0n0
   +0x128 RemoveEvent      : (null) 
   +0x12c AcpiObject       : (null) 
   +0x130 DeviceObject     : (null) 
   +0x134 TargetDeviceObject : (null) 
   +0x138 PhysicalDeviceObject : 0x00010008 _DEVICE_OBJECT
   +0x13c ParentExtension  : 0x7466744e _DEVICE_EXTENSION
   +0x140 ChildDeviceList  : _LIST_ENTRY [ 0xa030001 - 0x20616349 ]
   +0x148 SiblingDeviceList : _LIST_ENTRY [ 0x40000 - 0x0 ]
   +0x150 EjectDeviceHead  : _LIST_ENTRY [ 0x8212a318 - 0x8212a318 ]
   +0x158 EjectDeviceList  : _LIST_ENTRY [ 0xa080003 - 0x7266744e ]
嘿嘿,刚才找了下strct这个命令怎么不能用,把F:\WINDDK\7600.16385.1\Debuggers\w2kfre下的kdex2x86.dll
直接拷贝到system32目录下,然后使用命令!kdex2x86.strct 就可以查看啦,似乎比dt命令简洁多了
看效果:
kd> !kdex2x86.strct DEVOBJ_EXTENSION 8212a1c8
struct   _DEVOBJ_EXTENSION (sizeof=36)
+00 int16    Type =                              000d
+02 uint16   Size =                              0000
+04 struct   _DEVICE_OBJECT *DeviceObject =      8212A030
+08 uint32   PowerFlags =                        00000010
+0c  *Dope =                                     00000000
+10 uint32   ExtensionFlags =                    00000000
+14 void     *DeviceNode =                       00000000
+18 struct   _DEVICE_OBJECT *AttachedTo =        8212CEB8--直接找到下一层设备了
+1c struct   _LIST_ENTRY FileObjectList
+1c    struct   _LIST_ENTRY *Flink =             00000000
+20    struct   _LIST_ENTRY *Blink =             00000000

/***************************************************************************************/
第二个:依旧如前
kd> !devobj 8212ceb8
Device object (8212ceb8) is for:
  \Driver\DKbFltr DriverObject 82339398
Current Irp 00000000 RefCount 0 Type 0000000b Flags 00002004
DevExt 8212cf70 DevObjExt 8212cfd0 
ExtensionFlags (0000000000)  
AttachedDevice (Upper) 8212a030 \Driver\Kbdclass
AttachedTo (Lower) 822d7310 \Driver\i8042prt
Device queue is not busy.

kd> !strct device_object 8212ceb8
struct   _DEVICE_OBJECT (sizeof=184)
+00 int16    Type =                              0003
+02 uint16   Size =                              0114
+04 int32    ReferenceCount =                    00000000
+08 struct   _DRIVER_OBJECT *DriverObject =      82339398
+0c struct   _DEVICE_OBJECT *NextDevice =        81EBFCD8
+10 struct   _DEVICE_OBJECT *AttachedDevice =    8212A030
+14 struct   _IRP *CurrentIrp =                  00000000
+18 struct   _IO_TIMER *Timer =                  00000000
+1c uint32   Flags =                             00002004
+20 uint32   Characteristics =                   00000000
+24 struct   _VPB *Vpb =                         00000000
+28 void     *DeviceExtension =                  8212CF70
+2c uint32   DeviceType =                        0000000b
+30 char     StackSize =                         06                       .
+34 union    __unnamed62 Queue
+34    struct   _LIST_ENTRY ListEntry
+34       struct   _LIST_ENTRY *Flink =          00000000
+38       struct   _LIST_ENTRY *Blink =          00000000
+34    struct   _WAIT_CONTEXT_BLOCK Wcb
+34       struct   _KDEVICE_QUEUE_ENTRY WaitQueueEntry
+34          struct   _LIST_ENTRY DeviceListEntry
+34             struct   _LIST_ENTRY *Flink =    00000000
+38             struct   _LIST_ENTRY *Blink =    00000000
+3c          uint32   SortKey =                  00000000
+40          byte     Inserted =                 00                       .
+44       function *DeviceRoutine =              00000000
+48       void     *DeviceContext =              00000000
+4c       uint32   NumberOfMapRegisters =        00000000
+50       void     *DeviceObject =               00000000
+54       void     *CurrentIrp =                 00000000
+58       struct   _KDPC *BufferChainingDpc =    00000000
+5c uint32   AlignmentRequirement =              00000000
+60 struct   _KDEVICE_QUEUE DeviceQueue
+60    int16    Type =                           0014
+62    int16    Size =                           0014
+64    struct   _LIST_ENTRY DeviceListHead
+64       struct   _LIST_ENTRY *Flink =          8212CF1C
+68       struct   _LIST_ENTRY *Blink =          8212CF1C
+6c    uint32   Lock =                           00000000
+70    byte     Busy =                           00                       .
+74 struct   _KDPC Dpc
+74    int16    Type =                           0000
+76    byte     Number =                         00                       .
+77    byte     Importance =                     00                       .
+78    struct   _LIST_ENTRY DpcListEntry
+78       struct   _LIST_ENTRY *Flink =          00000000
+7c       struct   _LIST_ENTRY *Blink =          00000000
+80    function *DeferredRoutine =               00000000
+84    void     *DeferredContext =               00000000
+88    void     *SystemArgument1 =               00000000
+8c    void     *SystemArgument2 =               00000000
+90    uint32   *Lock =                          00000000
+94 uint32   ActiveThreadCount =                 00000000
+98 void     *SecurityDescriptor =               00000000
+9c struct   _KEVENT DeviceLock
+9c    struct   _DISPATCHER_HEADER Header
+9c       byte     Type =                        00                       .
+9d       byte     Absolute =                    00                       .
+9e       byte     Size =                        00                       .
+9f       byte     Inserted =                    00                       .
+a0       int32    SignalState =                 00000000
+a4       struct   _LIST_ENTRY WaitListHead
+a4          struct   _LIST_ENTRY *Flink =       00000000
+a8          struct   _LIST_ENTRY *Blink =       00000000
+ac uint16   SectorSize =                        0000
+ae uint16   Spare1 =                            0001
+b0 struct   _DEVOBJ_EXTENSION *DeviceObjectExtension =         8212CFD0--设备扩展在这
+b4 void     *Reserved =                         00000000
观看下一层设备
kd> !strct devobj_extension 8212CFD0
struct   _DEVOBJ_EXTENSION (sizeof=36)
+00 int16    Type =                              000d
+02 uint16   Size =                              0000
+04 struct   _DEVICE_OBJECT *DeviceObject =      8212CEB8
+08 uint32   PowerFlags =                        00000000
+0c  *Dope =                                     00000000
+10 uint32   ExtensionFlags =                    00000000
+14 void     *DeviceNode =                       00000000
+18 struct   _DEVICE_OBJECT *AttachedTo =        822D7310--下一层设备在此
+1c struct   _LIST_ENTRY FileObjectList
+1c    struct   _LIST_ENTRY *Flink =             00000000
+20    struct   _LIST_ENTRY *Blink =             00000000

/////////////.............................................................///////////////////////
看吧,跟上面设备栈描述的一模一样,下面的两个观察跟上面的一样了,下面总结一下
!DevObj   !DrvObj            !DevExt   ObjectName
  8212a030  \Driver\Kbdclass   8212a0e8  KeyboardClass0
  8212ceb8  \Driver\DKbFltr    8212cf70  
> 822d7310  \Driver\i8042prt   822d73c8  
  822dc888  \Driver\ACPI       8239bea0  00000076
从上至下:驱动对象\Driver\Kbdclass创建的设备/Device/KeyboardClass0从设备扩展中能够找到没有名字的

DKbFltr,驱动对象\Driver\DKbFltr创建的设备又可以找到没有名字的
i8042prt,驱动对象\Driver\i8042prt创建的设备又可以找到驱动对象\Driver\ACPI 创建的设备00000076

从下而上也是一样的,在我这理解为,同一个驱动创建的设备对象是成水平关联的,关联成一个设备链表结构。
而很多个功能驱动对象FDO为同一个物理驱动PDO对象服务,这些FDO所创建的设备分别附加到PDO上,形成垂直
结构,这应该就是过滤驱动的来源吧。整个键盘驱动的设备栈建立过程大概都了解了,我想整个过程可以这样说
驱动Kbdclass,驱动DKbFltr,驱动i8042prt分别创建附加设备到键盘设备栈中,而最底层的设备由ACPI 创建,这四个设备构成键盘设备栈。其中DKbFltr创建的设备是虚拟机独有的吗?不是太了解,有知道的跟我科普一下,另外初学WinDbg,命令繁多,不是太熟悉,附件里把常用命令集合在一起了,有需要的下下来。


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 6
支持
分享
最新回复 (90)
雪    币: 114
活跃值: (26)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
排版没有排好,刚学,貌似N多问题不懂啊,继续前进加油,过段时间再把我接下来两个周的学习总结一下,并来篇实战的。
2012-3-18 23:32
0
雪    币: 601
活跃值: (256)
能力值: ( LV11,RANK:190 )
在线值:
发帖
回帖
粉丝
3
谢谢分享,
2012-3-18 23:38
0
雪    币: 506
活跃值: (70)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
谢谢分亨,正在学.
2012-3-19 08:15
0
雪    币: 244
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
楼主加油写,我加紧看
2012-3-19 08:43
0
雪    币: 2323
活跃值: (4113)
能力值: ( LV12,RANK:530 )
在线值:
发帖
回帖
粉丝
6
支持~~~希望能坚持学下去~~
2012-3-19 09:30
0
雪    币: 114
活跃值: (26)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
嗯,谢谢你们的鼓励,我加油学好驱动编程,原来想拿毛德操那本书看得,发现刚开始就看不懂了,基础不行,所以先学最基本的东西。做好笔记,亲自动手实践调试,下周把本周学到的再做个总结。
2012-3-19 12:46
0
雪    币: 114
活跃值: (26)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
8
刚才看到这个ACPI了,原来ACPI是属于硬件抽象层里的东西了,微软随身携带几个HAL以适应不同的平台,封装在hal.dll,和halacpi.dll文件中。直接与硬件相关。
2012-3-19 13:04
0
雪    币: 113
活跃值: (100)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
9
很好的文章,由浅入深,我这种没写过驱动的也能够看懂。另外问个问题,

!DevObj   !DrvObj            !DevExt   ObjectName
  8212a030  \Driver\Kbdclass   8212a0e8  KeyboardClass0
  8212ceb8  \Driver\DKbFltr    8212cf70  
> 822d7310  \Driver\i8042prt   822d73c8  
  822dc888  \Driver\ACPI       8239bea0  00000076

x86的栈是向下增长的,设备栈应该类似于函数调用栈吧,为什么设备对象的地址不是依次变小的了?
2012-3-19 18:50
0
雪    币: 32
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
mark~~
2012-3-19 19:35
0
雪    币: 114
活跃值: (26)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
11
这个我也解释不好,也没查到有关这方面的解释,不好意思哪!
2012-3-21 17:02
0
雪    币: 226
活跃值: (41)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
很好的学习笔记
2012-3-22 21:33
0
雪    币: 220
活跃值: (40)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
13
首先,这里的设备栈实质是一个双向链表,与x86的栈(内存栈)没有关联。

其次,插入DO到链表的函数通常是:IoAttachDeviceToDeviceStack / IoAttachDeviceToDeviceStackSafe等
          从链表移除DO的函数通常是:IoDetachDevice
          操作链表以发送IRP的函数通常是:IoCallDriver / PoCallDriver

最后,之所以称之为设备栈,主要是从IRP的下发path来看的(见下图)。在IRP下发过程中,先加入链表的DOn最后
          得到IRP,后加入链表的DO1先得到IRP,这符合”栈“的特点(左边)。同样,如果从IRP返回的path来看,先加
         入链表的DOn最先得到IRP,最后加入链表的DO1最后得到IRP,这符合”队列“的特点(右边)。所以你也可以认
         为其是一个设备队列。
        I/O Manager
        ------------
        \|/    DO1        /|\
      IRP        ------------     IRP
        \|/    DO2        /|\
        ------------
        \|/      ...        /|\
        ------------
            DOn
        ------------

注:以上仅是个人理解,仅作参考!如有不当,多指教,谢谢!
2012-3-23 17:16
0
雪    币: 220
活跃值: (40)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
14
首先,这里的设备栈实质是一个双向链表,与x86的栈(内存栈)没有关联。

其次,插入DO到链表的函数通常是:IoAttachDeviceToDeviceStack / IoAttachDeviceToDeviceStackSafe等
          从链表移除DO的函数通常是:IoDetachDevice
          操作链表以发送IRP的函数通常是:IoCallDriver / PoCallDriver

最后,之所以称之为设备栈,主要是从IRP的下发path来看的(见下图)。在IRP下发过程中,先加入链表的DOn最后
          得到IRP,后加入链表的DO1先得到IRP,这符合”栈“的特点(左边)。同样,如果从IRP返回的path来看,先加
         入链表的DOn最先得到IRP,最后加入链表的DO1最后得到IRP,这符合”队列“的特点(右边)。所以你也可以认
         为其是一个设备队列。

        I/O Manager
                --------------
                \|/    DO1   /|\
         IRP  --------------      IRP
                \|/    DO2   /|\
                --------------
                \|/     ...      /|\
                --------------
                      DOn
                --------------

注:以上仅是个人理解,仅作参考!如有不当,多指教,谢谢!
2012-3-23 17:17
0
雪    币: 114
活跃值: (26)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
15
驱动学习笔记(二)
1:IRP--上节学到几个对象,驱动对象,设备对象,字符串还有IRP请求,
IRP有两个基本的属性,即MajorFunction和MinorFunction,分别记录IRP的主类型和子类型。操作系统
根据MajorFunction决定将IRP分发到哪个派遣例程,然后派遣例程根据MinorFunction进行细分处理。
文件I/O的相关函数例如CreateFile、ReadFile、WriteFile、CloseHandle等分别会引发操系统产生
IRP_MJ_CREATE、IRP_MJ_READ、IRP_MJ_WRITE、IRP_MJ_CLOSE等不同的IRP,这些IRP会作被传送到驱动
程序的相应派遣例程中。
这节详细看下IRP
使用命令!strct _IRP查看IRP结构
kd> !strct _IRP
struct   _IRP (sizeof=112)
+00 int16    Type--IRP类型
+02 uint16   Size--IRP大小
+04 struct   _MDL *MdlAddress--内存描述符表指针,这是一个缓冲区,用来存放内核请求的
+08 uint32   Flags--文件系统驱动所用的域
+0c union    __unnamed14 AssociatedIrp
+0c    struct   _IRP *MasterIrp
+0c    int32    IrpCount
+0c    void     *SystemBuffer--系统缓冲区,IRP也可能用这个,取决于I/O请求方式
+10 struct   _LIST_ENTRY ThreadListEntry
+10    struct   _LIST_ENTRY *Flink
+14    struct   _LIST_ENTRY *Blink
+18 struct   _IO_STATUS_BLOCK IoStatus--I/O状态,
+18    int32    Status
+18    void     *Pointer
+1c    uint32   Information
+20 char     RequestorMode--表示原始请求模式,分为用户模式和内核模式
+21 byte     PendingReturned--如果为TRUE,则表明处理该IRP的最低级派遣例程返回了STATUS_PENDING
+22 char     StackCount
+23 char     CurrentLocation--到CurrentLocation就是当前I/O堆栈单元的索引以及CurrentStackLocation
                              就是指向它的指针,会对驱动程序调试有一些帮助。
+24 byte     Cancel
+25 byte     CancelIrql
+26 char     ApcEnvironment
+27 byte     AllocationFlags
+28 struct   _IO_STATUS_BLOCK *UserIosb
+2c struct   _KEVENT *UserEvent
+30 union    __unnamed15 Overlay
+30    struct   __unnamed16 AsynchronousParameters
+30       function *UserApcRoutine
+34       void     *UserApcContext
+30    union    _LARGE_INTEGER AllocationSize
+30       uint32   LowPart
+34       int32    HighPart
+30       struct   __unnamed3 u
+30          uint32   LowPart
+34          int32    HighPart
+30       int64    QuadPart
+38 function *CancelRoutine--IRP取消例程
+3c void     *UserBuffer
+40 union    __unnamed17 Tail
+40    struct   __unnamed18 Overlay
+40       struct   _KDEVICE_QUEUE_ENTRY DeviceQueueEntry
+40          struct   _LIST_ENTRY DeviceListEntry
+40             struct   _LIST_ENTRY *Flink
+44             struct   _LIST_ENTRY *Blink
+48          uint32   SortKey
+4c          byte     Inserted
+40       void     *DriverContext[4]
+50       struct   _ETHREAD *Thread
+54       char     *AuxiliaryBuffer
+58       struct   _LIST_ENTRY ListEntry
+58          struct   _LIST_ENTRY *Flink
+5c          struct   _LIST_ENTRY *Blink
+60       struct   _IO_STACK_LOCATION *CurrentStackLocation
+60       uint32   PacketType
+64       struct   _FILE_OBJECT *OriginalFileObject
+40    struct   _KAPC Apc
+40       int16    Type
+42       int16    Size
+44       uint32   Spare0
+48       struct   _KTHREAD *Thread
+4c       struct   _LIST_ENTRY ApcListEntry
+4c          struct   _LIST_ENTRY *Flink
+50          struct   _LIST_ENTRY *Blink
+54       function *KernelRoutine
+58       function *RundownRoutine
+5c       function *NormalRoutine
+60       void     *NormalContext
+64       void     *SystemArgument1
+68       void     *SystemArgument2
+6c       char     ApcStateIndex
+6d       char     ApcMode
+6e       byte     Inserted
+40    void     *CompletionKey
这里面包含的结构体非常多,很多不是很明白,DDK的帮助里面看得也是一头雾水,等到接触到了再慢慢消化。
内核中有许多数据结构阅读起来不是很方便,因此我介绍两个工具来使用,SymbolTypeViewer,和PDBexplorer。
SymbolTypeViewer使用很简单,单击file找到windows\system32\ntkrnlpa.exe,接着单击Symbolpath设定存放下载
的符号文件,点server默认用微软的链接,最后点getsymbol就可以了,然后用PDBexplorer找到刚才下载的.pdb文
件,打开即可找到结构体IRP,很方便的说,其实我在用这个PDBexplorer时候,想起来我们在配置WinDbg时,默认
也有个存放符号文件的文件夹,找到那个文件夹,在下面有各种符号文件,这样前面那个软件也就不需要了。
要想更加详细的了解每个成员,直接看他的定义好了。F:\WINDDK\7600.16385.0\ inc\ddk\wdm.h。

2:这些的请求都是被分发函数所获得的,可以只设定一个分发函数,也可以给每个请求都设置一个分发函数。
分发函数原型一般是:NTSTATUS MyDispatch(PDEVICE_OBJECT device,PIRP irp);这个函数所需要传递的就是
设备指针和IRP请求指针。到具体应用时再讲。

3:常用内核函数
    Ex系列--分配内存,获取互斥体等
        ExAllocatePool,分配内存
        ExFreePool,释放内存
        ExAcquireFastMutex获取一个快速互斥体
        ExReleaseFastMutex释放一个快速互斥体
        ExRaiseStatus抛出异常
    Zw--Nt系列文件操作函数
        ZwCreateFile--创建文件
        ZwWriteFile--写文件
        ZwReadFile--读取文件
        ZwQueryDirectoryFile--查询目录
        ZwDeviceIoControlFile--发出设备控制请求
        ZwCreateKey--打开一个注册表键
        ZwQueryValueKey--读取一个注册表键
    Rtl系列字符串操作函数
        RtlInitUnicodeString--初始化一个Unicode字符串
        RtlCopyUnicode--拷贝字符串
        RtlAppendUnicodeToString--追加字符串到另一个字符串
        RtlStringCbPrintf--将字符打印到字符串中,相当于格式化字符串
        RtlCopyMemory--拷贝内存
        RtlMoveMemory--移动内存数据块
        RtlZeroMemory--内存数据块清零
        RtlCompareMemory--比较内存
        RtlGetVersion--得到当前windows版本
    Io开头的IO管理函数
        IoCreateFile--打开文件,比ZwCreateFile函数更加底层
        IoCreateDevice--生成一个设备对象
        IoCallDriver发送请求
        IoCompleteRequest--完成IRP请求
        IoCopyCurrentIrpStackLocationToNext--讲当前IRP栈空间拷贝到下一个栈空间
        IoSkipCurrentIrpStackLocationToNext--跳过当前IRP栈空间
        IoGetCurrentIrpStackLocation--得到当前IRP栈空间。
4:多线程问题
        如何判断多线程安全,主要是根据调用源来判定的,调用源说白了就是调用这个函数的函数,若调用源
  是单线程,则也是单线程,遵循以下几个规则,
  规则一:若函数A的所有调用源运行于同意单线程环境中,则A也只运行于单线程环境中
  规则二:若A其中的一个调用源可能运行于多线程环境中,而整个调用过程中未采用自旋锁或者互斥体等同步
          措施,那么其运行于多线程是不安全的
  规则三:若A的所有调用路径可能运行于多线程的路径都采取了强制转化为单线程的措施,那么其多线程是安
          全的,貌似跟规则二重复了。
  规则四:只是用函数内部资源,不使用全局变量,静态变量等是多线程安全的。
  规则五:若那些全局变量或静态变量被强制使用了同步手段规定同一时刻只能有一个线程访问,那么多线程也
          是安全的。
  内核代码各个函数可能运行的环境
        DriverEntry,DriverUnload 单线程
        各种分发函数,--多线程
        完成函数--多线程
        各种NDIS回调函数--多线程

5:代码的中断级概念。
        Windows为CPU的运行状态定义了许多的级别,即IRQL,任一时间中,CPU总是运行在其中的某一级别,各个
级别规定了CPU哪些事可以做,哪些事不可做。级别
定义如下:
#define PASSIVE_LEVEL        0        ;级别最低,CPU在用户层,或刚进内核运行于管理层时就运行在此级别上
#define LOW_LEVEL        0        ;
#define APC_LEVEL        1        ;比PASSIVE_LEVEL略高,运行APC函数(进程与线程)时需要的级别
#define DISPATCH_LEVEL        2        ;相当于cpu运行在内核层,线程切换时级别从此下降
#define PROFILE_LEVEL        27        ;级别3以上用于硬件中断。
#define CLOCK1_LEVEL        28
#define CLOCK2_LEVEL        28
#define IPI_LEVEL        29
#define POWER_LEVEL        30
#define HIGH_LEVEL        31
1:用于页面换出,只要级别不高于APC_LEVEL,其代码都允许换出,因此在管理层的代码中每个函数前放上PAGED_CODE();说明代码所占的页面可以换出去。
2:但是光引用PAGED_CODE()不会使函数所在页面被交换出去,真正使其倒换的是编译指令#program alloc_text(),例如#program alloc_text(PAGE,NtQueryObject).
现在只了解Dispach和Passive级别两种,一般功能比较简单的函数运行在Dispatch中断级别上,到时可以查看WDK。
        DriverEntry,DriverUnload--Passive
        各种分发函数,--Passive
        完成函数--Dispatch
        各种NDIS回调函数--Dispatch
若当前CPU运行在Dispatch上,而你又想调用Passive级别上的函数,那就采用另开一个线程去专门执行这部分代码吧
2012-3-23 23:38
0
雪    币: 114
活跃值: (26)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
16
驱动学习笔记(三)
1:学了这么多,来个经典的HelloWorld吧,源码如下很简单:
#include <ntddk.h>
       
// 提供一个Unload函数只是为了能够支持动态卸载,防止驻留内存
VOID DriverUnload(PDRIVER_OBJECT driver)
{
        // 但什么都不做,只打印一句话:
        DbgPrint("first: Our driver is unloading…\r\n");
}

// DriverEntry,入口函数。相当于main。
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
#if DBG//调试命令
//       _asm int 3
#endif
        // 这是我们的内核模块的入口,可以在这里写入我们想写的东西。
        // 我在这里打印一句话。
        DbgPrint("first: Hello, world!");

        // 设置一个卸载函数便于这个函数能退出。
        driver->DriverUnload = DriverUnload;
        return STATUS_SUCCESS;//返回成功,否则不执行
}
使用makefile文件和sources文件在DDK的build命令行下编译,记住,源文件扩展名一定要用.c,明确规定驱动
的入口一定要是C程序编译方式,否则将引起错误,若想在C++中编写驱动,前面要加上extern "C",表示以C程序
方式编译。编译后会得到一个sys文件。怎么安装驱动程序呢?书上介绍的是使用srvinstw.exe,可是我嫌他烦,
于是自己写了个加载驱动的程序,MFC写的,一键安装卸载驱动,在附件里,观察驱动输出
信息使用debugview.exe来查看。我们的第一个驱动就生成了。开启虚拟机来看一下效果吧。直接使用windbg看得
也比较清楚,可以正确加载和卸载。

若果将上面的调试命令前双斜线去掉,就可以在WinDbg中调试了,在windbg中可以清清楚楚看到执行了哪些语句。
无聊下我给他拖到IDA中看了一下,我只会F5,结果一看啊,跟我写的差不多,IDA太牛叉了。学驱动嘛,会编也要会
逆,不仅提高自己的编程知识,也能快速从别人那获得最新的知识。话说我汇编学的比较烂,闲来无事就找了个简单
的驱动逆了一下,下面这个驱动程序是驱动开发技术详解上的一个HelloNT驱动的实例,没做多少事,以前在刚想学
驱动编程时从网上下的,反正自己也没那么高水平,一下子逆个高难度的,就从最简单的开始吧。为以后学习打好基
础,更想学习好IDA这个工具,学习好静态分析,以后编写的驱动我都拿来用IDA分析一遍,训练自己的学习能力。我是
菜鸟,不对的地方欢迎指正。源文件及我用到的自己的加载驱动的程序我上传在附件中,附件我的程序有时运行在别得
机器上可能会出现MFC那几个动态链接库缺少的情况,我都打包在里面,加载驱动的程序我是照着网上的例子自己写的
很粗糙,也很简单,就是利用服务控制管理器(SCM)这类函数来创建加载服务。

/***************1:首先是入口函数,DriverEntry,下面是逆过后的函数***********************************/
        int __stdcall DriverEntry(_DRIVER_OBJECT *pDriverObject, _UNICODE_STRING *pRegistryPath)
{
  int v3; // ST04_4@1
  DbgPrint("Enter DriverEntry\n");
  pDriverObject->DriverUnload = HelloDDKUnload;
  pDriverObject->MajorFunction[0] = HelloDDKDispatchRoutine;
  pDriverObject->MajorFunction[2] = HelloDDKDispatchRoutine;
  pDriverObject->MajorFunction[4] = HelloDDKDispatchRoutine;
  pDriverObject->MajorFunction[3] = HelloDDKDispatchRoutine;
  v3 = CreateDevice(pDriverObject);
  DbgPrint("DriverEntry end\n");
  return v3;
}
基本还原的差不多了,也不需要看什么了,唯一一个值得注意的就是V3这个局部变量,虽然定义为整型,但是依据
这一句v3 = CreateDevice(pDriverObject);和 return v3;在加上驱动入口的返回值是NTSTATUS,可以明确知道
他就是NTSTATUS这个驱动编程中最常见的返回类型了。我们看看汇编代码什么样的
INIT:00010810                                ;INIT指明此函数只在加载的时候需要载入,后可被卸载
INIT:00010810 ; int __stdcall DriverEntry(_DRIVER_OBJECT *pDriverObject, _UNICODE_STRING *pRegistryPath)
INIT:00010810 _DriverEntry@8  proc near               ; CODE XREF: GsDriverEntry(x,x)+Bj
INIT:00010810
INIT:00010810 status          = dword ptr -4
INIT:00010810 pDriverObject   = dword ptr  8
INIT:00010810 pRegistryPath   = dword ptr  0Ch
INIT:00010810
INIT:00010810                 mov     edi, edi
INIT:00010812                 push    ebp
INIT:00010813                 mov     ebp, esp
INIT:00010815                 push    ecx
INIT:00010816                 push    offset aEnterDriverent ; 字符串enter DriverEntry
INIT:0001081B                 call    _DbgPrint       ; 调用DbgPrint打印上面那串字符串
INIT:00010820                 add     esp, 4
INIT:00010823                 mov     eax, [ebp+pDriverObject]
INIT:00010826                 mov     dword ptr [eax+34h], offset ?HelloDDKUnload@@YGXPAU_DRIVER_OBJECT@@@Z
                                        ; 我们的卸载函数
INIT:0001082D                 mov     ecx, [ebp+pDriverObject]
INIT:00010830                 mov     dword ptr [ecx+38h], offset ?HelloDDKDispatchRoutine@@YGJPAU_DEVICE_
                                        OBJECT@@PAU_IRP@@@Z ; 下面是各种IRP的分发函数
INIT:00010837                 mov     edx, [ebp+pDriverObject]
INIT:0001083A                 mov     dword ptr [edx+40h], offset ?HelloDDKDispatchRoutine@@YGJPAU_DEVICE_
                                        OBJECT@@PAU_IRP@@@Z ; HelloDDKDispatchRoutine(_DEVICE_OBJECT *,_IRP *)
INIT:00010841                 mov     eax, [ebp+pDriverObject]
INIT:00010844                 mov     dword ptr [eax+48h], offset ?HelloDDKDispatchRoutine@@YGJPAU_DEVICE_
                                        OBJECT@@PAU_IRP@@@Z ; HelloDDKDispatchRoutine(_DEVICE_OBJECT *,_IRP *)
INIT:0001084B                 mov     ecx, [ebp+pDriverObject]
INIT:0001084E                 mov     dword ptr [ecx+44h], offset ?HelloDDKDispatchRoutine@@YGJPAU_DEVICE_
                                        OBJECT@@PAU_IRP@@@Z ; HelloDDKDispatchRoutine(_DEVICE_OBJECT *,_IRP *)
INIT:00010855                 mov     edx, [ebp+pDriverObject]
INIT:00010858                 push    edx             ; pDriverObject
INIT:00010859                 call    ?CreateDevice@@YGJPAU_DRIVER_OBJECT@@@Z ; 调用创建设备函数(CreateDevice),传入驱动对象指针_DRIVRR_OBJECT
INIT:0001085E                 mov     [ebp+status], eax
INIT:00010861                 push    offset aDriverentryEnd ; 看下面的那个DbgPrint就知道上面这个压栈的只是一串提示字符串罢了
INIT:00010866                 call    _DbgPrint
INIT:0001086B                 add     esp, 4
INIT:0001086B                 add     esp, 4
INIT:0001086E                 mov     eax, [ebp+status]
INIT:00010871                 mov     esp, ebp
INIT:00010873                 pop     ebp
INIT:00010874                 retn    8
INIT:00010874 _Drive

////////////////逆向CreateDevice函数////////////////////////////////////
2:接着看看最重要的创建设备函数。先看一下IDA逆向后的代码;能看懂的我都注释了
其实看名字可以猜的,依照我们编程的习惯
int __stdcall CreateDevice(_DRIVER_OBJECT *pDriverObject)
{
  int result; // eax@2
  void *v2; // 这里的指针类型返回值都被用void代替了
  void *v3; // edx@3
  _UNICODE_STRING DeviceName; // UnicodeString设备名
  void *pDevExt; // [sp+8h] [bp-1Ch]@3
  int status; // [sp+Ch] [bp-18h]@1
  _UNICODE_STRING symLinkName; //符号链接名
  _UNICODE_STRING devName; // [sp+18h] [bp-Ch]@1
  void *pDevObj; // [sp+20h] [bp-4h]@1

  RtlInitUnicodeString(&devName, L"\\Device\\MyDDKDevice");//初始化设备名
  *(_DWORD *)&DeviceName.Length = *(_DWORD *)&devName;-|
                                                        }这两句应该就是把devName赋值给DeviceName方便
                                                        }IoCreateDevice去使用
  DeviceName.Buffer = devName.Buffer;------------------|
  //从这个函数的最后一个参数(PDEVICE_OBJECT *)&pDevObj来看,上面的这个变量void *pDevObj;返回类型就是
  //PDEVICE_OBJECT,IDA把变量pDevObj的类型理解成void*,其实是PDEVICE_OBJECT,又进一步了
  status = IoCreateDevice(pDriverObject, 0x14u, &DeviceName, 0x22u, 0, 1, (PDEVICE_OBJECT *)&pDevObj);
  //下面判断返回值的
  if ( status >= 0 )
  {
        //4u不知道是个什么家伙,但是pDevObj我们知道是PDEVICE_OBJECT这个结构体,查看DDK这个结构体说明
        //+ 7表示PDEVICE_OBJECT结构体偏移7*4==28字节处,根据我在驱动编程笔记一中提到的DEVICE_OBJECT;
        //在+0x01c(+28处) 为Flags标志,那么这一句可以理解为pDevObj->Flags设置标志,具体这个标志我就不
        //清楚了
    *((_DWORD *)pDevObj + 7) |= 4u;
    /*******************************************/
        //pDevObj + 10==DEVICE_OBJECT结构体偏移10*4=40字节处,即偏移+28H处+0x028 DeviceExtension  :
        //呵呵,看来是设备扩展,这么就容易理解了,pDevExt是设备扩展结构体指针,下面这几句大概都是跟
        //这个结构体有关的,从中可以大概猜出这个结构体有哪些成员,此句就是初始化设备扩展
    pDevExt = (void *)*((_DWORD *)pDevObj + 10);
    /*******************************************/
        //下面这一句表明这个结构体的第一个成员就是设备对象,计算机总是从零开始计算的
    *(_DWORD *)pDevExt = pDevObj;
    v2 = pDevExt;//从这可知v2是设备扩展结构体指针
        //第二个成员是设备名称,UNICODE_STRING
    *((_DWORD *)pDevExt + 1) = *(_DWORD *)&devName;
    *((_DWORD *)v2 + 2) = devName.Buffer;
        //初始化符号链接名,用于下面创建符号链接
    RtlInitUnicodeString(&symLinkName, L"\\??\\HelloDDK");
    v3 = pDevExt;
        //设备扩展的第三个成员就是符号链接名
    *((_DWORD *)pDevExt + 3) = *(_DWORD *)&symLinkName;
    *((_DWORD *)v3 + 4) = symLinkName.Buffer;
    status = IoCreateSymbolicLink(&symLinkName, &devName);//创建符号链接,判断返回值
    if ( status >= 0 )
    {
      result = 0;
    }
    else
    {
      IoDeleteDevice((PDEVICE_OBJECT)pDevObj);//如果创建符号链接失败,则删除创建成功的设备
      result = status;
    }
  }
  else
  {
    result = status;//从此处可知前面定义的int result变量,其实也是个NTSTATUS类型
  }
  return result;
}

/*******************下面给出我的伪代码**************************************/
NTSTATUS _stdcall CreateDevice(_DRIVER_OBJECT *pDriverObject)
{
        NTSTATUS result;
        NTSTATUS status;
        PDEVICE_OBJECT pDevObj;//设备对象
        _UNICODE_STRING DeviceName;//设备名称
        _UNICODE_STRING symLinkName; //符号链接名
        PDEVICE_EXTENSION pDevExt;//设备扩展
        RtlInitUnicodeString(&DeviceName, L"\\Device\\MyDDKDevice");//初始化设备名
        status = IoCreateDevice(pDriverObject, 0x14u, &DeviceName, 0x22u, 0, 1, (PDEVICE_OBJECT *)&pDevObj);
        if(status>=0)//这一句其实就是我们常用判断是否成功的NT_SUCCESS(status)
           {
                //如果创建成功,那么就设置相关标志并且填充设备扩展结构体
                pDevObj->Flags |= 4u;
                pDevExt = (PDEVICE_EXTENSION)=pDevObj->DeviceExtension;
                pDevExt->pDevObj = pDevObj;
                pDevExt->DevName = DeviceName;
                pDevExt->SymboName = symLinkName;
                //还原的设备扩展结构体在最后面
                RtlInitUnicodeString(&symLinkName, L"\\??\\HelloDDK");
                status = IoCreateSymbolicLink(&symLinkName, &devName);//创建符号链接,判断返回值
                if(status<=0)
                {
                        IoDeleteDevice((PDEVICE_OBJECT)pDevObj);//如果创建符号链接失败,则删除设备
                             result = status;       
                }
                else {result = status;}
           }
        retern result;
}

/********************3:分发函数******************************/
//分发函数比较简单,给出IDA F5后的代码,一目了然,相应的汇编也能看得大差不差
int __stdcall HelloDDKDispatchRoutine(_DEVICE_OBJECT *pDevObj, _IRP *pIrp)
{
  _IRP *v3; // edx@1
  DbgPrint("Enter HelloDDKDispatchRoutine\n");
  pIrp->IoStatus.___u0.Status = 0;//设置IRP状态
  v3 = pIrp;
  pIrp->IoStatus.Information = 0;//填充Information为0
  LOBYTE(v3) = 0;
  IofCompleteRequest(pIrp, v3);//设置IRP为完成
  DbgPrint("Leave HelloDDKDispatchRoutine\n");
  return 0;
}

/***********************4:卸载函数*********************************/
//此函数也比较简单,相信不用还原都能看的出来干什么了。
void __stdcall HelloDDKUnload(_DRIVER_OBJECT *pDriverObject)
{
  void *v1; // ST04_4@3
  unsigned __int16 *v2; // eax@3
  _UNICODE_STRING pLinkName; // 符号链接名
  _DEVICE_OBJECT *pNextObj; // 下一个设备对象

  DbgPrint("Enter DriverUnload\n");
  pNextObj = pDriverObject->DeviceObject;//设备链中的第一个设备
  while ( pNextObj )//这个循环遍历驱动创建设备链
  {
    v1 = pNextObj->DeviceExtension;//获得下一个设备的设备扩展
    v2 = (unsigned __int16 *)*((_DWORD *)v1 + 4);
    *(_DWORD *)&pLinkName.Length = *((_DWORD *)v1 + 3);
    pLinkName.Buffer = v2;
    IoDeleteSymbolicLink(&pLinkName);//删除相应的符号链接
    pNextObj = pNextObj->NextDevice;//指向设备链中的下一个设备
    IoDeleteDevice(*(PDEVICE_OBJECT *)v1);//删除设备
  }
}

/*****************5:还原的设备扩展结构体***********************/
typedef struct _DEVICE_EXTENSION {
        PDEVICE_OBJECT pDevObj;
        UNICODE_STRING DevName;        //设备名称
        UNICODE_STRING SymboName;        //符号链接名
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
上传的附件:
2012-3-23 23:45
0
雪    币: 2323
活跃值: (4113)
能力值: ( LV12,RANK:530 )
在线值:
发帖
回帖
粉丝
17
支持,继续学习~~坚持~~
2012-3-24 01:42
0
雪    币: 114
活跃值: (26)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
18
熊猫大大怎么睡这么迟啊?小心身体啊,谢谢支持鼓励啊。
2012-3-24 06:54
0
雪    币: 71
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
继续下去

非常有前途
2012-3-24 08:41
0
雪    币: 274
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
支持,有前途呀
2012-3-24 09:20
0
雪    币: 113
活跃值: (100)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
21
thanks, 有点理解,还有点胡涂。
就我的理解,IRP只有一个,相当于一段地址空间,每个DO使用它的一小部分,从这个角度来讲,是不是越在上的DO使用的IRP的地址越高,越在下的DO使用的IRP的地址越低?
2012-3-25 10:42
0
雪    币: 113
活跃值: (100)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
22
为什么驱动加载创建设备的时候只是调用了IoCreateDevice,而驱动卸载却要遍历设备链表?设备对象之间的链表关系不是在创建设备对象的时候由驱动程序链接的?
2012-3-25 21:47
0
雪    币: 107
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
这个不错
有些东西其实很多人都忽视掉了
学会逆向和调试的话
学习驱动也效果比较好 赞一个
2012-3-25 22:57
0
雪    币: 1763
活跃值: (964)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
学习笔记。。。精华啊~
2012-3-25 23:05
0
雪    币: 114
活跃值: (26)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
25
驱动创建时并未产生任何设备,因此设备链表为空,在驱动加载运行时,即开始创建设备了,由驱动对象自动把设备放到设备链表里,而在卸载驱动时,你不知道这个驱动到底创建了多少个设备,你没有记录他,但是你知道他有个设备链啊,通过这个设备链就可以找到与他相关的所有设备,并将其一一从设备链中删除,清空设备链,驱动才能卸载完成。否则,设备没有删除,驱动卸载肯定会出问题的,虽然这里我们知道他只创建了一个设备,在其他场合就未必了。我想应该是这样的。
2012-3-26 00:23
0
游客
登录 | 注册 方可回帖
返回
//