天天看點

驅動程式安裝之裝置協安裝器

    年前想把一個功能驅動和過濾驅動傳到wu上,但是因為過濾驅動和第三方廠商驅動的hardwareid值相同,直接傳到wu上後使用者一旦更新會有問題。一個同僚提到一個解決方案:把過濾驅動的hardwareid改成一個無關緊要的id,然後為功能驅動的inf檔案附加一個協安裝器(coinstaller),把他們一起傳到wu上。這樣,當使用者更新功能驅動時,通過coinstaller啟動過濾驅動安裝程式來加載過濾驅動。

    這樣說可能還是有點空洞,讓我用前面的SampleChar驅動來解釋同僚的方法。SampleChar是一個功能驅動,這個驅動安裝包中隻有sys/inf檔案(測試驅動不包含cat檔案):SampleChar.sys/SampleChar.inf,使用者通過inf來安裝功能驅動。以下是SampleChar.inf的内容:

[Version]
Signature="$WINDOWS NT$"
Class=System
ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318}
Provider=%ProviderName%
DriverVer=12/19/2016,15.52.17.267
CatalogFile=SampleChar.cat

[DestinationDirs]
DefaultDestDir = 12

[SourceDisksNames]
1 = %DiskName%,,,""

[SourceDisksFiles]
SampleChar.sys = 1,,

[Manufacturer]
%ManufacturerName%=Standard

[Standard]
%SampleChar_Desc%=SampleChar_Device, ROOT\Sample

[SampleChar_Device.NT]
CopyFiles=Drivers_Dir

[SampleChar_Device.NT.HW]
AddReg=SampleChar_Device.NT.AddReg

[SampleChar_Device.NT.AddReg]
HKR,,DeviceCharacteristics,0x10001,0x0100         
HKR,,Security,,"D:P(A;;GA;;;BA)(A;;GA;;;SY)"      

[Drivers_Dir]
SampleChar.sys

[SampleChar_Device.NT.Services]
AddService=SampleChar,%SPSVCINST_ASSOCSERVICE%,SampleChar_Service_Inst

[SampleChar_Service_Inst]
DisplayName    = %sampleChar.SVCDESC%
ServiceType    = 1               ; SERVICE_KERNEL_DRIVER
StartType      = 3               ; SERVICE_DEMAND_START 
ErrorControl   = 1               ; SERVICE_ERROR_NORMAL
ServiceBinary  = %12%\SampleChar.sys
LoadOrderGroup = Extended Base

[Strings]
ManufacturerName="Eugen"
ClassName=""
DiskName="SampleChar Source Disk"
ProviderName="Eugen"
SampleChar_Desc="SampleChar"
sampleChar.SVCDESC="Sample char driver"
SPSVCINST_ASSOCSERVICE= 0x00000002      

    為了能在安裝SampleChar.sys過程中運作裝置協安裝器,就需要修改inf檔案的内容,為drvinst.exe(windows驅動安裝程式)指明安裝過程中用到的協安裝器檔案名及其入口點。

[Version]
Signature="$WINDOWS NT$"
Class=System
ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318}
Provider=%ProviderName%
DriverVer=2016/11/18, 1.0.0
CatalogFile=SampleChar.cat

[DestinationDirs]
DefaultDestDir = 12
CoInstaller_CopyFiles=11 ;協安裝器檔案拷貝相關内容

[SourceDisksNames]
1 = %DiskName%,,,""

[SourceDisksFiles]
SampleChar.sys = 1,,
ConInstall.dll=1,, ;協安裝器源檔案清單

[Manufacturer]
%ManufacturerName%=Standard

[Standard]
%SampleChar_Desc%=SampleChar_Device, ROOT\Sample

[SampleChar_Device.NT]
CopyFiles=SampleChar_Device.NT.Copy

[SampleChar_Device.NT.HW]
AddReg=SampleChar_Device.NT.HW.AddReg

[SampleChar_Device.NT.HW.AddReg]
HKR,,DeviceCharacteristics,0x10001,0x0100         
HKR,,Security,,"D:P(A;;GA;;;BA)(A;;GA;;;SY)"      

[SampleChar_Device.NT.Copy]
SampleChar.sys

[SampleChar_Device.NT.Services]
AddService=SampleChar,%SPSVCINST_ASSOCSERVICE%,SampleChar_Service_Inst

[SampleChar_Service_Inst]
DisplayName    = %sampleChar.SVCDESC%
ServiceType    = 1               ; SERVICE_KERNEL_DRIVER
StartType      = 3               ; SERVICE_DEMAND_START 
ErrorControl   = 1               ; SERVICE_ERROR_NORMAL
ServiceBinary  = %12%\SampleChar.sys
LoadOrderGroup = Extended Base

[SampleChar_Device.NT.CoInstallers] ;裝置協安裝器安裝節
AddReg=CoInstaller_AddReg
CopyFiles=CoInstaller_CopyFiles

[CoInstaller_CopyFiles]
ConInstall.dll

[CoInstaller_AddReg]
HKR,,CoInstallers32,0x00010000,"ConInstall.dll,CoInstaller" ;指明協安裝器檔案名和入口函數名      

    僅僅有inf檔案還不夠,還需要提供dll形式的協安裝器并導出函數名。以下是我自定義的協安裝器,其目的是在驅動安裝過程中建立notepad子程式。

DWORD CALLBACK CoInstaller(DI_FUNCTION DifCode, 
                            HDEVINFO devInfoset,
                            PSP_DEVINFO_DATA devInfoData,
                            PCOINSTALLER_CONTEXT_DATA Context)
{
    FILE* fp = NULL;
    STARTUPINFO si = {sizeof(si)};
    PROCESS_INFORMATION pi;
    
    _asm int 3;
    
    switch(DifCode)
    {
    case DIF_INSTALLDEVICE:
        CreateProcess(NULL,
                    "notepad",
                    NULL,
                    NULL,
                    FALSE,
                    CREATE_NEW_CONSOLE,
                    NULL,
                    NULL,
                    &si,
                    &pi);
        DbgOut("DIF_INSTALLDEVICE");
        break;

    default:
        DbgOut("?????");
        break;
    }
    
    return NO_ERROR;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpReserved)
{
    _asm int 3;
    return TRUE;
}      

   函數本身很簡單,但需要注意的地方有很多:

1.函數的接口形式。裝置協安裝器的接口形式和類安裝器的接口形式有點相像,

類安裝器的接口形式:

DWORD CALLBACK ClassInstaller(DI_FUNCTION,HDEVINFO,PSP_DEVINFO_DATA);      

如果inf檔案中用到了[classinstall32]節,那麼就需要以這種形式提供類安裝器。

本文提到的裝置協安裝器的接口形式,比類安裝器多一個參數:

DWORD CALLBACK DevInstaller(DI_FUNCTION,HDEVINFO,PSP_DEVINFO_DATA,PCOINSTALLER_CONTEXT_DATA);      

如果inf檔案中用到了[.CoInstallers]節,就需要提供這種形式的裝置安裝器。如果不加以區分,會導緻安裝失敗。

2.函數導出名字。一般大家都會用vs編譯生成協安裝器DLL,預設導出的函數名是c++形式的,即_func@nXYZ的形式。會和inf檔案中指定的函數名不一緻,導緻無法安裝成功。是以建議大家在dll生成後,用dependence工具檢視一下函數名是否和預期的一緻。

3.調用時機。驅動安裝時,會加載裝置安裝器,這是會調用dll的通用接口----DllMain;之後,協安裝器會響應SETUPAPI!SetupDiCallClassInstaller發出的各種DI_FUNCTION功能碼,進入協安裝器入口。就是這裡的CoInstaller函數:

DWORD CALLBACK CoInstaller(DI_FUNCTION,HDEVINFO,PSP_DEVINFO_DATA,PCOINSTALLER_CONTEXT_DATA);      

這可以在協安裝器中增加int 3斷點來觀察。首次運作devcon.exe安裝SampleChar時,windbg會遇到int 3異常。

Break instruction exception - code 80000003 (first chance) ;第一次遇到int 3異常
001b:70d91a95 cc              int     3
kd> .symfix C:\symbols\w7RTMx86
kd> .sympath+ C:\studio\classinstaller\objchk_win7_x86\i386
Symbol search path is: srv*;C:\studio\classinstaller\objchk_win7_x86\i386
Expanded Symbol search path is: SRV*C:\symbols\w7RTMx86*http://msdl.microsoft.com/download/symbols;c:\studio\classinstaller\objchk_win7_x86\i386
kd> .reload /user ;因為現在是核心态調試,是以要手動加載使用者态調試符号
Loading User Symbols
.......................................
kd> kb ;通過函數調用棧,可以看到drvinst首先會用LdrLoadDll加載子產品,然後調用dll的DllMain函數
ChildEBP RetAddr  Args to Child              
0065df58 70d91d86 70d90000 00000001 00000000 ConInstall!DllMain+0x5 [c:\studio\classinstaller\coninstall.c @ 152] ;調用DllMain函數
0065dfb8 77ccaf24 70d90000 00000001 00000000 ConInstall!__DllMainCRTStartup+0xe1 [d:\5359\minkernel\crts\crtw32\dllstuff\crtdll.c @ 573]
0065dfd8 77ccfd2e 70d91f06 70d90000 00000001 ntdll!LdrpCallInitRoutine+0x14
0065e0cc 77cd01db 00000000 77b5b4fe 77cb70da ntdll!LdrpRunInitializeRoutines+0x26f
0065e238 77ccf5f9 0065e298 0065e264 00000000 ntdll!LdrpLoadDll+0x4d1
0065e26c 7607b8a4 00211ebc 0065e2ac 0065e298 ntdll!LdrLoadDll+0x92
0065e2a4 767f57c9 00000000 00000000 00000001 KERNELBASE!LoadLibraryExW+0x15a
0065e928 7680e414 ffffffff 00228e10 7680e480 SETUPAPI!GetModuleEntryPoint+0x2ca
0065e9ac 767f479a 00216150 0065fbf0 0021d214 SETUPAPI!pSetupDiGetCoInstallerList+0x275
0065eebc 767f44f8 00000020 00216150 0065fbf0 SETUPAPI!_SetupDiCallClassInstaller+0x742
0065ef04 004cf951 00000020 00216150 0065fbf0 SETUPAPI!SetupDiCallClassInstaller+0x4e
0065f95c 004d1246 00000080 00216150 0065fbf0 DrvInst!InstallSelectedDeviceDriver+0xbfd
0065fba8 004d14ba 00000080 00216150 0065fbf0 DrvInst!InstallSpecificDriver+0x152
0065fc10 004cbf0b 00000002 00000080 006a22c0 DrvInst!pHandleDeviceInstall+0x11a
0065fc64 76751174 001af9cc 0065fcb0 77ccb3f5 DrvInst!HandleDeviceInstallEntry+0x2f
0065fc70 77ccb3f5 001af9cc 77b5aa76 00000000 kernel32!BaseThreadInitThunk+0xe
0065fcb0 77ccb3c8 004cbedc 001af9cc 00000000 ntdll!__RtlUserThreadStart+0x70
0065fcc8 00000000 004cbedc 001af9cc 00000000 ntdll!_RtlUserThreadStart+0x1b
kd> lm
start    end        module name
004a0000 004e1000   DrvInst    (pdb symbols)          C:\symbols\w7RTMx86\DrvInst.pdb\6373CA6671B7426686B254FE79AC602F1\DrvInst.pdb         
70d90000 70d95000   ConInstall   (private pdb symbols)  c:\studio\classinstaller\objchk_win7_x86\i386\ConInstall.pdb      

當調用DllMain後,CoInstall開始等待并響應安裝管理子產品的SETUPAPI!SetupDiCallClassInstaller消息,

并且由于安裝管理子產品會多次發出DI_FUNCTION消息,裝置協安裝器的安裝入口會多次被調用,是以這裡需要妥善處理全局變量:

kd> g
Break instruction exception - code 80000003 (first chance)
ConInstall!CoInstaller+0x26:
001b:70d91766 cc              int     3
kd> kb
ChildEBP RetAddr  Args to Child              
0065e9ac 7680e2be 00000020 00216150 0065fbf0 ConInstall!CoInstaller+0x26 [c:\studio\classinstaller\coninstall.c @ 19] ;調用CoInstaller函數
0065eebc 767f44f8 00000020 00216150 0065fbf0 SETUPAPI!_SetupDiCallClassInstaller+0x95f
0065ef04 004cf951 00000020 00216150 0065fbf0 SETUPAPI!SetupDiCallClassInstaller+0x4e
0065f95c 004d1246 00000080 00216150 0065fbf0 DrvInst!InstallSelectedDeviceDriver+0xbfd
0065fba8 004d14ba 00000080 00216150 0065fbf0 DrvInst!InstallSpecificDriver+0x152
0065fc10 004cbf0b 00000002 00000080 006a22c0 DrvInst!pHandleDeviceInstall+0x11a
0065fc64 76751174 001af9cc 0065fcb0 77ccb3f5 DrvInst!HandleDeviceInstallEntry+0x2f
0065fc70 77ccb3f5 001af9cc 77b5aa76 00000000 kernel32!BaseThreadInitThunk+0xe
0065fcb0 77ccb3c8 004cbedc 001af9cc 00000000 ntdll!__RtlUserThreadStart+0x70
0065fcc8 00000000 004cbedc 001af9cc 00000000 ntdll!_RtlUserThreadStart+0x1b      

4.被裝置協安裝器調用啟動的可執行程式的賬戶。我初次調試時一直沒有看到notepad程式界面,是以懷疑程式是不是錯了。但CreateProcess每次都傳回TRUE。這就很疑惑了,notepad去哪了?