Citrix XenApp/XenDesktop API Hooking Explained
What is API Hooking?
API hooking is all about making others do things they never even knew they could do. More precisely: tricking other processes into doing things differently from what their developers programmed.
API hooking is done in two steps: first, you need access to another process’ memory. Second, you manipulate memory addresses so that whenever the other process wants to call certain operating system API functions, your code is called instead. Let’s explain this in more detail.
Getting Access: AppInit_DLLs
Getting access to the memory of another process can be tricky. It is by far easier to use a technique not too dissimilar to a trojan horse and have your code automatically loaded into all processes created in the system. That is exactly what AppInit_DLLs does.
AppInit_DLLs has been part of Windows since the dawn of time. Because tampering with unsuspecting processes can have severe security and stability implications Microsoft disabled the functionality by default starting with Vista, but enabling it is as simple as changing a registry value (LoadAppInit_DLLs).
Technically, AppInit_DLLs is a registry value located in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows. By default it is empty. When User32.dll initializes, it reads the AppInit_DLLs registry value and loads all DLL names it finds in that value into memory. As User32.dll is one of the most common Windows DLLs this basically affects every single process.
Think of AppInit_DLLs as a free delivery mechanism that puts your code right into the heart of all processes running on your Windows machine. But wait: I explained how you can get your DLL loaded into another process’ memory, but how do you make that other process execute your code so that you can install the API hook? Easy: whenever a DLL is loaded, the OS automatically calls the DllMain function of the DLL. That is where you install the hook.
The Hook
When processes need to do basic stuff like reading from a file, sending data across the network or accessing the registry, they call an API function provided by the operating system for that task. This might happen through multiple layers, but eventually there is always an API function that does the job. This is true for every programming language, including runtime environments like .NET and Java.
If you control how a process accesses the operating system APIs, you control the information flowing in and out of the process. You also control what that process is doing.
Here is an example: you want to capture a process’ registry activity? Simple; hook the registry API functions. Whenever the process tries to open a key or change a value, your code is called and you can log the activity. Then you call the original API function – you do not want to intrude, you just want to know what is going on.
But you could just as well change parameters before making the API call. Redirecting from HKLM to HKCU is as simple as changing a single parameter. The hooked process would never know what happened.
So how do we install our hooks? The typical way of calling operating system API functions is through an import address table (IAT). When the compiler generates code it does not know at which memory addresses in the OS DLLs the API functions will be located on the user’s machine, so it uses dummy addresses. These are replaced with the real addresses by the Windows DLL loader. To keep this process simple a lookup table is used, the IAT.
Installing the hook involves locating the entry for the API function to be hooked in the IAT. Then you replace it with the address of a function in your DLL. Done. From now on your code is called instead of, say, RegOpenKeyEx.
Citrix’ Way of Hooking
Remoting applications and desktops is no small feat, and Citrix needs many different hooks in order to pull it off. To simplify management a single hook DLL, Mfaphook.dll is added to the AppInit_DLLs registry value when the XenApp/XenDesktop VDA is installed. Actually, two hook DLLs are added:
- Mfaphook.dll to the 32-bit registry: HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs
- Mfaphook64.dll to the 64-bit registry: HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs
These two AppInit_DLLs registry values ensure that Mfaphook[64].dll is loaded into practically all processes created on machines with XenApp/XenDesktop installed. However, depending on the host process entirely different hooks are required. That is why Citrix has implemented a flexible configuration scheme to specify which hook DLLs are loaded into which processes. Mfaphook[64].dll reads that configuration from the registry key HKLM\SOFTWARE\Citrix\CtxHook\AppInit_Dlls:
As you can see above there is one registry key below the AppInit_DLLs key per hook. Each hook’s key has a FilePathName value that contains the path and name of the hook DLL to be loaded. Optional subkeys control which processes the hook DLL is loaded into; no subkey stands for all processes.
Disabling Hooks
API Hooks change the way an application operates. Due to that nature hooks may cause problems. If you experience weird application malfunctions it might be a good idea to test with some or all hooks disabled.
Disabling API Hooking
To disable API hooking altogether set the value LoadAppInit_DLLs to 0 (REG_DWORD).
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\LoadAppInit_DLLs = 0 [REG_DWORD]
Disabling Citrix Hooks
To disable specific Citrix hooks set the Flag value below the hook’s registry key to 0 (REG_DWORD), e.g. to disable the multi monitor hook:
HKLM\SOFTWARE\Citrix\CtxHook\AppInit_Dlls\Multiple Monitor Hook\Flag = 0 [REG_DWORD]
Disabling Citrix Hooks for one Executable
To disable all Citrix hooks for specific executables create a comma-separated list of executable names in the string value ExcludedImageNames, e.g.:
HKLM\SOFTWARE\Citrix\CtxHook\ExcludedImageNames = calc.exe,notepad.exe [REG_SZ]
List of all Citrix Hooks
XenApp 7.6: 64-Bit Hooks
The following table lists all hooks loaded into 64-bit processes.
Hook name | Hook DLL | Processes to hook | Description |
CreateProcessHook | CreateProcessHook64.dll | explorer.exe, tabtip.exe | |
CtxMFPlugin | CtxMFPlugin64.dll | * | Media player |
CtxNotif Launcher | ctxnflau64.dll | winlogon.exe | |
Flash Legacy | sphook64.dll | iexplore.exe | |
Graphics Helper | CtxGraphicsHelper64.dll | * | GPU sharing |
Multiple Monitor Hook | mmhook64.dll | * | |
Seamless Explorer | explhook64.dll | explorer.exe | Prevents published Explorer from displaying a full desktop |
SfrHook | Sfrhook64.dll | * | Special folder redirection |
Shell Hook | ShellHook64.dll | * | Support for FlashWindow() API |
Smart Card Hook | scardhook64.dll | * | |
UPD Printer hook | cpprov.dll | spoolsv.exe | |
UPS win32Spl hook | Win32SplHook.dll | spoolsv.exe | |
VIPHook | viphook64.dll | * | Virtual IP address hook (CTX125506) |
XenApp 7.6: 32-Bit Hooks
The following table lists all hooks loaded into 32-bit processes.
Hook name | Hook DLL | Processes to hook | Description |
ActiveSync | asynchook.dll | rapimgr.exe, wcescomm.exe, WCESMgr.exe | |
CreateProcessHook | CreateProcessHook.dll | explorer.exe, tabtip.exe | |
CtxMFPlugin | CtxMFPlugin.dll | * | Media player |
Flash Legacy | sphook.dll | iexplore.exe | |
Graphics Helper | CtxGraphicsHelper.dll | * | GPU sharing |
HDXMediaStreamForFlash | HdxFlashHook.dll | iexplore.exe | |
Multiple Monitor Hook | mmhook.dll | * | |
Seamless Explorer | explhook.dll | explorer.exe | Prevents published Explorer from displaying a full desktop |
SfrHook | Sfrhook.dll | * | Special folder redirection |
Shell Hook | ShellHook.dll | * | Support for FlashWindow() API |
Smart Card Hook | scardhook.dll | * | |
Twain Hook | twnhook.dll | * | Communicates with CtxTwnPA.exe on the client through a virtual channel |
VIPHook | viphook64.dll | * | Virtual IP address hook (CTX125506) |
XenDesktop 7.6: 64-Bit Hooks
The following table lists all hooks loaded into 64-bit processes.
Hook name | Hook DLL | Processes to hook | Description |
CreateProcessHook | CreateProcessHook64.dll | explorer.exe, tabtip.exe | |
FullScreenHook | picaFullScreenHookX64.dll | * | Citrix HDX 3D for Pro Graphics Full Screen Hook |
Multiple Monitor Hook | mmhook64.dll | control.exe, explorer.exe, logonui.exe, rundll32.exe, SelfService.exe | |
PicaFusChoiceDialogHook | PicaFusChoiceDialogHook64.dll | rundll32.exe | Citrix PortICA Fast User Switching Choice Dialog Hook DLL |
PicaWaveHook | PicaWaveHook64.dll | * | Citrix PortICA Wave Audio Hook DLL |
PicaWinlogonHook | PicaWinlogonHook64.dll | winlogon.exe | Citrix PortICA Winlogon Hook DLL |
Seamless Explorer | explhook64.dll | explorer.exe, userinit.exe | Prevents published Explorer from displaying a full desktop |
Shell Hook | ShellHook64.dll | * | Support for FlashWindow() API |
Smart Card Hook | SCardHook64.dll | * | |
UI Tweak | picaUiTweakHook64.dll | explorer.exe, rundll32.exe, SystemPropertiesAdvanced.exe, SystemPropertiesPerformance.exe, winlogon.exe | Citrix UI Preferences Hook |
Unicode Injection IME Hook | cxinjime64.dll | * | |
UPD Printer hook | cpprov.dll | spoolsv.exe | |
UPS win32Spl hook | Win32SplHook.dll | spoolsv.exe | |
WTS Hook | PicaWtsHook64.dll | * |
XenDesktop 7.6: 32-Bit Hooks
The following table lists all hooks loaded into 32-bit processes.
Hook name | Hook DLL | Processes to hook | Description |
CreateProcessHook | CreateProcessHook.dll | explorer.exe, tabtip.exe | |
CtxMFPlugin | CtxMFPlugin.dll | firefox.exe, iexplore.exe, lync.exe, realplay.exe, wmplayer.exe | Media player |
FullScreenHook | picaFullScreenHookX86.dll | * | Citrix HDX 3D for Pro Graphics Full Screen Hook |
HDXMediaStreamForFlash | HdxFlashHook.dll | iexplore.exe | |
Multiple Monitor Hook | mmhook.dll | control.exe, explorer.exe, logonui.exe, rundll32.exe, SelfService.exe | |
PICA Passthrough | picaPassThruHook.dll | wfica32.exe | |
PicaFusChoiceDialogHook | PicaFusChoiceDialogHook.dll | rundll32.exe | Citrix PortICA Fast User Switching Choice Dialog Hook DLL |
PicaWaveHook | PicaWaveHook.dll | * | Citrix PortICA Wave Audio Hook DLL |
PicaWinlogonHook | PicaWinlogonHook.dll | winlogon.exe | Citrix PortICA Winlogon Hook DLL |
Seamless Explorer | explhook.dll | explorer.exe, userinit.exe | Prevents published Explorer from displaying a full desktop |
Shell Hook | ShellHook.dll | * | Support for FlashWindow() API |
Smart Card Hook | SCardHook.dll | * | |
Twain Hook | twnhook.dll | * | Communicates with CtxTwnPA.exe on the client through a virtual channel |
UI Tweak | picaUiTweakHook.dll | explorer.exe, rundll32.exe, SystemPropertiesAdvanced.exe, SystemPropertiesPerformance.exe, winlogon.exe | Citrix UI Preferences Hook |
Unicode Injection IME Hook | cxinjime.dll | * | |
WTS Hook | PicaWtsHook.dll | * |
Fun Fact
Did you notice that Citrix is hooking one of their own processes on XenDesktop? The VDA hooks Receiver (wfica32.exe). It would most likely have been better to talk to the developers on the Receiver team and ask them to change some of their code. Hooking might achieve the same goal, but in a much less friendly way.
7 Comments
Hey Helge,
Excellent information.
Thanks,
S.
Nice post Helge. I add that AppInit_Dlls is only processed when the Windows component user32.dll is loaded into the process. An interesting side-effect is that if the process doesn’t link user32.lib, no hooks get installed. In Citrix App Streaming, we took an extra step to force-load user32.dll into executed processes, even if they didn’t link it. With this, our registry and COM filtering would reliably be in place.
MfAppHook is mostly a “layer above”. Since the technique of hooking has a habit of changing with each release of the operating system, most of the XA/XD system programs hooks using the internal “hook SDK” and MfAppHook. Changes to the operating system or bugs/enhancements in hook implementation can then be implemented in one-spot. None of this is easy. There are smart folks in the halls.
Thanks for the great additional information!
Very good information, thanks for sharing Helge.
Another fact: The AppInit_DLL feature is disabled by Windows when UEFI Secure Boot is enabled. That’s why you cannot run XenApp or XenDesktop VDAs on Hyper-V Generation 2 machines with Secure Boot enabled.
https://msdn.microsoft.com/de-de/library/windows/desktop/dn280412
Very, very good information to know! Thank you, Martin!
Hooks in wfica32 are need for double hop use case!
Also with 7.6 FP3, hook dll can be disabled per process. To do so, add the process name as a key under the hook key and create a “Flag” value of dword type and give it a value 512 (0x200).
Really very good explanation. Thanks for such great insights.