Yesterday I was troubleshooting an application that was migrated to Citrix XenApp.
The application is able to use a high precision scale which is attached to the client pc’s com port. This com port is redirected to XenApp.
While testing users reported several issues, let’s have a look at them.
Error configuring COM Port
Within the application the comport to which the scale is connected must be configured:
After pressing "Registreer" to register the new com port the following error message was shown
A Process Monitor trace quickly revealled the problem, the application wants to store the configuration in the HKLM hive:
We could circumvent this error by pre-creating this registry key and adding Modify permissions to it. However this presents us with a new issue in multi-user environments such as XenApp: if one user actives this setting, it is activated for all users on the same server.
That’s why I figured that redirecting this particular registry key from HKLM to HKCU would be the best solution.
I first tried the VirtualRegistry but even though AcLayers.dll was on stack right before the call to RegCreateKeyExW the redirection didn’t work.
Time to bring in other tools!
Regular readers probably expect me to launch Ida Pro now but I saw that mscorwks.dll was on stack in the Process Monitor trace. This means that we are dealing with a .NET application for which .NET Reflector is a better choice.
But before starting Reflector I needed to know what file(s) I needed to analyze. I used Total Commander, one of my favourite tools, to search for the word TransponderLezer since this is the registry value that contains the com port number.
I got no hits in the application’s program folder so I repeated the search but this time searching for Unicode. Again no hits, so I repeated the search on the .NET Assembly folder (C:\Windows\Assembly) and with Unicode I had a hit:
I loaded the file "ChipSoft.Ezis.Steriel.dll" in Reflector and expanded it. Under References I noticed Borland.Delphi which tells me the application is written in Delphi.NET. Therefore I set the language in Reflector to Delphi:
Note that choosing the same language as the application was built in is not required. Since .NET code is compiled to intermediate code (IL) you could also select C# or VB.NET. However I think that selecting the original language might make it easier to read code that calls into the language’s RTL.
I enabled Search via the View Menu and selected the Search String or Constant option:
I searched for TransponderLezer again and that yielded a couple of results:
I opened the one called btnSchrijfKeyInRegisterClick (Write Key to Registry) and watched the Disassembly output:
procedure TfrmCompoortInstellen.btnSchrijfKeyInRegisterClick(Sender: TObject); begin if (System.@WStrCmp(self.cmbComPoort.Text, '') <> 0) then begin SterielModuleUtils.schrijfRegisterSleutelString('Software\ChipSoft\Steriel', 'TransponderLezer', self.cmbComPoort.Text); self.chkStartStop.Checked := false; self.chkStartStopClick(Sender); if (System.@WStrCmp(self.cmbComPoort.Text, 'Wissen') <> 0) then begin self.chkStartStop.Checked := true; self.chkStartStopClick(Sender) end end end;
This procedure calls into a function named schrijfRegisterSleutelString. Let’s have a look at that one:
function SterielModuleUtils.schrijfRegisterSleutelString(pad: string; sleutel: string; waarde: string): boolean; begin try registry := TRegistry.Create($20006); try registry.RootKey := -2147483646; if (registry.OpenKey(pad, true)) then begin registry.WriteString(sleutel, waarde); if (System.@WStrCmp(waarde, 'Wissen') = 0) then registry.DeleteKey(sleutel) end finally registry.Free end except on obj1: TObject do begin obj2 := System.@ExceptObject := obj1; Msgbox.ErrorMsg('Het schrijven van de sleutel in het register is niet gelukt. Controleer of u schrijfrechten heeft, en probeer het opnieuw'); System.@ExceptObject := nil end end; begin Result := false; exit end end;
The interesting part is the marked line, the value -2147483646 is passed as the RootKey. The corresponding hex value is 80000002 which is the same as the constant value for HKEY_LOCAL_MACHINE:
#define HKEY_LOCAL_MACHINE (( HKEY ) (ULONG_PTR)((LONG)0x80000002) ) #define HKEY_CURRENT_USER (( HKEY ) (ULONG_PTR)((LONG)0x80000001) )
So if we edit this value to the constant value of HKEY_CURRENT_USER we have redirected the registry key from HKLM to HKCU.
Modifying the binary
To do this we need to download an Add-In for Reflector called Reflexil. To activate it, go to View | Add-Ins:
After adding it, choose Tools | Reflexil 1.6 to open Reflexil’s view. Select the line you want to edit, in my case line 06.
Right-click and choose Edit. The new value is the Decimal value of the HKEY_CURRENT_USER constant which is -2147483647:
Press update and make the same change to the read function which is called leesRegisterSleutelString.
It’s a good idea to check if other parts of the code are calling into these functions. You can do this by Right-clicking on the function in the left tree view and selecting Analyze.
In the Analyzer expand the "Used By" node, in my case only code related to the com port is affected. Therefore I consider the change to be safe:
Finally we can save the modified executable to disk by Right-clicking on the file in the left treeview and selecting Reflexil | Save As.
Usually I end my blogs with a sentence like: and that fixed it!
However now that the scale is working, testers have reported issues when they roam their XenApp session to another pc.
If the application is connected to the scale and the session is reconnected to a client pc without scale the application presents an error message which is acceptable.
But when you later on reconnect to a client with an attached scale the application crashes.
So in the end this fix did not go into production, not even as temporary workaround. We are going to contact the vendor and request a fix for both issues.