Short disclaimer: As this question includes topics regarding hacking/pentesting, I'd like to state that this question is only asked for educational purposes as part of a school project. To prevent possible abuse, I will only post code that is necessary for understanding the problem.
To demonstrate dangers and vulnerabilities of Windows 10, I'm currently writing a small C++/WinAPI application that utilizes two common techniques:
fodhelper.exe
", which will then read the registry value and execute it as command/launch the specified application).I managed to perform both of these techniques successfully in independent files. However, once I combine these two methods (i.e. first elevating, then injecting), a weird error appears.
When the injector gets started manually (by double clicking), everything works fine, but when the injector is launched by System32\fodhelper.exe
(x64) as a result of the UAC bypass, the following happens: After the injection has finished, the console window of the injected application appears, but instead of continuing the execution, I receive a bunch of error messages stating "The code execution cannot proceed because [garbage characters].dll was not found
". This indicates that something went wrong with the offsets, and the Windows loader is trying to read the imports at a wrong position.
To summarize: The code injection works fine, unless the injector was started by fodhelper.exe
. In this case the injected PE file is unable to run.
GetLastError
and printing the various memory addresses used during the injection. There is no difference if the file is manually started (and the injection is successful) or if it gets started by fodhelper.exe
(and the injection fails).WriteProcessMemory
calls with WriteFile
to compare the output file when the injector gets manually launched or by fodhelper.exe
. Both output files are exactly the same and runnable. This indicates that the injection itself is not the problem, but the Windows loader seems to act differently.fodhelper.exe
to another location (for example to the desktop) and launching this copy. In this case, the injection is successful. The injection only fails if the injector gets started by the original fodhelper.exe
in the System32
folder.It seems that the injection behaves completely identical, but the indicators show that due to some unknown impact of fodhelper.exe that gets passed down to the injector, the Windows loader seems to behave differently.
I appreciate any explanation or assumption! Feel free to ask if you require more information.
(with limited debug info and comments): https://0bin.net/paste/UPRIg12n#6nJvBok72UcDvIa56c-XEss7AibIh1Zrs+c3sUzvQMj
Note: See how the injection works if you exclude the elevateProcess
function or manually elevate the exe with UAC, and how it fails when including said function.
According to the answer by user RbMm, this error is a result of a specific exploit protection attribute (PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY
with the EnableModuleTamperingProtection
value) that gets automatically applied onto fodhelper.exe
and seemingly gets inherited by all child processes. According to this, removing/resetting this attribute when launching the target process should fix the error. So far I've tried the following, but couldn't achieve any change in the outcome:
DWORD resetPolicy = 0;
STARTUPINFOEXA siEx = { 0 };
SIZE_T AttributeListSize = 0;
siEx.StartupInfo.cb = sizeof(siEx);
InitializeProcThreadAttributeList(NULL, 1, 0, &AttributeListSize);
siEx.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, AttributeListSize);
InitializeProcThreadAttributeList(siEx.lpAttributeList, 1, 0, &AttributeListSize);
UpdateProcThreadAttribute(siEx.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &resetPolicy, sizeof(resetPolicy), NULL, NULL);
...
CreateProcessA(CurrentFilePath, NULL, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT | CREATE_SUSPENDED, NULL, NULL, (LPSTARTUPINFOA)&siEx, &PI);
when process created via RunAs with elevation - the appinfo.dll call RAiLaunchAdminProcess function (this is in some svchost.exe) and this function, pass STARTUPINFOEX
(and EXTENDED_STARTUPINFO_PRESENT
flag) to CreateProcessAsUser
. and here - lpAttributeList, in particular PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY
attribute key is used for set several exploit mitigation policy for the child process (fodhelper.exe in your case). and here EnableModuleTamperingProtection
is set for child process tree. effect of this - when system resolve import descriptor, it check (inside LdrpGetImportDescriptorForSnap) for this mitigation flag, and if it enabled - call LdrpCheckPagesForTampering
api, it return true, if SharedOriginal
is 0, this means this is a copy-on-write private copy of the EXE/IAT -- hence 'tampered' with.
after this LdrpMapCleanModuleView is called. at this point your try begin breaking
possible first public info about this, from Alex Ionescu -
LdrpCheckPagesForTampering/LdrpMapCleanModuleView (RS3) are pretty cool antihollowing mitigations (EPROCESS.EnableModuleTamperingProtection)
if you by self launch new process, you of course not call UpdateProcThreadAttribute
for set PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY
and in this case, your code sometime work. really only random and sometime - here exist many other errors and bad codding