Here's an interesting lesson which, quite honestly, I haven't thought about for a while. But it turns out it's rather important.
A little over a month ago, I talked about a change to Windows Vista in SP1 regarding per-user COM registration , indicating that we changed the behavior and it would now work with UAC disabled. Let's dissect how this is implemented specifically. First, I disassembled the function, and then I focused in on the CALL instructions, to find:
0:010> uf ole32!OpenClassesRootKeyExW
251 760878ba e888ffffff call ole32!RegHelpSuspendImpersonate (76087847)
257 760878c5 ff15e4110676 call dword ptr [ole32!_imp__GetCurrentProcess (760611e4)]
257 760878cc ff159c160676 call dword ptr [ole32!_imp__OpenProcessToken (7606169c)]
261 760878e1 e8c9000000 call ole32!IsUserHiveOK (760879af)
265 760878fd ff1554160676 call dword ptr [ole32!_imp__RegOpenUserClassesRoot (76061654)]
269 760f9014 e8fe430100 call ole32!WPP_SF_d (7610d417)
283 76087922 ff15fc160676 call dword ptr [ole32!_imp__RegOpenKeyExW (760616fc)]
286 760f905b e8b7430100 call ole32!WPP_SF_d (7610d417)
290 76087935 ff15e4130676 call dword ptr [ole32!_imp__CloseHandle (760613e4)]
294 760f9065 ff150c140676 call dword ptr [ole32!_imp__GetLastError (7606140c)]
295 760f90a1 e871430100 call ole32!WPP_SF_d (7610d417)
298 7608793e e811010000 call ole32!RegHelpResumeImpersonate (76087a54)
304 76087966 ff15fc160676 call dword ptr [ole32!_imp__RegOpenKeyExW (760616fc)]
307 760f90e3 e8dfd70100 call ole32!WPP_SF_Sdd (761168c7)
309 76087979 ff15f4160676 call dword ptr [ole32!_imp__RegCloseKey (760616f4)]
That ole32!IsUserHiveOK looks interesting, so let's chase that down and see what we find, again just focusing in on the call statements:
0:010> uf ole32!IsUserHiveOK
200 760879cc e83c000000 call ole32!IsElevatedToken (76087a0d)
205 760879e5 e83ae2ffff call ole32!GetTokenElevationType (76085c24)
221 760941e8 e81a000000 call ole32!IsUIAccessToken (76094207)
This is, indeed, interesting. We're checking IsElevatedToken, and then we're calling GetTokenElevationType. What can TokenElevationType tell us? Let's check MSDN:
TOKEN_ELEVATION_TYPE Enumeration
The TOKEN_ELEVATION_TYPE enumeration indicates the type of token linked to the token being queried by the GetTokenInformation function or set by the SetTokenInformation function.
Syntaxtypedef enum { TokenElevationTypeDefault = 1, TokenElevationTypeFull, TokenElevationTypeLimited } TOKEN_ELEVATION_TYPE ,
*PTOKEN_ELEVATION_TYPE; Constants TokenElevationTypeDefaultThe token does not have a linked token.
TokenElevationTypeFull
The token is linked to an elevated token.
TokenElevationTypeLimited
The token is linked to a limited token.
We can see how, using both elevation state and TokenElevationType, we can determine both the elevation state and if UAC is enabled or disabled. So, we've unraveled what's happening for COM.
But, almost immediately after posting, people began posting about exceptions to the rule, which we began investigating. The lesson here: OLE != OLE Automation. They are different things. And it turns out that OLE Automation doesn't have the fix we incorporated into COM itself. They're different code, incorporated into different DLLs, maintained by different teams. What does OLE Automation do? Let's take the same approach, disassembling the function and zeroing in on the calls it makes:
0:010> uf oleaut32!OpenClassesRootKeyExW
150 75a49f80 e8c7feffff call OLEAUT32!SuspendImpersonate (75a49e4c)
151 75a49f8b ff151412a475 call dword ptr [OLEAUT32!_imp__GetCurrentProcess (75a41214)]
151 75a49f92 ff157413a475 call dword ptr [OLEAUT32!_imp__OpenProcessToken (75a41374)]
155 75a49fad e8cafeffff call OLEAUT32!IsProcessElevated (75a49e7c)
159 75a5ab15 ff153413a475 call dword ptr [OLEAUT32!_imp__RegOpenUserClassesRoot (75a41334)]
173 75a49fd4 ffd3 call ebx
178 75a49fdc ff151012a475 call dword ptr [OLEAUT32!_imp__CloseHandle (75a41210)]
182 75a78782 ff15ec11a475 call dword ptr [OLEAUT32!_imp__GetLastError (75a411ec)]
185 75a49fe8 e842000000 call OLEAUT32!ResumeImpersonate (75a4a02f)
191 75a4a05e ffd3 call ebx
192 75a4a066 ff158013a475 call dword ptr [OLEAUT32!_imp__RegCloseKey (75a41380)]
Things look a bit different here - all we call is IsProcessElevated. If you're running as admin with UAC disabled, this returns true - and the function never calls in to determine the token elevation type. OLE Automation did not get the same update that COM did!
Now, from the Windows side of things, we already have a bug open to fix this, but since I spread the word that it's working and some of you have contacted me suggesting that it isn't, I wanted to make sure it was clear: COM was, indeed, fixed to work with per-user registrations and UAC disabled, but OLE Automation was not.
What can you do in the interim? These are the workarounds I have heard about so far:
Incidentally, for those of you who were perplexed that Live Mesh was released but didn't support running as admin with UAC disabled until Windows Vista SP1 ... what do you think the odds are that there are some per-user COM registrations in there? Hmm...