Arbitrary labels used as Primary keys must not be changed between versions
Msi Editing tools that write auto generated references inside installer tables may cause unnecessary content to be included inside a patch. ‘Patching’ components with unchanged content may cause them to be uninstalled when the patch is removed thereby breaking the original application.
“I create a small patch for my product. When I selectively uninstall the patch my original product is broken. The trouble is I’m using Msi 3.x and this isn’t supposed to happen. Doesn’t Msi 3.x ‘cache’ whatever I patch so I can just roll it back when I uninstall? I have followed all the rules to the letter but can’t see where things are going wrong?”
This Is How I Can Reproduce This Behaviour
I create an installation msi for my simple single file application msiExplorer.exe. There is nothing untoward about this installation. The file goes in Program Files\msiExplorer and puts a shortcut on the desktop. The installation also creates a single registry key HKLM\Software\msiExplorer with value Text set to “A”. It installs. It works. Here’s the registry table:
Next, I create a new ‘upgraded’ version of this product. The only thing I change is the label in the registry column of the registry table. (Why would I want to do that!? – well just bear with me and I’ll explain below). I change the label to “RegKey2”.
Now I go through the patch creation process by duly creating a PCP file. In the PCP I specify the old ‘RTM’ msi and the new ‘updated’ msi – the usual routine. When I execute MsiMsp.exe it works. No errors reported. I can even load the original RTM msi database into Orca and overlay it with my new msp. It confirms that the registry table has been modified and shows me what is now included in the patch.
Notice how the actual Registry key, value and data are the same. Only the Registry column has changed.
Ok, I install RTM. It runs without problem; the registry key is duly created. I apply the patch (msiexec /p msiExplorer.msp). It installs, it runs. The Registry key looks the same – well it would wouldn’t it? At this stage it’s difficult to tell who was responsible for writing that registry value – the RTM install or the subsequent patch.
However I know the patch has been successfully applied because when I look in Add\Remove programs, there is my app and the patch correctly listed.
So everything looks good. Unfortunately the trouble starts when I decide to selectively remove MsiExplorer_Patch1 (msiexec /i msiExplorer.msi MSIPATCHREMOVE=msiexplorer.msp). The patch uninstall executes successfully, Add\Remove programs shows it has been removed and I think I’m ok.
But when I look in the registry.......
.........my registry key has gone. So now you can imagine when I run my app – if it depended on this key – it’s going to fail.
Why does that happen?
The key aspect of the scenario is that the registry key with a new label ‘looks’ like a new registry resource added by the patch. When a patch is removed, the new product is restored to its view minus the patch. Additionally, the Installer processes the patch being removed for any resources that it added. It then schedules removal for those resources. Therefore, while the key with label RegKey1 became active (the verbose log file shows it being written).........
........... it was later deleted per label RegKey2 as part of the added resource cleanup (the last portion of the verbose log file before the end opcode).
So How Could You Unknowingly Get Into This Situation?
I’m using an ‘industrial strength household name Msi Editing tool’ (ISHNMET). It’s a very good product. It has a nice feature which will read a COM Server file and automatically extract the COM registration information and populate the tables. One of these tables is the registry table. It therefore populates the registry column with arbitrary labels Registry1, Registry2...etc. Here’s a mock up of how it might look:
I have to rebuild my msi package every day and have automated the process. The developers submit their updated files and the tables are built by script. When the msi is built by the ISHNMET it does its trick of automatically extracting the COM information. If the developer has built some extra COM functionality into a dll the entries are inserted mid table bumping all the lower rows down. The naming algorithm just re-labels all the rows. In this example Registry4 gets bumped down a row and re-labelled as Registry5
Now when we go through our patch creation process we correctly pick up that MsiExplorer.exe component has changed but inadvertently include component ‘SomeComponent’ because the label changed! Now we are going to deliver a patch which includes the problem discussed above.
To be fair the very latest versions of ISHNMET try to minimise this scenario. However you might for example delete a registry entry and then change your mind, re-create it using the GUI and have it reappear in the registry table with a brand new label. So it probably pays to be aware of the dangers of arbitrary labels.
Does this happen anywhere else?
Exactly the same issue applies to the file table. If the ISHNMET uses arbitrary labels for the files it is adding to the file table then they are subject to the same vulnerability. Where the label in the file table is used as the keypath in the component, it is automatically changed at both reference points when re-labelling occurs. From the ISHNMET point of view this keeps the referential integrity between Component and File nice and tidy. From our point of view though, this will be picked up by MsiMsp.exe. You could finish up attempting to patch a file with an identical copy. Here is what this looks like with my simple example:
Modify the File column of the file table (Was msiExplorer.exe, now msiExplorer1.exe)
... and modify the keypath reference in the component table....
Create the patch and just to confirm it, overlay the RTM msi with the msp.
Install RTM. Install the patch. Use Add\Remove programs to confirm the patch is registered
You might even inspect the patch cache and notice that no copy of msiExplorer.exe has been saved.
Now selectively uninstall the patch and the original msiExplorer.exe (The one in Program Files\msiExplorer) has disappeared. Try to run the shortcut and if you’re lucky the executable will be restored via install-on-demand resilience or in my case, just to prove the point, I hid the source msi so that I could see the install-on-demand dialog.
The app is broken.
This same explanation above works for the File as well – and it should be noted that you’d need to maintain the same filekey for binary delta patching to work (just look at the Patch table and you’ll know why by reviewing the primary key columns for that table)
Here’s the Message
The Installer’s format is a relational database. Identity is expressed by the primary key – which is why it is so crucial. An item is found in the database by searching for its primary key. That’s how transforms can know which data to modify (efficiently) and how Patchwiz can determine what has changed. It’s imperative that the primary keys are not changed between versions of the package.
[Author: Robin Drake]
This posting is provided "AS IS" with no warranties, and confers no rights. Use of included script samples are subject to the terms specified at http://www.microsoft.com/info/cpyright.htm.