About Terminal Server, Citrix, Delphi and other stuff
Today’s blog is about an application that was migrated to Citrix XenApp. During testing the users reported that several application menu’s were missing.
An example is the settings menu where the System tab is missing:
I suspected a permissions issue so I added the account to the Local Administrator group to verify that. And indeed the System tab was visible.
I removed the account from the Administrators group and fired up Process Monitor. I set a filter on the process name (ra60.exe) and on Result (ACCESS DENIED):
The Process Monitor trace shows that the application tries to register an ActiveX component at launch:
I don’t know why so many developers seem to think that’s a good idea, components should be registered at install time and only at install time!
Even though the failure to register the component is not blocking, it’s a good idea to fix this issue by either changing this registry keys permissions or by adding this key to HKCU\Software\Classes (HKCR is a merged view on HKLM and HKCU Classes key).
Applications tend to check permissions in two ways:
The Process Monitor trace does not show any trace of writing to Program Files or HKLM (other than the ActiveX component) so a hardcoded check is most likely the case here.
A hardcoded check usually works by calling the GetTokenInformation API to inspect the current process token.
I loaded ra60.exe in Ida Pro and waited for the autoanalysis to finish. Then I went to the Imports tab and searched for the GetTokenInformation API then pressed Ctrl-X so search for code that calls into this API:
The first occurrence is a direct hit, let’s have a look at the pseudo code:
if ( AllocateAndInitializeSid(&pIdentifierAuthority, 2u, 0x20u, a1, 0, 0, 0, 0, 0, 0, &pSid) )
v13 = &v21;
v12 = (unsigned int)&loc_954D88;
v11 = __readfsdword(0);
__writefsdword(0, (unsigned int)&v11);
v1 = 0;
if ( (unsigned __int8)GetVersion_0() >= 5u )
v2 = GetModuleHandleA_1("advapi32.dll");
v1 = GetProcAddress_0(v2, "CheckTokenMembership");
if ( v1 )
if ( ((int (__stdcall *)(_DWORD, PSID, unsigned int *, unsigned __int32))v1)(0, pSid, &v18, v11) )
v20 = v18 >= 1;
Let’s work on that code to make a little better readable:
bResult = 0; // FALSE
if ( AllocateAndInitializeSid(
CheckTokenMembership = 0;
if ( GetVersion() >= 5u )// Windows Version >= 2000 (win 5) ?
hModule = GetModuleHandleA("advapi32.dll");
CheckTokenMembership = GetProcAddress(hModule, "CheckTokenMembership");
if ( CheckTokenMembership )
if ( CheckTokenMembership(
0, // If TokenHandle is NULL, CheckTokenMembership uses the impersonation token of the calling thread
&bIsMember // TRUE if the SID is present
bResult = bIsMember >= 1;
The call to AllocateAndInitializeSid constructs the SID of the Administrators group. Then the CheckTokenMembership API is called to check if the current thread’s token contains the Administrators group SID. If it does the function returns 1 (TRUE), if not it returns 0 (FALSE).
Bug bug bug
Besides the fact that the developer who thought it was a good idea to include an admin check should be shot, this code is broken! On Vista and higher versions of Windows because the Admin token is filtered out by User Account Control (unless we are elevated).
I decided to patch this function and make it always return TRUE (1), this is actually quite easy.
Go to the Disassembly view (IDA View-A) and position the cursor on the first line of code in the function:
Go to the Edit Menu and choose the Assemble option from the Patch Program menu and enter the following code:
xor eax ,eax
The first line xor’s eax with itself, the result is that the eax register contains the value 0.
The second line, inc eax, increments the eax register value by one. This makes eax contain the value 1 (TRUE).
The last line, ret, returns from the function.
This is the end result:
Finally use the Apply patches to input file option from the Edit | Patch Program menu to save our changes back to disk (don’t forget to select the Create backup option:
I started the patched executable and guess what? The missing menu is back!
Question is though, should we go this far to get an application to run? Obviously it would be better if the vendor would fix this issue and in all honesty I didn’t ask…
It’s also possible that my customer is using an older version of the application (for whatever reason).
So let’s hear it, I am very much interested in your opinion! Is this solution acceptable for you? Or would you rather make those users Administrator?
.NET .NET FrameWork Active Directory Altiris Automation Manager bug Citrix Dell Delphi Excel Exchange Exchange2003 Exchange2010 Hack HP iOS Java LinkedIn Linux Lync MSI Office Office 2010 Outlook Passat Password PowerPoint PowerShell RES RNS510 SasLibEx Security Terminal Server ThinApp TSAdminEx Unattended VBS VCDS Vista Visual Basic VMWare Volkswagen Windows PE Wordpress XenApp