It’s been quite a while since I’ve done a GeeksWithBlogs.net Influencers review. This time, I had the opportunity to check out Red Gate’s SmartAssembly 6.1, which is a integrated collection of tools for modifying and manipulating assemblies. Now that phrase, while accurate, is quite a mouthful. So what all does it actually do?
SmartAssembly has the ability to:
- Do strong name signing;
- Add automated error reporting;
- Add feature usage reporting;
- Merge dependencies (e.g. libraries) into the main assembly (which lets you then apply obfuscation, etc., to them as well);
- Embed dependencies into the main assembly (letting you compress and encrypt them in the process, making deployment faster and maintenance nicer);
- Automatically prune unneeded metadata like the names of events, properties, and methods (making the assembly smaller such that it’ll run faster and also be much harder to understand if decompiled);
- Obfuscate the names of classes, methods (including properties), and fields, with varying levels of configurability;
- Obfuscate control flow (modifying the order that code runs in (without messing up how it works) such that the logic becomes very hard to understand;
- Have a proxy created at runtime for accessing methods, properties, and fields in external assemblies, which makes any attempts to modify the assembly render it non-working;
- Compress and encrypt resources;
- Encode non-constant strings, which is useful if you need to store sensitive information such as passwords, SQL queries, etc., in your code;
- Apply several other protections and optimizations (e.g. sealing classes that aren’t inherited from);
- Generate debugging information which will let you debug your modified assembly in your IDE; and
- Integrate into your build process.
Which of these you can use depends on the type of assembly you’re working with; various features aren’t available due to technology limitations, e.g. the automated error reporting and feature usage aren’t available on Xbox 360 XNA games since there’s no external internet access. For a list of feature compatibility, go to the Release notes - version 6.xx and scroll down to “Known limitations”, which includes a helpful chart of what is available in what.
Well, the first thing I expect many people will notice is that it does obfuscation. But I don’t want to talk about that first, since SmartAssembly does a lot of other really neat stuff that probably gets overlooked. One thing I found really interesting was the Automated Error Reporting functionality. You just check a box and it automatically adds a wrapper for unhandled exceptions that lets your users send in anonymous error reports either to the free service that Red Gate operates or, if you have SmartAssembly Pro, to your own server using the web service software they provide: Setting up a custom web server for reporting . The thing I really liked about it is that it works with Windows Phone 7 (both Silverlight and XNA), which is my primary development target these days. You get a full stack trace, including the values of various variables (try saying that five times fast :) ), along with a lot of in-depth information, such as the version of the app, the OS (including version), the CLR version, and a hash of the UserHostAddress (which should help you to sort out which reports come from which users). You can set flags on them, save the reports for browsing later, and mark your progress. This is an example of a saved report:
As you can see, it provides a lot of helpful information, including the exception message (in my case, the fact that I haven’t implemented the “Open” button yet, which is something I knew). The other thing Automated Error Reporting will do is try to recover from the exception if possible. In the case of my unimplemented “Open” button, it popped up the little notification message box telling me there was a problem, asking me if I’d like to send details about it in to help improve the software, and then continued on as though I hadn’t pressed the problematic button. This is one of those features that I know I’d normally overlook or pay little attention to, which is why I’m making a big deal about it. The quality of its implementation, the ease of applying it (as simple as just checking a box), and the value that it adds to a .NET application makes SmartAssembly worth its price on this feature alone in my opinion. Have a look at some of the walkthroughs and videos for more information; they’re short and interesting.
On to obfuscation. If you don’t know what obfuscation is, it’s basically the application of various techniques in order to make it very hard if not impossible for someone to take your .NET program and see its source code using a disassembly tool like ildasm.exe or .NET Reflector. I did several tests of its obfuscation capabilities based on projects I’ve completed, that I’m actively working on, and that I just use for testing. My experiences were mostly good.
I struggled a bit getting my WP7 Silverlight app to obfuscate properly and still work correctly. The obfuscated version was having issues with parsing XAML, which is not surprising given that XAML data binding relies on reflection to work properly and obfuscation messes around with all sorts of things.
It turned out that my problem was event handler methods. Specifically, in various places in my XAML, I was handling events with methods in my code-behind class for the Page that weren’t marked public. For Silverlight projects, SmartAssembly automatically excludes anything marked public from name mangling; anything else gets mangled by default. The solution in this case was to either mark those methods public and let SmartAssembly automatically exclude them, to go and mark them manually (when you put a check next to an assembly in the Obfuscation area, a link called “Exclusions” appears next to it, letting you go in and exclude any namespaces, class names, methods (including custom property getters and setters), and fields that you want from having their names mangled), or to apply custom attributes to them. The last solution is probably the best to go with since once it’s all setup, it’ll just work no matter what you change or refactor in your code.
Once I fixed that issue, almost everything worked. I also had an issue with a third-party control that I was using. I was hoping to be able to merge it and obfuscate it (either name mangling, control flow obfuscation, or both), but it was not meant to be. SmartAssembly recommends against trying to merge third party libraries, suggesting that you embed them instead. Embedding isn’t an option for WP7 though, so I had to modify how I was using the control. Initially I had it as a project within my solution and was including it as a reference as a project. What I did instead was build it as a separate DLL and apply obfuscation to it with a separate SmartAssembly project. I then included a reference to the obfuscated, pre-built DLL and it worked like a charm. Many would consider that a ‘best practice’ way of doing such a thing anyway (seeing as you’re rarely, if ever, going to be changing that third party code so rebuilding it all the time makes no sense), so it was a double benefit in that regard. With that issue resolved, everything worked great.
Next I tested a WP7 XNA app that I’m working on. Once I set up the SmartAssembly project, it worked perfectly the very first time. The error reporting was very simple to integrate and a really pleasant surprise (I created an intentional exception, which you can see in the screen shot above, just to test it). It’s something I will definitely be making use of and something I highly recommend to everyone. Do note that if you setup the automated error reporting to email you for every error report, it will indeed do this. If you publish something and a lot of people get errors in one particular place (which might be very common when doing a beta test, for example), you can expect to get a lot of emails as a result. So I recommend dedicating a special email address for that feature, if you choose to use it, so that you don’t wake up to find your personal inbox chock full of messages. :) (This did not happen to me, but I could easily see it happening by accident, especially for something that you’re just doing a private beta for such that you might not think to use a special email address for it.)
The last thing I tested was my PC XNA sandbox app. I have over half a dozen “sandbox” apps that I use to test out various things in different languages and technologies. I most recently used my PC XNA sandbox app to do some stress testing on SpriteBatch draw calls. So it was already setup to not run particularly well, making it a good measuring platform for how SmartAssembly’s various components affect performance. I modified the project to use a WeakReference to track when GC collections occurred, added in a pointless allocation of 5000 bytes every frame, then used a StreamWriter to write out GC data every time a collection occurred; specifically: the current count of collections, current total game time, the time difference (delta) from the last GC collection, and the average time delta between collections. It was already setup to display the current frame rate. For the SmartAssembly-modified version, I initially set it to use Automated Error Reporting, Obfuscation (Types/Methods Name Mangling set to “I want to obfuscate using only Unicode unprintable characters.” and Fields Name Mangling set to “I want to use standard renaming, so that all the fields in a class have a different name.”), Control Flow Obfuscation, and to seal all classes that are not inherited (under Other Optimizations).
The results? The original Release build (run as a standalone program outside of Visual Studio) and the SmartAssembly-modified version of that Release build ran virtually identically. Both maintained the same frame rate hovering between 34 and 35 fps. I stopped both after they hit a GC count of 21 (I didn’t track any GCs that occurred during the LoadContent phase, just during gameplay). The original Release build hit its 21st GC at 61.950 seconds in with an average delta of 2.950 seconds. The SA version hit its 21st GC at 62.067 seconds in with an average delta of 2.956 seconds. Yep, the SA version actually won (though just barely; it could easily be due to my computer doing stuff in the background). Since the test was to see if it hurt performance, and (with several runs of each just to avoid obvious anomalies) they both ran virtually the same, it seemed clear to me that it didn’t.
Just for kicks, I created another SmartAssembly version, this time with everything enabled except for Generate Debugging Information, Strings Encoding, and Strong Name Signing. I also turned up obfuscation to its maximum levels. I also ran this one several times just to confirm the results weren’t anomalous. This build runs an initial GC much earlier than the other tests (after ~1.7 seconds versus ~2.7 seconds for the previous two builds), but then runs virtually the same as the others. An example run hit its 21st GC at 61.065 seconds in with an average delta of 2.908 seconds. But if you exclude the first collection and average the other 20, you wind up with an average delta of 2.969 seconds. The other builds also showed a slightly early first GC so I’m willing to call them functionally identical. What all these tests told me is that this is a great way to protect my programs without any noticeable performance hits.
The last thing I want to talk about is the ability to automatically integrate a SmartAssembly project into your build pipeline. You can add SmartAssembly in to the MSBuild process or into a ClickOnce deployment. I’ve done this for a couple of projects already and it’s quite easy and very smooth. Some projects (e.g. XNA games) generate extra content that needs to be copied into the output directory (for XNA games, any of your content folders along with their XNB files). You can either configure an MSBuild Copy Task to do that for you (just add it in with the SmartAssembly Task into the AfterBuild target) or you can do it by hand or some other way. So just take note that SmartAssembly (when integrated into MSBuild at least; I haven’t tested ClickOnce) only creates the assembly or assemblies that it is operating on at its destination and that it’s up to you to setup a mechanism for copying any other content files that your assembly depends on.
Just as with ANTS Memory Profiler, I found I really liked SmartAssembly. It’s easy to work with, the help materials and learning videos are really good, and it makes a variety of otherwise complicated tasks very easy to do. There’s a 14-day, fully functional free trial for it, so if you’re on the fence, go check it out and see what you think.
(n.b. While I was finishing up this review, version 6.2 was released. The release notes indicate that the changes are very small and shouldn’t have an impact on anything I discussed above. They do mention the provision of sample error reports, which I think is a great addition since it lets you see what you’ll be getting from error reports without having to spend time creating your own.)