Standard Algorithms
Babel Obfuscator ships with three built-in string encryption algorithms: XOR, HASH and STREAM. All three are self-contained — they decrypt without any external input — so choosing between them is a trade-off between load time, output size, and how hard the strings are to recover. This page describes each algorithm and how to enable it; the comparison table at the end summarizes the differences.
XOR Algorithm
The XOR algorithm used by Babel Obfuscator for string encryption is a simple one that involves XOR-ing the in-line string characters with a random integer key. This method has the advantage of allowing for faster decryption and less load time compared to other encryption algorithms.
Command Line
To configure the XOR encryption algorithm for your application using Babel Obfuscator from the command line, use the simple command:
babel myapp.exe --string xorThis command instructs Babel to apply XOR encryption to the inline strings within your application executable, leveraging the simplicity and speed of the XOR algorithm for secure string obfuscation.
MSBuild Babel Task
Configuring the XOR algorithm for string encryption in your project using the Babel MSBuild task is straightforward. Simply add a property group and a Babel task to your MSBuild project file with the specific configuration for the XOR encryption method:
<PropertyGroup>
<StringEncryption>xor</StringEncryption>
</PropertyGroup>
<Babel StringEncryption="$(StringEncryption)" />This configuration ensures that Babel Obfuscator will apply the XOR encryption algorithm to the strings in your project, enhancing their security and making them more resistant to tampering and reverse-engineering efforts.
HASH Algorithm
The HASH algorithm used by Babel Obfuscator is based on hash tables addressed by integer keys. This algorithm performs both compression and encryption of string data, which can help to reduce the overall file size of the string data. However, since the compressed string data needs to be decrypted at runtime, it can have an impact on the load time of the application. Compared to the XOR algorithm, which xor-ed the inlined string characters with a random integer key, the HASH algorithm offers better protection against string decryption attacks but at the cost of a slower load time.
Command Line
To set up HASH encryption with Babel Obfuscator via CLI, use this command:
babel myapp.exe --string hashMSBuild Babel Task
To enable HASH encryption using Babel’s MSBuild task, set the StringEncryption property as follow:
<PropertyGroup>
<StringEncryption>hash</StringEncryption>
</PropertyGroup>
<Babel StringEncryption="$(StringEncryption)" />This integration of the HASH algorithm ensures that during the build process, your project’s strings are adequately protected.
STREAM Algorithm Ultimate
The STREAM algorithm is the modern, authenticated string-encryption option in Babel Obfuscator. It encrypts each string individually with a strong, industry-standard cipher and verifies an authentication tag before returning it. This gives STREAM four properties the older algorithms do not have:
- Lazy, per-string decryption. A string is decrypted only the first time it is actually used, and the result is cached. Unlike HASH, STREAM never decrypts the whole set up front, so a memory dump only ever exposes the few strings the application has already touched — there is no single point in time where every string sits in memory in clear text.
- Integrity and per-string randomization. The authentication tag detects any tampering with the encrypted data, and two identical strings encrypt to different bytes, defeating the frequency and equality analysis that a simple XOR scheme leaks.
- A key that is never stored inline. The key is not held as a literal next to the decryption call, so an automated deobfuscator has to execute the real code to recover a string rather than simply reading a key beside the call.
- Decryption bound to your code’s call context. Executing the decryptor is not enough: it returns a string only to a caller running inside the protected assembly itself (or inside the .NET runtime, so LINQ/PLINQ and async paths keep working). The usual one-command way an automated deobfuscator dumps every encrypted string at once — binding a delegate straight to the decryptor and batch-invoking it from its own helper assembly — is turned away. This does not make strings impossible to recover under full dynamic analysis, but it removes the cheap bulk-extraction path and forces an attacker to drive your real code.
The decryptor is written entirely in managed code, with no dependency on the platform cryptographic provider. As a result STREAM runs unchanged from .NET Framework 4.x (and 2.0) through .NET 10, on Mono/Xamarin, and — because it uses no Reflection.Emit — in NativeAOT and trimmed applications. It is also unaffected by the FIPS startup issue that can affect the platform-based algorithms.
STREAM is self-contained: like XOR and HASH it decrypts without any external input. Being a local-only scheme, it cannot make strings impossible to recover under full dynamic analysis — no self-contained scheme can — but it raises the cost of both automated extraction and manual reverse engineering well above XOR and HASH.
Platform and Framework Support
Because the decryptor is fully managed and uses no Reflection.Emit, dynamic code, stack walking, or platform crypto provider, STREAM has been verified across the whole .NET range and on mobile:
- .NET Framework 2.0 / 4.x through .NET 10, .NET Core, and Mono / Xamarin.
- Trimmed (ILLink) and NativeAOT applications — the injected decryptor and its embedded string table survive linking, and the code compiles ahead-of-time without a JIT.
- Android and iOS — verified end to end with real obfuscated apps on the Android emulator and the iOS simulator, including Release, fully trimmed builds: the strings decrypt at runtime and no plaintext remains in the packaged assembly. .NET MAUI builds on these same runtimes (
net*-android/net*-ios), so the same support applies. iOS device full-AOT is covered by the NativeAOT compatibility above.
On mobile, apply STREAM as a string-encryption-only pass so type and member names stay intact for the Java / Objective-C interop layer:
babel MyApp.dll --stringencryption stream --notypes --nomethods --nofields --noproperties --noevents --noparameters --novirtualmembersWhen Babel runs as an MSBuild step inside a .NET Android / iOS / MAUI build, hook it after the app assembly is compiled and before packaging, so the packaged app carries the obfuscated assembly.
Command Line
To apply STREAM encryption from the command line:
babel myapp.exe --string streamMSBuild Babel Task
To enable STREAM encryption using Babel’s MSBuild task, set the StringEncryption property as follows:
<PropertyGroup>
<StringEncryption>stream</StringEncryption>
</PropertyGroup>
<Babel StringEncryption="$(StringEncryption)" />STREAM works together with the other protections. It can be combined with code (MSIL) encryption and anti-tampering on the same assembly.
Anti-tampering and post-build rewriting. Anti-tampering stamps an integrity hash of the obfuscated image. Any step that rewrites the assembly after Babel — most notably a trimmed or NativeAOT publish, which re-emits the managed assembly — changes those bytes and makes the check fail fast at start-up. This is inherent to anti-tampering and independent of the string algorithm (it affects XOR, HASH and STREAM alike): enable anti-tampering on builds that are not further trimmed/AOT-rewritten, and rely on string encryption (plus code encryption) for trimmed/AOT outputs.
Comparing the Algorithms
All three built-in algorithms are self-contained — they decrypt without any external input — so choosing between them is a trade-off between load time, output size, and how hard the strings are to recover.
| XOR | HASH | STREAM | |
|---|---|---|---|
| Cipher | Inline XOR with a per-string integer key | Compressed table encrypted with the platform cipher | Authenticated, industry-standard per-string cipher |
| Strings kept as literals | Yes (scrambled in place) | No | No |
| Decryption model | Per string, on each access | Eager — the whole table is decrypted at first use | Lazy — each string on first use, then cached |
| Clear text held in memory | One string at a time | The entire table, for the whole run | Only the strings actually used |
| Per-string integrity tag | — | — | Yes |
| Identical strings encrypt differently | No | No | Yes |
| Refuses out-of-context (delegate-invoke) decryption | — | — | Yes |
| Decryption key | Inline, next to the call | Stored in the blob header | Never inline — rebuilt at runtime and woven into the control flow |
| Fully managed decryptor (no platform crypto provider; FIPS-safe) | No | No | Yes |
| Relative output size | Largest (literals stay in the assembly) | Smallest (single compressed blob) | Between XOR and HASH |
| Relative runtime cost | Lowest | Low, but paid up front for every string | Higher per string, paid only for strings that are used |
Which to choose.
- XOR — the fastest and lightest, but the weakest: the encrypted strings remain as literals in the assembly and a per-string key sits next to every call, so an automated tool can recover them in a single pass. Choose it when load time matters more than protection.
- HASH — removes the literals and packs the whole set into one compressed, encrypted blob (the smallest output), with tamper protection on the table. The cost is that the entire table is decrypted eagerly on first use — so every string sits in memory in clear text for the rest of the run — and its decryptor relies on the platform crypto provider (see the FIPS note).
- STREAM — the strongest of the three against both static and dynamic analysis: authenticated per-string encryption, no eager global decrypt, no inline key, and a single fully managed decryptor that publishes unchanged under NativeAOT and trimming and is unaffected by FIPS. You pay a higher per-string decryption cost, but only for the strings the application actually touches, and no point in time ever holds the full set of strings in clear text. Prefer STREAM for modern .NET, AOT/trimmed or FIPS deployments, and wherever resistance to bulk string extraction matters most.