Driver 加载步骤

Driver 加载步骤

转载 2016年06月30日 10:50:02
http://blog.csdn.net/wzsy/article/details/51788483

1)当硬件连接到PC或从PC移除时,在硬件上会有信号跳变,BUS Driver会检测到器件的插入和移除,比如DVI的hotplug,再比如USB1.1的1.5K上拉。BUS Driver将调用IOInvalidateDevcieRelations,PNP Manager将知道BUS上的Device关系有所改变。

 

2)为了获得新的Device关系,PNP Manager将调用IRP_MN_QUERY_DEVICE_RELATIONS。BUS Driver将会回应其BUS关系(目录)。

 

3)一旦得知Device之间的关系,PNP Manager就能容易地确定哪些器件没有被初始化。IRP_MN_QUERY_ID将被发出来获得Device ID。

 

4) 一旦获得这些详细信息后,PNP Manager就能确定转载那一类driver了。于是利用INF文件来安装Driver

 

5) 接下来进入标准DriverEntry流程来初始化Driver,同时系统调用AddDevice来添加器件。

 

6) Device添加完毕后,PNP Manger通过发出IRP_MN_QUERY_RESOURCE_REQUIREMENTS。这个IRP让BUS DRIVER来描述各种资源请求,包括中断,IO端口地址,IO MEMORY地址,DMA通道等等。

 

7) PNP Manger针对BUS DRIVER描述的资源请求将资源分配给Driver,配置好Driver后,发出IRP_MN_START_DEVICE,接收到IRP后,Driver配置和连接一些内核资源,然后等待着被使用。

 

后面Driver的工作,与System的交互,靠IRP来进行。而user mode与Driver交互也是通过WINAPI->System->Driver;Driver->System->WINAPI来进行。

 

当一个WDM总线驱动程序为响应IRP_MN_QUERY_DEVICE_RELATIONS而列出新设备时,如果类型是BusRelations,它将创建一个物理设备对象(PDO)。

下列规则决定了一个总线驱动程序是调用IoCreateDevice还是调用IoCreateDeviceSecure来创建一个设备对象:

l         如果设备可以在原始模式(raw mode)下使用,那么必须调用IoCreateDeviceSecure。

l         如果设备不能在原是模式下使用,那么总线驱动程序既可以调用IoCreateDevice,也可以调用IoCreateDeviceSecure。当对于总线上的设备的默认系统安全性足够的时候可以调用IoCreateDevice;调用IoCreateDeviceSecure可以指定更严格的安全描述符。

 

 

1、I/O管理器(根总线)为总线生成并维护PDO,总线驱动为插入的设备生成并维护PDO(即下层设备驱动为上层设备生成并维护PDO);驱动程序中的AddDevice例程接受的参数PhysicalDeviceObject就是下层设备为其生成的PDO, AddDevice例程Create的只能是该设备的FDO。

2、总线驱动发现新设备插入后,通过调用IoInvalidateDeviceRelations告诉PnP管理器BusRelations改变(注意:IoInvalidateDeviceRelations中传入的DeviceObject是总线的PDO);PnP管理器发送IRP_MN_QUERY_DEVICE_RELATIONS查询BusRelations,此时总线设备的FDO为新设备生成PDO,并报告。

BTW:TOASTER总线驱动是在发现新设备插入后就生成子PDO,然后通知PnP管理器;然后PnP管理器进行查询。这与DDK中说明不一致,可能无关紧要??

3、当UnPlug动作发生时,如同Plug动作发生,总线驱动(其FDO) 通过调用IoInvalidateDeviceRelations告诉I/O管理器BusRelations改变(注意:IoInvalidateDeviceRelations中传入的DeviceObject是总线的PDO,不是此时UnPlug的设备PDO)

4、当设备发生Eject动作时,总线驱动(其FDO) 通过调用IoRequestDeviceEject告诉I/O管理器,此时IoRequestDeviceEject的参数PhysicalDeviceObject是该设备的PDO。

5、总线驱动程序需要处理两类PnP事件,一类发给总线FDO的PnP事件,一类是总线驱动为插入的设备生成的PDO的PnP事件。对于发给总线FDO的PnP事件,一般都需要继续下传,而对于发给为插入的设备生成的PDO的PnP事件则IoCompleteRequest。

6、每一个设备都有一个设备堆栈,在该设备堆栈里,代表该设备的PDO永远都处于最底层。总线的PDO与在总线上的设备PDO没有层次关系。但是总线驱动程序负责生成其发现的设备的PDO,设备提供商提供的驱动程序主要是该设备的FDO,FDO进行数据传输/功能作业时一般不需要与代表设备的PDO进行交互。

7、FiDO都是相对FDO而言,Upper FiDO表明该FiDO在设备堆栈中处于FDO的上面,Lower FiDO表明该FiDO在设备堆栈中处于FDO的下面;但所有的一切都处于PDO的上面。

8、PnP管理器不保证设备堆栈中的驱动加载顺序,但是它保证了调用AddDevice的顺序来确保Stack的正确。

9、如果设备处于Raw状态,则它的设备堆栈里只有PDO与总线驱动生成的FiDO

win7 x64部署和串口调试虚拟驱动toaster

http://bbs.csdn.net/topics/390728347
就是把kmdf\inc中public.h中的GUID_DEVINTERFACE_BUSENUM_TOASTER的GUID用GUID_DEVINTERFACE_TOASTER进行了替换,然后就正常了。
http://blog.csdn.net/lixiangminghate/article/details/51378483
    WDK7600自带的toaster驱动是个很好的学习驱动的案例,从总线驱动到App层,Class-install/Co-install都有涉及。本文主要涉及驱动部署和调试。
1.先说说调试的准备工作。一般调试驱动都是在虚拟机中进行的,这里也不例外,用windbg+vmware双击联调。通常开发阶段,Wdm驱动以inf形式安装并加载,加载后依次执行DriverEntry,AddDevice并响应IRP_MJ_PNP&IRP_MN_START事件。安装的同时,pnp管理器会复制文件及改写注册表,待下次重启,驱动在系统重启阶段会加载运行驱动。因此,在DriverEntry时加入断点尤为重要,要不然将错过这些函数的调试时机。在x86时代,加入口断点无非是代码中插入 asm int 3.但是,这么美好的时代已经过去了,x64驱动不支持嵌入汇编。取而代之,必须通过编写一个asm文件,在源码中以调用外部函数的形式,共5步来实现。1.1第一步实现int 3的汇编代码形如:

  1. .code
  2. asm_int3 proc
  3. int 3
  4.     ret
  5. asm_int3 endp
  6. end

1.2将这段代码保存为softbp.asm。然后用wdk自带的ml64编译,注意仅编译生成obj文件:

  1. ml64 /c softbp.asm

1.3接着是在C文件中调用这个函数,如在wdm/bus/busenum.c的DriverEntry处加入断点

  1. extern void asm_int3(); //<————-外部函数声明
  2. NTSTATUS
  3. DriverEntry (
  4.     __in  PDRIVER_OBJECT  DriverObject,
  5.     __in  PUNICODE_STRING RegistryPath
  6.     )
  7. /*++
  8. Routine Description:
  9.     Initialize the driver dispatch table.
  10. Arguments:
  11.     DriverObject – pointer to the driver object
  12.     RegistryPath – pointer to a unicode string representing the path,
  13.                    to driver-specific key in the registry.
  14. Return Value:
  15.   NT Status Code
  16. –*/
  17. {
  18.     asm_int3(); //<————–外部函数调用
  19.     Bus_KdPrint_Def (BUS_DBG_SS_TRACE, (“Driver Entry \n”));
  20. //
  21. // Save the RegistryPath for WMI.

1.4修改sources文件,添加对softbp.obj的链接:原本sources文件中TARGETLIBS的值为

  1. TARGETLIBS=  $(DDK_LIB_PATH)\wdmsec.lib \
  2.              $(DDK_LIB_PATH)\ntstrsafe.lib

修改为:

  1. TARGETLIBS=  $(DDK_LIB_PATH)\wdmsec.lib \
  2.              $(DDK_LIB_PATH)\ntstrsafe.lib \
  3.              softbp.obj

1.5运行build -ceZ编译链接整个toaster工程。2.得到驱动文件后就是部署阶段。win7 x64的部署也是够磨人的:bcdedit增加启动项(再次怀念xp的好,修改boot.ini就够了),禁用驱动签名,最后安装驱动。以下的步骤都在虚拟机中进行

2.1增加启动项:

2.1.1 以administrator权限下进入cmd,运行bcdedit命令设置端口COM1为调试端口, baudrate为115200(我已将虚拟机的打印机移除,打印机可耻的占用着COM1,并将串口的名字改为COM1)

  1. bcdedit /dbgsettings serial baudrate:115200 debugport:1

2.1.2 复制一个开机选项, 以进入OS的debug模式

  1. bcdedit /copy {current} /d DebugEnty

2.1.3 接着增加一个新的选项到引导菜单

  1. bcdedit /displayorder {current} {6426f429-17fc-11e6-bf64-ad656b9740aa}

{6426f429-17fc-11e6-bf64-ad656b9740aa}是2.1.2运行结束后生成的id值2.1.4 激活前面生成的debug项

  1. bcdedit /debug {6426f429-17fc-11e6-bf64-ad656b9740aa} on

最后上一张效果图,然后重启机器准备调试:

3.windbg链接。

开始串口连接前,要设置调试符号和windbg启动参数,我直接写了个脚本setenv.bat完成这两步,以后双击运行即可:

  1. ;set _NT_SYMBOL_PATH=SRV*C:\sym;用于设置调试符号的路径
  2. set _NT_SYMBOL_PATH=SRV*C:\sym;
  3. ;我的winbdg装在下列位置,后面的参数是串口调试参数
  4. “D:\WinDDK\7600.16385.1\Debuggers\windbg.exe” -b -k com:pipe,port=\\.\pipe\com_1,baud=115200,pipe

系统重启后双击setenv.bat,在虚拟机中选择DebugEntry 按F8进入高级启动项,然后选择Disable Driver Signature Enforcement

(注,在等待虚拟机启动期间,经常出现windbg闪退,这时再双击setenv.bat即可。)

4.部署toaster

进入win7后,在控制面板中没有硬件向导的一席之地!需要通过在开始菜单-运行中输入hdwwiz.exe来打开。真是何其不易。ms出于安全目的,希望我们通过driver store安装受信得驱动,倒给开发驱动的找了一堆破事。安装toaster的步骤不再叙述,参照wdk中xp下安装方式在hdwwiz.exe中设置即可。

安装的末尾,也就是进入DriverEntry,由于我们在代码中设置了int3断点,因此windbg会获得执行权。这时可以追加并验证toaster的调试符号,因为此时驱动已经加载到内存中,因此设置断点不会报类似code not set之类的错误,而且符号也能正确加载。

  1. 追加toaster调试符号
  2. kd> .sympath+ C:\toaster\wdm\bus\objchk_win7_amd64\amd64
  3. Symbol search path is: SRV*C:\sym;C:\toaster\wdm\bus\objchk_win7_amd64\amd64
  4. 检验符号是否加载
  5. kd> lm m busenum*
  6. start             end                 module name
  7. fffff880`0392e000 fffff880`0393e000   busenum    (private pdb symbols)  c:\toaster\wdm\bus\objchk_win7_amd64\amd64\BusEnum.pdb
  8. 验证符号是否和sys版本一致
  9. kd> !itoldyouso busenum c:\toaster\wdm\bus\objchk_win7_amd64\amd64\busenum.sys
  10. busenum.sys
  11.     Timestamp: 57333596
  12.   SizeOfImage: 10000
  13.           pdb: c:\toaster\wdm\bus\objchk_win7_amd64\amd64\BusEnum.pdb
  14.       pdb sig: A8748D1B-8C93-4855-BBC7-4D773CADC3B0
  15.           age: 1
  16. Loaded pdb is c:\toaster\wdm\bus\objchk_win7_amd64\amd64\BusEnum.pdb
  17. BusEnum.pdb
  18.       pdb sig: A8748D1B-8C93-4855-BBC7-4D773CADC3B0
  19.           age: 1
  20. MATCH: BusEnum.pdb and busenum.sys
  21. 验证当前执行的位置是不是在busenum中
  22. kd> ln $ip
  23. (fffff880`039301a0)   busenum!asm_int3   |  (fffff880`039301c0)   busenum!__security_check_cookie
  24. Exact matches:

待一切验证完毕,就可以在AddDevice 等函数入口下断点。等下次系统在启动阶段时,会在这些断点处停下。如果想多次进入DriverEntry/AddDevice函数,可以在设备管理器中找到toaster设备,然后右键disable/enable设备,就能多次进入这些函数

版权声明:本文为博主原创文章,未经博主允许不得转载。
  • 本文已收录于以下专栏:
  • win内核

DDK样例toaster分析(2)

转载:http://blog.csdn.net/lixiangminghate/article/details/51705710

前一篇 DDK样例toaster分析(1) 主要讨论了toaster样例中的busenum总线驱动。本篇将讨论从总线驱动过渡到功能驱动,也就是toaster.sys。

成功安装busenum.sys后,运行toaster/exe/enum目录下的enum程序模拟一个toaster设备插入:

[cpp] view plain copy

  1. enum -p 1

前面busenum.sys!Bus_AddDevice创建Fdo的同时还为Fdo创建一个接口:

[cpp] view plain copy

  1. status = IoRegisterDeviceInterface (
  2.                 PhysicalDeviceObject,
  3.                 (LPGUID) &GUID_DEVINTERFACE_BUSENUM_TOASTER,
  4.                 NULL,
  5.                 &deviceData->InterfaceName);
  6. DEFINE_GUID (GUID_DEVINTERFACE_BUSENUM_TOASTER,
  7.         0xD35F7840, 0x6A0C, 0x11d2, 0xB8, 0x41, 0x00, 0xC0, 0x4F, 0xAD, 0x51, 0x71);
  8. //  {D35F7840-6A0C-11d2-B841-00C04FAD5171}

enum.exe通过SetupDi函数打开并通过ioctl访问这个接口:

[cpp] view plain copy

  1. hardwareDeviceInfo=SetupDiGetClassDevs(GUID_DEVINTERFACE_BUSENUM_TOASTER,…);
  2. SetupDiEnumDeviceInterfaces(hardwareDeviceInfo,…,&deviceInterfaceData);
  3. file = CreateFile ( deviceInterfaceDetailData->DevicePath,…);

调用Ioctl后,最终进入到Bus_Ioctl中,因为是插入设备,所以最终会调用Bus_PlugInDevice

[cpp] view plain copy

  1. NTSTATUS
  2. Bus_PlugInDevice (
  3.     PBUSENUM_PLUGIN_HARDWARE    PlugIn,
  4.     ULONG                       PlugInSize,
  5.     PFDO_DEVICE_DATA            FdoData
  6.     )

先看下FdoData中的内容,确定enum打开的是busenum.sys创建的Fdo:

[cpp] view plain copy

  1. kd> dd FdoData l1
  2. 0x81ec60e8
  3. kd> dt _FDO_DEVICE_DATA 81ec60e8
  4. busenum!_FDO_DEVICE_DATA
  5.     +0x01c UnderlyingPDO    : 0x823e73d0 _DEVICE_OBJECT
  6. kd> !devobj 0x823e73d0
  7. Device object (823e73d0) is for:
  8.  00000034 \Driver\PnpManager DriverObject 823eb2b0
  9. Current Irp 00000000 RefCount 1 Type 00000004 Flags 00001040
  10. Dacl e1594174 DevExt 823e7488 DevObjExt 823e7490 DevNode 823e7288
  11. ExtensionFlags (0000000000)
  12. AttachedDevice (Upper) 81ec6030 \Driver\busenum
  13. Device queue is not busy.

FdoData用UnderlyingPDO指出了堆叠在设备下面的设备。从windbg的输出看出,UnderlyingPDO就是busenum.sys所依赖的pnpmanager设备对象。

M$的帮助文档将busenum.sys!Bus_AddDevice创建的设备描述为Fdo,而将Bus_PlugInDevice创建的设备描述为Pdo。这是因为在Bus_PlugInDevice中创建出来的devobj代表了busenum总线对象,作为toaster.sys创建的设备对象的堆叠基石。另外也可以从windbg的输出看到,busenum新创建的设备对象并没有attach到任何已有设备对象上:
1).调用Bus_PlugInDevice!IoCreateDeviceSecure前,属于busenum.sys驱动对象的设备对象:

[cpp] view plain copy

  1. kd> !drvobj busenum
  2. Driver object (81dfdda0) is for:
  3.  \Driver\busenum
  4. Driver Extension List: (id , addr)
  5. Device Object list:
  6. 81ec6030 <—是attach在\Driver\PnpManager上的Fdo对象,上一张图中有描述
[cpp] view plain copy

  1. kd> !devstack 81ec6030
  2.   !DevObj   !DrvObj            !DevExt   ObjectName
  3. > 81ec6030  \Driver\busenum    81ec60e8
  4.   823e73d0  \Driver\PnpManager 823e7488  00000034
  5. !DevNode 823e7288 :
  6.   DeviceInst is “Root\UNKNOWN\0000”
  7.   ServiceName is “busenum”

上图中,busenum.sys只有一个设备对象
2).调用IoCreateDeviceSecure后,busenum.sys会新增一个设备对象:

[cpp] view plain copy

  1. kd> !drvobj busenum
  2. Driver object (81dfdda0) is for:
  3.  \Driver\busenum
  4. Driver Extension List: (id , addr)
  5. Device Object list:
  6. 82062408<—新加的设备对象  81ec6030 <—原有的设备对象
  7. kd> !devobj 82062408
  8. Device object (82062408) is for:
  9.  0000008a \Driver\busenum DriverObject 81dfdda0
  10. Current Irp 00000000 RefCount 0 Type 0000002a Flags 000000c0
  11. Dacl e23ad84c DevExt 820624c0 DevObjExt 820624f8
  12. ExtensionFlags (0000000000)
  13. Device queue is not busy.

从windbg输出可以看到,这个新的设备对象下面并没有attach其他设备对象

之后,busenum为这个新创建的设备对象创建HardwareID(总线设备的职责不就是为新加入的设备分配设备ID吗?)

[cpp] view plain copy

  1. pdoData->HardwareIDs =
  2.             ExAllocatePoolWithTag (NonPagedPool, length, BUSENUM_POOL_TAG);
  3. RtlCopyMemory (pdoData->HardwareIDs, PlugIn->HardwareIDs, length);
  4. 后分配busenum pdo的HardwardID
  5. kd> dd pdoData l1
  6. b21fdbec  820624c0
  7. kd> dt _PDO_DEVICE_DATA  820624c0
  8. busenum!_PDO_DEVICE_DATA
  9.     +0x020 HardwareIDs      : 0x8213aa50  -> 0x7b
  10. kd> du 0x8213aa50 <——注意,这是busenum.sys为pdo对象设置的硬件id,之后要按这个id匹配功能驱动
  11. 8213aa50  “{B85B7C50-6A01-11d2-B841-00C04FA”
  12. 8213aa90  “D5171}\MsToaster”

最后,Bus_PlugInDevice通过IoInvalidateDeviceRelations,通知pnp管理器:有新的设备加入设备树,pnp管理器要更新设备树关系(说人话,就是为新设备加载匹配的驱动)

由于系统中并没有安装这个Pdo的驱动,因此会跳出搜索驱动的对话框,这里选择手动安装,选择toaster/inf/sample.inf文件。至于为什么要选这个文件,因为sample.inf文件的硬件兼容列表里表示支持这个设备:

[cpp] view plain copy

  1. [Manufacturer]
  2. %StdMfg%=Standard
  3. [Standard]
  4. ; DisplayName               Section           DeviceId
  5. ; ———–               ——-           ——–
  6. %ToasterDevice.DeviceDesc%=Toaster_Device, {b85b7c50-6a01-11d2-b841-00c04fad5171}\MsToaster <—硬件兼容列表

这个列表中的内容完全匹配新创建设备对象的HardwareID,因此系统加载sample.inf中指定的sys文件,并创建对应的服务,最后把执行权限交给toaster.sys:

[cpp] view plain copy

  1. [Version]
  2. Signature=“$WINDOWS NT$”
  3. Class=TOASTER
  4. ClassGuid={B85B7C50-6A01-11d2-B841-00C04FAD5171}
  5. Provider=%MSFT%
  6. DriverVer=09/21/2006,6.0.5736.1
  7. CatalogFile=toaster.cat
  8. [Toaster_Device.NT]
  9. CopyFiles=Toaster_Device.NT.Copy
  10. [Toaster_Device.NT.Copy]
  11. toaster.sys

驱动安装后,设备管理器里多出一个toaster设备,查看Driver Detail可以看到驱动文件的信息:


扯点题外话,可以通过Update Driver替换新的inf文件,比如选择toasterf.inf(含设备过滤驱动的toaster.sys),并指定sys文件路径后,再查看Driver Detail可以看到功能驱动和过滤驱动:(tmd公司要做驱动测试,测试老问我加载过滤驱动后,怎么在设备管理器里看到!还一定要在设备管理器里看到,注册表里不算,我折腾很久才发现这个!!)

当然这种更新操作要底层设备支持disable/enable,像磁盘设备的,不支持disable/enable操作就不能这样更新驱动(这要感谢同事周AM的解释)
好好,扯远了,回到正题,执行权从busenum.sys过渡到toaster.sys。当然了进入toaster.sys后还会调用toaster的AddDevice函数ToasterAddDevice,因此要在这个函数上下断:

[cpp] view plain copy

  1. DriverEntry
  2. kd> !drvobj 8216db10
  3. Driver object (8216db10) is for:
  4.  \Driver\toaster
  5. Driver Extension List: (id , addr)
  6. Device Object list:
[cpp] view plain copy

  1. kd> bp toaster!ToasterAddDevice;g
  2. kd> dd DriverObject l1
  3. f8aed9e8  8216db10
  4. kd> !drvobj 8216db10
  5. Driver object (8216db10) is for:
  6.  \Driver\toaster
  7. Driver Extension List: (id , addr)
  8. Device Object list:

刚进入ToasterAddDevice时,驱动对象下还没有设备对象。顺带看下ToasterAddDevice的接口

[cpp] view plain copy

  1. NTSTATUS
  2. ToasterAddDevice(
  3.     __in PDRIVER_OBJECT DriverObject,
  4.     __in PDEVICE_OBJECT PhysicalDeviceObject
  5.     )

DriverObject不用说肯定是toaster.sys的,PhysicalDeviceObject,则是前面busenum创建的pdo了(由于刚才更新驱动时停用启用过设备,设备对象跟上文不能连续了,不过还是能通过!drvobj !devstack来观察结果):

[cpp] view plain copy

  1. kd> dd PhysicalDeviceObject l1 ;<—PhysicalDeviceObject的值
  2. f8ae99ec  82062408
  3. kd> !devstack 82062408  ;<—通过设备堆栈证明这个是前面busenum创建的Pdo
  4.   !DevObj   !DrvObj            !DevExt   ObjectName
  5. > 82062408  \Driver\busenum    820624c0  0000008a ;<—这个设备对象下面并没有attach其他设备,跟上文的结论一下,因此可以认为是Pdo
  6. !DevNode 82266878 :
  7.   DeviceInst is “{B85B7C50-6A01-11d2-B841-00C04FAD5171}\MsToaster\1&1aafb3d5&0&01”
  8.   ServiceName is “toaster”
  9. kd> !drvobj busenum  ;<—当然,有人不信,那只能列出busenum驱动对象下的设备对象
  10. Driver object (81dfdda0) is for:
  11.  \Driver\busenum
  12. Driver Extension List: (id , addr)
  13. Device Object list:
  14. 82062408  81ec6030  <—这个驱动对像目前只有2个设备对象,为了证明82062408对象是Pdo,只要证明81ec6030  是Fdo就行了,剩下的靠排除法就能证明
[cpp] view plain copy

  1. kd> !devstack 81ec6030 ;<—81ec6030的设备栈显示,这个就是Fdo,attach在pnpmanager设备对象上
  2. !DevObj !DrvObj !DevExt ObjectName
  3. > 81ec6030 \Driver\busenum 81ec60e8 823e73d0 \Driver\PnpManager 823e7488 00000034!DevNode 823e7288 : DeviceInst is “Root\UNKNOWN\0000” ServiceName is “busenum”

 


之后,Toaster.sys调用IoCreateDevice创建功能驱动(注意是toaster.sys的Fdo不是busenum.sys的Fdo),把这个Fdo堆叠到busenum.sys的Pdo上:

[html] view plain copy

  1. status = IoCreateDevice (DriverObject,
  2.                              sizeof (FDO_DATA),
  3.                              NULL,
  4.                              FILE_DEVICE_UNKNOWN,
  5.                              FILE_DEVICE_SECURE_OPEN,
  6.                              FALSE,
  7.                              &deviceObject);
  8. fdoData->NextLowerDriver = IoAttachDeviceToDeviceStack (deviceObject,
  9.                                                        PhysicalDeviceObject);

这样就完成了从busenum到toaster的过渡。

DDK样例toaster分析(1)

转载:http://blog.csdn.net/lixiangminghate/article/details/51700357

 

 

msddk样例中toaster是一个比较完整和值得学习的pnp驱动,包括了一个完整的设备栈(含总线驱动/功能驱动,以及设备栈中各层过滤驱动)和驱动安装程序(含驱动安装包/类安装程序/协安装程序)。本篇记录在xp下调试busenum驱动入口(关于环境的搭建网上一搜一大把,这里就省了,当然也可以参考这篇:win7 x64部署和串口调试虚拟驱动toaster)。

通过添加硬件的方式安装busenum.sys后,会依次进入busenum.c!DriverEntry和pnp.c!Bus_AddDevice。AddDevice是本篇的重点,因此进入DriverEntry后在这个函数上下一个断点:

[cpp] view plain copy

  1. kd> lmvm busenum //查看驱动和调试符号加载是否正确
  2. start    end        module name
  3. b20e9000 b20f2b80   busenum    (private pdb symbols)  d:\winddk\7600.16385.1\src\general\toaster\wdm\bus\objchk_wxp_x86\i386\BusEnum.pdb
  4.     Loaded symbol image file: busenum.sys
  5. kd> !itoldyouso busenum
  6. busenum.sys
  7.     Timestamp: 57639546
  8.   SizeOfImage: 9B80
  9.           pdb: d:\winddk\7600.16385.1\src\general\toaster\wdm\bus\objchk_wxp_x86\i386\BusEnum.pdb
  10.       pdb sig: A0EB3691-6995-416E-9BF4-C25A160C9318
  11.           age: 1
  12. Loaded pdb is d:\winddk\7600.16385.1\src\general\toaster\wdm\bus\objchk_wxp_x86\i386\BusEnum.pdb
  13. BusEnum.pdb
  14.       pdb sig: A0EB3691-6995-416E-9BF4-C25A160C9318
  15.           age: 1
  16. MATCH: BusEnum.pdb and busenum.sys

确定sys文件加载正确后,查找函数并下断点,然后继续运行:

[cpp] view plain copy

  1. kd> x busenum!Bus_AddDevice
  2. b20eca60 busenum!Bus_AddDevice (struct _DRIVER_OBJECT *, struct _DEVICE_OBJECT *)
  3. kd> bp busenum!Bus_AddDevice
  4. kd> bl
  5.  1 e b20eca60     0001 (0001) busenum!Bus_AddDevice

当windbg进入Bus_AddDevice遇到断点时,函数有两个参数:PDRIVER_OBJECT DriverObject和PDEVICE_OBJECT PhysicalDeviceObject。DriverObject很好理解,就是busenum.sys,有windbg为证:

[cpp] view plain copy

  1. kd> dd DriverObject l1
  2. f8ae99e8  8227a3b8
  3. kd> !drvobj 8227a3b8
  4. Driver object (8227a3b8) is for:
  5.  \Driver\busenum

PhysicalDeviceObject是什么 呢?如果是pnp设备驱动,那很简单,这就是底层总线驱动对应的设备。但busenum本身是总线驱动,谁提供底层设备?答案是pnp管理器

[cpp] view plain copy

  1. kd> dd PhysicalDeviceObject l1
  2. f8ae99ec  81f41030
  3. kd> !devobj 81f41030 —> 这个设备是pnp创建的设备
  4. Device object (81f41030) is for:
  5.  00000082 \Driver\PnpManager DriverObject 823eb2b0 —> windbg指出,创建设备对象的驱动对象
  6. kd> !drvobj 823eb2b0
  7. Driver object (823eb2b0) is for:
  8.  \Driver\PnpManager  —>驱动对象
  9. Driver Extension List: (id , addr)
  10. 该驱动对象的设备对象
  11. Device Object list:
  12. 81f41030  823e73d0  823e7610  823e7850

原来,pnp管理器为了维护一颗设备树,在加载总线驱动前会为总线设备创建一个pnpmanage设备对象,作为设备栈的栈底对象,让总线设备attach上去。在busenum.sys代码中也有这种设备栈的上下体现:

[cpp] view plain copy

  1. NTSTATUS
  2. Bus_AddDevice(
  3.     __in PDRIVER_OBJECT DriverObject,
  4.     __in PDEVICE_OBJECT PhysicalDeviceObject
  5.     )
  6. {
  7.     status = IoCreateDevice (
  8.                     DriverObject,…,&deviceObject);
  9.     deviceData->UnderlyingPDO = PhysicalDeviceObject;
  10.     deviceData->NextLowerDriver = IoAttachDeviceToDeviceStack (
  11.                                     deviceObject,
  12.                                     PhysicalDeviceObject);
  13. }

首先代码创建busenum.sys的设备对象,然后attach到pnpmanager设备对象上。难怪toaster的帮助文档中注释这个对象为Fdo,而把Bus_AddDevice的第二个参数认为是Pdo。

来看下创建设备后设备栈情况:

[cpp] view plain copy

  1. kd> !drvobj 8227a3b8
  2. Driver object (8227a3b8) is for:
  3.  \Driver\busenum
  4. Driver Extension List: (id , addr)
  5. ;相比刚进入DriverEntry时,现在busenum驱动对象下已经有一个设备对象了,这个就是调用IoCreateDevice时创建的
  6. Device Object list:
  7. 81f2a930

[cpp] view plain copy

  1. kd> dd deviceObject l1
  2. f8ae99dc  81f2a930 ;设备对象的地址
  3. kd> !devobj 81f2a930 ;验证一下设备对象的属主
  4. Device object (81f2a930) is for:
  5.   \Driver\busenum DriverObject 8227a3b8
  6. Current Irp 00000000 RefCount 0 Type 0000002a Flags 00000088
  7. DevExt 81f2a9e8 DevObjExt 81f2aa90
  8. ExtensionFlags (0000000000)
  9. Device queue is not busy.

[cpp] view plain copy

  1. kd> !devstack 81f2a930 ;81f2a930就是前面IoCreateDevice创建的设备对象
  2.   !DevObj   !DrvObj            !DevExt   ObjectName
  3.  > 81f2a930  \Driver\busenum    81f2a9e8
  4.   81f41030  \Driver\PnpManager 00000000  00000082
  5.   !DevNode 81f2a008 :
  6.   DeviceInst is “ROOT\UNKNOWN\0000” ;设备实例
  7.   ServiceName is “busenum”          ;设备对应的服务名

至此busenum的设备栈已经成形了,可以创建Fdo了。

在结束本文前,还有一些琐碎的东西需要记录。就是busenum.inf中DDinstall section

安装busenum驱动后,打开设备管理器,在system devices下可以看到设备对象。

如图所示,设备出现在system devices下,设备实例是ROOT\Unknow\0000。问题来了,为什么会这样?

首先,设备管理器是以类设备为视图,不同的设备类归入不同的类下(物以类聚)。因此出现在system devices下一定是刻意设定,那么这由谁来设定?当然是inf文件了,在version节中指定:下面的内容摘自bus.inf

[cpp] view plain copy

  1. [Version]
  2. Signature=“$WINDOWS NT$”
  3. Class=System ;指明busenum属于system类
  4. ClassGuid={4D36E97D-E325-11CE-BFC1-08002BE10318} ;类的guid
  5. Provider=%MSFT%
  6. DriverVer=09/21/2006,6.0.5736.1
  7. CatalogFile=toaster.cat

其次,硬件ID,由inf文件DDinstall节指定,msdn文档上一般将DDinstall节认为是从Manufacturer开始:

[cpp] view plain copy

  1. [Manufacturer]
  2. %StdMfg%=Standard
  3. [Standard]
  4. ; These are the toaster bus pnp ids
  5. %ToasterBus.DeviceDesc%=ToasterBus_Device, root\busenum ;root\busenum指定了设备的HardwareID

有了这两项,在结合驱动在注册表下的路径可以找到相应的信息:

先确定设备的总线关系,inf文件中已经指出了busenum属于root总线,它在注册表中的路径为:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\Root

设备实例ROOT\Unknow\0000表明,busenum在root总线的Unknow\0000下,在这个项中可以找到驱动的service和class信息:

1)通过ROOT\Unknow\0000索引驱动的service为busenum

在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\{SerivceName}下可以找到各种驱动注册的服务相关的注册表值:

对于busenum驱动ServiceName是busenum

ImagePath是驱动程序被安装的路径,这些信息在Inf文件中都有对应内容,

[cpp] view plain copy

  1. [ToasterBus_Device.NT.Services] ;这个节是服务安装节
  2. AddService = busenum,%SPSVCINST_ASSOCSERVICE%, busenum_Service_Inst

[cpp] view plain copy

  1. ;AddService对应驱动在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\下创建的服务名
  2. {4D36E97D-E325-11CE-BFC1-08002BE10318}
  3. ; ————– busenum driver install sections
  4. [busenum_Service_Inst]
  5. DisplayName    = %busenum.SVCDESC% ;<span style=“font-family: Arial, Helvetica, sans-serif;”>busenum.SVCDESC</span><span style=“font-family: Arial, Helvetica, sans-serif;”>=busenum.SVCDESC = “Toaster Bus Enumerator”对应注册表项DisplayName和在设备管理器上显示的名字</span>
  6. ServiceType    = 1               ; SERVICE_KERNEL_DRIVER
  7. StartType      = 3               ; SERVICE_DEMAND_START <span style=“font-family: Arial, Helvetica, sans-serif;”>;对应注册表的Start</span>
  8. ErrorControl   = 1               ; SERVICE_ERROR_NORMAL
  9. ServiceBinary  = %12%\busenum.sys                       ;对应注册表中ImagePath
  10. LoadOrderGroup = Extended Base

2)通过ROOT\Unknow\0000索引驱动的class信息

在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{ClassGuid}
对于busenum驱动ClassGuid是{4D36E97D-E325-11CE-BFC1-08002BE10318},挨个找过去可以找到busenum注册表项:

这里InfPath名为oem12.inf,是因为系统把所有inf文件拷到系统路径后(c:\windows\inf),统一命名为oemxx.inf形式。而InfSection应该对应了设备的DDInstall节,这个节可能是当系统重启时需要加载设备时到对应的oemxx.inf中寻找DDInstall进行驱动安装的依据