一个驱动无法加载的分析

 
一个驱动无法加载的分析
客户反馈一个问题,原工作很好的usb key设备,安装 NCT_2000_XP 后,运行测试程序找硬件,提示没找到。检查系统 %systemroot%/system32/dirvers 目录,驱动文件安详的躺在那里, %systemroot%/inf 下也有安装的 inf 文件,设备管理器里看到设备工作正常,这真邪门了。
为何usb key 驱动加载不了呢?此软件有管理 usb 设备的功能,但 U 盘能正常使用,先用 Total Uninstall 对比了此软件安装前后对注册表的影响 , 未果,(后面还用了 Regsnap )。
毫不犹豫,想到了先跟踪测试程序,看看哪里出错了。先说说该硬件驱动的架构,分二层, usb 层之上还有一层,负责接收用户具体的请求,把请求解密后再下发给 usb 驱动,此层包含在 upper.sys 文件里, usb 层由 usb.sys 负责再按照 usb1.1 协议发给硬件。
那就是说,要正常工作必需要加载这二个驱动,缺一不可,为了验证想法的正确性,卸掉 NCT_2000_XP 重装驱动,拔掉 Usb Key ,重启,打开 icesword 看内核模块,找到了 upper.sys 模块,这时插入 Usb Key ,运行测试程序,正常。
且慢, upper.sys 为何开机就加载了,看注册表,该驱动如下:
      名称
说明
Type
1
驱动程序的种类
Start
3
驱动程序的起始启动时间
ErrorControl
1
驱动装入失败的错误处理
Group
Extended Base
驱动程序的组名
DependOnGroup
 
所依赖的其他驱动程序
Tag
21
同组内驱动程序装入顺序
 
 
 
Start 各值含义如下:
 
 
0x0 (SERVICE_BOOT_START) :这个值指定本驱动程序应该由操作系统装入程序启动。一般的驱动程序不会采用本值,因为系统在这个时候几乎还没有启动,大部分系统尚不可用。
   0x1 (SERVICE_SYSTEM_START) :该值表示在操作系统装入后但同时初始化它自己时启动驱动程序。
   0x2 (SERVICE_AUTO_START) :该值表示在整个系统启动并运行后由服务控制管理器( SCM )装入。
   0x3 (SERVICE_DEMAND_START) :该值表示该驱动程序必须手工启动。可以通过控制面板的设备 applet 或者使用 WIN32 API 编程来启动。
0x4 (SERVICE_DISABLED) :表示本驱动程序被禁用。
0 1 2 都由系统加载,时机稍有差别,另一个文档如此说的,个人觉得很对:
       0 ntldr 加载, 1 IO 管理器加载, 2 系统启动后由 SCM 自动加载
那我这里 upper.sys 3 ,谁把它加载的呀?想不清,看官是不是和在下一样迷茫呢?什么你知道,你厉害,水平比俺高呢,反正我当时是不知道哩。不过后面弄明白了(正确与否,还请指正),小疑问一冒出水面。欲知为何,请看下文。
带着疑问,继续往下走吧。回到前面的安装了 NCT_2000_XP 的测试状态,用 icesword 观看内核模块,没找到 upper.Sys ,你小子不让我加载是把,那我通过 SCM 主动加载,看你如何为难我,大体代码如下
       scm = OpenSCManager ( NULL , NULL , SC_MANAGER_ALL_ACCESS );
if ( scm == NULL )
return( ERR_UNKNOWN );
   sc = OpenService ( scm , sysname , SERVICE_ALL_ACCESS );
       if ( sc == NULL )
       {
                     printf ( "/nOpenService error:%d /n" , GetLastError ());
                     retcode = ERR_UNKNOWN ;
       }
       if ( StartService ( sc , 0, NULL ) == 0)     
       {
                     retcode = ERR_UNKNOWN ;
                     printf ( "error:%d /n" , GetLastError ());
       }
       执行后 StartService 失败了,错误为驱动无法加载。看看驱动代码为何把, debugview 输出信息,发现驱动运行到了 Driverentry ,但 Adddevice 没执行就运行到 DriverUnload ,退出了,百思不得其解呀。谁发出了 Unload IRP ,那个进程发的?莫非是安装的那个软件?又是一个小疑问,姑且称为疑问二吧。看官你说啥原因呢?
       我找进程,如何找?搜了一把,网上来段代码,很多,原理就是调用 PsGetCurrentProcess 得到 EProcess, 再定位得到进程映射路径,或者用 PsGetCurrentProcessId 。代码先放在这里,以后可能有用。
PCWSTR GetCurrentProcessFileName()
{
 
       DWORD dwAddress = ( DWORD ) PsGetCurrentProcess ();
 
       if( dwAddress == 0 || dwAddress == 0xFFFFFFFF)
          {
          return NULL ;
          }
       dwAddress += 0x1B0;
       if(( dwAddress = *( DWORD *) dwAddress ) == 0) return 0;
 
       dwAddress += 0x10;
       if(( dwAddress = *( DWORD *) dwAddress ) == 0) return 0;
 
       dwAddress += 0x3C;
              if(( dwAddress = *( DWORD *) dwAddress ) == 0) return 0;
KdPrint (( "Current Process Full Path Name: %ws/n" , ( PCWSTR ) dwAddress ));
 
       return ( PCWSTR ) dwAddress ;
 
}
里面有些硬编码,不同系统不一样。我这里针对 XPsp2 。其余系统用 windbg 跟踪便可知。
运行后,得到了 EProcess ,但得不到进程执行文件路径,反过来一想,好像就算得到了也没用。此路不通,手动加载不了,那开机系统加载行否呢?
把该驱动注册表中 start 1 后,重启依然不行。看来只能深入系统加载驱动过程了,这样就明白了。
动用 windbg 吧,还好有二台机器 , 一台作为被调试机,运行被调试程序 upper.Sys ,一台主控机运行 windbg ,不需要虚拟机来搭建调试环境。
1.       用串口线连接 2 台机器,用附件里的超级终端可相互发消息,确认连接正确。
2.       改变被调试机 boot.ini 文件启动设置。
添加一条目
multi(0)disk(0)rdisk(0)partition(5)/WINDOWS="Microsoft Windows XP Professional " /FASTDETECT /debug /debugport=com1 /baudrate=115200
 
3.       保存修改后,重启被调试机,同时主控机打开 windbg ,选菜单 File kernel debug, 在弹出的对话框中选第一个标签页面。 Baud rate 写上 115200 port com1 ,确定。这时候 windbg 进入等待连接状态。为了在操作系统启动时尽早中断 按热键 ctrl+alt+K ,在下次启动时,在 ntoskrnl 加载之后的一小段时间,这时所有驱动还没有被加载,操作系统将会挂起,而 WinDbg 将会取得控制权。在系统引导时间,这是下断点时机。
4.       被调试机进入启动系统菜单后,选择调试系统项,回车,这时主控机 windbg 应该有信息了。
5.       过一会, WinDbg 取得了控制权,下断点: bu upper!driverentry 和断点 bp upper!driverunload 然后按 F5 继续运行。
6.       再次中断, windbg 已经得到为我们把驱动代码给显示出来了, K 命令看堆栈,如下:
bad03838 805810ec Rockey4!DriverEntry+0x21 [e:/work/upper/src/32/upper/upper.c @ 62]
bad03908 8058f28f nt!IopLoadDriver+0x66c
bad0394c 805e6ba3 nt!PipCallDriverAddDeviceQueryRoutine+0x235
bad03998 805e6ed8 nt!RtlpCallQueryRegistryRoutine+0x3b1
bad039fc 80590b17 nt!RtlQueryRegistryValues+0x2a6
bad03ad0 80591fd4 nt!PipCallDriverAddDevice+0x261
bad03d2c 805924de nt!PipProcessDevNodeTree+0x1a4
bad03d54 804f7878 nt!PiProcessStartSystemDevices+0x3a
bad03d7c 805389bd nt!PipDeviceActionWorker+0x170
bad03dac 805cf84c nt!ExpWorkerThread+0xef
bad03ddc 8054632e nt!PspSystemThreadStartup+0x34
00000000 00000000 nt!KiThreadStartup+0x16
原来是被 IopLoadDriver 所加载,那 DriverEntry 后为何被卸载。看汇编代码:
 
nt!IopLoadDriver+0x6b1:
80581131 e87eb10000      call    nt!IopIsLegacyDriver (8058c2b4)
80581136 84c0            test    al,al
80581138 752c            jne     nt!IopLoadDriver+0x6e6 (80581166)
8058113a 8d4598          lea     eax,[ebp-68h]
8058113d 50              push    eax
8058113e ff758c          push    dword ptr [ebp-74h]
80581141 57              push    edi
80581142 e80f5cf7ff      call    nt!IopPnpDriverStarted (804f6d56)
0: kd> u
nt!IopLoadDriver+0x6c7:
80581147 3bc3            cmp     eax,ebx
80581149 8945ac          mov     dword ptr [ebp-54h],eax
8058114c 7d2c            jge     nt!IopLoadDriver+0x6fa (8058117a)
8058114e 8b4734          mov     eax,dword ptr [edi+34h]
80581151 3bc3            cmp     eax,ebx
80581153 7411            je      nt!IopLoadDriver+0x6e6 (80581166)
80581155 834f0801        or      dword ptr [edi+8],1
80581159 57              push    edi
0: kd> u
nt!IopLoadDriver+0x6da:
8058115a ffd0            call    eax here call Driverunload
8058115c 53              push    ebx
8058115d 8d45a4          lea     eax,[ebp-5Ch]
80581160 50              push    eax
80581161 e856f4ffff      call    nt!IopBootLog (805805bc)
80581166 395dac          cmp     dword ptr [ebp-54h],ebx
80581169 7d0f            jge     nt!IopLoadDriver+0x6fa (8058117a)
8058116b 57              push    edi
IopPnpDriverStarted 这个函数比较关键。失败了就调用 Driverunload 了,那 IopPnpDriverStarted 干了些啥工作。深入看看。
 
nt!IopPnpDriverStarted:
804f6d56 8bff            mov     edi,edi
804f6d58 55              push    ebp
804f6d59 8bec            mov     ebp,esp
804f6d5b 56              push    esi
804f6d5c 57              push    edi
804f6d5d 8b7d08          mov     edi,dword ptr [ebp+8]
804f6d60 33f6            xor     esi,esi
804f6d62 397704          cmp     dword ptr [edi+4],esi
804f6d65 752a            jne     nt!IopPnpDriverStarted+0x3b (804f6d91)
804f6d67 8b4510          mov     eax,dword ptr [ebp+10h]
804f6d6a 397004          cmp     dword ptr [eax+4],esi
804f6d6d 7422            je      nt!IopPnpDriverStarted+0x3b (804f6d91)
804f6d6f 56              push    esi
804f6d70 56              push    esi
804f6d71 50              push    eax
804f6d72 e8b7780900      call    nt!IopIsAnyDeviceInstanceEnabled (8058e62e)
804f6d77 84c0            test    al,al
804f6d79 7516            jne     nt!IopPnpDriverStarted+0x3b (804f6d91)
804f6d7b f6470808        test    byte ptr [edi+8],8
804f6d7f 7510            jne     nt!IopPnpDriverStarted+0x3b (804f6d91)
804f6d81 56              push    esi
804f6d82 ff750c          push    dword ptr [ebp+0Ch]
804f6d85 e84c620900      call    nt!IopDriverLoadingFailed (8058cfd6)
804f6d8a be5e0200c0      mov     esi,0C000025Eh
804f6d8f eb06            jmp     nt!IopPnpDriverStarted+0x41 (804f6d97)
804f6d91 57              push    edi
804f6d92 e81d6d0900      call    nt!IopDeleteLegacyKey (8058dab4)
804f6d97 5f              pop     edi
804f6d98 8bc6            mov     eax,esi
804f6d9a 5e              pop     esi
804f6d9b 5d              pop     ebp
804f6d9c c20c00          ret     0Ch
不复杂,调用 IopIsAnyDeviceInstanceEnabled 来看是否有此实例,没有就调用 IopDriverLoadingFailed ,表示失败了。继续跟踪 IopIsAnyDeviceInstanceEnabled ,发现访问了 HKLM/system/currentcontrolset/enum/ 下的东东,导致的失败,没仔细跟了,读者有兴趣可以继续深入。看来还是注册表得问题。
Regsnap 看看,前后对比发现是在 HKLM/system/currentcontrolset/enum/root 下少了东东,添加后,重启,加载正常。
原来 HKLM/system/currentcontrolset/enum/root 为非 pnp 设备所用,系统启动后 IO 管理器会加载该下的驱动,而与 start 值无关同时,也可断定 Unload IRP IO 管理器主动发出的。
谨以此文纪念此次现在看来迂回的、低效率的跟踪过程。如果仔细对比注册表,就不会有此次费神的跟踪发生,但还是有些意外的小收获,;)。
 
 
 
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值