This dllhost.exe writeup is a continuation / revisit of my original blog from 2020 titled What is the “DLLHOST.EXE” Process Actually Running. This time around the focus is code analysis from a reverse engineering perspective. Lets dive in!
| Default location |
|---|
| C:\Windows\System32\dllhost.exe |
| C:\Windows\SysWOW64\dllhost.exe |
Explaining Dllhost.exe's From Main#
The dllhost.exe binary itself is pretty small. It's just a wWinMain function that does the following:
- Parse the CLI to look for a colon
:. - If a colon exists and the text before it is
/ProcessID(case insensitive), use the text after the colon. - Otherwise, use the command line text itself as the GUID string.
- Pass the selected string to
IIDFromString. - If GUID parsing succeeds, call
CoInitializeEx. - Call
CoRegisterSurrogateEx(&processGuid, 0).
So the two useful command line shapes are:
dllhost.exe {GUID}dllhost.exe /ProcessID:{GUID}(case insensitiveProcessID)
Here is a snippet from IDA pseudocode (beautified for readability) showing the initial parsing loop looking for the :, and /ProcessID:
currentChar = commandLineBuffer[0];
for ( optionEnd = commandLineBuffer; ; ++optionEnd )
{
if ( !currentChar )
goto useWholeCommandLine;
guidTextAfterColon = optionEnd + 1;
if ( currentChar == ':' )
break;
currentChar = *guidTextAfterColon;
}
*optionEnd = 0;
guidTextSource = (char *)(optionEnd + 1);
if ( !*guidTextAfterColon || (unsigned int)_o__wcsicmp(commandLineBuffer, L"/ProcessID") )
useWholeCommandLine:
guidTextSource = (char *)commandLineBuffer;The GUID is then copied into a buffer and passed to IIDFromString.
if ( IIDFromString(guidTextBuffer, &processGuid) >= 0 )
{
...
}If that succeeds, dllhost.exe initializes COM and registers the process as a surrogate, and then waits :)
if ( CoInitializeEx(nullptr, 0) >= 0 )
{
CoRegisterSurrogateEx(&processGuid, 0);
CoUninitialize();
CurrentProcess = GetCurrentProcess();
TerminateProcess(CurrentProcess, 0);
}As we can see, dllhost.exe is not doing much on its own. It just registers itself as a COM surrogate with the parsed GUID, and then waits for COM to do the real work of activating classes in it.
This btw explains why it cannot be used as a direct LOLBin, since it only does the registration. So its execution is an indicator of COM surrogate activity, but it's not the one initiating that activity.
Special GUID Policy Branch#
There is one GUID-specific policy branch before the call to CoRegisterSurrogateEx.
If the COMSysAppRedirectionTrustPolicy feature is enabled and the parsed GUID matches:
02d4b3f1-fd88-11d1-960d-00805fc79235then dllhost.exe checks whether the token is elevated. If it is elevated, it applies a process mitigation policy before continuing into COM surrogate registration.
if ( (unsigned __int8)wil::details::FeatureImpl<__WilFeatureTraits_Feature_COMSysAppRedirectionTrustPolicy>::__private_IsEnabled(...)
&& *(_QWORD *)&processGuid.Data1 == 0x11D1FD8802D4B3F1LL
&& *(_QWORD *)processGuid.Data4 == 0x3592C75F80000D96LL )
{
TokenInformation = 0;
returnLength = 4;
if ( !GetTokenInformation((HANDLE)0xFFFFFFFFFFFFFFFCLL, TokenElevation, &TokenInformation, 4u, &returnLength) )
return wil::details::in1diag3::Win32_Return_GetLastError(...);
if ( TokenInformation )
{
mitigationPolicyEnabled = 1;
if ( !(unsigned int)SetProcessMitigationPolicy(16, &mitigationPolicyEnabled, 4) )
return wil::details::in1diag3::Win32_Return_GetLastError(...);
}
}Appendix: Registry Surrogate Configuration And -Embedding#
In order to know when dllhost.exe will be chosen as the COM surrogate, we investigate the following registry keys:
HKCR\AppID\{AppID}\DllSurrogate
HKLM\SOFTWARE\Classes\AppID\{AppID}\DllSurrogateSome important notes about these keys:
AppID DllSurrogate value | COM behavior |
|---|---|
| Missing | No surrogate is selected from this value. |
| Empty string | Use the default system surrogate: DllHost.exe /Processid:{AppID}. |
| Non-empty string | Use that custom surrogate executable. COM builds <surrogate path> {CLSID} -Embedding. |
That means the GUID passed to default dllhost.exe is the AppID:
DllHost.exe /Processid:{AppID}For a custom surrogate, its whatever executable + the CLSID of the class being activated + -Embedding:
<custom surrogate path> {CLSID} -EmbeddingI encourage you to check out,
CComProcessInfo::FinalConstructandCComClassInfo::InitializeSurrogateInfoincombase.dll, to see theDllSurrogateclassification and command consturction
Here is an example of what we just talked about. The following registry configuration sets up a custom surrogate for the AppID {1D278EEF-5C38-4F2A-8C7D-D5C13B662567}.
HKCR\CLSID\{E041C90B-68BA-42C9-991E-477B73A75C90}
AppID = {1D278EEF-5C38-4F2A-8C7D-D5C13B662567}
HKLM\SOFTWARE\Classes\AppID\{1D278EEF-5C38-4F2A-8C7D-D5C13B662567}
DllSurrogate = \\?\C:\Windows\System32\SecurityHealth\10.0.29554.1001-0\SecurityHealthHost.exeThe running process shows the custom surrogate shape:
\\?\C:\Windows\System32\SecurityHealth\10.0.29554.1001-0\SecurityHealthHost.exe {E041C90B-68BA-42C9-991E-477B73A75C90} -Embedding