Dll injection is one of the oldest techniques used to run custom code inside a target application in Windows. It is usually used to intercept and modify normal application behavior or add new functionality.
Injecting a DLL in a target process is a relatively easy task: you simply create a remote thread that calls LoadLibrary using CreateRemoteThread or NtCreateThreadEx. You will need some privileges to be able to access the injected process but that is beyond the scope of this article.
When you try to inject a library into a Windows 8 Modern UI Metro application you will find that although the injection code works as expected, your DLL will NOT load, LoadLibrary will return FALSE and GetLastError will return ERROR_ACCESS_DENIED.
Well, you think… Modern UI applications have very limited access to computer resources and run in a sandboxed environment, so problems are to be expected.
While doing some research on how to add new functionallity to the Windows Mail application that comes with Windows 8 and how to hook Modern UI apps using Deviare, we needed find out why LoadLibrary was failing.
Reverse engineering comes into play
We started to analyze what LoadLibrary does. It calls LoadLibraryEx with dwFlags=0 and LoadLibraryEx does some checks. First stop.
If you want to load a package you must use the LoadPackagedLibrary API. If you want to load a normal DLL, you have to use LoadLibrary[Ex]. LoadPackagedLibrary documentation says that the path cannot be absolute or contain “..” but these checks are mainly done in the LoadLibraryEx routine. The only difference between LoadLibrary and LoadPackagedLibrary is wether the dwFlags parameter has a value of 4 or of zero.
Among other things, LoadLibraryEx will build the search path to locate your DLL and then call the undocumented LdrLoadDll. Because we want to enforce using the path where our library is located, we changed our code to call LdrLoadDll directly.
Although LdrLoadDll correctly found our dll, when we used SpyStudio to check for errors, we saw that a call to NtOpenFile failed, reporting STATUS_ACCESS_DENIED. We realized that there was a security-related issue.
Using the icacls.exe utility, we set the DLL file privileges to allow read and execute access to low integrity processes. Also we added a special Windows 8 user named “ALL APPLICATION PACKAGES” to the list of users with permission to read and execute the DLL code.
NtOpenFile passed initial security checks, but our DLL was still not loading.
Continuing to research LdrLoadDll, we jumped into the kernel-mode of the “NtCreateSection” API and got that the “CiValidateImageHeader” function of ci.dll was returning a STATUS_INVALID_IMAGE_HASH error so we added a digital signature to the file. To prevent future problems, we used a real certificate instead of the self-signed one.
Now CiValidateImageHeader was ok, but a later call to “CiValidateImageData” returned the same error. We then added the “/ph” parameter to the SignTool.exe utility to include pages hashes in the signing process.
Well, we thought: we have a signed dll, privileges are ok. Let’s try again.
This time, the culprit was a function named “SeGetImageRequiredSigningLevel” located in ntoskrnl.exe. SeGetImageRequiredSigningLevel checks the minimum certificate requirements to load a DLL inside a WinRT application.
We realized we needed to sign our DLL with a cross-certificate, like those used to sign kernel-mode drivers.
We could not continue our tests because we do not have that kind of certificate right now, but we discovered that a kernel setting determines what kind of certificate is checked by SeGetImageRequiredSigningLevel.
This blog post explains how to manually bypass the security check and run untrusted applications on a Microsoft Surface device using WinDbg. We can follow a similar procedure to manually bypass the security check and correctly map and inject the DLL in the WinRT application on the desktop Windows operating system.
Before starting our whole research, we already had a method for injecting a DLL in WinRT applications: copy the DLL file inside the System32 folder and voilá! Although you need administrative privileges to copy a file to the System32 folder, once there, you can load it using LoadLibrary without using a path, since this folder is one of the default locations the Windows operating system will search. In addition, because you are using a relative-path, some security checks are skipped. Another plus is that you will not ever need to digitally sign the file!
But like many companies, we want to avoid copying DLL files into the ever-growing System32 folder and keep our files in the same location as our application. This is why we started our research.