34

I wanted to see what Microsoft are doing when you click "Restart now" button in their Settings -> Update window on Windows 10:

enter image description here

Somehow the results are not the same what is available via InitiateSystemShutdownEx or InitiateShutdown WinAPIs, especially concerning installation of updates.

So I wanted to look into the code they use for that Restart button. Usually I would use Spy++ to look up a parent window's WndProc and then load the process in IDA Pro and put a breakpoint on it. Then trap a condition for the BM_CLICK message.

In this case though, the whole settings is a window of its own. The button does not appear in Spy++:

enter image description here

Any ideas how do I proceed from there?

2
  • 4
    I'd love to see someone offer an approach to this. Maybe consider starting a bounty at this point to garner more interest from the heavy-hitters?
    – dsasmblr
    Jan 8, 2018 at 16:08
  • @dsasmblr: Here you go. Wiped out on the points. Hopefully someone can answer it.
    – c00000fd
    Jan 9, 2018 at 4:56

4 Answers 4

32
+250

I have thought carefully before adding this as "Another Answer" rather than editing my existing answer that I posted above this morning.

I feel that this answer deserves another post of its own as it's not just a simple continuation of the material posted in my answer above.

I have spent over 2 hours on this post to avoid just cutting and pasting content from other sites. What I have focussed on rather, is on the point that I only include content in this post here, such that this answer would still remain relevant even if the original websites (from where this content is taken from) would ever go down in the future.

I am trying to focus on the essentials that would enable us to hook and modify the code in UWP Apps, as asked by the OP.

Hacking and Modding Windows Universal Apps :

DLL-Injection and Function-Hooking, work perfectly fine in UWP-Apps with most, if not all, injection and hooking techniques.

A couple of main differences regarding Function Hooking and Dll Injection, between the UWP apps and the "standard" Win32 apps :

First:
The Window, in which the UWP app renders its content, is not owned by the Apps executable. Instead “ApplicationFrameHost” does,and so you should not target the Window but rather 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. 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 from StackOverflow (so don’t mind the “goto”s) to set the permissions programmatically.

Code-Snippet

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.

Taking control over the (hidden) “C:\Program Files\WindowsApps\” directory :
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.

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

Code-Snippet :

#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:\\ProgramFiles\\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.

If we want to do something to an App before it is launched, we can suspend it before that using the code below.

Code Snippet :

// 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:

Code Snippet:

// 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.

#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;
}

Important note: Call

// Initialize COM objects, only need to do this once per thread
DWORD hresult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (!SUCCEEDED(hresult)) return hresult;

Before you launch an App or do anything call this afterward:

CoUninitialize();

References:
(1) Basic and Intermediate Techniques of UWP App Modding
(2) Hacking and Modding Windows Universal Apps

I sincerely hope that these two answers of mine would be helpful to anyone searching SE in future on the topics of UWP App hooking/modification.

7
  • Great answer! I'll make sure to review the code examples and references - kudos.
    – barnaby-b
    Jan 15, 2018 at 15:31
  • Yes, thank you. This answer is closer to answering my original question. (Your other answer is about global hooks. Unfortunately in this case you won't be able to do much with them. I can as well find a click on the UWP app's window, i.e Windows.UI.Core.CoreWindow by trapping a mouse click event in its WndProc with IDA. Unfortunately, it's not easy to proceed further on from that point. Or how to get to this class that can help route the call further from a button click.)
    – c00000fd
    Jan 15, 2018 at 21:42
  • 1
    I'll mark it as the answer so that you can receive the bounty for your work. I just want to point out that there's more to it left to be done. Can you contact me via email? I'll post an update here when I get it resolved.
    – c00000fd
    Jan 16, 2018 at 1:45
  • 1
    Thank you c00000fd ! Had been working overtime a lot these few days (working even this weekend) and hence did not have time to work on the actual "Restart" button in the Windows Update that you were mentioning. Hopefully in the next few days I will be able to create a solution for that exact "Restart" button issue and update it here. Will also email you as requested. @barnaby-b - Thank you for the compliment!
    – TechLord
    Jan 16, 2018 at 4:35
  • 1
    Awesome job, TechLord, and great feedback and attitude towards this whole thing, c00000fd! I hope to see you follow through with that Restart button solution, TechLord. Also, would quite enjoy an update from you as well once you get things pinned down, c00000fd. Thanks, both of you!
    – dsasmblr
    Jan 16, 2018 at 18:13
22

As promised, I am back to post a more specific answer to the question that was asked by the OP.

Decided to write this as a separate answer as I believe that this content stands out on its own and would not properly fit in with either of the two answers that I posted above.

Addressing his first issue :

In this case though, the whole settings is a window of its own. The button does not appear in Spy++

Well, the button does appear when using tools other than Spy++.
I am pasting screenshots of 2 tools with the help of which the properties of the RESTART button can be discovered.

Using the INSPECT TOOL that is a part of the Microsoft SDK :

INSPECT Tool showing Properties of the Button

and also using the UI Spy Tool (Please note that many other tools can also be used) for this purpose ...

Properties of the RESTART Button using the UI Spy Tool

Now we will see what code we BREAK ON when we click the RESTART button :

Firstly, Run the System Settings Manager (SystemSettings.exe would be seen in the Task Manager).
"Attach" to the SystemSettings.exe process with a debugger (I used the [excellent] x64dbg Debugger for this purpose).
Make sure that you tick the Break on DLL Load in the Debugger Settings as shown below :

DLL Load

Now we *click on the RESTART NOW button* (of the Windows Settings Window). We will break in the debugger as the "SettingsHandlers_nt.dll" gets loaded into the process.

This is the main dll that handles the events (clicks etc) of the Settings Window.

SettingsHandlers_nt.dll Loaded

We land here in the SettingsHandlers_nt.dll module

This is a part of the Control Flow Graph that actually takes the decision to go for a restart :

Restarts as Single User Mode

Somehow the results are not the same what is available via InitiateSystemShutdownEx or InitiateShutdown WinAPIs, especially concerning installation of updates.

The OP is right .
The actual Decision-Making Part for the [Automatic] Restarts after Microsoft Updates is handled by the MusUpdateHandlers.dll as can be seen below:

CFG of Forced Reboots after Updates

and also here :

Code View for Reboots after UPDATES

I have added the last 2 screenshots as the OP had wanted to know what API are invoked when the system restarts after an UPDATE...

I hope that this now concludes the answer to what @c00000fd wanted to know...

1
9

"I'd like to see details on how to hook and step into the function that processes a button click event in a UWP app."

I would suggest using the EventHook Library which is intended to hook global windows user events.This is available as a Nuget Package.

The Project Repo (named as Windows User Action Hook) is available on Github and my fork can be accessed here. This is for the same Library (EventHook Library) that I mentioned above. You can visit the Github repo if you want to get hold of the source code.

From the Github Project's README page, just let me excerpt a few details :

Supported Events : (These are the events that you can hook globally)

  • Keyboard Events
  • Mouse Events
  • Clipboard Events
  • Application events
  • Print events

Usage - Install by nuget

Install-Package EventHook

SAMPLE CODE:

KeyboardWatcher.Start();  
KeyboardWatcher.OnKeyInput += (s, e) =>  
{  
    Console.WriteLine(string.Format("Key {0} event of key {1}", e.KeyData.EventType, e.KeyData.Keyname));  
};  

MouseWatcher.Start();  
MouseWatcher.OnMouseInput += (s, e) =>  
{  
    Console.WriteLine(string.Format("Mouse event {0} at point {1},{2}", e.Message.ToString(), e.Point.x, e.Point.y));    
};  

ClipboardWatcher.Start();  
ClipboardWatcher.OnClipboardModified += (s, e) =>  
{  
    Console.WriteLine(string.Format("Clipboard updated with data '{0}' of format {1}", e.Data, e.DataFormat.ToString()));  
};  

ApplicationWatcher.Start();  
ApplicationWatcher.OnApplicationWindowChange += (s, e) =>  
{  
    Console.WriteLine(string.Format("Application window of '{0}' with the title '{1}' was {2}", e.ApplicationData.AppName, e.ApplicationData.AppTitle, e.Event));    
};  

PrintWatcher.Start();  
PrintWatcher.OnPrintEvent += (s, e) =>  
{  
    Console.WriteLine(string.Format("Printer '{0}' currently printing {1} pages.", e.EventData.PrinterName, e.EventData.Pages));    
};  

Console.Read();  
KeyboardWatcher.Stop();  
MouseWatcher.Stop();  
ClipboardWatcher.Stop();  

ApplicationWatcher.Stop();  
PrintWatcher.Stop();  

Sample Output (Screenshot) :

https://raw.githubusercontent.com/justcoding121/Windows-User-Action-Hook/stable/EventHook.Examples/EventHook.ConsoleApp.Example/Capture.PNG

I am sure that once you can see how we are able to globally hook various Windows User Events. In your case, using this library, we'd be able to code a tiny C# program for example, which would hook the mouse-press event for the "Restart Now" button and thus allow us to take control of the subsequent events.

Since this question was about hooking UWP Apps, I would like to give an example where this library was used to specifically hook UWP apps so that the code from that repo can be used as an example if required. Please note that I am not excerpting anything directly from that repo as I only wanted to show that this library works very well with UWP Apps as well. You can visit my fork of the Github Repo UWPHook here.

5

I searched in the System32 folder inside every file named SystemSettting*.* for the word Shutdown. I used Total Commander as it can search with multiple encodings such as ASCII and Unicode: Total Commander | Find Files

Then builtin viewer showed a reference to InitiateSystemShutdownExW: Total Commander Lister.

From MSDN:

InitiateSystemShutdownEx function Initiates a shutdown and optional restart of the specified computer, and optionally records the reason for the shutdown.

BOOL WINAPI InitiateSystemShutdownEx(
  _In_opt_ LPTSTR lpMachineName,
  _In_opt_ LPTSTR lpMessage,
  _In_     DWORD  dwTimeout,
  _In_     BOOL   bForceAppsClosed,
  _In_     BOOL   bRebootAfterShutdown,
  _In_     DWORD  dwReason
);

To verify, a next step would be to set a breakpoint on this API using a tool like Ida Pro or API Monitor.

4
  • I'm not sure if SystemSettingsThresholdAdminFlowUI.dll is even mapped into C:\Windows\ImmersiveControlPanel\SystemSettings.exe process at the time.
    – c00000fd
    Jan 10, 2018 at 3:59
  • You can check that with e.g. Process Explorer but you could be right that it's calling this api in another way.
    – Remko
    Jan 10, 2018 at 9:49
  • 1
    Do debug a UWP app you will have to use PLMDebug, you can use windbg or x64dbg for that.
    – mrexodia
    Jan 10, 2018 at 15:29
  • @mrexodia: Well, yeah, obviously you need to use PLMDebug to turn off Process Lifetime Management on UWP app. In other words, so that it doesn't get suspended by the broker when it's in the background. But you don't debug with it, per se. It's not a debugger.
    – c00000fd
    Jan 11, 2018 at 6:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.