Keeping performance with obfuscation

Introduction

Performance is important for all of us, however, some projects require the best performance available and can't afford to waste a single nanosecond. This page will focus on those developers that want to safeguard their project and keep their performance intact.

Perfect protection for any performance-critical application, the code of your application won't be modified, but its metadata. Bear free knowing that you can use this protection without hesitation.

You can use code inlining to erase a function's existence, obscuring the structure of your application and its code with no performance cost at all. As mentioned on the documentation page, you can suffer from performance penalties when inlining functions that are too big.

Assembly Merging

This feature from our platform allows you to merge dependencies of your application and bundle them into a single file, apply this feature with symbol renaming and code inlining to get the best results.

Control flow protection obscures the pathing of your application by generating challenges that are hardly understood by humans and automated tools.

The default settings won't affect the execution performance, but why? Without entering into too many details, the JIT is smart enough to detect and clean the challenges generated in those opaques, thus make it possible to obscure the IL while having the exact same assembly code.

However, there are some scenarios that might cause the JIT not to optimize those methods due to them being too big for the JIT to consider optimizing them. If this is something you noticed, consider outlining parts of said method or exclude Control Flow Protection from that method using attributes.

If you want to know the exact details behind inlining being discarded by the JIT, we recommend checking out the following source codes: inlining policies and inline class. The TL;DR is that the CLR will try to keep compiled methods small and never inline methods that are over 100 bytes.

Use .NET Core or newer

The shape of an obfuscator layer might vary between different .NET versions, by default, we will use the "classical" types and structs, however, in an effort to deliver the best performance after obfuscation, we are cloning and migrating our existing protections and porting them to newer versions of the CLR, allowing us to take advantage of high-performance APIs such as Span or Memory.

Protections to consider

Until now we have mentioned protections that have no performance in execution times, from now on we will discuss protections that you might consider implementing even if they come with some costs.

Constant Encryption

We mentioned multiple times in our blog and protips that constants can reveal crucial information about your application, we will always advise you to protect them whenever possible. Now, for performance perfectionists this might introduce a problem, the decryption process is rather expensive due to the decryption routine we use.

However, ever since our first public release, we have an embedded caching system that makes the protection considerably faster, the maximum performance penalty your will suffer is an extra reference to the heap.

Anti Tamper

Our anti-tamper protection will keep the methods of your application encrypted until necessary, it will not, however, obscure the code. Your application might have a slightly delayed startup time, however, after that the performance hit is inexistent.

Lightweight Body Mutations

When enabling this protection, our engine will only mutate and generate code that the JIT can optimize away but is hardly comprehensible by humans. Be advised that this mode removes the resilience levels of the protection almost entirely, we advise you to only enable this protection when you need to conserve the performance of your application at all costs.

Last updated