#Context
@Engine: Unity 2022.3.16f1 (IL2CPP)
@Target: Windows x64, D3D11; GPU e.g. RTX 2080 Ti
@Protected binary: GameAssembly.dll
@Project layout: GameAssembly.dll links my own static lib. Inside that lib I’ve instrumented ~200 functions with VMProtectBegin.../End (mix of Mutation/Virtualization/Ultra).
@VMProtect options:
-With Memory Protection = ON → startup takes 40–60 seconds, and calling into the protected C++ frequently stutters.
-With Memory Protection = OFF (other protections ON: Import Protection, Resource Protection, etc.) → startup ≈ 3–4 seconds, runtime smooth.
#Observed behavior
-The slowdown doesn’t correlate with “code size” or “number of protected functions” alone. The decisive switch seems to be Memory Protection.
-The protected module is very large (typical IL2CPP GameAssembly.dll) and touched frequently by Unity runtime.
#My questions
-Is this performance behavior expected for very large, hot modules with Memory Protection?
-Are there official best practices to keep Memory Protection but avoid the massive slowdown on such modules?
-e.g. per-function MP only on cold paths, exclude hot code, or limit protected ranges?
-any options related to guard pages / exceptions / integrity checks that can be tuned?
-Would moving sensitive code from GameAssembly.dll into a small native DLL (only a few MB, loaded on demand) and enabling Memory Protection only there be the recommended solution?
-Any known issues or recommendations when VMP markers live in a static library that’s linked into a big DLL (like GameAssembly.dll)?
-Are there linker/optimizer settings (e.g., LTCG/LTO, inlining) that interact badly with Memory Protection on huge modules?
-Any guidance for interacting with AV/EDR (whitelisting folders/process) to avoid overhead when Memory Protection decrypts/touches pages?
-If there’s no practical way to keep Memory Protection on this big module without the slowdown, what’s the official stance: split critical logic to a small DLL + MP there, and keep the large module on Mutation/Virtualization only?
#What I’ve tried / knobs considered
-Keeping other protections ON (Import Protection, Resource Protection) while toggling MP: only MP flips the behavior.
-Reducing VM “Complexity” and instances for hot functions; still large startup hit with MP ON.
#Goal
I want to keep Memory Protection for meaningful security gains without the extreme startup/runtime cost. If the recommended path is to split into a small, strongly protected native DLL for key logic and keep GameAssembly.dll on lighter protections, please confirm that’s the right approach and share any do’s/don’ts (loading strategy, settings, pitfalls).
Thanks!