Developers trust compiled binaries blindly. They assume that because they selected the IL2CPP backend in Unity, their C# logic converts to native C++ machine code and becomes unreadable. They are wrong. Attackers reverse engineer Unity binaries trivially. They dump the memory. They manipulate the client execution flow. If your Unity application handles real money, digital wallets, or sports betting transactions, client-side trust leads to immediate financial loss.
Reverse engineering IL2CPP metadata
Unity applications built using the IL2CPP backend compile C# code into native C++ code (like libil2cpp.so on Android or a Mach-O binary on iOS). This makes direct decompilation harder than standard Mono builds where the raw .NET DLLs sit unencrypted in the APK. Harder does not mean impossible.
We bypass this protection using tools like Il2CppDumper. This tool parses the global-metadata.dat file shipped with the application. This metadata file contains the string names of every class, method, variable, and namespace originally written in your C# source code.
By cross-referencing the metadata against the compiled native binary, we rebuild a dummy DLL. We load this DLL into dnSpy or Ghidra. We see your exact application architecture. We locate critical functions inside the binary. If you name a function ProcessBettingTransaction() or CreditUserWallet(), we find it instantly.
We use Frida to hook these methods at runtime. We attach a debugger to the running application on a physical device. We intercept the function execution. We read variables in memory. We modify values. We change wallet balances before the network request fires. We bypass local transaction verification checks.
Local purchase verification bypassed in memory
A method such as IsSubscriptionActive() can be forced to return true at runtime using a Frida script. This becomes a critical security defect when the server grants content or account rights based on that client result. The server must verify the receipt and entitlement cryptographically, entirely independent of the client's reported state.
Auditing payment gateway integrations in Unity
The most common vulnerability we find in Unity deployments is verifying transaction status entirely on the mobile client. Developers build beautiful 3D interfaces but implement fundamentally broken payment architectures.
Developers often write code where the Unity client calls a local payment API SDK, receives a confirmation token from the gateway, and tells the backend database to credit the account directly. The backend trusts the client. The backend executes the SQL update based on the client payload.
This is a critical security mistake. Attackers intercept the network traffic using proxies like Burp Suite. They replay successful transaction tokens. They forge responses. They modify the JSON payload to state that a 10,000 Naira transaction was actually a 1,000,000 Naira transaction. All payment verifications must occur on the server. The backend must query the payment gateway (like Paystack, Flutterwave, or Interswitch) directly using server-to-server communication to confirm the transaction status before updating the database.
Attacking the network layer
Unity applications handle networking differently than native iOS or Android applications. They often rely on `UnityWebRequest` or third-party C# networking libraries. We test how these libraries handle SSL/TLS certificate pinning. Most Unity applications fail to implement pinning correctly, allowing us to Man-in-the-Middle (MitM) the traffic trivially by installing a custom root CA on the test device.
If pinning is implemented, we locate the verification function in the dumped IL2CPP assembly and patch it out of the binary. Once we control the network flow, we execute standard API attacks: Insecure Direct Object Reference (IDOR), Mass Assignment, and Server-Side Request Forgery (SSRF). We treat the Unity client as a hostile actor sending malicious data to your backend APIs.
Unity Security Checklist
To secure your Unity deployments, verify these defensive items immediately:
- Server-Side Purchase Validation: The mobile client must never have the authority to update its own balance. Every transaction must be verified on the server using webhooks or direct gateway API queries.
- Metadata Obfuscation: Use specialized obfuscation tools designed for Unity to scramble the IL2CPP class metadata. Rename sensitive functions. Do not leave a clear roadmap for attackers.
- Encrypt local data: If you store configuration values, authentication tokens, or local cache variables on disk using `PlayerPrefs` or raw files, encrypt them. Use AES keys managed by the host OS secure storage layers (Keychain on iOS, Keystore on Android). Do not hardcode encryption keys in the C# source code.
- Implement robust anti-tampering: Detect if the binary has been modified or resigned. Detect if a debugger is attached. Detect Frida hooks. Crash the application if tampering is detected.
Exploiting PlayerPrefs and AssetBundles
Unity developers frequently use `PlayerPrefs` to store user settings. Because it is so easy to use, they often accidentally store sensitive data there: API keys, JWT session tokens, or even boolean flags like `isPremium=true`. On Android, `PlayerPrefs` is simply an unencrypted XML file located in `/data/data/[bundle_id]/shared_prefs/`. On iOS, it maps directly to `NSUserDefaults`, which is an unencrypted plist file in the application container.
During a penetration test, if the device is rooted or jailbroken, we simply open this file in a text editor. We change `isPremium=false` to `isPremium=true`. We change `wallet_balance=0` to `wallet_balance=50000`. If the application trusts this local file without verifying the state against the backend server, the application is fundamentally compromised. All sensitive data must be encrypted before being written to disk.
Furthermore, we audit Unity `AssetBundles` and `Addressables`. These are packages of assets (3D models, textures, scenes) downloaded from remote servers at runtime. If the application downloads an AssetBundle over plain HTTP, or over HTTPS without certificate pinning, we intercept the download. We replace the legitimate AssetBundle with a malicious one. We overwrite textures with phishing messages. We modify UI prefabs to redirect payment button clicks to attacker-controlled addresses. AssetBundles must be signed cryptographically, downloaded over pinned TLS connections, and verified via checksum before they are loaded into memory.
Test each purchase as a server workflow
Start with a new account and record the product ID, price, currency, receipt, transaction ID, and entitlement state. Send the same purchase twice. Change the product ID after payment. Reuse a receipt on another account. Send a refunded, expired, or sandbox receipt to production. Skip one step in the purchase flow. The server must verify the receipt with the store, bind it to one account, reject replays, and grant the exact item that was paid for.
Keep the original and changed requests, the store verification response, server logs for the transaction ID, and screenshots of the entitlement before and after the test. Remove tokens and payment data from the report. Retest with the same request after the fix.
For wider game logic tests, read the Unity game security guide. Teams shipping mobile payments should also use the API security testing scope.
Let Simpa Labs audit your Unity app
We specialize in reverse-engineering mobile binaries and testing complex API integrations. We decompile your Unity application, extract your hardcoded secrets, bypass your local protections, and attack your backend payment endpoints exactly like a motivated threat actor.