您的位置:首页 > 其它

一个驱动无法加载的分析

2008-02-01 16:44 218 查看
一个驱动无法加载的分析
客户反馈一个问题,原工作很好的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()
{

DWORDdwAddress = (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管理器主动发出的。
谨以此文纪念此次现在看来迂回的、低效率的跟踪过程。如果仔细对比注册表,就不会有此次费神的跟踪发生,但还是有些意外的小收获,;)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐