I’ve done some digging over the weekend and I have found over 2000 special exe names which will trigger the same behavior, not just main.exe.
This post does contain most of my answer on the SO platform, but I also explain my reverse engineering process.
After confirming the quirky behavior on my machine, I spin up Process Hacker (fantastic software by the way) to get additional informations on the Xbox popup. That’s where it gets tricky : the popup is a modal that disappear whenever it lose focus. Which means every time I click somewhere in the Process Hacker GUI, the XBox process exit.
Anyway, If I can’t have any detailed informations about the GameLauncher process (loaded modules, etc.) I still got the most important one : explorer.exe is the parent process.
That means I need to break into explorer.exe, not the nicest process to suspend on Windows if you want to multitask. For example, if a breakpoint fires up and suspend explorer, you can still use the Alt-Tab shortcut (to switch applications) but not the Home-Ctrl-Arrow (to switch Virtual Desktops). Handle with care.
Tried and true reversing
Since explorer.exe does launch the executable and the xbox popup, I figured there must be a callback mechanism involved in the explorer process. To analyze it, I simply put breakpoints in various CreateProcess imports:
0:108> x *!CreateProcess*
0:108> bp explorer!CreateProcessW 0:108> bp advapi32!CreateProcessWithLogonW 0:108> bp advapi32!CreateProcessWithLogonCommonW 0:108> bp advapi32!CreateProcessWithTokenW 0:108> bp KERNELBASE!CreateProcessW 0:108> bp KERNELBASE!CreateProcessA 0:108> g
We successfully broke in the CreateProcess launching our main.exe executable. 0:107> g
Just using this simple dynamic analysis, I’ve managed to get the executable responsible for the XBox popup : C:\Windows\System32\GamePanel.exe. By looking and comparing the two stack traces, we can see the last common return call is this monstruosity of a class :
When opening shell32.dll in IDA, you can see that the CreateProcess call for the GamePanel.exe process is located in a Thread procedure (tagged as pfnThreadProc by IDA) in the shell32!CShellExecute::_RunThreadMaybeWait function. Backtracking a bit, I managed to disclose where the caller is :
0:101> bp Shell32+0x9e1e5 0:101> g
twinui.dll has an instance called BroadcastDVRComponent which is fired up whenever an “Application” GUI is updated. The TwinUI!BroadcastDVRComponent::LaunchGameOverlayWithStartupTipsFlag method tells me I’m on the right track.
Here is the flow graph of BroadcastDVRComponent::OnApplicationViewChanged:
BroadcastDVRComponent::EvaluateIfViewIsGame has several branches to treat differently Universal applications, .NET managed ones or traditionnal Win32 exe. In the latter branch, it end up relying on the resourcepolicyclient.dll to retrieve the GameId using the application’s PID.
resourcepolicyclient!ExecutionModel::GameConfigStoreClient::GetGameIdByPID is the fork point between a traditional app and a game since for the former it return the error code E_PROP_ID_UNSUPPORTED = 0x80070490 whereas returning E_ERROR_SUCCESS for the latter. Unfortunately, the GameId retrieval is hidden behind a RPC call.
RPC procedures are particulary painful to reverse, since you can’t know easily if the RPC server is in the same process or in a remote process. Moreover, RPC calls are asynchronous so you can’t just trace the whole rpccrt.dll dynamically until it lands somewhere in the RPC server’s side. If there is one thing I don’t want to do, it’s reversing the NT’s kernel ALPC event loop.
Most of the times I end up relying on runtime type information (RTTI) and public symbols.
RPCView can help if it recognize an interface, but in my case it did not. The RPC engine I’m looking for is probably located in-process, which means I’m back to square one where I have to statically reverse the explorer.exe binary and its slew of loaded modules.
For the people who do some reversing, it’s one of those moments where you are tempted to throw the towel. You’ve tried, and you’re wondering if the hurdle you facing is really worth the extra effort you’ll need to put into to potentially overcome it. Basically like when you’re trying to reverse a needlessly obfuscated binary in a CTF and you’re thinking : “I should get paid to do this shit”.
You’ve got handles ?
Anyway, while fishing for some COM interfaces in the explorer.exe process, I’ve found that explorer.exe has a handle on the following folder : C:\Windows\broadcastdvr. Since I know the XBox popup is handled by the twinui!BroadcastDVRComponent class this folder got my attention.
Fortunately, the C:\Windows\broadcastdvr folder contains only one file : KnownGameList.bin, which looks like a winner to me.
NB : explorer.exe also has a handle on the C:\Users\YOUR_USERNAME\AppData\Local\Microsoft\GamesDVR\KnownGameList.bin file. This file must be a copy from the C:\Windows\broadcastdvr one and updated for each logon user.
KnownGameList.bin does indeed list all the special executables which triggers the XBox recorder popup. You can see the main.exe entry here (entry #1007) :
I’ve written a 010 template file to parse the entry list and it comes with 2089 entries on my computer. From what I’ve seen by reversing the binary file, there is three types of entry:
the “simple” one where there is only a match on the executable name.
For example : main.exe or ai.exe
the more complex one where there is a match on the executable name and the path where the exe is stored must contains some strings.
For example : acu.exe must be located in a subfolder of Assassin's Creed Unity.
Some entries have additional strings to match, but I haven’t found how to trigger the game DVR popup for them.
Back in twinui there is a whole class called CQueryKnownGameList which happen to open this very file (and some others in order to update the list). TwinUI!CQueryKnownGameList::GetGameEntryByIdentifier and TwinUI!CQueryKnownGameList::GetTOCIndexByIdentifier are called whenever a new executable (there is a lookup cache mechanism) is launched. In the latter function there is a case-insensitive string comparison made by wcsnicmp between the process name and the entry name.
NB : the Win32 subsystem is case-insensitive (I think) so it makes sense that the executable name’s case does not matter.
If that behavior is annoying, it is always possible to globally disable it by setting the ShowStartup registry key to 0. It is located in HKEY_CURRENT_USER\Software\Microsoft\GameBar.
I haven’t found how to disable specifically an executable from triggering it, but I might be possible just by looking at the machine code in twinui.
We have a situation where we can launch a process just by changing the name of an executable. That might be dangerous.
Fortunately, the game launcher command line is located in HKEY_LOCAL_MACHINE\Software\Microsoft\GameOverlay which needs admin level to write into, so there is no UAC bypass possible here. The game launcher executable is located in the system dir C:\Windows, so here again admin levels are needed for modify it.
Moreover, only the TrustedInstaller “user” (as the way Windows defines it) has the authorization to modify the command line value in the registry :