David Grey's Blog

October 2007 - Posts

Windows Installer fails when passing parameters to managed custom action

I've just spent an hour or so debugging a frustrating issue with a Windows Installer (MSI) installation for one of our products. The MSI is created by a Visual Studio setup project and it contains some managed custom actions. One of the custom actions has custom action data which is passed to it. This custom action data consists of the values of several installer properties.

When the installation was run it would fail every time it tried to call the custom action. It turns out that this problem is caused by the way I was passing properties as parameters to the custom action. The safe way of passing custom action data is to enclose it in quotes in case the user enters a value containing spaces e.g.

/MyVal="[SOMEPROPERTY]"

However, if the property contains a directory path with a trailing backslash (e.g. C:\MyFolder\) then the command used to fire the custom action will contain data such as

(Action=_EBBC1917_9B5F_478F_BEC3_3242496333D4.install,
ActionType=3073,Source=BinaryData,Target=ManagedInstall,
CustomActionData=/installtype=notransaction /action=install
/LogFile= /MyVal="C:\MyFolder\" )

The trailing backslash followed by a " will be interpreted as an escape sequence and will cause any following parameters to be parsed incorrectly. This results in the call to the custom action, and thus the install, to fail. e.g.

/MyVal="[SOMEPROPERTY]\"

The solution to this issue is to escape the closing quote on any property which is likely to contain a directory path (e.g. TARGETDIR), although it may be safest to escape all closing quotes. The command used to fire the custom action will then look like the following and the quotes will be correctly interpreted

(Action=_EBBC1917_9B5F_478F_BEC3_3242496333D4.install,
ActionType=3073,Source=BinaryData,Target=ManagedInstall,
CustomActionData=/installtype=notransaction /action=install
/LogFile= /MyVal="C:\MyFolder\\" )

del.ico.us del.ico.us | Digg It Digg It | Technorati Technorati | StumbleUpon StumbleUpon | Furl Furl | reddit reddit

Global Code Analysis Suppressions Compromise Effectiveness of Obfuscation

One of the things I always do when I start a new C# project is turn on Code Analysis for the project and set all of the rules to return errors rather than warnings. This might seem rather draconian but once you get used to the rules it's fairly straightforward to write code that complies with them. Just now and again though you get tripped up by a rule and you can't change the way the code is implemented for a perfectly valid reason. In these specific cases you can prevent the rule from causing problems by suppressing the rule. Rule suppression works by adding a rule suppression attribute either to the code entity (class, method, property, field, etc) directly or in a GlobalSuppressions.cs file.

One of the other things I regularly do is enable obfuscation of my code using an obfuscation tool as I want to have maximum protection of my intellectual property. I usually pick the obfuscator settings so that it munges the binaries as much as possible and removes any intelligence about my code that namespace and type names might reveal.

Imagine my surprise then when I opened up an obfuscated assembly in Reflector the other day and choose to disassemble an assembly that had global code analysis rule suppressions. There, sat in the assembly metadata in plaintext, was the code analysis global rule suppression attributes with the names of the types, etc to which they apply. There are some other non-global rule suppression attributes in the same assembly but they are just attached to an (obfuscated) type/field and because they contain no name information these attributes do not reveal any additional information that might help someone trying to reverse engineer my code. However the global attributes might do - although the types they apply to are obfuscated, the global attributes contain the plaintext name information which gives some information about the organisation and possible functionality of my code. Although it doesn't give much to go on, it does give an attacker a bit of an advantage.

So in short, if you want to maximise your protection through obfuscation, watch out for those global code analysis suppressions.

del.ico.us del.ico.us | Digg It Digg It | Technorati Technorati | StumbleUpon StumbleUpon | Furl Furl | reddit reddit

Using Demeanor Obfuscate MSBuild task with a Team Foundation Build

One of the tools I have used extensively for a number of years is Wise Owl's Demeanor obfuscator. I did quite a bit of research on available obfuscators some time back and settled on Demeanor as it offered the best balance of features, performance, flexibility and price and I've been happy with the purchase (and the excellent support which Wise Owl provide) ever since.

One of the features of Demeanor Enterprise Edition provides is an Obfuscate task for MSBuild which can be used to obfuscate a project during the build process. I've not made use of this until recently but I have just used it in a project I'm currently working on. I included the Obfuscate task in the MSBuild project file and when built inside Visual Studio it worked a treat, obfuscating my code as I intended.

Things didn't work quite so well when the project was build as part of a Team Build on our Team Foundation Server. Every time the obfuscate task was called it would bomb out and the build would fail. I've spoken to Brent at Wise Owl and it appears that Demeanor installs itself on a per-user basis to avoid the need for administrative permissions during install. When Demeanor installs, it creates a named value (“InstallationFolder”) under the “HKEY_CURRENT_USER\Software\Wise Owl Consulting LLC\Demeanor for .NET, Enterprise Edition\v4.0” registry key recording the installation folder. The Obfuscate task uses this registry entry to locate the Demeanor executable. If it the registry value doesn't exist then the Obfuscate task fails.

This was the problem affecting our Team Foundation Server. Demeanor had been installed in the context of an administrative account but Team Builds execute in the context of the build service account. Because Demeanor wasn't installed from the build service account, the registry value did not exist in the build service account's HKEY_CURRENT_USER hive. Manually adding the value fixes the problem and hey presto! The Obfuscate task now works perfectly on both team and local builds.

del.ico.us del.ico.us | Digg It Digg It | Technorati Technorati | StumbleUpon StumbleUpon | Furl Furl | reddit reddit