Exploring the features of Dotfuscator Software Services - Community Edition, a free post-build hardening and application monitoring tool that ships with Visual Studio
Understanding how obfuscation can be used to prevent your assemblies from being easily decompiled
Using tamper defense to protect your application assemblies from unauthorized modification
Configuring application expiry to encode a specific date after which your application can't be executed
Setting up usage tracking to determine what applications and features are being used
If you've peeked under the covers at the details of how .NET assemblies are executed, you will have picked up on the fact that instead of compiling to machine language (and regardless of the programming language used), all .NET source code is compiled into the Microsoft Intermediary Language (MSIL, or just IL, for short). The IL is then just-in-time compiled when it is required for execution. This two-stage approach has a number of significant advantages, such as allowing you to dynamically query an assembly for type and method information, using reflection. However, this is a double-edged sword, because this same flexibility means that once-hidden algorithms and business logic can easily be reverse-engineered and modified, legally or otherwise. This chapter introduces tools and techniques that will help to protect your source code from prying eyes and monitor the execution of your applications.
Before looking at how you can protect your code from other people and monitor its behavior "in the wild," this section describes a couple of tools that can help you build better applications. The first tool is the Microsoft .NET Framework IL Disassembler, or IL Dasm. This is included as part of the Microsoft Windows SDK, which is installed by default with Visual Studio 2010. You can find it under Start
To compare the IL that is generated, the original source code for the MathematicalGenius
class is as follows:
namespace ObfuscationSample { public class MathematicalGenius { public static Int32 GenerateMagicNumber(Int32 age, Int32 height) { return (age * height) + DateTime.Now.DayOfYear; } } }
Namespace ObfuscationSample Public Class MathematicalGenius Public Shared Function GenerateMagicNumber(ByVal age As Integer, _ ByVal height As Integer) As Integer Return (age * height) + Today.DayOfWeek
End Function End Class End Namespace
Double-clicking the GenerateMagicNumber
method in IL Dasm opens up an additional window that shows the IL for that method. Figure 47-2 shows the IL for the GenerateMagicNumber
method, which represents your super-secret, patent-pending algorithm. In actual fact, anyone who is prepared to spend a couple of hours learning how to interpret MSIL could quickly work out that the method simply multiplies the two int32
parameters, age
and height
, and then adds the current day of the year to the result.
For those who haven't spent any time understanding how to read MSIL, a decompiler can convert this IL back into one or more .NET languages.
One of the most widely used decompilers is .NET Reflector from Red Gate Software (available for download at www.red-gate.com/products/reflector/
). Reflector can be used to decompile any .NET assembly into C#, Visual Basic, Managed C11, and even Delphi. In Figure 47-3, the same assembly you just accessed is opened using IL Dasm, in Reflector.
In the pane on the left of Figure 47-3, you can see the namespaces, type, and method information in a layout similar to IL Dasm. Double-clicking a method should open the Disassembler pane on the right, which displays the contents of that method in the language specified in the toolbar. In this case, you can see the C# code that generates the magic number, which is almost identical to the original code.
You may have noticed in Figure 47-3 that some of the .NET Framework base class
library assemblies are listed, including System, System.Data, and System.Web. Because obfuscation has not been applied to these assemblies, they can be decompiled just as easily using Reflector. However, Microsoft has made large portions of the actual .NET Framework source code publicly available, which means you can browse the original source code of these assemblies including the inline comments. This is shown in Chapter 43.
If the generation of the magic number were a real secret on which your organization made money, the ability to decompile this application would pose a significant risk. This is made worse
when you add the Reflector.FileDisassembler add-in, written by Denis Bauer (available at www.denisbauer.com/NETTools/FileDisassembler.aspx
). With this add-in, an entire assembly can be decompiled into source files, complete with a project file.
So far, this chapter has highlighted the need for better protection for the logic that is embedded in your applications. Obfuscation is the art of renaming symbols and modifying code paths in an assembly so that the logic is unintelligible and can't be easily understood if decompiled. Numerous products can obfuscate your code, each using its own tricks to make the output less likely to be understood. Visual Studio 2010 ships with the Community Edition of Dotfuscator Software Services from PreEmptive Solutions, which this chapter uses as an example of how you can apply obfuscation to your code.
Obfuscation does not prevent your code from being decompiled; it simply makes it more difficult for a programmer to understand the source code if it is decompiled. Using obfuscation also has some consequences that need to be considered if you need to use reflection or strong-name your application.
Although Dotfuscator can be launched from the Tools menu within Visual Studio 2010, it is a separate product with its own licensing. The Community Edition (CE) contains only a subset of the functionality of the commercial edition of the product, the Dotfuscator Suite. If you are serious about trying to hide the functionality embedded in your application, you should consider upgrading. You can find more information on the commercial version of Dotfuscator at www.preemptive.com/products/dotfuscator/compare-editions
.
Dotfuscator CE uses its own project format to keep track of which assemblies you are obfuscating and any options that you specify. After starting Dotfuscator from the Tools menu, it opens with a new unsaved project. Select the Input Assemblies node in the navigation tree, and then click the button with an ellipsis (...) under the Assembly Name listing to add the .NET assemblies that you want to obfuscate. Figure 47-4 shows a new Dotfuscator project into which has been added the assembly for the application from earlier in this chapter.
Unlike other build activities that are typically executed based on source files, obfuscation is a post-build activity that works with an already compiled set of assemblies. Dotfuscator takes an existing set of assemblies, applies the obfuscation algorithms to the IL, and generates a set of new assemblies.
Without needing to adjust any other settings, you can select Build Project from the Build menu, or click the "play" button (fourth from the left) on the toolbar, to obfuscate this application. If you have saved the Dotfuscator project, the obfuscated assemblies will be added to a Dotfuscated folder under the folder where the project was saved. If the project has not been saved, the output is written to c:Dotfuscated
.
If you open the generated assembly using Reflector, as shown in Figure 47-5, you will notice that the GenerateMagicNumber
method has been renamed, along with the input parameters. In addition, the namespace hierarchy has been removed and classes have been renamed. Although this is a rather simple example, you can see how numerous methods with similar, non-intuitive names could cause confusion and make the source code very difficult to understand when decompiled.
The free version of Dotfuscator only obfuscates assemblies by renaming classes, variables, and functions. The commercial version employs several additional methods to obfuscate assemblies, such as modifying the control flow of the assembly and performing string encryption. In many cases, control flow will actually trigger an unrecoverable exception inside decompilers, effectively preventing automated decompilation.
The previous example obfuscated the public method of a class, which is fine if the method will only be called from assemblies obfuscated along with the one containing the class definition. However, if this was a class library or API that will be referenced by other, unobfuscated applications, you would see a list of classes that have no apparent structure, relationship, or even naming convention. This would make working with this assembly very difficult. Luckily, Dotfuscator enables you to control what is renamed during obfuscation. Before going ahead, you will need to refactor the code slightly to pull the functionality out of the public method. If you didn't do this and you excluded this method from being renamed, your secret algorithm would not be obfuscated. By separating the logic into another method, you can obfuscate that while keeping the public interface unchanged. The refactored code would look like the following:
namespace ObfuscationSample { public class MathematicalGenius { public static Int32 GenerateMagicNumber(Int32 age, Int32 height) { return SecretGenerateMagicNumber(age, height); } private static Int32 SecretGenerateMagicNumber(Int32 age, Int32 height) { return (age * height) + DateTime.Now.DayOfYear; } } }
Namespace ObfuscationSample Public Class MathematicalGenius Public Shared Function GenerateMagicNumber(ByVal age As Integer, _ ByVal height As Integer) As Integer Return SecretGenerateMagicNumber(age, height) End Function Private Shared Function SecretGenerateMagicNumber(ByVal age As Integer, _
ByVal height As Integer) As Integer Return (age * height) + Today.DayOfWeek End Function End Class End Namespace
After rebuilding the application, you will need to reopen the Dotfuscator project by selecting it from the Recent Projects list. You have several different ways of selectively applying obfuscation to an assembly. First, you can enable Library mode on specific assemblies by selecting the appropriate checkbox on the Input Assemblies screen (see Figure 47-4). This has the effect of keeping the namespace, class name, and all public properties and methods intact, while renaming all private methods and variables. Second, you can manually select which elements should not be renamed from within Dotfuscator. To do this, open the Renaming item from the navigation tree, shown in Figure 47-6.
The Renaming dialog opens on the Exclusions tab where you can see the familiar tree view of your assembly, with the attributes, namespaces, types, and methods listed. As the name of the tab suggests, this tree enables you to exclude certain elements from being renamed. In Figure 47-6, the GenerateMagicNumber
method, as well as the class that it is contained in, is excluded (otherwise, you would have ended up with something like b.GenerateMagicNumber
, where b
is the renamed class). In addition to explicitly choosing which elements will be excluded, you can also define custom rules that can include regular expressions.
After you build the Dotfuscator project, click the Results item in the navigation tree. This screen shows the actions that Dotfuscator performed during obfuscation. The new name of each class, property, and method is displayed as a sub-node under each renamed element in the tree. You will see that the MathematicalGenius
class and the GenerateMagicNumber
method have not been renamed, as shown in Figure 47-7.
The SecretGenerateMagicNumber
method has been renamed to a
, as indicated by the sub-node with the Dotfuscator icon.
In the previous example you saw how to choose which types and methods to obfuscate within Dotfuscator. Of course, if you were to start using a different obfuscating product you would have to configure it to exclude the public members. It would be more convenient to be able to annotate your code with attributes indicating whether a symbol should be obfuscated. You can do this by using the Obfuscation
and ObfuscationAssemblyAttribute
attributes from the System.Reflection
namespace.
The default behavior in Dotfuscator is to override exclusions specified in the project with the settings specified by any obfuscation attributes. In Figure 47-4 is a series of checkboxes for each assembly added to the project, of which one is Honor Obfuscation Attributes. You can change the default behavior so that any exclusions set within the project take precedence by unchecking the Honor Obfuscation Attributes option on a per-assembly basis.
The ObfuscationAssemblyAttribute
attribute can be applied to an assembly to control whether it should be treated as a class library or as a private assembly. The distinction is that with a class library it is expected that other assemblies will be referencing the public types and methods it exposes. As such, the obfuscation tool needs to ensure that these symbols are not renamed.
Alternatively, as a private assembly, every symbol can be potentially renamed. The following is the syntax for ObfuscationAssemblyAttribute
:
[assembly: ObfuscateAssemblyAttribute(false, StripAfterObfuscation=true)]
<Assembly: ObfuscateAssemblyAttribute(False, StripAfterObfuscation:=True)>
The two arguments that this attribute takes indicate whether it is a private assembly and whether to strip the attribute off after obfuscation. The preceding snippet indicates that this is not a private assembly, and that public symbols should not be renamed. In addition, the snippet indicates that the obfuscation attribute should be stripped off after obfuscation — after all, the less information available to anyone wishing to decompile the assembly, the better.
Adding this attribute to the AssemblyInfo.cs
or AssemblyInfo.vb
file will automatically preserve the names of all public symbols in the ObfuscationSample application. This means that you can remove the exclusion you created earlier for the GenerateMagicNumber
method.
The downside of the ObfuscationAssemblyAttribute
attribute is that it exposes all the public types and methods regardless of whether they existed for internal use only. On the other hand, the ObfuscationAttribute
attribute can be applied to individual types and methods, so it provides a much finer level of control over what is obfuscated. To illustrate the use of this attribute, refactor the example to include an additional public method, EvaluatePerson
, and place the logic into another class, HiddenGenius
:
namespace ObfuscationSample { [System.Reflection.ObfuscationAttribute(ApplyToMembers=true, Exclude=true)] public class MathematicalGenius { public static Int32 GenerateMagicNumber(Int32 age, Int32 height) { return HiddenGenius.GenerateMagicNumber(age, height); } public static Boolean EvaluatePerson(Int32 age, Int32 height) { return HiddenGenius.EvaluatePerson(age, height); } } [System.Reflection.ObfuscationAttribute(ApplyToMembers=false, Exclude=true)] public class HiddenGenius { public static Int32 GenerateMagicNumber(Int32 age, Int32 height) {
return (age * height) + DateTime.Now.DayOfYear; } [System.Reflection.ObfuscationAttribute(Exclude=true)] public static Boolean EvaluatePerson(Int32 age, Int32 height) { return GenerateMagicNumber(age, height) > 6000; } } }
Namespace ObfuscationSample <System.Reflection.ObfuscationAttribute(ApplyToMembers:=True,Exclude:=True)> _ Public Class MathematicalGenius Public Shared Function GenerateMagicNumber(ByVal age As Integer, _ ByVal height As Integer) As Integer Return HiddenGenius.GenerateMagicNumber(age, height) End Function Public Shared Function EvaluatePerson(ByVal age As Integer, _ ByVal height As Integer) As Boolean Return HiddenGenius.EvaluatePerson(age, height) End Function End Class <System.Reflection.ObfuscationAttribute(ApplyToMembers:=False,Exclude:=True)> _ Public Class HiddenGenius Public Shared Function GenerateMagicNumber(ByVal age As Integer, _ ByVal height As Integer) As Integer Return (age * height) + Today.DayOfWeek End Function <System.Reflection.ObfuscationAttribute(Exclude:=True)> _ Public Shared Function EvaluatePerson(ByVal age As Integer, _ ByVal height As Integer) As Boolean Return GenerateMagicNumber(age, height) > 6000 End Function End Class End Namespace
In this example, the MathematicalGenius
class is the class that you want to expose outside of this library. As such, you want to exclude this class and all its methods from being obfuscated. You do this by applying the ObfuscationAttribute
attribute with both the Exclude
and ApplyToMembers
parameters set to True
.
The second class, HiddenGenius
, has mixed obfuscation. As a result of some squabbling among the developers who wrote this class, the EvaluatePerson
method needs to be exposed, but all other methods in this class should be obfuscated. Again, the ObfuscationAttribute
attribute is applied to the class so that the class does not get obfuscated. However, this time you want the default behavior to be such that symbols contained in the class are obfuscated, so the ApplyToMembers
parameter is set to False
. In addition, the Obfuscation
attribute is applied to the EvaluatePerson
method so that it will still be accessible.
In a couple of places it is worth considering what will happen when obfuscation — or more precisely, renaming — occurs, and how it will affect the workings of the application.
The .NET Framework provides a rich reflection model through which types can be queried and instantiated dynamically. Unfortunately, some of the reflection methods use string lookups for type and member names. Clearly, the use of renaming obfuscation will prevent these lookups from working, and the only solution is not to mangle any symbols that may be invoked using reflection. Note that control flow obfuscation does not have this particular undesirable side-effect. Dotfuscator's smart obfuscation feature attempts to automatically determine a limited set of symbols to exclude based on how the application uses reflection. For example, say that you are using the field names of an enum type. Smart obfuscation will detect the reflection call used to retrieve the enum's field name, and then automatically exclude the enum fields from renaming.
One of the purposes behind giving an assembly a strong name is that it prevents the assembly from being tampered with. Unfortunately, obfuscating relies on being able to take an existing assembly and modify the names and code flow, before generating a new assembly. This would mean that the assembly no longer has a valid strong name. To allow obfuscation to occur you need to delay signing of your assembly by checking the Delay Sign Only checkbox on the Signing tab of the Project Properties window, as shown in Figure 47-8.
After building the assembly, you can then obfuscate it in the normal way. The only difference is that after obfuscating you need to sign the obfuscated assembly, which you can do manually using the Strong Name utility, as shown in this example:
sn -R ObfuscationSample.exe ObfuscationKey.snk
The Strong Name utility is not included in the default path, so you will either need to run this from a Visual Studio Command Prompt (Start
As displayed on the Project Properties window, checking the Delay Sign Only box prevents the application from being able to be run or debugged. This is because the assembly will fail the strong-name verification process. To enable debugging for an application with delayed signing, you can register the appropriate assemblies for verification skipping. This is also done using the Strong Name utility. For example, the following code will skip verification for the ObfuscationSample.exe
application:
sn -Vr ObfuscationSample.exe
Similarly, the following will reactivate verification for this application:
sn -Vu ObfuscationSample.exe
This is a pain for you to have to do every time you build an application, so you can add the following lines to the post-build events for the application:
"$(DevEnvDir)......Microsoft SDKsWindowsv7.0Ainsn.exe" -Vr "$(TargetPath)" "$(DevEnvDir)......Microsoft SDKsWindowsv7.0Ainsn.exe" -Vr "$(TargetDir)$(TargetName).vshost$(TargetExt)"
Depending on your environment, you may need to modify the post-build event to ensure that the correct path to sn.exe
is specified.
The first line skips verification for the compiled application. However, Visual Studio uses an additional vshost file to bootstrap the application when it executes. This also needs to be registered to skip verification when launching a debugging session.
The version of Dotfuscator that ships with Visual Studio 2010 has a whole lot of new functionality for adding run time monitoring and management functionality to your applications. As with obfuscation, these new capabilities are injected into your application as a post-build step, which means you typically don't need to modify your source code in any way to take advantage of them.
The new application monitoring and management capabilities include:
Tamper Defense: Exits your application, and optionally notifies, if it has been modified in an unauthorized manner.
Application Expiry: Configure an expiration date for your application, after which it will no longer run.
Application Usage Tracking: Instrument your code to track usage, including specific features within your application.
To enable the new monitoring and management functionality you must enable instrumentation for your Dotfuscator project. Select the Instrumentation item from the navigation tree and select the Options tab. Select the Enable Instrumentation option as shown in Figure 47-9.
Once instrumentation has been enabled, you can specify the new functionality to be injected into your application by adding Dotfuscator attributes — either as a custom attribute within your source code, or through the Dotfuscator UI.
Tamper defense provides a way for you to detect when your applications have been modified in an unauthorized manner. Whereas obfuscation is a preventative control designed to reduce the risks that stem from unauthorized reverse engineering, tamper defense is a detective control designed to reduce the risks that stem from unauthorized modification of your managed assemblies. The pairing of preventative and detective controls is a widely accepted risk management pattern, for example, fire prevention and detection.
Tamper defense is applied on a per-method basis, and tamper detection is performed at run time when a protected method is invoked.
To add tamper defense to your application, select the Instrumentation item from the navigation menu and then select the Attributes tab. You will see a tree that contains the assemblies you have added to the Dotfuscator project with a hierarchy of the classes and methods that each assembly contains. Navigate to the HiddenGenius.GenerateMagicNumber
function, right-click it, and select Add Attribute. This displays the list of available Dotfuscator attributes as shown in Figure 47-10.
Select the InsertTamperCheckAttribute
attribute and click OK. The attribute is added to the selected method and the attribute properties are listed as shown in Figure 47-11. Finally, select the ApplicationNotificationSinkElement
property and change the value to DefaultAction
.
You can now build the Dotfuscator project to inject the tamper defense functionality into your application.
To help you test the tamper defense functionality, Dotfuscator ships with a simple utility that simulates tampering of an assembly. Called TamperTester, you can find this utility in the same directory in which Dotfuscator is installed (by default C:Program FilesMicrosoft Visual Studio 10.0PreEmptive SolutionsDotfuscator Community Edition
). This should be run from the command line with the name of the assembly and the output folder as arguments:
tampertester ObfuscationSample.exe c: amperedapps
Make sure you run the TamperTester utility against the assemblies that were generated by Dotfuscator and not the original assemblies built by Visual Studio.
By default, your application will immediately exit if the method has been tampered with. You can optionally configure Dotfuscator to generate a notification message to an endpoint of your choosing. The commercial edition of Dotfuscator includes two primary extensions to the CE version; it allows you to add a custom handler to be executed when tampering is detected supporting a custom real-time tamper defense in lieu of the default exit behavior; and PreEmptive Solutions offers a notification service that accepts tamper alerts and automatically notifies your organization as an incident response.
The term Runtime Intelligence (RI) refers to technologies and managed services for the collection, integration, analysis, and presentation of application usage patterns and practices. In Visual Studio 2010, Dotfuscator CE can inject RI instrumentation into your assemblies to stream session and feature usage data to an arbitrary endpoint. The following sections describe how to use Dotfuscator's Runtime Intelligence instrumentation and some of the free and for-fee Runtime Intelligence analytics options that are available.
To use Dotfuscator CE instrumentation, you must first enable it within your Dotfuscator project. Click the Instrumentation item in the navigation tree and select the Options tab. Ensure that all the options under Runtime Intelligence Configuration are enabled, as shown in Figure 47-12.
Next you must add some attributes to your assemblies to ensure that they can be uniquely identified and any instrumentation data can be accessed. Under the Attributes tab of the Instrumentation node is the class hierarchy of any assemblies you have added to Dotfuscator. Right-click each of the top-level nodes (the node that contains the full path to your assembly), select Add Attribute, and add a new BusinessAttribute
attribute.
Select CompanyKey
from the attribute properties listing. This attribute provides a unique identifier for your company and should be the same across all of your assemblies. You can click the button labeled with the ellipsis to generate a new CompanyKey
. Also enter a value for the CompanyName
property that will be displayed in the portal and any reports.
Repeat this to add a new ApplicationAttribute
attribute to your assemblies. The GUID
property of this attribute should contain a unique identifier that is the same across all assemblies within this application. As with the CompanyKey
property, you can generate a new value for the GUID
property by clicking the button labeled with the ellipsis. You should also enter values for the Name
and Version
properties, but the ApplicationType
property can be left blank.
Once you have added these attributes, your project should look similar to Figure 47-13.
The final step is to add SetupAttribute
and TeardownAttribute
attributes to your application. These attributes can be added to any method and are usually defined once each per application, though that is not strictly necessary if your application has multiple entry and exit points. SetupAttribute
should be placed on a method that is called soon after application startup. Likewise, the TeardownAttribute
attribute must be added to a method that is called just before the application exits. It is sometimes a good idea to create methods specifically for these attributes.
For a C# Windows Forms application, you can place the attributes on the Main
method; alternatively, you can modify the Program.cs
class by adding the AppStart
and AppEnd
methods as shown in the following listing:
static class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
AppStart();
Application.Run(new Form1());AppEnd();
}static void AppStart()
{
}
static void AppEnd()
{
}
}
For a VB Windows Forms application, you can use the Application Events functionality provided the Windows application framework to specify Startup
and Shutdown
methods as shown in the following listing:
Imports Microsoft.VisualBasic.ApplicationServices Namespace My Partial Friend Class MyApplicationPrivate Sub MyApp_Startup(ByVal sender As Object, _
ByVal e As StartupEventArgs) _
Handles Me.Startup
End Sub
Private Sub MyApp_Shutdown(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles Me.Shutdown
End Sub
End Class End Namespace
Once you have a startup
and shutdown
method defined in your application, you can add the SetupAttribute
and TeardownAttribute
attributes. Locate your startup method in the tree, then right-click it and select Add Attribute. Select SetupAttribute
and click OK. In the attribute properties you will need to specify a value for the CustomEndpoint
property, which instructs Dotfuscator where to send any instrumentation messages. Click the ellipsis button and select PreEmptive's Free Runtime Intelligence Services from the list.
You shouldn't collect information about application usage without asking permission from your users, otherwise your application could be flagged as spyware. The SetupAttribute
provides three properties to help configure this — OptInSourceElement
, OptInSourceName
, and OptInSourceOwner
.
The Runtime Intelligence Service is a managed service provided by PreEmptive Solutions that aggregates and manages Runtime Intelligence data generated by your application, and a web portal that includes runtime analytics. PreEmptive Solutions offers a free and a commercial version of its Runtime Intelligence Service.
The free version of PreEmptive's Runtime Intelligence Service is suitable only for preliminary testing purposes because it lacks sufficient security and does not include any guarantees of uptime or data retention. You should transition to the commercial version or find/build an alternative for production deployment.
Finally, add the TeardownAttribute
attribute to the appropriate method and then build the Dotfuscator project. Run the application a couple of times to generate some instrumentation messages. You can also use the TamperTester utility described in the previous section to create a "tampered" version of the application that generates Tamper notifications.
Once you have run the application a number of times, open your browser and visit http:/free.runtimeintelligence.com
. You can log on to the portal using the unique identifier you generated earlier for the CompanyKey
property.
The Welcome screen, which is displayed after you log on, shows the number of notification messages that are queued waiting to be loaded into the database. You may need to wait until any pending messages are loaded before you can view any data.
On the Application Overview menu you can view summary graphs that show data such as the application usage over time, and the operating systems and .NET Framework versions used by your users. The Application Scorecard report, shown in Figure 47-14, displays a list of all the applications registered under this Company Key, along with key metrics about their usage.
Later in this chapter you see how the usage tracking can be extended to cover usage of specific features within your application.
The application expiry feature, also known as Shelf Life, allows you to specify an expiration date for your application, after which it will no longer run. This can be useful in a number of scenarios, such as when releasing beta or trial versions of software.
Application expiry requires a Shelf Life Activation Key (SLAK). This key is issued by PreEmptive, and can be requested by visiting http://www.preemptive.com/products/shelflife
.
Two attributes are available to help implement application expiry. The InsertShelfLifeAttribute
attribute enforces the expiration dates, ensuring that the application will not run after the specified date. It can also send a notification to an arbitrary endpoint when an expired application is executed. The InsertSignOfLifeAttribute
attribute sends a notification to the Runtime Intelligence service every time your application is executed. This allows you to find out how often an application was executed.
Before adding the application expiry attributes, you should set up your assembly with the BusinessAttribute
, ApplicationAttribute
, SetupAttribute
, and TeardownAttribute
attributes, as described in the previous section on Runtime Intelligence.
It's a good idea to add the application expiry attributes to a method that is called shortly after the application is started. You may also want to add it to a method that is called regularly, just in case your users leave your application running after the expiry date. In Figure 47-15, the InsertShelfLifeAttribute
and InsertSignOfLifeAttribute
attributes have been added to the Form1.InitializeComponent
method. This ensures that the application expiry date will be checked every time Form1 is invoked.
Both attributes require a Shelf Life Activation Key. Once you have obtained this key from PreEmptive, save it to your local disk and set the path to this file in the ActivationKeyFile
property. Setting the ExpirationDate
property to a date in the past is a good way to test this feature.
When an application expires, the behavior is determined by two settings. First, if you have Send Shelf Life Notification Messages checked on the Instrumentation Options tab, it will send a notification message to the endpoint you have specified. Second, if you have set the ExpirationNotificationSinkElement
property to DefaultAction
, the application will immediately exit.
The commercial edition of Dotfuscator allows you to specify a warning date and add custom handlers that are executed when the warning date or expiration date are reached. You could use this to deactivate specific features or display a friendly message to the users advising them that the application has expired.
The commercial version of Dotfuscator also allows your application to obtain the shelf life information from an external location, such as a web service or configuration file. This allows you to support other expiration scenarios such as expiring 30 days from installation or renewing annual subscriptions.
Earlier in this chapter you saw how to add the SetupAttribute
and TeardownAttribute
attributes to your application. By adding these attributes your application can send notification messages, and thereby allow you to track usage data and system environment statistics for your applications. These attributes are also used to determine application stability, because a missing Teardown notification indicates that the application may have crashed or a user may have gotten frustrated and simply forced an exit.
In addition to tracking application startup and shutdown, Dotfuscator allows you to further instrument your code to track usage of specific features within your application. With Dotfuscator CE, you can add up to ten FeatureAttribute
attributes to your methods, each one specifying the same or a different feature. This allows you to aggregate your application's methods into a logical, high-level "feature" grouping that is independent of the actual class hierarchy of your code.
In Figure 47-16, you can see that a FeatureAttribute
attribute has been added to the EvaluatePerson
and the GenerateMagicNumber
methods of the MathematicalGenius
class. These features have been given a descriptive name, which is displayed when viewing the usage reports. These attributes also have the FeatureEventType
property set to Tick
, which simply tracks that the feature has been used.
In addition to Tick feature tracking, you can also track the amount of time a feature was used. In this case you will need to add two FeatureAttribute
attributes — one with the FeatureEventType
property set to Start
and the other set to Stop
. This generates two instrumentation messages and allows the Runtime Intelligence analytics service to calculate feature usage duration.
The commercial edition of Dotfuscator includes:
Unlimited feature tracking
Injection of the Microsoft WMI SDK for hardware and software stack detection
Extensible data capture to include custom data values
An SSL runtime data transmission option
Runtime Intelligence data can be used to improve the development process, provide greater visibility into application usage for IT operations, and serve as an additional data source for business activity and performance monitoring. Microsoft's own Customer Experience Improvement Program (CEIP) relies on this kind of usage data (for a description of its program, visit http://www.microsoft.com/products/ceip/
).
As managed code moves beyond the desktop and the in-house server to the web client (Silverlight), the cloud (Azure), and your mobile devices, Runtime Intelligence will likely become an increasingly important part of your application life cycle management toolkit.
This chapter introduced two tools, IL Dasm and Reflector, which demonstrated how easy it is to reverse-engineer .NET assemblies and learn their inner workings. You also learned how to use Dotfuscator Software Services to:
Protect your intellectual property using obfuscation
Harden your applications against modification using tamper defense
Monitor and measure application usage with Runtime Intelligence instrumentation
Enforce your application's end-of-life with shelf life
3.144.110.155