Techniques for Automating Obfuscation
This article provides different techniques of obfuscation available and their pros/cons; it also explains how to leverage post-build commands in Visual Studio.
Join the DZone community and get the full member experience.
Join For FreeThe rich Microsoft intermediate language (MSIL) of the .NET Framework is focused on application flexibility and inherently provides significant information about how an application was written. Obfuscation technology helps to secure the intellectual property contained in .NET Framework applications. Obfuscation tools rename symbols and rearrange code blocks to complicate decompiling. They also might encrypt strings that contain sensitive data.
These tools also determine exactly which parts of your program you're really using (down to the method level). From there, the tools can parse out unneeded pieces, leaving you with the smallest possible executable. By removing unnecessary program elements and renaming identifiers to shorter names, an obfuscator can actually speed up programs. When combined with obfuscation and pruning, assembly linking provides a powerful packaging solution for .NET Framework applications. You need automation not only for obfuscation but to cover related tasks, such as extraction, re-signing, and uploading. Automating obfuscation in such scenarios will save effort and time.
Note: For more information about obfuscation, see Thwart Reverse Engineering of Your Visual Basic .NET or C# Code.
There are mainly two ways to automate obfuscation:
- Post-build commands
- Batch files
By using post-build commands, you can obfuscate an assembly into a temporary directory, copy the obfuscated assembly over the original, and then clean up your temporary files. The effect is almost as though the IDE and compiler performed the obfuscation automatically.
Here are the post-build action commands that make this happen:
Obfuscate the assembly into a temporary directory:
"C:\Program Files\Microsoft Visual Studio 11.0\PreEmptive Solutions\Dotfuscator and Analytics Community Edition \dotfuscator.exe" /q /p=SourceDirectory=$(TargetDir),SourceFile=$(TargetFileName) $(ProjectDir)Dotfuscator.xml
This command will depend on the type of obfuscator you use. Here, for example, the code uses Dotfuscator Community Edition, which is provided free with Visual Studio tools. After you run this command, the obfuscator gets the file from the target directory and puts the obfuscated version of it in a newly created folder named "dotfuscator" in the target directory.
Copy the obfuscated assembly over the original:
copy $(TargetDir)Dotfuscator\$(TargetFileName) $(TargetDir)$(TargetFileName)
Clean up the temporary files:
rmdir /S /Q $(TargetDir)Dotfuscator
You need to create a Dotfuscator.xml file similar to the following in the root of your project folder:
<?xml version="1.0" encoding="utf-8" standalone="no"?> <!DOCTYPE dotfuscator SYSTEM "http://www.preemptive.com/dotfuscator/dtd/dotfuscator_v1.1.dtd"> <dotfuscator version="1.1"> <propertylist> <property name="SourceDirectory" value="This Path Will Be Replaced By Visual Studio" /> <property name="SourceFile" value="This Filename Will Be Replaced By Visual Studio" /> </propertylist> <trigger> <filelist> <file dir="${SourceDirectory}\" name="${SourceFile}" /> </filelist> </trigger> <output> <file dir="${SourceDirectory}\Dotfuscator\" /> </output> </dotfuscator>
Overview of Obfuscation Automation by Using a Batch File
Another alternative to achieve the same result is to create a batch file. The automated obfuscation process performs four main functions: extraction, obfuscation, re-signing, and uploading:
- Extractor extracts/copies files to a temporary folder.
- Obfuscator obfuscates the files.
- Re-signer signs the files.
- Uploader organizes the files in the output structure required by the installer.
1. Extraction
All files that need to be obfuscated are extracted or copied to the temporary folder Unobfuscated. Whether to extract or copy will depend on your project requirements.
Command To Extract
msiexec /a " D:\Obfuscated\VSBuild\xyz.msi" /qb TARGETDIR=" D:\Obfuscated \UnObfuscated" >>" D:\Obfuscated \output.txt"
Here, the .dll files are extracted from the .msi that's generated by Visual Studio (using the InstallShield Limited Edition template) or by Windows Installer XML to a common folder that acts as an input folder to the obfuscator.
Command To Copy
ROBOCOPY " D:\Obfuscated\VSBuild " " D:\Obfuscated \UnObfuscated" *.dll >>" D:\Obfuscated\output.txt"
Here, all the .dlls are copied from the VSBuild folder to a common folder that acts as an input folder to the obfuscator.
2. Obfuscation
This command depends on the type of obfuscator you're using. Here, Dotfuscator 4.9 Professional is used.
Command To Navigate to Target Path
cd "C:\Program Files (x86)\PreEmptive Solutions\Dotfuscator Professional Edition 4.9"
The target path is the location of the obfuscator.
Command To Obfuscate and Generate Output
dotfuscator " D:\Obfuscated\settings.xml" > " D:\Obfuscated\OutputLogs\DotfuscatorOutput.txt"
Dotfuscator.exe uses settings.xml to obfuscate the .dll files present in the input folder and also generates user-friendly output.
3. Re-Sign
The obfuscator might mess up the signatures of the previously signed assemblies, therefore you need to sign the assemblies again (post obfuscation).
Command To Navigate to Target Path
cd "C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin"
Command to Re-Sign the .Dll Files and Generate Signing Output
sn.exe -R "D:\Obfuscated\Dotfuscated\xyz.dll" "D:\Workspace\Keys\abc.snk" >> "D:\Obfuscated\OutputLogs\signingOutput.txt"
4. Arrange in Output Structure
The output structure is project-specific and should be in accordance with the installer.
Command To Copy Selected Files from Dotfuscated to ObfuscatedVSBuild Structure
ROBOCOPY " D:\Obfuscated \Dotfuscated" " D:\Obfuscated \ObfuscatedVSBuild\SubFolder" [ abc1.dll abc2.dll abc3.dll]
Command To Copy All Files From Dotfuscated to ObfuscatedVSBuild Structure
ROBOCOPY " D:\Obfuscated \Dotfuscated " " D:\Obfuscated \ObfuscatedVSBuild \AllObfuscatedAssemblies" *.* >>" D:\Obfuscated \OutputLogs\output.txt"
Note that the previous commands are based on the following folder structure in D:\Obfuscated\:
- VSBuild: Input folder from which files are extracted and copied to temporary folder Unobfuscated.
- Unobfuscated: Temporary folder to hold all unobfuscated assemblies copied or extracted from the VSBuild folder. It also acts as an input folder to the obfuscator.
- Dotfuscated: Obfuscator picks assemblies from the Unobfuscated folder and puts obfuscated assemblies in this folder.
- ObfuscatedVSBuild: Obfuscated signed assemblies in the Dotfuscated folder are rearranged in subfolders in ObfuscatedVSBuild, per installer requirements.
- OutputLogs: Output Folder to hold all output logs.
- Settings.xml: This is described in the next paragraph.
Before you start obfuscation by using a batch file, you need to have settings.xml in place. You can create settings.xml manually or from the obfuscator GUI by creating a new project. A sample is shown here:
<?xml version="1.0" encoding="utf-8" standalone="no"?> <!DOCTYPE dotfuscator SYSTEM "http://www.preemptive.com/dotfuscator/dtd/dotfuscator_v2.3.dtd"> <dotfuscator version="2.3"> <input> <loadpaths /> <!—Provide name and location of input assemblies-->
<asmlist> <inputassembly refid="1c6cdd30-c3a7-4739-a01f-1ea44358e758"> <option>honoroas</option> <option>stripoa</option> <option>library</option> <file dir=" D:\Obfuscated\Unobfuscated" name="abc.dll" /> </inputassembly> </asmlist> </input> <output> <file dir="${configdir}\Dotfuscated" /> </output> <renaming> <!—Exclude resources and overloaded methods from obfuscation--> <option>xmlserialization</option> <excludelist> <type name="MyCompany.MyProduct.Outlook.OutlookRibbon" excludetype="false"> <method name="<GetActiveOutlookWrapperItem>b__3e" signature="bool(MyCompany.MyProduct.Outlook.Wrappers.ExplorerWrapper)" /> <method name="<GetActiveOutlookWrapperItem>b__3f" signature="bool(MyCompany.MyProduct.Outlook.Wrappers.InspectorWrapper)" /> <method name="GetActiveOutlookWrapperItem" signature="object(object)" /> </type> </excludelist> <mapping> <mapoutput overwrite="false"> <file dir="${configdir}\Dotfuscated" name="Map.xml" /> </mapoutput> </mapping> </renaming> <controlflow level="high"> <!—Exclude overloaded methods, similar to renaming--> <excludelist> <type name="MyCompany.MyProduct.Outlook.OutlookRibbon"> <method name="<GetActiveOutlookWrapperItem>b__3e" signature="bool(MyCompany.MyProduct.Outlook.Wrappers.ExplorerWrapper)" /> <method name="<GetActiveOutlookWrapperItem>b__3f" signature="bool(MyCompany.MyProduct.Outlook.Wrappers.InspectorWrapper)" /> <method name="GetActiveOutlookWrapperItem" signature="object(object)" /> </type> </excludelist> </controlflow> <sos mergeruntime="true"> <option>dontsendtamper</option> </sos> <smartobfuscation> <smartobfuscationreport verbosity="all" overwrite="false" /> </smartobfuscation> </dotfuscator>
Following are some considerations to be aware of:
- Resources that are related to textual content or graphics should be kept out of scope for the obfuscation process. Otherwise, the mapping with the main application might get corrupted as they are encrypted by obfuscation, and this might cause problems.
- Exclude overloaded functions and even functions that are called overloaded functions. If you don't exclude them, mapping to the correct overload of the function might fail. This, again, depends on the type of obfuscator used. (In Dotfuscator, you can exclude resources or overloaded functions from renaming on the Renaming or Control Flow tab.)
- The obfuscator prompts for any missing required .dll files that are used internally and not present in the global assembly cache. You need to add these .dll files to the Unobfuscated folder because Dotfuscator searches for them in the global assembly cache if they aren't found in the local folder.
You can verify obfuscation by running Ildasm.exe from the Visual Studio command prompt and opening the assembly in it. An obfuscated assembly will show the obfuscator attribute, and the names of functions will be replaced with literals like "a" and "b."
Also, you can see the output generated.
Following are some tradeoffs and some considerations that are outside the scope of this article:
- The process is time-consuming.
- You can't completely protect your intellectual property. Because the code is on the client machine, a determined hacker with lots of time can study the code and data structures enough to understand what's going on behind the scenes. Obfuscators do provide value in defeating most decompiling tools and preventing the casual hacker from stealing your intellectual property. They can make your code as difficult to reverse engineer as optimized native code.
- In Java, obfuscation limits the use of the Reflection API on the obfuscated code.
- Obfuscating your code might make debugging more difficult or impossible. Many of the third-party obfuscators have features that help with debugging, however, such as a file that shows how obfuscated symbol names correspond to original symbol names.
Finally, some concluding considerations:
- Protecting software is as important as protecting hosts.
- Watermarking, tamper-proofing, and obfuscation are important tools for protecting software.
- No technique can prevent all attacks.
- The goal is to increase the difficulty for the attacker.
Published at DZone with permission of Naga Santhosh Reddy Vootukuri. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments