Integrating Microsoft Code Protector
September 5, 2008
In the last post I wrote about how I came to use Microsoft Code Protector for preventing tampering with my .NET licensing scheme. Here I’ll go though how I use it, including how I design my code to get the best out of it.
To protect an application you configure a project using the SLP Code Protector GUI. Select the assemblies and methods to protect and then run. The IL in the assemblies will be manipulated and the method bodies will now be just calls to the Code Protector runtime which uses the unique ‘permutation’ you get issued.
The GUI is a little bit clunky in terms of selecting methods and there is the issue of keeping the Code Protector project file in sync with the code as methods are added and renamed. A nice trick is to use type name filtering by working with the project file directly – it is not supported in the gui.
<Rule>
<Filter>
<CodeEntityFilters>
<Assembly>
<Name>MyAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=e4d4e46e5e55655d</Name>
</Assembly>
<Namespace>
<Name>DW.Licensing</Name>
</Namespace>
<Type>
<Name>DW.Licensing.LicenseManager</Name>
</Type>
<Method />
<Property />
<Field />
</CodeEntityFilters>
</Filter>
<RuleParams>
<UnlockingFeature>Execute</UnlockingFeature>
</RuleParams>
</Rule>
Note that you might get the occasional IL op code that can’t be translated with a message along the lines of
Warning Failed to transform method DW.LicenseManager.ActivationState:ProcessKey(string). Unsupported OpCode found in “IL_0018 starg.s 1″. Method was automatically excluded from the transformation.
In this case was a simple aString = aString.Replace("-", String.Empty); to blame (Reflexil was useful here), which can easily be worked around.
The protection process can be run from the command line for automated builds and assemblies can be resigned but not with a key container.
There aren’t really any settings to tweak about the code protection itself apart from selecting ‘Cloaking’ method calls to increase security. Details in the manual! However architecture and implementation should be tweaked to get the most out of Code Protector.
So protection is method based and it is not designed to protect every method. It’s slow, you lose stack traces and debugging, and there are restrictions on what it can protect such as no generics. In this case I want to prevent the licensing code being tampered with, or bypassed altogether, so I:
- Protect the method containing the call to check the license, and all of the licensing code itself.
- Place the call to check the license in a method that is vital to the operation of the application. In this case initialisation code. This will stop the application working in the case of an upstream unprotected method call being removed to bypass the licensing check.
Code Protected methods may be slow, but in this case because they are all in the initialisation it didn’t really matter. The only effect was to increase start up time by a few seconds.
I like to distribute pdb files so that there are line numbers in the stack traces in the error log. Using Code Protector will mangle the stack traces much like an obfuscator does and this is to be expected. But it also seems to stop line numbers being displayed for the whole assembly, and there is the issue of the pdb containing information about protected methods. Because of this isolating the protected initialisation in a separate assembly (in addition to putting the licensing code in another assembly) seems like a good idea. Moving all the initialisation methods of an important class into a sub class in a new assembly does the trick with a bit of help from InternalsVisibleTo.
It can also be seen that exceptions thrown from unprotected code have their stack traces mangled when passing through the protected code. Calling out to unprotected methods by wrapping them in a delegate passed to the following method sorts that:
// usage: CallOut(delegate() { <code goes here> }); public void CallOut(ThreadStart toCall) { try { toCall(); } catch (Exception ex) { TraceHelper.WriteException(ex, "License Manager Logging Stack Trace"); throw; } }
So we can consider anything happening in protected methods safe, statically in terms of IL and also at runtime in terms of memory. Whereas fields are not protected and are vulnerable, to reading secrets from the IL, and to malicious manipulation at runtime. This is not a big deal as we can design the class to take advantage of the method centric protection. Any inline initialisers should be put in constructors instead where they can be protected (.NET will do this anyway for instance members, at least when I looked, but not static members). Taking things further — note that I’m not a cracker and don’t know how much these measures really help, just a developer using his imagination and deferring to the defensive option — we can work to minimise any state, preferring to calculate it instead. For example instead of caching a boolean isLicenseOk the whole license checking code should be run through to calculate this instead. If a state must be stored it can be done as an encrypted byte array (see the code at the bottom to do this). Minimising its lifetime probably helps too. Am I getting paranoid?
All in all I was glad to find Code Protector as it completed my licensing system by protecting the licensing system itself. Working out how best, or at least better, to use it was a bit of a step into the unknown.
Code for storing encrypted members:
Byte[] Store(String info) {
if (info == null)
return null;
SymmetricAlgorithm symmetricAlgorithm = GetSymmetricAlgorithm();
MemoryStream underlying = new MemoryStream();
byte[] iv = symmetricAlgorithm.IV;
underlying.Write(iv, 0, iv.Length);
// We don't use 'using' as CryptoStream.Dispose does not close underlying Stream but
// Close does.
TextWriter writer = null;
try {
ICryptoTransform encryptor = symmetricAlgorithm.CreateEncryptor();
Stream crypto = new CryptoStream(underlying, encryptor, CryptoStreamMode.Write);
writer = new StreamWriter(crypto);
writer.Write(info);
} finally {
// StreamWriter.Close and CryptoStream.Close close their wrapped streams.
writer.Close();
}
return underlying.ToArray();
}
String Retrieve(Byte[] bytes) {
if (bytes == null)
return null;
SymmetricAlgorithm symmetricAlgorithm = GetSymmetricAlgorithm();
// We dont use 'using' as CryptoStream.Dispose does not close underlying Stream but
// Close does.
MemoryStream underlying = new MemoryStream(bytes);
byte[] iv = symmetricAlgorithm.IV;
IOUtils.ReadByteBlock(underlying, iv, iv.Length);
symmetricAlgorithm.IV = iv;
TextReader reader = null;
try {
ICryptoTransform decryptor = symmetricAlgorithm.CreateDecryptor();
Stream crypto = new CryptoStream(underlying, decryptor, CryptoStreamMode.Read);
reader = new StreamReader(crypto);
return reader.ReadToEnd();
} finally {
// StreamReader.Close and CryptoStream.Close close their wrapped streams.
reader.Close();
}
}
// We generate the SymmetricAlgorithm just as we try to minimise state elsewhere.
SymmetricAlgorithm GetSymmetricAlgorithm() {
// Add your own tricks for hiding keys here!
byte[] key =
new Byte[] {69, 100, 103, 345, 67, 746, 736, 162, 463, ...
SymmetricAlgorithm symmetricAlgorithm = new RijndaelManaged();
symmetricAlgorithm.Key = key;
return symmetricAlgorithm;
}