Using Dotfuscator to obfuscate code within TeamCity

I recently had the need to use Dotfuscator for some code that was built automatically with TeamCity. Dotfuscator is designed as a standalone product that you build up a "project" file for. There is some integration with Visual Studio, but I couldn't find much help when trying to ensure dotfuscator was running when I was building code within TeamCity. So this post is trying to fix that :)

Our secret application

The application we're going to obfuscate is incredibly simple. It's just a C# console app with two classes:

If we compile this program and examine it with DotPeek the code comes back in exactly the same form (shocking I know):

The problem

The challenge here is that Dotfuscator is primarily a GUI application. You get started by creating a new Dotfuscator project file and adding the DLLs you want obfuscated to it.

dotfuscator UI

Once you've added your files you can set whatever obfuscation properties you require, and run the obfuscation by clicking the play button in the UI. By default it will put the obfuscated files in a folder called Dotfuscated, but this of course is configurable.

Just for completeness, if we examine this new exe we'll see that the obfuscation ran, and our code still runs as expected: how secure that really is isn't my concern today :)

Integrating obfuscation into the TeamCity build process

While dotfuscator has a nice GUI for running obfuscation, that isn't really going to help us within a continuous integration pipeline like the one TeamCity provides. So we need to set things up so we can take advantage of Dotfuscator's command line tool. Another challenge is that not everyone who's working on your code might have a license for Dotfuscator. This means we need to guard against the chance that it's not available, and let others continue to build the code.

I spent a good amount of time searching for a clean solution, and even talked with our sales rep, and couldn't find anything cleaner than what I'm going to demonstrate next. If you know of something better please tell me!

Here are the overall steps involved

  1. Add the dotfuscator project file to the visual studio solution
  2. Update the Dotfuscator project file to use relative paths
  3. Use the Post-Build event available within a Visual Studio project file to run Dotfuscator
  4. Require an environment variable be defined before executing the dotfuscator code
    • This also works nicely because environment variables are easy to define in TeamCity
  5. Setup the environment variable withing TeamCity
Steps 1 & 2

Adding the solution file is easy enough, but in order for the process to work within TeamCity we need to update it to remove the full paths it will have to your files. If we open the dotfuscator file we can see the problem:

The good news is this is simple enough to fix. We can replace those paths with the ${configdir} variable that Dotfuscator provides and everything will work:

Steps 3 & 4

Once we have the project file ready we need to add the following as a post-build step in the application's project properties

REM +-----------------------------------------------------------------------  
REM |   DOTFUSCATION (for team city builds only!)  
REM +-----------------------------------------------------------------------  
if "%TEAM_CITY_BUILD%" == "TRUE" (  
  echo "Running obfuscation process"
  REM Run dotfuscator to produce obfuscated DLLs since this
  REM is the last project in the build order
  "%DOTFUSCATOR_EXE_PATH%" /v "$(ProjectDir)\dotfuscator.project.xml"

  REM Now copy over the obfuscated DLLs into the bin folder
  xcopy /y $(ProjectDir)\Dotfuscated $(ProjectDir)\Bin

In that code we're requiring that an environment variable called TEAM_CITY_BUILD be defined before running the dotfuscator. We also need to add a variable for DOTFUSCATOR_EXE_PATH to the command line version of the tool. Assuming it's defined, we run the dotfuscator exe and pass it the project file we created from the GUI app. Once it completes we simply use xcopy to replace the DLLs already built with the obfuscated versions.

What's nice about this process is that if you have any steps following this one in TeamCity (like packaging for Octopus Deploy or creating a ClickOnce package) they'll just use the obfuscated versions automatically since they replaced the originals.

Step 5

The last step is to update your TeamCity project to include the environment variable this post-build step will be looking for:

Of course this assumes that you have a licensed installation of Dotfuscator on your build server, and that the paths match your own system.

Wrapping up

So in this post I laid out an approach to obfuscating code as part of a CI process built with TeamCity. This approach allows you to conditionally obfuscate code using an environment variable, which provides options when you have multiple developers building the code locally that might not be licensed for Dotfuscator. It also works within a larger series of steps that don't want to worry about pulling files from the Dotfuscated folder generated in the process.

Hope you found this helpful, and if you have any suggestions on how to improve this please let me know!

comments powered by Disqus