Archive: Error from CoInitializeSecurity (WMI)


Error from CoInitializeSecurity (WMI)
I borrowed the System plugin calls that F0rt wrote about and wrote my own program to access WMI. On my laptop at home (Vista 64) it works fine, and here at work (XP Pro) it works fine. But on our target systems (Win 2000 Pro) the very first system call fails.

I stripped the program down to some essentials, and it still fails. The routines (in the attached file) work on my XP desktop and fail with RPC_E_TOO_LATE (0x80010119) at the very first line of code. On other Win2k machines here, that call works but it fails later with a line that says failed ExecQuery 0xc0000005.

Is there anyone that can see why this fails (either error, or best, both errors)?

thanks,
Don


first one just means CoInitializeSecurity has already been called (this function is process wide and only works once) Maybe some kind of injected shell extension or something on the failing machines?

as far as the other one goes, what does WBEMTest.exe do on the failing machine?

as a final note, getting logical drives can be done without using WMI


When it doesn't fail on the ole32::CoInitializeSecurity, it makes it through the next 2 calls OK (ole32::CoCreateInstance, IWbemLocator->ConnectServer) but gets a 0xC0000005 returned by the call on line 92 to IWbemServices->ExecQuery. The program doesn't terminate, but I guess it means to COM-connected process terminated. I don't try to use the other value (a IEnumWbemClassObject) that it ought to return.

I'm home now, so I can't try any experiments or investigate the injected shell extension theory until tomorrow.

About other ways to get logical drives - are you suggesting there is an API for it? I want to find the logical drives, and for each one I want to know if it is a CD or removable drive, what size it is, and the current volume name (if there is media in it).

Don


if WBEMTest.exe works where IWbemServices->ExecQuery called by system.dll fails, there could be a bug in the COM handling or using wrong parameter somewhere. If it also fails with WBEMTest.exe, I'm guessing there is a problem with the COM default security on that machine

As far as native API goes, call GetLogicalDriveStrings() or GetDriveType() directly in a loop. GetVolumeInformation() gives you label, GetDiskFreeSpace[Ex]() the size. It should also be noted that a volume could be mounted in a subfolder somewhere and not have a dos style drive letter at all, for those, call FindFirstVolume+FindFirstVolumeMountPoint and friends IIRC


I misunderstood your earlier question asking what WBEMTEST.exe does on the failing machine. It works properly, I get successful data back from all the queries I'm using.


Thought I found a solution, but it still crashes
I read the MSDN article on Getting WMI Data from the Local Computer and saw a note that says on Win2000 you have to specify the default authentication credentials for a user by using a SOLE_AUTHENTICATION_LIST structure in the pAuthList parameter of CoInitializeSecurity .

I've tried to do that but my program crashes now on the call to CoInitializeSecurity. I know this isn't really a NSIS problem at this point, but hope that Anders, F0rt or someone can spot what I've done wrong. Here's the part that sets up the call to CoInitializeSecurity. I have tried it with and without the SOLE_AUTHENTICATION_SERVICE structure, it crashes either way. It runs fine on XP and Vista.

!define RPC_C_AUTHN_DEFAULT  0xFFFFFFFF
!define RPC_C_AUTHZ_DEFAULT 0xFFFFFFFF

${If} ${IsWin2000}
; allocate and initialize SOLE_AUTHENTICATION_INFO Structure
System::Call "*(i ${RPC_C_AUTHN_DEFAULT}, i ${RPC_C_AUTHZ_DEFAULT}, i 0)i.r2"
; allocate and initialize SOLE_AUTHENTICATION_LIST Structure
System::Call "*(i 1, i r2)i.r3"
; allocate and initialize SOLE_AUTHENTICATION_SERVICE Structure
System::Call "*(i ${RPC_C_AUTHN_DEFAULT}, i ${RPC_C_AUTHZ_DEFAULT}, i 0, i 0)i.r4"
StrCpy $5 1
${Else}
StrCpy $2 0
StrCpy $3 0
StrCpy $4 0
StrCpy $5 -1
${EndIf}

System::Call "ole32::CoInitializeSecurity( \
i 0, ; PSECURITY_DESCRIPTOR pSecDesc \
i r5, ; LONG cAuthSvc \
i r4, ; SOLE_AUTHENTICATION_SERVICE *asAuthSvc \
i 0, ; void *pReserved1 \
i ${RPC_C_AUTHN_LEVEL_DEFAULT}, ; DWORD dwAuthnLevel \
i ${RPC_C_IMP_LEVEL_IMPERSONATE}, ; DWORD dwImpLevel \
i r3, ; void *pAuthList \
i ${EOAC_NONE}, ; DWORD dwCapabilities \
i 0) i.r1"
TIA
Don

Your code looks correct to me.

Would it be possible for you to write a little app in C to test so we could rule out the system plugin? or alternatively debug WBEMTEST.exe and check the parameters it passes to CoInitializeSecurity

(Also, using comments like that in a plugin parameter might not be so smart, not sure if the script parser is able to handle it)

edit:
(this is on XP, not sure if wbemtest is the same on 2000)


0:000> bp ole32!CoInitializeSecurity
...
0:000> kd
0013febc 0013ff1c
0013fec0 01006a64 wbemtest!WinMain+0xc4
0013fec4 00000000
0013fec8 ffffffff << cAuthSvc (-1)
0013fecc 00000000
0013fed0 00000000
0013fed4 00000001 << they are passing RPC_C_AUTHN_LEVEL_NONE
0013fed8 00000003
0013fedc 00000000
0013fee0 00000000
0013fee4 00000000
0013fee8 00000000
0013feec 7c80b529 kernel32!GetModuleHandleA
0013fef0 00151f05
0013fef4 7c80b529 kernel32!GetModuleHandleA
0013fef8 00151f05
0013fefc 00000000
0013ff00 77c39d7a msvcrt!_initterm+0x13
0013ff04 00fbf758
0013ff08 0013ffc0

RPC_C_AUTHN_LEVEL_DEFAULT might equal RPC_C_AUTHN_LEVEL_NONE on a local machine, I don't know (And its documented that you need to pass those extra structs on 2k, so I'm sure its not going to fix it)

Thanks for the suggestions. You're right, the script parser does not like the comments embedded in the SYSTEM plugin call strings. I took those out after I spotted that.

I have pretty much duplicated (in NSIS code) the example MSDN showed, it runs on XP but only gets as far as the IWbemServices::ExecQuery call on Win2000. That call doesn't crash the app, but returns a 0xC0000005 error code.

The latest code is attached to this post.

I will look at trying to run the debugger on WbemTest.exe. Here is the call to CoInitializeSecurity that seems to work on both OS's.

; allocate and initialize SOLE_AUTHENTICATION_INFO
System::Call "*(i ${RPC_C_AUTHN_WINNT}, i ${RPC_C_AUTHZ_NONE}, i 0) i .r2"
; allocate and initialize SOLE_AUTHENTICATION_LIST Structure
System::Call "*(i 1, i r2) i .r3"

System::Call "ole32::CoInitializeSecurity( \
i 0, i -1, i 0, i 0, i ${RPC_C_AUTHN_LEVEL_CALL}, \
i ${RPC_C_IMP_LEVEL_IMPERSONATE}, i r3, i ${EOAC_NONE}, i 0) i.r1"

Don

the WBEMTEST.exe version on windows 2000 does not have CoInitializeSecurity in its import table (it could still be calling it, but it is a bit weird to just not import it normally)

edit: So it does end up calling CoInitializeSecurity after GetProcAddress'ing it (why, I don't know, that function should exist on every version of windows so there is no point in delay loading it)


looking over your code some more, you are using "w" as the arguments in some places, those arguments are really BSTR's and BSTR != plain unicode string, you can use a BSTR where a unicode string is expected, but not the other way around and the system plugin does not have a BSTR parameter type AFAIK, you can make a proper BSTR with the SysAllocString() function


I made a little test app in c++, it works fine on XP but fails with WBEM_E_INVALID_CLASS (0x80041010) on IWbemServices->ExecQuery()

Not really sure where to go from here


Right, got it working now!

For whatever crazy reason, using CLSID_WbemAdministrativeLocator in the call to CoCreateInstance seems to fix things on 2000 (plain CLSID_WbemLocator works fine on XP)

I'll end with a few notes...


Anders, thanks for continuing work on my problem, and the suggestion that CLSID_WbemAdministrativeLocator might work for me on Win2000.

After a little playing around, I've got it working! I switched to CLSID_WbemAdministrativeLocator and commented out the CoSetProxyBlanket. The SerialNumber came back different (even though here at home I run Win2000 under VirtualBox), but that is accurate (according to what WBEMTest shows.

Then I tried a different query (Select * from Win32Bios) and property ('Name'), and after crashing the WMI repository a few times, it comes back with the BIOS name correctly. Hopefully the crash is just an artifact of the VirtualBox. XP continues to work correctly.

[A few minutes go by] Now I've incorporated those changes into my WMI.nsh, and my app runs in XP and Win2000! Thank you, Anders! I'll need to clean up WMI.nsh, but I plan to put it in the wiki when I've done that.

I'm in the middle of moving to a new apartment and may not be able to try on a true Win2000 system until either Monday or Tuesday.

Don


As far as crashes go, one thing you should fix is the w vs BSTR problem.

code like System::Call "$3->20(w 'WQL', w 'Select * from Win32_BIOS' ... is wrong, like I said, you can't use w, you need proper BSTR's (you can fake BSTR's with the system plugin struct syntax (on BSTR's you are going to free yourself) something like *{i 3,w "abc"}i.r0 but the pointer you must pass is not $0 but $0+4 (length is at -4) )