一個驅動無法加載的分析

 
一個驅動無法加載的分析
客戶反饋一個問題,原工作很好的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):表示本驅動程序被禁用。
012都由系統加載,時機稍有差別,另一個文檔如此說的,個人覺得很對:
       0ntldr加載,1IO管理器加載,2系統啓動後由SCM自動加載
那我這裏upper.sys3,誰把它加載的呀?想不清,看官是不是和在下一樣迷茫呢?什麼你知道,你厲害,水平比俺高呢,反正我當時是不知道哩。不過後面弄明白了(正確與否,還請指正),小疑問一冒出水面。欲知爲何,請看下文。
帶着疑問,繼續往下走吧。回到前面的安裝了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,退出了,百思不得其解呀。誰發出了UnloadIRP,那個進程發的?莫非是安裝的那個軟件?又是一個小疑問,姑且稱爲疑問二吧。看官你說啥原因呢?
       我找進程,如何找?搜了一把,網上來段代碼,很多,原理就是調用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,但得不到進程執行文件路徑,反過來一想,好像就算得到了也沒用。此路不通,手動加載不了,那開機系統加載行否呢?
把該驅動註冊表中start1後,重啓依然不行。看來只能深入系統加載驅動過程了,這樣就明白了。
動用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,選菜單Filekernel debug,在彈出的對話框中選第一個標籤頁面。Baud rate 寫上115200portcom1,確定。這時候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值無關同時,也可斷定UnloadIRPIO管理器主動發出的。
謹以此文紀念此次現在看來迂迴的、低效率的跟蹤過程。如果仔細對比註冊表,就不會有此次費神的跟蹤發生,但還是有些意外的小收穫,;)。
 
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章