Note:

This article was migrated and used some custom plugins on the old Blog.
Some things might look a bit broken, but should be perfectly readable.


There are a lot of misconceptions about Windows 10 UWP Apps.
Most people think that you can’t do anything to them, in terms of modding or hacking.

Well, this is not true at all.

In fact, you can do a whole heck of a lot with them and have fun in all sorts of ways. Including to mod the shit out of them.

Windows 10s UWP Apps are built upon Win32, which we all know and love (and/or hate to the core…)
Windows 8s UWP Apps are a slightly different story but who ever used that shit, right?

Here I’ll give you a quick rundown on how you can hack and mod the shit out of them.
We first begin with just reading and modifying things in memory, go over DLL-Injections and

Misconception #1: Cheating

To kick things off, let’s begin with something real easy… Cheatengine, which can also be used for way more than what its name implies.

Note: I don’t support cheating in (multiplayer) Games, but it’s here to prove a point. 

A lot of people seem to think that there at least won’t be much cheating in Games when they are UWP exclusive, at least a single strong point for them, you might think.

But nope, Cheatengine just works perfectly fine. The inbuilt debugger from Cheatengine just plain works, too!
Here is a screenshot of Cheatengine modifying a text string in Forza Motorsport 6: Apex

Cheatengine modifying Forza Motorsport 6: Apex

I’ve also tried and casually played with x64dbg, but didn’t play around all too seriously, but I also expect it to work just fine for more serious usage (outside of cheating).

Misconception #2: Programs like FRAPS cannot and will never Work.

This is also everything else but true.
It is correct that FRAPS itself does not work, however, the latest FRAPS release was from February 2013.
Let that sink in for a minute.

But now, let us first look at how Programs like FRAPS, other in-game overlays, recording or benchmarking software even work.

Those programs, basically, work by hooking DirectX’s “End-Scene” call, which, as you might guess, is called at the end of every frame rendering.
Of course, this is slightly different when recording OpenGL or Vulkan or whatever but the general idea is the same.

How do they hook this function? They basically just inject a DLL and then hook the specific method.

So we’re talking about DLL-Injection and Function-Hooking, which also just works perfectly fine in UWP-Apps.  with most, if not all, injection and hooking techniques.

But, and there is always a but, you have to look out for two things.

First:
The Window, in which the UWP app renders its content, is not owned by the Apps executable.
Instead “ApplicationFrameHost” does, and this is where FRAPS falls short since FRAPS directly targets the window, rather than the process itself.
Note: Because of this, you cannot create new windows, like message boxes for example, when injected in a UWP-App

Second:
The DLL you want to inject has to have “Read, Execute” as well as the “Read” permissions set for the “ALL APPLICATION PACKAGES”-Group

Properties

You can set this via the properties tab of the DLL-file but the name may differ depending on your system language.
You could also just use the following little code snippet which I’ve taken from StackOverflow (so don’t mind the “goto”s) to set the permissions programmatically.

DWORD SetPermissions(std::wstring wstrFilePath)
    {
    PACL pOldDACL = NULL, pNewDACL = NULL;
    PSECURITY_DESCRIPTOR pSD = NULL;
    EXPLICIT_ACCESS eaAccess;
    SECURITY_INFORMATION siInfo = DACL_SECURITY_INFORMATION;
    DWORD dwResult = ERROR_SUCCESS;
    PSID pSID;
    // Get a pointer to the existing DACL
    dwResult = GetNamedSecurityInfo(wstrFilePath.c_str(), SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &pOldDACL, NULL, &pSD);
    if (dwResult != ERROR_SUCCESS)
    goto Cleanup;
    // Get the SID for ALL APPLICATION PACKAGES using its SID string
    ConvertStringSidToSid(L"S-1-15-2-1", &pSID);
    if (pSID == NULL)
    goto Cleanup;
    ZeroMemory(&eaAccess, sizeof(EXPLICIT_ACCESS));
    eaAccess.grfAccessPermissions = GENERIC_READ | GENERIC_EXECUTE;
    eaAccess.grfAccessMode = SET_ACCESS;
    eaAccess.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
    eaAccess.Trustee.TrusteeForm = TRUSTEE_IS_SID;
    eaAccess.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
    eaAccess.Trustee.ptstrName = (LPWSTR)pSID;
    // Create a new ACL that merges the new ACE into the existing DACL
    dwResult = SetEntriesInAcl(1, &eaAccess, pOldDACL, &pNewDACL);
    if (ERROR_SUCCESS != dwResult)
    goto Cleanup;
    // Attach the new ACL as the object's DACL
    dwResult = SetNamedSecurityInfo((LPWSTR)wstrFilePath.c_str(), SE_FILE_OBJECT, siInfo, NULL, NULL, pNewDACL, NULL);
    if (ERROR_SUCCESS != dwResult)
    goto Cleanup;
    Cleanup:
    if (pSD != NULL)
    LocalFree((HLOCAL)pSD);
    if (pNewDACL != NULL)
    LocalFree((HLOCAL)pNewDACL);
    return dwResult;
    }

Afterward, inject your DLL with your preferred injector/method, and your DLLs code will magically function.

Since UWP-Apps use the Win32 API under the hood, you can expect KernelBase.dll, Kernel32.dll, ntdll.dll, and user32.dll to be loaded in them. You will also find d2d1.dll and either d3d11.dll or d3d12.dll (used in a handful of apps) loaded in all UWP apps, including the new UWP calculator app.

For function hooking, as you might now expect, it works the same way it does for Win32 Programs.
A Handy little library which I’ve used for this is MinHook

So recording and benchmarking software and in-game overlays could work just fine.
An example of a perfectly fine working recording software would be Dxtory which was updated back in September 2015 to support UWP-Apps!

Misconception #3: You cannot create Mods

Well… Again you very well can create mods!
With Cheatengine, debuggers like x64dbg, and DLL-injection and function hooking working, there is nothing to stop anyone from modding the shit out of any UWP-App.

But let us begin with why this misconception exists in the first place.

Without taking control over the (hidden) “C:\Program Files\WindowsApps\” directory, or wherever you might have it, you cannot access the files of UWP-Apps. But you can just take control of this, and any subdirectories and its files without any problems.

You could also always just open up a shell as NT-Authority and access them that way.

If you just wanted to mod a simple config file or something you should be fine.
However, some Apps, not all of them, check if their files were tampered with. But that’s easily circumvented.

All you have to do is Hook the “CreateFileW“-Method in “KernelBase.dll“, monitor the file access and then reroute those access requests to load your modified version from some directory you can access just fine.

Unfortunately though, this method doesn’t appear to work for sound files or files that are streamed. If anyone has a fix for this, I’d love to know…

Here’s an example that does exactly what just described, using the previously mentioned MinHook library

#include <Windows.h>
    #include <atlbase.h>
    #include <Shlobj.h>
    #include <string>
    #include "MinHook.h"
     
    // Path to modified game files store in AppData
    std::wstring MOD_FILES_PATH;
     
    // Path to the apps protected resources in WindowsApps
    // Don't use the full path name, just keep the Publisher.AppName part
    std::wstring APP_LOCATION(L"C:\\Program Files\\WindowsApps\\Publisher.AppName");
     
    // Sets a hook on the function at origAddress function and provides a trampoline to the original function
    BOOL setHook(LPVOID* origAddress, LPVOID* hookFunction, LPVOID* trampFunction);
     
    // Attaches a hook on a function given the name of the owning module and the name of the function
    BOOL attach(LPWSTR wstrModule, LPCSTR strFunction, LPVOID* hook, LPVOID* original);
     
    // Basic hook setup for CreateFileW
    typedef HANDLE(WINAPI *PfnCreateFileW)(LPCWSTR lpFilename, DWORD dwAccess, DWORD dwSharing, LPSECURITY_ATTRIBUTES saAttributes, DWORD dwCreation, DWORD dwAttributes, HANDLE hTemplate);
    PfnCreateFileW pfnCreateFileW = NULL; // Will hold the trampoline to the original CreateFileW function
     
    // CreateFileW hook function
    HANDLE WINAPI HfnCreateFileW(LPCWSTR lpFilename, DWORD dwAccess, DWORD dwSharing, LPSECURITY_ATTRIBUTES saAttributes, DWORD dwCreation, DWORD dwAttributes, HANDLE hTemplate)
    {
     std::wstring filePath(lpFilename);
     
     // Check if the app is accessing protected resources
     if (filePath.find(APP_LOCATION) != filePath.npos)
     {
     std::wstring newPath(MOD_FILES_PATH);
     
     // Windows provides the app the location of the WindowsApps directory, so the first half the file path will use back slashes
     // After that, some apps will use back slashes while others use forward slashes so be aware of what the app uses
     newPath += filePath.substr(filePath.find(L"\\", APP_LOCATION.size()) + 1, filePath.size());
     
     // Check if the file being accessed exists at the new path and reroute access to that file
     // Don't reroute directories as bad things can happen such as directories being ghost locked
     if (PathFileExists(newPath.c_str()) && !PathIsDirectory(newPath.c_str()))
     return pfnCreateFileW(newPath.c_str(), dwAccess, dwSharing, saAttributes, dwCreation, dwAttributes, hTemplate);
     }
     
     // Let the app load other files normally
     return pfnCreateFileW(lpFilename, dwAccess, dwSharing, saAttributes, dwCreation, dwAttributes, hTemplate);
    }
     
    BOOL Initialize()
    {
     // Initialize MinHook
     if (MH_Initialize() != MH_OK)
     return FALSE;
     
     // Get the path to the apps AppData folder
     // When inside a UWP app, CSIDL_LOCAL_APPDATA returns the location of the apps AC folder in AppData
     TCHAR szPath[MAX_PATH];
     if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, szPath)))
     {
     // Get the path to the mod files folder
     std::wstring appData(szPath);
     appData = appData.substr(0, appData.rfind(L"AC")); // Get the base directory
     appData += L"LocalState\\ModFiles\\"; // Get the location of any new files you want the app to use
     
     MOD_FILES_PATH = appData;
     }
     else
     return FALSE;
     
     // Attach a hook on CreateProcessW and return the status of the hook
     BOOL hook = TRUE;
     hook &= attach(L"KernelBase.dll", "CreateFileW", (LPVOID*)&HfnCreateFileW, (LPVOID*)&pfnCreateFileW);
     
     return hook;
    }
     
    BOOL Uninitialize()
    {
     // Uninitialize MinHook
     if (MH_Uninitialize() != MH_OK)
     return FALSE; // This status will end up being ignored
     
     return TRUE;
    }
     
    BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
    {
     switch (ul_reason_for_call)
     {
     case DLL_PROCESS_ATTACH:
     return Initialize(); // If initialization failed, the DLL will detach
     break;
     case DLL_THREAD_ATTACH:
     break;
     case DLL_THREAD_DETACH:
     break;
     case DLL_PROCESS_DETACH:
     Uninitialize(); // Return value doesn't matter when detaching
     break;
     }
     return TRUE;
    }
     
    BOOL setHook(LPVOID* origAddress, LPVOID* hookFunction, LPVOID* trampFunction)
    {
     if (MH_CreateHook(origAddress, hookFunction, reinterpret_cast<LPVOID*>(trampFunction)) != MH_OK)
     return FALSE;
     
     if (MH_EnableHook(origAddress) != MH_OK)
     return FALSE;
     
     return TRUE;
    }
     
    BOOL attach(LPWSTR wstrModule, LPCSTR strFunction, LPVOID* hook, LPVOID* original)
    {
     HMODULE hModule = GetModuleHandle(wstrModule);
     if (hModule == NULL)
     return FALSE;
     
     FARPROC hFunction = GetProcAddress(hModule, strFunction);
     if (hFunction == NULL)
     return FALSE;
     
     return setHook((LPVOID*)hFunction, hook, original);
    }

A few more things

You can’t just launch a UWP-App like a regular Win32 Program using CreateProcess.
Luckily for us, M$ has provided us with  the IApplicationActivationManager interface which lets developers launch UWP apps from regular Win32 programs.

HRESULT LaunchApplication(LPCWSTR userModelId, PDWORD pdwProcessId)
    {
    
     CComPtr<IApplicationActivationManager> spAppActivationManager;
     HRESULT result = E_INVALIDARG;
     // Initialize IApplicationActivationManager
     result = CoCreateInstance(CLSID_ApplicationActivationManager, NULL, CLSCTX_LOCAL_SERVER, IID_IApplicationActivationManager, (LPVOID*)&spAppActivationManager);
    
     if (!SUCCEEDED(result))
     return result;
    
     // This call ensures that the app is launched as the foreground window
     result = CoAllowSetForegroundWindow(spAppActivationManager, NULL);
    
    
     if (!SUCCEEDED(result))
     return result;
    
     // Launch the app
     result = spAppActivationManager->ActivateApplication(userModelId, NULL, AO_NONE, pdwProcessId);
    
     return result;
    }
    

You can get the AppUserModelID or “userModelId” called in code from the registry.
It’ is in an AppX_SOMESTRING_ container in HKEY_CLASSES_ROOT
Getting AppUserModelID

If we want to do something to an App before it is launched, we can suspend it before that

// Gets the current application's UserModelId and PackageId from the registry
    // Substitute your own methods in place of these
    std::wstring appName = GetApplicationUserModelId();
    std::wstring appFullName = GetApplicationPackageId();
     
    HRESULT hResult = S_OK;
     
    // Create a new instance of IPackageDebugSettings
    ATL::CComQIPtr debugSettings;
    hResult = debugSettings.CoCreateInstance(CLSID_PackageDebugSettings, NULL, CLSCTX_ALL);
    if(hResult != S_OK) return hResult;
     
    // Enable debugging
    hResult = debugSettings->EnableDebugging(appFullName.c_str(), NULL, NULL);
    if(hResult != S_OK) return hResult;
     
    // Launch the application using the function discussed above
    DWORD dwProcessId = 0;
    hResult = LaunchApplication(appName, &dwProcessId);
    if(hResult != S_OK) return hResult;
     
    /* Do more stuff after the app has been resumed */
     
    // Stop debugging the application so it can run as normal
    hResult = debugSettings->DisableDebugging(appFullName.c_str());
    if(hResult != S_OK) return hResult;
    

Using the code above, your program will hang until the app is resumed as it is waiting on the app to reply back to the IApplicationActivationManager on its launch status. To resume the app, you can simply specify the path to your executable file when enabling debugging:

// Enable Debugging with a custom debugger executable
    hResult = debugSettings->EnableDebugging(appFullName.c_str(), pathToExecutable.c_str(), NULL);
    if(hResult != S_OK) return hResult;

Windows will pass the process ID for the app process to the executable acting as the debugger using the command line argument -p followed by the process ID. From the debugger executable, you can do whatever you want to while the app is suspended such as injecting mods, and finally resume the app using NtResumeProcess.

Here’s an example of the main() function from such “debugger executable”

#define IMPORT extern __declspec(dllimport)
     
    IMPORT int __argc;
    IMPORT char** __argv;
    //IMPORT wchar_t** __wargv;
     
    // Turning this into a normal Windows program so it's invisible when run
    int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
    {
        DWORD dwProcessId = 0;
     
        // Process the arguments passed to the debugger
        for (int i = 1; i < __argc; i += 2)
        {
            std::string arg(__argv[i]);
            if (arg == "-p")
                dwProcessId = atoi(__argv[i + 1]);
        }
        
        if(dwProcessId == 0)
            return E_FAIL;
     
        // Can do additional error checking to make sure the app is active and not tombstoned
     
        ModLoader::InjectMods(dwProcessId);
        ProcessUtils::ResumeProcess(dwProcessId); // Uses NtResumeProcess
     
        return S_OK;
    }