Run a Process when Citrix Receiver Exits

A while ago I was doing some research for Magic Filter when I stumbled upon something interesting within Receiver.

Inside wfica32.exe is a function called _Eng_RunExecutableOnExit. That name caught my interest, I’ve made it a little more readable with Ida Pro:

LSTATUS __cdecl _Eng_RunExecutableOnExit()
{
  LSTATUS result; // eax@1
  size_t v1; // eax@8
  bool v2; // zf@10
  char *v3; // eax@10
  const CHAR *v4; // edi@11
  size_t v5; // eax@13
  HKEY hKey; // [sp+4h] [bp-38Ch]@1
  DWORD cbData; // [sp+8h] [bp-388h]@2
  DWORD Type; // [sp+Ch] [bp-384h]@2
  BOOL bRunOnExit; // [sp+10h] [bp-380h]@2
  struct _PROCESS_INFORMATION ProcessInformation; // [sp+14h] [bp-37Ch]@15
  struct _STARTUPINFOA StartupInfo; // [sp+24h] [bp-36Ch]@15
  char Src[4]; // [sp+68h] [bp-328h]@8
  char v13; // [sp+6Fh] [bp-321h]@10
  char Str; // [sp+70h] [bp-320h]@5
  const CHAR ApplicationName; // [sp+71h] [bp-31Fh]@5
  CHAR CommandLine; // [sp+178h] [bp-218h]@8
  char v17; // [sp+179h] [bp-217h]@8
  char lpApplicationName; // [sp+284h] [bp-10Ch]@5

  hKey = 0;
  result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Citrix\\XenDesktop", 0, KEY_READ, &hKey);
  if ( !result )
  {
    bRunOnExit = 0;
    cbData = REG_DWORD;
    if ( !RegQueryValueExA(hKey, "XDControlRunOnExit", 0, &Type, (LPBYTE)&bRunOnExit, &cbData) && Type == REG_DWORD )
    {
      if ( bRunOnExit )
      {
        lpApplicationName = 0;
        memset(&lpApplicationName + 1, 0, 260u);
        Str = 0;
        memset((void *)&ApplicationName, 0, 260u);
        cbData = 260;
        if ( !RegQueryValueExA(hKey, "XDControlExe", 0, &Type, (LPBYTE)&lpApplicationName, &cbData)
          && (Type == REG_SZ || Type == REG_EXPAND_SZ) )
        {
          strcpy(Src, "ctx ");
          CommandLine = 0;
          memset(&v17, 0, 265u);
          _strcpy_s(&CommandLine, 266u, Src);
          ExpandEnvironmentStringsA(&lpApplicationName, &Str, 0x105u);
          v1 = _strnlen(&Str, 0x7FFFFFFFu);
          if ( v1 > 0 && Str == 34 && (v2 = *(&v13 + v1) == 34, v3 = &v13 + v1, v2) )
          {
            *v3 = 0;
            v4 = &ApplicationName;
          }
          else
          {
            v4 = &Str;
          }
          cbData = 260;
          v5 = _strnlen(Src, 0x7FFFFFFFu);
          if ( !RegQueryValueExA(hKey, "XDControlArgs", 0, &Type, (LPBYTE)&CommandLine + v5, &cbData) && Type == REG_SZ )
          {
            memset(&StartupInfo.lpReserved, 0, 0x40u);
            ProcessInformation.hThread = 0;
            ProcessInformation.dwProcessId = 0;
            ProcessInformation.dwThreadId = 0;
            ProcessInformation.hProcess = 0;
            StartupInfo.cb = 68;
            if ( CreateProcessA(v4, &CommandLine, 0, 0, 0, 0, 0, 0, &StartupInfo, &ProcessInformation) )
            {
              CloseHandle(ProcessInformation.hProcess);
              CloseHandle(ProcessInformation.hThread);
            }
          }
        }
      }
    }
    result = RegCloseKey(hKey);
  }
  return result;
}

So what’s the conclusion?

imageWe can create the following  registry values in HKLM\SOFTWARE\Wow6432Node\Citrix\ICA Client\Engine\Lockdown Profiles\All Regions\Lockdown\XenDesktop:

XDControlRunOnExit DWORD: 0 for off 1 for on
XDControlExe String or Expandable String: Executable including path to run, maximum length 260 characters. Environment variables can be used
XDControlArgs String: Commandline arguments, maximum length 260 characters

How is that useful?

An example is automatically locking the workstation when the Citrix session ends. Use the following settings:

XDControlRunOnExit 1
XDControlExe rundll32.exe
XDControlArgs user32.dll,LockWorkStation