2025-04-05 19:02:50 +08:00
using dnlib.DotNet ;
2025-05-10 11:25:07 +08:00
using dnlib.Protection ;
using Obfuz.Data ;
2025-05-01 10:45:31 +08:00
using Obfuz.Emit ;
2025-05-11 12:53:24 +08:00
using Obfuz.EncryptionVM ;
2025-05-04 19:55:10 +08:00
using Obfuz.ObfusPasses ;
2025-05-10 18:25:43 +08:00
using Obfuz.ObfusPasses.CleanUp ;
2025-05-12 17:21:20 +08:00
using Obfuz.ObfusPasses.SymbolObfus ;
2025-05-04 19:24:14 +08:00
using Obfuz.Utils ;
2025-04-05 19:02:50 +08:00
using System ;
using System.Collections.Generic ;
2025-04-05 21:47:28 +08:00
using System.IO ;
2025-04-05 19:02:50 +08:00
using System.Linq ;
using System.Text ;
using System.Threading.Tasks ;
2025-04-05 21:47:28 +08:00
using UnityEngine ;
2025-05-10 18:25:43 +08:00
using static UnityEditor . ObjectChangeEventStream ;
2025-04-05 19:02:50 +08:00
namespace Obfuz
{
public class Obfuscator
{
2025-05-03 21:43:50 +08:00
private readonly string _obfuscatedAssemblyOutputDir ;
2025-04-05 19:02:50 +08:00
2025-05-03 21:43:50 +08:00
private readonly List < string > _toObfuscatedAssemblyNames ;
private readonly List < string > _notObfuscatedAssemblyNamesReferencingObfuscated ;
2025-05-12 17:21:20 +08:00
private readonly List < string > _assemblySearchDirs ;
2025-04-21 09:57:34 +08:00
2025-05-12 22:01:35 +08:00
private readonly ConfigurablePassPolicy _passPolicy ;
2025-05-12 17:21:20 +08:00
private readonly Pipeline _pipeline1 = new Pipeline ( ) ;
private readonly Pipeline _pipeline2 = new Pipeline ( ) ;
2025-05-13 08:56:19 +08:00
private readonly byte [ ] _byteSecret ;
private readonly int [ ] _intSecret ;
2025-05-12 08:13:01 +08:00
private readonly int _randomSeed ;
private readonly string _encryptionVmGenerationSecret ;
2025-05-11 17:36:58 +08:00
private readonly int _encryptionVmOpCodeCount ;
2025-05-11 19:28:19 +08:00
private readonly string _encryptionVmCodeFile ;
2025-04-21 09:57:34 +08:00
2025-05-04 19:24:14 +08:00
private ObfuscationPassContext _ctx ;
2025-04-17 22:02:48 +08:00
2025-05-11 17:36:58 +08:00
public Obfuscator ( ObfuscatorBuilder builder )
2025-04-05 19:02:50 +08:00
{
2025-05-13 08:56:19 +08:00
_byteSecret = KeyGenerator . GenerateKey ( builder . Secret , VirtualMachine . SecretKeyLength ) ;
_intSecret = KeyGenerator . ConvertToIntKey ( _byteSecret ) ;
SaveKey ( _byteSecret , builder . SecretOutputPath ) ;
2025-05-12 08:13:01 +08:00
_randomSeed = builder . RandomSeed ;
_encryptionVmGenerationSecret = builder . EncryptionVmGenerationSecretKey ;
2025-05-11 17:36:58 +08:00
_encryptionVmOpCodeCount = builder . EncryptionVmOpCodeCount ;
2025-05-11 19:28:19 +08:00
_encryptionVmCodeFile = builder . EncryptionVmCodeFile ;
2025-05-11 09:17:04 +08:00
2025-05-11 17:36:58 +08:00
_toObfuscatedAssemblyNames = builder . ToObfuscatedAssemblyNames ;
_notObfuscatedAssemblyNamesReferencingObfuscated = builder . NotObfuscatedAssemblyNamesReferencingObfuscated ;
_obfuscatedAssemblyOutputDir = builder . ObfuscatedAssemblyOutputDir ;
2025-05-12 17:21:20 +08:00
_assemblySearchDirs = builder . AssemblySearchDirs ;
2025-05-03 21:43:50 +08:00
2025-05-12 22:01:35 +08:00
_passPolicy = new ConfigurablePassPolicy ( _toObfuscatedAssemblyNames , builder . EnableObfuscationPasses , builder . ObfuscationPassConfigFiles ) ;
2025-05-11 17:36:58 +08:00
foreach ( var pass in builder . ObfuscationPasses )
2025-05-03 21:43:50 +08:00
{
2025-05-12 17:21:20 +08:00
if ( pass is SymbolObfusPass symbolObfusPass )
{
_pipeline2 . AddPass ( pass ) ;
}
else
{
_pipeline1 . AddPass ( pass ) ;
}
2025-05-03 21:43:50 +08:00
}
2025-05-12 17:21:20 +08:00
_pipeline1 . AddPass ( new CleanUpInstructionPass ( ) ) ;
2025-04-05 19:02:50 +08:00
}
2025-05-12 08:13:01 +08:00
public static void SaveKey ( byte [ ] secret , string secretOutputPath )
{
FileUtil . CreateParentDir ( secretOutputPath ) ;
File . WriteAllBytes ( secretOutputPath , secret ) ;
Debug . Log ( $"Save secret key to {secretOutputPath}, secret length:{secret.Length}" ) ;
}
2025-04-16 23:03:41 +08:00
public void Run ( )
2025-05-03 21:43:50 +08:00
{
2025-05-12 17:21:20 +08:00
FileUtil . RecreateDir ( _obfuscatedAssemblyOutputDir ) ;
RunPipeline ( _pipeline1 ) ;
_assemblySearchDirs . Insert ( 0 , _obfuscatedAssemblyOutputDir ) ;
RunPipeline ( _pipeline2 ) ;
}
private void RunPipeline ( Pipeline pipeline )
{
if ( pipeline . Empty )
{
return ;
}
OnPreObfuscation ( pipeline ) ;
DoObfuscation ( pipeline ) ;
OnPostObfuscation ( pipeline ) ;
2025-05-03 21:43:50 +08:00
}
2025-05-11 12:48:53 +08:00
private IEncryptor CreateEncryptionVirtualMachine ( )
{
2025-05-12 08:13:01 +08:00
var vmCreator = new VirtualMachineCreator ( _encryptionVmGenerationSecret ) ;
2025-05-11 17:36:58 +08:00
var vm = vmCreator . CreateVirtualMachine ( _encryptionVmOpCodeCount ) ;
2025-05-11 19:28:19 +08:00
var vmGenerator = new VirtualMachineCodeGenerator ( vm ) ;
if ( ! File . Exists ( _encryptionVmCodeFile ) )
{
throw new Exception ( $"EncryptionVm CodeFile:`{_encryptionVmCodeFile}` not exists! Please run `Obfuz/GenerateVm` to generate it!" ) ;
}
if ( ! vmGenerator . ValidateMatch ( _encryptionVmCodeFile ) )
{
throw new Exception ( $"EncryptionVm CodeFile:`{_encryptionVmCodeFile}` not match with encryptionVM settings! Please run `Obfuz/GenerateVm` to update it!" ) ;
}
2025-05-13 08:56:19 +08:00
var vms = new VirtualMachineSimulator ( vm , _byteSecret ) ;
2025-05-11 19:28:19 +08:00
var generatedVmTypes = AppDomain . CurrentDomain . GetAssemblies ( )
. Select ( assembly = > assembly . GetType ( "Obfuz.EncryptionVM.GeneratedEncryptionVirtualMachine" ) )
. Where ( type = > type ! = null )
. ToList ( ) ;
if ( generatedVmTypes . Count = = 0 )
{
throw new Exception ( $"class Obfuz.EncryptionVM.GeneratedEncryptionVirtualMachine not found in any assembly! Please run `Obfuz/GenerateVm` to generate it!" ) ;
}
if ( generatedVmTypes . Count > 1 )
{
throw new Exception ( $"class Obfuz.EncryptionVM.GeneratedEncryptionVirtualMachine found in multiple assemblies! Please retain only one!" ) ;
}
2025-05-13 08:56:19 +08:00
var gvmInstance = ( IEncryptor ) Activator . CreateInstance ( generatedVmTypes [ 0 ] , new object [ ] { _byteSecret } ) ;
2025-05-11 19:28:19 +08:00
int testValue = 11223344 ;
2025-05-14 10:46:42 +08:00
string testString = "hello,world" ;
2025-05-11 19:28:19 +08:00
for ( int i = 0 ; i < vm . opCodes . Length ; i + + )
{
2025-05-14 10:46:42 +08:00
int ops = i * vm . opCodes . Length + i ;
2025-05-14 12:21:25 +08:00
//int salt = i;
//int ops = -1135538782;
int salt = - 879409147 ;
int encryptedValueOfVms = vms . Encrypt ( testValue , ops , salt ) ;
int decryptedValueOfVms = vms . Decrypt ( encryptedValueOfVms , ops , salt ) ;
2025-05-11 19:28:19 +08:00
if ( decryptedValueOfVms ! = testValue )
{
throw new Exception ( $"VirtualMachineSimulator decrypt failed! opCode:{i}, originalValue:{testValue} decryptedValue:{decryptedValueOfVms}" ) ;
}
2025-05-14 12:21:25 +08:00
int encryptedValueOfGvm = gvmInstance . Encrypt ( testValue , ops , salt ) ;
int decryptedValueOfGvm = gvmInstance . Decrypt ( encryptedValueOfGvm , ops , salt ) ;
2025-05-11 19:28:19 +08:00
if ( encryptedValueOfGvm ! = encryptedValueOfVms )
{
throw new Exception ( $"encryptedValue not match! opCode:{i}, originalValue:{testValue} encryptedValue VirtualMachineSimulator:{encryptedValueOfVms} GeneratedEncryptionVirtualMachine:{encryptedValueOfGvm}" ) ;
}
if ( decryptedValueOfGvm ! = testValue )
{
throw new Exception ( $"GeneratedEncryptionVirtualMachine decrypt failed! opCode:{i}, originalValue:{testValue} decryptedValue:{decryptedValueOfGvm}" ) ;
}
2025-05-14 10:46:42 +08:00
2025-05-14 12:21:25 +08:00
byte [ ] encryptedStrOfVms = vms . Encrypt ( testString , ops , salt ) ;
string descryptedStrOfVms = vms . DecryptString ( encryptedStrOfVms , 0 , encryptedStrOfVms . Length , ops , salt ) ;
2025-05-14 10:46:42 +08:00
if ( descryptedStrOfVms ! = testString )
{
throw new Exception ( $"VirtualMachineSimulator decrypt string failed! opCode:{i}, originalValue:{testString} decryptedValue:{descryptedStrOfVms}" ) ;
}
2025-05-14 12:21:25 +08:00
byte [ ] encryptedStrOfGvm = gvmInstance . Encrypt ( testString , ops , salt ) ;
string descryptedStrOfGvm = gvmInstance . DecryptString ( encryptedStrOfGvm , 0 , encryptedStrOfGvm . Length , ops , salt ) ;
2025-05-14 10:46:42 +08:00
if ( ! encryptedStrOfGvm . SequenceEqual ( encryptedStrOfVms ) )
{
throw new Exception ( $"encryptedValue not match! opCode:{i}, originalValue:{testString} encryptedValue VirtualMachineSimulator:{encryptedStrOfVms} GeneratedEncryptionVirtualMachine:{encryptedStrOfGvm}" ) ;
}
if ( descryptedStrOfGvm ! = testString )
{
throw new Exception ( $"GeneratedEncryptionVirtualMachine decrypt string failed! opCode:{i}, originalValue:{testString} decryptedValue:{descryptedStrOfGvm}" ) ;
}
2025-05-11 19:28:19 +08:00
}
return vms ;
2025-05-11 12:48:53 +08:00
}
2025-05-12 17:21:20 +08:00
private void OnPreObfuscation ( Pipeline pipeline )
2025-04-05 19:02:50 +08:00
{
2025-05-12 17:21:20 +08:00
AssemblyCache assemblyCache = new AssemblyCache ( new PathAssemblyResolver ( _assemblySearchDirs . ToArray ( ) ) ) ;
List < ModuleDef > toObfuscatedModules = new List < ModuleDef > ( ) ;
List < ModuleDef > obfuscatedAndNotObfuscatedModules = new List < ModuleDef > ( ) ;
LoadAssemblies ( assemblyCache , toObfuscatedModules , obfuscatedAndNotObfuscatedModules ) ;
2025-05-03 22:32:18 +08:00
2025-05-13 08:56:19 +08:00
var random = new RandomWithKey ( _intSecret , _randomSeed ) ;
2025-05-11 12:48:53 +08:00
var encryptor = CreateEncryptionVirtualMachine ( ) ;
2025-05-13 08:49:57 +08:00
var moduleEntityManager = new GroupByModuleEntityManager ( ) ;
var rvaDataAllocator = new RvaDataAllocator ( random , encryptor , moduleEntityManager ) ;
var constFieldAllocator = new ConstFieldAllocator ( encryptor , random , rvaDataAllocator , moduleEntityManager ) ;
2025-05-04 19:24:14 +08:00
_ctx = new ObfuscationPassContext
2025-05-03 22:32:18 +08:00
{
2025-05-12 17:21:20 +08:00
assemblyCache = assemblyCache ,
toObfuscatedModules = toObfuscatedModules ,
obfuscatedAndNotObfuscatedModules = obfuscatedAndNotObfuscatedModules ,
2025-05-03 22:32:18 +08:00
toObfuscatedAssemblyNames = _toObfuscatedAssemblyNames ,
notObfuscatedAssemblyNamesReferencingObfuscated = _notObfuscatedAssemblyNamesReferencingObfuscated ,
obfuscatedAssemblyOutputDir = _obfuscatedAssemblyOutputDir ,
2025-05-13 08:49:57 +08:00
moduleEntityManager = moduleEntityManager ,
2025-05-10 11:25:07 +08:00
2025-05-13 08:56:19 +08:00
globalRandom = random ,
2025-05-13 09:27:44 +08:00
localRandomCreator = ( seed ) = > new RandomWithKey ( _intSecret , _randomSeed ^ seed ) ,
2025-05-10 11:25:07 +08:00
encryptor = encryptor ,
rvaDataAllocator = rvaDataAllocator ,
constFieldAllocator = constFieldAllocator ,
2025-05-12 18:03:39 +08:00
whiteList = new NotObfuscatedMethodWhiteList ( ) ,
2025-05-12 22:01:35 +08:00
passPolicy = _passPolicy ,
2025-05-03 22:32:18 +08:00
} ;
2025-05-13 08:49:57 +08:00
ObfuscationPassContext . Current = _ctx ;
pipeline . Start ( ) ;
2025-04-05 19:02:50 +08:00
}
2025-05-12 17:21:20 +08:00
private void LoadAssemblies ( AssemblyCache assemblyCache , List < ModuleDef > toObfuscatedModules , List < ModuleDef > obfuscatedAndNotObfuscatedModules )
2025-04-05 19:02:50 +08:00
{
2025-05-03 23:23:16 +08:00
foreach ( string assName in _toObfuscatedAssemblyNames . Concat ( _notObfuscatedAssemblyNamesReferencingObfuscated ) )
2025-04-05 19:02:50 +08:00
{
2025-05-12 17:21:20 +08:00
ModuleDefMD mod = assemblyCache . TryLoadModule ( assName ) ;
2025-04-17 22:02:48 +08:00
if ( mod = = null )
{
Debug . Log ( $"assembly: {assName} not found! ignore." ) ;
continue ;
}
2025-05-03 23:23:16 +08:00
if ( _toObfuscatedAssemblyNames . Contains ( assName ) )
2025-04-05 19:02:50 +08:00
{
2025-05-12 17:21:20 +08:00
toObfuscatedModules . Add ( mod ) ;
2025-04-05 19:02:50 +08:00
}
2025-05-12 17:21:20 +08:00
obfuscatedAndNotObfuscatedModules . Add ( mod ) ;
2025-04-05 19:02:50 +08:00
}
}
2025-05-12 17:21:20 +08:00
private void WriteAssemblies ( )
2025-04-05 19:02:50 +08:00
{
2025-05-12 17:21:20 +08:00
foreach ( ModuleDef mod in _ctx . obfuscatedAndNotObfuscatedModules )
2025-04-05 21:47:28 +08:00
{
2025-05-03 23:23:16 +08:00
string assNameWithExt = mod . Name ;
string outputFile = $"{_obfuscatedAssemblyOutputDir}/{assNameWithExt}" ;
mod . Write ( outputFile ) ;
Debug . Log ( $"save module. name:{mod.Assembly.Name} output:{outputFile}" ) ;
2025-04-05 21:47:28 +08:00
}
}
2025-05-12 17:21:20 +08:00
private void DoObfuscation ( Pipeline pipeline )
{
2025-05-13 08:49:57 +08:00
pipeline . Run ( ) ;
2025-05-12 17:21:20 +08:00
}
private void OnPostObfuscation ( Pipeline pipeline )
{
2025-05-13 08:49:57 +08:00
pipeline . Stop ( ) ;
2025-05-14 10:46:42 +08:00
_ctx . constFieldAllocator . Done ( ) ;
_ctx . rvaDataAllocator . Done ( ) ;
2025-05-12 17:21:20 +08:00
WriteAssemblies ( ) ;
}
2025-04-05 19:02:50 +08:00
}
}