obfuz/Editor/Obfuscator.cs

345 lines
17 KiB
C#
Raw Normal View History

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;
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;
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;
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;
private readonly Pipeline _pipeline1 = new Pipeline();
private readonly Pipeline _pipeline2 = new Pipeline();
2025-05-16 11:33:03 +08:00
private readonly byte[] _defaultStaticByteSecret;
private readonly int[] _defaultStaticIntSecret;
private readonly byte[] _defaultDynamicByteSecret;
private readonly int[] _defaultDynamicIntSecret;
private readonly HashSet<string> _dynamicSecretAssemblyNames;
private readonly int _randomSeed;
private readonly string _encryptionVmGenerationSecret;
2025-05-11 17:36:58 +08:00
private readonly int _encryptionVmOpCodeCount;
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-16 11:33:03 +08:00
_defaultStaticByteSecret = KeyGenerator.GenerateKey(builder.DefaultStaticSecret, VirtualMachine.SecretKeyLength);
_defaultStaticIntSecret = KeyGenerator.ConvertToIntKey(_defaultStaticByteSecret);
SaveKey(_defaultStaticByteSecret, builder.DefaultStaticSecretOutputPath);
_defaultDynamicByteSecret = KeyGenerator.GenerateKey(builder.DefaultDynamicSecret, VirtualMachine.SecretKeyLength);
_defaultDynamicIntSecret = KeyGenerator.ConvertToIntKey(_defaultDynamicByteSecret);
SaveKey(_defaultDynamicByteSecret, builder.DefaultDynamicSecretOutputPath);
_dynamicSecretAssemblyNames = new HashSet<string>(builder.DynamicSecretAssemblyNames);
_randomSeed = builder.RandomSeed;
_encryptionVmGenerationSecret = builder.EncryptionVmGenerationSecretKey;
2025-05-11 17:36:58 +08:00
_encryptionVmOpCodeCount = builder.EncryptionVmOpCodeCount;
_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;
_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
{
if (pass is SymbolObfusPass symbolObfusPass)
{
_pipeline2.AddPass(pass);
}
else
{
_pipeline1.AddPass(pass);
}
2025-05-03 21:43:50 +08:00
}
_pipeline1.AddPass(new CleanUpInstructionPass());
_pipeline2.AddPass(new RemoveObfuzAttributesPass());
2025-04-05 19:02:50 +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
{
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-16 11:33:03 +08:00
private IEncryptor CreateEncryptionVirtualMachine(byte[] secret)
2025-05-11 12:48:53 +08:00
{
var vmCreator = new VirtualMachineCreator(_encryptionVmGenerationSecret);
2025-05-11 17:36:58 +08:00
var vm = vmCreator.CreateVirtualMachine(_encryptionVmOpCodeCount);
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-16 11:33:03 +08:00
var vms = new VirtualMachineSimulator(vm, secret);
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-16 11:33:03 +08:00
var gvmInstance = (IEncryptor)Activator.CreateInstance(generatedVmTypes[0], new object[] { secret } );
2025-05-14 14:36:25 +08:00
VerifyVm(vm, vms, gvmInstance);
return vms;
}
private void VerifyVm(VirtualMachine vm, VirtualMachineSimulator vms, IEncryptor gvmInstance)
{
int testInt = 11223344;
long testLong = 1122334455667788L;
float testFloat = 1234f;
double testDouble = 1122334455.0;
string testString = "hello,world";
for (int i = 0; i < vm.opCodes.Length; i++)
{
int ops = i * vm.opCodes.Length + i;
//int salt = i;
//int ops = -1135538782;
int salt = -879409147;
{
2025-05-14 14:36:25 +08:00
int encryptedIntOfVms = vms.Encrypt(testInt, ops, salt);
int decryptedIntOfVms = vms.Decrypt(encryptedIntOfVms, ops, salt);
if (decryptedIntOfVms != testInt)
{
throw new Exception($"VirtualMachineSimulator decrypt failed! opCode:{i}, originalValue:{testInt} decryptedValue:{decryptedIntOfVms}");
}
int encryptedValueOfGvm = gvmInstance.Encrypt(testInt, ops, salt);
int decryptedValueOfGvm = gvmInstance.Decrypt(encryptedValueOfGvm, ops, salt);
if (encryptedValueOfGvm != encryptedIntOfVms)
{
throw new Exception($"encryptedValue not match! opCode:{i}, originalValue:{testInt} encryptedValue VirtualMachineSimulator:{encryptedIntOfVms} GeneratedEncryptionVirtualMachine:{encryptedValueOfGvm}");
}
if (decryptedValueOfGvm != testInt)
{
throw new Exception($"GeneratedEncryptionVirtualMachine decrypt failed! opCode:{i}, originalValue:{testInt} decryptedValue:{decryptedValueOfGvm}");
}
}
{
2025-05-14 14:36:25 +08:00
long encryptedLongOfVms = vms.Encrypt(testLong, ops, salt);
long decryptedLongOfVms = vms.Decrypt(encryptedLongOfVms, ops, salt);
if (decryptedLongOfVms != testLong)
{
throw new Exception($"VirtualMachineSimulator decrypt long failed! opCode:{i}, originalValue:{testLong} decryptedValue:{decryptedLongOfVms}");
}
long encryptedValueOfGvm = gvmInstance.Encrypt(testLong, ops, salt);
long decryptedValueOfGvm = gvmInstance.Decrypt(encryptedValueOfGvm, ops, salt);
if (encryptedValueOfGvm != encryptedLongOfVms)
{
throw new Exception($"encryptedValue not match! opCode:{i}, originalValue:{testLong} encryptedValue VirtualMachineSimulator:{encryptedLongOfVms} GeneratedEncryptionVirtualMachine:{encryptedValueOfGvm}");
}
if (decryptedValueOfGvm != testLong)
{
throw new Exception($"GeneratedEncryptionVirtualMachine decrypt long failed! opCode:{i}, originalValue:{testLong} decryptedValue:{decryptedValueOfGvm}");
}
}
{
2025-05-14 14:36:25 +08:00
float encryptedFloatOfVms = vms.Encrypt(testFloat, ops, salt);
float decryptedFloatOfVms = vms.Decrypt(encryptedFloatOfVms, ops, salt);
if (decryptedFloatOfVms != testFloat)
{
throw new Exception("encryptedFloat not match");
}
float encryptedValueOfGvm = gvmInstance.Encrypt(testFloat, ops, salt);
float decryptedValueOfGvm = gvmInstance.Decrypt(encryptedFloatOfVms, ops, salt);
if (encryptedFloatOfVms != encryptedValueOfGvm)
{
throw new Exception($"encryptedValue not match! opCode:{i}, originalValue:{testFloat} encryptedValue");
}
if (decryptedValueOfGvm != testFloat)
{
throw new Exception($"GeneratedEncryptionVirtualMachine decrypt float failed! opCode:{i}, originalValue:{testFloat}");
}
}
{
2025-05-14 14:36:25 +08:00
double encryptedFloatOfVms = vms.Encrypt(testDouble, ops, salt);
double decryptedFloatOfVms = vms.Decrypt(encryptedFloatOfVms, ops, salt);
if (decryptedFloatOfVms != testDouble)
{
throw new Exception("encryptedFloat not match");
}
double encryptedValueOfGvm = gvmInstance.Encrypt(testDouble, ops, salt);
double decryptedValueOfGvm = gvmInstance.Decrypt(encryptedFloatOfVms, ops, salt);
if (encryptedFloatOfVms != encryptedValueOfGvm)
{
throw new Exception($"encryptedValue not match! opCode:{i}, originalValue:{testDouble} encryptedValue");
}
if (decryptedValueOfGvm != testDouble)
{
throw new Exception($"GeneratedEncryptionVirtualMachine decrypt float failed! opCode:{i}, originalValue:{testDouble}");
}
}
2025-05-14 14:36:25 +08:00
{
2025-05-14 14:36:25 +08:00
byte[] encryptedStrOfVms = vms.Encrypt(testString, ops, salt);
string descryptedStrOfVms = vms.DecryptString(encryptedStrOfVms, 0, encryptedStrOfVms.Length, ops, salt);
if (descryptedStrOfVms != testString)
{
throw new Exception($"VirtualMachineSimulator decrypt string failed! opCode:{i}, originalValue:{testString} decryptedValue:{descryptedStrOfVms}");
}
byte[] encryptedStrOfGvm = gvmInstance.Encrypt(testString, ops, salt);
string descryptedStrOfGvm = gvmInstance.DecryptString(encryptedStrOfGvm, 0, encryptedStrOfGvm.Length, ops, salt);
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 12:48:53 +08:00
}
2025-05-16 11:33:03 +08:00
private EncryptionScopeInfo CreateEncryptionScope(byte[] byteSecret, int[] intSecret)
{
IEncryptor encryption = CreateEncryptionVirtualMachine(byteSecret);
RandomCreator localRandomCreator = (seed) => new RandomWithKey(intSecret, _randomSeed ^ seed);
return new EncryptionScopeInfo(byteSecret, intSecret, encryption, localRandomCreator);
}
private EncryptionScopeProvider CreateEncryptionScopeProvider()
{
var defaultStaticScope = CreateEncryptionScope(_defaultStaticByteSecret, _defaultStaticIntSecret);
var defaultDynamicScope = CreateEncryptionScope(_defaultDynamicByteSecret, _defaultDynamicIntSecret);
return new EncryptionScopeProvider(defaultStaticScope, defaultDynamicScope, _dynamicSecretAssemblyNames);
}
private void OnPreObfuscation(Pipeline pipeline)
2025-04-05 19:02:50 +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-16 11:33:03 +08:00
EncryptionScopeProvider encryptionScopeProvider = CreateEncryptionScopeProvider();
var moduleEntityManager = new GroupByModuleEntityManager();
2025-05-16 11:33:03 +08:00
var rvaDataAllocator = new RvaDataAllocator(encryptionScopeProvider, moduleEntityManager);
var constFieldAllocator = new ConstFieldAllocator(encryptionScopeProvider, rvaDataAllocator, moduleEntityManager);
2025-05-04 19:24:14 +08:00
_ctx = new ObfuscationPassContext
{
assemblyCache = assemblyCache,
toObfuscatedModules = toObfuscatedModules,
obfuscatedAndNotObfuscatedModules = obfuscatedAndNotObfuscatedModules,
toObfuscatedAssemblyNames = _toObfuscatedAssemblyNames,
notObfuscatedAssemblyNamesReferencingObfuscated = _notObfuscatedAssemblyNamesReferencingObfuscated,
obfuscatedAssemblyOutputDir = _obfuscatedAssemblyOutputDir,
moduleEntityManager = moduleEntityManager,
2025-05-10 11:25:07 +08:00
2025-05-16 11:33:03 +08:00
encryptionScopeProvider = encryptionScopeProvider,
2025-05-10 11:25:07 +08:00
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,
};
ObfuscationPassContext.Current = _ctx;
pipeline.Start();
2025-04-05 19:02:50 +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
{
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
{
toObfuscatedModules.Add(mod);
2025-04-05 19:02:50 +08:00
}
obfuscatedAndNotObfuscatedModules.Add(mod);
2025-04-05 19:02:50 +08:00
}
}
private void WriteAssemblies()
2025-04-05 19:02:50 +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
}
}
private void DoObfuscation(Pipeline pipeline)
{
pipeline.Run();
}
private void OnPostObfuscation(Pipeline pipeline)
{
pipeline.Stop();
_ctx.constFieldAllocator.Done();
_ctx.rvaDataAllocator.Done();
WriteAssemblies();
}
2025-04-05 19:02:50 +08:00
}
}