Compare commits
No commits in common. "main" and "1.0.0-alpha" have entirely different histories.
main
...
1.0.0-alpha
|
@ -0,0 +1,22 @@
|
|||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
||||
|
||||
**/Library/
|
||||
**/Logs/
|
||||
**/Temp/
|
||||
**/Build-*/
|
||||
**/Release-*/
|
||||
**/Debug-*/
|
||||
|
||||
**/.vs/
|
||||
**/bin/
|
||||
**/obj/
|
||||
|
||||
|
||||
.vsconfig
|
||||
**/UserSettings/
|
||||
*.csproj
|
||||
*.sln
|
||||
packages-lock.json
|
|
@ -0,0 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>disable</Nullable>
|
||||
<Version>1.0.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandLineParser" Version="2.9.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,24 @@
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.13.35931.197
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeobfuscateStackTrace", "DeobfuscateStackTrace.csproj", "{B7192F39-1EEA-4F31-885B-B606D700FC79}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{B7192F39-1EEA-4F31-885B-B606D700FC79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B7192F39-1EEA-4F31-885B-B606D700FC79}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B7192F39-1EEA-4F31-885B-B606D700FC79}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B7192F39-1EEA-4F31-885B-B606D700FC79}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {9F39E3ED-EF31-43DE-B085-0F7BF60844E8}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,57 @@
|
|||
using CommandLine;
|
||||
|
||||
namespace DeobfuscateStackTrace
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
private class CommandOptions
|
||||
{
|
||||
|
||||
[Option('m', "mappingFile", Required = true, HelpText = "mapping xml file")]
|
||||
public string MappingFile { get; set; }
|
||||
|
||||
[Option('i', "input", Required = true, HelpText = "input obfuscated log file")]
|
||||
public string InputFile { get; set; }
|
||||
|
||||
[Option('o', "output", Required = true, HelpText = "output deobfuscated log file")]
|
||||
public string OutputFile { get; set; }
|
||||
|
||||
}
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
CommandOptions opt = ParseArgs(args);
|
||||
|
||||
if (!File.Exists(opt.MappingFile))
|
||||
{
|
||||
Console.Error.WriteLine($"Mapping file {opt.MappingFile} not found");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
if (!File.Exists(opt.InputFile))
|
||||
{
|
||||
Console.Error.WriteLine($"Input file {opt.InputFile} not found");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
var reader = new SymbolMappingReader(opt.MappingFile);
|
||||
StackTraceDeObfuscator.Convert(reader, opt.InputFile, opt.OutputFile);
|
||||
}
|
||||
|
||||
private static CommandOptions ParseArgs(string[] args)
|
||||
{
|
||||
var helpWriter = new StringWriter();
|
||||
var parser = new Parser(settings =>
|
||||
{
|
||||
settings.AllowMultiInstance = true;
|
||||
settings.HelpWriter = helpWriter;
|
||||
});
|
||||
|
||||
var result = parser.ParseArguments<CommandOptions>(args);
|
||||
if (result.Tag == ParserResultType.NotParsed)
|
||||
{
|
||||
Console.Error.WriteLine(helpWriter.ToString());
|
||||
Environment.Exit(1);
|
||||
}
|
||||
return ((Parsed<CommandOptions>)result).Value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"profiles": {
|
||||
"DeobfuscateStackTrace": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "-m ../../../mapping.xml -i ../../../obfuscated.log -o ../../../deobfuscated.log"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DeobfuscateStackTrace
|
||||
{
|
||||
public class StackTraceDeObfuscator
|
||||
{
|
||||
public static void Convert(SymbolMappingReader reader, string oldLogFile, string newLogFile)
|
||||
{
|
||||
var obfuscatedLines = File.ReadAllLines(oldLogFile, Encoding.UTF8);
|
||||
var deObfuscatedLines = new List<string>();
|
||||
|
||||
bool logLineFound = false;
|
||||
foreach (string line in obfuscatedLines)
|
||||
{
|
||||
if (TryConvertLine(line, reader, ref logLineFound, out var newLine))
|
||||
{
|
||||
deObfuscatedLines.Add(newLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
deObfuscatedLines.Add(line);
|
||||
}
|
||||
}
|
||||
File.WriteAllLines(newLogFile, deObfuscatedLines, Encoding.UTF8);
|
||||
}
|
||||
|
||||
private static bool TryConvertLine(string line, SymbolMappingReader reader, ref bool logLineFound, out string deObfuscatedStackTrace)
|
||||
{
|
||||
deObfuscatedStackTrace = line;
|
||||
if (string.IsNullOrEmpty(line))
|
||||
{
|
||||
logLineFound = false;
|
||||
return false;
|
||||
}
|
||||
if (!logLineFound)
|
||||
{
|
||||
logLineFound = line.StartsWith("UnityEngine.DebugLogHandler:Internal_Log")
|
||||
|| line.StartsWith("UnityEngine.DebugLogHandler:LogFormat")
|
||||
|| line.StartsWith("UnityEngine.Logger:Log");
|
||||
return false;
|
||||
}
|
||||
return reader.TryDeObfuscateStackTrace(line, out deObfuscatedStackTrace);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
|
||||
using System.Xml;
|
||||
|
||||
namespace DeobfuscateStackTrace
|
||||
{
|
||||
|
||||
public class SymbolMappingReader
|
||||
{
|
||||
|
||||
private readonly Dictionary<string, List<string>> _fullSignatureMapper = new Dictionary<string, List<string>>();
|
||||
private readonly Dictionary<string, List<string>> _signatureWithParamsMapper = new Dictionary<string, List<string>>();
|
||||
|
||||
public SymbolMappingReader(string mappingFile)
|
||||
{
|
||||
LoadXmlMappingFile(mappingFile);
|
||||
}
|
||||
|
||||
private void LoadXmlMappingFile(string mappingFile)
|
||||
{
|
||||
var doc = new XmlDocument();
|
||||
doc.Load(mappingFile);
|
||||
var root = doc.DocumentElement;
|
||||
foreach (XmlNode node in root.ChildNodes)
|
||||
{
|
||||
if (!(node is XmlElement element))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
LoadAssemblyMapping(element);
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadAssemblyMapping(XmlElement ele)
|
||||
{
|
||||
if (ele.Name != "assembly")
|
||||
{
|
||||
throw new System.Exception($"Invalid node name: {ele.Name}. Expected 'assembly'.");
|
||||
}
|
||||
foreach (XmlNode node in ele.ChildNodes)
|
||||
{
|
||||
if (!(node is XmlElement element))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (element.Name == "type")
|
||||
{
|
||||
LoadTypeMapping(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadTypeMapping(XmlElement ele)
|
||||
{
|
||||
foreach (XmlNode node in ele.ChildNodes)
|
||||
{
|
||||
if (!(node is XmlElement c))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (node.Name == "method")
|
||||
{
|
||||
LoadMethodMapping(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private string GetMethodSignatureWithoutParams(string signature)
|
||||
{
|
||||
int index = signature.IndexOf('(');
|
||||
if (index < 0)
|
||||
{
|
||||
return signature;
|
||||
}
|
||||
return signature.Substring(0, index);
|
||||
}
|
||||
|
||||
private void LoadMethodMapping(XmlElement ele)
|
||||
{
|
||||
if (!ele.HasAttribute("oldStackTraceSignature"))
|
||||
{
|
||||
throw new System.Exception($"Invalid node name: {ele.Name}. attribute 'oldStackTraceSignature' missing.");
|
||||
}
|
||||
if (!ele.HasAttribute("newStackTraceSignature"))
|
||||
{
|
||||
throw new System.Exception($"Invalid node name: {ele.Name}. attribute 'newStackTraceSignature' missing.");
|
||||
}
|
||||
string oldStackTraceSignature = ele.Attributes["oldStackTraceSignature"].Value;
|
||||
string newStackTraceSignature = ele.Attributes["newStackTraceSignature"].Value;
|
||||
|
||||
if (!_fullSignatureMapper.TryGetValue(newStackTraceSignature, out var oldFullSignatures))
|
||||
{
|
||||
oldFullSignatures = new List<string>();
|
||||
_fullSignatureMapper[newStackTraceSignature] = oldFullSignatures;
|
||||
}
|
||||
oldFullSignatures.Add(oldStackTraceSignature);
|
||||
|
||||
string oldStackTraceSignatureWithoutParams = GetMethodSignatureWithoutParams(oldStackTraceSignature);
|
||||
string newStackTraceSignatureWithoutParams = GetMethodSignatureWithoutParams(newStackTraceSignature);
|
||||
if (!_signatureWithParamsMapper.TryGetValue(newStackTraceSignatureWithoutParams, out var oldSignaturesWithoutParams))
|
||||
{
|
||||
oldSignaturesWithoutParams = new List<string>();
|
||||
_signatureWithParamsMapper[newStackTraceSignatureWithoutParams] = oldSignaturesWithoutParams;
|
||||
}
|
||||
oldSignaturesWithoutParams.Add(oldStackTraceSignatureWithoutParams);
|
||||
}
|
||||
|
||||
|
||||
public bool TryDeObfuscateStackTrace(string obfuscatedStackTraceLog, out string deObfuscatedStackTrace)
|
||||
{
|
||||
obfuscatedStackTraceLog = obfuscatedStackTraceLog.Trim();
|
||||
if (_fullSignatureMapper.TryGetValue(obfuscatedStackTraceLog, out var oldFullSignatures))
|
||||
{
|
||||
deObfuscatedStackTrace = string.Join("|", oldFullSignatures);
|
||||
return true;
|
||||
}
|
||||
|
||||
string obfuscatedStackTraceSignatureWithoutParams = GetMethodSignatureWithoutParams(obfuscatedStackTraceLog);
|
||||
if (_signatureWithParamsMapper.TryGetValue(obfuscatedStackTraceSignatureWithoutParams, out var oldSignaturesWithoutParams))
|
||||
{
|
||||
deObfuscatedStackTrace = obfuscatedStackTraceLog.Replace(obfuscatedStackTraceSignatureWithoutParams, string.Join("|", oldSignaturesWithoutParams));
|
||||
return true;
|
||||
}
|
||||
deObfuscatedStackTrace = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
test stack trace
|
||||
UnityEngine.DebugLogHandler:Internal_Log(LogType, LogOption, String, Object)
|
||||
UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
|
||||
UnityEngine.Logger:Log(LogType, Object)
|
||||
UnityEngine.Debug:Log(Object)
|
||||
Obfus2.TestStackTrace:Stack3()
|
||||
Obfus2.NestedClass`1:Stack2(TestStackTrace, Int32[], List`1, Banana)
|
||||
Obfus2.TestStackTrace:Stack1(Int64, UInt64, Single, Double, String, Object)
|
||||
Obfus2.TestStackTrace:Stack0(Byte, SByte, Int16, UInt16, Int32, UInt32)
|
||||
Tests.TC_StackTrace:PrintStackTrace()
|
||||
System.Reflection.RuntimeMethodInfo:InternalInvoke(Object, Object[], Exception&)
|
||||
System.Reflection.RuntimeMethodInfo:Invoke(Object, BindingFlags, Binder, Object[], CultureInfo)
|
||||
System.Reflection.MethodBase:Invoke(Object, Object[])
|
||||
SharpUnit.TestCase:Run(TestResult)
|
||||
SharpUnit.TestSuite:Run(TestResult)
|
||||
TestRunner:Run()
|
||||
Bootstrap:Start()
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,17 @@
|
|||
test stack trace
|
||||
UnityEngine.DebugLogHandler:Internal_Log(LogType, LogOption, String, Object)
|
||||
UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
|
||||
UnityEngine.Logger:Log(LogType, Object)
|
||||
UnityEngine.Debug:Log(Object)
|
||||
F.g:A()
|
||||
F.G:a(g, Int32[], List`1, Banana)
|
||||
F.g:a(Int64, UInt64, Single, Double, String, Object)
|
||||
F.g:b(Byte, SByte, Int16, UInt16, Int32, UInt32)
|
||||
Tests.TC_StackTrace:PrintStackTrace()
|
||||
System.Reflection.RuntimeMethodInfo:InternalInvoke(Object, Object[], Exception&)
|
||||
System.Reflection.RuntimeMethodInfo:Invoke(Object, BindingFlags, Binder, Object[], CultureInfo)
|
||||
System.Reflection.MethodBase:Invoke(Object, Object[])
|
||||
SharpUnit.TestCase:Run(TestResult)
|
||||
SharpUnit.TestSuite:Run(TestResult)
|
||||
TestRunner:Run()
|
||||
Bootstrap:Start()
|
|
@ -1,32 +0,0 @@
|
|||
namespace Obfuz.Editor
|
||||
{
|
||||
public static class ConstValues
|
||||
{
|
||||
public const string ObfuzInternalSymbolNamePrefix = "$Obfuz$";
|
||||
|
||||
public const string ObfuzRuntimeAssemblyName = "Obfuz.Runtime";
|
||||
|
||||
public const string ObfuzIgnoreAttributeFullName = "Obfuz.ObfuzIgnoreAttribute";
|
||||
|
||||
public const string ObfuzScopeFullName = "Obfuz.ObfuzScope";
|
||||
|
||||
public const string EncryptFieldAttributeFullName = "Obfuz.EncryptFieldAttribute";
|
||||
public const string GeneratedEncryptionVirtualMachineFullName = "Obfuz.EncryptionVM.GeneratedEncryptionVirtualMachine";
|
||||
|
||||
public const string EmbeddedAttributeFullName = "Microsoft.CodeAnalysis.EmbeddedAttribute";
|
||||
|
||||
public const string MonoPInvokeCallbackAttributeName = "MonoPInvokeCallbackAttribute";
|
||||
|
||||
public const string ZluaLuaInvokeAttributeFullName = "Zlua.LuaInvokeAttribute";
|
||||
public const string ZluaLuaCallbackAttributeFullName = "Zlua.LuaCallbackAttribute";
|
||||
public const string ZluaLuaMarshalAsAttributeFullName = "Zlua.LuaMarshalAsAttribute";
|
||||
|
||||
public const string BurstCompileFullName = "Unity.Burst.BurstCompileAttribute";
|
||||
public const string DOTSCompilerGeneratedAttributeFullName = "Unity.Jobs.DOTSCompilerGeneratedAttribute";
|
||||
|
||||
public const string RuntimeInitializedOnLoadMethodAttributeFullName = "UnityEngine.RuntimeInitializeOnLoadMethodAttribute";
|
||||
public const string BlackboardEnumAttributeFullName = "Unity.Behavior.BlackboardEnumAttribute";
|
||||
|
||||
public const string CompilerGeneratedAttributeFullName = "System.Runtime.CompilerServices.CompilerGeneratedAttribute";
|
||||
}
|
||||
}
|
|
@ -1,323 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Emit;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace Obfuz.Data
|
||||
{
|
||||
public struct RvaData
|
||||
{
|
||||
public readonly FieldDef field;
|
||||
public readonly int offset;
|
||||
public readonly int size;
|
||||
|
||||
public RvaData(FieldDef field, int offset, int size)
|
||||
{
|
||||
this.field = field;
|
||||
this.offset = offset;
|
||||
this.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
public class RvaDataAllocator : GroupByModuleEntityBase
|
||||
{
|
||||
const int maxRvaDataSize = 2 * 1024;
|
||||
|
||||
// in HybridCLR version below 8.3.0, the max total static field size of a type is 16KB, so we limit the total size of RVA data to 16KB
|
||||
const int maxTotalRvaDataFieldSizeInHybridCLR = 16 * 1024;
|
||||
|
||||
private IRandom _random;
|
||||
|
||||
class RvaField
|
||||
{
|
||||
public FieldDef holderDataField;
|
||||
public FieldDef runtimeValueField;
|
||||
public int encryptionOps;
|
||||
public uint size;
|
||||
public List<byte> bytes;
|
||||
public int salt;
|
||||
|
||||
public void FillPaddingToSize(int newSize)
|
||||
{
|
||||
for (int i = bytes.Count; i < newSize; i++)
|
||||
{
|
||||
bytes.Add(0xAB);
|
||||
}
|
||||
}
|
||||
|
||||
public void FillPaddingToEnd()
|
||||
{
|
||||
// fill with random value
|
||||
for (int i = bytes.Count; i < size; i++)
|
||||
{
|
||||
bytes.Add(0xAB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class RvaTypeDefInfo
|
||||
{
|
||||
public readonly TypeDef typeDef;
|
||||
public readonly int index;
|
||||
public readonly List<RvaField> rvaFields = new List<RvaField>();
|
||||
|
||||
public RvaTypeDefInfo(TypeDef typeDef, int index)
|
||||
{
|
||||
this.typeDef = typeDef;
|
||||
this.index = index;
|
||||
}
|
||||
}
|
||||
|
||||
private RvaField _currentField;
|
||||
|
||||
private RvaTypeDefInfo _currentRvaType;
|
||||
private readonly List<RvaTypeDefInfo> _rvaTypeDefs = new List<RvaTypeDefInfo>();
|
||||
|
||||
private readonly Dictionary<int, TypeDef> _dataHolderTypeBySizes = new Dictionary<int, TypeDef>();
|
||||
private bool _done;
|
||||
|
||||
public RvaDataAllocator()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
_random = EncryptionScope.localRandomCreator(HashUtil.ComputeHash(Module.Name));
|
||||
}
|
||||
|
||||
private (FieldDef, FieldDef) CreateDataHolderRvaField(TypeDef dataHolderType)
|
||||
{
|
||||
if (_currentRvaType == null || _currentRvaType.rvaFields.Count >= maxTotalRvaDataFieldSizeInHybridCLR / maxRvaDataSize - 1)
|
||||
{
|
||||
using (var scope = new DisableTypeDefFindCacheScope(Module))
|
||||
{
|
||||
var rvaTypeDef = new TypeDefUser($"$Obfuz$RVA${_rvaTypeDefs.Count}", Module.CorLibTypes.Object.ToTypeDefOrRef());
|
||||
Module.Types.Add(rvaTypeDef);
|
||||
_currentRvaType = new RvaTypeDefInfo(rvaTypeDef, _rvaTypeDefs.Count);
|
||||
_rvaTypeDefs.Add(_currentRvaType);
|
||||
}
|
||||
}
|
||||
|
||||
var holderField = new FieldDefUser($"$RVA_Data{_currentRvaType.rvaFields.Count}", new FieldSig(dataHolderType.ToTypeSig()), FieldAttributes.InitOnly | FieldAttributes.Static | FieldAttributes.HasFieldRVA);
|
||||
holderField.DeclaringType = _currentRvaType.typeDef;
|
||||
|
||||
var runtimeValueField = new FieldDefUser($"$RVA_Value{_currentRvaType.rvaFields.Count}", new FieldSig(new SZArraySig(Module.CorLibTypes.Byte)), FieldAttributes.Static | FieldAttributes.Public);
|
||||
runtimeValueField.DeclaringType = _currentRvaType.typeDef;
|
||||
return (holderField, runtimeValueField);
|
||||
}
|
||||
|
||||
private TypeDef GetDataHolderType(int size)
|
||||
{
|
||||
size = (size + 15) & ~15; // align to 16 bytes
|
||||
if (_dataHolderTypeBySizes.TryGetValue(size, out var type))
|
||||
return type;
|
||||
|
||||
using (var scope = new DisableTypeDefFindCacheScope(Module))
|
||||
{
|
||||
var dataHolderType = new TypeDefUser($"$ObfuzRVA$DataHolder{size}", Module.Import(typeof(ValueType)));
|
||||
dataHolderType.Attributes = TypeAttributes.Public | TypeAttributes.Sealed;
|
||||
dataHolderType.Layout = TypeAttributes.ExplicitLayout;
|
||||
dataHolderType.PackingSize = 1;
|
||||
dataHolderType.ClassSize = (uint)size;
|
||||
_dataHolderTypeBySizes.Add(size, dataHolderType);
|
||||
Module.Types.Add(dataHolderType);
|
||||
return dataHolderType;
|
||||
}
|
||||
}
|
||||
|
||||
private static int AlignTo(int size, int alignment)
|
||||
{
|
||||
return (size + alignment - 1) & ~(alignment - 1);
|
||||
}
|
||||
|
||||
private RvaField CreateRvaField(int size)
|
||||
{
|
||||
TypeDef dataHolderType = GetDataHolderType(size);
|
||||
var (holderDataField, runtimeValueField) = CreateDataHolderRvaField(dataHolderType);
|
||||
var newRvaField = new RvaField
|
||||
{
|
||||
holderDataField = holderDataField,
|
||||
runtimeValueField = runtimeValueField,
|
||||
size = dataHolderType.ClassSize,
|
||||
bytes = new List<byte>((int)dataHolderType.ClassSize),
|
||||
encryptionOps = _random.NextInt(),
|
||||
salt = _random.NextInt(),
|
||||
};
|
||||
_currentRvaType.rvaFields.Add(newRvaField);
|
||||
return newRvaField;
|
||||
}
|
||||
|
||||
private RvaField GetRvaField(int preservedSize, int alignment)
|
||||
{
|
||||
if (_done)
|
||||
{
|
||||
throw new Exception("can't GetRvaField after done");
|
||||
}
|
||||
Assert.IsTrue(preservedSize % alignment == 0);
|
||||
// for big size, create a new field
|
||||
if (preservedSize >= maxRvaDataSize)
|
||||
{
|
||||
return CreateRvaField(preservedSize);
|
||||
}
|
||||
|
||||
if (_currentField != null)
|
||||
{
|
||||
int offset = AlignTo(_currentField.bytes.Count, alignment);
|
||||
|
||||
int expectedSize = offset + preservedSize;
|
||||
if (expectedSize <= _currentField.size)
|
||||
{
|
||||
_currentField.FillPaddingToSize(offset);
|
||||
return _currentField;
|
||||
}
|
||||
|
||||
_currentField.FillPaddingToEnd();
|
||||
}
|
||||
_currentField = CreateRvaField(maxRvaDataSize);
|
||||
return _currentField;
|
||||
}
|
||||
|
||||
public RvaData Allocate(int value)
|
||||
{
|
||||
RvaField field = GetRvaField(4, 4);
|
||||
int offset = field.bytes.Count;
|
||||
Assert.IsTrue(offset % 4 == 0);
|
||||
field.bytes.AddRange(BitConverter.GetBytes(value));
|
||||
return new RvaData(field.runtimeValueField, offset, 4);
|
||||
}
|
||||
|
||||
public RvaData Allocate(long value)
|
||||
{
|
||||
RvaField field = GetRvaField(8, 8);
|
||||
int offset = field.bytes.Count;
|
||||
Assert.IsTrue(offset % 8 == 0);
|
||||
field.bytes.AddRange(BitConverter.GetBytes(value));
|
||||
return new RvaData(field.runtimeValueField, offset, 8);
|
||||
}
|
||||
|
||||
public RvaData Allocate(float value)
|
||||
{
|
||||
RvaField field = GetRvaField(4, 4);
|
||||
int offset = field.bytes.Count;
|
||||
Assert.IsTrue(offset % 4 == 0);
|
||||
field.bytes.AddRange(BitConverter.GetBytes(value));
|
||||
return new RvaData(field.runtimeValueField, offset, 4);
|
||||
}
|
||||
|
||||
public RvaData Allocate(double value)
|
||||
{
|
||||
RvaField field = GetRvaField(8, 8);
|
||||
int offset = field.bytes.Count;
|
||||
Assert.IsTrue(offset % 8 == 0);
|
||||
field.bytes.AddRange(BitConverter.GetBytes(value));
|
||||
return new RvaData(field.runtimeValueField, offset, 8);
|
||||
}
|
||||
|
||||
public RvaData Allocate(string value)
|
||||
{
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(value);
|
||||
return Allocate(bytes);
|
||||
}
|
||||
|
||||
public RvaData Allocate(byte[] value)
|
||||
{
|
||||
RvaField field = GetRvaField(value.Length, 1);
|
||||
int offset = field.bytes.Count;
|
||||
field.bytes.AddRange(value);
|
||||
return new RvaData(field.runtimeValueField, offset, value.Length);
|
||||
}
|
||||
|
||||
|
||||
private void AddVerifyCodes(IList<Instruction> insts, DefaultMetadataImporter importer)
|
||||
{
|
||||
int verifyIntValue = 0x12345678;
|
||||
EncryptionScopeInfo encryptionScope = this.EncryptionScope;
|
||||
IRandom verifyRandom = encryptionScope.localRandomCreator(verifyIntValue);
|
||||
int verifyOps = EncryptionUtil.GenerateEncryptionOpCodes(verifyRandom, encryptionScope.encryptor, EncryptionScopeInfo.MaxEncryptionLevel, false);
|
||||
int verifySalt = verifyRandom.NextInt();
|
||||
int encryptedVerifyIntValue = encryptionScope.encryptor.Encrypt(verifyIntValue, verifyOps, verifySalt);
|
||||
|
||||
insts.Add(Instruction.Create(OpCodes.Ldc_I4, verifyIntValue));
|
||||
insts.Add(Instruction.CreateLdcI4(encryptedVerifyIntValue));
|
||||
insts.Add(Instruction.CreateLdcI4(verifyOps));
|
||||
insts.Add(Instruction.CreateLdcI4(verifySalt));
|
||||
insts.Add(Instruction.Create(OpCodes.Call, importer.DecryptInt));
|
||||
insts.Add(Instruction.Create(OpCodes.Call, importer.VerifySecretKey));
|
||||
|
||||
}
|
||||
|
||||
private void CreateCCtorOfRvaTypeDef()
|
||||
{
|
||||
foreach (RvaTypeDefInfo rvaTypeDef in _rvaTypeDefs)
|
||||
{
|
||||
ModuleDef mod = rvaTypeDef.typeDef.Module;
|
||||
var cctorMethod = new MethodDefUser(".cctor",
|
||||
MethodSig.CreateStatic(Module.CorLibTypes.Void),
|
||||
MethodImplAttributes.IL | MethodImplAttributes.Managed,
|
||||
MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Private);
|
||||
cctorMethod.DeclaringType = rvaTypeDef.typeDef;
|
||||
//_rvaTypeDef.Methods.Add(cctor);
|
||||
var body = new CilBody();
|
||||
cctorMethod.Body = body;
|
||||
var ins = body.Instructions;
|
||||
|
||||
DefaultMetadataImporter importer = this.GetDefaultModuleMetadataImporter();
|
||||
AddVerifyCodes(ins, importer);
|
||||
foreach (var field in rvaTypeDef.rvaFields)
|
||||
{
|
||||
// ldc
|
||||
// newarr
|
||||
// dup
|
||||
// stsfld
|
||||
// ldtoken
|
||||
// RuntimeHelpers.InitializeArray(array, fieldHandle);
|
||||
ins.Add(Instruction.Create(OpCodes.Ldc_I4, (int)field.size));
|
||||
ins.Add(Instruction.Create(OpCodes.Newarr, field.runtimeValueField.FieldType.Next.ToTypeDefOrRef()));
|
||||
ins.Add(Instruction.Create(OpCodes.Dup));
|
||||
ins.Add(Instruction.Create(OpCodes.Dup));
|
||||
ins.Add(Instruction.Create(OpCodes.Stsfld, field.runtimeValueField));
|
||||
ins.Add(Instruction.Create(OpCodes.Ldtoken, field.holderDataField));
|
||||
ins.Add(Instruction.Create(OpCodes.Call, importer.InitializedArray));
|
||||
|
||||
// EncryptionService.DecryptBlock(array, field.encryptionOps, field.salt);
|
||||
ins.Add(Instruction.CreateLdcI4(field.encryptionOps));
|
||||
ins.Add(Instruction.Create(OpCodes.Ldc_I4, field.salt));
|
||||
ins.Add(Instruction.Create(OpCodes.Call, importer.DecryptBlock));
|
||||
|
||||
}
|
||||
ins.Add(Instruction.Create(OpCodes.Ret));
|
||||
}
|
||||
}
|
||||
|
||||
private void SetFieldsRVA()
|
||||
{
|
||||
foreach (var field in _rvaTypeDefs.SelectMany(t => t.rvaFields))
|
||||
{
|
||||
Assert.IsTrue(field.bytes.Count <= field.size);
|
||||
if (field.bytes.Count < field.size)
|
||||
{
|
||||
field.FillPaddingToEnd();
|
||||
}
|
||||
byte[] data = field.bytes.ToArray();
|
||||
EncryptionScope.encryptor.EncryptBlock(data, field.encryptionOps, field.salt);
|
||||
field.holderDataField.InitialValue = data;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Done()
|
||||
{
|
||||
if (_done)
|
||||
{
|
||||
throw new Exception("can't call Done twice");
|
||||
}
|
||||
_done = true;
|
||||
SetFieldsRVA();
|
||||
CreateCCtorOfRvaTypeDef();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,423 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace Obfuz.Emit
|
||||
{
|
||||
public class EncryptionServiceMetadataImporter
|
||||
{
|
||||
private readonly ModuleDef _module;
|
||||
private readonly Type _encryptionServiceType;
|
||||
|
||||
private IMethod _encryptBlock;
|
||||
private IMethod _decryptBlock;
|
||||
private IMethod _encryptInt;
|
||||
private IMethod _decryptInt;
|
||||
private IMethod _encryptLong;
|
||||
private IMethod _decryptLong;
|
||||
private IMethod _encryptFloat;
|
||||
private IMethod _decryptFloat;
|
||||
private IMethod _encryptDouble;
|
||||
private IMethod _decryptDouble;
|
||||
private IMethod _encryptString;
|
||||
private IMethod _decryptString;
|
||||
private IMethod _encryptBytes;
|
||||
private IMethod _decryptBytes;
|
||||
|
||||
private IMethod _decryptFromRvaInt;
|
||||
private IMethod _decryptFromRvaLong;
|
||||
private IMethod _decryptFromRvaFloat;
|
||||
private IMethod _decryptFromRvaDouble;
|
||||
private IMethod _decryptFromRvaString;
|
||||
private IMethod _decryptFromRvaBytes;
|
||||
|
||||
private IMethod _decryptInitializeArray;
|
||||
|
||||
public IMethod EncryptBlock => _encryptBlock;
|
||||
public IMethod DecryptBlock => _decryptBlock;
|
||||
|
||||
public IMethod EncryptInt => _encryptInt;
|
||||
public IMethod DecryptInt => _decryptInt;
|
||||
public IMethod EncryptLong => _encryptLong;
|
||||
public IMethod DecryptLong => _decryptLong;
|
||||
public IMethod EncryptFloat => _encryptFloat;
|
||||
public IMethod DecryptFloat => _decryptFloat;
|
||||
public IMethod EncryptDouble => _encryptDouble;
|
||||
public IMethod DecryptDouble => _decryptDouble;
|
||||
public IMethod EncryptString => _encryptString;
|
||||
public IMethod DecryptString => _decryptString;
|
||||
public IMethod EncryptBytes => _encryptBytes;
|
||||
public IMethod DecryptBytes => _decryptBytes;
|
||||
|
||||
public IMethod DecryptFromRvaInt => _decryptFromRvaInt;
|
||||
public IMethod DecryptFromRvaLong => _decryptFromRvaLong;
|
||||
public IMethod DecryptFromRvaFloat => _decryptFromRvaFloat;
|
||||
public IMethod DecryptFromRvaDouble => _decryptFromRvaDouble;
|
||||
public IMethod DecryptFromRvaBytes => _decryptFromRvaBytes;
|
||||
public IMethod DecryptFromRvaString => _decryptFromRvaString;
|
||||
|
||||
public IMethod DecryptInitializeArray => _decryptInitializeArray;
|
||||
|
||||
public EncryptionServiceMetadataImporter(ModuleDef mod, Type encryptionServiceType)
|
||||
{
|
||||
_module = mod;
|
||||
_encryptionServiceType = encryptionServiceType;
|
||||
_encryptBlock = mod.Import(encryptionServiceType.GetMethod("EncryptBlock", new[] { typeof(byte[]), typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_encryptBlock);
|
||||
_decryptBlock = mod.Import(encryptionServiceType.GetMethod("DecryptBlock", new[] { typeof(byte[]), typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_decryptBlock);
|
||||
_encryptInt = mod.Import(encryptionServiceType.GetMethod("Encrypt", new[] { typeof(int), typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_encryptInt);
|
||||
_decryptInt = mod.Import(encryptionServiceType.GetMethod("Decrypt", new[] { typeof(int), typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_decryptInt);
|
||||
_encryptLong = mod.Import(encryptionServiceType.GetMethod("Encrypt", new[] { typeof(long), typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_encryptLong);
|
||||
_decryptLong = mod.Import(encryptionServiceType.GetMethod("Decrypt", new[] { typeof(long), typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_decryptLong);
|
||||
_encryptFloat = mod.Import(encryptionServiceType.GetMethod("Encrypt", new[] { typeof(float), typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_encryptFloat);
|
||||
_decryptFloat = mod.Import(encryptionServiceType.GetMethod("Decrypt", new[] { typeof(float), typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_decryptFloat);
|
||||
_encryptDouble = mod.Import(encryptionServiceType.GetMethod("Encrypt", new[] { typeof(double), typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_encryptDouble);
|
||||
_decryptDouble = mod.Import(encryptionServiceType.GetMethod("Decrypt", new[] { typeof(double), typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_decryptDouble);
|
||||
_encryptString = mod.Import(encryptionServiceType.GetMethod("Encrypt", new[] { typeof(string), typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_encryptString);
|
||||
_decryptString = mod.Import(encryptionServiceType.GetMethod("DecryptString", new[] { typeof(byte[]), typeof(int), typeof(int), typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_decryptString);
|
||||
_encryptBytes = mod.Import(encryptionServiceType.GetMethod("Encrypt", new[] { typeof(byte[]), typeof(int), typeof(int), typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_encryptBytes);
|
||||
_decryptBytes = mod.Import(encryptionServiceType.GetMethod("Decrypt", new[] { typeof(byte[]), typeof(int), typeof(int), typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_decryptBytes);
|
||||
|
||||
_decryptFromRvaInt = mod.Import(encryptionServiceType.GetMethod("DecryptFromRvaInt", new[] { typeof(byte[]), typeof(int), typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_decryptFromRvaInt);
|
||||
_decryptFromRvaLong = mod.Import(encryptionServiceType.GetMethod("DecryptFromRvaLong", new[] { typeof(byte[]), typeof(int), typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_decryptFromRvaLong);
|
||||
_decryptFromRvaFloat = mod.Import(encryptionServiceType.GetMethod("DecryptFromRvaFloat", new[] { typeof(byte[]), typeof(int), typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_decryptFromRvaFloat);
|
||||
_decryptFromRvaDouble = mod.Import(encryptionServiceType.GetMethod("DecryptFromRvaDouble", new[] { typeof(byte[]), typeof(int), typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_decryptFromRvaDouble);
|
||||
_decryptFromRvaBytes = mod.Import(encryptionServiceType.GetMethod("DecryptFromRvaBytes", new[] { typeof(byte[]), typeof(int), typeof(int), typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_decryptFromRvaBytes);
|
||||
_decryptFromRvaString = mod.Import(encryptionServiceType.GetMethod("DecryptFromRvaString", new[] { typeof(byte[]), typeof(int), typeof(int), typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_decryptFromRvaString);
|
||||
_decryptInitializeArray = mod.Import(encryptionServiceType.GetMethod("DecryptInitializeArray", new[] { typeof(System.Array), typeof(System.RuntimeFieldHandle), typeof(int), typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_decryptInitializeArray);
|
||||
}
|
||||
}
|
||||
|
||||
public class DefaultMetadataImporter : GroupByModuleEntityBase
|
||||
{
|
||||
private EncryptionServiceMetadataImporter _defaultEncryptionServiceMetadataImporter;
|
||||
|
||||
|
||||
private EncryptionServiceMetadataImporter _staticDefaultEncryptionServiceMetadataImporter;
|
||||
private EncryptionServiceMetadataImporter _dynamicDefaultEncryptionServiceMetadataImporter;
|
||||
|
||||
public DefaultMetadataImporter()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
ModuleDef mod = Module;
|
||||
|
||||
var constUtilityType = typeof(ConstUtility);
|
||||
|
||||
_castIntAsFloat = mod.Import(constUtilityType.GetMethod("CastIntAsFloat"));
|
||||
Assert.IsNotNull(_castIntAsFloat, "CastIntAsFloat not found");
|
||||
_castLongAsDouble = mod.Import(constUtilityType.GetMethod("CastLongAsDouble"));
|
||||
Assert.IsNotNull(_castLongAsDouble, "CastLongAsDouble not found");
|
||||
_castFloatAsInt = mod.Import(constUtilityType.GetMethod("CastFloatAsInt"));
|
||||
Assert.IsNotNull(_castFloatAsInt, "CastFloatAsInt not found");
|
||||
_castDoubleAsLong = mod.Import(constUtilityType.GetMethod("CastDoubleAsLong"));
|
||||
Assert.IsNotNull(_castDoubleAsLong, "CastDoubleAsLong not found");
|
||||
|
||||
_initializeArray = mod.Import(typeof(System.Runtime.CompilerServices.RuntimeHelpers).GetMethod("InitializeArray", new[] { typeof(Array), typeof(RuntimeFieldHandle) }));
|
||||
Assert.IsNotNull(_initializeArray);
|
||||
_verifySecretKey = mod.Import(typeof(AssertUtility).GetMethod("VerifySecretKey", new[] { typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_verifySecretKey, "VerifySecretKey not found");
|
||||
|
||||
_obfuscationTypeMapperRegisterType = mod.Import(typeof(ObfuscationTypeMapper).GetMethod("RegisterType", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(string) }, null));
|
||||
Assert.IsNotNull(_obfuscationTypeMapperRegisterType, "ObfuscationTypeMapper.RegisterType not found");
|
||||
|
||||
var exprUtilityType = typeof(ExprUtility);
|
||||
_addInt = mod.Import(exprUtilityType.GetMethod("Add", new[] { typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_addInt, "ExprUtility.Add(int, int) not found");
|
||||
_addLong = mod.Import(exprUtilityType.GetMethod("Add", new[] { typeof(long), typeof(long) }));
|
||||
Assert.IsNotNull(_addLong, "ExprUtility.Add(long, long) not found");
|
||||
_addFloat = mod.Import(exprUtilityType.GetMethod("Add", new[] { typeof(float), typeof(float) }));
|
||||
Assert.IsNotNull(_addFloat, "ExprUtility.Add(float, float) not found");
|
||||
_addDouble = mod.Import(exprUtilityType.GetMethod("Add", new[] { typeof(double), typeof(double) }));
|
||||
Assert.IsNotNull(_addDouble, "ExprUtility.Add(double, double) not found");
|
||||
_addIntPtr = mod.Import(exprUtilityType.GetMethod("Add", new[] { typeof(IntPtr), typeof(IntPtr) }));
|
||||
Assert.IsNotNull(_addIntPtr, "ExprUtility.Add(IntPtr, IntPtr) not found");
|
||||
_addIntPtrInt = mod.Import(exprUtilityType.GetMethod("Add", new[] { typeof(IntPtr), typeof(int) }));
|
||||
Assert.IsNotNull(_addIntPtrInt, "ExprUtility.Add(IntPtr, int) not found");
|
||||
|
||||
_subtractInt = mod.Import(exprUtilityType.GetMethod("Subtract", new[] { typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_subtractInt, "ExprUtility.Subtract(int, int) not found");
|
||||
_subtractLong = mod.Import(exprUtilityType.GetMethod("Subtract", new[] { typeof(long), typeof(long) }));
|
||||
Assert.IsNotNull(_subtractLong, "ExprUtility.Subtract(long, long) not found");
|
||||
_subtractFloat = mod.Import(exprUtilityType.GetMethod("Subtract", new[] { typeof(float), typeof(float) }));
|
||||
Assert.IsNotNull(_subtractFloat, "ExprUtility.Subtract(float, float) not found");
|
||||
_subtractDouble = mod.Import(exprUtilityType.GetMethod("Subtract", new[] { typeof(double), typeof(double) }));
|
||||
Assert.IsNotNull(_subtractDouble, "ExprUtility.Subtract(double, double) not found");
|
||||
_subtractIntPtr = mod.Import(exprUtilityType.GetMethod("Subtract", new[] { typeof(IntPtr), typeof(IntPtr) }));
|
||||
Assert.IsNotNull(_subtractIntPtr, "ExprUtility.Subtract(IntPtr, IntPtr) not found");
|
||||
_subtractIntPtrInt = mod.Import(exprUtilityType.GetMethod("Subtract", new[] { typeof(IntPtr), typeof(int) }));
|
||||
Assert.IsNotNull(_subtractIntPtrInt, "ExprUtility.Subtract(IntPtr, int) not found");
|
||||
|
||||
_multiplyInt = mod.Import(exprUtilityType.GetMethod("Multiply", new[] { typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_multiplyInt, "ExprUtility.Multiply(int, int) not found");
|
||||
_multiplyLong = mod.Import(exprUtilityType.GetMethod("Multiply", new[] { typeof(long), typeof(long) }));
|
||||
Assert.IsNotNull(_multiplyLong, "ExprUtility.Multiply(long, long) not found");
|
||||
_multiplyFloat = mod.Import(exprUtilityType.GetMethod("Multiply", new[] { typeof(float), typeof(float) }));
|
||||
Assert.IsNotNull(_multiplyFloat, "ExprUtility.Multiply(float, float) not found");
|
||||
_multiplyDouble = mod.Import(exprUtilityType.GetMethod("Multiply", new[] { typeof(double), typeof(double) }));
|
||||
Assert.IsNotNull(_multiplyDouble, "ExprUtility.Multiply(double, double) not found");
|
||||
_multiplyIntPtr = mod.Import(exprUtilityType.GetMethod("Multiply", new[] { typeof(IntPtr), typeof(IntPtr) }));
|
||||
Assert.IsNotNull(_multiplyIntPtr, "ExprUtility.Multiply(IntPtr, IntPtr) not found");
|
||||
_multiplyIntPtrInt = mod.Import(exprUtilityType.GetMethod("Multiply", new[] { typeof(IntPtr), typeof(int) }));
|
||||
Assert.IsNotNull(_multiplyIntPtrInt, "ExprUtility.Multiply(IntPtr, int) not found");
|
||||
|
||||
_divideInt = mod.Import(exprUtilityType.GetMethod("Divide", new[] { typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_divideInt, "ExprUtility.Divide(int, int) not found");
|
||||
_divideLong = mod.Import(exprUtilityType.GetMethod("Divide", new[] { typeof(long), typeof(long) }));
|
||||
Assert.IsNotNull(_divideLong);
|
||||
_divideFloat = mod.Import(exprUtilityType.GetMethod("Divide", new[] { typeof(float), typeof(float) }));
|
||||
Assert.IsNotNull(_divideFloat, "ExprUtility.Divide(float, float) not found");
|
||||
_divideDouble = mod.Import(exprUtilityType.GetMethod("Divide", new[] { typeof(double), typeof(double) }));
|
||||
Assert.IsNotNull(_divideDouble, "ExprUtility.Divide(double, double) not found");
|
||||
_divideUnInt = mod.Import(exprUtilityType.GetMethod("DivideUn", new[] { typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_divideUnInt, "ExprUtility.DivideUn(int, int) not found");
|
||||
_divideUnLong = mod.Import(exprUtilityType.GetMethod("DivideUn", new[] { typeof(long), typeof(long) }));
|
||||
Assert.IsNotNull(_divideUnLong, "ExprUtility.DivideUn(long, long) not found");
|
||||
_remInt = mod.Import(exprUtilityType.GetMethod("Rem", new[] { typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_remInt, "ExprUtility.Rem(int, int) not found");
|
||||
_remLong = mod.Import(exprUtilityType.GetMethod("Rem", new[] { typeof(long), typeof(long) }));
|
||||
Assert.IsNotNull(_remLong, "ExprUtility.Rem(long, long) not found");
|
||||
_remFloat = mod.Import(exprUtilityType.GetMethod("Rem", new[] { typeof(float), typeof(float) }));
|
||||
Assert.IsNotNull(_remFloat, "ExprUtility.Rem(float, float) not found");
|
||||
_remDouble = mod.Import(exprUtilityType.GetMethod("Rem", new[] { typeof(double), typeof(double) }));
|
||||
Assert.IsNotNull(_remDouble, "ExprUtility.Rem(double, double) not found");
|
||||
_remUnInt = mod.Import(exprUtilityType.GetMethod("RemUn", new[] { typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_remUnInt, "ExprUtility.RemUn(int, int) not found");
|
||||
_remUnLong = mod.Import(exprUtilityType.GetMethod("RemUn", new[] { typeof(long), typeof(long) }));
|
||||
Assert.IsNotNull(_remUnLong, "ExprUtility.RemUn(long, long) not found");
|
||||
_negInt = mod.Import(exprUtilityType.GetMethod("Negate", new[] { typeof(int) }));
|
||||
Assert.IsNotNull(_negInt, "ExprUtility.Negate(int) not found");
|
||||
_negLong = mod.Import(exprUtilityType.GetMethod("Negate", new[] { typeof(long) }));
|
||||
Assert.IsNotNull(_negLong, "ExprUtility.Negate(long) not found");
|
||||
_negFloat = mod.Import(exprUtilityType.GetMethod("Negate", new[] { typeof(float) }));
|
||||
Assert.IsNotNull(_negFloat, "ExprUtility.Negate(float) not found");
|
||||
_negDouble = mod.Import(exprUtilityType.GetMethod("Negate", new[] { typeof(double) }));
|
||||
Assert.IsNotNull(_negDouble, "ExprUtility.Negate(double) not found");
|
||||
|
||||
_andInt = mod.Import(exprUtilityType.GetMethod("And", new[] { typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_andInt, "ExprUtility.And(int, int) not found");
|
||||
_andLong = mod.Import(exprUtilityType.GetMethod("And", new[] { typeof(long), typeof(long) }));
|
||||
Assert.IsNotNull(_andLong, "ExprUtility.And(long, long) not found");
|
||||
_orInt = mod.Import(exprUtilityType.GetMethod("Or", new[] { typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_orInt, "ExprUtility.Or(int, int) not found");
|
||||
_orLong = mod.Import(exprUtilityType.GetMethod("Or", new[] { typeof(long), typeof(long) }));
|
||||
Assert.IsNotNull(_orLong, "ExprUtility.Or(long, long) not found");
|
||||
_xorInt = mod.Import(exprUtilityType.GetMethod("Xor", new[] { typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_xorInt, "ExprUtility.Xor(int, int) not found");
|
||||
_xorLong = mod.Import(exprUtilityType.GetMethod("Xor", new[] { typeof(long), typeof(long) }));
|
||||
Assert.IsNotNull(_xorLong, "ExprUtility.Xor(long, long) not found");
|
||||
_notInt = mod.Import(exprUtilityType.GetMethod("Not", new[] { typeof(int) }));
|
||||
Assert.IsNotNull(_notInt, "ExprUtility.Not(int) not found");
|
||||
_notLong = mod.Import(exprUtilityType.GetMethod("Not", new[] { typeof(long) }));
|
||||
Assert.IsNotNull(_notLong, "ExprUtility.Not(long) not found");
|
||||
|
||||
_shlInt = mod.Import(exprUtilityType.GetMethod("ShiftLeft", new[] { typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_shlInt, "ExprUtility.ShiftLeft(int, int) not found");
|
||||
_shlLong = mod.Import(exprUtilityType.GetMethod("ShiftLeft", new[] { typeof(long), typeof(int) }));
|
||||
Assert.IsNotNull(_shlLong, "ExprUtility.ShiftLeft(long, int) not found");
|
||||
_shrInt = mod.Import(exprUtilityType.GetMethod("ShiftRight", new[] { typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_shrInt, "ExprUtility.ShiftRight(int, int) not found");
|
||||
_shrLong = mod.Import(exprUtilityType.GetMethod("ShiftRight", new[] { typeof(long), typeof(int) }));
|
||||
Assert.IsNotNull(_shrLong, "ExprUtility.ShiftRight(long, int) not found");
|
||||
_shrUnInt = mod.Import(exprUtilityType.GetMethod("ShiftRightUn", new[] { typeof(int), typeof(int) }));
|
||||
Assert.IsNotNull(_shrUnInt, "ExprUtility.ShiftRightUn(int, int) not found");
|
||||
_shrUnLong = mod.Import(exprUtilityType.GetMethod("ShiftRightUn", new[] { typeof(long), typeof(int) }));
|
||||
Assert.IsNotNull(_shrUnLong, "ExprUtility.ShiftRightUn(long, int) not found");
|
||||
|
||||
|
||||
_staticDefaultEncryptionServiceMetadataImporter = new EncryptionServiceMetadataImporter(mod, typeof(EncryptionService<DefaultStaticEncryptionScope>));
|
||||
_dynamicDefaultEncryptionServiceMetadataImporter = new EncryptionServiceMetadataImporter(mod, typeof(EncryptionService<DefaultDynamicEncryptionScope>));
|
||||
if (EncryptionScopeProvider.IsDynamicSecretAssembly(mod))
|
||||
{
|
||||
_defaultEncryptionServiceMetadataImporter = _dynamicDefaultEncryptionServiceMetadataImporter;
|
||||
}
|
||||
else
|
||||
{
|
||||
_defaultEncryptionServiceMetadataImporter = _staticDefaultEncryptionServiceMetadataImporter;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Done()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public EncryptionServiceMetadataImporter GetEncryptionServiceMetadataImporterOfModule(ModuleDef mod)
|
||||
{
|
||||
return EncryptionScopeProvider.IsDynamicSecretAssembly(mod) ? _dynamicDefaultEncryptionServiceMetadataImporter : _staticDefaultEncryptionServiceMetadataImporter;
|
||||
}
|
||||
|
||||
private ModuleDef _module;
|
||||
private IMethod _castIntAsFloat;
|
||||
private IMethod _castLongAsDouble;
|
||||
private IMethod _castFloatAsInt;
|
||||
private IMethod _castDoubleAsLong;
|
||||
private IMethod _initializeArray;
|
||||
private IMethod _verifySecretKey;
|
||||
|
||||
private IMethod _obfuscationTypeMapperRegisterType;
|
||||
|
||||
private IMethod _addInt;
|
||||
private IMethod _addLong;
|
||||
private IMethod _addFloat;
|
||||
private IMethod _addDouble;
|
||||
private IMethod _addIntPtr;
|
||||
private IMethod _addIntPtrInt;
|
||||
private IMethod _subtractInt;
|
||||
private IMethod _subtractLong;
|
||||
private IMethod _subtractFloat;
|
||||
private IMethod _subtractDouble;
|
||||
private IMethod _subtractIntPtr;
|
||||
private IMethod _subtractIntPtrInt;
|
||||
private IMethod _multiplyInt;
|
||||
private IMethod _multiplyLong;
|
||||
private IMethod _multiplyFloat;
|
||||
private IMethod _multiplyDouble;
|
||||
private IMethod _multiplyIntPtr;
|
||||
private IMethod _multiplyIntPtrInt;
|
||||
private IMethod _divideInt;
|
||||
private IMethod _divideLong;
|
||||
private IMethod _divideFloat;
|
||||
private IMethod _divideDouble;
|
||||
private IMethod _divideUnInt;
|
||||
private IMethod _divideUnLong;
|
||||
private IMethod _remInt;
|
||||
private IMethod _remLong;
|
||||
private IMethod _remFloat;
|
||||
private IMethod _remDouble;
|
||||
private IMethod _remUnInt;
|
||||
private IMethod _remUnLong;
|
||||
private IMethod _negInt;
|
||||
private IMethod _negLong;
|
||||
private IMethod _negFloat;
|
||||
private IMethod _negDouble;
|
||||
|
||||
private IMethod _andInt;
|
||||
private IMethod _andLong;
|
||||
private IMethod _orInt;
|
||||
private IMethod _orLong;
|
||||
private IMethod _xorInt;
|
||||
private IMethod _xorLong;
|
||||
private IMethod _notInt;
|
||||
private IMethod _notLong;
|
||||
|
||||
private IMethod _shlInt;
|
||||
private IMethod _shlLong;
|
||||
private IMethod _shrInt;
|
||||
private IMethod _shrLong;
|
||||
private IMethod _shrUnInt;
|
||||
private IMethod _shrUnLong;
|
||||
|
||||
public IMethod CastIntAsFloat => _castIntAsFloat;
|
||||
public IMethod CastLongAsDouble => _castLongAsDouble;
|
||||
public IMethod CastFloatAsInt => _castFloatAsInt;
|
||||
public IMethod CastDoubleAsLong => _castDoubleAsLong;
|
||||
|
||||
public IMethod InitializedArray => _initializeArray;
|
||||
|
||||
public IMethod VerifySecretKey => _verifySecretKey;
|
||||
|
||||
public IMethod ObfuscationTypeMapperRegisterType => _obfuscationTypeMapperRegisterType;
|
||||
|
||||
public IMethod EncryptBlock => _defaultEncryptionServiceMetadataImporter.EncryptBlock;
|
||||
public IMethod DecryptBlock => _defaultEncryptionServiceMetadataImporter.DecryptBlock;
|
||||
|
||||
public IMethod EncryptInt => _defaultEncryptionServiceMetadataImporter.EncryptInt;
|
||||
public IMethod DecryptInt => _defaultEncryptionServiceMetadataImporter.DecryptInt;
|
||||
public IMethod EncryptLong => _defaultEncryptionServiceMetadataImporter.EncryptLong;
|
||||
public IMethod DecryptLong => _defaultEncryptionServiceMetadataImporter.DecryptLong;
|
||||
public IMethod EncryptFloat => _defaultEncryptionServiceMetadataImporter.EncryptFloat;
|
||||
public IMethod DecryptFloat => _defaultEncryptionServiceMetadataImporter.DecryptFloat;
|
||||
public IMethod EncryptDouble => _defaultEncryptionServiceMetadataImporter.EncryptDouble;
|
||||
public IMethod DecryptDouble => _defaultEncryptionServiceMetadataImporter.DecryptDouble;
|
||||
public IMethod EncryptString => _defaultEncryptionServiceMetadataImporter.EncryptString;
|
||||
public IMethod DecryptString => _defaultEncryptionServiceMetadataImporter.DecryptString;
|
||||
public IMethod EncryptBytes => _defaultEncryptionServiceMetadataImporter.EncryptBytes;
|
||||
public IMethod DecryptBytes => _defaultEncryptionServiceMetadataImporter.DecryptBytes;
|
||||
|
||||
public IMethod DecryptFromRvaInt => _defaultEncryptionServiceMetadataImporter.DecryptFromRvaInt;
|
||||
public IMethod DecryptFromRvaLong => _defaultEncryptionServiceMetadataImporter.DecryptFromRvaLong;
|
||||
public IMethod DecryptFromRvaFloat => _defaultEncryptionServiceMetadataImporter.DecryptFromRvaFloat;
|
||||
public IMethod DecryptFromRvaDouble => _defaultEncryptionServiceMetadataImporter.DecryptFromRvaDouble;
|
||||
public IMethod DecryptFromRvaBytes => _defaultEncryptionServiceMetadataImporter.DecryptFromRvaBytes;
|
||||
public IMethod DecryptFromRvaString => _defaultEncryptionServiceMetadataImporter.DecryptFromRvaString;
|
||||
|
||||
public IMethod DecryptInitializeArray => _defaultEncryptionServiceMetadataImporter.DecryptInitializeArray;
|
||||
|
||||
public IMethod AddInt => _addInt;
|
||||
public IMethod AddLong => _addLong;
|
||||
public IMethod AddFloat => _addFloat;
|
||||
public IMethod AddDouble => _addDouble;
|
||||
public IMethod AddIntPtr => _addIntPtr;
|
||||
public IMethod AddIntPtrInt => _addIntPtrInt;
|
||||
public IMethod SubtractInt => _subtractInt;
|
||||
public IMethod SubtractLong => _subtractLong;
|
||||
public IMethod SubtractFloat => _subtractFloat;
|
||||
public IMethod SubtractDouble => _subtractDouble;
|
||||
public IMethod SubtractIntPtr => _subtractIntPtr;
|
||||
public IMethod SubtractIntPtrInt => _subtractIntPtrInt;
|
||||
|
||||
public IMethod MultiplyInt => _multiplyInt;
|
||||
public IMethod MultiplyLong => _multiplyLong;
|
||||
public IMethod MultiplyFloat => _multiplyFloat;
|
||||
public IMethod MultiplyDouble => _multiplyDouble;
|
||||
public IMethod MultiplyIntPtr => _multiplyIntPtr;
|
||||
public IMethod MultiplyIntPtrInt => _multiplyIntPtrInt;
|
||||
|
||||
public IMethod DivideInt => _divideInt;
|
||||
public IMethod DivideLong => _divideLong;
|
||||
public IMethod DivideFloat => _divideFloat;
|
||||
public IMethod DivideDouble => _divideDouble;
|
||||
public IMethod DivideUnInt => _divideUnInt;
|
||||
public IMethod DivideUnLong => _divideUnLong;
|
||||
public IMethod RemInt => _remInt;
|
||||
public IMethod RemLong => _remLong;
|
||||
public IMethod RemFloat => _remFloat;
|
||||
public IMethod RemDouble => _remDouble;
|
||||
public IMethod RemUnInt => _remUnInt;
|
||||
public IMethod RemUnLong => _remUnLong;
|
||||
public IMethod NegInt => _negInt;
|
||||
public IMethod NegLong => _negLong;
|
||||
public IMethod NegFloat => _negFloat;
|
||||
public IMethod NegDouble => _negDouble;
|
||||
public IMethod AndInt => _andInt;
|
||||
public IMethod AndLong => _andLong;
|
||||
public IMethod OrInt => _orInt;
|
||||
public IMethod OrLong => _orLong;
|
||||
public IMethod XorInt => _xorInt;
|
||||
public IMethod XorLong => _xorLong;
|
||||
public IMethod NotInt => _notInt;
|
||||
public IMethod NotLong => _notLong;
|
||||
public IMethod ShlInt => _shlInt;
|
||||
public IMethod ShlLong => _shlLong;
|
||||
public IMethod ShrInt => _shrInt;
|
||||
public IMethod ShrLong => _shrLong;
|
||||
public IMethod ShrUnInt => _shrUnInt;
|
||||
public IMethod ShrUnLong => _shrUnLong;
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
namespace Obfuz.Emit
|
||||
{
|
||||
public static class EntityExtensions
|
||||
{
|
||||
public static T GetEntity<T>(this IGroupByModuleEntity entity) where T : IGroupByModuleEntity, new()
|
||||
{
|
||||
return entity.Manager.GetEntity<T>(entity.Module);
|
||||
}
|
||||
|
||||
public static DefaultMetadataImporter GetDefaultModuleMetadataImporter(this IGroupByModuleEntity entity)
|
||||
{
|
||||
return entity.GetEntity<DefaultMetadataImporter>();
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,89 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.Emit
|
||||
{
|
||||
public interface IGroupByModuleEntity
|
||||
{
|
||||
GroupByModuleEntityManager Manager { get; set; }
|
||||
|
||||
ModuleDef Module { get; set; }
|
||||
|
||||
EncryptionScopeProvider EncryptionScopeProvider { get; }
|
||||
|
||||
EncryptionScopeInfo EncryptionScope { get; set; }
|
||||
|
||||
void Init();
|
||||
|
||||
void Done();
|
||||
}
|
||||
|
||||
public abstract class GroupByModuleEntityBase : IGroupByModuleEntity
|
||||
{
|
||||
public GroupByModuleEntityManager Manager { get; set; }
|
||||
|
||||
public ModuleDef Module { get; set; }
|
||||
|
||||
public EncryptionScopeInfo EncryptionScope { get; set; }
|
||||
|
||||
public EncryptionScopeProvider EncryptionScopeProvider => Manager.EncryptionScopeProvider;
|
||||
|
||||
public T GetEntity<T>() where T : IGroupByModuleEntity, new()
|
||||
{
|
||||
return Manager.GetEntity<T>(Module);
|
||||
}
|
||||
|
||||
public abstract void Init();
|
||||
|
||||
public abstract void Done();
|
||||
}
|
||||
|
||||
public class GroupByModuleEntityManager
|
||||
{
|
||||
private readonly Dictionary<(ModuleDef, Type), IGroupByModuleEntity> _moduleEntityManagers = new Dictionary<(ModuleDef, Type), IGroupByModuleEntity>();
|
||||
|
||||
public EncryptionScopeProvider EncryptionScopeProvider { get; set; }
|
||||
|
||||
public T GetEntity<T>(ModuleDef mod) where T : IGroupByModuleEntity, new()
|
||||
{
|
||||
var key = (mod, typeof(T));
|
||||
if (_moduleEntityManagers.TryGetValue(key, out var emitManager))
|
||||
{
|
||||
return (T)emitManager;
|
||||
}
|
||||
else
|
||||
{
|
||||
T newEmitManager = new T();
|
||||
newEmitManager.Manager = this;
|
||||
newEmitManager.Module = mod;
|
||||
newEmitManager.EncryptionScope = EncryptionScopeProvider.GetScope(mod);
|
||||
newEmitManager.Init();
|
||||
_moduleEntityManagers[key] = newEmitManager;
|
||||
return newEmitManager;
|
||||
}
|
||||
}
|
||||
|
||||
public List<T> GetEntities<T>() where T : IGroupByModuleEntity, new()
|
||||
{
|
||||
var managers = new List<T>();
|
||||
foreach (var kv in _moduleEntityManagers)
|
||||
{
|
||||
if (kv.Key.Item2 == typeof(T))
|
||||
{
|
||||
managers.Add((T)kv.Value);
|
||||
}
|
||||
}
|
||||
return managers;
|
||||
}
|
||||
|
||||
public void Done<T>() where T : IGroupByModuleEntity, new()
|
||||
{
|
||||
var managers = GetEntities<T>();
|
||||
foreach (var manager in managers)
|
||||
{
|
||||
manager.Done();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.Emit
|
||||
{
|
||||
class ScopeLocalVariables : IDisposable
|
||||
{
|
||||
private readonly LocalVariableAllocator _localVariableAllocator;
|
||||
|
||||
private readonly List<Local> _allocatedVars = new List<Local>();
|
||||
|
||||
public IReadOnlyList<Local> AllocatedLocals => _allocatedVars;
|
||||
|
||||
|
||||
public ScopeLocalVariables(LocalVariableAllocator localVariableAllocator)
|
||||
{
|
||||
_localVariableAllocator = localVariableAllocator;
|
||||
}
|
||||
|
||||
public Local AllocateLocal(TypeSig type)
|
||||
{
|
||||
var local = _localVariableAllocator.AllocateLocal(type);
|
||||
_allocatedVars.Add(local);
|
||||
return local;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var local in _allocatedVars)
|
||||
{
|
||||
_localVariableAllocator.ReturnLocal(local);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LocalVariableAllocator
|
||||
{
|
||||
private readonly MethodDef _method;
|
||||
private readonly List<Local> _freeLocals = new List<Local>();
|
||||
|
||||
public LocalVariableAllocator(MethodDef method)
|
||||
{
|
||||
_method = method;
|
||||
}
|
||||
|
||||
public Local AllocateLocal(TypeSig type)
|
||||
{
|
||||
foreach (var local in _freeLocals)
|
||||
{
|
||||
if (TypeEqualityComparer.Instance.Equals(local.Type, type))
|
||||
{
|
||||
_freeLocals.Remove(local);
|
||||
return local;
|
||||
}
|
||||
}
|
||||
var newLocal = new Local(type);
|
||||
// _freeLocals.Add(newLocal);
|
||||
_method.Body.Variables.Add(newLocal);
|
||||
return newLocal;
|
||||
}
|
||||
|
||||
public void ReturnLocal(Local local)
|
||||
{
|
||||
_freeLocals.Add(local);
|
||||
}
|
||||
|
||||
public ScopeLocalVariables CreateScope()
|
||||
{
|
||||
return new ScopeLocalVariables(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Obfuz.GarbageCodeGeneration
|
||||
{
|
||||
|
||||
public class ConfigGarbageCodeGenerator : SpecificGarbageCodeGeneratorBase
|
||||
{
|
||||
|
||||
private readonly string[] _types = new string[]
|
||||
{
|
||||
"bool",
|
||||
"byte",
|
||||
"short",
|
||||
"int",
|
||||
"long",
|
||||
"float",
|
||||
"double",
|
||||
};
|
||||
|
||||
private string CreateRandomType(IRandom random)
|
||||
{
|
||||
return _types[random.NextInt(_types.Length)];
|
||||
}
|
||||
|
||||
private string GetReadMethodNameOfType(string type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case "bool": return "ReadBoolean";
|
||||
case "byte": return "ReadByte";
|
||||
case "short": return "ReadInt16";
|
||||
case "int": return "ReadInt32";
|
||||
case "long": return "ReadInt64";
|
||||
case "float": return "ReadSingle";
|
||||
case "double": return "ReadDouble";
|
||||
default: throw new ArgumentException($"Unsupported type: {type}");
|
||||
}
|
||||
}
|
||||
class FieldGenerationInfo
|
||||
{
|
||||
public int index;
|
||||
public string name;
|
||||
public string type;
|
||||
}
|
||||
|
||||
class MethodGenerationInfo
|
||||
{
|
||||
public int index;
|
||||
public string name;
|
||||
}
|
||||
|
||||
protected override object CreateField(int index, IRandom random, GenerationParameters parameters)
|
||||
{
|
||||
return new FieldGenerationInfo
|
||||
{
|
||||
index = index,
|
||||
name = $"x{index}",
|
||||
type = CreateRandomType(random),
|
||||
};
|
||||
}
|
||||
|
||||
protected override object CreateMethod(int index, IRandom random, GenerationParameters parameters)
|
||||
{
|
||||
return new MethodGenerationInfo
|
||||
{
|
||||
index = index,
|
||||
name = $"Load{index}",
|
||||
};
|
||||
}
|
||||
|
||||
protected override void GenerateUsings(StringBuilder result, IClassGenerationInfo cgi)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void GenerateField(StringBuilder result, IClassGenerationInfo cgi, IRandom random, object field, string indent)
|
||||
{
|
||||
var fgi = (FieldGenerationInfo)field;
|
||||
result.AppendLine($"{indent}public {fgi.type} {fgi.name};");
|
||||
}
|
||||
|
||||
protected override void GenerateMethod(StringBuilder result, IClassGenerationInfo cgi, IRandom random, object method, string indent)
|
||||
{
|
||||
var mgi = (MethodGenerationInfo)method;
|
||||
result.AppendLine($"{indent}public void {mgi.name}(BinaryReader reader)");
|
||||
result.AppendLine($"{indent}{{");
|
||||
|
||||
string indent2 = indent + " ";
|
||||
result.AppendLine($"{indent2}int a = 0;");
|
||||
result.AppendLine($"{indent2}int b = 0;");
|
||||
int maxN = 100;
|
||||
var shuffledFields = cgi.Fields.ToList();
|
||||
RandomUtil.ShuffleList(shuffledFields, random);
|
||||
foreach (FieldGenerationInfo fgi in shuffledFields)
|
||||
{
|
||||
result.AppendLine($"{indent2}this.{fgi.name} = reader.{GetReadMethodNameOfType(fgi.type)}();");
|
||||
if (random.NextInPercentage(0.5f))
|
||||
{
|
||||
result.AppendLine($"{indent2}a = b * {random.NextInt(maxN)} + reader.ReadInt32();");
|
||||
result.AppendLine($"{indent2}b = a * reader.ReadInt32() + {random.NextInt(maxN)};");
|
||||
}
|
||||
if (random.NextInPercentage(0.5f))
|
||||
{
|
||||
result.AppendLine($"{indent2}a += {random.NextInt(0, 10000)};");
|
||||
}
|
||||
if (random.NextInPercentage(0.5f))
|
||||
{
|
||||
result.AppendLine($"{indent2}b += {random.NextInt(0, 10000)};");
|
||||
}
|
||||
}
|
||||
|
||||
result.AppendLine($"{indent}}}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
using Obfuz.Settings;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obfuz.GarbageCodeGeneration
|
||||
{
|
||||
|
||||
public class GarbageCodeGenerator
|
||||
{
|
||||
private const int CodeGenerationSecretKeyLength = 1024;
|
||||
|
||||
private readonly GarbageCodeGenerationSettings _settings;
|
||||
private readonly int[] _intGenerationSecretKey;
|
||||
|
||||
public GarbageCodeGenerator(GarbageCodeGenerationSettings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
|
||||
byte[] byteGenerationSecretKey = KeyGenerator.GenerateKey(settings.codeGenerationSecret, CodeGenerationSecretKeyLength);
|
||||
_intGenerationSecretKey = KeyGenerator.ConvertToIntKey(byteGenerationSecretKey);
|
||||
}
|
||||
|
||||
public void Generate()
|
||||
{
|
||||
GenerateTask(_settings.defaultTask);
|
||||
if (_settings.additionalTasks != null && _settings.additionalTasks.Length > 0)
|
||||
{
|
||||
foreach (var task in _settings.additionalTasks)
|
||||
{
|
||||
GenerateTask(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CleanCodes()
|
||||
{
|
||||
Debug.Log($"Cleaning generated garbage codes begin.");
|
||||
if (_settings.defaultTask != null)
|
||||
{
|
||||
FileUtil.RemoveDir(_settings.defaultTask.outputPath, true);
|
||||
}
|
||||
if (_settings.additionalTasks != null && _settings.additionalTasks.Length > 0)
|
||||
{
|
||||
foreach (var task in _settings.additionalTasks)
|
||||
{
|
||||
FileUtil.RemoveDir(task.outputPath, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateTask(GarbageCodeGenerationTask task)
|
||||
{
|
||||
Debug.Log($"Generating garbage code with seed: {task.codeGenerationRandomSeed}, class count: {task.classCount}, method count per class: {task.methodCountPerClass}, types: {task.garbageCodeType}, output path: {task.outputPath}");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(task.outputPath))
|
||||
{
|
||||
throw new Exception("outputPath of GarbageCodeGenerationTask is empty!");
|
||||
}
|
||||
|
||||
var generator = CreateSpecificCodeGenerator(task.garbageCodeType);
|
||||
|
||||
var parameters = new GenerationParameters
|
||||
{
|
||||
random = new RandomWithKey(_intGenerationSecretKey, task.codeGenerationRandomSeed),
|
||||
classNamespace = task.classNamespace,
|
||||
classNamePrefix = task.classNamePrefix,
|
||||
classCount = task.classCount,
|
||||
methodCountPerClass = task.methodCountPerClass,
|
||||
fieldCountPerClass = task.fieldCountPerClass,
|
||||
outputPath = task.outputPath,
|
||||
};
|
||||
generator.Generate(parameters);
|
||||
|
||||
Debug.Log($"Generate garbage code end.");
|
||||
}
|
||||
|
||||
private ISpecificGarbageCodeGenerator CreateSpecificCodeGenerator(GarbageCodeType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GarbageCodeType.Config: return new ConfigGarbageCodeGenerator();
|
||||
case GarbageCodeType.UI: return new UIGarbageCodeGenerator();
|
||||
default: throw new NotSupportedException($"Garbage code type {type} is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ff64fd1e6f7b8874db5a5228fab159f9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,21 +0,0 @@
|
|||
using Obfuz.Utils;
|
||||
|
||||
namespace Obfuz.GarbageCodeGeneration
|
||||
{
|
||||
public class GenerationParameters
|
||||
{
|
||||
public IRandom random;
|
||||
|
||||
public string classNamespace;
|
||||
public string classNamePrefix;
|
||||
public int classCount;
|
||||
public int methodCountPerClass;
|
||||
public int fieldCountPerClass;
|
||||
public string outputPath;
|
||||
}
|
||||
|
||||
public interface ISpecificGarbageCodeGenerator
|
||||
{
|
||||
void Generate(GenerationParameters parameters);
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 74a17802b5aab2e40a3c89e0ddbcec0d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,106 +0,0 @@
|
|||
using Obfuz.Utils;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obfuz.GarbageCodeGeneration
|
||||
{
|
||||
public abstract class SpecificGarbageCodeGeneratorBase : ISpecificGarbageCodeGenerator
|
||||
{
|
||||
protected interface IClassGenerationInfo
|
||||
{
|
||||
string Namespace { get; set; }
|
||||
|
||||
string Name { get; set; }
|
||||
|
||||
IList<object> Fields { get; set; }
|
||||
|
||||
IList<object> Methods { get; set; }
|
||||
}
|
||||
|
||||
protected class ClassGenerationInfo : IClassGenerationInfo
|
||||
{
|
||||
public string Namespace { get; set; }
|
||||
public string Name { get; set; }
|
||||
public IList<object> Fields { get; set; } = new List<object>();
|
||||
public IList<object> Methods { get; set; } = new List<object>();
|
||||
}
|
||||
|
||||
public virtual void Generate(GenerationParameters parameters)
|
||||
{
|
||||
FileUtil.RecreateDir(parameters.outputPath);
|
||||
|
||||
for (int i = 0; i < parameters.classCount; i++)
|
||||
{
|
||||
Debug.Log($"[{GetType().Name}] Generating class {i}");
|
||||
var localRandom = new RandomWithKey(((RandomWithKey)parameters.random).Key, parameters.random.NextInt());
|
||||
string outputFile = $"{parameters.outputPath}/__GeneratedGarbageClass_{i}.cs";
|
||||
var result = new StringBuilder(64 * 1024);
|
||||
GenerateClass(i, localRandom, result, parameters);
|
||||
File.WriteAllText(outputFile, result.ToString(), Encoding.UTF8);
|
||||
Debug.Log($"[{GetType().Name}] Generated class {i} to {outputFile}");
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract object CreateField(int index, IRandom random, GenerationParameters parameters);
|
||||
|
||||
protected abstract object CreateMethod(int index, IRandom random, GenerationParameters parameters);
|
||||
|
||||
protected virtual IClassGenerationInfo CreateClassGenerationInfo(string classNamespace, string className, IRandom random, GenerationParameters parameters)
|
||||
{
|
||||
var cgi = new ClassGenerationInfo
|
||||
{
|
||||
Namespace = classNamespace,
|
||||
Name = className,
|
||||
};
|
||||
|
||||
for (int i = 0; i < parameters.fieldCountPerClass; i++)
|
||||
{
|
||||
cgi.Fields.Add(CreateField(i, random, parameters));
|
||||
}
|
||||
|
||||
for (int i = 0; i < parameters.methodCountPerClass; i++)
|
||||
{
|
||||
cgi.Methods.Add(CreateMethod(i, random, parameters));
|
||||
}
|
||||
|
||||
return cgi;
|
||||
}
|
||||
|
||||
protected virtual void GenerateClass(int classIndex, IRandom random, StringBuilder result, GenerationParameters parameters)
|
||||
{
|
||||
IClassGenerationInfo cgi = CreateClassGenerationInfo(parameters.classNamespace, $"{parameters.classNamePrefix}{classIndex}", random, parameters);
|
||||
result.AppendLine("using System;");
|
||||
result.AppendLine("using System.Collections.Generic;");
|
||||
result.AppendLine("using System.Linq;");
|
||||
result.AppendLine("using System.IO;");
|
||||
result.AppendLine("using UnityEngine;");
|
||||
|
||||
GenerateUsings(result, cgi);
|
||||
|
||||
result.AppendLine($"namespace {cgi.Namespace}");
|
||||
result.AppendLine("{");
|
||||
result.AppendLine($" public class {cgi.Name}");
|
||||
result.AppendLine(" {");
|
||||
|
||||
string indent = " ";
|
||||
foreach (object field in cgi.Fields)
|
||||
{
|
||||
GenerateField(result, cgi, random, field, indent);
|
||||
}
|
||||
foreach (object method in cgi.Methods)
|
||||
{
|
||||
GenerateMethod(result, cgi, random, method, indent);
|
||||
}
|
||||
result.AppendLine(" }");
|
||||
result.AppendLine("}");
|
||||
}
|
||||
|
||||
protected abstract void GenerateUsings(StringBuilder result, IClassGenerationInfo cgi);
|
||||
|
||||
protected abstract void GenerateField(StringBuilder result, IClassGenerationInfo cgi, IRandom random, object field, string indent);
|
||||
|
||||
protected abstract void GenerateMethod(StringBuilder result, IClassGenerationInfo cgi, IRandom random, object method, string indent);
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bae18fd49482f00439d37f28a6a78d9b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,157 +0,0 @@
|
|||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Obfuz.GarbageCodeGeneration
|
||||
{
|
||||
|
||||
public class UIGarbageCodeGenerator : SpecificGarbageCodeGeneratorBase
|
||||
{
|
||||
/*
|
||||
*
|
||||
* public Button b1;
|
||||
public Image b2;
|
||||
public RawImage b30;
|
||||
public Text b3;
|
||||
public Slider b4;
|
||||
public ScrollRect b5;
|
||||
public Scrollbar b6;
|
||||
public Mask b7;
|
||||
public RectMask2D b70;
|
||||
public Canvas b8;
|
||||
public CanvasGroup b9;
|
||||
public RectTransform b10;
|
||||
public Transform b11;
|
||||
public GameObject b12;
|
||||
*/
|
||||
|
||||
private readonly string[] _types = new string[]
|
||||
{
|
||||
"Button",
|
||||
"Image",
|
||||
"RawImage",
|
||||
"Text",
|
||||
"Slider",
|
||||
"ScrollRect",
|
||||
"Scrollbar",
|
||||
"Mask",
|
||||
"RectMask2D",
|
||||
"Canvas",
|
||||
"CanvasGroup",
|
||||
"RectTransform",
|
||||
//"Transform",
|
||||
//"GameObject",
|
||||
};
|
||||
|
||||
private string CreateRandomType(IRandom random)
|
||||
{
|
||||
return _types[random.NextInt(_types.Length)];
|
||||
}
|
||||
|
||||
private string GetReadMethodNameOfType(string type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case "bool": return "ReadBoolean";
|
||||
case "byte": return "ReadByte";
|
||||
case "short": return "ReadInt16";
|
||||
case "int": return "ReadInt32";
|
||||
case "long": return "ReadInt64";
|
||||
case "float": return "ReadSingle";
|
||||
case "double": return "ReadDouble";
|
||||
default: throw new ArgumentException($"Unsupported type: {type}");
|
||||
}
|
||||
}
|
||||
class FieldGenerationInfo
|
||||
{
|
||||
public int index;
|
||||
public string name;
|
||||
public string type;
|
||||
}
|
||||
|
||||
class MethodGenerationInfo
|
||||
{
|
||||
public int index;
|
||||
public string name;
|
||||
}
|
||||
|
||||
protected override object CreateField(int index, IRandom random, GenerationParameters parameters)
|
||||
{
|
||||
return new FieldGenerationInfo
|
||||
{
|
||||
index = index,
|
||||
name = $"x{index}",
|
||||
type = CreateRandomType(random),
|
||||
};
|
||||
}
|
||||
|
||||
protected override object CreateMethod(int index, IRandom random, GenerationParameters parameters)
|
||||
{
|
||||
return new MethodGenerationInfo
|
||||
{
|
||||
index = index,
|
||||
name = $"Init{index}",
|
||||
};
|
||||
}
|
||||
|
||||
protected override void GenerateUsings(StringBuilder result, IClassGenerationInfo cgi)
|
||||
{
|
||||
result.AppendLine("using UnityEngine.UI;");
|
||||
}
|
||||
|
||||
protected override void GenerateField(StringBuilder result, IClassGenerationInfo cgi, IRandom random, object field, string indent)
|
||||
{
|
||||
var fgi = (FieldGenerationInfo)field;
|
||||
result.AppendLine($"{indent}public {fgi.type} {fgi.name};");
|
||||
}
|
||||
|
||||
protected override void GenerateMethod(StringBuilder result, IClassGenerationInfo cgi, IRandom random, object method, string indent)
|
||||
{
|
||||
var mgi = (MethodGenerationInfo)method;
|
||||
result.AppendLine($"{indent}public void {mgi.name}(GameObject go)");
|
||||
result.AppendLine($"{indent}{{");
|
||||
|
||||
string indent2 = indent + " ";
|
||||
result.AppendLine($"{indent2}int a = 0;");
|
||||
result.AppendLine($"{indent2}int b = 0;");
|
||||
int maxN = 100;
|
||||
var shuffledFields = cgi.Fields.ToList();
|
||||
RandomUtil.ShuffleList(shuffledFields, random);
|
||||
foreach (FieldGenerationInfo fgi in shuffledFields)
|
||||
{
|
||||
if (random.NextInPercentage(0.5f))
|
||||
{
|
||||
result.AppendLine($"{indent2}this.{fgi.name} = go.transform.Find(\"ui/{fgi.name}\").GetComponent<{fgi.type}>();");
|
||||
}
|
||||
else
|
||||
{
|
||||
result.AppendLine($"{indent2}this.{fgi.name} = go.GetComponent<{fgi.type}>();");
|
||||
}
|
||||
if (random.NextInPercentage(0.5f))
|
||||
{
|
||||
result.AppendLine($"{indent2}a = b * {random.NextInt(maxN)} + go.layer;");
|
||||
result.AppendLine($"{indent2}b = a * go.layer + {random.NextInt(maxN)};");
|
||||
}
|
||||
if (random.NextInPercentage(0.5f))
|
||||
{
|
||||
result.AppendLine($"{indent2}a *= {random.NextInt(0, 10000)};");
|
||||
}
|
||||
if (random.NextInPercentage(0.5f))
|
||||
{
|
||||
result.AppendLine($"{indent2}b /= {random.NextInt(0, 10000)};");
|
||||
}
|
||||
if (random.NextInPercentage(0.5f))
|
||||
{
|
||||
result.AppendLine($"{indent2}a = a * b << {random.NextInt(0, 10000)};");
|
||||
}
|
||||
if (random.NextInPercentage(0.5f))
|
||||
{
|
||||
result.AppendLine($"{indent2}b = a / b & {random.NextInt(0, 10000)};");
|
||||
}
|
||||
}
|
||||
|
||||
result.AppendLine($"{indent}}}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5071c4b9c7f5aef409f3e7fdb45ecd8d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,166 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Emit;
|
||||
using Obfuz.Settings;
|
||||
using Obfuz.Utils;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.ObfusPasses.CallObfus
|
||||
{
|
||||
class ObfusMethodContext
|
||||
{
|
||||
public MethodDef method;
|
||||
public LocalVariableAllocator localVariableAllocator;
|
||||
public IRandom localRandom;
|
||||
public EncryptionScopeInfo encryptionScope;
|
||||
}
|
||||
|
||||
public class CallObfusPass : ObfuscationMethodPassBase
|
||||
{
|
||||
public static CallObfuscationSettingsFacade CurrentSettings { get; private set; }
|
||||
|
||||
private readonly CallObfuscationSettingsFacade _settings;
|
||||
private SpecialWhiteListMethodCalculator _specialWhiteListMethodCache;
|
||||
|
||||
private IObfuscator _dynamicProxyObfuscator;
|
||||
private IObfuscationPolicy _dynamicProxyPolicy;
|
||||
|
||||
public override ObfuscationPassType Type => ObfuscationPassType.CallObfus;
|
||||
|
||||
public CallObfusPass(CallObfuscationSettingsFacade settings)
|
||||
{
|
||||
_settings = settings;
|
||||
CurrentSettings = settings;
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
_dynamicProxyObfuscator.Done();
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
var ctx = ObfuscationPassContext.Current;
|
||||
|
||||
_specialWhiteListMethodCache = new SpecialWhiteListMethodCalculator(ctx.coreSettings.targetRuntime, _settings.obfuscateCallToMethodInMscorlib);
|
||||
_dynamicProxyObfuscator = CreateObfuscator(ctx, _settings.proxyMode);
|
||||
_dynamicProxyPolicy = new ConfigurableObfuscationPolicy(ctx.coreSettings.assembliesToObfuscate, _settings.ruleFiles);
|
||||
}
|
||||
|
||||
private IObfuscator CreateObfuscator(ObfuscationPassContext ctx, ProxyMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case ProxyMode.Dispatch:
|
||||
return new DispatchProxyObfuscator(ctx.moduleEntityManager);
|
||||
case ProxyMode.Delegate:
|
||||
return new DelegateProxyObfuscator(ctx.moduleEntityManager);
|
||||
default:
|
||||
throw new System.NotSupportedException($"Unsupported proxy mode: {mode}");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ObfuscateData(MethodDef method)
|
||||
{
|
||||
BasicBlockCollection bbc = new BasicBlockCollection(method, false);
|
||||
|
||||
IList<Instruction> instructions = method.Body.Instructions;
|
||||
|
||||
var outputInstructions = new List<Instruction>();
|
||||
var totalFinalInstructions = new List<Instruction>();
|
||||
|
||||
ObfuscationPassContext ctx = ObfuscationPassContext.Current;
|
||||
var encryptionScope = ctx.moduleEntityManager.EncryptionScopeProvider.GetScope(method.Module);
|
||||
var localRandom = encryptionScope.localRandomCreator(MethodEqualityComparer.CompareDeclaringTypes.GetHashCode(method));
|
||||
var omc = new ObfusMethodContext
|
||||
{
|
||||
method = method,
|
||||
localVariableAllocator = new LocalVariableAllocator(method),
|
||||
localRandom = localRandom,
|
||||
encryptionScope = encryptionScope,
|
||||
};
|
||||
Instruction lastInst = null;
|
||||
for (int i = 0; i < instructions.Count; i++)
|
||||
{
|
||||
Instruction inst = instructions[i];
|
||||
BasicBlock block = bbc.GetBasicBlockByInstruction(inst);
|
||||
outputInstructions.Clear();
|
||||
if (TryObfuscateInstruction(method, lastInst, inst, outputInstructions, omc))
|
||||
{
|
||||
// current instruction may be the target of control flow instruction, so we can't remove it directly.
|
||||
// we replace it with nop now, then remove it in CleanUpInstructionPass
|
||||
inst.OpCode = outputInstructions[0].OpCode;
|
||||
inst.Operand = outputInstructions[0].Operand;
|
||||
totalFinalInstructions.Add(inst);
|
||||
for (int k = 1; k < outputInstructions.Count; k++)
|
||||
{
|
||||
totalFinalInstructions.Add(outputInstructions[k]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
totalFinalInstructions.Add(inst);
|
||||
}
|
||||
lastInst = inst;
|
||||
}
|
||||
|
||||
instructions.Clear();
|
||||
foreach (var obInst in totalFinalInstructions)
|
||||
{
|
||||
instructions.Add(obInst);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool NeedObfuscateMethod(MethodDef method)
|
||||
{
|
||||
return _dynamicProxyPolicy.NeedObfuscateCallInMethod(method);
|
||||
}
|
||||
|
||||
private bool TryObfuscateInstruction(MethodDef callerMethod, Instruction lastInst, Instruction inst, List<Instruction> outputInstructions, ObfusMethodContext ctx)
|
||||
{
|
||||
IMethod calledMethod = inst.Operand as IMethod;
|
||||
if (calledMethod == null || !calledMethod.IsMethod)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (MetaUtil.ContainsContainsGenericParameter(calledMethod))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool callVir;
|
||||
switch (inst.OpCode.Code)
|
||||
{
|
||||
case Code.Call:
|
||||
{
|
||||
callVir = false;
|
||||
break;
|
||||
}
|
||||
case Code.Callvirt:
|
||||
{
|
||||
if (lastInst != null && lastInst.OpCode.Code == Code.Constrained)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
callVir = true;
|
||||
break;
|
||||
}
|
||||
default: return false;
|
||||
}
|
||||
|
||||
|
||||
if (_specialWhiteListMethodCache.IsInWhiteList(calledMethod))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!_dynamicProxyPolicy.NeedObfuscateCalledMethod(callerMethod, calledMethod, callVir))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _dynamicProxyObfuscator.Obfuscate(callerMethod, calledMethod, callVir, outputInstructions);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,265 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Data;
|
||||
using Obfuz.Emit;
|
||||
using Obfuz.Settings;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Obfuz.ObfusPasses.CallObfus
|
||||
{
|
||||
|
||||
struct DelegateProxyMethodData
|
||||
{
|
||||
public readonly FieldDef delegateInstanceField;
|
||||
public readonly MethodDef delegateInvokeMethod;
|
||||
|
||||
public DelegateProxyMethodData(FieldDef delegateInstanceField, MethodDef delegateInvokeMethod)
|
||||
{
|
||||
this.delegateInstanceField = delegateInstanceField;
|
||||
this.delegateInvokeMethod = delegateInvokeMethod;
|
||||
}
|
||||
}
|
||||
|
||||
class DelegateProxyAllocator : GroupByModuleEntityBase
|
||||
{
|
||||
private readonly CachedDictionary<MethodSig, TypeDef> _delegateTypes;
|
||||
private readonly HashSet<string> _allocatedDelegateNames = new HashSet<string>();
|
||||
|
||||
private TypeDef _delegateInstanceHolderType;
|
||||
private bool _done;
|
||||
|
||||
class CallInfo
|
||||
{
|
||||
public string key1;
|
||||
public int key2;
|
||||
public IMethod method;
|
||||
public bool callVir;
|
||||
|
||||
public int index;
|
||||
public TypeDef delegateType;
|
||||
public FieldDef delegateInstanceField;
|
||||
public MethodDef delegateInvokeMethod;
|
||||
public MethodDef proxyMethod;
|
||||
}
|
||||
private readonly Dictionary<MethodKey, CallInfo> _callMethods = new Dictionary<MethodKey, CallInfo>();
|
||||
private CallObfuscationSettingsFacade _settings;
|
||||
|
||||
public DelegateProxyAllocator()
|
||||
{
|
||||
_delegateTypes = new CachedDictionary<MethodSig, TypeDef>(SignatureEqualityComparer.Instance, CreateDelegateForSignature);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
_delegateInstanceHolderType = CreateDelegateInstanceHolderTypeDef();
|
||||
_settings = CallObfusPass.CurrentSettings;
|
||||
}
|
||||
|
||||
private string AllocateDelegateTypeName(MethodSig delegateInvokeSig)
|
||||
{
|
||||
uint hashCode = (uint)SignatureEqualityComparer.Instance.GetHashCode(delegateInvokeSig);
|
||||
string typeName = $"$Obfuz$Delegate_{hashCode}";
|
||||
if (_allocatedDelegateNames.Add(typeName))
|
||||
{
|
||||
return typeName;
|
||||
}
|
||||
for (int i = 0; ; i++)
|
||||
{
|
||||
typeName = $"$Obfuz$Delegate_{hashCode}_{i}";
|
||||
if (_allocatedDelegateNames.Add(typeName))
|
||||
{
|
||||
return typeName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TypeDef CreateDelegateForSignature(MethodSig delegateInvokeSig)
|
||||
{
|
||||
ModuleDef mod = Module;
|
||||
using (var scope = new DisableTypeDefFindCacheScope(mod))
|
||||
{
|
||||
|
||||
string typeName = AllocateDelegateTypeName(delegateInvokeSig);
|
||||
mod.Import(typeof(MulticastDelegate));
|
||||
|
||||
TypeDef delegateType = new TypeDefUser("", typeName, mod.CorLibTypes.GetTypeRef("System", "MulticastDelegate"));
|
||||
delegateType.Attributes = TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.Public;
|
||||
mod.Types.Add(delegateType);
|
||||
|
||||
MethodDef ctor = new MethodDefUser(
|
||||
".ctor",
|
||||
MethodSig.CreateInstance(mod.CorLibTypes.Void, mod.CorLibTypes.Object, mod.CorLibTypes.IntPtr),
|
||||
MethodImplAttributes.Runtime,
|
||||
MethodAttributes.RTSpecialName | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Public
|
||||
);
|
||||
ctor.DeclaringType = delegateType;
|
||||
|
||||
|
||||
MethodDef invokeMethod = new MethodDefUser(
|
||||
"Invoke",
|
||||
MethodSig.CreateInstance(delegateInvokeSig.RetType, delegateInvokeSig.Params.ToArray()),
|
||||
MethodImplAttributes.Runtime,
|
||||
MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.NewSlot | MethodAttributes.Virtual
|
||||
);
|
||||
invokeMethod.DeclaringType = delegateType;
|
||||
return delegateType;
|
||||
}
|
||||
}
|
||||
|
||||
private TypeDef CreateDelegateInstanceHolderTypeDef()
|
||||
{
|
||||
ModuleDef mod = Module;
|
||||
using (var scope = new DisableTypeDefFindCacheScope(mod))
|
||||
{
|
||||
string typeName = "$Obfuz$DelegateInstanceHolder";
|
||||
TypeDef holderType = new TypeDefUser("", typeName, mod.CorLibTypes.Object.ToTypeDefOrRef());
|
||||
holderType.Attributes = TypeAttributes.Class | TypeAttributes.Public;
|
||||
mod.Types.Add(holderType);
|
||||
return holderType;
|
||||
}
|
||||
}
|
||||
|
||||
private string AllocateFieldName(IMethod method, bool callVir)
|
||||
{
|
||||
uint hashCode = (uint)MethodEqualityComparer.CompareDeclaringTypes.GetHashCode(method);
|
||||
string typeName = $"$Obfuz$Delegate$Field_{hashCode}_{callVir}";
|
||||
if (_allocatedDelegateNames.Add(typeName))
|
||||
{
|
||||
return typeName;
|
||||
}
|
||||
for (int i = 0; ; i++)
|
||||
{
|
||||
typeName = $"$Obfuz$Delegate$Field_{hashCode}_{callVir}_{i}";
|
||||
if (_allocatedDelegateNames.Add(typeName))
|
||||
{
|
||||
return typeName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MethodDef CreateProxyMethod(string name, IMethod calledMethod, bool callVir, MethodSig delegateInvokeSig)
|
||||
{
|
||||
var proxyMethod = new MethodDefUser(name, delegateInvokeSig, MethodImplAttributes.Managed, MethodAttributes.Public | MethodAttributes.Static);
|
||||
var body = new CilBody();
|
||||
proxyMethod.Body = body;
|
||||
var ins = body.Instructions;
|
||||
|
||||
foreach (Parameter param in proxyMethod.Parameters)
|
||||
{
|
||||
ins.Add(Instruction.Create(OpCodes.Ldarg, param));
|
||||
}
|
||||
|
||||
ins.Add(Instruction.Create(callVir ? OpCodes.Callvirt : OpCodes.Call, calledMethod));
|
||||
ins.Add(Instruction.Create(OpCodes.Ret));
|
||||
return proxyMethod;
|
||||
}
|
||||
|
||||
public DelegateProxyMethodData Allocate(IMethod method, bool callVir, MethodSig delegateInvokeSig)
|
||||
{
|
||||
var key = new MethodKey(method, callVir);
|
||||
if (!_callMethods.TryGetValue(key, out var callInfo))
|
||||
{
|
||||
TypeDef delegateType = _delegateTypes.GetValue(delegateInvokeSig);
|
||||
MethodDef delegateInvokeMethod = delegateType.FindMethod("Invoke");
|
||||
string fieldName = AllocateFieldName(method, callVir);
|
||||
FieldDef delegateInstanceField = new FieldDefUser(fieldName, new FieldSig(delegateType.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.InitOnly);
|
||||
string key1 = $"{method.FullName}_{callVir}";
|
||||
callInfo = new CallInfo
|
||||
{
|
||||
key1 = key1,
|
||||
key2 = HashUtil.ComputePrimitiveOrStringOrBytesHashCode(key1) * 33445566,
|
||||
method = method,
|
||||
callVir = callVir,
|
||||
delegateType = delegateType,
|
||||
delegateInstanceField = delegateInstanceField,
|
||||
delegateInvokeMethod = delegateInvokeMethod,
|
||||
proxyMethod = CreateProxyMethod($"{fieldName}$Proxy", method, callVir, delegateInvokeSig),
|
||||
};
|
||||
_callMethods.Add(key, callInfo);
|
||||
}
|
||||
return new DelegateProxyMethodData(callInfo.delegateInstanceField, callInfo.delegateInvokeMethod);
|
||||
}
|
||||
|
||||
public override void Done()
|
||||
{
|
||||
if (_done)
|
||||
{
|
||||
throw new Exception("Already done");
|
||||
}
|
||||
_done = true;
|
||||
|
||||
ModuleDef mod = Module;
|
||||
|
||||
// for stable order, we sort methods by name
|
||||
List<CallInfo> callMethodList = _callMethods.Values.ToList();
|
||||
callMethodList.Sort((a, b) => a.key1.CompareTo(b.key1));
|
||||
|
||||
var cctor = new MethodDefUser(".cctor",
|
||||
MethodSig.CreateStatic(mod.CorLibTypes.Void),
|
||||
MethodImplAttributes.IL | MethodImplAttributes.Managed,
|
||||
MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Private);
|
||||
cctor.DeclaringType = _delegateInstanceHolderType;
|
||||
//_rvaTypeDef.Methods.Add(cctor);
|
||||
var body = new CilBody();
|
||||
cctor.Body = body;
|
||||
var ins = body.Instructions;
|
||||
|
||||
// var arr = new array[];
|
||||
// var d = new delegate;
|
||||
// arr[index] = d;
|
||||
int index = 0;
|
||||
ins.Add(Instruction.CreateLdcI4(callMethodList.Count));
|
||||
ins.Add(Instruction.Create(OpCodes.Newarr, mod.CorLibTypes.Object));
|
||||
foreach (CallInfo ci in callMethodList)
|
||||
{
|
||||
ci.index = index;
|
||||
_delegateInstanceHolderType.Methods.Add(ci.proxyMethod);
|
||||
ins.Add(Instruction.Create(OpCodes.Dup));
|
||||
ins.Add(Instruction.CreateLdcI4(index));
|
||||
ins.Add(Instruction.Create(OpCodes.Ldnull));
|
||||
ins.Add(Instruction.Create(OpCodes.Ldftn, ci.proxyMethod));
|
||||
MethodDef ctor = ci.delegateType.FindMethod(".ctor");
|
||||
UnityEngine.Assertions.Assert.IsNotNull(ctor, $"Delegate type {ci.delegateType.FullName} does not have a constructor.");
|
||||
ins.Add(Instruction.Create(OpCodes.Newobj, ctor));
|
||||
ins.Add(Instruction.Create(OpCodes.Stelem_Ref));
|
||||
++index;
|
||||
}
|
||||
|
||||
|
||||
|
||||
List<CallInfo> callMethodList2 = callMethodList.ToList();
|
||||
callMethodList2.Sort((a, b) => a.key2.CompareTo(b.key2));
|
||||
|
||||
EncryptionScopeInfo encryptionScope = EncryptionScope;
|
||||
DefaultMetadataImporter importer = this.GetDefaultModuleMetadataImporter();
|
||||
RvaDataAllocator rvaDataAllocator = this.GetEntity<RvaDataAllocator>();
|
||||
foreach (CallInfo ci in callMethodList2)
|
||||
{
|
||||
_delegateInstanceHolderType.Fields.Add(ci.delegateInstanceField);
|
||||
|
||||
|
||||
ins.Add(Instruction.Create(OpCodes.Dup));
|
||||
|
||||
IRandom localRandom = encryptionScope.localRandomCreator(HashUtil.ComputePrimitiveOrStringOrBytesHashCode(ci.key1));
|
||||
int ops = EncryptionUtil.GenerateEncryptionOpCodes(localRandom, encryptionScope.encryptor, _settings.obfuscationLevel);
|
||||
int salt = localRandom.NextInt();
|
||||
|
||||
int encryptedValue = encryptionScope.encryptor.Encrypt(ci.index, ops, salt);
|
||||
RvaData rvaData = rvaDataAllocator.Allocate(encryptedValue);
|
||||
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
|
||||
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
|
||||
ins.Add(Instruction.CreateLdcI4(ops));
|
||||
ins.Add(Instruction.CreateLdcI4(salt));
|
||||
ins.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaInt));
|
||||
ins.Add(Instruction.Create(OpCodes.Ldelem_Ref));
|
||||
ins.Add(Instruction.Create(OpCodes.Stsfld, ci.delegateInstanceField));
|
||||
}
|
||||
|
||||
ins.Add(Instruction.Create(OpCodes.Pop));
|
||||
ins.Add(Instruction.Create(OpCodes.Ret));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 02761bacbed8a8b489ae3e7f49f0f84a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,81 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Emit;
|
||||
using Obfuz.Utils;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Obfuz.ObfusPasses.CallObfus
|
||||
{
|
||||
|
||||
public class DelegateProxyObfuscator : ObfuscatorBase
|
||||
{
|
||||
private readonly GroupByModuleEntityManager _entityManager;
|
||||
|
||||
public DelegateProxyObfuscator(GroupByModuleEntityManager moduleEntityManager)
|
||||
{
|
||||
_entityManager = moduleEntityManager;
|
||||
}
|
||||
|
||||
public override void Done()
|
||||
{
|
||||
_entityManager.Done<DelegateProxyAllocator>();
|
||||
}
|
||||
|
||||
private MethodSig CreateProxyMethodSig(ModuleDef module, IMethod method)
|
||||
{
|
||||
MethodSig methodSig = MetaUtil.ToSharedMethodSig(module.CorLibTypes, MetaUtil.GetInflatedMethodSig(method, null));
|
||||
//MethodSig methodSig = MetaUtil.GetInflatedMethodSig(method).Clone();
|
||||
//methodSig.Params
|
||||
switch (MetaUtil.GetThisArgType(method))
|
||||
{
|
||||
case ThisArgType.Class:
|
||||
{
|
||||
methodSig.Params.Insert(0, module.CorLibTypes.Object);
|
||||
break;
|
||||
}
|
||||
case ThisArgType.ValueType:
|
||||
{
|
||||
methodSig.Params.Insert(0, module.CorLibTypes.IntPtr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return MethodSig.CreateStatic(methodSig.RetType, methodSig.Params.ToArray());
|
||||
}
|
||||
|
||||
public override bool Obfuscate(MethodDef callingMethod, IMethod calledMethod, bool callVir, List<Instruction> obfuscatedInstructions)
|
||||
{
|
||||
DelegateProxyAllocator allocator = _entityManager.GetEntity<DelegateProxyAllocator>(callingMethod.Module);
|
||||
LocalVariableAllocator localVarAllocator = new LocalVariableAllocator(callingMethod);
|
||||
MethodSig methodSig = CreateProxyMethodSig(callingMethod.Module, calledMethod);
|
||||
DelegateProxyMethodData proxyData = allocator.Allocate(calledMethod, callVir, methodSig);
|
||||
bool isVoidReturn = MetaUtil.IsVoidType(methodSig.RetType);
|
||||
|
||||
using (var varScope = localVarAllocator.CreateScope())
|
||||
{
|
||||
List<Local> localVars = new List<Local>();
|
||||
if (!isVoidReturn)
|
||||
{
|
||||
varScope.AllocateLocal(methodSig.RetType);
|
||||
}
|
||||
foreach (var p in methodSig.Params)
|
||||
{
|
||||
localVars.Add(varScope.AllocateLocal(p));
|
||||
}
|
||||
// save args
|
||||
for (int i = localVars.Count - 1; i >= 0; i--)
|
||||
{
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Stloc, localVars[i]));
|
||||
}
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, proxyData.delegateInstanceField));
|
||||
foreach (var local in localVars)
|
||||
{
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldloc, local));
|
||||
}
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Callvirt, proxyData.delegateInvokeMethod));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1102cd9f03de27c4b9fde3d6a87277c7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,52 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Data;
|
||||
using Obfuz.Emit;
|
||||
using Obfuz.Utils;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.ObfusPasses.CallObfus
|
||||
{
|
||||
|
||||
public class DispatchProxyObfuscator : ObfuscatorBase
|
||||
{
|
||||
private readonly GroupByModuleEntityManager _moduleEntityManager;
|
||||
|
||||
public DispatchProxyObfuscator(GroupByModuleEntityManager moduleEntityManager)
|
||||
{
|
||||
_moduleEntityManager = moduleEntityManager;
|
||||
}
|
||||
|
||||
public override void Done()
|
||||
{
|
||||
_moduleEntityManager.Done<ModuleDispatchProxyAllocator>();
|
||||
}
|
||||
|
||||
public override bool Obfuscate(MethodDef callerMethod, IMethod calledMethod, bool callVir, List<Instruction> obfuscatedInstructions)
|
||||
{
|
||||
ModuleDispatchProxyAllocator proxyCallAllocator = _moduleEntityManager.GetEntity<ModuleDispatchProxyAllocator>(callerMethod.Module);
|
||||
MethodSig sharedMethodSig = MetaUtil.ToSharedMethodSig(calledMethod.Module.CorLibTypes, MetaUtil.GetInflatedMethodSig(calledMethod, null));
|
||||
ProxyCallMethodData proxyCallMethodData = proxyCallAllocator.Allocate(calledMethod, callVir);
|
||||
DefaultMetadataImporter importer = proxyCallAllocator.GetDefaultModuleMetadataImporter();
|
||||
|
||||
//if (needCacheCall)
|
||||
//{
|
||||
// FieldDef cacheField = _constFieldAllocator.Allocate(callerMethod.Module, proxyCallMethodData.index);
|
||||
// obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, cacheField));
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// obfuscatedInstructions.Add(Instruction.CreateLdcI4(proxyCallMethodData.encryptedIndex));
|
||||
// obfuscatedInstructions.Add(Instruction.CreateLdcI4(proxyCallMethodData.encryptOps));
|
||||
// obfuscatedInstructions.Add(Instruction.CreateLdcI4(proxyCallMethodData.salt));
|
||||
// obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptInt));
|
||||
//}
|
||||
|
||||
ConstFieldAllocator constFieldAllocator = proxyCallAllocator.GetEntity<ConstFieldAllocator>();
|
||||
FieldDef cacheField = constFieldAllocator.Allocate(proxyCallMethodData.index);
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, cacheField));
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, proxyCallMethodData.proxyMethod));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
|
||||
namespace Obfuz.ObfusPasses.CallObfus
|
||||
{
|
||||
|
||||
public interface IObfuscationPolicy
|
||||
{
|
||||
bool NeedObfuscateCallInMethod(MethodDef method);
|
||||
|
||||
bool NeedObfuscateCalledMethod(MethodDef callerMethod, IMethod calledMethod, bool callVir);
|
||||
}
|
||||
|
||||
public abstract class ObfuscationPolicyBase : IObfuscationPolicy
|
||||
{
|
||||
public abstract bool NeedObfuscateCallInMethod(MethodDef method);
|
||||
|
||||
public abstract bool NeedObfuscateCalledMethod(MethodDef callerMethod, IMethod calledMethod, bool callVir);
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.ObfusPasses.CallObfus
|
||||
{
|
||||
public interface IObfuscator
|
||||
{
|
||||
bool Obfuscate(MethodDef callingMethod, IMethod calledMethod, bool callVir, List<Instruction> obfuscatedInstructions);
|
||||
|
||||
void Done();
|
||||
}
|
||||
|
||||
public abstract class ObfuscatorBase : IObfuscator
|
||||
{
|
||||
public abstract bool Obfuscate(MethodDef callingMethod, IMethod calledMethod, bool callVir, List<Instruction> obfuscatedInstructions);
|
||||
|
||||
public abstract void Done();
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
|
||||
namespace Obfuz.ObfusPasses.CallObfus
|
||||
{
|
||||
class MethodKey : IEquatable<MethodKey>
|
||||
{
|
||||
public readonly IMethod _method;
|
||||
public readonly bool _callVir;
|
||||
private readonly int _hashCode;
|
||||
|
||||
public MethodKey(IMethod method, bool callVir)
|
||||
{
|
||||
_method = method;
|
||||
_callVir = callVir;
|
||||
_hashCode = HashUtil.CombineHash(MethodEqualityComparer.CompareDeclaringTypes.GetHashCode(method), callVir ? 1 : 0);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _hashCode;
|
||||
}
|
||||
|
||||
public bool Equals(MethodKey other)
|
||||
{
|
||||
return MethodEqualityComparer.CompareDeclaringTypes.Equals(_method, other._method) && _callVir == other._callVir;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1193647b317b56f4b83aa080d0a17f7a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,125 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using Obfuz.Settings;
|
||||
using Obfuz.Utils;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.ObfusPasses.CallObfus
|
||||
{
|
||||
class SpecialWhiteListMethodCalculator
|
||||
{
|
||||
private readonly RuntimeType _targetRuntime;
|
||||
private readonly bool _obfuscateCallToMethodInMscorlib;
|
||||
private readonly CachedDictionary<IMethod, bool> _specialWhiteListMethodCache;
|
||||
|
||||
public SpecialWhiteListMethodCalculator(RuntimeType targetRuntime, bool obfuscateCallToMethodInMscorlib)
|
||||
{
|
||||
_targetRuntime = targetRuntime;
|
||||
_obfuscateCallToMethodInMscorlib = obfuscateCallToMethodInMscorlib;
|
||||
_specialWhiteListMethodCache = new CachedDictionary<IMethod, bool>(MethodEqualityComparer.CompareDeclaringTypes, this.ComputeIsInWhiteList);
|
||||
}
|
||||
|
||||
public bool IsInWhiteList(IMethod calledMethod)
|
||||
{
|
||||
return _specialWhiteListMethodCache.GetValue(calledMethod);
|
||||
}
|
||||
|
||||
private static readonly HashSet<string> _specialTypeFullNames = new HashSet<string>
|
||||
{
|
||||
"System.Enum",
|
||||
"System.Delegate",
|
||||
"System.MulticastDelegate",
|
||||
"Obfuz.EncryptionService`1",
|
||||
};
|
||||
|
||||
private static readonly HashSet<string> _specialMethodNames = new HashSet<string>
|
||||
{
|
||||
"GetEnumerator", // List<T>.Enumerator.GetEnumerator()
|
||||
".ctor", // constructor
|
||||
};
|
||||
|
||||
private static readonly HashSet<string> _specialMethodFullNames = new HashSet<string>
|
||||
{
|
||||
"System.Reflection.MethodBase.GetCurrentMethod",
|
||||
"System.Reflection.Assembly.GetCallingAssembly",
|
||||
"System.Reflection.Assembly.GetExecutingAssembly",
|
||||
"System.Reflection.Assembly.GetEntryAssembly",
|
||||
};
|
||||
|
||||
private bool ComputeIsInWhiteList(IMethod calledMethod)
|
||||
{
|
||||
MethodDef calledMethodDef = calledMethod.ResolveMethodDef();
|
||||
// mono has more strict access control, calls non-public method will raise exception.
|
||||
if (_targetRuntime == RuntimeType.Mono)
|
||||
{
|
||||
if (calledMethodDef != null && (!calledMethodDef.IsPublic || !IsTypeSelfAndParentPublic(calledMethodDef.DeclaringType)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ITypeDefOrRef declaringType = calledMethod.DeclaringType;
|
||||
TypeSig declaringTypeSig = calledMethod.DeclaringType.ToTypeSig();
|
||||
declaringTypeSig = declaringTypeSig.RemovePinnedAndModifiers();
|
||||
switch (declaringTypeSig.ElementType)
|
||||
{
|
||||
case ElementType.ValueType:
|
||||
case ElementType.Class:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case ElementType.GenericInst:
|
||||
{
|
||||
if (MetaUtil.ContainsContainsGenericParameter(calledMethod))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: return true;
|
||||
}
|
||||
|
||||
TypeDef typeDef = declaringType.ResolveTypeDef();
|
||||
|
||||
if (!_obfuscateCallToMethodInMscorlib && typeDef.Module.IsCoreLibraryModule == true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeDef.IsDelegate || typeDef.IsEnum)
|
||||
return true;
|
||||
|
||||
string fullName = typeDef.FullName;
|
||||
if (_specialTypeFullNames.Contains(fullName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
//if (fullName.StartsWith("System.Runtime.CompilerServices."))
|
||||
//{
|
||||
// return true;
|
||||
//}
|
||||
|
||||
string methodName = calledMethod.Name;
|
||||
if (_specialMethodNames.Contains(methodName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
string methodFullName = $"{fullName}.{methodName}";
|
||||
if (_specialMethodFullNames.Contains(methodFullName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsTypeSelfAndParentPublic(TypeDef type)
|
||||
{
|
||||
if (type.DeclaringType != null && !IsTypeSelfAndParentPublic(type.DeclaringType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return type.IsPublic;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 904e80c4b98911c40b6a9173ca24f3ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,337 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Data;
|
||||
using Obfuz.Emit;
|
||||
using Obfuz.Settings;
|
||||
using Obfuz.Utils;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace Obfuz.ObfusPasses.ConstEncrypt
|
||||
{
|
||||
public class DefaultConstEncryptor : IConstEncryptor
|
||||
{
|
||||
private readonly GroupByModuleEntityManager _moduleEntityManager;
|
||||
private readonly ConstEncryptionSettingsFacade _settings;
|
||||
|
||||
public DefaultConstEncryptor(GroupByModuleEntityManager moduleEntityManager, ConstEncryptionSettingsFacade settings)
|
||||
{
|
||||
_moduleEntityManager = moduleEntityManager;
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
private IRandom CreateRandomForValue(EncryptionScopeInfo encryptionScope, int value)
|
||||
{
|
||||
return encryptionScope.localRandomCreator(value);
|
||||
}
|
||||
|
||||
private int GenerateEncryptionOperations(EncryptionScopeInfo encryptionScope, IRandom random)
|
||||
{
|
||||
return EncryptionUtil.GenerateEncryptionOpCodes(random, encryptionScope.encryptor, _settings.encryptionLevel);
|
||||
}
|
||||
|
||||
public int GenerateSalt(IRandom random)
|
||||
{
|
||||
return random.NextInt();
|
||||
}
|
||||
|
||||
private DefaultMetadataImporter GetModuleMetadataImporter(MethodDef method)
|
||||
{
|
||||
return _moduleEntityManager.GetEntity<DefaultMetadataImporter>(method.Module);
|
||||
}
|
||||
|
||||
public void ObfuscateInt(MethodDef method, bool needCacheValue, int value, List<Instruction> obfuscatedInstructions)
|
||||
{
|
||||
EncryptionScopeInfo encryptionScope = _moduleEntityManager.EncryptionScopeProvider.GetScope(method.Module);
|
||||
IRandom random = CreateRandomForValue(encryptionScope, value.GetHashCode());
|
||||
ConstFieldAllocator constFieldAllocator = _moduleEntityManager.GetEntity<ConstFieldAllocator>(method.Module);
|
||||
RvaDataAllocator rvaDataAllocator = _moduleEntityManager.GetEntity<RvaDataAllocator>(method.Module);
|
||||
DefaultMetadataImporter importer = GetModuleMetadataImporter(method);
|
||||
|
||||
switch (random.NextInt(5))
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// = c = encrypted static field
|
||||
FieldDef cacheField = constFieldAllocator.Allocate(value);
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, cacheField));
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// c = a + b
|
||||
int a = random.NextInt();
|
||||
int b = value - a;
|
||||
float constProbability = 0.5f;
|
||||
ConstObfusUtil.LoadConstTwoInt(a, b, random, constProbability, constFieldAllocator, obfuscatedInstructions);
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Add));
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// c = a * b
|
||||
int a = random.NextInt() | 0x1;
|
||||
int ra = MathUtil.ModInverse32(a);
|
||||
int b = ra * value;
|
||||
float constProbability = 0.5f;
|
||||
ConstObfusUtil.LoadConstTwoInt(a, b, random, constProbability, constFieldAllocator, obfuscatedInstructions);
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Mul));
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
// c = a ^ b
|
||||
int a = random.NextInt();
|
||||
int b = a ^ value;
|
||||
float constProbability = 0.5f;
|
||||
ConstObfusUtil.LoadConstTwoInt(a, b, random, constProbability, constFieldAllocator, obfuscatedInstructions);
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Xor));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (needCacheValue)
|
||||
{
|
||||
FieldDef cacheField = constFieldAllocator.Allocate(value);
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, cacheField));
|
||||
return;
|
||||
}
|
||||
int ops = GenerateEncryptionOperations(encryptionScope, random);
|
||||
int salt = GenerateSalt(random);
|
||||
int encryptedValue = encryptionScope.encryptor.Encrypt(value, ops, salt);
|
||||
RvaData rvaData = rvaDataAllocator.Allocate(encryptedValue);
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
|
||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(rvaData.offset));
|
||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(ops));
|
||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(salt));
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaInt));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void ObfuscateLong(MethodDef method, bool needCacheValue, long value, List<Instruction> obfuscatedInstructions)
|
||||
{
|
||||
EncryptionScopeInfo encryptionScope = _moduleEntityManager.EncryptionScopeProvider.GetScope(method.Module);
|
||||
IRandom random = CreateRandomForValue(encryptionScope, value.GetHashCode());
|
||||
ConstFieldAllocator constFieldAllocator = _moduleEntityManager.GetEntity<ConstFieldAllocator>(method.Module);
|
||||
RvaDataAllocator rvaDataAllocator = _moduleEntityManager.GetEntity<RvaDataAllocator>(method.Module);
|
||||
DefaultMetadataImporter importer = GetModuleMetadataImporter(method);
|
||||
|
||||
switch (random.NextInt(5))
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// c = encrypted static field
|
||||
FieldDef cacheField = constFieldAllocator.Allocate(value);
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, cacheField));
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// c = a + b
|
||||
long a = random.NextLong();
|
||||
long b = value - a;
|
||||
float constProbability = 0.5f;
|
||||
ConstObfusUtil.LoadConstTwoLong(a, b, random, constProbability, constFieldAllocator, obfuscatedInstructions);
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Add));
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// c = a * b
|
||||
long a = random.NextLong() | 0x1;
|
||||
long ra = MathUtil.ModInverse64(a);
|
||||
long b = ra * value;
|
||||
float constProbability = 0.5f;
|
||||
ConstObfusUtil.LoadConstTwoLong(a, b, random, constProbability, constFieldAllocator, obfuscatedInstructions);
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Mul));
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
// c = a ^ b
|
||||
long a = random.NextLong();
|
||||
long b = a ^ value;
|
||||
float constProbability = 0.5f;
|
||||
ConstObfusUtil.LoadConstTwoLong(a, b, random, constProbability, constFieldAllocator, obfuscatedInstructions);
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Xor));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (needCacheValue)
|
||||
{
|
||||
FieldDef cacheField = constFieldAllocator.Allocate(value);
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, cacheField));
|
||||
return;
|
||||
}
|
||||
|
||||
int ops = GenerateEncryptionOperations(encryptionScope, random);
|
||||
int salt = GenerateSalt(random);
|
||||
long encryptedValue = encryptionScope.encryptor.Encrypt(value, ops, salt);
|
||||
RvaData rvaData = rvaDataAllocator.Allocate(encryptedValue);
|
||||
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
|
||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(rvaData.offset));
|
||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(ops));
|
||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(salt));
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaLong));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void ObfuscateFloat(MethodDef method, bool needCacheValue, float value, List<Instruction> obfuscatedInstructions)
|
||||
{
|
||||
EncryptionScopeInfo encryptionScope = _moduleEntityManager.EncryptionScopeProvider.GetScope(method.Module);
|
||||
IRandom random = CreateRandomForValue(encryptionScope, value.GetHashCode());
|
||||
ConstFieldAllocator constFieldAllocator = _moduleEntityManager.GetEntity<ConstFieldAllocator>(method.Module);
|
||||
RvaDataAllocator rvaDataAllocator = _moduleEntityManager.GetEntity<RvaDataAllocator>(method.Module);
|
||||
DefaultMetadataImporter importer = GetModuleMetadataImporter(method);
|
||||
|
||||
if (needCacheValue)
|
||||
{
|
||||
FieldDef cacheField = constFieldAllocator.Allocate(value);
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, cacheField));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int ops = GenerateEncryptionOperations(encryptionScope, random);
|
||||
int salt = GenerateSalt(random);
|
||||
float encryptedValue = encryptionScope.encryptor.Encrypt(value, ops, salt);
|
||||
RvaData rvaData = rvaDataAllocator.Allocate(encryptedValue);
|
||||
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
|
||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(rvaData.offset));
|
||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(ops));
|
||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(salt));
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaFloat));
|
||||
}
|
||||
|
||||
public void ObfuscateDouble(MethodDef method, bool needCacheValue, double value, List<Instruction> obfuscatedInstructions)
|
||||
{
|
||||
EncryptionScopeInfo encryptionScope = _moduleEntityManager.EncryptionScopeProvider.GetScope(method.Module);
|
||||
IRandom random = CreateRandomForValue(encryptionScope, value.GetHashCode());
|
||||
ConstFieldAllocator constFieldAllocator = _moduleEntityManager.GetEntity<ConstFieldAllocator>(method.Module);
|
||||
RvaDataAllocator rvaDataAllocator = _moduleEntityManager.GetEntity<RvaDataAllocator>(method.Module);
|
||||
DefaultMetadataImporter importer = GetModuleMetadataImporter(method);
|
||||
|
||||
if (needCacheValue)
|
||||
{
|
||||
FieldDef cacheField = constFieldAllocator.Allocate(value);
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, cacheField));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int ops = GenerateEncryptionOperations(encryptionScope, random);
|
||||
int salt = GenerateSalt(random);
|
||||
double encryptedValue = encryptionScope.encryptor.Encrypt(value, ops, salt);
|
||||
RvaData rvaData = rvaDataAllocator.Allocate(encryptedValue);
|
||||
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
|
||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(rvaData.offset));
|
||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(ops));
|
||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(salt));
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaDouble));
|
||||
}
|
||||
|
||||
|
||||
class EncryptedRvaDataInfo
|
||||
{
|
||||
public readonly FieldDef fieldDef;
|
||||
public readonly byte[] originalBytes;
|
||||
public readonly byte[] encryptedBytes;
|
||||
public readonly int opts;
|
||||
public readonly int salt;
|
||||
|
||||
public EncryptedRvaDataInfo(FieldDef fieldDef, byte[] originalBytes, byte[] encryptedBytes, int opts, int salt)
|
||||
{
|
||||
this.fieldDef = fieldDef;
|
||||
this.originalBytes = originalBytes;
|
||||
this.encryptedBytes = encryptedBytes;
|
||||
this.opts = opts;
|
||||
this.salt = salt;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Dictionary<FieldDef, EncryptedRvaDataInfo> _encryptedRvaFields = new Dictionary<FieldDef, EncryptedRvaDataInfo>();
|
||||
|
||||
private EncryptedRvaDataInfo GetEncryptedRvaData(FieldDef fieldDef)
|
||||
{
|
||||
if (!_encryptedRvaFields.TryGetValue(fieldDef, out var encryptedRvaData))
|
||||
{
|
||||
EncryptionScopeInfo encryptionScope = _moduleEntityManager.EncryptionScopeProvider.GetScope(fieldDef.Module);
|
||||
IRandom random = CreateRandomForValue(encryptionScope, FieldEqualityComparer.CompareDeclaringTypes.GetHashCode(fieldDef));
|
||||
int ops = GenerateEncryptionOperations(encryptionScope, random);
|
||||
int salt = GenerateSalt(random);
|
||||
byte[] originalBytes = fieldDef.InitialValue;
|
||||
byte[] encryptedBytes = (byte[])originalBytes.Clone();
|
||||
encryptionScope.encryptor.EncryptBlock(encryptedBytes, ops, salt);
|
||||
Assert.AreNotEqual(originalBytes, encryptedBytes, "Original bytes should not be the same as encrypted bytes.");
|
||||
encryptedRvaData = new EncryptedRvaDataInfo(fieldDef, originalBytes, encryptedBytes, ops, salt);
|
||||
_encryptedRvaFields.Add(fieldDef, encryptedRvaData);
|
||||
fieldDef.InitialValue = encryptedBytes;
|
||||
byte[] decryptedBytes = (byte[])encryptedBytes.Clone();
|
||||
encryptionScope.encryptor.DecryptBlock(decryptedBytes, ops, salt);
|
||||
AssertUtil.AreArrayEqual(originalBytes, decryptedBytes, "Decrypted bytes should match the original bytes after encryption and decryption.");
|
||||
}
|
||||
return encryptedRvaData;
|
||||
}
|
||||
|
||||
|
||||
public void ObfuscateBytes(MethodDef method, bool needCacheValue, FieldDef field, byte[] value, List<Instruction> obfuscatedInstructions)
|
||||
{
|
||||
EncryptedRvaDataInfo encryptedData = GetEncryptedRvaData(field);
|
||||
Assert.AreEqual(value.Length, encryptedData.encryptedBytes.Length);
|
||||
|
||||
DefaultMetadataImporter importer = GetModuleMetadataImporter(method);
|
||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(encryptedData.encryptedBytes.Length));
|
||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(encryptedData.opts));
|
||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(encryptedData.salt));
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptInitializeArray));
|
||||
}
|
||||
|
||||
public void ObfuscateString(MethodDef method, bool needCacheValue, string value, List<Instruction> obfuscatedInstructions)
|
||||
{
|
||||
EncryptionScopeInfo encryptionScope = _moduleEntityManager.EncryptionScopeProvider.GetScope(method.Module);
|
||||
IRandom random = CreateRandomForValue(encryptionScope, value.GetHashCode());
|
||||
ConstFieldAllocator constFieldAllocator = _moduleEntityManager.GetEntity<ConstFieldAllocator>(method.Module);
|
||||
RvaDataAllocator rvaDataAllocator = _moduleEntityManager.GetEntity<RvaDataAllocator>(method.Module);
|
||||
DefaultMetadataImporter importer = GetModuleMetadataImporter(method);
|
||||
|
||||
if (needCacheValue)
|
||||
{
|
||||
FieldDef cacheField = constFieldAllocator.Allocate(value);
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, cacheField));
|
||||
return;
|
||||
}
|
||||
|
||||
int ops = GenerateEncryptionOperations(encryptionScope, random);
|
||||
int salt = GenerateSalt(random);
|
||||
int stringByteLength = Encoding.UTF8.GetByteCount(value);
|
||||
byte[] encryptedValue = encryptionScope.encryptor.Encrypt(value, ops, salt);
|
||||
Assert.AreEqual(stringByteLength, encryptedValue.Length);
|
||||
RvaData rvaData = rvaDataAllocator.Allocate(encryptedValue);
|
||||
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
|
||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(rvaData.offset));
|
||||
// should use stringByteLength, can't use rvaData.size, because rvaData.size is align to 4, it's not the actual length.
|
||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(stringByteLength));
|
||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(ops));
|
||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(salt));
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaString));
|
||||
}
|
||||
|
||||
public void Done()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using Obfuz.Conf;
|
||||
using Obfuz.Settings;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
|
||||
namespace Obfuz.ObfusPasses.ControlFlowObfus
|
||||
{
|
||||
struct ObfuscationRuleData
|
||||
{
|
||||
public readonly ObfuscationLevel obfuscationLevel;
|
||||
public ObfuscationRuleData(ObfuscationLevel level)
|
||||
{
|
||||
obfuscationLevel = level;
|
||||
}
|
||||
}
|
||||
|
||||
interface IObfuscationPolicy
|
||||
{
|
||||
bool NeedObfuscate(MethodDef method);
|
||||
|
||||
ObfuscationRuleData GetObfuscationRuleData(MethodDef method);
|
||||
}
|
||||
|
||||
abstract class ObfuscationPolicyBase : IObfuscationPolicy
|
||||
{
|
||||
public abstract bool NeedObfuscate(MethodDef method);
|
||||
|
||||
public abstract ObfuscationRuleData GetObfuscationRuleData(MethodDef method);
|
||||
}
|
||||
|
||||
class ConfigurableObfuscationPolicy : ObfuscationPolicyBase
|
||||
{
|
||||
class ObfuscationRule : IRule<ObfuscationRule>
|
||||
{
|
||||
public ObfuscationLevel? obfuscationLevel;
|
||||
|
||||
public void InheritParent(ObfuscationRule parentRule)
|
||||
{
|
||||
if (obfuscationLevel == null)
|
||||
obfuscationLevel = parentRule.obfuscationLevel;
|
||||
}
|
||||
}
|
||||
|
||||
class MethodSpec : MethodRuleBase<ObfuscationRule>
|
||||
{
|
||||
}
|
||||
|
||||
class TypeSpec : TypeRuleBase<MethodSpec, ObfuscationRule>
|
||||
{
|
||||
}
|
||||
|
||||
class AssemblySpec : AssemblyRuleBase<TypeSpec, MethodSpec, ObfuscationRule>
|
||||
{
|
||||
}
|
||||
|
||||
private static readonly ObfuscationRule s_default = new ObfuscationRule()
|
||||
{
|
||||
obfuscationLevel = ObfuscationLevel.Basic,
|
||||
};
|
||||
|
||||
private ObfuscationRule _global;
|
||||
|
||||
private readonly XmlAssemblyTypeMethodRuleParser<AssemblySpec, TypeSpec, MethodSpec, ObfuscationRule> _xmlParser;
|
||||
|
||||
private readonly Dictionary<MethodDef, ObfuscationRule> _methodRuleCache = new Dictionary<MethodDef, ObfuscationRule>();
|
||||
|
||||
public ConfigurableObfuscationPolicy(List<string> toObfuscatedAssemblyNames, List<string> xmlConfigFiles)
|
||||
{
|
||||
_xmlParser = new XmlAssemblyTypeMethodRuleParser<AssemblySpec, TypeSpec, MethodSpec, ObfuscationRule>(
|
||||
toObfuscatedAssemblyNames, ParseObfuscationRule, ParseGlobal);
|
||||
LoadConfigs(xmlConfigFiles);
|
||||
}
|
||||
|
||||
private void LoadConfigs(List<string> configFiles)
|
||||
{
|
||||
_xmlParser.LoadConfigs(configFiles);
|
||||
|
||||
if (_global == null)
|
||||
{
|
||||
_global = s_default;
|
||||
}
|
||||
else
|
||||
{
|
||||
_global.InheritParent(s_default);
|
||||
}
|
||||
_xmlParser.InheritParentRules(_global);
|
||||
}
|
||||
|
||||
private void ParseGlobal(string configFile, XmlElement ele)
|
||||
{
|
||||
switch (ele.Name)
|
||||
{
|
||||
case "global": _global = ParseObfuscationRule(configFile, ele); break;
|
||||
default: throw new Exception($"Invalid xml file {configFile}, unknown node {ele.Name}");
|
||||
}
|
||||
}
|
||||
|
||||
private ObfuscationRule ParseObfuscationRule(string configFile, XmlElement ele)
|
||||
{
|
||||
var rule = new ObfuscationRule();
|
||||
if (ele.HasAttribute("obfuscationLevel"))
|
||||
{
|
||||
rule.obfuscationLevel = ConfigUtil.ParseObfuscationLevel(ele.GetAttribute("obfuscationLevel"));
|
||||
}
|
||||
return rule;
|
||||
}
|
||||
|
||||
private ObfuscationRule GetMethodObfuscationRule(MethodDef method)
|
||||
{
|
||||
if (!_methodRuleCache.TryGetValue(method, out var rule))
|
||||
{
|
||||
rule = _xmlParser.GetMethodRule(method, _global);
|
||||
_methodRuleCache[method] = rule;
|
||||
}
|
||||
return rule;
|
||||
}
|
||||
|
||||
public override bool NeedObfuscate(MethodDef method)
|
||||
{
|
||||
ObfuscationRule rule = GetMethodObfuscationRule(method);
|
||||
return rule.obfuscationLevel.Value > ObfuscationLevel.None;
|
||||
}
|
||||
|
||||
public override ObfuscationRuleData GetObfuscationRuleData(MethodDef method)
|
||||
{
|
||||
var rule = GetMethodObfuscationRule(method);
|
||||
return new ObfuscationRuleData(rule.obfuscationLevel.Value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f6983877d8859df4882c30f75be7a70e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,80 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using Obfuz.Data;
|
||||
using Obfuz.Emit;
|
||||
using Obfuz.Settings;
|
||||
using Obfuz.Utils;
|
||||
|
||||
namespace Obfuz.ObfusPasses.ControlFlowObfus
|
||||
{
|
||||
class ObfusMethodContext
|
||||
{
|
||||
public MethodDef method;
|
||||
public LocalVariableAllocator localVariableAllocator;
|
||||
public IRandom localRandom;
|
||||
public EncryptionScopeInfo encryptionScope;
|
||||
public DefaultMetadataImporter importer;
|
||||
public ConstFieldAllocator constFieldAllocator;
|
||||
public int minInstructionCountOfBasicBlockToObfuscate;
|
||||
|
||||
public IRandom CreateRandom()
|
||||
{
|
||||
return encryptionScope.localRandomCreator(MethodEqualityComparer.CompareDeclaringTypes.GetHashCode(method));
|
||||
}
|
||||
}
|
||||
|
||||
internal class ControlFlowObfusPass : ObfuscationMethodPassBase
|
||||
{
|
||||
private readonly ControlFlowObfuscationSettingsFacade _settings;
|
||||
|
||||
private IObfuscationPolicy _obfuscationPolicy;
|
||||
private IObfuscator _obfuscator;
|
||||
|
||||
public ControlFlowObfusPass(ControlFlowObfuscationSettingsFacade settings)
|
||||
{
|
||||
_settings = settings;
|
||||
_obfuscator = new DefaultObfuscator();
|
||||
}
|
||||
|
||||
public override ObfuscationPassType Type => ObfuscationPassType.ControlFlowObfus;
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
ObfuscationPassContext ctx = ObfuscationPassContext.Current;
|
||||
_obfuscationPolicy = new ConfigurableObfuscationPolicy(
|
||||
ctx.coreSettings.assembliesToObfuscate,
|
||||
_settings.ruleFiles);
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override bool NeedObfuscateMethod(MethodDef method)
|
||||
{
|
||||
return _obfuscationPolicy.NeedObfuscate(method);
|
||||
}
|
||||
|
||||
protected override void ObfuscateData(MethodDef method)
|
||||
{
|
||||
//Debug.Log($"Obfuscating method: {method.FullName} with EvalStackObfusPass");
|
||||
|
||||
ObfuscationPassContext ctx = ObfuscationPassContext.Current;
|
||||
GroupByModuleEntityManager moduleEntityManager = ctx.moduleEntityManager;
|
||||
var encryptionScope = moduleEntityManager.EncryptionScopeProvider.GetScope(method.Module);
|
||||
var ruleData = _obfuscationPolicy.GetObfuscationRuleData(method);
|
||||
var localRandom = encryptionScope.localRandomCreator(MethodEqualityComparer.CompareDeclaringTypes.GetHashCode(method));
|
||||
var obfusMethodCtx = new ObfusMethodContext
|
||||
{
|
||||
method = method,
|
||||
localVariableAllocator = new LocalVariableAllocator(method),
|
||||
encryptionScope = encryptionScope,
|
||||
constFieldAllocator = moduleEntityManager.GetEntity<ConstFieldAllocator>(method.Module),
|
||||
localRandom = localRandom,
|
||||
importer = moduleEntityManager.GetEntity<DefaultMetadataImporter>(method.Module),
|
||||
minInstructionCountOfBasicBlockToObfuscate = _settings.minInstructionCountOfBasicBlockToObfuscate,
|
||||
};
|
||||
_obfuscator.Obfuscate(method, obfusMethodCtx);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cf62db4d3137e6447bd5cb2a65f101d3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,19 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
|
||||
namespace Obfuz.ObfusPasses.ControlFlowObfus
|
||||
{
|
||||
class DefaultObfuscator : ObfuscatorBase
|
||||
{
|
||||
public override bool Obfuscate(MethodDef method, ObfusMethodContext ctx)
|
||||
{
|
||||
//Debug.Log($"Obfuscating method: {method.FullName} with ControlFlowObfusPass");
|
||||
var mcfc = new MethodControlFlowCalculator(method, ctx.CreateRandom(), ctx.constFieldAllocator, ctx.minInstructionCountOfBasicBlockToObfuscate);
|
||||
if (!mcfc.TryObfus())
|
||||
{
|
||||
//Debug.LogWarning($"not obfuscate method: {method.FullName}");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8aa2a2e43fa066541b982dbb63452458
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,14 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
|
||||
namespace Obfuz.ObfusPasses.ControlFlowObfus
|
||||
{
|
||||
interface IObfuscator
|
||||
{
|
||||
bool Obfuscate(MethodDef method, ObfusMethodContext ctx);
|
||||
}
|
||||
|
||||
abstract class ObfuscatorBase : IObfuscator
|
||||
{
|
||||
public abstract bool Obfuscate(MethodDef method, ObfusMethodContext ctx);
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4ada5f6005768f745a18dc8b968e1684
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,947 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Data;
|
||||
using Obfuz.Emit;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace Obfuz.ObfusPasses.ControlFlowObfus
|
||||
{
|
||||
class MethodControlFlowCalculator
|
||||
{
|
||||
class BasicBlockInputOutputArguments
|
||||
{
|
||||
public readonly List<Local> locals = new List<Local>();
|
||||
|
||||
public BasicBlockInputOutputArguments()
|
||||
{
|
||||
}
|
||||
|
||||
public BasicBlockInputOutputArguments(MethodDef method, List<EvalDataTypeWithSig> inputStackDatas)
|
||||
{
|
||||
ICorLibTypes corLibTypes = method.Module.CorLibTypes;
|
||||
foreach (var data in inputStackDatas)
|
||||
{
|
||||
Local local = new Local(GetLocalTypeSig(corLibTypes, data));
|
||||
locals.Add(local);
|
||||
method.Body.Variables.Add(local);
|
||||
}
|
||||
}
|
||||
|
||||
private TypeSig GetLocalTypeSig(ICorLibTypes corLibTypes, EvalDataTypeWithSig type)
|
||||
{
|
||||
TypeSig typeSig = type.typeSig;
|
||||
switch (type.type)
|
||||
{
|
||||
case EvalDataType.Int32: return corLibTypes.Int32;
|
||||
case EvalDataType.Int64: return corLibTypes.Int64;
|
||||
case EvalDataType.Float: return corLibTypes.Single;
|
||||
case EvalDataType.Double: return corLibTypes.Double;
|
||||
case EvalDataType.I: return typeSig ?? corLibTypes.IntPtr;
|
||||
case EvalDataType.Ref: return typeSig == null || MetaUtil.IsValueType(typeSig) ? corLibTypes.Object : typeSig;
|
||||
case EvalDataType.ValueType: Assert.IsNotNull(typeSig); return typeSig;
|
||||
case EvalDataType.Token: throw new System.NotSupportedException("Token type is not supported in BasicBlockInputOutputArguments");
|
||||
default: throw new System.NotSupportedException("not supported EvalDataType");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BasicBlockInfo
|
||||
{
|
||||
public BlockGroup group;
|
||||
|
||||
//public int order;
|
||||
public bool isSaveStackBlock;
|
||||
public BasicBlockInfo prev;
|
||||
public BasicBlockInfo next;
|
||||
|
||||
public List<Instruction> instructions;
|
||||
public List<EvalDataTypeWithSig> inputStackDatas;
|
||||
public List<EvalDataTypeWithSig> outputStackDatas;
|
||||
|
||||
public List<BasicBlockInfo> inBasicBlocks = new List<BasicBlockInfo>();
|
||||
public List<BasicBlockInfo> outBasicBlocks = new List<BasicBlockInfo>();
|
||||
|
||||
public BasicBlockInputOutputArguments inputArgs;
|
||||
public BasicBlockInputOutputArguments outputArgs;
|
||||
|
||||
public Instruction FirstInstruction => instructions[0];
|
||||
|
||||
public Instruction LastInstruction => instructions[instructions.Count - 1];
|
||||
|
||||
public Instruction GroupFirstInstruction => group.basicBlocks[0].FirstInstruction;
|
||||
|
||||
|
||||
//public void InsertNext(BasicBlockInfo nextBb)
|
||||
//{
|
||||
// if (next != null)
|
||||
// {
|
||||
// next.prev = nextBb;
|
||||
// nextBb.next = next;
|
||||
// }
|
||||
// nextBb.prev = this;
|
||||
// next = nextBb;
|
||||
//}
|
||||
|
||||
public void InsertBefore(BasicBlockInfo prevBb)
|
||||
{
|
||||
prev.next = prevBb;
|
||||
prevBb.prev = prev;
|
||||
prevBb.next = this;
|
||||
this.prev = prevBb;
|
||||
}
|
||||
|
||||
public void AddOutBasicBlock(BasicBlockInfo outBb)
|
||||
{
|
||||
if (!outBasicBlocks.Contains(outBb))
|
||||
{
|
||||
outBasicBlocks.Add(outBb);
|
||||
outBb.inBasicBlocks.Add(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearInBasicBlocks()
|
||||
{
|
||||
foreach (var inBb in inBasicBlocks)
|
||||
{
|
||||
inBb.outBasicBlocks.Remove(this);
|
||||
}
|
||||
inBasicBlocks.Clear();
|
||||
}
|
||||
|
||||
public void RetargetInBasicBlocksTo(BasicBlockInfo prevBb, Dictionary<Instruction, BasicBlockInfo> inst2bb)
|
||||
{
|
||||
var oldInBlocks = new List<BasicBlockInfo>(inBasicBlocks);
|
||||
ClearInBasicBlocks();
|
||||
foreach (var oldInBb in oldInBlocks)
|
||||
{
|
||||
oldInBb.AddOutBasicBlock(prevBb);
|
||||
}
|
||||
// inBB => saveBb => cur
|
||||
foreach (BasicBlockInfo inBb in prevBb.inBasicBlocks)
|
||||
{
|
||||
if (inBb.instructions.Count == 0)
|
||||
{
|
||||
// empty block, no need to retarget
|
||||
continue;
|
||||
}
|
||||
Instruction lastInst = inBb.instructions.Last();
|
||||
if (lastInst.Operand is Instruction targetInst)
|
||||
{
|
||||
if (inst2bb.TryGetValue(targetInst, out BasicBlockInfo targetBb) && targetBb == this)
|
||||
{
|
||||
// retarget to prevBb
|
||||
lastInst.Operand = prevBb.FirstInstruction;
|
||||
}
|
||||
}
|
||||
else if (lastInst.Operand is Instruction[] targetInsts)
|
||||
{
|
||||
for (int i = 0; i < targetInsts.Length; i++)
|
||||
{
|
||||
targetInst = targetInsts[i];
|
||||
if (inst2bb.TryGetValue(targetInst, out BasicBlockInfo targetBb) && targetBb == this)
|
||||
{
|
||||
targetInsts[i] = prevBb.FirstInstruction;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly MethodDef _method;
|
||||
private readonly IRandom _random;
|
||||
private readonly ConstFieldAllocator _constFieldAllocator;
|
||||
private readonly int _minInstructionCountOfBasicBlockToObfuscate;
|
||||
private readonly BasicBlockInfo _bbHead;
|
||||
|
||||
public MethodControlFlowCalculator(MethodDef method, IRandom random, ConstFieldAllocator constFieldAllocator, int minInstructionCountOfBasicBlockToObfuscate)
|
||||
{
|
||||
_method = method;
|
||||
_random = random;
|
||||
_constFieldAllocator = constFieldAllocator;
|
||||
_minInstructionCountOfBasicBlockToObfuscate = minInstructionCountOfBasicBlockToObfuscate;
|
||||
|
||||
_bbHead = new BasicBlockInfo()
|
||||
{
|
||||
instructions = new List<Instruction>(),
|
||||
inputStackDatas = new List<EvalDataTypeWithSig>(),
|
||||
outputStackDatas = new List<EvalDataTypeWithSig>(),
|
||||
};
|
||||
}
|
||||
|
||||
private void BuildBasicBlockLink(EvalStackCalculator evc)
|
||||
{
|
||||
BasicBlockInfo prev = _bbHead;
|
||||
var bb2bb = new Dictionary<BasicBlock, BasicBlockInfo>();
|
||||
foreach (BasicBlock bb in evc.BasicBlockCollection.Blocks)
|
||||
{
|
||||
EvalStackState ess = evc.GetEvalStackState(bb);
|
||||
var newBB = new BasicBlockInfo
|
||||
{
|
||||
prev = prev,
|
||||
next = null,
|
||||
instructions = bb.instructions,
|
||||
inputStackDatas = ess.inputStackDatas,
|
||||
outputStackDatas = ess.runStackDatas,
|
||||
};
|
||||
prev.next = newBB;
|
||||
prev = newBB;
|
||||
bb2bb.Add(bb, newBB);
|
||||
}
|
||||
foreach (BasicBlock bb in evc.BasicBlockCollection.Blocks)
|
||||
{
|
||||
BasicBlockInfo bbi = bb2bb[bb];
|
||||
foreach (var inBb in bb.inBlocks)
|
||||
{
|
||||
bbi.inBasicBlocks.Add(bb2bb[inBb]);
|
||||
}
|
||||
foreach (var outBb in bb.outBlocks)
|
||||
{
|
||||
bbi.outBasicBlocks.Add(bb2bb[outBb]);
|
||||
}
|
||||
}
|
||||
|
||||
// let _bbHead point to the first basic block
|
||||
//_bbHead.instructions.Add(Instruction.Create(OpCodes.Br, _bbHead.next.FirstInstruction));
|
||||
_bbHead.next.inBasicBlocks.Add(_bbHead);
|
||||
_bbHead.outBasicBlocks.Add(_bbHead.next);
|
||||
}
|
||||
|
||||
private bool CheckNotContainsNotSupportedEvalStackData()
|
||||
{
|
||||
for (BasicBlockInfo cur = _bbHead; cur != null; cur = cur.next)
|
||||
{
|
||||
foreach (var data in cur.inputStackDatas)
|
||||
{
|
||||
if (data.type == EvalDataType.Unknown || data.type == EvalDataType.Token)
|
||||
{
|
||||
Debug.LogError($"NotSupported EvalStackData found in method: {_method.FullName}, type: {data.type}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private void WalkInputArgumentGroup(BasicBlockInfo cur, BasicBlockInputOutputArguments inputArgs)
|
||||
{
|
||||
if (cur.inputArgs != null)
|
||||
{
|
||||
Assert.AreEqual(cur.inputArgs, inputArgs, "input arguments not match");
|
||||
return;
|
||||
}
|
||||
cur.inputArgs = inputArgs;
|
||||
foreach (BasicBlockInfo inputBB in cur.inBasicBlocks)
|
||||
{
|
||||
if (inputBB.outputArgs != null)
|
||||
{
|
||||
Assert.AreEqual(inputBB.outputArgs, inputArgs, $"Input BB {inputBB} outputArgs does not match in method: {_method.FullName}");
|
||||
continue;
|
||||
}
|
||||
inputBB.outputArgs = cur.inputArgs;
|
||||
foreach (var outBB in inputBB.outBasicBlocks)
|
||||
{
|
||||
WalkInputArgumentGroup(outBB, inputArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly BasicBlockInputOutputArguments emptyEvalStackArgs = new BasicBlockInputOutputArguments();
|
||||
|
||||
private void ComputeInputOutputArguments()
|
||||
{
|
||||
for (BasicBlockInfo cur = _bbHead; cur != null; cur = cur.next)
|
||||
{
|
||||
if (cur.inputArgs == null)
|
||||
{
|
||||
if (cur.inputStackDatas.Count == 0)
|
||||
{
|
||||
cur.inputArgs = emptyEvalStackArgs;
|
||||
}
|
||||
else
|
||||
{
|
||||
var inputArgs = new BasicBlockInputOutputArguments(_method, cur.inputStackDatas);
|
||||
WalkInputArgumentGroup(cur, inputArgs);
|
||||
}
|
||||
}
|
||||
if (cur.outputArgs == null && cur.outputStackDatas.Count == 0)
|
||||
{
|
||||
cur.outputArgs = emptyEvalStackArgs;
|
||||
}
|
||||
}
|
||||
for (BasicBlockInfo cur = _bbHead; cur != null; cur = cur.next)
|
||||
{
|
||||
if (cur.inputArgs == null)
|
||||
{
|
||||
throw new System.Exception($"Input arguments for BasicBlock {cur} in method {_method.FullName} is null");
|
||||
}
|
||||
if (cur.outputArgs == null)
|
||||
{
|
||||
if (cur.instructions.Count > 0)
|
||||
{
|
||||
Code lastInstCode = cur.LastInstruction.OpCode.Code;
|
||||
Assert.IsTrue(lastInstCode == Code.Throw || lastInstCode == Code.Rethrow);
|
||||
cur.outputStackDatas = new List<EvalDataTypeWithSig>();
|
||||
}
|
||||
cur.outputArgs = emptyEvalStackArgs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private BasicBlockInfo CreateSaveStackBasicBlock(BasicBlockInfo to)
|
||||
{
|
||||
if (to.group == null)
|
||||
{
|
||||
throw new Exception($"BasicBlock {to} in method {_method.FullName} does not belong to any group. This should not happen.");
|
||||
}
|
||||
|
||||
var saveLocalBasicBlock = new BasicBlockInfo
|
||||
{
|
||||
group = to.group,
|
||||
isSaveStackBlock = true,
|
||||
inputStackDatas = to.inputStackDatas,
|
||||
inputArgs = to.inputArgs,
|
||||
outputStackDatas = new List<EvalDataTypeWithSig>(),
|
||||
outputArgs = emptyEvalStackArgs,
|
||||
instructions = new List<Instruction>(),
|
||||
};
|
||||
|
||||
var locals = to.inputArgs.locals;
|
||||
if (locals.Count > 0)
|
||||
{
|
||||
to.instructions.InsertRange(0, locals.Select(l => Instruction.Create(OpCodes.Ldloc, l)));
|
||||
|
||||
}
|
||||
for (int i = locals.Count - 1; i >= 0; i--)
|
||||
{
|
||||
saveLocalBasicBlock.instructions.Add(Instruction.Create(OpCodes.Stloc, locals[i]));
|
||||
}
|
||||
|
||||
to.inputArgs = emptyEvalStackArgs;
|
||||
to.inputStackDatas = new List<EvalDataTypeWithSig>();
|
||||
|
||||
BlockGroup group = to.group;
|
||||
group.basicBlocks.Insert(group.basicBlocks.IndexOf(to), saveLocalBasicBlock);
|
||||
group.switchMachineCases.Add(new SwitchMachineCase { index = -1, prepareBlock = saveLocalBasicBlock, targetBlock = to });
|
||||
saveLocalBasicBlock.instructions.Add(Instruction.Create(OpCodes.Ldsfld, (FieldDef)null));
|
||||
saveLocalBasicBlock.instructions.Add(Instruction.Create(OpCodes.Stloc, GlobalSwitchIndexLocal));
|
||||
saveLocalBasicBlock.instructions.Add(Instruction.Create(OpCodes.Br, group.switchMachineEntryInst));
|
||||
|
||||
|
||||
return saveLocalBasicBlock;
|
||||
}
|
||||
|
||||
private void AdjustInputOutputEvalStack()
|
||||
{
|
||||
Dictionary<Instruction, BasicBlockInfo> inst2bb = BuildInstructionToBasicBlockInfoDic();
|
||||
for (BasicBlockInfo cur = _bbHead.next; cur != null; cur = cur.next)
|
||||
{
|
||||
if (cur.inputArgs.locals.Count == 0 && cur.instructions.Count < _minInstructionCountOfBasicBlockToObfuscate)
|
||||
{
|
||||
// small block, no need to save stack
|
||||
continue;
|
||||
}
|
||||
|
||||
BasicBlockInfo saveBb = CreateSaveStackBasicBlock(cur);
|
||||
cur.InsertBefore(saveBb);
|
||||
cur.RetargetInBasicBlocksTo(saveBb, inst2bb);
|
||||
//saveBb.AddOutBasicBlock(cur);
|
||||
}
|
||||
}
|
||||
|
||||
private void InsertSwitchMachineBasicBlockForGroups(BlockGroup rootGroup)
|
||||
{
|
||||
Dictionary<Instruction, BasicBlockInfo> inst2bb = BuildInstructionToBasicBlockInfoDic();
|
||||
|
||||
InsertSwitchMachineBasicBlockForGroup(rootGroup, inst2bb);
|
||||
}
|
||||
|
||||
private void ShuffleBasicBlocks0(List<BasicBlockInfo> bbs)
|
||||
{
|
||||
if (bbs.Count <= 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var subBlocksExcludeFirstLast = bbs.GetRange(1, bbs.Count - 2);
|
||||
|
||||
var blocksInputArgsCountZero = new List<BasicBlockInfo>();
|
||||
var blocksInputArgsCountNonZero = new List<BasicBlockInfo>();
|
||||
foreach (var bb in subBlocksExcludeFirstLast)
|
||||
{
|
||||
if (bb.inputArgs.locals.Count == 0)
|
||||
{
|
||||
blocksInputArgsCountZero.Add(bb);
|
||||
}
|
||||
else
|
||||
{
|
||||
blocksInputArgsCountNonZero.Add(bb);
|
||||
}
|
||||
}
|
||||
RandomUtil.ShuffleList(blocksInputArgsCountZero, _random);
|
||||
|
||||
int index = 1;
|
||||
foreach (var bb in blocksInputArgsCountZero)
|
||||
{
|
||||
bbs[index++] = bb;
|
||||
}
|
||||
foreach (var bb in blocksInputArgsCountNonZero)
|
||||
{
|
||||
bbs[index++] = bb;
|
||||
}
|
||||
Assert.AreEqual(bbs.Count - 1, index, "Shuffled basic blocks count should be the same as original count minus first and last blocks");
|
||||
|
||||
//var firstSection = new List<BasicBlockInfo>() { bbs[0] };
|
||||
//var sectionsExcludeFirstLast = new List<List<BasicBlockInfo>>();
|
||||
//List<BasicBlockInfo> currentSection = firstSection;
|
||||
//for (int i = 1; i < n; i++)
|
||||
//{
|
||||
// BasicBlockInfo cur = bbs[i];
|
||||
// if (cur.inputArgs.locals.Count == 0)
|
||||
// {
|
||||
// currentSection = new List<BasicBlockInfo>() { cur };
|
||||
// sectionsExcludeFirstLast.Add(currentSection);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// currentSection.Add(cur);
|
||||
// }
|
||||
//}
|
||||
//if (sectionsExcludeFirstLast.Count <= 1)
|
||||
//{
|
||||
// return;
|
||||
//}
|
||||
//var lastSection = sectionsExcludeFirstLast.Last();
|
||||
//sectionsExcludeFirstLast.RemoveAt(sectionsExcludeFirstLast.Count - 1);
|
||||
|
||||
|
||||
//RandomUtil.ShuffleList(sectionsExcludeFirstLast, _random);
|
||||
|
||||
//bbs.Clear();
|
||||
//bbs.AddRange(firstSection);
|
||||
//bbs.AddRange(sectionsExcludeFirstLast.SelectMany(section => section));
|
||||
//bbs.AddRange(lastSection);
|
||||
//Assert.AreEqual(n, bbs.Count, "Shuffled basic blocks count should be the same as original count");
|
||||
}
|
||||
|
||||
private void ShuffleBasicBlocks(List<BasicBlockInfo> bbs)
|
||||
{
|
||||
// TODO
|
||||
|
||||
int n = bbs.Count;
|
||||
BasicBlockInfo groupPrev = bbs[0].prev;
|
||||
BasicBlockInfo groupNext = bbs[n - 1].next;
|
||||
//RandomUtil.ShuffleList(bbs, _random);
|
||||
ShuffleBasicBlocks0(bbs);
|
||||
BasicBlockInfo prev = groupPrev;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
BasicBlockInfo cur = bbs[i];
|
||||
cur.prev = prev;
|
||||
prev.next = cur;
|
||||
prev = cur;
|
||||
}
|
||||
prev.next = groupNext;
|
||||
if (groupNext != null)
|
||||
{
|
||||
groupNext.prev = prev;
|
||||
}
|
||||
}
|
||||
|
||||
private Local _globalSwitchIndexLocal;
|
||||
|
||||
Local GlobalSwitchIndexLocal
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_globalSwitchIndexLocal == null)
|
||||
{
|
||||
_globalSwitchIndexLocal = new Local(_method.Module.CorLibTypes.Int32);
|
||||
_method.Body.Variables.Add(_globalSwitchIndexLocal);
|
||||
}
|
||||
return _globalSwitchIndexLocal;
|
||||
}
|
||||
}
|
||||
|
||||
private void InsertSwitchMachineBasicBlockForGroup(BlockGroup group, Dictionary<Instruction, BasicBlockInfo> inst2bb)
|
||||
{
|
||||
if (group.subGroups != null && group.subGroups.Count > 0)
|
||||
{
|
||||
foreach (var subGroup in group.subGroups)
|
||||
{
|
||||
InsertSwitchMachineBasicBlockForGroup(subGroup, inst2bb);
|
||||
}
|
||||
}
|
||||
else if (group.switchMachineCases.Count > 0)
|
||||
{
|
||||
Assert.IsTrue(group.basicBlocks.Count > 0, "Group should contain at least one basic block");
|
||||
|
||||
BasicBlockInfo firstBlock = group.basicBlocks[0];
|
||||
var firstCase = group.switchMachineCases[0];
|
||||
//Assert.AreEqual(firstCase.prepareBlock, firstBlock, "First case prepare block should be the first basic block in group");
|
||||
|
||||
Assert.IsTrue(firstCase.targetBlock.inputArgs.locals.Count == 0);
|
||||
Assert.IsTrue(firstCase.targetBlock.inputStackDatas.Count == 0);
|
||||
|
||||
var instructions = new List<Instruction>()
|
||||
{
|
||||
Instruction.Create(OpCodes.Ldsfld, (FieldDef)null),
|
||||
Instruction.Create(OpCodes.Stloc, GlobalSwitchIndexLocal),
|
||||
group.switchMachineEntryInst,
|
||||
group.switchMachineInst,
|
||||
Instruction.Create(OpCodes.Br, firstCase.targetBlock.FirstInstruction),
|
||||
};
|
||||
if (firstCase.prepareBlock != firstBlock || firstBlock.inputStackDatas.Count != 0)
|
||||
{
|
||||
instructions.Insert(0, Instruction.Create(OpCodes.Br, firstBlock.FirstInstruction));
|
||||
}
|
||||
|
||||
var switchMachineBb = new BasicBlockInfo()
|
||||
{
|
||||
group = group,
|
||||
inputArgs = firstBlock.inputArgs,
|
||||
outputArgs = emptyEvalStackArgs,
|
||||
inputStackDatas = firstBlock.inputStackDatas,
|
||||
outputStackDatas = new List<EvalDataTypeWithSig>(),
|
||||
instructions = instructions,
|
||||
};
|
||||
firstBlock.InsertBefore(switchMachineBb);
|
||||
group.basicBlocks.Insert(0, switchMachineBb);
|
||||
ShuffleBasicBlocks(group.basicBlocks);
|
||||
|
||||
List<Instruction> switchTargets = (List<Instruction>)group.switchMachineInst.Operand;
|
||||
|
||||
RandomUtil.ShuffleList(group.switchMachineCases, _random);
|
||||
|
||||
for (int i = 0, n = group.switchMachineCases.Count; i < n; i++)
|
||||
{
|
||||
SwitchMachineCase switchMachineCase = group.switchMachineCases[i];
|
||||
switchMachineCase.index = i;
|
||||
List<Instruction> prepareBlockInstructions = switchMachineCase.prepareBlock.instructions;
|
||||
|
||||
Instruction setBranchIndexInst = prepareBlockInstructions[prepareBlockInstructions.Count - 3];
|
||||
Assert.AreEqual(setBranchIndexInst.OpCode, OpCodes.Ldsfld, "first instruction of prepareBlock should be Ldsfld");
|
||||
//setBranchIndexInst.Operand = i;
|
||||
var indexField = _constFieldAllocator.Allocate(i);
|
||||
setBranchIndexInst.Operand = indexField;
|
||||
switchTargets.Add(switchMachineCase.targetBlock.FirstInstruction);
|
||||
}
|
||||
|
||||
// after shuffle
|
||||
//Assert.IsTrue(instructions.Count == 4 || instructions.Count == 5, "Switch machine basic block should contain 4 or 5 instructions");
|
||||
Instruction loadFirstIndex = instructions[instructions.Count - 5];
|
||||
Assert.AreEqual(Code.Ldsfld, loadFirstIndex.OpCode.Code, "First instruction should be Ldsfld");
|
||||
loadFirstIndex.Operand = _constFieldAllocator.Allocate(firstCase.index);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsPrevBasicBlockControlFlowNextToThis(BasicBlockInfo cur)
|
||||
{
|
||||
Instruction lastInst = cur.prev.LastInstruction;
|
||||
switch (lastInst.OpCode.FlowControl)
|
||||
{
|
||||
case FlowControl.Cond_Branch:
|
||||
case FlowControl.Call:
|
||||
case FlowControl.Next:
|
||||
case FlowControl.Break:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void InsertBrInstructionForConjoinedBasicBlocks()
|
||||
{
|
||||
for (BasicBlockInfo cur = _bbHead.next.next; cur != null; cur = cur.next)
|
||||
{
|
||||
if (cur.group == cur.prev.group && IsPrevBasicBlockControlFlowNextToThis(cur))
|
||||
{
|
||||
cur.prev.instructions.Add(Instruction.Create(OpCodes.Br, cur.FirstInstruction));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<Instruction, BasicBlockInfo> BuildInstructionToBasicBlockInfoDic()
|
||||
{
|
||||
var inst2bb = new Dictionary<Instruction, BasicBlockInfo>();
|
||||
for (BasicBlockInfo cur = _bbHead.next; cur != null; cur = cur.next)
|
||||
{
|
||||
foreach (var inst in cur.instructions)
|
||||
{
|
||||
inst2bb[inst] = cur;
|
||||
}
|
||||
}
|
||||
return inst2bb;
|
||||
}
|
||||
|
||||
|
||||
private class SwitchMachineCase
|
||||
{
|
||||
public int index;
|
||||
public BasicBlockInfo prepareBlock;
|
||||
public BasicBlockInfo targetBlock;
|
||||
}
|
||||
|
||||
private class BlockGroup
|
||||
{
|
||||
public BlockGroup parent;
|
||||
|
||||
public List<Instruction> instructions;
|
||||
|
||||
public List<BlockGroup> subGroups;
|
||||
|
||||
public List<BasicBlockInfo> basicBlocks;
|
||||
|
||||
public Instruction switchMachineEntryInst;
|
||||
public Instruction switchMachineInst;
|
||||
public List<SwitchMachineCase> switchMachineCases;
|
||||
|
||||
public BlockGroup(List<Instruction> instructions, Dictionary<Instruction, BlockGroup> inst2group)
|
||||
{
|
||||
this.instructions = instructions;
|
||||
UpdateInstructionGroup(inst2group);
|
||||
}
|
||||
|
||||
public BlockGroup(BlockGroup parent, List<Instruction> instructions, Dictionary<Instruction, BlockGroup> inst2group)
|
||||
{
|
||||
this.instructions = instructions;
|
||||
UpdateInstructionGroup(parent, inst2group);
|
||||
}
|
||||
|
||||
public BlockGroup RootParent => parent == null ? this : parent.RootParent;
|
||||
|
||||
public void SetParent(BlockGroup newParent)
|
||||
{
|
||||
if (parent != null)
|
||||
{
|
||||
Assert.IsTrue(parent != newParent, "Parent group should not be the same as new parent");
|
||||
Assert.IsTrue(parent.subGroups.Contains(this), "Parent group should already contain this group");
|
||||
parent.subGroups.Remove(this);
|
||||
}
|
||||
parent = newParent;
|
||||
if (newParent.subGroups == null)
|
||||
{
|
||||
newParent.subGroups = new List<BlockGroup>();
|
||||
}
|
||||
Assert.IsFalse(newParent.subGroups.Contains(this), "New parent group should not already contain this group");
|
||||
newParent.subGroups.Add(this);
|
||||
}
|
||||
|
||||
private void UpdateInstructionGroup(Dictionary<Instruction, BlockGroup> inst2group)
|
||||
{
|
||||
foreach (var inst in instructions)
|
||||
{
|
||||
if (inst2group.TryGetValue(inst, out BlockGroup existGroup))
|
||||
{
|
||||
if (this != existGroup)
|
||||
{
|
||||
BlockGroup rootParent = existGroup.RootParent;
|
||||
if (rootParent != this)
|
||||
{
|
||||
rootParent.SetParent(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
inst2group[inst] = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateInstructionGroup(BlockGroup parentGroup, Dictionary<Instruction, BlockGroup> inst2group)
|
||||
{
|
||||
foreach (var inst in instructions)
|
||||
{
|
||||
BlockGroup existGroup = inst2group[inst];
|
||||
Assert.AreEqual(parentGroup, existGroup, "Instruction group parent should be the same as parent group");
|
||||
inst2group[inst] = this;
|
||||
}
|
||||
SetParent(parentGroup);
|
||||
}
|
||||
|
||||
public void SplitInstructionsNotInAnySubGroupsToIndividualGroups(Dictionary<Instruction, BlockGroup> inst2group)
|
||||
{
|
||||
if (subGroups == null || subGroups.Count == 0 || instructions.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var subGroup in subGroups)
|
||||
{
|
||||
subGroup.SplitInstructionsNotInAnySubGroupsToIndividualGroups(inst2group);
|
||||
}
|
||||
|
||||
var finalGroupList = new List<BlockGroup>();
|
||||
var curGroupInstructions = new List<Instruction>();
|
||||
|
||||
var firstInst2SubGroup = subGroups.ToDictionary(g => g.instructions[0]);
|
||||
foreach (var inst in instructions)
|
||||
{
|
||||
BlockGroup group = inst2group[inst];
|
||||
if (group == this)
|
||||
{
|
||||
curGroupInstructions.Add(inst);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (curGroupInstructions.Count > 0)
|
||||
{
|
||||
finalGroupList.Add(new BlockGroup(this, curGroupInstructions, inst2group));
|
||||
curGroupInstructions = new List<Instruction>();
|
||||
}
|
||||
if (firstInst2SubGroup.TryGetValue(inst, out var subGroup))
|
||||
{
|
||||
finalGroupList.Add(subGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (curGroupInstructions.Count > 0)
|
||||
{
|
||||
finalGroupList.Add(new BlockGroup(this, curGroupInstructions, inst2group));
|
||||
}
|
||||
this.subGroups = finalGroupList;
|
||||
}
|
||||
|
||||
public void ComputeBasicBlocks(Dictionary<Instruction, BasicBlockInfo> inst2bb, Func<Local> switchIndexLocalGetter)
|
||||
{
|
||||
if (subGroups == null || subGroups.Count == 0)
|
||||
{
|
||||
basicBlocks = new List<BasicBlockInfo>();
|
||||
foreach (var inst in instructions)
|
||||
{
|
||||
BasicBlockInfo block = inst2bb[inst];
|
||||
if (block.group != null)
|
||||
{
|
||||
if (block.group != this)
|
||||
{
|
||||
throw new Exception("BasicBlockInfo group should be the same as this BlockGroup");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
block.group = this;
|
||||
basicBlocks.Add(block);
|
||||
}
|
||||
}
|
||||
switchMachineEntryInst = Instruction.Create(OpCodes.Ldloc, switchIndexLocalGetter());
|
||||
switchMachineInst = Instruction.Create(OpCodes.Switch, new List<Instruction>());
|
||||
switchMachineCases = new List<SwitchMachineCase>();
|
||||
return;
|
||||
}
|
||||
foreach (var subGroup in subGroups)
|
||||
{
|
||||
subGroup.ComputeBasicBlocks(inst2bb, switchIndexLocalGetter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TryBlockGroup : BlockGroup
|
||||
{
|
||||
public TryBlockGroup(List<Instruction> instructions, Dictionary<Instruction, BlockGroup> inst2group) : base(instructions, inst2group)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class ExceptionHandlerGroup : BlockGroup
|
||||
{
|
||||
public readonly ExceptionHandler exceptionHandler;
|
||||
|
||||
public ExceptionHandlerGroup(ExceptionHandler exceptionHandler, List<Instruction> instructions, Dictionary<Instruction, BlockGroup> inst2group) : base(instructions, inst2group)
|
||||
{
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
}
|
||||
}
|
||||
|
||||
private class ExceptionFilterGroup : BlockGroup
|
||||
{
|
||||
public readonly ExceptionHandler exceptionHandler;
|
||||
|
||||
public ExceptionFilterGroup(ExceptionHandler exceptionHandler, List<Instruction> instructions, Dictionary<Instruction, BlockGroup> inst2group) : base(instructions, inst2group)
|
||||
{
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
}
|
||||
}
|
||||
|
||||
private class ExceptionHandlerWithFilterGroup : BlockGroup
|
||||
{
|
||||
public readonly ExceptionHandler exceptionHandler;
|
||||
//public readonly ExceptionFilterGroup filterGroup;
|
||||
//public readonly ExceptionHandlerGroup handlerGroup;
|
||||
public ExceptionHandlerWithFilterGroup(ExceptionHandler exceptionHandler, List<Instruction> filterInstructions, List<Instruction> handlerInstructions, List<Instruction> allInstructions, Dictionary<Instruction, BlockGroup> inst2group) : base(allInstructions, inst2group)
|
||||
{
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
var filterGroup = new ExceptionFilterGroup(exceptionHandler, filterInstructions, inst2group);
|
||||
var handlerGroup = new ExceptionHandlerGroup(exceptionHandler, handlerInstructions, inst2group);
|
||||
}
|
||||
}
|
||||
|
||||
class TryBlockInfo
|
||||
{
|
||||
public Instruction tryStart;
|
||||
public Instruction tryEnd;
|
||||
public TryBlockGroup blockGroup;
|
||||
}
|
||||
|
||||
private Dictionary<Instruction, int> BuildInstruction2Index()
|
||||
{
|
||||
IList<Instruction> instructions = _method.Body.Instructions;
|
||||
var inst2Index = new Dictionary<Instruction, int>(instructions.Count);
|
||||
for (int i = 0; i < instructions.Count; i++)
|
||||
{
|
||||
Instruction inst = instructions[i];
|
||||
inst2Index.Add(inst, i);
|
||||
}
|
||||
return inst2Index;
|
||||
}
|
||||
|
||||
private BlockGroup SplitBasicBlockGroup()
|
||||
{
|
||||
Dictionary<Instruction, int> inst2Index = BuildInstruction2Index();
|
||||
var inst2blockGroup = new Dictionary<Instruction, BlockGroup>();
|
||||
|
||||
List<Instruction> instructions = (List<Instruction>)_method.Body.Instructions;
|
||||
|
||||
var tryBlocks = new List<TryBlockInfo>();
|
||||
foreach (var ex in _method.Body.ExceptionHandlers)
|
||||
{
|
||||
TryBlockInfo tryBlock = tryBlocks.Find(block => block.tryStart == ex.TryStart && block.tryEnd == ex.TryEnd);
|
||||
if (tryBlock == null)
|
||||
{
|
||||
int startIndex = inst2Index[ex.TryStart];
|
||||
int endIndex = ex.TryEnd != null ? inst2Index[ex.TryEnd] : inst2Index.Count;
|
||||
TryBlockGroup blockGroup = new TryBlockGroup(instructions.GetRange(startIndex, endIndex - startIndex), inst2blockGroup);
|
||||
tryBlock = new TryBlockInfo
|
||||
{
|
||||
tryStart = ex.TryStart,
|
||||
tryEnd = ex.TryEnd,
|
||||
blockGroup = blockGroup,
|
||||
};
|
||||
tryBlocks.Add(tryBlock);
|
||||
}
|
||||
if (ex.FilterStart != null)
|
||||
{
|
||||
int filterStartIndex = inst2Index[ex.FilterStart];
|
||||
int filterEndIndex = ex.HandlerStart != null ? inst2Index[ex.HandlerStart] : inst2Index.Count;
|
||||
int handlerStartIndex = filterEndIndex;
|
||||
int handlerEndIndex = ex.HandlerEnd != null ? inst2Index[ex.HandlerEnd] : inst2Index.Count;
|
||||
var filterHandlerGroup = new ExceptionHandlerWithFilterGroup(ex,
|
||||
instructions.GetRange(filterStartIndex, filterEndIndex - filterStartIndex),
|
||||
instructions.GetRange(handlerStartIndex, handlerEndIndex - handlerStartIndex),
|
||||
instructions.GetRange(filterStartIndex, handlerEndIndex - filterStartIndex), inst2blockGroup);
|
||||
}
|
||||
else
|
||||
{
|
||||
int handlerStartIndex = inst2Index[ex.HandlerStart];
|
||||
int handlerEndIndex = ex.HandlerEnd != null ? inst2Index[ex.HandlerEnd] : inst2Index.Count;
|
||||
ExceptionHandlerGroup handlerGroup = new ExceptionHandlerGroup(ex, instructions.GetRange(handlerStartIndex, handlerEndIndex - handlerStartIndex), inst2blockGroup);
|
||||
}
|
||||
}
|
||||
var rootGroup = new BlockGroup(new List<Instruction>(instructions), inst2blockGroup);
|
||||
rootGroup.SplitInstructionsNotInAnySubGroupsToIndividualGroups(inst2blockGroup);
|
||||
|
||||
rootGroup.ComputeBasicBlocks(BuildInstructionToBasicBlockInfoDic(), () => GlobalSwitchIndexLocal);
|
||||
return rootGroup;
|
||||
}
|
||||
|
||||
private void FixInstructionTargets()
|
||||
{
|
||||
var inst2bb = BuildInstructionToBasicBlockInfoDic();
|
||||
foreach (var ex in _method.Body.ExceptionHandlers)
|
||||
{
|
||||
if (ex.TryStart != null)
|
||||
{
|
||||
ex.TryStart = inst2bb[ex.TryStart].GroupFirstInstruction;
|
||||
}
|
||||
if (ex.TryEnd != null)
|
||||
{
|
||||
ex.TryEnd = inst2bb[ex.TryEnd].GroupFirstInstruction;
|
||||
}
|
||||
if (ex.HandlerStart != null)
|
||||
{
|
||||
ex.HandlerStart = inst2bb[ex.HandlerStart].GroupFirstInstruction;
|
||||
}
|
||||
if (ex.HandlerEnd != null)
|
||||
{
|
||||
ex.HandlerEnd = inst2bb[ex.HandlerEnd].GroupFirstInstruction;
|
||||
}
|
||||
if (ex.FilterStart != null)
|
||||
{
|
||||
ex.FilterStart = inst2bb[ex.FilterStart].GroupFirstInstruction;
|
||||
}
|
||||
}
|
||||
//foreach (var inst in inst2bb.Keys)
|
||||
//{
|
||||
// if (inst.Operand is Instruction targetInst)
|
||||
// {
|
||||
// inst.Operand = inst2bb[targetInst].FirstInstruction;
|
||||
// }
|
||||
// else if (inst.Operand is Instruction[] targetInsts)
|
||||
// {
|
||||
// for (int i = 0; i < targetInsts.Length; i++)
|
||||
// {
|
||||
// targetInsts[i] = inst2bb[targetInsts[i]].FirstInstruction;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
private void BuildInstructions()
|
||||
{
|
||||
IList<Instruction> methodInstructions = _method.Body.Instructions;
|
||||
methodInstructions.Clear();
|
||||
for (BasicBlockInfo cur = _bbHead.next; cur != null; cur = cur.next)
|
||||
{
|
||||
foreach (Instruction inst in cur.instructions)
|
||||
{
|
||||
methodInstructions.Add(inst);
|
||||
}
|
||||
}
|
||||
_method.Body.InitLocals = true;
|
||||
//_method.Body.MaxStack = Math.Max(_method.Body.MaxStack , (ushort)1); // TODO: set to a reasonable value
|
||||
//_method.Body.KeepOldMaxStack = true;
|
||||
//_method.Body.UpdateInstructionOffsets();
|
||||
}
|
||||
|
||||
public bool TryObfus()
|
||||
{
|
||||
// TODO: TEMP
|
||||
//if (_method.Body.HasExceptionHandlers)
|
||||
//{
|
||||
// return false;
|
||||
//}
|
||||
if (_method.HasGenericParameters || _method.DeclaringType.HasGenericParameters)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var evc = new EvalStackCalculator(_method);
|
||||
BuildBasicBlockLink(evc);
|
||||
if (!CheckNotContainsNotSupportedEvalStackData())
|
||||
{
|
||||
Debug.LogError($"Method {_method.FullName} contains unsupported EvalStackData, obfuscation skipped.");
|
||||
return false;
|
||||
}
|
||||
BlockGroup rootGroup = SplitBasicBlockGroup();
|
||||
if (rootGroup.basicBlocks != null && rootGroup.basicBlocks.Count == 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ComputeInputOutputArguments();
|
||||
AdjustInputOutputEvalStack();
|
||||
InsertBrInstructionForConjoinedBasicBlocks();
|
||||
InsertSwitchMachineBasicBlockForGroups(rootGroup);
|
||||
|
||||
FixInstructionTargets();
|
||||
BuildInstructions();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 144b6474de40382498899f8b1c7f92a3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,147 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using Obfuz.Conf;
|
||||
using Obfuz.Settings;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
|
||||
namespace Obfuz.ObfusPasses.EvalStackObfus
|
||||
{
|
||||
struct ObfuscationRuleData
|
||||
{
|
||||
public readonly ObfuscationLevel obfuscationLevel;
|
||||
public readonly float obfuscationPercentage;
|
||||
public ObfuscationRuleData(ObfuscationLevel level, float percentage)
|
||||
{
|
||||
obfuscationLevel = level;
|
||||
obfuscationPercentage = percentage;
|
||||
}
|
||||
}
|
||||
|
||||
interface IObfuscationPolicy
|
||||
{
|
||||
bool NeedObfuscate(MethodDef method);
|
||||
|
||||
ObfuscationRuleData GetObfuscationRuleData(MethodDef method);
|
||||
}
|
||||
|
||||
abstract class ObfuscationPolicyBase : IObfuscationPolicy
|
||||
{
|
||||
public abstract bool NeedObfuscate(MethodDef method);
|
||||
|
||||
public abstract ObfuscationRuleData GetObfuscationRuleData(MethodDef method);
|
||||
}
|
||||
|
||||
class ConfigurableObfuscationPolicy : ObfuscationPolicyBase
|
||||
{
|
||||
class ObfuscationRule : IRule<ObfuscationRule>
|
||||
{
|
||||
public ObfuscationLevel? obfuscationLevel;
|
||||
public float? obfuscationPercentage;
|
||||
|
||||
public void InheritParent(ObfuscationRule parentRule)
|
||||
{
|
||||
if (obfuscationLevel == null)
|
||||
obfuscationLevel = parentRule.obfuscationLevel;
|
||||
if (obfuscationPercentage == null)
|
||||
obfuscationPercentage = parentRule.obfuscationPercentage;
|
||||
}
|
||||
}
|
||||
|
||||
class MethodSpec : MethodRuleBase<ObfuscationRule>
|
||||
{
|
||||
}
|
||||
|
||||
class TypeSpec : TypeRuleBase<MethodSpec, ObfuscationRule>
|
||||
{
|
||||
}
|
||||
|
||||
class AssemblySpec : AssemblyRuleBase<TypeSpec, MethodSpec, ObfuscationRule>
|
||||
{
|
||||
}
|
||||
|
||||
private static readonly ObfuscationRule s_default = new ObfuscationRule()
|
||||
{
|
||||
obfuscationLevel = ObfuscationLevel.Basic,
|
||||
obfuscationPercentage = 0.05f,
|
||||
};
|
||||
|
||||
private ObfuscationRule _global;
|
||||
|
||||
private readonly XmlAssemblyTypeMethodRuleParser<AssemblySpec, TypeSpec, MethodSpec, ObfuscationRule> _xmlParser;
|
||||
|
||||
private readonly Dictionary<MethodDef, ObfuscationRule> _methodRuleCache = new Dictionary<MethodDef, ObfuscationRule>();
|
||||
|
||||
public ConfigurableObfuscationPolicy(List<string> toObfuscatedAssemblyNames, List<string> xmlConfigFiles)
|
||||
{
|
||||
_xmlParser = new XmlAssemblyTypeMethodRuleParser<AssemblySpec, TypeSpec, MethodSpec, ObfuscationRule>(
|
||||
toObfuscatedAssemblyNames, ParseObfuscationRule, ParseGlobal);
|
||||
LoadConfigs(xmlConfigFiles);
|
||||
}
|
||||
|
||||
private void LoadConfigs(List<string> configFiles)
|
||||
{
|
||||
_xmlParser.LoadConfigs(configFiles);
|
||||
|
||||
if (_global == null)
|
||||
{
|
||||
_global = s_default;
|
||||
}
|
||||
else
|
||||
{
|
||||
_global.InheritParent(s_default);
|
||||
}
|
||||
if (_global.obfuscationPercentage.Value > 0.1f)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"EvalStackObfus significantly increases the size of the obfuscated hot-update DLL. It is recommended to keep the obfuscationPercentage ≤ 0.1 (currently set to {_global.obfuscationPercentage.Value}).");
|
||||
}
|
||||
_xmlParser.InheritParentRules(_global);
|
||||
}
|
||||
|
||||
private void ParseGlobal(string configFile, XmlElement ele)
|
||||
{
|
||||
switch (ele.Name)
|
||||
{
|
||||
case "global": _global = ParseObfuscationRule(configFile, ele); break;
|
||||
default: throw new Exception($"Invalid xml file {configFile}, unknown node {ele.Name}");
|
||||
}
|
||||
}
|
||||
|
||||
private ObfuscationRule ParseObfuscationRule(string configFile, XmlElement ele)
|
||||
{
|
||||
var rule = new ObfuscationRule();
|
||||
if (ele.HasAttribute("obfuscationLevel"))
|
||||
{
|
||||
rule.obfuscationLevel = ConfigUtil.ParseObfuscationLevel(ele.GetAttribute("obfuscationLevel"));
|
||||
}
|
||||
if (ele.HasAttribute("obfuscationPercentage"))
|
||||
{
|
||||
rule.obfuscationPercentage = float.Parse(ele.GetAttribute("obfuscationPercentage"));
|
||||
}
|
||||
return rule;
|
||||
}
|
||||
|
||||
private ObfuscationRule GetMethodObfuscationRule(MethodDef method)
|
||||
{
|
||||
if (!_methodRuleCache.TryGetValue(method, out var rule))
|
||||
{
|
||||
rule = _xmlParser.GetMethodRule(method, _global);
|
||||
_methodRuleCache[method] = rule;
|
||||
}
|
||||
return rule;
|
||||
}
|
||||
|
||||
public override bool NeedObfuscate(MethodDef method)
|
||||
{
|
||||
ObfuscationRule rule = GetMethodObfuscationRule(method);
|
||||
return rule.obfuscationLevel.Value > ObfuscationLevel.None;
|
||||
}
|
||||
|
||||
public override ObfuscationRuleData GetObfuscationRuleData(MethodDef method)
|
||||
{
|
||||
var rule = GetMethodObfuscationRule(method);
|
||||
return new ObfuscationRuleData(rule.obfuscationLevel.Value, rule.obfuscationPercentage.Value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8a2603d51f31a134d90599d33664f6c7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,225 +0,0 @@
|
|||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Utils;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.ObfusPasses.EvalStackObfus
|
||||
{
|
||||
class DefaultObfuscator : ObfuscatorBase
|
||||
{
|
||||
public override bool ObfuscateInt(Instruction inst, List<Instruction> outputInsts, ObfusMethodContext ctx)
|
||||
{
|
||||
IRandom random = ctx.localRandom;
|
||||
switch (random.NextInt(4))
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// x = x + a
|
||||
int a = 0;
|
||||
float constProbability = 0f;
|
||||
ConstObfusUtil.LoadConstInt(a, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
return true;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// x = x * a * ra
|
||||
int a = random.NextInt() | 0x1; // Ensure a is not zero
|
||||
int ra = MathUtil.ModInverse32(a);
|
||||
float constProbability = 0.5f;
|
||||
ConstObfusUtil.LoadConstInt(a, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
ConstObfusUtil.LoadConstInt(ra, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
return true;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// x = (x * a + b) * ra - (b * ra)
|
||||
int a = random.NextInt() | 0x1; // Ensure a is not zero
|
||||
int ra = MathUtil.ModInverse32(a);
|
||||
int b = random.NextInt();
|
||||
int b_ra = -b * ra;
|
||||
float constProbability = 0.5f;
|
||||
ConstObfusUtil.LoadConstInt(a, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
ConstObfusUtil.LoadConstInt(b, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
ConstObfusUtil.LoadConstInt(ra, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
ConstObfusUtil.LoadConstInt(b_ra, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
return true;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
// x = ((x + a) * b + c) * rb - (a*b + c) * rb
|
||||
int a = random.NextInt();
|
||||
int b = random.NextInt() | 0x1; // Ensure b is not zero
|
||||
int rb = MathUtil.ModInverse32(b);
|
||||
int c = random.NextInt();
|
||||
int r = -(a * b + c) * rb;
|
||||
float constProbability = 0.5f;
|
||||
ConstObfusUtil.LoadConstInt(a, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
ConstObfusUtil.LoadConstInt(b, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
ConstObfusUtil.LoadConstInt(c, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
ConstObfusUtil.LoadConstInt(rb, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
ConstObfusUtil.LoadConstInt(r, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
return true;
|
||||
}
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ObfuscateLong(Instruction inst, List<Instruction> outputInsts, ObfusMethodContext ctx)
|
||||
{
|
||||
IRandom random = ctx.localRandom;
|
||||
switch (random.NextInt(4))
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// x = x + a
|
||||
long a = 0;
|
||||
float constProbability = 0f;
|
||||
ConstObfusUtil.LoadConstLong(a, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
return true;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// x = x * a * ra
|
||||
long a = random.NextLong() | 0x1L; // Ensure a is not zero
|
||||
long ra = MathUtil.ModInverse64(a);
|
||||
float constProbability = 0.5f;
|
||||
ConstObfusUtil.LoadConstLong(a, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
ConstObfusUtil.LoadConstLong(ra, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
return true;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// x = (x * a + b) * ra - (b * ra)
|
||||
long a = random.NextLong() | 0x1L; // Ensure a is not zero
|
||||
long ra = MathUtil.ModInverse64(a);
|
||||
long b = random.NextLong();
|
||||
long b_ra = -b * ra;
|
||||
float constProbability = 0.5f;
|
||||
ConstObfusUtil.LoadConstLong(a, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
ConstObfusUtil.LoadConstLong(b, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
ConstObfusUtil.LoadConstLong(ra, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
ConstObfusUtil.LoadConstLong(b_ra, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
return true;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
// x = ((x + a) * b + c) * rb - (a*b + c) * rb
|
||||
long a = random.NextLong();
|
||||
long b = random.NextLong() | 0x1L; // Ensure b is not zero
|
||||
long rb = MathUtil.ModInverse64(b);
|
||||
long c = random.NextLong();
|
||||
long r = -(a * b + c) * rb;
|
||||
float constProbability = 0.5f;
|
||||
ConstObfusUtil.LoadConstLong(a, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
ConstObfusUtil.LoadConstLong(b, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
ConstObfusUtil.LoadConstLong(c, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
ConstObfusUtil.LoadConstLong(rb, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
ConstObfusUtil.LoadConstLong(r, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
return true;
|
||||
}
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ObfuscateFloat(Instruction inst, List<Instruction> outputInsts, ObfusMethodContext ctx)
|
||||
{
|
||||
IRandom random = ctx.localRandom;
|
||||
switch (random.NextInt(3))
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// x = x + 0f
|
||||
float a = 0.0f;
|
||||
float constProbability = 0f;
|
||||
ConstObfusUtil.LoadConstFloat(a, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
return true;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// x = x * 1f;
|
||||
float a = 1.0f;
|
||||
float constProbability = 0f;
|
||||
ConstObfusUtil.LoadConstFloat(a, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
return true;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// x = (x + a) * b; a = 0.0f, b = 1.0f
|
||||
float a = 0.0f;
|
||||
float b = 1.0f;
|
||||
float constProbability = 0f;
|
||||
ConstObfusUtil.LoadConstFloat(a, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
ConstObfusUtil.LoadConstFloat(b, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
return true;
|
||||
}
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ObfuscateDouble(Instruction inst, List<Instruction> outputInsts, ObfusMethodContext ctx)
|
||||
{
|
||||
IRandom random = ctx.localRandom;
|
||||
switch (random.NextInt(3))
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// x = x + 0.0
|
||||
double a = 0.0;
|
||||
float constProbability = 0f;
|
||||
ConstObfusUtil.LoadConstDouble(a, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
return true;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// x = x * 1.0;
|
||||
double a = 1.0;
|
||||
float constProbability = 0f;
|
||||
ConstObfusUtil.LoadConstDouble(a, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
return true;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// x = (x + a) * b; a = 0.0, b = 1.0
|
||||
double a = 0.0;
|
||||
double b = 1.0;
|
||||
float constProbability = 0f;
|
||||
ConstObfusUtil.LoadConstDouble(a, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
ConstObfusUtil.LoadConstDouble(b, random, constProbability, ctx.constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
return true;
|
||||
}
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5200244f403139c40b578b2e845508f2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,114 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Data;
|
||||
using Obfuz.Emit;
|
||||
using Obfuz.Settings;
|
||||
using Obfuz.Utils;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.ObfusPasses.EvalStackObfus
|
||||
{
|
||||
class ObfusMethodContext
|
||||
{
|
||||
public MethodDef method;
|
||||
public EvalStackCalculator evalStackCalculator;
|
||||
public LocalVariableAllocator localVariableAllocator;
|
||||
public IRandom localRandom;
|
||||
public EncryptionScopeInfo encryptionScope;
|
||||
public DefaultMetadataImporter importer;
|
||||
public ConstFieldAllocator constFieldAllocator;
|
||||
public float obfuscationPercentage;
|
||||
}
|
||||
|
||||
internal class EvalStackObfusPass : ObfuscationMethodPassBase
|
||||
{
|
||||
private readonly EvalStackObfuscationSettingsFacade _settings;
|
||||
|
||||
private IObfuscationPolicy _obfuscationPolicy;
|
||||
private IObfuscator _obfuscator;
|
||||
|
||||
public EvalStackObfusPass(EvalStackObfuscationSettingsFacade settings)
|
||||
{
|
||||
_settings = settings;
|
||||
_obfuscator = new DefaultObfuscator();
|
||||
}
|
||||
|
||||
public override ObfuscationPassType Type => ObfuscationPassType.EvalStackObfus;
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
ObfuscationPassContext ctx = ObfuscationPassContext.Current;
|
||||
_obfuscationPolicy = new ConfigurableObfuscationPolicy(
|
||||
ctx.coreSettings.assembliesToObfuscate,
|
||||
_settings.ruleFiles);
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override bool NeedObfuscateMethod(MethodDef method)
|
||||
{
|
||||
return _obfuscationPolicy.NeedObfuscate(method);
|
||||
}
|
||||
|
||||
protected bool TryObfuscateInstruction(Instruction inst, EvalDataType dataType, List<Instruction> outputInstructions, ObfusMethodContext ctx)
|
||||
{
|
||||
switch (dataType)
|
||||
{
|
||||
case EvalDataType.Int32: return _obfuscator.ObfuscateInt(inst, outputInstructions, ctx);
|
||||
case EvalDataType.Int64: return _obfuscator.ObfuscateLong(inst, outputInstructions, ctx);
|
||||
case EvalDataType.Float: return _obfuscator.ObfuscateFloat(inst, outputInstructions, ctx);
|
||||
case EvalDataType.Double: return _obfuscator.ObfuscateDouble(inst, outputInstructions, ctx);
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ObfuscateData(MethodDef method)
|
||||
{
|
||||
//Debug.Log($"Obfuscating method: {method.FullName} with EvalStackObfusPass");
|
||||
IList<Instruction> instructions = method.Body.Instructions;
|
||||
var outputInstructions = new List<Instruction>();
|
||||
var totalFinalInstructions = new List<Instruction>();
|
||||
|
||||
ObfuscationPassContext ctx = ObfuscationPassContext.Current;
|
||||
var calc = new EvalStackCalculator(method);
|
||||
|
||||
GroupByModuleEntityManager moduleEntityManager = ctx.moduleEntityManager;
|
||||
var encryptionScope = moduleEntityManager.EncryptionScopeProvider.GetScope(method.Module);
|
||||
var ruleData = _obfuscationPolicy.GetObfuscationRuleData(method);
|
||||
var localRandom = encryptionScope.localRandomCreator(MethodEqualityComparer.CompareDeclaringTypes.GetHashCode(method));
|
||||
var obfusMethodCtx = new ObfusMethodContext
|
||||
{
|
||||
method = method,
|
||||
evalStackCalculator = calc,
|
||||
localVariableAllocator = new LocalVariableAllocator(method),
|
||||
encryptionScope = encryptionScope,
|
||||
constFieldAllocator = moduleEntityManager.GetEntity<ConstFieldAllocator>(method.Module),
|
||||
localRandom = localRandom,
|
||||
importer = moduleEntityManager.GetEntity<DefaultMetadataImporter>(method.Module),
|
||||
obfuscationPercentage = ruleData.obfuscationPercentage,
|
||||
};
|
||||
for (int i = 0; i < instructions.Count; i++)
|
||||
{
|
||||
Instruction inst = instructions[i];
|
||||
totalFinalInstructions.Add(inst);
|
||||
if (calc.TryGetPushResult(inst, out EvalDataType dataType) && localRandom.NextInPercentage(ruleData.obfuscationPercentage))
|
||||
{
|
||||
outputInstructions.Clear();
|
||||
if (TryObfuscateInstruction(inst, dataType, outputInstructions, obfusMethodCtx))
|
||||
{
|
||||
totalFinalInstructions.AddRange(outputInstructions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instructions.Clear();
|
||||
foreach (var obInst in totalFinalInstructions)
|
||||
{
|
||||
instructions.Add(obInst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9fa7d3313f260794da2cc36dadaf4fb4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,24 +0,0 @@
|
|||
using dnlib.DotNet.Emit;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.ObfusPasses.EvalStackObfus
|
||||
{
|
||||
interface IObfuscator
|
||||
{
|
||||
bool ObfuscateInt(Instruction inst, List<Instruction> outputInsts, ObfusMethodContext ctx);
|
||||
|
||||
bool ObfuscateLong(Instruction inst, List<Instruction> outputInsts, ObfusMethodContext ctx);
|
||||
|
||||
bool ObfuscateFloat(Instruction inst, List<Instruction> outputInsts, ObfusMethodContext ctx);
|
||||
|
||||
bool ObfuscateDouble(Instruction inst, List<Instruction> outputInsts, ObfusMethodContext ctx);
|
||||
}
|
||||
|
||||
abstract class ObfuscatorBase : IObfuscator
|
||||
{
|
||||
public abstract bool ObfuscateInt(Instruction inst, List<Instruction> outputInsts, ObfusMethodContext ctx);
|
||||
public abstract bool ObfuscateLong(Instruction inst, List<Instruction> outputInsts, ObfusMethodContext ctx);
|
||||
public abstract bool ObfuscateFloat(Instruction inst, List<Instruction> outputInsts, ObfusMethodContext ctx);
|
||||
public abstract bool ObfuscateDouble(Instruction inst, List<Instruction> outputInsts, ObfusMethodContext ctx);
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 17a9f3181d9711f4ca1d0cfb9e813bb0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,143 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using Obfuz.Conf;
|
||||
using Obfuz.Settings;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
|
||||
namespace Obfuz.ObfusPasses.ExprObfus
|
||||
{
|
||||
struct ObfuscationRuleData
|
||||
{
|
||||
public readonly ObfuscationLevel obfuscationLevel;
|
||||
public readonly float obfuscationPercentage;
|
||||
public ObfuscationRuleData(ObfuscationLevel level, float percentage)
|
||||
{
|
||||
obfuscationLevel = level;
|
||||
obfuscationPercentage = percentage;
|
||||
}
|
||||
}
|
||||
|
||||
interface IObfuscationPolicy
|
||||
{
|
||||
bool NeedObfuscate(MethodDef method);
|
||||
|
||||
ObfuscationRuleData GetObfuscationRuleData(MethodDef method);
|
||||
}
|
||||
|
||||
abstract class ObfuscationPolicyBase : IObfuscationPolicy
|
||||
{
|
||||
public abstract bool NeedObfuscate(MethodDef method);
|
||||
|
||||
public abstract ObfuscationRuleData GetObfuscationRuleData(MethodDef method);
|
||||
}
|
||||
|
||||
class ConfigurableObfuscationPolicy : ObfuscationPolicyBase
|
||||
{
|
||||
class ObfuscationRule : IRule<ObfuscationRule>
|
||||
{
|
||||
public ObfuscationLevel? obfuscationLevel;
|
||||
public float? obfuscationPercentage;
|
||||
|
||||
public void InheritParent(ObfuscationRule parentRule)
|
||||
{
|
||||
if (obfuscationLevel == null)
|
||||
obfuscationLevel = parentRule.obfuscationLevel;
|
||||
if (obfuscationPercentage == null)
|
||||
obfuscationPercentage = parentRule.obfuscationPercentage;
|
||||
}
|
||||
}
|
||||
|
||||
class MethodSpec : MethodRuleBase<ObfuscationRule>
|
||||
{
|
||||
}
|
||||
|
||||
class TypeSpec : TypeRuleBase<MethodSpec, ObfuscationRule>
|
||||
{
|
||||
}
|
||||
|
||||
class AssemblySpec : AssemblyRuleBase<TypeSpec, MethodSpec, ObfuscationRule>
|
||||
{
|
||||
}
|
||||
|
||||
private static readonly ObfuscationRule s_default = new ObfuscationRule()
|
||||
{
|
||||
obfuscationLevel = ObfuscationLevel.Basic,
|
||||
obfuscationPercentage = 0.3f,
|
||||
};
|
||||
|
||||
private ObfuscationRule _global;
|
||||
|
||||
private readonly XmlAssemblyTypeMethodRuleParser<AssemblySpec, TypeSpec, MethodSpec, ObfuscationRule> _xmlParser;
|
||||
|
||||
private readonly Dictionary<MethodDef, ObfuscationRule> _methodRuleCache = new Dictionary<MethodDef, ObfuscationRule>();
|
||||
|
||||
public ConfigurableObfuscationPolicy(List<string> toObfuscatedAssemblyNames, List<string> xmlConfigFiles)
|
||||
{
|
||||
_xmlParser = new XmlAssemblyTypeMethodRuleParser<AssemblySpec, TypeSpec, MethodSpec, ObfuscationRule>(
|
||||
toObfuscatedAssemblyNames, ParseObfuscationRule, ParseGlobal);
|
||||
LoadConfigs(xmlConfigFiles);
|
||||
}
|
||||
|
||||
private void LoadConfigs(List<string> configFiles)
|
||||
{
|
||||
_xmlParser.LoadConfigs(configFiles);
|
||||
|
||||
if (_global == null)
|
||||
{
|
||||
_global = s_default;
|
||||
}
|
||||
else
|
||||
{
|
||||
_global.InheritParent(s_default);
|
||||
}
|
||||
_xmlParser.InheritParentRules(_global);
|
||||
}
|
||||
|
||||
private void ParseGlobal(string configFile, XmlElement ele)
|
||||
{
|
||||
switch (ele.Name)
|
||||
{
|
||||
case "global": _global = ParseObfuscationRule(configFile, ele); break;
|
||||
default: throw new Exception($"Invalid xml file {configFile}, unknown node {ele.Name}");
|
||||
}
|
||||
}
|
||||
|
||||
private ObfuscationRule ParseObfuscationRule(string configFile, XmlElement ele)
|
||||
{
|
||||
var rule = new ObfuscationRule();
|
||||
if (ele.HasAttribute("obfuscationLevel"))
|
||||
{
|
||||
rule.obfuscationLevel = ConfigUtil.ParseObfuscationLevel(ele.GetAttribute("obfuscationLevel"));
|
||||
}
|
||||
if (ele.HasAttribute("obfuscationPercentage"))
|
||||
{
|
||||
rule.obfuscationPercentage = float.Parse(ele.GetAttribute("obfuscationPercentage"));
|
||||
}
|
||||
return rule;
|
||||
}
|
||||
|
||||
private ObfuscationRule GetMethodObfuscationRule(MethodDef method)
|
||||
{
|
||||
if (!_methodRuleCache.TryGetValue(method, out var rule))
|
||||
{
|
||||
rule = _xmlParser.GetMethodRule(method, _global);
|
||||
_methodRuleCache[method] = rule;
|
||||
}
|
||||
return rule;
|
||||
}
|
||||
|
||||
public override bool NeedObfuscate(MethodDef method)
|
||||
{
|
||||
ObfuscationRule rule = GetMethodObfuscationRule(method);
|
||||
return rule.obfuscationLevel.Value > ObfuscationLevel.None;
|
||||
}
|
||||
|
||||
public override ObfuscationRuleData GetObfuscationRuleData(MethodDef method)
|
||||
{
|
||||
var rule = GetMethodObfuscationRule(method);
|
||||
return new ObfuscationRuleData(rule.obfuscationLevel.Value, rule.obfuscationPercentage.Value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5f820a225c981b8499016958e6c69747
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,173 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Data;
|
||||
using Obfuz.Emit;
|
||||
using Obfuz.ObfusPasses.ExprObfus.Obfuscators;
|
||||
using Obfuz.Settings;
|
||||
using Obfuz.Utils;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.ObfusPasses.ExprObfus
|
||||
{
|
||||
class ObfusMethodContext
|
||||
{
|
||||
public MethodDef method;
|
||||
public EvalStackCalculator evalStackCalculator;
|
||||
public LocalVariableAllocator localVariableAllocator;
|
||||
public IRandom localRandom;
|
||||
public EncryptionScopeInfo encryptionScope;
|
||||
public DefaultMetadataImporter importer;
|
||||
public ConstFieldAllocator constFieldAllocator;
|
||||
public float obfuscationPercentage;
|
||||
}
|
||||
|
||||
class ExprObfusPass : ObfuscationMethodPassBase
|
||||
{
|
||||
private readonly ExprObfuscationSettingsFacade _settings;
|
||||
private readonly IObfuscator _basicObfuscator;
|
||||
private readonly IObfuscator _advancedObfuscator;
|
||||
private readonly IObfuscator _mostAdvancedObfuscator;
|
||||
|
||||
private IObfuscationPolicy _obfuscationPolicy;
|
||||
|
||||
public ExprObfusPass(ExprObfuscationSettingsFacade settings)
|
||||
{
|
||||
_settings = settings;
|
||||
_basicObfuscator = new BasicObfuscator();
|
||||
_advancedObfuscator = new AdvancedObfuscator();
|
||||
_mostAdvancedObfuscator = new MostAdvancedObfuscator();
|
||||
}
|
||||
|
||||
public override ObfuscationPassType Type => ObfuscationPassType.ExprObfus;
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
ObfuscationPassContext ctx = ObfuscationPassContext.Current;
|
||||
_obfuscationPolicy = new ConfigurableObfuscationPolicy(
|
||||
ctx.coreSettings.assembliesToObfuscate,
|
||||
_settings.ruleFiles);
|
||||
}
|
||||
|
||||
private IObfuscator GetObfuscator(ObfuscationLevel level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case ObfuscationLevel.None: return null;
|
||||
case ObfuscationLevel.Basic: return _basicObfuscator;
|
||||
case ObfuscationLevel.Advanced: return _advancedObfuscator;
|
||||
case ObfuscationLevel.MostAdvanced: return _mostAdvancedObfuscator;
|
||||
default: throw new System.ArgumentOutOfRangeException(nameof(level), level, "Unknown obfuscation level");
|
||||
}
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override bool NeedObfuscateMethod(MethodDef method)
|
||||
{
|
||||
return _obfuscationPolicy.NeedObfuscate(method);
|
||||
}
|
||||
|
||||
protected bool TryObfuscateInstruction(IObfuscator obfuscator, InstructionParameterInfo pi, Instruction inst, List<Instruction> outputInstructions, ObfusMethodContext ctx)
|
||||
{
|
||||
//Debug.Log($"Obfuscating instruction: {inst} in method: {ctx.method.FullName}");
|
||||
IRandom localRandom = ctx.localRandom;
|
||||
float obfuscationPercentage = ctx.obfuscationPercentage;
|
||||
switch (inst.OpCode.Code)
|
||||
{
|
||||
case Code.Neg:
|
||||
{
|
||||
return localRandom.NextInPercentage(obfuscationPercentage) && obfuscator.ObfuscateBasicUnaryOp(inst, pi.op1, pi.retType, outputInstructions, ctx);
|
||||
}
|
||||
case Code.Add:
|
||||
case Code.Sub:
|
||||
case Code.Mul:
|
||||
case Code.Div:
|
||||
case Code.Div_Un:
|
||||
case Code.Rem:
|
||||
case Code.Rem_Un:
|
||||
{
|
||||
return localRandom.NextInPercentage(obfuscationPercentage) && obfuscator.ObfuscateBasicBinOp(inst, pi.op1, pi.op2, pi.retType, outputInstructions, ctx);
|
||||
}
|
||||
case Code.And:
|
||||
case Code.Or:
|
||||
case Code.Xor:
|
||||
{
|
||||
return localRandom.NextInPercentage(obfuscationPercentage) && obfuscator.ObfuscateBinBitwiseOp(inst, pi.op1, pi.op2, pi.retType, outputInstructions, ctx);
|
||||
}
|
||||
case Code.Not:
|
||||
{
|
||||
return localRandom.NextInPercentage(obfuscationPercentage) && obfuscator.ObfuscateUnaryBitwiseOp(inst, pi.op1, pi.retType, outputInstructions, ctx);
|
||||
}
|
||||
case Code.Shl:
|
||||
case Code.Shr:
|
||||
case Code.Shr_Un:
|
||||
{
|
||||
return localRandom.NextInPercentage(obfuscationPercentage) && obfuscator.ObfuscateBitShiftOp(inst, pi.op1, pi.op2, pi.retType, outputInstructions, ctx);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void ObfuscateData(MethodDef method)
|
||||
{
|
||||
//Debug.Log($"Obfuscating method: {method.FullName} with ExprObfusPass");
|
||||
IList<Instruction> instructions = method.Body.Instructions;
|
||||
var outputInstructions = new List<Instruction>();
|
||||
var totalFinalInstructions = new List<Instruction>();
|
||||
|
||||
ObfuscationPassContext ctx = ObfuscationPassContext.Current;
|
||||
var calc = new EvalStackCalculator(method);
|
||||
|
||||
GroupByModuleEntityManager moduleEntityManager = ctx.moduleEntityManager;
|
||||
var encryptionScope = moduleEntityManager.EncryptionScopeProvider.GetScope(method.Module);
|
||||
var ruleData = _obfuscationPolicy.GetObfuscationRuleData(method);
|
||||
var obfuscator = GetObfuscator(ruleData.obfuscationLevel);
|
||||
var obfusMethodCtx = new ObfusMethodContext
|
||||
{
|
||||
method = method,
|
||||
evalStackCalculator = calc,
|
||||
localVariableAllocator = new LocalVariableAllocator(method),
|
||||
encryptionScope = encryptionScope,
|
||||
constFieldAllocator = moduleEntityManager.GetEntity<ConstFieldAllocator>(method.Module),
|
||||
localRandom = encryptionScope.localRandomCreator(MethodEqualityComparer.CompareDeclaringTypes.GetHashCode(method)),
|
||||
importer = moduleEntityManager.GetEntity<DefaultMetadataImporter>(method.Module),
|
||||
obfuscationPercentage = ruleData.obfuscationPercentage,
|
||||
};
|
||||
for (int i = 0; i < instructions.Count; i++)
|
||||
{
|
||||
Instruction inst = instructions[i];
|
||||
bool add = false;
|
||||
if (calc.TryGetParameterInfo(inst, out InstructionParameterInfo pi))
|
||||
{
|
||||
outputInstructions.Clear();
|
||||
if (TryObfuscateInstruction(obfuscator, pi, inst, outputInstructions, obfusMethodCtx))
|
||||
{
|
||||
// current instruction may be the target of control flow instruction, so we can't remove it directly.
|
||||
// we replace it with nop now, then remove it in CleanUpInstructionPass
|
||||
inst.OpCode = outputInstructions[0].OpCode;
|
||||
inst.Operand = outputInstructions[0].Operand;
|
||||
totalFinalInstructions.Add(inst);
|
||||
for (int k = 1; k < outputInstructions.Count; k++)
|
||||
{
|
||||
totalFinalInstructions.Add(outputInstructions[k]);
|
||||
}
|
||||
add = true;
|
||||
}
|
||||
}
|
||||
if (!add)
|
||||
{
|
||||
totalFinalInstructions.Add(inst);
|
||||
}
|
||||
}
|
||||
|
||||
instructions.Clear();
|
||||
foreach (var obInst in totalFinalInstructions)
|
||||
{
|
||||
instructions.Add(obInst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Emit;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.ObfusPasses.ExprObfus
|
||||
{
|
||||
interface IObfuscator
|
||||
{
|
||||
bool ObfuscateBasicUnaryOp(Instruction inst, EvalDataType op, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx);
|
||||
|
||||
bool ObfuscateBasicBinOp(Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx);
|
||||
|
||||
bool ObfuscateUnaryBitwiseOp(Instruction inst, EvalDataType op, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx);
|
||||
|
||||
bool ObfuscateBinBitwiseOp(Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx);
|
||||
|
||||
bool ObfuscateBitShiftOp(Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx);
|
||||
}
|
||||
|
||||
abstract class ObfuscatorBase : IObfuscator
|
||||
{
|
||||
public abstract bool ObfuscateBasicUnaryOp(Instruction inst, EvalDataType op, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx);
|
||||
public abstract bool ObfuscateBasicBinOp(Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx);
|
||||
public abstract bool ObfuscateUnaryBitwiseOp(Instruction inst, EvalDataType op, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx);
|
||||
public abstract bool ObfuscateBinBitwiseOp(Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx);
|
||||
public abstract bool ObfuscateBitShiftOp(Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx);
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a88981a87bcd9e84b883e39c81cfbf44
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,110 +0,0 @@
|
|||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Data;
|
||||
using Obfuz.Emit;
|
||||
using Obfuz.Utils;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.ObfusPasses.ExprObfus.Obfuscators
|
||||
{
|
||||
class AdvancedObfuscator : BasicObfuscator
|
||||
{
|
||||
protected bool GenerateIdentityTransformForArgument(Instruction inst, EvalDataType op, List<Instruction> outputInsts, ObfusMethodContext ctx)
|
||||
{
|
||||
IRandom random = ctx.localRandom;
|
||||
ConstFieldAllocator constFieldAllocator = ctx.constFieldAllocator;
|
||||
switch (op)
|
||||
{
|
||||
case EvalDataType.Int32:
|
||||
{
|
||||
// = x + y = x + (y * a + b) * ra + (-b * ra)
|
||||
int a = random.NextInt() | 0x1;
|
||||
int ra = MathUtil.ModInverse32(a);
|
||||
int b = random.NextInt();
|
||||
int b_ra = -b * ra;
|
||||
float constProbability = 0.5f;
|
||||
ConstObfusUtil.LoadConstInt(a, random, constProbability, constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
ConstObfusUtil.LoadConstInt(b, random, constProbability, constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
ConstObfusUtil.LoadConstInt(ra, random, constProbability, constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
ConstObfusUtil.LoadConstInt(b_ra, random, constProbability, constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
outputInsts.Add(inst.Clone());
|
||||
return true;
|
||||
}
|
||||
case EvalDataType.Int64:
|
||||
{
|
||||
// = x + y = x + (y * a + b) * ra + (-b * ra)
|
||||
long a = random.NextLong() | 0x1L;
|
||||
long ra = MathUtil.ModInverse64(a);
|
||||
long b = random.NextLong();
|
||||
long b_ra = -b * ra;
|
||||
float constProbability = 0.5f;
|
||||
ConstObfusUtil.LoadConstLong(a, random, constProbability, constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
ConstObfusUtil.LoadConstLong(b, random, constProbability, constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
ConstObfusUtil.LoadConstLong(ra, random, constProbability, constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
ConstObfusUtil.LoadConstLong(b_ra, random, constProbability, constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
outputInsts.Add(inst.Clone());
|
||||
return true;
|
||||
}
|
||||
case EvalDataType.Float:
|
||||
{
|
||||
// = x + y = x + (y + a) * b; a = 0.0f, b = 1.0f
|
||||
float a = 0.0f;
|
||||
float b = 1.0f;
|
||||
float constProbability = 0f;
|
||||
ConstObfusUtil.LoadConstFloat(a, random, constProbability, constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
ConstObfusUtil.LoadConstFloat(b, random, constProbability, constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
outputInsts.Add(inst.Clone());
|
||||
return true;
|
||||
}
|
||||
case EvalDataType.Double:
|
||||
{
|
||||
// = x + y = x + (y + a) * b; a = 0.0, b = 1.0
|
||||
double a = 0.0;
|
||||
double b = 1.0;
|
||||
float constProbability = 0f;
|
||||
ConstObfusUtil.LoadConstDouble(a, random, constProbability, constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Add));
|
||||
ConstObfusUtil.LoadConstDouble(b, random, constProbability, constFieldAllocator, outputInsts);
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Mul));
|
||||
outputInsts.Add(inst.Clone());
|
||||
return true;
|
||||
}
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ObfuscateBasicUnaryOp(Instruction inst, EvalDataType op, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx)
|
||||
{
|
||||
return GenerateIdentityTransformForArgument(inst, op, outputInsts, ctx) || base.ObfuscateBasicUnaryOp(inst, op, ret, outputInsts, ctx);
|
||||
}
|
||||
|
||||
public override bool ObfuscateBasicBinOp(Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx)
|
||||
{
|
||||
return GenerateIdentityTransformForArgument(inst, op2, outputInsts, ctx) || base.ObfuscateBasicBinOp(inst, op1, op2, ret, outputInsts, ctx);
|
||||
}
|
||||
|
||||
public override bool ObfuscateUnaryBitwiseOp(Instruction inst, EvalDataType op, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx)
|
||||
{
|
||||
return GenerateIdentityTransformForArgument(inst, op, outputInsts, ctx) || base.ObfuscateUnaryBitwiseOp(inst, op, ret, outputInsts, ctx);
|
||||
}
|
||||
|
||||
public override bool ObfuscateBinBitwiseOp(Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx)
|
||||
{
|
||||
return GenerateIdentityTransformForArgument(inst, op2, outputInsts, ctx) || base.ObfuscateBinBitwiseOp(inst, op1, op2, ret, outputInsts, ctx);
|
||||
}
|
||||
|
||||
public override bool ObfuscateBitShiftOp(Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx)
|
||||
{
|
||||
return GenerateIdentityTransformForArgument(inst, op2, outputInsts, ctx) || base.ObfuscateBitShiftOp(inst, op1, op2, ret, outputInsts, ctx);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ef717515402ca2f41a52db7ea1300f32
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,282 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Emit;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obfuz.ObfusPasses.ExprObfus.Obfuscators
|
||||
{
|
||||
|
||||
class BasicObfuscator : ObfuscatorBase
|
||||
{
|
||||
private IMethod GetUnaryOpMethod(DefaultMetadataImporter importer, Code code, EvalDataType op1)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case Code.Neg:
|
||||
{
|
||||
switch (op1)
|
||||
{
|
||||
case EvalDataType.Int32: return importer.NegInt;
|
||||
case EvalDataType.Int64: return importer.NegLong;
|
||||
case EvalDataType.Float: return importer.NegFloat;
|
||||
case EvalDataType.Double: return importer.NegDouble;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
case Code.Not:
|
||||
{
|
||||
switch (op1)
|
||||
{
|
||||
case EvalDataType.Int32: return importer.NotInt;
|
||||
case EvalDataType.Int64: return importer.NotLong;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
private IMethod GetBinaryOpMethod(DefaultMetadataImporter importer, Code code, EvalDataType op1, EvalDataType op2)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case Code.Add:
|
||||
{
|
||||
switch (op1)
|
||||
{
|
||||
case EvalDataType.Int32: return op2 == op1 ? importer.AddInt : null;
|
||||
case EvalDataType.Int64: return op2 == op1 ? importer.AddLong : null;
|
||||
case EvalDataType.Float: return op2 == op1 ? importer.AddFloat : null;
|
||||
case EvalDataType.Double: return op2 == op1 ? importer.AddDouble : null;
|
||||
case EvalDataType.I:
|
||||
{
|
||||
switch (op2)
|
||||
{
|
||||
case EvalDataType.I: return importer.AddIntPtr;
|
||||
case EvalDataType.Int32: return importer.AddIntPtrInt;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
case Code.Sub:
|
||||
{
|
||||
switch (op1)
|
||||
{
|
||||
case EvalDataType.Int32: return op2 == op1 ? importer.SubtractInt : null;
|
||||
case EvalDataType.Int64: return op2 == op1 ? importer.SubtractLong : null;
|
||||
case EvalDataType.Float: return op2 == op1 ? importer.SubtractFloat : null;
|
||||
case EvalDataType.Double: return op2 == op1 ? importer.SubtractDouble : null;
|
||||
case EvalDataType.I:
|
||||
{
|
||||
switch (op2)
|
||||
{
|
||||
case EvalDataType.I: return importer.SubtractIntPtr;
|
||||
case EvalDataType.Int32: return importer.SubtractIntPtrInt;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
case Code.Mul:
|
||||
{
|
||||
switch (op1)
|
||||
{
|
||||
case EvalDataType.Int32: return op2 == op1 ? importer.MultiplyInt : null;
|
||||
case EvalDataType.Int64: return op2 == op1 ? importer.MultiplyLong : null;
|
||||
case EvalDataType.Float: return op2 == op1 ? importer.MultiplyFloat : null;
|
||||
case EvalDataType.Double: return op2 == op1 ? importer.MultiplyDouble : null;
|
||||
case EvalDataType.I:
|
||||
{
|
||||
switch (op2)
|
||||
{
|
||||
case EvalDataType.I: return importer.MultiplyIntPtr;
|
||||
case EvalDataType.Int32: return importer.MultiplyIntPtrInt;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
case Code.Div:
|
||||
{
|
||||
switch (op1)
|
||||
{
|
||||
case EvalDataType.Int32: return importer.DivideInt;
|
||||
case EvalDataType.Int64: return importer.DivideLong;
|
||||
case EvalDataType.Float: return importer.DivideFloat;
|
||||
case EvalDataType.Double: return importer.DivideDouble;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
case Code.Div_Un:
|
||||
{
|
||||
switch (op1)
|
||||
{
|
||||
case EvalDataType.Int32: return importer.DivideUnInt;
|
||||
case EvalDataType.Int64: return importer.DivideUnLong;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
case Code.Rem:
|
||||
{
|
||||
switch (op1)
|
||||
{
|
||||
case EvalDataType.Int32: return importer.RemInt;
|
||||
case EvalDataType.Int64: return importer.RemLong;
|
||||
case EvalDataType.Float: return importer.RemFloat;
|
||||
case EvalDataType.Double: return importer.RemDouble;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
case Code.Rem_Un:
|
||||
{
|
||||
switch (op1)
|
||||
{
|
||||
case EvalDataType.Int32: return importer.RemUnInt;
|
||||
case EvalDataType.Int64: return importer.RemUnLong;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
case Code.Neg:
|
||||
{
|
||||
switch (op1)
|
||||
{
|
||||
case EvalDataType.Int32: return importer.NegInt;
|
||||
case EvalDataType.Int64: return importer.NegLong;
|
||||
case EvalDataType.Float: return importer.NegFloat;
|
||||
case EvalDataType.Double: return importer.NegDouble;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
case Code.And:
|
||||
{
|
||||
switch (op1)
|
||||
{
|
||||
case EvalDataType.Int32: return importer.AndInt;
|
||||
case EvalDataType.Int64: return importer.AndLong;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
case Code.Or:
|
||||
{
|
||||
switch (op1)
|
||||
{
|
||||
case EvalDataType.Int32: return importer.OrInt;
|
||||
case EvalDataType.Int64: return importer.OrLong;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
case Code.Xor:
|
||||
{
|
||||
switch (op1)
|
||||
{
|
||||
case EvalDataType.Int32: return importer.XorInt;
|
||||
case EvalDataType.Int64: return importer.XorLong;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
case Code.Not:
|
||||
{
|
||||
switch (op1)
|
||||
{
|
||||
case EvalDataType.Int32: return importer.NotInt;
|
||||
case EvalDataType.Int64: return importer.NotLong;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
case Code.Shl:
|
||||
{
|
||||
switch (op1)
|
||||
{
|
||||
case EvalDataType.Int32: return importer.ShlInt;
|
||||
case EvalDataType.Int64: return importer.ShlLong;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
case Code.Shr:
|
||||
{
|
||||
switch (op1)
|
||||
{
|
||||
case EvalDataType.Int32: return importer.ShrInt;
|
||||
case EvalDataType.Int64: return importer.ShrLong;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
case Code.Shr_Un:
|
||||
{
|
||||
switch (op1)
|
||||
{
|
||||
case EvalDataType.Int32: return importer.ShrUnInt;
|
||||
case EvalDataType.Int64: return importer.ShrUnLong;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ObfuscateBasicUnaryOp(Instruction inst, EvalDataType op, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx)
|
||||
{
|
||||
IMethod opMethod = GetUnaryOpMethod(ctx.importer, inst.OpCode.Code, op);
|
||||
if (opMethod == null)
|
||||
{
|
||||
Debug.LogWarning($"BasicObfuscator: Cannot obfuscate unary operation {inst.OpCode.Code} with different operand types: op={op}. This is a limitation of the BasicObfuscator.");
|
||||
return false;
|
||||
}
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Call, opMethod));
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool ObfuscateBasicBinOp(Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx)
|
||||
{
|
||||
IMethod opMethod = GetBinaryOpMethod(ctx.importer, inst.OpCode.Code, op1, op2);
|
||||
if (opMethod == null)
|
||||
{
|
||||
Debug.LogWarning($"BasicObfuscator: Cannot obfuscate binary operation {inst.OpCode.Code} with different operand types: op1={op1}, op2={op2}, ret={ret}. This is a limitation of the BasicObfuscator.");
|
||||
return false;
|
||||
}
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Call, opMethod));
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool ObfuscateUnaryBitwiseOp(Instruction inst, EvalDataType op, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx)
|
||||
{
|
||||
IMethod opMethod = GetUnaryOpMethod(ctx.importer, inst.OpCode.Code, op);
|
||||
if (opMethod == null)
|
||||
{
|
||||
Debug.LogWarning($"BasicObfuscator: Cannot obfuscate unary operation {inst.OpCode.Code} with different operand types: op={op}. This is a limitation of the BasicObfuscator.");
|
||||
return false;
|
||||
}
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Call, opMethod));
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool ObfuscateBinBitwiseOp(Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx)
|
||||
{
|
||||
IMethod opMethod = GetBinaryOpMethod(ctx.importer, inst.OpCode.Code, op1, op2);
|
||||
if (opMethod == null)
|
||||
{
|
||||
Debug.LogWarning($"BasicObfuscator: Cannot obfuscate binary operation {inst.OpCode.Code} with different operand types: op1={op1}, op2={op2}, ret={ret}. This is a limitation of the BasicObfuscator.");
|
||||
return false;
|
||||
}
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Call, opMethod));
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool ObfuscateBitShiftOp(Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx)
|
||||
{
|
||||
IMethod opMethod = GetBinaryOpMethod(ctx.importer, inst.OpCode.Code, op1, op2);
|
||||
if (opMethod == null)
|
||||
{
|
||||
Debug.LogWarning($"BasicObfuscator: Cannot obfuscate binary operation {inst.OpCode.Code} with operand type {op2}. This is a limitation of the BasicObfuscator.");
|
||||
return false;
|
||||
}
|
||||
outputInsts.Add(Instruction.Create(OpCodes.Call, opMethod));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 578caeae17526b54c9ff1979d897feb7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,83 +0,0 @@
|
|||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Emit;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Obfuz.ObfusPasses.ExprObfus.Obfuscators
|
||||
{
|
||||
class MostAdvancedObfuscator : AdvancedObfuscator
|
||||
{
|
||||
private readonly BasicObfuscator _basicObfuscator = new BasicObfuscator();
|
||||
|
||||
public override bool ObfuscateBasicUnaryOp(Instruction inst, EvalDataType op, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx)
|
||||
{
|
||||
if (!base.ObfuscateBasicUnaryOp(inst, op, ret, outputInsts, ctx))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (outputInsts.Last().OpCode.Code != inst.OpCode.Code)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
outputInsts.RemoveAt(outputInsts.Count - 1);
|
||||
return _basicObfuscator.ObfuscateBasicUnaryOp(inst, op, ret, outputInsts, ctx);
|
||||
}
|
||||
|
||||
public override bool ObfuscateBasicBinOp(Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx)
|
||||
{
|
||||
if (!base.ObfuscateBasicBinOp(inst, op1, op2, ret, outputInsts, ctx))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (outputInsts.Last().OpCode.Code != inst.OpCode.Code)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
outputInsts.RemoveAt(outputInsts.Count - 1);
|
||||
return _basicObfuscator.ObfuscateBasicBinOp(inst, op1, op2, ret, outputInsts, ctx);
|
||||
}
|
||||
|
||||
public override bool ObfuscateUnaryBitwiseOp(Instruction inst, EvalDataType op, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx)
|
||||
{
|
||||
if (!base.ObfuscateUnaryBitwiseOp(inst, op, ret, outputInsts, ctx))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (outputInsts.Last().OpCode.Code != inst.OpCode.Code)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
outputInsts.RemoveAt(outputInsts.Count - 1);
|
||||
return _basicObfuscator.ObfuscateUnaryBitwiseOp(inst, op, ret, outputInsts, ctx);
|
||||
}
|
||||
|
||||
public override bool ObfuscateBinBitwiseOp(Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx)
|
||||
{
|
||||
if (!base.ObfuscateBinBitwiseOp(inst, op1, op2, ret, outputInsts, ctx))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (outputInsts.Last().OpCode.Code != inst.OpCode.Code)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
outputInsts.RemoveAt(outputInsts.Count - 1);
|
||||
return _basicObfuscator.ObfuscateBinBitwiseOp(inst, op1, op2, ret, outputInsts, ctx);
|
||||
}
|
||||
|
||||
public override bool ObfuscateBitShiftOp(Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, List<Instruction> outputInsts, ObfusMethodContext ctx)
|
||||
{
|
||||
if (!base.ObfuscateBitShiftOp(inst, op1, op2, ret, outputInsts, ctx))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (outputInsts.Last().OpCode.Code != inst.OpCode.Code)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
outputInsts.RemoveAt(outputInsts.Count - 1);
|
||||
return _basicObfuscator.ObfuscateBitShiftOp(inst, op1, op2, ret, outputInsts, ctx);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: af5946ac6cb0a8b4fa75321439785133
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bb4f71e54c6a07341883ba0c642505c1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,138 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Editor;
|
||||
using Obfuz.Emit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace Obfuz.ObfusPasses.Instinct
|
||||
{
|
||||
|
||||
public class InstinctPass : InstructionObfuscationPassBase
|
||||
{
|
||||
public override ObfuscationPassType Type => ObfuscationPassType.None;
|
||||
|
||||
protected override bool ForceProcessAllAssembliesAndIgnoreAllPolicy => true;
|
||||
|
||||
public InstinctPass()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override bool NeedObfuscateMethod(MethodDef method)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private string GetTypeName(TypeSig type)
|
||||
{
|
||||
type = type.RemovePinnedAndModifiers();
|
||||
switch (type.ElementType)
|
||||
{
|
||||
case ElementType.Class:
|
||||
case ElementType.ValueType:
|
||||
{
|
||||
return type.ReflectionName;
|
||||
}
|
||||
case ElementType.GenericInst:
|
||||
{
|
||||
type = ((GenericInstSig)type).GenericType;
|
||||
return type.ReflectionName;
|
||||
}
|
||||
default: return type.ReflectionName;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetTypeFullName(TypeSig type)
|
||||
{
|
||||
type = type.RemovePinnedAndModifiers();
|
||||
|
||||
switch (type.ElementType)
|
||||
{
|
||||
case ElementType.Class:
|
||||
case ElementType.ValueType:
|
||||
{
|
||||
return type.ReflectionFullName;
|
||||
}
|
||||
case ElementType.GenericInst:
|
||||
{
|
||||
GenericInstSig genericInstSig = (GenericInstSig)type;
|
||||
var typeName = new StringBuilder(genericInstSig.GenericType.ReflectionFullName);
|
||||
typeName.Append("<").Append(string.Join(",", genericInstSig.GenericArguments.Select(GetTypeFullName))).Append(">");
|
||||
return typeName.ToString();
|
||||
}
|
||||
default: return type.ReflectionFullName;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool TryObfuscateInstruction(MethodDef callingMethod, Instruction inst, IList<Instruction> instructions, int instructionIndex, List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions)
|
||||
{
|
||||
Code code = inst.OpCode.Code;
|
||||
if (!(inst.Operand is IMethod method) || !method.IsMethod)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
MethodDef methodDef = method.ResolveMethodDef();
|
||||
if (methodDef == null || methodDef.DeclaringType.Name != "ObfuscationInstincts" || methodDef.DeclaringType.DefinitionAssembly.Name != ConstValues.ObfuzRuntimeAssemblyName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ObfuscationPassContext ctx = ObfuscationPassContext.Current;
|
||||
var importer = ctx.moduleEntityManager.GetEntity<DefaultMetadataImporter>(callingMethod.Module);
|
||||
|
||||
string methodName = methodDef.Name;
|
||||
switch (methodName)
|
||||
{
|
||||
case "FullNameOf":
|
||||
case "NameOf":
|
||||
case "RegisterReflectionType":
|
||||
{
|
||||
MethodSpec methodSpec = (MethodSpec)method;
|
||||
GenericInstMethodSig gims = methodSpec.GenericInstMethodSig;
|
||||
Assert.AreEqual(1, gims.GenericArguments.Count, "FullNameOf should have exactly one generic argument");
|
||||
TypeSig type = gims.GenericArguments[0];
|
||||
switch (methodName)
|
||||
{
|
||||
case "FullNameOf":
|
||||
{
|
||||
string typeFullName = GetTypeFullName(type);
|
||||
outputInstructions.Add(Instruction.Create(OpCodes.Ldstr, typeFullName));
|
||||
break;
|
||||
}
|
||||
case "NameOf":
|
||||
{
|
||||
string typeName = GetTypeName(type);
|
||||
outputInstructions.Add(Instruction.Create(OpCodes.Ldstr, typeName));
|
||||
break;
|
||||
}
|
||||
case "RegisterReflectionType":
|
||||
{
|
||||
string typeFullName = GetTypeFullName(type);
|
||||
outputInstructions.Add(Instruction.Create(OpCodes.Ldstr, typeFullName));
|
||||
var finalMethod = new MethodSpecUser((IMethodDefOrRef)importer.ObfuscationTypeMapperRegisterType, gims);
|
||||
outputInstructions.Add(Instruction.Create(OpCodes.Call, finalMethod));
|
||||
break;
|
||||
}
|
||||
default: throw new NotSupportedException($"Unsupported instinct method: {methodDef.FullName}");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: throw new NotSupportedException($"Unsupported instinct method: {methodDef.FullName}");
|
||||
}
|
||||
//Debug.Log($"memory encrypt field: {field}");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 08027d16e09664c40b561715ef9326fc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,46 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.ObfusPasses
|
||||
{
|
||||
public abstract class InstructionObfuscationPassBase : ObfuscationMethodPassBase
|
||||
{
|
||||
protected abstract bool TryObfuscateInstruction(MethodDef callingMethod, Instruction inst, IList<Instruction> instructions, int instructionIndex,
|
||||
List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions);
|
||||
|
||||
protected override void ObfuscateData(MethodDef method)
|
||||
{
|
||||
IList<Instruction> instructions = method.Body.Instructions;
|
||||
var outputInstructions = new List<Instruction>();
|
||||
var totalFinalInstructions = new List<Instruction>();
|
||||
for (int i = 0; i < instructions.Count; i++)
|
||||
{
|
||||
Instruction inst = instructions[i];
|
||||
outputInstructions.Clear();
|
||||
if (TryObfuscateInstruction(method, inst, instructions, i, outputInstructions, totalFinalInstructions))
|
||||
{
|
||||
// current instruction may be the target of control flow instruction, so we can't remove it directly.
|
||||
// we replace it with nop now, then remove it in CleanUpInstructionPass
|
||||
inst.OpCode = outputInstructions[0].OpCode;
|
||||
inst.Operand = outputInstructions[0].Operand;
|
||||
totalFinalInstructions.Add(inst);
|
||||
for (int k = 1; k < outputInstructions.Count; k++)
|
||||
{
|
||||
totalFinalInstructions.Add(outputInstructions[k]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
totalFinalInstructions.Add(inst);
|
||||
}
|
||||
}
|
||||
|
||||
instructions.Clear();
|
||||
foreach (var obInst in totalFinalInstructions)
|
||||
{
|
||||
instructions.Add(obInst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using System.Linq;
|
||||
|
||||
namespace Obfuz.ObfusPasses
|
||||
{
|
||||
public abstract class ObfuscationMethodPassBase : ObfuscationPassBase
|
||||
{
|
||||
protected virtual bool ForceProcessAllAssembliesAndIgnoreAllPolicy => false;
|
||||
|
||||
protected abstract bool NeedObfuscateMethod(MethodDef method);
|
||||
|
||||
protected abstract void ObfuscateData(MethodDef method);
|
||||
|
||||
public override void Process()
|
||||
{
|
||||
var ctx = ObfuscationPassContext.Current;
|
||||
var modules = ForceProcessAllAssembliesAndIgnoreAllPolicy ? ctx.allObfuscationRelativeModules : ctx.modulesToObfuscate;
|
||||
ObfuscationMethodWhitelist whiteList = ctx.whiteList;
|
||||
ConfigurablePassPolicy passPolicy = ctx.passPolicy;
|
||||
foreach (ModuleDef mod in modules)
|
||||
{
|
||||
if (!ForceProcessAllAssembliesAndIgnoreAllPolicy && whiteList.IsInWhiteList(mod))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// ToArray to avoid modify list exception
|
||||
foreach (TypeDef type in mod.GetTypes().ToArray())
|
||||
{
|
||||
if (!ForceProcessAllAssembliesAndIgnoreAllPolicy && whiteList.IsInWhiteList(type))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// ToArray to avoid modify list exception
|
||||
foreach (MethodDef method in type.Methods.ToArray())
|
||||
{
|
||||
if (!method.HasBody || (!ForceProcessAllAssembliesAndIgnoreAllPolicy && (ctx.whiteList.IsInWhiteList(method) || !Support(passPolicy.GetMethodObfuscationPasses(method)) || !NeedObfuscateMethod(method))))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// TODO if isGeneratedBy Obfuscator, continue
|
||||
ObfuscateData(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 84b0592af70b0cc41b546cf8ac39f889
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d2c28a04d2997bc4d91a4c7693983d12
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,34 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using Obfuz.Conf;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
|
||||
namespace Obfuz.ObfusPasses.RemoveConstField
|
||||
{
|
||||
public class ConfigurableRemoveConstFieldPolicy : RemoveConstFieldBase
|
||||
{
|
||||
class ObfuscationRule
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private readonly XmlFieldRuleParser<ObfuscationRule> _configParser;
|
||||
|
||||
public ConfigurableRemoveConstFieldPolicy(List<string> toObfuscatedAssemblyNames, List<string> configFiles)
|
||||
{
|
||||
_configParser = new XmlFieldRuleParser<ObfuscationRule>(toObfuscatedAssemblyNames, ParseRule, null);
|
||||
_configParser.LoadConfigs(configFiles);
|
||||
}
|
||||
|
||||
private ObfuscationRule ParseRule(string configFile, XmlElement ele)
|
||||
{
|
||||
return new ObfuscationRule();
|
||||
}
|
||||
|
||||
public override bool NeedPreserved(FieldDef field)
|
||||
{
|
||||
var rule = _configParser.GetFieldRule(field);
|
||||
return rule != null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e3b708559fbf755419d7daf4ddce72f2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,14 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
|
||||
namespace Obfuz.ObfusPasses.RemoveConstField
|
||||
{
|
||||
public interface IRemoveConstFieldPolicy
|
||||
{
|
||||
bool NeedPreserved(FieldDef field);
|
||||
}
|
||||
|
||||
public abstract class RemoveConstFieldBase : IRemoveConstFieldPolicy
|
||||
{
|
||||
public abstract bool NeedPreserved(FieldDef field);
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5fad0b7ef9225b24cacc94f8dcaee26d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,73 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using Obfuz.Settings;
|
||||
using Obfuz.Utils;
|
||||
using System.Linq;
|
||||
|
||||
namespace Obfuz.ObfusPasses.RemoveConstField
|
||||
{
|
||||
|
||||
public class RemoveConstFieldPass : ObfuscationPassBase
|
||||
{
|
||||
private RemoveConstFieldSettingsFacade _settings;
|
||||
private ObfuzIgnoreScopeComputeCache _obfuzIgnoreScopeComputeCache;
|
||||
private IRemoveConstFieldPolicy _removeConstFieldPolicy;
|
||||
|
||||
public override ObfuscationPassType Type => ObfuscationPassType.RemoveConstField;
|
||||
|
||||
public RemoveConstFieldPass(RemoveConstFieldSettingsFacade settings)
|
||||
{
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
var ctx = ObfuscationPassContext.Current;
|
||||
_obfuzIgnoreScopeComputeCache = ctx.obfuzIgnoreScopeComputeCache;
|
||||
_removeConstFieldPolicy = new ConfigurableRemoveConstFieldPolicy(ctx.coreSettings.assembliesToObfuscate, _settings.ruleFiles);
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void Process()
|
||||
{
|
||||
var ctx = ObfuscationPassContext.Current;
|
||||
var modules = ctx.modulesToObfuscate;
|
||||
ConfigurablePassPolicy passPolicy = ctx.passPolicy;
|
||||
foreach (ModuleDef mod in modules)
|
||||
{
|
||||
// ToArray to avoid modify list exception
|
||||
foreach (TypeDef type in mod.GetTypes())
|
||||
{
|
||||
if (type.IsEnum)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foreach (FieldDef field in type.Fields.ToArray())
|
||||
{
|
||||
if (!field.IsLiteral)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!Support(passPolicy.GetFieldObfuscationPasses(field)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (_obfuzIgnoreScopeComputeCache.HasSelfOrDeclaringOrEnclosingOrInheritObfuzIgnoreScope(field, field.DeclaringType, ObfuzScope.Field))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (_removeConstFieldPolicy.NeedPreserved(field))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
field.DeclaringType = null;
|
||||
//Debug.Log($"Remove const field {field.FullName} in type {type.FullName} in module {mod.Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3188de094ab4cdd47b55c2f622251cf5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,29 +0,0 @@
|
|||
namespace Obfuz.ObfusPasses.SymbolObfus.NameMakers
|
||||
{
|
||||
public class DebugNameMaker : NameMakerBase
|
||||
{
|
||||
private class DebugNameScope : INameScope
|
||||
{
|
||||
|
||||
public bool AddPreservedName(string name)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public string GetNewName(string originalName, bool reuse)
|
||||
{
|
||||
return $"${originalName}";
|
||||
}
|
||||
|
||||
public bool IsNamePreserved(string name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override INameScope CreateNameScope()
|
||||
{
|
||||
return new DebugNameScope();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,814 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obfuz.ObfusPasses.SymbolObfus.Policies
|
||||
{
|
||||
|
||||
public class ConfigurableRenamePolicy : ObfuscationPolicyBase
|
||||
{
|
||||
enum ModifierType
|
||||
{
|
||||
None = 0x0,
|
||||
Private = 0x1,
|
||||
Protected = 0x2,
|
||||
Public = 0x4,
|
||||
}
|
||||
|
||||
class MethodRuleSpec
|
||||
{
|
||||
public NameMatcher nameMatcher;
|
||||
public ModifierType? modifierType;
|
||||
public bool? obfuscateName;
|
||||
}
|
||||
|
||||
class FieldRuleSpec
|
||||
{
|
||||
public NameMatcher nameMatcher;
|
||||
public ModifierType? modifierType;
|
||||
public bool? obfuscateName;
|
||||
}
|
||||
|
||||
class PropertyRuleSpec
|
||||
{
|
||||
public NameMatcher nameMatcher;
|
||||
public ModifierType? modifierType;
|
||||
public bool? obfuscateName;
|
||||
public ObfuzScope? applyToMembers;
|
||||
}
|
||||
|
||||
class EventRuleSpec
|
||||
{
|
||||
public NameMatcher nameMatcher;
|
||||
public ModifierType? modifierType;
|
||||
public bool? obfuscateName;
|
||||
public ObfuzScope? applyToMembers;
|
||||
}
|
||||
|
||||
class TypeRuleSpec
|
||||
{
|
||||
public NameMatcher nameMatcher;
|
||||
public ModifierType? modifierType;
|
||||
public ClassType? classType;
|
||||
public List<string> inheritTypes;
|
||||
public List<string> hasCustomAttributes;
|
||||
public bool? obfuscateName;
|
||||
public ObfuzScope? applyToMembers;
|
||||
public bool applyToNestedTypes;
|
||||
|
||||
public List<FieldRuleSpec> fields;
|
||||
public List<MethodRuleSpec> methods;
|
||||
public List<PropertyRuleSpec> properties;
|
||||
public List<EventRuleSpec> events;
|
||||
}
|
||||
|
||||
class AssemblyRuleSpec
|
||||
{
|
||||
public string assemblyName;
|
||||
public List<TypeRuleSpec> types;
|
||||
}
|
||||
|
||||
private readonly Dictionary<string, List<AssemblyRuleSpec>> _assemblyRuleSpecs = new Dictionary<string, List<AssemblyRuleSpec>>();
|
||||
|
||||
private AssemblyRuleSpec ParseAssembly(XmlElement ele)
|
||||
{
|
||||
string assemblyName = ele.GetAttribute("name");
|
||||
if (string.IsNullOrEmpty(assemblyName))
|
||||
{
|
||||
throw new Exception($"Invalid xml file, assembly name is empty");
|
||||
}
|
||||
if (!_obfuscationAssemblyNames.Contains(assemblyName))
|
||||
{
|
||||
throw new Exception($"unknown assembly name:{assemblyName}, not in ObfuzSettings.obfuscationAssemblyNames");
|
||||
}
|
||||
var rule = new AssemblyRuleSpec()
|
||||
{
|
||||
assemblyName = assemblyName,
|
||||
types = new List<TypeRuleSpec>(),
|
||||
};
|
||||
|
||||
foreach (XmlNode node in ele.ChildNodes)
|
||||
{
|
||||
if (!(node is XmlElement childElement))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (childElement.Name != "type")
|
||||
{
|
||||
throw new Exception($"Invalid xml file, unknown node {childElement.Name}");
|
||||
}
|
||||
TypeRuleSpec type = ParseType(childElement);
|
||||
rule.types.Add(type);
|
||||
}
|
||||
return rule;
|
||||
}
|
||||
|
||||
private enum ClassType
|
||||
{
|
||||
None = 0x0,
|
||||
Class = 0x1,
|
||||
Struct = 0x2,
|
||||
Interface = 0x4,
|
||||
Enum = 0x8,
|
||||
Delegate = 0x10,
|
||||
}
|
||||
|
||||
private ClassType? ParseClassType(string classType)
|
||||
{
|
||||
if (string.IsNullOrEmpty(classType))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ClassType type = ClassType.None;
|
||||
foreach (var s in classType.Split(','))
|
||||
{
|
||||
switch (s)
|
||||
{
|
||||
case "class": type |= ClassType.Class; break;
|
||||
case "struct": type |= ClassType.Struct; break;
|
||||
case "interface": type |= ClassType.Interface; break;
|
||||
case "enum": type |= ClassType.Enum; break;
|
||||
case "delegate": type |= ClassType.Delegate; break;
|
||||
default: throw new Exception($"Invalid class type {s}");
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
private ModifierType? ParseModifierType(string modifierType)
|
||||
{
|
||||
if (string.IsNullOrEmpty(modifierType))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ModifierType type = ModifierType.None;
|
||||
foreach (var s in modifierType.Split(','))
|
||||
{
|
||||
switch (s)
|
||||
{
|
||||
case "public": type |= ModifierType.Public; break;
|
||||
case "protected": type |= ModifierType.Protected; break;
|
||||
case "private": type |= ModifierType.Private; break;
|
||||
default: throw new Exception($"Invalid modifier type {s}");
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
private ObfuzScope? ParseApplyToMembersScope(string membersScopeStr)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(membersScopeStr))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ObfuzScope scope = ObfuzScope.None;
|
||||
|
||||
foreach (string s in membersScopeStr.Split(','))
|
||||
{
|
||||
var s2 = s.Trim().ToLowerInvariant();
|
||||
switch (s2)
|
||||
{
|
||||
case "none": break;
|
||||
case "field": scope |= ObfuzScope.Field; break;
|
||||
case "eventname": scope |= ObfuzScope.EventName; break;
|
||||
case "eventaddremovefirename": scope |= ObfuzScope.EventAddRemoveFireName; break;
|
||||
case "event": scope |= ObfuzScope.Event; break;
|
||||
case "methodname": scope |= ObfuzScope.MethodName; break;
|
||||
case "method": scope |= ObfuzScope.MethodName; break;
|
||||
case "propertyname": scope |= ObfuzScope.PropertyName; break;
|
||||
case "propertygettersettername": scope |= ObfuzScope.PropertyGetterSetterName; break;
|
||||
case "property": scope |= ObfuzScope.Property; break;
|
||||
case "all":
|
||||
case "*": scope |= ObfuzScope.All; break;
|
||||
default:
|
||||
{
|
||||
throw new Exception($"Invalid applyToMembers scope {s2}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
private List<string> ParseTypes(string inheritStr)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(inheritStr))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var inheritTypes = new List<string>();
|
||||
foreach (var s in inheritStr.Split(','))
|
||||
{
|
||||
var trimmed = s.Trim();
|
||||
if (!string.IsNullOrEmpty(trimmed))
|
||||
{
|
||||
inheritTypes.Add(trimmed);
|
||||
}
|
||||
}
|
||||
return inheritTypes;
|
||||
}
|
||||
|
||||
private TypeRuleSpec ParseType(XmlElement element)
|
||||
{
|
||||
var rule = new TypeRuleSpec();
|
||||
|
||||
rule.nameMatcher = new NameMatcher(element.GetAttribute("name"));
|
||||
rule.obfuscateName = ConfigUtil.ParseNullableBool(element.GetAttribute("obName"));
|
||||
rule.applyToMembers = ParseApplyToMembersScope(element.GetAttribute("applyToMembers"));
|
||||
rule.applyToNestedTypes = ConfigUtil.ParseNullableBool(element.GetAttribute("applyToNestedTypes")) ?? true;
|
||||
rule.modifierType = ParseModifierType(element.GetAttribute("modifier"));
|
||||
rule.classType = ParseClassType(element.GetAttribute("classType"));
|
||||
rule.inheritTypes = ParseTypes(element.GetAttribute("inherit"));
|
||||
rule.hasCustomAttributes = ParseTypes(element.GetAttribute("hasCustomAttributes"));
|
||||
|
||||
//rule.nestTypeRuleSpecs = new List<TypeRuleSpec>();
|
||||
rule.fields = new List<FieldRuleSpec>();
|
||||
rule.methods = new List<MethodRuleSpec>();
|
||||
rule.properties = new List<PropertyRuleSpec>();
|
||||
rule.events = new List<EventRuleSpec>();
|
||||
foreach (XmlNode node in element.ChildNodes)
|
||||
{
|
||||
if (!(node is XmlElement childElement))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
switch (childElement.Name)
|
||||
{
|
||||
case "field":
|
||||
{
|
||||
var fieldRuleSpec = new FieldRuleSpec();
|
||||
fieldRuleSpec.nameMatcher = new NameMatcher(childElement.GetAttribute("name"));
|
||||
fieldRuleSpec.modifierType = ParseModifierType(childElement.GetAttribute("modifier"));
|
||||
fieldRuleSpec.obfuscateName = ConfigUtil.ParseNullableBool(childElement.GetAttribute("obName"));
|
||||
rule.fields.Add(fieldRuleSpec);
|
||||
break;
|
||||
}
|
||||
case "method":
|
||||
{
|
||||
var methodRuleSpec = new MethodRuleSpec();
|
||||
methodRuleSpec.nameMatcher = new NameMatcher(childElement.GetAttribute("name"));
|
||||
methodRuleSpec.modifierType = ParseModifierType(childElement.GetAttribute("modifier"));
|
||||
methodRuleSpec.obfuscateName = ConfigUtil.ParseNullableBool(childElement.GetAttribute("obName"));
|
||||
rule.methods.Add(methodRuleSpec);
|
||||
break;
|
||||
}
|
||||
case "property":
|
||||
{
|
||||
var propertyRulerSpec = new PropertyRuleSpec();
|
||||
propertyRulerSpec.nameMatcher = new NameMatcher(childElement.GetAttribute("name"));
|
||||
propertyRulerSpec.modifierType = ParseModifierType(childElement.GetAttribute("modifier"));
|
||||
propertyRulerSpec.obfuscateName = ConfigUtil.ParseNullableBool(childElement.GetAttribute("obName"));
|
||||
propertyRulerSpec.applyToMembers = ParseApplyToMembersScope(childElement.GetAttribute("applyToMembers"));
|
||||
rule.properties.Add(propertyRulerSpec);
|
||||
break;
|
||||
}
|
||||
case "event":
|
||||
{
|
||||
var eventRuleSpec = new EventRuleSpec();
|
||||
eventRuleSpec.nameMatcher = new NameMatcher(childElement.GetAttribute("name"));
|
||||
eventRuleSpec.modifierType = ParseModifierType(childElement.GetAttribute("modifier"));
|
||||
eventRuleSpec.obfuscateName = ConfigUtil.ParseNullableBool(childElement.GetAttribute("obName"));
|
||||
eventRuleSpec.applyToMembers = ParseApplyToMembersScope(childElement.GetAttribute("applyToMembers"));
|
||||
rule.events.Add(eventRuleSpec);
|
||||
break;
|
||||
}
|
||||
default: throw new Exception($"Invalid xml file, unknown node {childElement.Name} in type node");
|
||||
}
|
||||
}
|
||||
return rule;
|
||||
}
|
||||
|
||||
private void LoadXmls(List<string> xmlFiles)
|
||||
{
|
||||
var rawAssemblySpecElements = new List<XmlElement>();
|
||||
foreach (string file in xmlFiles)
|
||||
{
|
||||
LoadRawXml(file, rawAssemblySpecElements);
|
||||
}
|
||||
ResolveAssemblySpecs(rawAssemblySpecElements);
|
||||
}
|
||||
|
||||
private void ResolveAssemblySpecs(List<XmlElement> rawAssemblySpecElements)
|
||||
{
|
||||
foreach (XmlElement ele in rawAssemblySpecElements)
|
||||
{
|
||||
var assemblyRule = ParseAssembly(ele);
|
||||
if (!_assemblyRuleSpecs.TryGetValue(assemblyRule.assemblyName, out var existAssemblyRules))
|
||||
{
|
||||
existAssemblyRules = new List<AssemblyRuleSpec>();
|
||||
_assemblyRuleSpecs.Add(assemblyRule.assemblyName, existAssemblyRules);
|
||||
}
|
||||
existAssemblyRules.Add(assemblyRule);
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadRawXml(string xmlFile, List<XmlElement> rawAssemblyElements)
|
||||
{
|
||||
Debug.Log($"ObfuscateRule::LoadXml {xmlFile}");
|
||||
var doc = new XmlDocument();
|
||||
doc.Load(xmlFile);
|
||||
var root = doc.DocumentElement;
|
||||
if (root.Name != "obfuz")
|
||||
{
|
||||
throw new Exception($"Invalid xml file {xmlFile}, root name should be 'obfuz'");
|
||||
}
|
||||
foreach (XmlNode node in root.ChildNodes)
|
||||
{
|
||||
if (!(node is XmlElement element))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
switch (element.Name)
|
||||
{
|
||||
case "assembly":
|
||||
{
|
||||
rawAssemblyElements.Add(element);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new Exception($"Invalid xml file {xmlFile}, unknown node {element.Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ModifierType ComputeModifierType(TypeAttributes visibility)
|
||||
{
|
||||
if (visibility == TypeAttributes.NotPublic || visibility == TypeAttributes.NestedPrivate)
|
||||
{
|
||||
return ModifierType.Private;
|
||||
}
|
||||
if (visibility == TypeAttributes.Public || visibility == TypeAttributes.NestedPublic)
|
||||
{
|
||||
return ModifierType.Public;
|
||||
}
|
||||
return ModifierType.Protected;
|
||||
}
|
||||
|
||||
private ModifierType ComputeModifierType(FieldAttributes access)
|
||||
{
|
||||
if (access == FieldAttributes.Private || access == FieldAttributes.PrivateScope)
|
||||
{
|
||||
return ModifierType.Private;
|
||||
}
|
||||
if (access == FieldAttributes.Public)
|
||||
{
|
||||
return ModifierType.Public;
|
||||
}
|
||||
return ModifierType.Protected;
|
||||
}
|
||||
|
||||
//private ModifierType ComputeModifierType(MethodAttributes access)
|
||||
//{
|
||||
// if (access == MethodAttributes.Private || access == MethodAttributes.PrivateScope)
|
||||
// {
|
||||
// return ModifierType.Private;
|
||||
// }
|
||||
// if (access == MethodAttributes.Public)
|
||||
// {
|
||||
// return ModifierType.Public;
|
||||
// }
|
||||
// return ModifierType.Protected;
|
||||
//}
|
||||
|
||||
private bool MatchModifier(ModifierType? modifierType, TypeDef typeDef)
|
||||
{
|
||||
return modifierType == null || (modifierType & ComputeModifierType(typeDef.Visibility)) != 0;
|
||||
}
|
||||
|
||||
private bool MatchModifier(ModifierType? modifierType, FieldDef fieldDef)
|
||||
{
|
||||
return modifierType == null || (modifierType & ComputeModifierType(fieldDef.Access)) != 0;
|
||||
}
|
||||
|
||||
private bool MatchModifier(ModifierType? modifierType, MethodDef methodDef)
|
||||
{
|
||||
return modifierType == null || (modifierType & ComputeModifierType((FieldAttributes)methodDef.Access)) != 0;
|
||||
}
|
||||
|
||||
private bool MatchModifier(ModifierType? modifierType, PropertyDef propertyDef)
|
||||
{
|
||||
FieldAttributes access = default;
|
||||
if (propertyDef.GetMethod != null)
|
||||
{
|
||||
access |= (FieldAttributes)propertyDef.GetMethod.Access;
|
||||
}
|
||||
if (propertyDef.SetMethod != null)
|
||||
{
|
||||
access |= (FieldAttributes)propertyDef.SetMethod.Access;
|
||||
}
|
||||
return modifierType == null || (modifierType & ComputeModifierType(access)) != 0;
|
||||
}
|
||||
|
||||
private bool MatchModifier(ModifierType? modifierType, EventDef eventDef)
|
||||
{
|
||||
FieldAttributes access = default;
|
||||
if (eventDef.AddMethod != null)
|
||||
{
|
||||
access |= (FieldAttributes)eventDef.AddMethod.Access;
|
||||
}
|
||||
if (eventDef.RemoveMethod != null)
|
||||
{
|
||||
access |= (FieldAttributes)eventDef.RemoveMethod.Access;
|
||||
}
|
||||
if (eventDef.InvokeMethod != null)
|
||||
{
|
||||
access |= (FieldAttributes)eventDef.InvokeMethod.Access;
|
||||
}
|
||||
return modifierType == null || (modifierType & ComputeModifierType(access)) != 0;
|
||||
}
|
||||
|
||||
private class MethodComputeCache
|
||||
{
|
||||
public bool obfuscateName = true;
|
||||
public bool obfuscateParam = true;
|
||||
public bool obfuscateBody = true;
|
||||
}
|
||||
|
||||
private class RuleResult
|
||||
{
|
||||
public bool? obfuscateName;
|
||||
}
|
||||
|
||||
private readonly Dictionary<TypeDef, RuleResult> _typeSpecCache = new Dictionary<TypeDef, RuleResult>();
|
||||
private readonly Dictionary<MethodDef, RuleResult> _methodSpecCache = new Dictionary<MethodDef, RuleResult>();
|
||||
private readonly Dictionary<FieldDef, RuleResult> _fieldSpecCache = new Dictionary<FieldDef, RuleResult>();
|
||||
private readonly Dictionary<PropertyDef, RuleResult> _propertySpecCache = new Dictionary<PropertyDef, RuleResult>();
|
||||
private readonly Dictionary<EventDef, RuleResult> _eventSpecCache = new Dictionary<EventDef, RuleResult>();
|
||||
|
||||
|
||||
private readonly HashSet<string> _obfuscationAssemblyNames;
|
||||
private readonly List<ModuleDef> _assembliesToObfuscate;
|
||||
|
||||
public ConfigurableRenamePolicy(List<string> obfuscationAssemblyNames, List<ModuleDef> assembliesToObfuscate, List<string> xmlFiles)
|
||||
{
|
||||
_obfuscationAssemblyNames = new HashSet<string>(obfuscationAssemblyNames);
|
||||
_assembliesToObfuscate = assembliesToObfuscate;
|
||||
LoadXmls(xmlFiles);
|
||||
BuildRuleResultCaches();
|
||||
}
|
||||
|
||||
private bool MatchClassType(ClassType? classType, TypeDef typeDef)
|
||||
{
|
||||
if (classType == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (typeDef.IsInterface && (classType & ClassType.Interface) != 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (typeDef.IsEnum && (classType & ClassType.Enum) != 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (typeDef.IsDelegate && (classType & ClassType.Delegate) != 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (typeDef.IsValueType && !typeDef.IsEnum && (classType & ClassType.Struct) != 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!typeDef.IsValueType && !typeDef.IsInterface && !typeDef.IsDelegate && (classType & ClassType.Class) != 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private RuleResult GetOrCreateTypeRuleResult(TypeDef typeDef)
|
||||
{
|
||||
if (!_typeSpecCache.TryGetValue(typeDef, out var ruleResult))
|
||||
{
|
||||
ruleResult = new RuleResult();
|
||||
_typeSpecCache.Add(typeDef, ruleResult);
|
||||
}
|
||||
return ruleResult;
|
||||
}
|
||||
|
||||
private RuleResult GetOrCreateFieldRuleResult(FieldDef field)
|
||||
{
|
||||
if (!_fieldSpecCache.TryGetValue(field, out var ruleResult))
|
||||
{
|
||||
ruleResult = new RuleResult();
|
||||
_fieldSpecCache.Add(field, ruleResult);
|
||||
}
|
||||
return ruleResult;
|
||||
}
|
||||
|
||||
private RuleResult GetOrCreateMethodRuleResult(MethodDef method)
|
||||
{
|
||||
if (!_methodSpecCache.TryGetValue(method, out var ruleResult))
|
||||
{
|
||||
ruleResult = new RuleResult();
|
||||
_methodSpecCache.Add(method, ruleResult);
|
||||
}
|
||||
return ruleResult;
|
||||
}
|
||||
|
||||
private RuleResult GetOrCreatePropertyRuleResult(PropertyDef property)
|
||||
{
|
||||
if (!_propertySpecCache.TryGetValue(property, out var ruleResult))
|
||||
{
|
||||
ruleResult = new RuleResult();
|
||||
_propertySpecCache.Add(property, ruleResult);
|
||||
}
|
||||
return ruleResult;
|
||||
}
|
||||
|
||||
private RuleResult GetOrCreateEventRuleResult(EventDef eventDef)
|
||||
{
|
||||
if (!_eventSpecCache.TryGetValue(eventDef, out var ruleResult))
|
||||
{
|
||||
ruleResult = new RuleResult();
|
||||
_eventSpecCache.Add(eventDef, ruleResult);
|
||||
}
|
||||
return ruleResult;
|
||||
}
|
||||
|
||||
private void BuildTypeRuleResult(TypeRuleSpec typeSpec, TypeDef typeDef, RuleResult typeRuleResult)
|
||||
{
|
||||
string typeName = typeDef.FullName;
|
||||
|
||||
if (typeSpec.obfuscateName != null)
|
||||
{
|
||||
typeRuleResult.obfuscateName = typeSpec.obfuscateName;
|
||||
}
|
||||
|
||||
foreach (var fieldDef in typeDef.Fields)
|
||||
{
|
||||
RuleResult fieldRuleResult = GetOrCreateFieldRuleResult(fieldDef);
|
||||
if (typeSpec.applyToMembers != null && (typeSpec.applyToMembers & ObfuzScope.Field) != 0 && typeSpec.obfuscateName != null)
|
||||
{
|
||||
fieldRuleResult.obfuscateName = typeSpec.obfuscateName;
|
||||
}
|
||||
foreach (var fieldSpec in typeSpec.fields)
|
||||
{
|
||||
if (fieldSpec.nameMatcher.IsMatch(fieldDef.Name) && MatchModifier(fieldSpec.modifierType, fieldDef))
|
||||
{
|
||||
if (fieldSpec.obfuscateName != null)
|
||||
{
|
||||
fieldRuleResult.obfuscateName = fieldSpec.obfuscateName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (MethodDef methodDef in typeDef.Methods)
|
||||
{
|
||||
RuleResult methodRuleResult = GetOrCreateMethodRuleResult(methodDef);
|
||||
if (typeSpec.applyToMembers != null && (typeSpec.applyToMembers & ObfuzScope.Method) != 0 && typeSpec.obfuscateName != null)
|
||||
{
|
||||
methodRuleResult.obfuscateName = typeSpec.obfuscateName;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var eventDef in typeDef.Events)
|
||||
{
|
||||
RuleResult eventRuleResult = GetOrCreateEventRuleResult(eventDef);
|
||||
if (typeSpec.applyToMembers != null && (typeSpec.applyToMembers & ObfuzScope.EventName) != 0 && typeSpec.obfuscateName != null)
|
||||
{
|
||||
eventRuleResult.obfuscateName = typeSpec.obfuscateName;
|
||||
}
|
||||
foreach (var eventSpec in typeSpec.events)
|
||||
{
|
||||
if (!eventSpec.nameMatcher.IsMatch(eventDef.Name) || !MatchModifier(eventSpec.modifierType, eventDef))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (typeSpec.obfuscateName != null && typeSpec.applyToMembers != null && (typeSpec.applyToMembers & ObfuzScope.EventAddRemoveFireName) != 0)
|
||||
{
|
||||
if (eventDef.AddMethod != null)
|
||||
{
|
||||
GetOrCreateMethodRuleResult(eventDef.AddMethod).obfuscateName = typeSpec.obfuscateName;
|
||||
}
|
||||
if (eventDef.RemoveMethod != null)
|
||||
{
|
||||
GetOrCreateMethodRuleResult(eventDef.RemoveMethod).obfuscateName = typeSpec.obfuscateName;
|
||||
}
|
||||
if (eventDef.InvokeMethod != null)
|
||||
{
|
||||
GetOrCreateMethodRuleResult(eventDef.InvokeMethod).obfuscateName = typeSpec.obfuscateName;
|
||||
}
|
||||
}
|
||||
if (eventSpec.obfuscateName != null)
|
||||
{
|
||||
eventRuleResult.obfuscateName = eventSpec.obfuscateName;
|
||||
if (eventSpec.applyToMembers != null && (eventSpec.applyToMembers & ObfuzScope.EventAddRemoveFireName) != 0)
|
||||
{
|
||||
if (eventDef.AddMethod != null)
|
||||
{
|
||||
GetOrCreateMethodRuleResult(eventDef.AddMethod).obfuscateName = eventSpec.obfuscateName;
|
||||
}
|
||||
if (eventDef.RemoveMethod != null)
|
||||
{
|
||||
GetOrCreateMethodRuleResult(eventDef.RemoveMethod).obfuscateName = eventSpec.obfuscateName;
|
||||
}
|
||||
if (eventDef.InvokeMethod != null)
|
||||
{
|
||||
GetOrCreateMethodRuleResult(eventDef.InvokeMethod).obfuscateName = eventSpec.obfuscateName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var propertyDef in typeDef.Properties)
|
||||
{
|
||||
RuleResult propertyRuleResult = GetOrCreatePropertyRuleResult(propertyDef);
|
||||
if (typeSpec.applyToMembers != null && (typeSpec.applyToMembers & ObfuzScope.PropertyName) != 0 && typeSpec.obfuscateName != null)
|
||||
{
|
||||
propertyRuleResult.obfuscateName = typeSpec.obfuscateName;
|
||||
}
|
||||
foreach (var propertySpec in typeSpec.properties)
|
||||
{
|
||||
if (!propertySpec.nameMatcher.IsMatch(propertyDef.Name) || !MatchModifier(propertySpec.modifierType, propertyDef))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (typeSpec.obfuscateName != null && typeSpec.applyToMembers != null && (typeSpec.applyToMembers & ObfuzScope.PropertyGetterSetterName) != 0)
|
||||
{
|
||||
if (propertyDef.GetMethod != null)
|
||||
{
|
||||
GetOrCreateMethodRuleResult(propertyDef.GetMethod).obfuscateName = typeSpec.obfuscateName;
|
||||
}
|
||||
if (propertyDef.SetMethod != null)
|
||||
{
|
||||
GetOrCreateMethodRuleResult(propertyDef.SetMethod).obfuscateName = typeSpec.obfuscateName;
|
||||
}
|
||||
}
|
||||
if (propertySpec.obfuscateName != null)
|
||||
{
|
||||
propertyRuleResult.obfuscateName = propertySpec.obfuscateName;
|
||||
if (propertySpec.applyToMembers != null && (propertySpec.applyToMembers & ObfuzScope.PropertyGetterSetterName) != 0)
|
||||
{
|
||||
if (propertyDef.GetMethod != null)
|
||||
{
|
||||
GetOrCreateMethodRuleResult(propertyDef.GetMethod).obfuscateName = propertySpec.obfuscateName;
|
||||
}
|
||||
if (propertyDef.SetMethod != null)
|
||||
{
|
||||
GetOrCreateMethodRuleResult(propertyDef.SetMethod).obfuscateName = propertySpec.obfuscateName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (MethodDef methodDef in typeDef.Methods)
|
||||
{
|
||||
RuleResult methodRuleResult = GetOrCreateMethodRuleResult(methodDef);
|
||||
foreach (MethodRuleSpec methodSpec in typeSpec.methods)
|
||||
{
|
||||
if (!methodSpec.nameMatcher.IsMatch(methodDef.Name) || !MatchModifier(methodSpec.modifierType, methodDef))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (methodSpec.obfuscateName != null)
|
||||
{
|
||||
methodRuleResult.obfuscateName = methodSpec.obfuscateName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeSpec.applyToNestedTypes)
|
||||
{
|
||||
foreach (TypeDef nestedType in typeDef.NestedTypes)
|
||||
{
|
||||
var nestedRuleResult = GetOrCreateTypeRuleResult(nestedType);
|
||||
BuildTypeRuleResult(typeSpec, nestedType, nestedRuleResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool MatchInheritTypes(List<string> inheritTypes, TypeDef typeDef)
|
||||
{
|
||||
if (inheritTypes == null || inheritTypes.Count == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
TypeDef currentType = typeDef;
|
||||
while (currentType != null)
|
||||
{
|
||||
if (inheritTypes.Contains(currentType.FullName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
foreach (var interfaceType in currentType.Interfaces)
|
||||
{
|
||||
if (inheritTypes.Contains(interfaceType.Interface.FullName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
currentType = MetaUtil.GetBaseTypeDef(currentType);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool MatchCustomAttributes(List<string> customAttributes, TypeDef typeDef)
|
||||
{
|
||||
if (customAttributes == null || customAttributes.Count == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
foreach (string customAttributeName in customAttributes)
|
||||
{
|
||||
if (typeDef.CustomAttributes.Find(customAttributeName) != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private IEnumerable<TypeDef> GetMatchTypes(ModuleDef mod, List<TypeDef> types, TypeRuleSpec typeSpec)
|
||||
{
|
||||
if (typeSpec.nameMatcher.IsWildcardPattern)
|
||||
{
|
||||
foreach (var typeDef in types)
|
||||
{
|
||||
if (!typeSpec.nameMatcher.IsMatch(typeDef.FullName)
|
||||
|| !MatchModifier(typeSpec.modifierType, typeDef)
|
||||
|| !MatchClassType(typeSpec.classType, typeDef)
|
||||
|| !MatchInheritTypes(typeSpec.inheritTypes, typeDef)
|
||||
|| !MatchCustomAttributes(typeSpec.hasCustomAttributes, typeDef))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
yield return typeDef;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TypeDef typeDef = mod.FindNormal(typeSpec.nameMatcher.NameOrPattern);
|
||||
if (typeDef != null
|
||||
&& MatchModifier(typeSpec.modifierType, typeDef)
|
||||
&& MatchClassType(typeSpec.classType, typeDef)
|
||||
&& MatchInheritTypes(typeSpec.inheritTypes, typeDef)
|
||||
&& MatchCustomAttributes(typeSpec.hasCustomAttributes, typeDef))
|
||||
{
|
||||
yield return typeDef;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildRuleResultCaches()
|
||||
{
|
||||
foreach (AssemblyRuleSpec assSpec in _assemblyRuleSpecs.Values.SelectMany(arr => arr))
|
||||
{
|
||||
ModuleDef module = _assembliesToObfuscate.FirstOrDefault(m => m.Assembly.Name == assSpec.assemblyName);
|
||||
if (module == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
List<TypeDef> types = module.GetTypes().ToList();
|
||||
foreach (TypeRuleSpec typeSpec in assSpec.types)
|
||||
{
|
||||
foreach (var typeDef in GetMatchTypes(module, types, typeSpec))
|
||||
{
|
||||
var ruleResult = GetOrCreateTypeRuleResult(typeDef);
|
||||
if (typeSpec.obfuscateName != null)
|
||||
{
|
||||
ruleResult.obfuscateName = typeSpec.obfuscateName;
|
||||
}
|
||||
BuildTypeRuleResult(typeSpec, typeDef, ruleResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool NeedRename(TypeDef typeDef)
|
||||
{
|
||||
return GetOrCreateTypeRuleResult(typeDef).obfuscateName != false;
|
||||
}
|
||||
|
||||
public override bool NeedRename(MethodDef methodDef)
|
||||
{
|
||||
return GetOrCreateMethodRuleResult(methodDef).obfuscateName != false;
|
||||
}
|
||||
|
||||
public override bool NeedRename(FieldDef fieldDef)
|
||||
{
|
||||
return GetOrCreateFieldRuleResult(fieldDef).obfuscateName != false;
|
||||
}
|
||||
|
||||
public override bool NeedRename(PropertyDef propertyDef)
|
||||
{
|
||||
return GetOrCreatePropertyRuleResult(propertyDef).obfuscateName != false;
|
||||
}
|
||||
|
||||
public override bool NeedRename(EventDef eventDef)
|
||||
{
|
||||
return GetOrCreateEventRuleResult(eventDef).obfuscateName != false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using Obfuz.Editor;
|
||||
using Obfuz.Utils;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.ObfusPasses.SymbolObfus.Policies
|
||||
{
|
||||
public class SystemRenamePolicy : ObfuscationPolicyBase
|
||||
{
|
||||
private readonly ObfuzIgnoreScopeComputeCache _obfuzIgnoreScopeComputeCache;
|
||||
|
||||
public SystemRenamePolicy(ObfuzIgnoreScopeComputeCache obfuzIgnoreScopeComputeCache)
|
||||
{
|
||||
_obfuzIgnoreScopeComputeCache = obfuzIgnoreScopeComputeCache;
|
||||
}
|
||||
|
||||
private readonly HashSet<string> _fullIgnoreTypeFullNames = new HashSet<string>
|
||||
{
|
||||
ConstValues.ObfuzIgnoreAttributeFullName,
|
||||
ConstValues.ObfuzScopeFullName,
|
||||
ConstValues.EncryptFieldAttributeFullName,
|
||||
ConstValues.EmbeddedAttributeFullName,
|
||||
ConstValues.ZluaLuaInvokeAttributeFullName,
|
||||
ConstValues.ZluaLuaCallbackAttributeFullName,
|
||||
ConstValues.ZluaLuaMarshalAsAttributeFullName,
|
||||
ConstValues.BurstCompileFullName,
|
||||
};
|
||||
|
||||
|
||||
private readonly HashSet<string> _fullIgnoreTypeNames = new HashSet<string>
|
||||
{
|
||||
ConstValues.MonoPInvokeCallbackAttributeName,
|
||||
};
|
||||
|
||||
private bool IsFullIgnoreObfuscatedType(TypeDef typeDef)
|
||||
{
|
||||
return _fullIgnoreTypeFullNames.Contains(typeDef.FullName) || _fullIgnoreTypeNames.Contains(typeDef.Name) || MetaUtil.HasMicrosoftCodeAnalysisEmbeddedAttribute(typeDef);
|
||||
}
|
||||
|
||||
public override bool NeedRename(TypeDef typeDef)
|
||||
{
|
||||
string name = typeDef.Name;
|
||||
if (name == "<Module>")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (IsFullIgnoreObfuscatedType(typeDef))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_obfuzIgnoreScopeComputeCache.HasSelfOrEnclosingOrInheritObfuzIgnoreScope(typeDef, ObfuzScope.TypeName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool NeedRename(MethodDef methodDef)
|
||||
{
|
||||
if (methodDef.DeclaringType.IsDelegate || IsFullIgnoreObfuscatedType(methodDef.DeclaringType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (methodDef.Name == ".ctor" || methodDef.Name == ".cctor")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_obfuzIgnoreScopeComputeCache.HasSelfOrInheritPropertyOrEventOrOrTypeDefIgnoreMethodName(methodDef))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool NeedRename(FieldDef fieldDef)
|
||||
{
|
||||
if (fieldDef.DeclaringType.IsDelegate || IsFullIgnoreObfuscatedType(fieldDef.DeclaringType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (_obfuzIgnoreScopeComputeCache.HasSelfOrDeclaringOrEnclosingOrInheritObfuzIgnoreScope(fieldDef, fieldDef.DeclaringType, ObfuzScope.Field))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (fieldDef.DeclaringType.IsEnum && !fieldDef.IsStatic)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool NeedRename(PropertyDef propertyDef)
|
||||
{
|
||||
if (propertyDef.DeclaringType.IsDelegate || IsFullIgnoreObfuscatedType(propertyDef.DeclaringType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (_obfuzIgnoreScopeComputeCache.HasSelfOrDeclaringOrEnclosingOrInheritObfuzIgnoreScope(propertyDef, propertyDef.DeclaringType, ObfuzScope.PropertyName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool NeedRename(EventDef eventDef)
|
||||
{
|
||||
if (eventDef.DeclaringType.IsDelegate || IsFullIgnoreObfuscatedType(eventDef.DeclaringType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (_obfuzIgnoreScopeComputeCache.HasSelfOrDeclaringOrEnclosingOrInheritObfuzIgnoreScope(eventDef, eventDef.DeclaringType, ObfuzScope.EventName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,264 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using Obfuz.Utils;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Obfuz.ObfusPasses.SymbolObfus.Policies
|
||||
{
|
||||
|
||||
public class UnityRenamePolicy : ObfuscationPolicyBase
|
||||
{
|
||||
private static HashSet<string> s_monoBehaviourEvents = new HashSet<string> {
|
||||
|
||||
// MonoBehaviour events
|
||||
"Awake",
|
||||
"FixedUpdate",
|
||||
"LateUpdate",
|
||||
"OnAnimatorIK",
|
||||
|
||||
"OnAnimatorMove",
|
||||
"OnApplicationFocus",
|
||||
"OnApplicationPause",
|
||||
"OnApplicationQuit",
|
||||
"OnAudioFilterRead",
|
||||
|
||||
"OnBecameVisible",
|
||||
"OnBecameInvisible",
|
||||
|
||||
"OnCollisionEnter",
|
||||
"OnCollisionEnter2D",
|
||||
"OnCollisionExit",
|
||||
"OnCollisionExit2D",
|
||||
"OnCollisionStay",
|
||||
"OnCollisionStay2D",
|
||||
"OnConnectedToServer",
|
||||
"OnControllerColliderHit",
|
||||
|
||||
"OnDrawGizmos",
|
||||
"OnDrawGizmosSelected",
|
||||
"OnDestroy",
|
||||
"OnDisable",
|
||||
"OnDisconnectedFromServer",
|
||||
|
||||
"OnEnable",
|
||||
|
||||
"OnFailedToConnect",
|
||||
"OnFailedToConnectToMasterServer",
|
||||
|
||||
"OnGUI",
|
||||
|
||||
"OnJointBreak",
|
||||
"OnJointBreak2D",
|
||||
|
||||
"OnMasterServerEvent",
|
||||
"OnMouseDown",
|
||||
"OnMouseDrag",
|
||||
"OnMouseEnter",
|
||||
"OnMouseExit",
|
||||
"OnMouseOver",
|
||||
"OnMouseUp",
|
||||
"OnMouseUpAsButton",
|
||||
|
||||
"OnNetworkInstantiate",
|
||||
|
||||
"OnParticleSystemStopped",
|
||||
"OnParticleTrigger",
|
||||
"OnParticleUpdateJobScheduled",
|
||||
"OnPlayerConnected",
|
||||
"OnPlayerDisconnected",
|
||||
"OnPostRender",
|
||||
"OnPreCull",
|
||||
"OnPreRender",
|
||||
"OnRenderImage",
|
||||
"OnRenderObject",
|
||||
|
||||
"OnSerializeNetworkView",
|
||||
"OnServerInitialized",
|
||||
|
||||
"OnTransformChildrenChanged",
|
||||
"OnTransformParentChanged",
|
||||
"OnTriggerEnter",
|
||||
"OnTriggerEnter2D",
|
||||
"OnTriggerExit",
|
||||
"OnTriggerExit2D",
|
||||
"OnTriggerStay",
|
||||
"OnTriggerStay2D",
|
||||
|
||||
"OnValidate",
|
||||
"OnWillRenderObject",
|
||||
"Reset",
|
||||
"Start",
|
||||
"Update",
|
||||
|
||||
// Animator/StateMachineBehaviour
|
||||
"OnStateEnter",
|
||||
"OnStateExit",
|
||||
"OnStateMove",
|
||||
"OnStateUpdate",
|
||||
"OnStateIK",
|
||||
"OnStateMachineEnter",
|
||||
"OnStateMachineExit",
|
||||
|
||||
// ParticleSystem
|
||||
"OnParticleTrigger",
|
||||
"OnParticleCollision",
|
||||
"OnParticleSystemStopped",
|
||||
|
||||
// UGUI/EventSystems
|
||||
"OnPointerClick",
|
||||
"OnPointerDown",
|
||||
"OnPointerUp",
|
||||
"OnPointerEnter",
|
||||
"OnPointerExit",
|
||||
"OnDrag",
|
||||
"OnBeginDrag",
|
||||
"OnEndDrag",
|
||||
"OnDrop",
|
||||
"OnScroll",
|
||||
"OnSelect",
|
||||
"OnDeselect",
|
||||
"OnMove",
|
||||
"OnSubmit",
|
||||
"OnCancel",
|
||||
};
|
||||
|
||||
private readonly CachedDictionary<TypeDef, bool> _computeDeclaringTypeDisableAllMemberRenamingCache;
|
||||
private readonly CachedDictionary<TypeDef, bool> _isSerializableCache;
|
||||
private readonly CachedDictionary<TypeDef, bool> _isInheritFromMonoBehaviourCache;
|
||||
private readonly CachedDictionary<TypeDef, bool> _isScriptOrSerializableTypeCache;
|
||||
|
||||
public UnityRenamePolicy()
|
||||
{
|
||||
_computeDeclaringTypeDisableAllMemberRenamingCache = new CachedDictionary<TypeDef, bool>(ComputeDeclaringTypeDisableAllMemberRenaming);
|
||||
_isSerializableCache = new CachedDictionary<TypeDef, bool>(MetaUtil.IsSerializableType);
|
||||
_isInheritFromMonoBehaviourCache = new CachedDictionary<TypeDef, bool>(MetaUtil.IsInheritFromMonoBehaviour);
|
||||
_isScriptOrSerializableTypeCache = new CachedDictionary<TypeDef, bool>(MetaUtil.IsScriptOrSerializableType);
|
||||
}
|
||||
|
||||
private bool IsUnitySourceGeneratedAssemblyType(TypeDef typeDef)
|
||||
{
|
||||
if (typeDef.Name.StartsWith("UnitySourceGeneratedAssemblyMonoScriptTypes_"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (typeDef.FullName == "Unity.Entities.CodeGeneratedRegistry.AssemblyTypeRegistry")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (typeDef.Name.StartsWith("__JobReflectionRegistrationOutput"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (MetaUtil.HasDOTSCompilerGeneratedAttribute(typeDef))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (typeDef.DeclaringType != null)
|
||||
{
|
||||
return IsUnitySourceGeneratedAssemblyType(typeDef.DeclaringType);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool ComputeDeclaringTypeDisableAllMemberRenaming(TypeDef typeDef)
|
||||
{
|
||||
if (typeDef.IsEnum && MetaUtil.HasBlackboardEnumAttribute(typeDef))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (IsUnitySourceGeneratedAssemblyType(typeDef))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (MetaUtil.IsInheritFromDOTSTypes(typeDef))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool NeedRename(TypeDef typeDef)
|
||||
{
|
||||
if (_isScriptOrSerializableTypeCache.GetValue(typeDef))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (_computeDeclaringTypeDisableAllMemberRenamingCache.GetValue(typeDef))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (MetaUtil.HasBurstCompileAttribute(typeDef))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (typeDef.Methods.Any(m => MetaUtil.HasRuntimeInitializeOnLoadMethodAttribute(m)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool NeedRename(MethodDef methodDef)
|
||||
{
|
||||
TypeDef typeDef = methodDef.DeclaringType;
|
||||
if (s_monoBehaviourEvents.Contains(methodDef.Name) && _isInheritFromMonoBehaviourCache.GetValue(typeDef))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (_computeDeclaringTypeDisableAllMemberRenamingCache.GetValue(typeDef))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (MetaUtil.HasRuntimeInitializeOnLoadMethodAttribute(methodDef))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (MetaUtil.HasBurstCompileAttribute(methodDef) || MetaUtil.HasBurstCompileAttribute(methodDef.DeclaringType) || MetaUtil.HasDOTSCompilerGeneratedAttribute(methodDef))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool NeedRename(FieldDef fieldDef)
|
||||
{
|
||||
TypeDef typeDef = fieldDef.DeclaringType;
|
||||
if (_isScriptOrSerializableTypeCache.GetValue(typeDef))
|
||||
{
|
||||
if (typeDef.IsEnum)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (fieldDef.IsPublic && !fieldDef.IsStatic)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!fieldDef.IsStatic && MetaUtil.IsSerializableField(fieldDef))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (_computeDeclaringTypeDisableAllMemberRenamingCache.GetValue(typeDef))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool NeedRename(PropertyDef propertyDef)
|
||||
{
|
||||
TypeDef typeDef = propertyDef.DeclaringType;
|
||||
if (_isSerializableCache.GetValue(typeDef))
|
||||
{
|
||||
bool isGetterPublic = propertyDef.GetMethod != null && propertyDef.GetMethod.IsPublic && !propertyDef.GetMethod.IsStatic;
|
||||
bool isSetterPublic = propertyDef.SetMethod != null && propertyDef.SetMethod.IsPublic && !propertyDef.SetMethod.IsStatic;
|
||||
|
||||
if (isGetterPublic || isSetterPublic)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using Obfuz.Settings;
|
||||
using Obfuz.Utils;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Obfuz.ObfusPasses.SymbolObfus
|
||||
{
|
||||
public class ReflectionCompatibilityDetectionPass : ObfuscationPassBase
|
||||
{
|
||||
private readonly SymbolObfuscationSettingsFacade _settings;
|
||||
|
||||
public override ObfuscationPassType Type => ObfuscationPassType.SymbolObfus;
|
||||
|
||||
public ReflectionCompatibilityDetectionPass(SymbolObfuscationSettingsFacade settings)
|
||||
{
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void Process()
|
||||
{
|
||||
var ctx = ObfuscationPassContext.Current;
|
||||
var assemblyCache = ctx.assemblyCache;
|
||||
var toObfuscatedModules = ctx.modulesToObfuscate;
|
||||
var obfuscatedAndNotObfuscatedModules = ctx.allObfuscationRelativeModules;
|
||||
var toObfuscatedModuleSet = new HashSet<ModuleDef>(ctx.modulesToObfuscate);
|
||||
var renamePolicy = SymbolRename.CreateDefaultRenamePolicy(_settings.ruleFiles, _settings.customRenamePolicyTypes);
|
||||
var reflectionCompatibilityDetector = new ReflectionCompatibilityDetector(ctx.modulesToObfuscate, ctx.allObfuscationRelativeModules, renamePolicy);
|
||||
reflectionCompatibilityDetector.Analyze();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b8a72b9a9142429429296aafb05a2372
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,314 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obfuz.ObfusPasses.SymbolObfus
|
||||
{
|
||||
public class ReflectionCompatibilityDetector
|
||||
{
|
||||
private readonly HashSet<ModuleDef> _assembliesToObfuscate;
|
||||
private readonly List<ModuleDef> _obfuscatedAndNotObfuscatedModules;
|
||||
private readonly IObfuscationPolicy _renamePolicy;
|
||||
|
||||
public ReflectionCompatibilityDetector(List<ModuleDef> assembliesToObfuscate, List<ModuleDef> obfuscatedAndNotObfuscatedModules, IObfuscationPolicy renamePolicy)
|
||||
{
|
||||
_assembliesToObfuscate = new HashSet<ModuleDef>(assembliesToObfuscate);
|
||||
_obfuscatedAndNotObfuscatedModules = obfuscatedAndNotObfuscatedModules;
|
||||
_renamePolicy = renamePolicy;
|
||||
}
|
||||
|
||||
public void Analyze()
|
||||
{
|
||||
foreach (ModuleDef mod in _obfuscatedAndNotObfuscatedModules)
|
||||
{
|
||||
foreach (TypeDef type in mod.GetTypes())
|
||||
{
|
||||
foreach (MethodDef method in type.Methods)
|
||||
{
|
||||
AnalyzeMethod(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MethodDef _curCallingMethod;
|
||||
private IList<Instruction> _curInstructions;
|
||||
private int _curInstIndex;
|
||||
|
||||
private void AnalyzeMethod(MethodDef method)
|
||||
{
|
||||
if (!method.HasBody)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_curCallingMethod = method;
|
||||
_curInstructions = method.Body.Instructions;
|
||||
_curInstIndex = 0;
|
||||
for (int n = _curInstructions.Count; _curInstIndex < n; _curInstIndex++)
|
||||
{
|
||||
var inst = _curInstructions[_curInstIndex];
|
||||
switch (inst.OpCode.Code)
|
||||
{
|
||||
case Code.Call:
|
||||
{
|
||||
AnalyzeCall(inst.Operand as IMethod);
|
||||
break;
|
||||
}
|
||||
case Code.Callvirt:
|
||||
{
|
||||
ITypeDefOrRef constrainedType = null;
|
||||
if (_curInstIndex > 0)
|
||||
{
|
||||
var prevInst = _curInstructions[_curInstIndex - 1];
|
||||
if (prevInst.OpCode.Code == Code.Constrained)
|
||||
{
|
||||
constrainedType = prevInst.Operand as ITypeDefOrRef;
|
||||
}
|
||||
}
|
||||
AnalyzeCallvir(inst.Operand as IMethod, constrainedType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ITypeDefOrRef FindLatestTypeOf(int backwardFindInstructionCount)
|
||||
{
|
||||
// find sequence ldtoken <type>;
|
||||
for (int i = 2; i <= backwardFindInstructionCount; i++)
|
||||
{
|
||||
int index = _curInstIndex - i;
|
||||
if (index < 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Instruction inst1 = _curInstructions[index];
|
||||
Instruction inst2 = _curInstructions[index + 1];
|
||||
if (inst1.OpCode.Code == Code.Ldtoken && inst2.OpCode.Code == Code.Call)
|
||||
{
|
||||
if (!(inst1.Operand is ITypeDefOrRef typeDefOrRef))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
IMethod method = inst2.Operand as IMethod;
|
||||
if (method.Name == "GetTypeFromHandle" && method.DeclaringType.FullName == "System.Type")
|
||||
{
|
||||
// Ldtoken <type>; Call System.Type.GetTypeFromHandle(System.RuntimeTypeHandle handle)
|
||||
return typeDefOrRef;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void AnalyzeCall(IMethod calledMethod)
|
||||
{
|
||||
TypeDef callType = calledMethod.DeclaringType.ResolveTypeDef();
|
||||
if (callType == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
switch (callType.FullName)
|
||||
{
|
||||
case "System.Enum":
|
||||
{
|
||||
AnalyzeEnum(calledMethod, callType);
|
||||
break;
|
||||
}
|
||||
case "System.Type":
|
||||
{
|
||||
AnalyzeGetType(calledMethod, callType);
|
||||
break;
|
||||
}
|
||||
case "System.Reflection.Assembly":
|
||||
{
|
||||
if (calledMethod.Name == "GetType")
|
||||
{
|
||||
AnalyzeGetType(calledMethod, callType);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private bool IsAnyEnumItemRenamed(TypeDef typeDef)
|
||||
{
|
||||
return _assembliesToObfuscate.Contains(typeDef.Module) && typeDef.Fields.Any(f => _renamePolicy.NeedRename(f));
|
||||
}
|
||||
|
||||
private void AnalyzeCallvir(IMethod calledMethod, ITypeDefOrRef constrainedType)
|
||||
{
|
||||
TypeDef callType = calledMethod.DeclaringType.ResolveTypeDef();
|
||||
if (callType == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
string calledMethodName = calledMethod.Name;
|
||||
switch (callType.FullName)
|
||||
{
|
||||
case "System.Object":
|
||||
{
|
||||
if (calledMethodName == "ToString")
|
||||
{
|
||||
if (constrainedType != null)
|
||||
{
|
||||
TypeDef enumTypeDef = constrainedType.ResolveTypeDef();
|
||||
if (enumTypeDef != null && enumTypeDef.IsEnum && IsAnyEnumItemRenamed(enumTypeDef))
|
||||
{
|
||||
Debug.LogError($"[ReflectionCompatibilityDetector] Reflection compatibility issue in {_curCallingMethod}: {enumTypeDef.FullName}.ToString() the enum members are renamed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "System.Type":
|
||||
{
|
||||
AnalyzeGetType(calledMethod, callType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TypeSig GetMethodGenericParameter(IMethod method)
|
||||
{
|
||||
if (method is MethodSpec ms)
|
||||
{
|
||||
return ms.GenericInstMethodSig.GenericArguments.FirstOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void AnalyzeEnum(IMethod method, TypeDef typeDef)
|
||||
{
|
||||
const int extraSearchInstructionCount = 3;
|
||||
TypeSig parseTypeSig = GetMethodGenericParameter(method);
|
||||
TypeDef parseType = parseTypeSig?.ToTypeDefOrRef().ResolveTypeDef();
|
||||
switch (method.Name)
|
||||
{
|
||||
case "Parse":
|
||||
{
|
||||
if (parseTypeSig != null)
|
||||
{
|
||||
// Enum.Parse<T>(string name) or Enum.Parse<T>(string name, bool caseInsensitive)
|
||||
if (parseType != null)
|
||||
{
|
||||
if (IsAnyEnumItemRenamed(parseType))
|
||||
{
|
||||
Debug.LogError($"[ReflectionCompatibilityDetector] Reflection compatibility issue in {_curCallingMethod}: Enum.Parse<T> field of T:{parseType.FullName} is renamed.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[ReflectionCompatibilityDetector] Reflection compatibility issue in {_curCallingMethod}: Enum.Parse<T> field of T should not be renamed.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Enum.Parse(Type type, string name) or Enum.Parse(Type type, string name, bool ignoreCase)
|
||||
TypeDef enumType = FindLatestTypeOf(method.GetParamCount() + extraSearchInstructionCount)?.ResolveTypeDef();
|
||||
if (enumType != null && enumType.IsEnum && IsAnyEnumItemRenamed(enumType))
|
||||
{
|
||||
Debug.LogError($"[ReflectionCompatibilityDetector] Reflection compatibility issue in {_curCallingMethod}: Enum.Parse field of argument type:{enumType.FullName} is renamed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[ReflectionCompatibilityDetector] Reflection compatibility issue in {_curCallingMethod}: Enum.Parse field of argument `type` should not be renamed.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "TryParse":
|
||||
{
|
||||
if (parseTypeSig != null)
|
||||
{
|
||||
// Enum.TryParse<T>(string name, out T result) or Enum.TryParse<T>(string name, bool ignoreCase, out T result)
|
||||
if (parseType != null)
|
||||
{
|
||||
if (IsAnyEnumItemRenamed(parseType))
|
||||
{
|
||||
Debug.LogError($"[ReflectionCompatibilityDetector] Reflection compatibility issue in {_curCallingMethod}: Enum.TryParse<T> field of T:{parseType.FullName} is renamed.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[ReflectionCompatibilityDetector] Reflection compatibility issue in {_curCallingMethod}: Enum.TryParse<T> field of T should not be renamed.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TypeDef enumType = FindLatestTypeOf(method.GetParamCount() + extraSearchInstructionCount)?.ResolveTypeDef();
|
||||
if (enumType != null && enumType.IsEnum && IsAnyEnumItemRenamed(enumType))
|
||||
{
|
||||
Debug.LogError($"[ReflectionCompatibilityDetector] Reflection compatibility issue in {_curCallingMethod}: Enum.TryParse field of argument type:{enumType.FullName} is renamed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[ReflectionCompatibilityDetector] Reflection compatibility issue in {_curCallingMethod}: Enum.TryParse field of argument `type` should not be renamed.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "GetName":
|
||||
{
|
||||
// Enum.GetName(Type type, object value)
|
||||
TypeDef enumType = FindLatestTypeOf(method.GetParamCount() + extraSearchInstructionCount)?.ResolveTypeDef();
|
||||
if (enumType != null && enumType.IsEnum && IsAnyEnumItemRenamed(enumType))
|
||||
{
|
||||
Debug.LogError($"[ReflectionCompatibilityDetector] Reflection compatibility issue in {_curCallingMethod}: Enum.GetName field of type:{enumType.FullName} is renamed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[ReflectionCompatibilityDetector] Reflection compatibility issue in {_curCallingMethod}: Enum.GetName field of argument `type` should not be renamed.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "GetNames":
|
||||
{
|
||||
// Enum.GetNames(Type type)
|
||||
TypeDef enumType = FindLatestTypeOf(method.GetParamCount() + extraSearchInstructionCount)?.ResolveTypeDef();
|
||||
if (enumType != null && enumType.IsEnum && IsAnyEnumItemRenamed(enumType))
|
||||
{
|
||||
Debug.LogError($"[ReflectionCompatibilityDetector] Reflection compatibility issue in {_curCallingMethod}: Enum.GetNames field of type:{enumType.FullName} is renamed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[ReflectionCompatibilityDetector] Reflection compatibility issue in {_curCallingMethod}: Enum.GetNames field of argument `type` should not be renamed.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AnalyzeGetType(IMethod method, TypeDef declaringType)
|
||||
{
|
||||
switch (method.Name)
|
||||
{
|
||||
case "GetType":
|
||||
{
|
||||
Debug.LogWarning($"[ReflectionCompatibilityDetector] Reflection compatibility issue in {_curCallingMethod}: Type.GetType argument `typeName` should not be renamed.");
|
||||
break;
|
||||
}
|
||||
case "GetField":
|
||||
case "GetFields":
|
||||
case "GetMethod":
|
||||
case "GetMethods":
|
||||
case "GetProperty":
|
||||
case "GetProperties":
|
||||
case "GetEvent":
|
||||
case "GetEvents":
|
||||
case "GetMembers":
|
||||
{
|
||||
Debug.LogWarning($"[ReflectionCompatibilityDetector] Reflection compatibility issue in {_curCallingMethod}: called method:{method} the members of type should not be renamed.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b98c97a4d1db5d945bce0fdd2bd09202
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,299 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using Obfuz.Utils;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.ObfusPasses.SymbolObfus
|
||||
{
|
||||
|
||||
public class VirtualMethodGroup
|
||||
{
|
||||
public List<MethodDef> methods;
|
||||
|
||||
private HashSet<TypeDef> _nameScopes;
|
||||
|
||||
private HashSet<TypeDef> _rootNameScope;
|
||||
|
||||
public HashSet<TypeDef> GetNameConflictTypeScopes()
|
||||
{
|
||||
if (_nameScopes != null)
|
||||
{
|
||||
return _nameScopes;
|
||||
}
|
||||
|
||||
_nameScopes = new HashSet<TypeDef>();
|
||||
foreach (var method in methods)
|
||||
{
|
||||
TypeDef cur = method.DeclaringType;
|
||||
while (cur != null)
|
||||
{
|
||||
_nameScopes.Add(cur);
|
||||
cur = MetaUtil.GetBaseTypeDef(cur);
|
||||
}
|
||||
}
|
||||
return _nameScopes;
|
||||
}
|
||||
|
||||
public HashSet<TypeDef> GetRootBeInheritedTypes()
|
||||
{
|
||||
if (_rootNameScope != null)
|
||||
{
|
||||
return _rootNameScope;
|
||||
}
|
||||
_rootNameScope = new HashSet<TypeDef>();
|
||||
var nameScopes = GetNameConflictTypeScopes();
|
||||
foreach (var type in nameScopes)
|
||||
{
|
||||
TypeDef parentType = MetaUtil.GetBaseTypeDef(type);
|
||||
if (parentType == null || !nameScopes.Contains(parentType))
|
||||
{
|
||||
_rootNameScope.Add(type);
|
||||
}
|
||||
}
|
||||
return _rootNameScope;
|
||||
}
|
||||
|
||||
public IEnumerable<TypeDef> GetNameDeclaringTypeScopes()
|
||||
{
|
||||
foreach (var method in methods)
|
||||
{
|
||||
yield return method.DeclaringType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class VirtualMethodGroupCalculator
|
||||
{
|
||||
|
||||
private class TypeFlatMethods
|
||||
{
|
||||
public HashSet<MethodDef> flatMethods = new HashSet<MethodDef>();
|
||||
|
||||
|
||||
private bool IsFinalTypeSig(TypeSig type)
|
||||
{
|
||||
switch (type.ElementType)
|
||||
{
|
||||
case ElementType.Void:
|
||||
case ElementType.Boolean:
|
||||
case ElementType.Char:
|
||||
case ElementType.I1:
|
||||
case ElementType.I2:
|
||||
case ElementType.I4:
|
||||
case ElementType.I8:
|
||||
case ElementType.U1:
|
||||
case ElementType.U2:
|
||||
case ElementType.U4:
|
||||
case ElementType.U8:
|
||||
case ElementType.R4:
|
||||
case ElementType.R8:
|
||||
case ElementType.String:
|
||||
case ElementType.Object:
|
||||
case ElementType.Class:
|
||||
case ElementType.ValueType:
|
||||
return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsVarType(TypeSig t)
|
||||
{
|
||||
return t.ElementType == ElementType.MVar || t.ElementType == ElementType.Var;
|
||||
}
|
||||
|
||||
private bool IsClassOrValueType(TypeSig t)
|
||||
{
|
||||
return t.ElementType == ElementType.Class || t.ElementType == ElementType.ValueType;
|
||||
}
|
||||
|
||||
private bool IsLooseTypeSigMatch(TypeSig t1, TypeSig t2)
|
||||
{
|
||||
t1 = t1.RemovePinnedAndModifiers();
|
||||
t2 = t2.RemovePinnedAndModifiers();
|
||||
|
||||
if (t1.ElementType != t2.ElementType)
|
||||
{
|
||||
return IsVarType(t1) || IsVarType(t2);
|
||||
}
|
||||
|
||||
switch (t1.ElementType)
|
||||
{
|
||||
case ElementType.Void:
|
||||
case ElementType.Boolean:
|
||||
case ElementType.Char:
|
||||
case ElementType.I1:
|
||||
case ElementType.I2:
|
||||
case ElementType.I4:
|
||||
case ElementType.I8:
|
||||
case ElementType.U1:
|
||||
case ElementType.U2:
|
||||
case ElementType.U4:
|
||||
case ElementType.U8:
|
||||
case ElementType.R4:
|
||||
case ElementType.R8:
|
||||
case ElementType.I:
|
||||
case ElementType.U:
|
||||
case ElementType.R:
|
||||
case ElementType.String:
|
||||
case ElementType.Object:
|
||||
case ElementType.TypedByRef:
|
||||
return true;
|
||||
case ElementType.Class:
|
||||
case ElementType.ValueType:
|
||||
{
|
||||
return t1.AssemblyQualifiedName == t2.AssemblyQualifiedName;
|
||||
}
|
||||
case ElementType.Ptr:
|
||||
case ElementType.ByRef:
|
||||
case ElementType.SZArray:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case ElementType.Array:
|
||||
{
|
||||
var a1 = (ArraySig)t1;
|
||||
var a2 = (ArraySig)t2;
|
||||
if (a1.Rank != a2.Rank)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ElementType.Var:
|
||||
case ElementType.MVar:
|
||||
{
|
||||
//var v1 = (GenericSig)t1;
|
||||
//var v2 = (GenericSig)t2;
|
||||
//return v1.Number == v2.Number;
|
||||
return true;
|
||||
}
|
||||
default: return true;
|
||||
}
|
||||
if (t1.Next != null && t2.Next != null)
|
||||
{
|
||||
return IsLooseTypeSigMatch(t1.Next, t2.Next);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsLooseMatch(MethodDef method1, MethodDef method2)
|
||||
{
|
||||
if (method1.Name != method2.Name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (method1.GetParamCount() != method2.GetParamCount())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!IsLooseTypeSigMatch(method1.ReturnType, method2.ReturnType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int i = 0, n = method1.GetParamCount(); i < n; i++)
|
||||
{
|
||||
if (!IsLooseTypeSigMatch(method1.GetParam(i), method2.GetParam(i)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryFindMatchVirtualMethod(MethodDef method, out MethodDef matchMethodDef)
|
||||
{
|
||||
foreach (var parentOrInterfaceMethod in flatMethods)
|
||||
{
|
||||
if (IsLooseMatch(method, parentOrInterfaceMethod))
|
||||
{
|
||||
matchMethodDef = parentOrInterfaceMethod;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
matchMethodDef = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private readonly Dictionary<MethodDef, VirtualMethodGroup> _methodGroups = new Dictionary<MethodDef, VirtualMethodGroup>();
|
||||
private readonly Dictionary<TypeDef, TypeFlatMethods> _visitedTypes = new Dictionary<TypeDef, TypeFlatMethods>();
|
||||
|
||||
|
||||
|
||||
public VirtualMethodGroup GetMethodGroup(MethodDef methodDef)
|
||||
{
|
||||
if (_methodGroups.TryGetValue(methodDef, out var group))
|
||||
{
|
||||
return group;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void CalculateType(TypeDef typeDef)
|
||||
{
|
||||
if (_visitedTypes.ContainsKey(typeDef))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var typeMethods = new TypeFlatMethods();
|
||||
|
||||
var interfaceMethods = new List<MethodDef>();
|
||||
if (typeDef.BaseType != null)
|
||||
{
|
||||
TypeDef baseTypeDef = MetaUtil.GetTypeDefOrGenericTypeBaseThrowException(typeDef.BaseType);
|
||||
CalculateType(baseTypeDef);
|
||||
typeMethods.flatMethods.AddRange(_visitedTypes[baseTypeDef].flatMethods);
|
||||
foreach (var intfType in typeDef.Interfaces)
|
||||
{
|
||||
TypeDef intfTypeDef = MetaUtil.GetTypeDefOrGenericTypeBaseThrowException(intfType.Interface);
|
||||
CalculateType(intfTypeDef);
|
||||
//typeMethods.flatMethods.AddRange(_visitedTypes[intfTypeDef].flatMethods);
|
||||
interfaceMethods.AddRange(_visitedTypes[intfTypeDef].flatMethods);
|
||||
}
|
||||
}
|
||||
foreach (MethodDef method in interfaceMethods)
|
||||
{
|
||||
if (typeMethods.TryFindMatchVirtualMethod(method, out var matchMethodDef))
|
||||
{
|
||||
// merge group
|
||||
var group = _methodGroups[matchMethodDef];
|
||||
var matchGroup = _methodGroups[method];
|
||||
if (group != matchGroup)
|
||||
{
|
||||
foreach (var m in matchGroup.methods)
|
||||
{
|
||||
group.methods.Add(m);
|
||||
_methodGroups[m] = group;
|
||||
}
|
||||
}
|
||||
}
|
||||
typeMethods.flatMethods.Add(method);
|
||||
}
|
||||
|
||||
foreach (MethodDef method in typeDef.Methods)
|
||||
{
|
||||
if (!method.IsVirtual)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (typeMethods.TryFindMatchVirtualMethod(method, out var matchMethodDef))
|
||||
{
|
||||
var group = _methodGroups[matchMethodDef];
|
||||
group.methods.Add(method);
|
||||
_methodGroups.Add(method, group);
|
||||
}
|
||||
else
|
||||
{
|
||||
_methodGroups.Add(method, new VirtualMethodGroup() { methods = new List<MethodDef> { method } });
|
||||
}
|
||||
if (method.IsNewSlot)
|
||||
{
|
||||
typeMethods.flatMethods.Add(method);
|
||||
}
|
||||
}
|
||||
_visitedTypes.Add(typeDef, typeMethods);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8b381c6b90aae174a994bd6f2ae6464f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,242 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Emit;
|
||||
using Obfuz.Settings;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
using FieldAttributes = dnlib.DotNet.FieldAttributes;
|
||||
using HashUtil = Obfuz.Utils.HashUtil;
|
||||
using IRandom = Obfuz.Utils.IRandom;
|
||||
using KeyGenerator = Obfuz.Utils.KeyGenerator;
|
||||
using TypeAttributes = dnlib.DotNet.TypeAttributes;
|
||||
|
||||
namespace Obfuz.ObfusPasses.Watermark
|
||||
{
|
||||
public class WatermarkPass : ObfuscationPassBase
|
||||
{
|
||||
private readonly WatermarkSettingsFacade _watermarkSettings;
|
||||
|
||||
public WatermarkPass(WatermarkSettingsFacade watermarkSettingsFacade)
|
||||
{
|
||||
this._watermarkSettings = watermarkSettingsFacade;
|
||||
}
|
||||
|
||||
public override ObfuscationPassType Type => ObfuscationPassType.WaterMark;
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void Process()
|
||||
{
|
||||
var ctx = ObfuscationPassContext.Current;
|
||||
foreach (ModuleDef mod in ctx.modulesToObfuscate)
|
||||
{
|
||||
AddWaterMarkToAssembly(mod, _watermarkSettings.text);
|
||||
}
|
||||
}
|
||||
|
||||
private TypeDef GetDataHolderType(ModuleDef module, TypeDef declaringType, int size)
|
||||
{
|
||||
|
||||
using (var scope = new DisableTypeDefFindCacheScope(module))
|
||||
{
|
||||
var dataHolderType = new TypeDefUser($"$Obfuz$WatermarkDataHolderSize{size}_{declaringType.NestedTypes.Count}", module.Import(typeof(System.ValueType)));
|
||||
dataHolderType.Attributes = TypeAttributes.NestedPrivate | TypeAttributes.Sealed;
|
||||
dataHolderType.Layout = TypeAttributes.ExplicitLayout;
|
||||
dataHolderType.PackingSize = 1;
|
||||
dataHolderType.ClassSize = (uint)size;
|
||||
dataHolderType.DeclaringType = declaringType;
|
||||
return dataHolderType;
|
||||
}
|
||||
}
|
||||
|
||||
class WatermarkInfo
|
||||
{
|
||||
public string text;
|
||||
public byte[] signature;
|
||||
public readonly List<FieldDef> signatureHoldFields = new List<FieldDef>();
|
||||
}
|
||||
|
||||
private WatermarkInfo CreateWatermarkInfo(ModuleDef module, EncryptionScopeInfo encryptionScope, string waterMarkText)
|
||||
{
|
||||
string finalWatermarkText = $"{waterMarkText} [{module.Name}]";
|
||||
byte[] watermarkBytes = KeyGenerator.GenerateKey(finalWatermarkText, _watermarkSettings.signatureLength);
|
||||
|
||||
var watermarkInfo = new WatermarkInfo()
|
||||
{
|
||||
text = finalWatermarkText,
|
||||
signature = watermarkBytes,
|
||||
};
|
||||
|
||||
TypeDef moduleType = module.FindNormal("<PrivateImplementationDetails>");
|
||||
if (moduleType == null)
|
||||
{
|
||||
//throw new Exception($"Module '{module.Name}' does not contain a '<PrivateImplementationDetails>' type.");
|
||||
moduleType = new TypeDefUser("<PrivateImplementationDetails>", module.Import(typeof(object)));
|
||||
moduleType.Attributes = TypeAttributes.NotPublic | TypeAttributes.Sealed;
|
||||
moduleType.CustomAttributes.Add(new CustomAttribute(module.Import(module.Import(typeof(CompilerGeneratedAttribute)).ResolveTypeDefThrow().FindDefaultConstructor())));
|
||||
module.Types.Add(moduleType);
|
||||
}
|
||||
var random = encryptionScope.localRandomCreator(0);
|
||||
for (int subIndex = 0; subIndex < watermarkBytes.Length;)
|
||||
{
|
||||
int subSegmentLength = Math.Min(random.NextInt(16, 32) & ~3, watermarkBytes.Length - subIndex);
|
||||
int paddingLength = random.NextInt(8, 32) & ~3;
|
||||
int totalLength = subSegmentLength + paddingLength;
|
||||
TypeDef dataHolderType = GetDataHolderType(module, moduleType, totalLength);
|
||||
|
||||
byte[] subSegment = new byte[totalLength];
|
||||
Buffer.BlockCopy(watermarkBytes, subIndex, subSegment, 0, subSegmentLength);
|
||||
|
||||
for (int i = subSegmentLength; i < totalLength; i++)
|
||||
{
|
||||
subSegment[i] = (byte)random.NextInt(0, 256);
|
||||
}
|
||||
|
||||
subIndex += subSegmentLength;
|
||||
var field = new FieldDefUser($"$Obfuz$WatermarkDataHolderField{moduleType.Fields.Count}",
|
||||
new FieldSig(dataHolderType.ToTypeSig()),
|
||||
FieldAttributes.Assembly | FieldAttributes.InitOnly | FieldAttributes.Static | FieldAttributes.HasFieldRVA);
|
||||
field.DeclaringType = moduleType;
|
||||
field.InitialValue = subSegment;
|
||||
watermarkInfo.signatureHoldFields.Add(field);
|
||||
}
|
||||
|
||||
var moduleTypeFields = moduleType.Fields.ToList();
|
||||
RandomUtil.ShuffleList(moduleTypeFields, random);
|
||||
moduleType.Fields.Clear();
|
||||
foreach (var field in moduleTypeFields)
|
||||
{
|
||||
moduleType.Fields.Add(field);
|
||||
}
|
||||
return watermarkInfo;
|
||||
}
|
||||
|
||||
private int GetRandomInsertPosition(List<Instruction> instructions, IRandom random)
|
||||
{
|
||||
var insertPositions = instructions
|
||||
.Select((inst, index) => new { Instruction = inst, Index = index })
|
||||
.Where(x => x.Instruction.OpCode.FlowControl == FlowControl.Next)
|
||||
.Select(x => x.Index)
|
||||
.ToList();
|
||||
if (insertPositions.Count == 0)
|
||||
{
|
||||
return 0; // No valid position to insert
|
||||
}
|
||||
return insertPositions[random.NextInt(insertPositions.Count)] + 1;
|
||||
}
|
||||
|
||||
private void AddFieldAccessToSignatureHolder(ModuleDef module, EncryptionScopeInfo encryptionScope, WatermarkInfo watermarkInfo)
|
||||
{
|
||||
BurstCompileComputeCache burstCompileComputeCache = ObfuscationPassContext.Current.burstCompileComputeCache;
|
||||
var insertTargetMethods = module.Types
|
||||
.Where(t => !MetaUtil.HasBurstCompileAttribute(t))
|
||||
.SelectMany(t => t.Methods)
|
||||
.Where(m => m.HasBody && m.Body.Instructions.Count > 10 && !MetaUtil.HasBurstCompileAttribute(m)
|
||||
&& !burstCompileComputeCache.IsBurstCompileMethodOrReferencedByBurstCompileMethod(m))
|
||||
.ToList();
|
||||
|
||||
if (insertTargetMethods.Count == 0)
|
||||
{
|
||||
Debug.LogWarning($"No suitable methods found in module '{module.Name}' to insert access to watermark signature.");
|
||||
return;
|
||||
}
|
||||
|
||||
var random = encryptionScope.localRandomCreator(HashUtil.ComputeHash($"AddFieldAccessToSignatureHolder:{module.Name}"));
|
||||
DefaultMetadataImporter importer = ObfuscationPassContext.Current.moduleEntityManager.GetEntity<DefaultMetadataImporter>(module);
|
||||
foreach (var fieldDef in watermarkInfo.signatureHoldFields)
|
||||
{
|
||||
// Randomly select a method to insert the access
|
||||
var targetMethod = insertTargetMethods[random.NextInt(insertTargetMethods.Count)];
|
||||
var insts = (List<Instruction>)targetMethod.Body.Instructions;
|
||||
int insertIndex = GetRandomInsertPosition(insts, random);
|
||||
Instruction nop = Instruction.Create(OpCodes.Nop);
|
||||
insts.InsertRange(insertIndex, new[]
|
||||
{
|
||||
Instruction.CreateLdcI4(random.NextInt(1, 10000000)),
|
||||
Instruction.Create(OpCodes.Brtrue, nop),
|
||||
Instruction.CreateLdcI4(random.NextInt(fieldDef.InitialValue.Length)),
|
||||
Instruction.Create(OpCodes.Newarr, module.CorLibTypes.Byte),
|
||||
Instruction.Create(OpCodes.Ldtoken, fieldDef),
|
||||
Instruction.Create(OpCodes.Call, importer.InitializedArray),
|
||||
nop,
|
||||
});
|
||||
//Debug.Log($"Inserted watermark access for field '{fieldDef.Name}' in method '{targetMethod.FullName}' at index {insertIndex}.");
|
||||
}
|
||||
}
|
||||
|
||||
private readonly OpCode[] binOpCodes = new[]
|
||||
{
|
||||
OpCodes.Add, OpCodes.Sub, OpCodes.Mul, OpCodes.Div, OpCodes.Rem,
|
||||
OpCodes.And, OpCodes.Or, OpCodes.Xor
|
||||
};
|
||||
|
||||
private OpCode GetRandomBinOpCode(IRandom random)
|
||||
{
|
||||
return binOpCodes[random.NextInt(binOpCodes.Length)];
|
||||
}
|
||||
|
||||
private void AddWaterMarkILSequences(ModuleDef module, EncryptionScopeInfo encryptionScope, WatermarkInfo watermarkInfo)
|
||||
{
|
||||
var insertTargetMethods = module.Types
|
||||
.SelectMany(t => t.Methods)
|
||||
.Where(m => m.HasBody && m.Body.Instructions.Count > 10)
|
||||
.ToList();
|
||||
|
||||
if (insertTargetMethods.Count == 0)
|
||||
{
|
||||
Debug.LogWarning($"No suitable methods found in module '{module.Name}' to insert watermark IL sequences.");
|
||||
return;
|
||||
}
|
||||
var random = encryptionScope.localRandomCreator(HashUtil.ComputeHash($"AddWaterMarkILSequences:{module.Name}"));
|
||||
int[] signature = KeyGenerator.ConvertToIntKey(watermarkInfo.signature);
|
||||
for (int intIndex = 0; intIndex < signature.Length;)
|
||||
{
|
||||
int ldcCount = Math.Min(random.NextInt(2, 4), signature.Length - intIndex);
|
||||
// Randomly select a method to insert the IL sequence
|
||||
var targetMethod = insertTargetMethods[random.NextInt(insertTargetMethods.Count)];
|
||||
var insts = (List<Instruction>)targetMethod.Body.Instructions;
|
||||
int insertIndex = GetRandomInsertPosition(insts, random);
|
||||
var insertInstructions = new List<Instruction>()
|
||||
{
|
||||
Instruction.CreateLdcI4(random.NextInt(1, 10000000)),
|
||||
Instruction.Create(OpCodes.Brtrue, insts[insertIndex]),
|
||||
};
|
||||
for (int i = 0; i < ldcCount; i++)
|
||||
{
|
||||
insertInstructions.Add(Instruction.CreateLdcI4(signature[intIndex + i]));
|
||||
if (i > 0)
|
||||
{
|
||||
insertInstructions.Add(Instruction.Create(GetRandomBinOpCode(random)));
|
||||
}
|
||||
}
|
||||
insertInstructions.Add(Instruction.Create(OpCodes.Pop));
|
||||
|
||||
insts.InsertRange(insertIndex, insertInstructions);
|
||||
intIndex += ldcCount;
|
||||
//Debug.Log($"Inserted watermark IL sequence for in method '{targetMethod.FullName}' at index {insertIndex}.");
|
||||
}
|
||||
}
|
||||
|
||||
private void AddWaterMarkToAssembly(ModuleDef module, string waterMarkText)
|
||||
{
|
||||
var ctx = ObfuscationPassContext.Current;
|
||||
EncryptionScopeInfo encryptionScope = ctx.moduleEntityManager.EncryptionScopeProvider.GetScope(module);
|
||||
WatermarkInfo watermarkInfo = CreateWatermarkInfo(module, encryptionScope, waterMarkText);
|
||||
AddFieldAccessToSignatureHolder(module, encryptionScope, watermarkInfo);
|
||||
AddWaterMarkILSequences(module, encryptionScope, watermarkInfo);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 20c95bc217949f344909cc7049d6046e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,110 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using Obfuz.Editor;
|
||||
using Obfuz.Utils;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obfuz
|
||||
{
|
||||
public class ObfuscationMethodWhitelist
|
||||
{
|
||||
private readonly ObfuzIgnoreScopeComputeCache _obfuzComputeCache;
|
||||
private readonly BurstCompileComputeCache _burstCompileComputeCache;
|
||||
|
||||
public ObfuscationMethodWhitelist(ObfuzIgnoreScopeComputeCache obfuzComputeCache, BurstCompileComputeCache burstCompileComputeCache)
|
||||
{
|
||||
_obfuzComputeCache = obfuzComputeCache;
|
||||
_burstCompileComputeCache = burstCompileComputeCache;
|
||||
}
|
||||
|
||||
public bool IsInWhiteList(ModuleDef module)
|
||||
{
|
||||
string modName = module.Assembly.Name;
|
||||
if (modName == ConstValues.ObfuzRuntimeAssemblyName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
//if (MetaUtil.HasObfuzIgnoreScope(module))
|
||||
//{
|
||||
// return true;
|
||||
//}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool DoesMethodContainsRuntimeInitializeOnLoadMethodAttributeAndLoadTypeGreaterEqualAfterAssembliesLoaded(MethodDef method)
|
||||
{
|
||||
CustomAttribute ca = method.CustomAttributes.Find(ConstValues.RuntimeInitializedOnLoadMethodAttributeFullName);
|
||||
if (ca != null && ca.ConstructorArguments.Count > 0)
|
||||
{
|
||||
RuntimeInitializeLoadType loadType = (RuntimeInitializeLoadType)ca.ConstructorArguments[0].Value;
|
||||
if (loadType >= RuntimeInitializeLoadType.AfterAssembliesLoaded)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsInWhiteList(MethodDef method)
|
||||
{
|
||||
TypeDef typeDef = method.DeclaringType;
|
||||
//if (IsInWhiteList(typeDef))
|
||||
//{
|
||||
// return true;
|
||||
//}
|
||||
if (method.Name.StartsWith(ConstValues.ObfuzInternalSymbolNamePrefix))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (_obfuzComputeCache.HasSelfOrDeclaringOrEnclosingOrInheritObfuzIgnoreScope(method, typeDef, ObfuzScope.MethodBody))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
CustomAttribute ca = method.CustomAttributes.Find(ConstValues.RuntimeInitializedOnLoadMethodAttributeFullName);
|
||||
if (DoesMethodContainsRuntimeInitializeOnLoadMethodAttributeAndLoadTypeGreaterEqualAfterAssembliesLoaded(method))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (MetaUtil.HasBurstCompileAttribute(method) || _burstCompileComputeCache.IsBurstCompileMethodOrReferencedByBurstCompileMethod(method))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// don't obfuscate cctor when it has RuntimeInitializeOnLoadMethodAttribute with load type AfterAssembliesLoaded
|
||||
if (method.IsStatic && method.Name == ".cctor" && typeDef.Methods.Any(m => DoesMethodContainsRuntimeInitializeOnLoadMethodAttributeAndLoadTypeGreaterEqualAfterAssembliesLoaded(m)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsInWhiteList(TypeDef type)
|
||||
{
|
||||
if (type.Name.StartsWith(ConstValues.ObfuzInternalSymbolNamePrefix))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (IsInWhiteList(type.Module))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (MetaUtil.HasBurstCompileAttribute(type))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (_obfuzComputeCache.HasSelfOrDeclaringOrEnclosingOrInheritObfuzIgnoreScope(type, type.DeclaringType, ObfuzScope.MethodBody))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
//if (type.DeclaringType != null && IsInWhiteList(type.DeclaringType))
|
||||
//{
|
||||
// return true;
|
||||
//}
|
||||
if (type.FullName == ConstValues.GeneratedEncryptionVirtualMachineFullName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,234 +0,0 @@
|
|||
using dnlib.DotNet.Writer;
|
||||
using Obfuz.EncryptionVM;
|
||||
using Obfuz.ObfusPasses;
|
||||
using Obfuz.ObfusPasses.CallObfus;
|
||||
using Obfuz.ObfusPasses.ConstEncrypt;
|
||||
using Obfuz.ObfusPasses.ControlFlowObfus;
|
||||
using Obfuz.ObfusPasses.EvalStackObfus;
|
||||
using Obfuz.ObfusPasses.ExprObfus;
|
||||
using Obfuz.ObfusPasses.FieldEncrypt;
|
||||
using Obfuz.ObfusPasses.RemoveConstField;
|
||||
using Obfuz.ObfusPasses.SymbolObfus;
|
||||
using Obfuz.ObfusPasses.Watermark;
|
||||
using Obfuz.Settings;
|
||||
using Obfuz.Utils;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obfuz
|
||||
{
|
||||
|
||||
public class CoreSettingsFacade
|
||||
{
|
||||
public BuildTarget buildTarget;
|
||||
public RuntimeType targetRuntime;
|
||||
|
||||
public byte[] defaultStaticSecretKey;
|
||||
public byte[] defaultDynamicSecretKey;
|
||||
public List<string> assembliesUsingDynamicSecretKeys;
|
||||
public int randomSeed;
|
||||
|
||||
public string encryptionVmGenerationSecretKey;
|
||||
public int encryptionVmOpCodeCount;
|
||||
public string encryptionVmCodeFile;
|
||||
|
||||
public List<string> assembliesToObfuscate;
|
||||
public List<string> nonObfuscatedButReferencingObfuscatedAssemblies;
|
||||
public List<string> assemblySearchPaths;
|
||||
public string obfuscatedAssemblyOutputPath;
|
||||
public string obfuscatedAssemblyTempOutputPath;
|
||||
|
||||
public ObfuscationPassType enabledObfuscationPasses;
|
||||
public List<string> obfuscationPassRuleConfigFiles;
|
||||
public List<IObfuscationPass> obfuscationPasses;
|
||||
}
|
||||
|
||||
public class ObfuscatorBuilder
|
||||
{
|
||||
private CoreSettingsFacade _coreSettingsFacade;
|
||||
|
||||
public CoreSettingsFacade CoreSettingsFacade => _coreSettingsFacade;
|
||||
|
||||
public void InsertTopPriorityAssemblySearchPaths(List<string> assemblySearchPaths)
|
||||
{
|
||||
_coreSettingsFacade.assemblySearchPaths.InsertRange(0, assemblySearchPaths);
|
||||
}
|
||||
|
||||
public ObfuscatorBuilder AddPass(IObfuscationPass pass)
|
||||
{
|
||||
_coreSettingsFacade.obfuscationPasses.Add(pass);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Obfuscator Build()
|
||||
{
|
||||
return new Obfuscator(this);
|
||||
}
|
||||
|
||||
public static List<string> BuildUnityAssemblySearchPaths(bool searchPathIncludeUnityEditorDll = false)
|
||||
{
|
||||
string applicationContentsPath = EditorApplication.applicationContentsPath;
|
||||
var searchPaths = new List<string>
|
||||
{
|
||||
#if UNITY_2021_1_OR_NEWER
|
||||
#if UNITY_STANDALONE_WIN || (UNITY_EDITOR_WIN && UNITY_SERVER) || UNITY_WSA || UNITY_LUMIN
|
||||
"MonoBleedingEdge/lib/mono/unityaot-win32",
|
||||
"MonoBleedingEdge/lib/mono/unityaot-win32/Facades",
|
||||
#elif UNITY_STANDALONE_OSX || (UNITY_EDITOR_OSX && UNITY_SERVER) || UNITY_IOS || UNITY_TVOS
|
||||
"MonoBleedingEdge/lib/mono/unityaot-macos",
|
||||
"MonoBleedingEdge/lib/mono/unityaot-macos/Facades",
|
||||
#else
|
||||
"MonoBleedingEdge/lib/mono/unityaot-linux",
|
||||
"MonoBleedingEdge/lib/mono/unityaot-linux/Facades",
|
||||
#endif
|
||||
#else
|
||||
"MonoBleedingEdge/lib/mono/unityaot",
|
||||
"MonoBleedingEdge/lib/mono/unityaot/Facades",
|
||||
#endif
|
||||
|
||||
#if UNITY_STANDALONE_WIN || (UNITY_EDITOR_WIN && UNITY_SERVER)
|
||||
"PlaybackEngines/windowsstandalonesupport/Variations/il2cpp/Managed",
|
||||
#elif UNITY_STANDALONE_OSX || (UNITY_EDITOR_OSX && UNITY_SERVER)
|
||||
"PlaybackEngines/MacStandaloneSupport/Variations/il2cpp/Managed",
|
||||
#elif UNITY_STANDALONE_LINUX || (UNITY_EDITOR_LINUX && UNITY_SERVER)
|
||||
"PlaybackEngines/LinuxStandaloneSupport/Variations/il2cpp/Managed",
|
||||
#elif UNITY_ANDROID
|
||||
"PlaybackEngines/AndroidPlayer/Variations/il2cpp/Managed",
|
||||
#elif UNITY_IOS
|
||||
"PlaybackEngines/iOSSupport/Variations/il2cpp/Managed",
|
||||
#elif UNITY_MINIGAME || UNITY_WEIXINMINIGAME
|
||||
#if TUANJIE_1_1_OR_NEWER
|
||||
"PlaybackEngines/WeixinMiniGameSupport/Variations/il2cpp/nondevelopment/Data/Managed",
|
||||
#else
|
||||
"PlaybackEngines/WeixinMiniGameSupport/Variations/nondevelopment/Data/Managed",
|
||||
#endif
|
||||
#elif UNITY_OPENHARMONY
|
||||
"PlaybackEngines/OpenHarmonyPlayer/Variations/il2cpp/Managed",
|
||||
#elif UNITY_WEBGL
|
||||
"PlaybackEngines/WebGLSupport/Variations/nondevelopment/Data/Managed",
|
||||
#elif UNITY_TVOS
|
||||
"PlaybackEngines/AppleTVSupport/Variations/il2cpp/Managed",
|
||||
#elif UNITY_WSA
|
||||
"PlaybackEngines/WSASupport/Variations/il2cpp/Managed",
|
||||
#elif UNITY_LUMIN
|
||||
"PlaybackEngines/LuminSupport/Variations/il2cpp/Managed",
|
||||
#else
|
||||
#error "Unsupported platform, please report to us"
|
||||
#endif
|
||||
};
|
||||
|
||||
if (searchPathIncludeUnityEditorDll)
|
||||
{
|
||||
searchPaths.Add("Managed/UnityEngine");
|
||||
}
|
||||
|
||||
var resultPaths = new List<string>();
|
||||
foreach (var path in searchPaths)
|
||||
{
|
||||
string candidatePath1 = Path.Combine(applicationContentsPath, path);
|
||||
if (Directory.Exists(candidatePath1))
|
||||
{
|
||||
resultPaths.Add(candidatePath1);
|
||||
}
|
||||
if (path.StartsWith("PlaybackEngines"))
|
||||
{
|
||||
string candidatePath2 = Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName(applicationContentsPath)), path);
|
||||
if (Directory.Exists(candidatePath2))
|
||||
{
|
||||
resultPaths.Add(candidatePath2);
|
||||
}
|
||||
}
|
||||
}
|
||||
return resultPaths;
|
||||
}
|
||||
|
||||
public static ObfuscatorBuilder FromObfuzSettings(ObfuzSettings settings, BuildTarget target, bool searchPathIncludeUnityEditorInstallLocation, bool searchPathIncludeUnityEditorDll = false)
|
||||
{
|
||||
List<string> searchPaths = searchPathIncludeUnityEditorInstallLocation ?
|
||||
BuildUnityAssemblySearchPaths(searchPathIncludeUnityEditorDll).Concat(settings.assemblySettings.additionalAssemblySearchPaths).ToList()
|
||||
: settings.assemblySettings.additionalAssemblySearchPaths.ToList();
|
||||
foreach (var path in searchPaths)
|
||||
{
|
||||
bool exists = Directory.Exists(path);
|
||||
UnityEngine.Debug.Log($"search path:{path} exists:{exists}");
|
||||
}
|
||||
RuntimeType targetRuntime = settings.compatibilitySettings.targetRuntime != RuntimeType.ActivatedScriptingBackend ?
|
||||
settings.compatibilitySettings.targetRuntime :
|
||||
(PlatformUtil.IsMonoBackend() ? RuntimeType.Mono : RuntimeType.IL2CPP);
|
||||
if (searchPathIncludeUnityEditorDll && targetRuntime != RuntimeType.Mono)
|
||||
{
|
||||
Debug.LogError($"obfuscate dll for editor, but ObfuzSettings.CompatibilitySettings.targetRuntime isn't RuntimeType.Mono. Use RuntimeType.Mono for targetRuntime automatically.");
|
||||
targetRuntime = RuntimeType.Mono;
|
||||
}
|
||||
var builder = new ObfuscatorBuilder
|
||||
{
|
||||
_coreSettingsFacade = new CoreSettingsFacade()
|
||||
{
|
||||
buildTarget = target,
|
||||
targetRuntime = targetRuntime,
|
||||
defaultStaticSecretKey = KeyGenerator.GenerateKey(settings.secretSettings.defaultStaticSecretKey, VirtualMachine.SecretKeyLength),
|
||||
defaultDynamicSecretKey = KeyGenerator.GenerateKey(settings.secretSettings.defaultDynamicSecretKey, VirtualMachine.SecretKeyLength),
|
||||
assembliesUsingDynamicSecretKeys = settings.secretSettings.assembliesUsingDynamicSecretKeys.ToList(),
|
||||
randomSeed = settings.secretSettings.randomSeed,
|
||||
encryptionVmGenerationSecretKey = settings.encryptionVMSettings.codeGenerationSecretKey,
|
||||
encryptionVmOpCodeCount = settings.encryptionVMSettings.encryptionOpCodeCount,
|
||||
encryptionVmCodeFile = settings.encryptionVMSettings.codeOutputPath,
|
||||
assembliesToObfuscate = settings.assemblySettings.GetAssembliesToObfuscate(),
|
||||
nonObfuscatedButReferencingObfuscatedAssemblies = settings.assemblySettings.nonObfuscatedButReferencingObfuscatedAssemblies.ToList(),
|
||||
assemblySearchPaths = searchPaths,
|
||||
obfuscatedAssemblyOutputPath = settings.GetObfuscatedAssemblyOutputPath(target),
|
||||
obfuscatedAssemblyTempOutputPath = settings.GetObfuscatedAssemblyTempOutputPath(target),
|
||||
enabledObfuscationPasses = settings.obfuscationPassSettings.enabledPasses,
|
||||
obfuscationPassRuleConfigFiles = settings.obfuscationPassSettings.ruleFiles?.ToList() ?? new List<string>(),
|
||||
obfuscationPasses = new List<IObfuscationPass>(),
|
||||
},
|
||||
};
|
||||
ObfuscationPassType obfuscationPasses = settings.obfuscationPassSettings.enabledPasses;
|
||||
|
||||
if (obfuscationPasses.HasFlag(ObfuscationPassType.SymbolObfus) && settings.symbolObfusSettings.detectReflectionCompatibility)
|
||||
{
|
||||
builder.AddPass(new ReflectionCompatibilityDetectionPass(settings.symbolObfusSettings.ToFacade()));
|
||||
}
|
||||
if (obfuscationPasses.HasFlag(ObfuscationPassType.ConstEncrypt))
|
||||
{
|
||||
builder.AddPass(new ConstEncryptPass(settings.constEncryptSettings.ToFacade()));
|
||||
}
|
||||
if (obfuscationPasses.HasFlag(ObfuscationPassType.RemoveConstField))
|
||||
{
|
||||
builder.AddPass(new RemoveConstFieldPass(settings.removeConstFieldSettings.ToFacade()));
|
||||
}
|
||||
if (obfuscationPasses.HasFlag(ObfuscationPassType.ExprObfus))
|
||||
{
|
||||
builder.AddPass(new ExprObfusPass(settings.exprObfusSettings.ToFacade()));
|
||||
}
|
||||
//if (obfuscationPasses.HasFlag(ObfuscationPassType.EvalStackObfus))
|
||||
//{
|
||||
// builder.AddPass(new EvalStackObfusPass(settings.evalStackObfusSettings.ToFacade()));
|
||||
//}
|
||||
if (obfuscationPasses.HasFlag(ObfuscationPassType.FieldEncrypt))
|
||||
{
|
||||
builder.AddPass(new FieldEncryptPass(settings.fieldEncryptSettings.ToFacade()));
|
||||
}
|
||||
if (obfuscationPasses.HasFlag(ObfuscationPassType.CallObfus))
|
||||
{
|
||||
builder.AddPass(new CallObfusPass(settings.callObfusSettings.ToFacade()));
|
||||
}
|
||||
if (obfuscationPasses.HasFlag(ObfuscationPassType.ControlFlowObfus))
|
||||
{
|
||||
builder.AddPass(new ControlFlowObfusPass(settings.controlFlowObfusSettings.ToFacade()));
|
||||
}
|
||||
if (obfuscationPasses.HasFlag(ObfuscationPassType.WaterMark))
|
||||
{
|
||||
builder.AddPass(new WatermarkPass(settings.watermarkSettings.ToFacade()));
|
||||
}
|
||||
if (obfuscationPasses.HasFlag(ObfuscationPassType.SymbolObfus))
|
||||
{
|
||||
builder.AddPass(new SymbolObfusPass(settings.symbolObfusSettings.ToFacade()));
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
using Obfuz.Editor;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obfuz.Settings
|
||||
{
|
||||
[Serializable]
|
||||
public class AssemblySettings
|
||||
{
|
||||
|
||||
[Tooltip("name of assemblies to obfuscate, please don't add 'Obfuz.Runtime'")]
|
||||
public string[] assembliesToObfuscate;
|
||||
|
||||
[Tooltip("name of assemblies not obfuscated but reference assemblies to obfuscated ")]
|
||||
public string[] nonObfuscatedButReferencingObfuscatedAssemblies;
|
||||
|
||||
[Tooltip("additional assembly search paths")]
|
||||
public string[] additionalAssemblySearchPaths;
|
||||
|
||||
[Tooltip("obfuscate Obfuz.Runtime")]
|
||||
public bool obfuscateObfuzRuntime = true;
|
||||
|
||||
public List<string> GetAssembliesToObfuscate()
|
||||
{
|
||||
var asses = new List<string>(assembliesToObfuscate ?? Array.Empty<string>());
|
||||
if (obfuscateObfuzRuntime && !asses.Contains(ConstValues.ObfuzRuntimeAssemblyName))
|
||||
{
|
||||
asses.Add(ConstValues.ObfuzRuntimeAssemblyName);
|
||||
}
|
||||
return asses;
|
||||
}
|
||||
|
||||
public List<string> GetObfuscationRelativeAssemblyNames()
|
||||
{
|
||||
var asses = GetAssembliesToObfuscate();
|
||||
asses.AddRange(nonObfuscatedButReferencingObfuscatedAssemblies ?? Array.Empty<string>());
|
||||
return asses;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obfuz.Settings
|
||||
{
|
||||
[Serializable]
|
||||
public class BuildPipelineSettings
|
||||
{
|
||||
[Tooltip("enable Obfuz")]
|
||||
public bool enable = true;
|
||||
|
||||
[Tooltip("callback order of LinkXmlProcessor")]
|
||||
public int linkXmlProcessCallbackOrder = 10000;
|
||||
|
||||
[Tooltip("callback order of ObfuscationProcess")]
|
||||
public int obfuscationProcessCallbackOrder = 10000;
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 68737b215ecfe344a93d56007e186432
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,53 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obfuz.Settings
|
||||
{
|
||||
public enum ProxyMode
|
||||
{
|
||||
Dispatch,
|
||||
Delegate,
|
||||
}
|
||||
|
||||
public class CallObfuscationSettingsFacade
|
||||
{
|
||||
public ProxyMode proxyMode;
|
||||
public int obfuscationLevel;
|
||||
public int maxProxyMethodCountPerDispatchMethod;
|
||||
public bool obfuscateCallToMethodInMscorlib;
|
||||
public List<string> ruleFiles;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class CallObfuscationSettings
|
||||
{
|
||||
public ProxyMode proxyMode = ProxyMode.Dispatch;
|
||||
|
||||
[Tooltip("The obfuscation level for the obfuscation. Higher levels provide more security but may impact performance.")]
|
||||
[Range(1, 4)]
|
||||
public int obfuscationLevel = 1;
|
||||
|
||||
[Tooltip("The maximum number of proxy methods that can be generated per dispatch method. This helps to limit the complexity of the generated code and improve performance.")]
|
||||
public int maxProxyMethodCountPerDispatchMethod = 100;
|
||||
|
||||
[Tooltip("Whether to obfuscate calls to methods in mscorlib. Enable this option will impact performance.")]
|
||||
public bool obfuscateCallToMethodInMscorlib;
|
||||
|
||||
[Tooltip("rule config xml files")]
|
||||
public string[] ruleFiles;
|
||||
|
||||
public CallObfuscationSettingsFacade ToFacade()
|
||||
{
|
||||
return new CallObfuscationSettingsFacade
|
||||
{
|
||||
proxyMode = proxyMode,
|
||||
obfuscationLevel = obfuscationLevel,
|
||||
maxProxyMethodCountPerDispatchMethod = maxProxyMethodCountPerDispatchMethod,
|
||||
obfuscateCallToMethodInMscorlib = obfuscateCallToMethodInMscorlib,
|
||||
ruleFiles = ruleFiles?.ToList() ?? new List<string>(),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace Obfuz.Settings
|
||||
{
|
||||
public enum RuntimeType
|
||||
{
|
||||
ActivatedScriptingBackend,
|
||||
IL2CPP,
|
||||
Mono,
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class CompatibilitySettings
|
||||
{
|
||||
public RuntimeType targetRuntime;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue