mirror of
https://github.com/Alex-Rachel/TEngine.git
synced 2025-08-14 16:51:28 +00:00
接入obfuz->3.0
This commit is contained in:
@@ -2,37 +2,37 @@
|
||||
"versions": [
|
||||
{
|
||||
"unity_version":"2019",
|
||||
"hybridclr" : { "branch":"v8.3.0"},
|
||||
"hybridclr" : { "branch":"v8.4.0"},
|
||||
"il2cpp_plus": { "branch":"v2019-8.1.0"}
|
||||
},
|
||||
{
|
||||
"unity_version":"2020",
|
||||
"hybridclr" : { "branch":"v8.3.0"},
|
||||
"hybridclr" : { "branch":"v8.4.0"},
|
||||
"il2cpp_plus": { "branch":"v2020-8.1.0"}
|
||||
},
|
||||
{
|
||||
"unity_version":"2021",
|
||||
"hybridclr" : { "branch":"v8.3.0"},
|
||||
"hybridclr" : { "branch":"v8.4.0"},
|
||||
"il2cpp_plus": { "branch":"v2021-8.1.0"}
|
||||
},
|
||||
{
|
||||
"unity_version":"2022",
|
||||
"hybridclr" : { "branch":"v8.3.0"},
|
||||
"hybridclr" : { "branch":"v8.4.0"},
|
||||
"il2cpp_plus": { "branch":"v2022-8.2.0"}
|
||||
},
|
||||
{
|
||||
"unity_version":"2022-tuanjie",
|
||||
"hybridclr" : { "branch":"v8.3.0"},
|
||||
"hybridclr" : { "branch":"v8.4.0"},
|
||||
"il2cpp_plus": { "branch":"v2022-tuanjie-8.3.0"}
|
||||
},
|
||||
{
|
||||
"unity_version":"2023",
|
||||
"hybridclr" : { "branch":"v8.3.0"},
|
||||
"hybridclr" : { "branch":"v8.4.0"},
|
||||
"il2cpp_plus": { "branch":"v2023-8.1.0"}
|
||||
},
|
||||
{
|
||||
"unity_version":"6000",
|
||||
"hybridclr" : { "branch":"v8.3.0"},
|
||||
"hybridclr" : { "branch":"v8.4.0"},
|
||||
"il2cpp_plus": { "branch":"v6000-8.1.0"}
|
||||
}
|
||||
]
|
||||
|
@@ -136,8 +136,8 @@ namespace HybridCLR.Editor.Commands
|
||||
options = buildOptions,
|
||||
target = target,
|
||||
targetGroup = BuildPipeline.GetBuildTargetGroup(target),
|
||||
#if UNITY_2021_1_OR_NEWER
|
||||
subtarget = (int)EditorUserBuildSettings.standaloneBuildSubtarget,
|
||||
#if UNITY_SERVER
|
||||
subtarget = (int)StandaloneBuildSubtarget.Server,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@@ -1,5 +1,20 @@
|
||||
# ReleaseLog
|
||||
|
||||
## 8.4.0
|
||||
|
||||
Release Date: 2025-07-26.
|
||||
|
||||
### Runtime
|
||||
|
||||
- **[new] IMPORTANT! support custom image format**
|
||||
- [change] the type of field `offset` of ldsfld, stfld, ldthreadlocalfld、stthreadlocalfld changed from uint16_t to uint32_t so that supports class with huge static fields.
|
||||
- [opt] optimize to use NewValueTypeVar_Ctor_0 for new zero-argument value type and System.Activator.CreateInstance<T>()
|
||||
- [opt] optimize new ValueType with zero arguments.
|
||||
|
||||
### Editor
|
||||
|
||||
- [fix] fix the issue that `Texture Compression` option in Build Settings was changed after running `HybridCLR/Generate/All` on Android platform
|
||||
|
||||
## 8.3.0
|
||||
|
||||
Release Date: 2025-07-04.
|
||||
|
@@ -11,6 +11,9 @@ namespace HybridCLR
|
||||
HOMOLOGOUS_ASSEMBLY_HAS_LOADED, // can not load supplementary metadata assembly for the same assembly
|
||||
INVALID_HOMOLOGOUS_MODE, // invalid homologous image mode
|
||||
PDB_BAD_FILE, // invalid pdb file
|
||||
UNKNOWN_IMAGE_FORMAT,
|
||||
UNSUPPORT_FORMAT_VERSION,
|
||||
UNMATCH_FORMAT_VARIANT,
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "com.code-philosophy.hybridclr",
|
||||
"version": "8.3.0",
|
||||
"version": "8.4.0",
|
||||
"displayName": "HybridCLR",
|
||||
"description": "HybridCLR is a fully featured, zero-cost, high-performance, low-memory solution for Unity's all-platform native c# hotupdate.",
|
||||
"category": "Scripting",
|
||||
|
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2326b426d539e084dbddf7f7c23ed1bd
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 241df8eaf3a34dc47a0873c37ddb2695
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,263 +0,0 @@
|
||||
using dnlib.DotNet;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obfuz.Conf
|
||||
{
|
||||
public interface IRule<T>
|
||||
{
|
||||
void InheritParent(T parentRule);
|
||||
}
|
||||
|
||||
|
||||
public interface IMethodRule<R> where R : IRule<R>
|
||||
{
|
||||
string Name { get; set; }
|
||||
NameMatcher NameMatcher { get; set; }
|
||||
|
||||
R Rule { get; set; }
|
||||
}
|
||||
|
||||
public abstract class MethodRuleBase<R> : IMethodRule<R> where R : IRule<R>
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public NameMatcher NameMatcher { get; set; }
|
||||
|
||||
public R Rule { get; set; }
|
||||
}
|
||||
|
||||
public interface ITypeRule<T, R> where T : IMethodRule<R> where R : IRule<R>
|
||||
{
|
||||
string Name { get; set; }
|
||||
|
||||
NameMatcher NameMatcher { get; set; }
|
||||
|
||||
R Rule { get; set; }
|
||||
|
||||
List<T> Methods { get; set; }
|
||||
}
|
||||
|
||||
public abstract class TypeRuleBase<T, R> : ITypeRule<T, R> where T : IMethodRule<R> where R : IRule<R>
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public NameMatcher NameMatcher { get; set; }
|
||||
|
||||
public R Rule { get; set; }
|
||||
|
||||
public List<T> Methods { get; set; }
|
||||
}
|
||||
|
||||
public interface IAssemblyRule<TType, TMethod, TRule> where TType : ITypeRule<TMethod, TRule> where TMethod : IMethodRule<TRule> where TRule : IRule<TRule>
|
||||
{
|
||||
string Name { get; set; }
|
||||
|
||||
TRule Rule { get; set; }
|
||||
|
||||
List<TType> Types { get; set; }
|
||||
}
|
||||
public abstract class AssemblyRuleBase<TType, TMethod, TRule> : IAssemblyRule<TType, TMethod, TRule> where TType : ITypeRule<TMethod, TRule> where TMethod : IMethodRule<TRule> where TRule : IRule<TRule>
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public TRule Rule { get; set; }
|
||||
|
||||
public List<TType> Types { get; set; }
|
||||
}
|
||||
|
||||
public class XmlAssemblyTypeMethodRuleParser<TAssembly, TType, TMethod, TRule>
|
||||
where TMethod : IMethodRule<TRule>, new()
|
||||
where TType : ITypeRule<TMethod, TRule>, new()
|
||||
where TAssembly : IAssemblyRule<TType, TMethod, TRule>, new()
|
||||
where TRule : IRule<TRule>, new()
|
||||
{
|
||||
private readonly HashSet<string> _toObfuscatedAssemblyNames;
|
||||
private readonly Func<string, XmlElement, TRule> _ruleParser;
|
||||
private readonly Action<string, XmlElement> _unknownNodeTypeHandler;
|
||||
private readonly Dictionary<string, TAssembly> _assemblySpecs = new Dictionary<string, TAssembly>();
|
||||
|
||||
public XmlAssemblyTypeMethodRuleParser(IEnumerable<string> toObfuscatedAssemblyNames, Func<string, XmlElement, TRule> ruleParser, Action<string, XmlElement> unknownNodeTypeHandler)
|
||||
{
|
||||
_toObfuscatedAssemblyNames = new HashSet<string>(toObfuscatedAssemblyNames);
|
||||
_ruleParser = ruleParser;
|
||||
_unknownNodeTypeHandler = unknownNodeTypeHandler;
|
||||
}
|
||||
|
||||
public Dictionary<string, TAssembly> AssemblySpecs => _assemblySpecs;
|
||||
|
||||
public void LoadConfigs(IEnumerable<string> configFiles)
|
||||
{
|
||||
foreach (var configFile in configFiles)
|
||||
{
|
||||
LoadConfig(configFile);
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadConfig(string configFile)
|
||||
{
|
||||
if (string.IsNullOrEmpty(configFile))
|
||||
{
|
||||
throw new Exception($"Invalid xml file {configFile}, file name is empty");
|
||||
}
|
||||
Debug.Log($"ConfigurableObfuscationPolicy::LoadConfig {configFile}");
|
||||
var doc = new XmlDocument();
|
||||
doc.Load(configFile);
|
||||
var root = doc.DocumentElement;
|
||||
if (root.Name != "obfuz")
|
||||
{
|
||||
throw new Exception($"Invalid xml file {configFile}, root name should be 'obfuz'");
|
||||
}
|
||||
foreach (XmlNode node in root.ChildNodes)
|
||||
{
|
||||
if (!(node is XmlElement ele))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
switch (ele.Name)
|
||||
{
|
||||
case "assembly":
|
||||
{
|
||||
TAssembly assSpec = ParseAssembly(configFile, ele);
|
||||
_assemblySpecs.Add(assSpec.Name, assSpec);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (_unknownNodeTypeHandler == null)
|
||||
{
|
||||
throw new Exception($"Invalid xml file {configFile}, unknown node {ele.Name}");
|
||||
}
|
||||
_unknownNodeTypeHandler(configFile, ele);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TAssembly ParseAssembly(string configFile, XmlElement ele)
|
||||
{
|
||||
var assemblySpec = new TAssembly();
|
||||
string name = ele.GetAttribute("name");
|
||||
if (!_toObfuscatedAssemblyNames.Contains(name))
|
||||
{
|
||||
throw new Exception($"Invalid xml file {configFile}, assembly name {name} isn't in toObfuscatedAssemblyNames");
|
||||
}
|
||||
if (_assemblySpecs.ContainsKey(name))
|
||||
{
|
||||
throw new Exception($"Invalid xml file {configFile}, assembly name {name} is duplicated");
|
||||
}
|
||||
assemblySpec.Name = name;
|
||||
assemblySpec.Rule = _ruleParser(configFile, ele);
|
||||
|
||||
var types = new List<TType>();
|
||||
assemblySpec.Types = types;
|
||||
foreach (XmlNode node in ele.ChildNodes)
|
||||
{
|
||||
if (!(node is XmlElement childEle))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
switch (childEle.Name)
|
||||
{
|
||||
case "type":
|
||||
{
|
||||
types.Add(ParseType(configFile, childEle));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new Exception($"Invalid xml file, unknown node {childEle.Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
return assemblySpec;
|
||||
}
|
||||
|
||||
private TType ParseType(string configFile, XmlElement element)
|
||||
{
|
||||
var typeSpec = new TType();
|
||||
|
||||
string name = element.GetAttribute("name");
|
||||
typeSpec.Name = name;
|
||||
typeSpec.NameMatcher = new NameMatcher(name);
|
||||
typeSpec.Rule = _ruleParser(configFile, element);
|
||||
|
||||
var methods = new List<TMethod>();
|
||||
typeSpec.Methods = methods;
|
||||
foreach (XmlNode node in element.ChildNodes)
|
||||
{
|
||||
if (!(node is XmlElement ele))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
switch (ele.Name)
|
||||
{
|
||||
case "method":
|
||||
{
|
||||
methods.Add(ParseMethod(configFile, ele));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new Exception($"Invalid xml file, unknown node {ele.Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
return typeSpec;
|
||||
}
|
||||
|
||||
private TMethod ParseMethod(string configFile, XmlElement element)
|
||||
{
|
||||
var methodSpec = new TMethod();
|
||||
string name = element.GetAttribute("name");
|
||||
methodSpec.Name = name;
|
||||
methodSpec.NameMatcher = new NameMatcher(name);
|
||||
methodSpec.Rule = _ruleParser(configFile, element);
|
||||
return methodSpec;
|
||||
}
|
||||
|
||||
public TRule GetMethodRule(MethodDef method, TRule defaultRule)
|
||||
{
|
||||
var assemblyName = method.DeclaringType.Module.Assembly.Name;
|
||||
if (!_assemblySpecs.TryGetValue(assemblyName, out var assSpec))
|
||||
{
|
||||
return defaultRule;
|
||||
}
|
||||
string declaringTypeName = method.DeclaringType.FullName;
|
||||
foreach (var typeSpec in assSpec.Types)
|
||||
{
|
||||
if (typeSpec.NameMatcher.IsMatch(declaringTypeName))
|
||||
{
|
||||
foreach (var methodSpec in typeSpec.Methods)
|
||||
{
|
||||
if (methodSpec.NameMatcher.IsMatch(method.Name))
|
||||
{
|
||||
return methodSpec.Rule;
|
||||
}
|
||||
}
|
||||
return typeSpec.Rule;
|
||||
}
|
||||
}
|
||||
return assSpec.Rule;
|
||||
}
|
||||
|
||||
public void InheritParentRules(TRule defaultRule)
|
||||
{
|
||||
foreach (TAssembly assSpec in _assemblySpecs.Values)
|
||||
{
|
||||
assSpec.Rule.InheritParent(defaultRule);
|
||||
foreach (TType typeSpec in assSpec.Types)
|
||||
{
|
||||
typeSpec.Rule.InheritParent(assSpec.Rule);
|
||||
foreach (TMethod methodSpec in typeSpec.Methods)
|
||||
{
|
||||
methodSpec.Rule.InheritParent(typeSpec.Rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 36a3e142db81f6d4bb54938525e31973
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,203 +0,0 @@
|
||||
using dnlib.DotNet;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
|
||||
namespace Obfuz.Conf
|
||||
{
|
||||
|
||||
|
||||
|
||||
public class XmlFieldRuleParser<R> where R : class, new()
|
||||
{
|
||||
private readonly HashSet<string> _toObfuscatedAssemblyNames;
|
||||
private readonly Func<string, XmlElement, R> _ruleParser;
|
||||
private readonly Action<string, XmlElement> _unknownNodeTypeHandler;
|
||||
private readonly Dictionary<string, AssemblySpec> _assemblySpecs = new Dictionary<string, AssemblySpec>();
|
||||
|
||||
|
||||
private class FieldSpec
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public NameMatcher NameMatcher { get; set; }
|
||||
|
||||
public R Rule { get; set; }
|
||||
}
|
||||
|
||||
private class TypeSpec
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public NameMatcher NameMatcher { get; set; }
|
||||
|
||||
public List<FieldSpec> Fields { get; set; }
|
||||
}
|
||||
|
||||
private class AssemblySpec
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public List<TypeSpec> Types { get; set; }
|
||||
}
|
||||
|
||||
public XmlFieldRuleParser(IEnumerable<string> toObfuscatedAssemblyNames, Func<string, XmlElement, R> ruleParser, Action<string, XmlElement> unknownNodeTypeHandler)
|
||||
{
|
||||
_toObfuscatedAssemblyNames = new HashSet<string>(toObfuscatedAssemblyNames);
|
||||
_ruleParser = ruleParser;
|
||||
_unknownNodeTypeHandler = unknownNodeTypeHandler;
|
||||
}
|
||||
|
||||
public void LoadConfigs(IEnumerable<string> configFiles)
|
||||
{
|
||||
foreach (var configFile in configFiles)
|
||||
{
|
||||
LoadConfig(configFile);
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadConfig(string configFile)
|
||||
{
|
||||
if (string.IsNullOrEmpty(configFile))
|
||||
{
|
||||
throw new Exception($"Invalid xml file {configFile}, file name is empty");
|
||||
}
|
||||
var doc = new XmlDocument();
|
||||
doc.Load(configFile);
|
||||
var root = doc.DocumentElement;
|
||||
if (root.Name != "obfuz")
|
||||
{
|
||||
throw new Exception($"Invalid xml file {configFile}, root name should be 'obfuz'");
|
||||
}
|
||||
foreach (XmlNode node in root.ChildNodes)
|
||||
{
|
||||
if (!(node is XmlElement ele))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
switch (ele.Name)
|
||||
{
|
||||
case "assembly":
|
||||
{
|
||||
AssemblySpec assSpec = ParseAssembly(configFile, ele);
|
||||
_assemblySpecs.Add(assSpec.Name, assSpec);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (_unknownNodeTypeHandler == null)
|
||||
{
|
||||
throw new Exception($"Invalid xml file {configFile}, unknown node {ele.Name}");
|
||||
}
|
||||
_unknownNodeTypeHandler(configFile, ele);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private AssemblySpec ParseAssembly(string configFile, XmlElement ele)
|
||||
{
|
||||
var assemblySpec = new AssemblySpec();
|
||||
string name = ele.GetAttribute("name");
|
||||
if (!_toObfuscatedAssemblyNames.Contains(name))
|
||||
{
|
||||
throw new Exception($"Invalid xml file {configFile}, assembly name {name} isn't in toObfuscatedAssemblyNames");
|
||||
}
|
||||
if (_assemblySpecs.ContainsKey(name))
|
||||
{
|
||||
throw new Exception($"Invalid xml file {configFile}, assembly name {name} is duplicated");
|
||||
}
|
||||
assemblySpec.Name = name;
|
||||
|
||||
var types = new List<TypeSpec>();
|
||||
assemblySpec.Types = types;
|
||||
foreach (XmlNode node in ele.ChildNodes)
|
||||
{
|
||||
if (!(node is XmlElement childEle))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
switch (childEle.Name)
|
||||
{
|
||||
case "type":
|
||||
{
|
||||
types.Add(ParseType(configFile, childEle));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new Exception($"Invalid xml file, unknown node {childEle.Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
return assemblySpec;
|
||||
}
|
||||
|
||||
private TypeSpec ParseType(string configFile, XmlElement element)
|
||||
{
|
||||
var typeSpec = new TypeSpec();
|
||||
|
||||
string name = element.GetAttribute("name");
|
||||
typeSpec.Name = name;
|
||||
typeSpec.NameMatcher = new NameMatcher(name);
|
||||
|
||||
var fields = new List<FieldSpec>();
|
||||
typeSpec.Fields = fields;
|
||||
foreach (XmlNode node in element.ChildNodes)
|
||||
{
|
||||
if (!(node is XmlElement ele))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
switch (ele.Name)
|
||||
{
|
||||
case "field":
|
||||
{
|
||||
fields.Add(ParseField(configFile, ele));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new Exception($"Invalid xml file, unknown node {ele.Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
return typeSpec;
|
||||
}
|
||||
|
||||
private FieldSpec ParseField(string configFile, XmlElement element)
|
||||
{
|
||||
var fieldSpec = new FieldSpec();
|
||||
string name = element.GetAttribute("name");
|
||||
fieldSpec.Name = name;
|
||||
fieldSpec.NameMatcher = new NameMatcher(name);
|
||||
fieldSpec.Rule = _ruleParser(configFile, element);
|
||||
return fieldSpec;
|
||||
}
|
||||
|
||||
public R GetFieldRule(FieldDef field)
|
||||
{
|
||||
var assemblyName = field.DeclaringType.Module.Assembly.Name;
|
||||
if (!_assemblySpecs.TryGetValue(assemblyName, out var assSpec))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
string declaringTypeName = field.DeclaringType.FullName;
|
||||
foreach (var typeSpec in assSpec.Types)
|
||||
{
|
||||
if (typeSpec.NameMatcher.IsMatch(declaringTypeName))
|
||||
{
|
||||
foreach (var fieldSpec in typeSpec.Fields)
|
||||
{
|
||||
if (fieldSpec.NameMatcher.IsMatch(field.Name))
|
||||
{
|
||||
return fieldSpec.Rule;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1578270b9b81e1e4dba84d562c91090f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,544 +0,0 @@
|
||||
using dnlib.DotNet;
|
||||
using Obfuz.ObfusPasses;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obfuz
|
||||
{
|
||||
public class ConfigurablePassPolicy
|
||||
{
|
||||
class PassRule
|
||||
{
|
||||
public ObfuscationPassType? enablePasses;
|
||||
public ObfuscationPassType? disablePasses;
|
||||
public ObfuscationPassType? addPasses;
|
||||
public ObfuscationPassType? removePasses;
|
||||
public ObfuscationPassType finalPasses;
|
||||
|
||||
public void InheritParent(PassRule parentRule, ObfuscationPassType globalEnabledPasses)
|
||||
{
|
||||
finalPasses = parentRule.finalPasses;
|
||||
if (enablePasses != null)
|
||||
{
|
||||
finalPasses = enablePasses.Value;
|
||||
}
|
||||
if (disablePasses != null)
|
||||
{
|
||||
finalPasses = ~disablePasses.Value;
|
||||
}
|
||||
if (addPasses != null)
|
||||
{
|
||||
finalPasses |= addPasses.Value;
|
||||
}
|
||||
if (removePasses != null)
|
||||
{
|
||||
finalPasses &= ~removePasses.Value;
|
||||
}
|
||||
finalPasses &= globalEnabledPasses;
|
||||
}
|
||||
}
|
||||
|
||||
class SpecBase
|
||||
{
|
||||
public string name;
|
||||
public NameMatcher nameMatcher;
|
||||
public PassRule rule;
|
||||
}
|
||||
|
||||
class MethodSpec : SpecBase
|
||||
{
|
||||
}
|
||||
|
||||
class FieldSpec : SpecBase
|
||||
{
|
||||
}
|
||||
|
||||
class PropertySpec : SpecBase
|
||||
{
|
||||
}
|
||||
|
||||
class EventSpec : SpecBase
|
||||
{
|
||||
}
|
||||
|
||||
class TypeSpec : SpecBase
|
||||
{
|
||||
public List<FieldSpec> fields = new List<FieldSpec>();
|
||||
public List<MethodSpec> methods = new List<MethodSpec>();
|
||||
public List<PropertySpec> properties = new List<PropertySpec>();
|
||||
public List<EventSpec> events = new List<EventSpec>();
|
||||
}
|
||||
|
||||
class AssemblySpec
|
||||
{
|
||||
public string name;
|
||||
public NameMatcher nameMatcher;
|
||||
public PassRule rule;
|
||||
public List<TypeSpec> types = new List<TypeSpec>();
|
||||
}
|
||||
|
||||
private readonly ObfuscationPassType _enabledPasses;
|
||||
private readonly HashSet<string> _toObfuscatedAssemblyNames;
|
||||
private readonly List<AssemblySpec> _assemblySpecs = new List<AssemblySpec>();
|
||||
private readonly PassRule _defaultPassRule;
|
||||
|
||||
private string _curLoadingConfig;
|
||||
|
||||
public ConfigurablePassPolicy(IEnumerable<string> toObfuscatedAssemblyNames, ObfuscationPassType enabledPasses, List<string> configFiles)
|
||||
{
|
||||
_toObfuscatedAssemblyNames = new HashSet<string>(toObfuscatedAssemblyNames);
|
||||
_enabledPasses = enabledPasses;
|
||||
_defaultPassRule = new PassRule { finalPasses = enabledPasses };
|
||||
LoadConfigs(configFiles);
|
||||
InheritParentRules(enabledPasses);
|
||||
}
|
||||
|
||||
private void LoadConfigs(IEnumerable<string> configFiles)
|
||||
{
|
||||
foreach (var configFile in configFiles)
|
||||
{
|
||||
LoadConfig(configFile);
|
||||
}
|
||||
}
|
||||
|
||||
private void InheritParentRules(ObfuscationPassType enablePasses)
|
||||
{
|
||||
var defaultRule = new PassRule
|
||||
{
|
||||
enablePasses = enablePasses,
|
||||
finalPasses = enablePasses,
|
||||
};
|
||||
foreach (AssemblySpec assSpec in _assemblySpecs)
|
||||
{
|
||||
assSpec.rule.InheritParent(defaultRule, enablePasses);
|
||||
foreach (TypeSpec typeSpec in assSpec.types)
|
||||
{
|
||||
typeSpec.rule.InheritParent(assSpec.rule, enablePasses);
|
||||
foreach (FieldSpec fieldSpec in typeSpec.fields)
|
||||
{
|
||||
fieldSpec.rule.InheritParent(typeSpec.rule, enablePasses);
|
||||
}
|
||||
foreach (MethodSpec methodSpec in typeSpec.methods)
|
||||
{
|
||||
methodSpec.rule.InheritParent(typeSpec.rule, enablePasses);
|
||||
}
|
||||
foreach (PropertySpec propertySpec in typeSpec.properties)
|
||||
{
|
||||
propertySpec.rule.InheritParent(typeSpec.rule, enablePasses);
|
||||
}
|
||||
foreach (EventSpec eventSpec in typeSpec.events)
|
||||
{
|
||||
eventSpec.rule.InheritParent(typeSpec.rule, enablePasses);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadConfig(string configFile)
|
||||
{
|
||||
if (string.IsNullOrEmpty(configFile))
|
||||
{
|
||||
throw new Exception($"Invalid xml file {configFile}, file name is empty");
|
||||
}
|
||||
_curLoadingConfig = configFile;
|
||||
|
||||
Debug.Log($"ConfigurablePassPolicy::LoadConfig {configFile}");
|
||||
var doc = new XmlDocument();
|
||||
doc.Load(configFile);
|
||||
var root = doc.DocumentElement;
|
||||
if (root.Name != "obfuz")
|
||||
{
|
||||
throw new Exception($"Invalid xml file {configFile}, root name should be 'obfuz'");
|
||||
}
|
||||
foreach (XmlNode node in root.ChildNodes)
|
||||
{
|
||||
if (!(node is XmlElement ele))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
switch (ele.Name)
|
||||
{
|
||||
case "assembly":
|
||||
{
|
||||
AssemblySpec assSpec = ParseAssembly(ele);
|
||||
_assemblySpecs.Add(assSpec);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new Exception($"Invalid xml file {configFile}, unknown node {ele.Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(bool, ObfuscationPassType) ParseObfuscationType(string obfuscationPassTypesStr)
|
||||
{
|
||||
bool delta = false;
|
||||
if (obfuscationPassTypesStr[0] == '+' || obfuscationPassTypesStr[0] == '-')
|
||||
{
|
||||
delta = true;
|
||||
obfuscationPassTypesStr = obfuscationPassTypesStr.Substring(1);
|
||||
}
|
||||
ObfuscationPassType passType = ObfuscationPassType.None;
|
||||
foreach (var passName in obfuscationPassTypesStr.Split('|'))
|
||||
{
|
||||
if (Enum.TryParse<ObfuscationPassType>(passName, out var pass))
|
||||
{
|
||||
passType |= pass;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Invalid xml file {_curLoadingConfig}, unknown pass type {passName}");
|
||||
}
|
||||
}
|
||||
return (delta, passType);
|
||||
}
|
||||
|
||||
private PassRule ParseRule(XmlElement ele)
|
||||
{
|
||||
var r = new PassRule();
|
||||
if (ele.HasAttribute("enable"))
|
||||
{
|
||||
string enablePassStr = ele.GetAttribute("enable");
|
||||
if (string.IsNullOrEmpty(enablePassStr))
|
||||
{
|
||||
throw new Exception($"Invalid xml file {_curLoadingConfig}, enable attribute is empty");
|
||||
}
|
||||
var (delta, passType) = ParseObfuscationType(enablePassStr);
|
||||
if (delta)
|
||||
{
|
||||
r.addPasses = passType;
|
||||
}
|
||||
else
|
||||
{
|
||||
r.enablePasses = passType;
|
||||
}
|
||||
}
|
||||
if (ele.HasAttribute("disable"))
|
||||
{
|
||||
string disablePassStr = ele.GetAttribute("disable");
|
||||
if (string.IsNullOrEmpty(disablePassStr))
|
||||
{
|
||||
throw new Exception($"Invalid xml file {_curLoadingConfig}, disable attribute is empty");
|
||||
}
|
||||
var (delta, passType) = ParseObfuscationType(disablePassStr);
|
||||
if (delta)
|
||||
{
|
||||
r.removePasses = passType;
|
||||
}
|
||||
else
|
||||
{
|
||||
r.disablePasses = passType;
|
||||
}
|
||||
}
|
||||
if (r.enablePasses != null && (r.disablePasses != null || r.addPasses != null || r.removePasses != null))
|
||||
{
|
||||
throw new Exception($"Invalid xml file {_curLoadingConfig}, enable and disable can't be used together");
|
||||
}
|
||||
if (r.disablePasses != null && (r.enablePasses != null || r.addPasses != null || r.removePasses != null))
|
||||
{
|
||||
throw new Exception($"Invalid xml file {_curLoadingConfig}, disable and enable can't be used together");
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private AssemblySpec ParseAssembly(XmlElement ele)
|
||||
{
|
||||
var assemblySpec = new AssemblySpec();
|
||||
string name = ele.GetAttribute("name");
|
||||
if (!_toObfuscatedAssemblyNames.Contains(name))
|
||||
{
|
||||
throw new Exception($"Invalid xml file {_curLoadingConfig}, assembly name {name} isn't in toObfuscatedAssemblyNames");
|
||||
}
|
||||
assemblySpec.name = name;
|
||||
assemblySpec.nameMatcher = new NameMatcher(name);
|
||||
assemblySpec.rule = ParseRule(ele);
|
||||
|
||||
|
||||
var types = assemblySpec.types;
|
||||
foreach (XmlNode node in ele.ChildNodes)
|
||||
{
|
||||
if (!(node is XmlElement childEle))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
switch (childEle.Name)
|
||||
{
|
||||
case "type":
|
||||
{
|
||||
types.Add(ParseType(childEle));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new Exception($"Invalid xml file, unknown node {childEle.Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
return assemblySpec;
|
||||
}
|
||||
|
||||
private TypeSpec ParseType(XmlElement element)
|
||||
{
|
||||
var typeSpec = new TypeSpec();
|
||||
|
||||
string name = element.GetAttribute("name");
|
||||
typeSpec.name = name;
|
||||
typeSpec.nameMatcher = new NameMatcher(name);
|
||||
typeSpec.rule = ParseRule(element);
|
||||
|
||||
List<FieldSpec> fields = typeSpec.fields;
|
||||
List<MethodSpec> methods = typeSpec.methods;
|
||||
List<PropertySpec> properties = typeSpec.properties;
|
||||
List<EventSpec> events = typeSpec.events;
|
||||
foreach (XmlNode node in element.ChildNodes)
|
||||
{
|
||||
if (!(node is XmlElement ele))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
switch (ele.Name)
|
||||
{
|
||||
case "field":
|
||||
{
|
||||
fields.Add(ParseField(ele));
|
||||
break;
|
||||
}
|
||||
case "method":
|
||||
{
|
||||
methods.Add(ParseMethod(ele));
|
||||
break;
|
||||
}
|
||||
case "property":
|
||||
{
|
||||
properties.Add(ParseProperty(ele));
|
||||
break;
|
||||
}
|
||||
case "event":
|
||||
{
|
||||
events.Add(ParseEvent(ele));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new Exception($"Invalid xml file, unknown node {ele.Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
return typeSpec;
|
||||
}
|
||||
|
||||
private void ParseSpecObject(XmlElement element, SpecBase obj)
|
||||
{
|
||||
string name = element.GetAttribute("name");
|
||||
obj.name = name;
|
||||
obj.nameMatcher = new NameMatcher(name);
|
||||
obj.rule = ParseRule(element);
|
||||
}
|
||||
|
||||
private FieldSpec ParseField(XmlElement element)
|
||||
{
|
||||
var fieldSpec = new FieldSpec();
|
||||
ParseSpecObject(element, fieldSpec);
|
||||
return fieldSpec;
|
||||
}
|
||||
|
||||
private MethodSpec ParseMethod(XmlElement element)
|
||||
{
|
||||
var methodSpec = new MethodSpec();
|
||||
ParseSpecObject(element, methodSpec);
|
||||
return methodSpec;
|
||||
}
|
||||
|
||||
private PropertySpec ParseProperty(XmlElement element)
|
||||
{
|
||||
var propertySpec = new PropertySpec();
|
||||
ParseSpecObject(element, propertySpec);
|
||||
return propertySpec;
|
||||
}
|
||||
|
||||
private EventSpec ParseEvent(XmlElement element)
|
||||
{
|
||||
var eventSpec = new EventSpec();
|
||||
ParseSpecObject(element, eventSpec);
|
||||
return eventSpec;
|
||||
}
|
||||
|
||||
private readonly Dictionary<ModuleDef, (AssemblySpec, PassRule)> _modulePassRuleCaches = new Dictionary<ModuleDef, (AssemblySpec, PassRule)>();
|
||||
private readonly Dictionary<TypeDef, (TypeSpec, PassRule)> _typePassRuleCaches = new Dictionary<TypeDef, (TypeSpec, PassRule)>();
|
||||
private readonly Dictionary<MethodDef, (MethodSpec, PassRule)> _methodPassRuleCaches = new Dictionary<MethodDef, (MethodSpec, PassRule)>();
|
||||
private readonly Dictionary<FieldDef, (FieldSpec, PassRule)> _fieldPassRuleCaches = new Dictionary<FieldDef, (FieldSpec, PassRule)>();
|
||||
private readonly Dictionary<PropertyDef, (PropertySpec, PassRule)> _propertyPassRuleCaches = new Dictionary<PropertyDef, (PropertySpec, PassRule)>();
|
||||
private readonly Dictionary<EventDef, (EventSpec, PassRule)> _eventPassRuleCaches = new Dictionary<EventDef, (EventSpec, PassRule)>();
|
||||
|
||||
|
||||
private (AssemblySpec, PassRule) GetAssemblySpec(ModuleDef module)
|
||||
{
|
||||
if (!_modulePassRuleCaches.TryGetValue(module, out var result))
|
||||
{
|
||||
result = (null, _defaultPassRule);
|
||||
string assName = module.Assembly.Name;
|
||||
foreach (var ass in _assemblySpecs)
|
||||
{
|
||||
if (ass.nameMatcher.IsMatch(assName))
|
||||
{
|
||||
result = (ass, ass.rule);
|
||||
break;
|
||||
}
|
||||
}
|
||||
_modulePassRuleCaches.Add(module, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private (TypeSpec, PassRule) GetTypeSpec(TypeDef type)
|
||||
{
|
||||
if (!_typePassRuleCaches.TryGetValue(type, out var result))
|
||||
{
|
||||
var assResult = GetAssemblySpec(type.Module);
|
||||
result = (null, assResult.Item2);
|
||||
if (assResult.Item1 != null)
|
||||
{
|
||||
string typeName = type.FullName;
|
||||
foreach (var typeSpec in assResult.Item1.types)
|
||||
{
|
||||
if (typeSpec.nameMatcher.IsMatch(typeName))
|
||||
{
|
||||
result = (typeSpec, typeSpec.rule);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_typePassRuleCaches.Add(type, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private (MethodSpec, PassRule) GetMethodSpec(MethodDef method)
|
||||
{
|
||||
if (!_methodPassRuleCaches.TryGetValue(method, out var result))
|
||||
{
|
||||
var typeResult = GetTypeSpec(method.DeclaringType);
|
||||
result = (null, typeResult.Item2);
|
||||
if (typeResult.Item1 != null)
|
||||
{
|
||||
string methodName = method.Name;
|
||||
foreach (var methodSpec in typeResult.Item1.methods)
|
||||
{
|
||||
if (methodSpec.nameMatcher.IsMatch(methodName))
|
||||
{
|
||||
result = (methodSpec, methodSpec.rule);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_methodPassRuleCaches.Add(method, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private (FieldSpec, PassRule) GetFieldSpec(FieldDef field)
|
||||
{
|
||||
if (!_fieldPassRuleCaches.TryGetValue(field, out var result))
|
||||
{
|
||||
var typeResult = GetTypeSpec(field.DeclaringType);
|
||||
result = (null, typeResult.Item2);
|
||||
if (typeResult.Item1 != null)
|
||||
{
|
||||
string fieldName = field.Name;
|
||||
foreach (var fieldSpec in typeResult.Item1.fields)
|
||||
{
|
||||
if (fieldSpec.nameMatcher.IsMatch(fieldName))
|
||||
{
|
||||
result = (fieldSpec, fieldSpec.rule);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_fieldPassRuleCaches.Add(field, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private (PropertySpec, PassRule) GetPropertySpec(PropertyDef property)
|
||||
{
|
||||
if (!_propertyPassRuleCaches.TryGetValue(property, out var result))
|
||||
{
|
||||
var typeResult = GetTypeSpec(property.DeclaringType);
|
||||
result = (null, typeResult.Item2);
|
||||
if (typeResult.Item1 != null)
|
||||
{
|
||||
string propertyName = property.Name;
|
||||
foreach (var propertySpec in typeResult.Item1.properties)
|
||||
{
|
||||
if (propertySpec.nameMatcher.IsMatch(propertyName))
|
||||
{
|
||||
result = (propertySpec, propertySpec.rule);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_propertyPassRuleCaches.Add(property, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private (EventSpec, PassRule) GetEventSpec(EventDef eventDef)
|
||||
{
|
||||
if (!_eventPassRuleCaches.TryGetValue(eventDef, out var result))
|
||||
{
|
||||
var typeResult = GetTypeSpec(eventDef.DeclaringType);
|
||||
result = (null, typeResult.Item2);
|
||||
if (typeResult.Item1 != null)
|
||||
{
|
||||
string eventName = eventDef.Name;
|
||||
foreach (var eventSpec in typeResult.Item1.events)
|
||||
{
|
||||
if (eventSpec.nameMatcher.IsMatch(eventName))
|
||||
{
|
||||
result = (eventSpec, eventSpec.rule);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_eventPassRuleCaches.Add(eventDef, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public ObfuscationPassType GetAssemblyObfuscationPasses(ModuleDef module)
|
||||
{
|
||||
return GetAssemblySpec(module).Item2.finalPasses;
|
||||
}
|
||||
|
||||
public ObfuscationPassType GetTypeObfuscationPasses(TypeDef type)
|
||||
{
|
||||
return GetTypeSpec(type).Item2.finalPasses;
|
||||
}
|
||||
|
||||
public ObfuscationPassType GetMethodObfuscationPasses(MethodDef method)
|
||||
{
|
||||
return GetMethodSpec(method).Item2.finalPasses;
|
||||
}
|
||||
|
||||
public ObfuscationPassType GetFieldObfuscationPasses(FieldDef field)
|
||||
{
|
||||
return GetFieldSpec(field).Item2.finalPasses;
|
||||
}
|
||||
|
||||
public ObfuscationPassType GetPropertyObfuscationPasses(PropertyDef property)
|
||||
{
|
||||
return GetPropertySpec(property).Item2.finalPasses;
|
||||
}
|
||||
|
||||
public ObfuscationPassType GetEventObfuscationPasses(EventDef eventDef)
|
||||
{
|
||||
return GetEventSpec(eventDef).Item2.finalPasses;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 41044699810a34f4780e14de084bf7d7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,34 +0,0 @@
|
||||
using System.Text;
|
||||
|
||||
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,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aee7817ed523a5e4ea42104013e8a775
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3319ebe75a42f3d4d996846ca09ed099
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,269 +0,0 @@
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Editor;
|
||||
using Obfuz.Emit;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace Obfuz.Data
|
||||
{
|
||||
public class ConstFieldAllocator : GroupByModuleEntityBase
|
||||
{
|
||||
private RandomCreator _randomCreator;
|
||||
private IEncryptor _encryptor;
|
||||
|
||||
private TypeDef _holderTypeDef;
|
||||
|
||||
class ConstFieldInfo
|
||||
{
|
||||
public FieldDef field;
|
||||
public object value;
|
||||
}
|
||||
|
||||
class AnyComparer : IEqualityComparer<object>
|
||||
{
|
||||
public new bool Equals(object x, object y)
|
||||
{
|
||||
if (x is byte[] xBytes && y is byte[] yBytes)
|
||||
{
|
||||
return StructuralComparisons.StructuralEqualityComparer.Equals(xBytes, yBytes);
|
||||
}
|
||||
return x.Equals(y);
|
||||
}
|
||||
|
||||
public static int ComputeHashCode(object obj)
|
||||
{
|
||||
return HashUtil.ComputePrimitiveOrStringOrBytesHashCode(obj);
|
||||
}
|
||||
|
||||
public int GetHashCode(object obj)
|
||||
{
|
||||
return ComputeHashCode(obj);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Dictionary<object, ConstFieldInfo> _allocatedFields = new Dictionary<object, ConstFieldInfo>(new AnyComparer());
|
||||
private readonly Dictionary<FieldDef, ConstFieldInfo> _field2Fields = new Dictionary<FieldDef, ConstFieldInfo>();
|
||||
|
||||
private readonly List<TypeDef> _holderTypeDefs = new List<TypeDef>();
|
||||
private bool _done;
|
||||
|
||||
|
||||
public ConstFieldAllocator()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
_randomCreator = EncryptionScope.localRandomCreator;
|
||||
_encryptor = EncryptionScope.encryptor;
|
||||
}
|
||||
|
||||
const int maxFieldCount = 1000;
|
||||
|
||||
|
||||
private TypeSig GetTypeSigOfValue(object value)
|
||||
{
|
||||
ModuleDef mod = Module;
|
||||
if (value is int)
|
||||
return mod.CorLibTypes.Int32;
|
||||
if (value is long)
|
||||
return mod.CorLibTypes.Int64;
|
||||
if (value is float)
|
||||
return mod.CorLibTypes.Single;
|
||||
if (value is double)
|
||||
return mod.CorLibTypes.Double;
|
||||
if (value is string)
|
||||
return mod.CorLibTypes.String;
|
||||
if (value is byte[])
|
||||
return new SZArraySig(mod.CorLibTypes.Byte);
|
||||
throw new NotSupportedException($"Unsupported type: {value.GetType()}");
|
||||
}
|
||||
|
||||
private ConstFieldInfo CreateConstFieldInfo(object value)
|
||||
{
|
||||
ModuleDef mod = Module;
|
||||
if (_holderTypeDef == null || _holderTypeDef.Fields.Count >= maxFieldCount)
|
||||
{
|
||||
using (var scope = new DisableTypeDefFindCacheScope(mod))
|
||||
{
|
||||
ITypeDefOrRef objectTypeRef = mod.Import(typeof(object));
|
||||
_holderTypeDef = new TypeDefUser($"{ConstValues.ObfuzInternalSymbolNamePrefix}ConstFieldHolder${_holderTypeDefs.Count}", objectTypeRef);
|
||||
mod.Types.Add(_holderTypeDef);
|
||||
_holderTypeDefs.Add(_holderTypeDef);
|
||||
}
|
||||
}
|
||||
|
||||
var field = new FieldDefUser($"{ConstValues.ObfuzInternalSymbolNamePrefix}RVA_Value{_holderTypeDef.Fields.Count}", new FieldSig(GetTypeSigOfValue(value)), FieldAttributes.Static | FieldAttributes.Public);
|
||||
field.DeclaringType = _holderTypeDef;
|
||||
return new ConstFieldInfo
|
||||
{
|
||||
field = field,
|
||||
value = value,
|
||||
};
|
||||
}
|
||||
|
||||
private FieldDef AllocateAny(object value)
|
||||
{
|
||||
if (_done)
|
||||
{
|
||||
throw new Exception("can't Allocate after done");
|
||||
}
|
||||
if (!_allocatedFields.TryGetValue(value, out var field))
|
||||
{
|
||||
field = CreateConstFieldInfo(value);
|
||||
_allocatedFields.Add(value, field);
|
||||
_field2Fields.Add(field.field, field);
|
||||
}
|
||||
return field.field;
|
||||
}
|
||||
|
||||
public FieldDef Allocate(int value)
|
||||
{
|
||||
return AllocateAny(value);
|
||||
}
|
||||
|
||||
public FieldDef Allocate(long value)
|
||||
{
|
||||
return AllocateAny(value);
|
||||
}
|
||||
|
||||
public FieldDef Allocate(float value)
|
||||
{
|
||||
return AllocateAny(value);
|
||||
}
|
||||
|
||||
public FieldDef Allocate(double value)
|
||||
{
|
||||
return AllocateAny(value);
|
||||
}
|
||||
|
||||
public FieldDef Allocate(string value)
|
||||
{
|
||||
return AllocateAny(value);
|
||||
}
|
||||
|
||||
public FieldDef Allocate(byte[] value)
|
||||
{
|
||||
return AllocateAny(value);
|
||||
}
|
||||
|
||||
|
||||
private void CreateCCtorOfRvaTypeDef(TypeDef type)
|
||||
{
|
||||
ModuleDef mod = Module;
|
||||
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 = type;
|
||||
var body = new CilBody();
|
||||
cctor.Body = body;
|
||||
var ins = body.Instructions;
|
||||
|
||||
|
||||
DefaultMetadataImporter importer = this.GetDefaultModuleMetadataImporter();
|
||||
RvaDataAllocator rvaDataAllocator = GetEntity<RvaDataAllocator>();
|
||||
// TODO. obfuscate init codes
|
||||
foreach (var field in type.Fields)
|
||||
{
|
||||
ConstFieldInfo constInfo = _field2Fields[field];
|
||||
IRandom localRandom = _randomCreator(HashUtil.ComputePrimitiveOrStringOrBytesHashCode(constInfo.value));
|
||||
int ops = EncryptionUtil.GenerateEncryptionOpCodes(localRandom, _encryptor, 4);
|
||||
int salt = localRandom.NextInt();
|
||||
switch (constInfo.value)
|
||||
{
|
||||
case int i:
|
||||
{
|
||||
int encryptedValue = _encryptor.Encrypt(i, 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));
|
||||
break;
|
||||
}
|
||||
case long l:
|
||||
{
|
||||
long encryptedValue = _encryptor.Encrypt(l, 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.DecryptFromRvaLong));
|
||||
break;
|
||||
}
|
||||
case float f:
|
||||
{
|
||||
float encryptedValue = _encryptor.Encrypt(f, 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.DecryptFromRvaFloat));
|
||||
break;
|
||||
}
|
||||
case double d:
|
||||
{
|
||||
double encryptedValue = _encryptor.Encrypt(d, 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.DecryptFromRvaDouble));
|
||||
break;
|
||||
}
|
||||
case string s:
|
||||
{
|
||||
byte[] encryptedValue = _encryptor.Encrypt(s, ops, salt);
|
||||
RvaData rvaData = rvaDataAllocator.Allocate(encryptedValue);
|
||||
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
|
||||
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
|
||||
Assert.AreEqual(encryptedValue.Length, rvaData.size);
|
||||
ins.Add(Instruction.CreateLdcI4(encryptedValue.Length));
|
||||
ins.Add(Instruction.CreateLdcI4(ops));
|
||||
ins.Add(Instruction.CreateLdcI4(salt));
|
||||
ins.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaString));
|
||||
break;
|
||||
}
|
||||
case byte[] bs:
|
||||
{
|
||||
byte[] encryptedValue = _encryptor.Encrypt(bs, 0, bs.Length, ops, salt);
|
||||
Assert.AreEqual(encryptedValue.Length, bs.Length);
|
||||
RvaData rvaData = rvaDataAllocator.Allocate(encryptedValue);
|
||||
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
|
||||
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
|
||||
ins.Add(Instruction.CreateLdcI4(bs.Length));
|
||||
ins.Add(Instruction.CreateLdcI4(ops));
|
||||
ins.Add(Instruction.CreateLdcI4(salt));
|
||||
ins.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaBytes));
|
||||
break;
|
||||
}
|
||||
default: throw new NotSupportedException($"Unsupported type: {constInfo.value.GetType()}");
|
||||
}
|
||||
ins.Add(Instruction.Create(OpCodes.Stsfld, field));
|
||||
}
|
||||
ins.Add(Instruction.Create(OpCodes.Ret));
|
||||
}
|
||||
|
||||
public override void Done()
|
||||
{
|
||||
if (_done)
|
||||
{
|
||||
throw new Exception("Already done");
|
||||
}
|
||||
_done = true;
|
||||
foreach (var typeDef in _holderTypeDefs)
|
||||
{
|
||||
CreateCCtorOfRvaTypeDef(typeDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e75f5cdfd47370d4ea6c4dee7e55a881
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,324 +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.Runtime.InteropServices;
|
||||
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, 4);
|
||||
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,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c00ca514f46605645bf40b0135e7e504
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a513a192808ba5f47b1ef8a3ecf02533
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,310 +0,0 @@
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Obfuz.Emit
|
||||
{
|
||||
public class BasicBlock
|
||||
{
|
||||
public readonly List<Instruction> instructions = new List<Instruction>();
|
||||
|
||||
public readonly List<BasicBlock> inBlocks = new List<BasicBlock>();
|
||||
|
||||
public readonly List<BasicBlock> outBlocks = new List<BasicBlock>();
|
||||
|
||||
public bool inLoop;
|
||||
|
||||
public void AddTargetBasicBlock(BasicBlock target)
|
||||
{
|
||||
if (!outBlocks.Contains(target))
|
||||
{
|
||||
outBlocks.Add(target);
|
||||
}
|
||||
if (!target.inBlocks.Contains(this))
|
||||
{
|
||||
target.inBlocks.Add(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class BasicBlockCollection
|
||||
{
|
||||
private readonly MethodDef _method;
|
||||
|
||||
private readonly List<BasicBlock> _blocks = new List<BasicBlock>();
|
||||
private readonly Dictionary<Instruction, BasicBlock> _inst2BlockMap = new Dictionary<Instruction, BasicBlock>();
|
||||
|
||||
public IList<BasicBlock> Blocks => _blocks;
|
||||
|
||||
public BasicBlockCollection(MethodDef method, bool computeInLoop)
|
||||
{
|
||||
_method = method;
|
||||
HashSet<Instruction> splitPoints = BuildSplitPoint(method);
|
||||
BuildBasicBlocks(method, splitPoints);
|
||||
BuildInOutGraph(method);
|
||||
if (computeInLoop)
|
||||
{
|
||||
ComputeBlocksInLoop();
|
||||
}
|
||||
}
|
||||
|
||||
public void ComputeBlocksInLoop()
|
||||
{
|
||||
var loopBlocks = FindLoopBlocks(_blocks);
|
||||
foreach (var block in loopBlocks)
|
||||
{
|
||||
block.inLoop = true;
|
||||
}
|
||||
}
|
||||
|
||||
public BasicBlock GetBasicBlockByInstruction(Instruction inst)
|
||||
{
|
||||
return _inst2BlockMap[inst];
|
||||
}
|
||||
|
||||
private HashSet<Instruction> BuildSplitPoint(MethodDef method)
|
||||
{
|
||||
var insts = method.Body.Instructions;
|
||||
var splitPoints = new HashSet<Instruction>();
|
||||
foreach (ExceptionHandler eh in method.Body.ExceptionHandlers)
|
||||
{
|
||||
if (eh.TryStart != null)
|
||||
{
|
||||
splitPoints.Add(eh.TryStart);
|
||||
}
|
||||
if (eh.TryEnd != null)
|
||||
{
|
||||
splitPoints.Add(eh.TryEnd);
|
||||
}
|
||||
if (eh.HandlerStart != null)
|
||||
{
|
||||
splitPoints.Add(eh.HandlerStart);
|
||||
}
|
||||
if (eh.HandlerEnd != null)
|
||||
{
|
||||
splitPoints.Add(eh.HandlerEnd);
|
||||
}
|
||||
if (eh.FilterStart != null)
|
||||
{
|
||||
splitPoints.Add(eh.FilterStart);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0, n = insts.Count; i < n; i++)
|
||||
{
|
||||
Instruction curInst = insts[i];
|
||||
Instruction nextInst = i + 1 < n ? insts[i + 1] : null;
|
||||
switch (curInst.OpCode.FlowControl)
|
||||
{
|
||||
case FlowControl.Branch:
|
||||
{
|
||||
if (nextInst != null)
|
||||
{
|
||||
splitPoints.Add(nextInst);
|
||||
}
|
||||
splitPoints.Add((Instruction)curInst.Operand);
|
||||
break;
|
||||
}
|
||||
case FlowControl.Cond_Branch:
|
||||
{
|
||||
if (nextInst != null)
|
||||
{
|
||||
splitPoints.Add(nextInst);
|
||||
}
|
||||
if (curInst.Operand is Instruction targetInst)
|
||||
{
|
||||
splitPoints.Add(targetInst);
|
||||
}
|
||||
else if (curInst.Operand is Instruction[] targetInsts)
|
||||
{
|
||||
foreach (var target in targetInsts)
|
||||
{
|
||||
splitPoints.Add(target);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FlowControl.Return:
|
||||
{
|
||||
if (nextInst != null)
|
||||
{
|
||||
splitPoints.Add(nextInst);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FlowControl.Throw:
|
||||
{
|
||||
if (nextInst != null)
|
||||
{
|
||||
splitPoints.Add(nextInst);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return splitPoints;
|
||||
}
|
||||
|
||||
|
||||
private void BuildBasicBlocks(MethodDef method, HashSet<Instruction> splitPoints)
|
||||
{
|
||||
var insts = method.Body.Instructions;
|
||||
|
||||
|
||||
BasicBlock curBlock = new BasicBlock();
|
||||
foreach (Instruction inst in insts)
|
||||
{
|
||||
if (splitPoints.Contains(inst) && curBlock.instructions.Count > 0)
|
||||
{
|
||||
_blocks.Add(curBlock);
|
||||
curBlock = new BasicBlock();
|
||||
}
|
||||
curBlock.instructions.Add(inst);
|
||||
_inst2BlockMap.Add(inst, curBlock);
|
||||
}
|
||||
if (curBlock.instructions.Count > 0)
|
||||
{
|
||||
_blocks.Add(curBlock);
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildInOutGraph(MethodDef method)
|
||||
{
|
||||
var insts = method.Body.Instructions;
|
||||
for (int i = 0, n = _blocks.Count; i < n; i++)
|
||||
{
|
||||
BasicBlock curBlock = _blocks[i];
|
||||
BasicBlock nextBlock = i + 1 < n ? _blocks[i + 1] : null;
|
||||
Instruction lastInst = curBlock.instructions.Last();
|
||||
switch (lastInst.OpCode.FlowControl)
|
||||
{
|
||||
case FlowControl.Branch:
|
||||
{
|
||||
Instruction targetInst = (Instruction)lastInst.Operand;
|
||||
BasicBlock targetBlock = GetBasicBlockByInstruction(targetInst);
|
||||
curBlock.AddTargetBasicBlock(targetBlock);
|
||||
break;
|
||||
}
|
||||
case FlowControl.Cond_Branch:
|
||||
{
|
||||
if (lastInst.Operand is Instruction targetInst)
|
||||
{
|
||||
BasicBlock targetBlock = GetBasicBlockByInstruction(targetInst);
|
||||
curBlock.AddTargetBasicBlock(targetBlock);
|
||||
}
|
||||
else if (lastInst.Operand is Instruction[] targetInsts)
|
||||
{
|
||||
foreach (var target in targetInsts)
|
||||
{
|
||||
BasicBlock targetBlock = GetBasicBlockByInstruction(target);
|
||||
curBlock.AddTargetBasicBlock(targetBlock);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Invalid operand type for conditional branch");
|
||||
}
|
||||
if (nextBlock != null)
|
||||
{
|
||||
curBlock.AddTargetBasicBlock(nextBlock);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FlowControl.Call:
|
||||
case FlowControl.Next:
|
||||
{
|
||||
if (nextBlock != null)
|
||||
{
|
||||
curBlock.AddTargetBasicBlock(nextBlock);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FlowControl.Return:
|
||||
case FlowControl.Throw:
|
||||
{
|
||||
break;
|
||||
}
|
||||
default: throw new NotSupportedException($"Unsupported flow control: {lastInst.OpCode.FlowControl} in method {method.FullName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static HashSet<BasicBlock> FindLoopBlocks(List<BasicBlock> allBlocks)
|
||||
{
|
||||
// Tarjan算法找强连通分量
|
||||
var sccList = FindStronglyConnectedComponents(allBlocks);
|
||||
|
||||
// 筛选有效循环
|
||||
var loopBlocks = new HashSet<BasicBlock>();
|
||||
foreach (var scc in sccList)
|
||||
{
|
||||
// 有效循环需满足以下条件之一:
|
||||
// 1. 分量包含多个块
|
||||
// 2. 单个块有自环(跳转自己)
|
||||
if (scc.Count > 1 ||
|
||||
(scc.Count == 1 && scc[0].outBlocks.Contains(scc[0])))
|
||||
{
|
||||
foreach (var block in scc)
|
||||
{
|
||||
loopBlocks.Add(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
return loopBlocks;
|
||||
}
|
||||
|
||||
private static List<List<BasicBlock>> FindStronglyConnectedComponents(List<BasicBlock> allBlocks)
|
||||
{
|
||||
int index = 0;
|
||||
var stack = new Stack<BasicBlock>();
|
||||
var indexes = new Dictionary<BasicBlock, int>();
|
||||
var lowLinks = new Dictionary<BasicBlock, int>();
|
||||
var onStack = new HashSet<BasicBlock>();
|
||||
var sccList = new List<List<BasicBlock>>();
|
||||
|
||||
foreach (var block in allBlocks.Where(b => !indexes.ContainsKey(b)))
|
||||
{
|
||||
StrongConnect(block);
|
||||
}
|
||||
|
||||
return sccList;
|
||||
|
||||
void StrongConnect(BasicBlock v)
|
||||
{
|
||||
indexes[v] = index;
|
||||
lowLinks[v] = index;
|
||||
index++;
|
||||
stack.Push(v);
|
||||
onStack.Add(v);
|
||||
|
||||
foreach (var w in v.outBlocks)
|
||||
{
|
||||
if (!indexes.ContainsKey(w))
|
||||
{
|
||||
StrongConnect(w);
|
||||
lowLinks[v] = System.Math.Min(lowLinks[v], lowLinks[w]);
|
||||
}
|
||||
else if (onStack.Contains(w))
|
||||
{
|
||||
lowLinks[v] = System.Math.Min(lowLinks[v], indexes[w]);
|
||||
}
|
||||
}
|
||||
|
||||
if (lowLinks[v] == indexes[v])
|
||||
{
|
||||
var scc = new List<BasicBlock>();
|
||||
BasicBlock w;
|
||||
do
|
||||
{
|
||||
w = stack.Pop();
|
||||
onStack.Remove(w);
|
||||
scc.Add(w);
|
||||
} while (!w.Equals(v));
|
||||
sccList.Add(scc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 77c19c023bb7f77489998d994a3be1bd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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(AssetUtility).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,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76438ce96146edd469872feada7857ba
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e9557733f180764692756653eb60f88
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,967 +0,0 @@
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace Obfuz.Emit
|
||||
{
|
||||
enum EvalDataType
|
||||
{
|
||||
None,
|
||||
Int32,
|
||||
Int64,
|
||||
Float,
|
||||
Double,
|
||||
I,
|
||||
Ref,
|
||||
ValueType,
|
||||
Token,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
struct EvalDataTypeWithSig
|
||||
{
|
||||
public readonly EvalDataType type;
|
||||
public readonly TypeSig typeSig;
|
||||
public EvalDataTypeWithSig(EvalDataType type, TypeSig typeSig)
|
||||
{
|
||||
this.type = type;
|
||||
this.typeSig = typeSig;
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{type} ({typeSig})";
|
||||
}
|
||||
}
|
||||
|
||||
class InstructionParameterInfo
|
||||
{
|
||||
public readonly EvalDataType op1;
|
||||
public readonly EvalDataType op2;
|
||||
public readonly EvalDataType retType;
|
||||
public InstructionParameterInfo(EvalDataType op1, EvalDataType op2, EvalDataType retType)
|
||||
{
|
||||
this.op1 = op1;
|
||||
this.op2 = op2;
|
||||
this.retType = retType;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class EvalStackState
|
||||
{
|
||||
public bool visited;
|
||||
|
||||
public readonly List<EvalDataTypeWithSig> inputStackDatas = new List<EvalDataTypeWithSig>();
|
||||
public readonly List<EvalDataTypeWithSig> runStackDatas = new List<EvalDataTypeWithSig>();
|
||||
}
|
||||
|
||||
class EvalStackCalculator
|
||||
{
|
||||
private readonly MethodDef _method;
|
||||
private readonly BasicBlockCollection _basicBlocks;
|
||||
private readonly Dictionary<Instruction, InstructionParameterInfo> _instructionParameterInfos = new Dictionary<Instruction, InstructionParameterInfo>();
|
||||
private readonly Dictionary<Instruction, EvalDataType> _evalStackTopDataTypeAfterInstructions = new Dictionary<Instruction, EvalDataType>();
|
||||
private readonly Dictionary<BasicBlock, EvalStackState> _blockEvalStackStates;
|
||||
|
||||
public EvalStackCalculator(MethodDef method)
|
||||
{
|
||||
_method = method;
|
||||
_basicBlocks = new BasicBlockCollection(method, false);
|
||||
_blockEvalStackStates = _basicBlocks.Blocks.ToDictionary(b => b, b => new EvalStackState());
|
||||
|
||||
SimulateRunAllBlocks();
|
||||
}
|
||||
|
||||
public BasicBlockCollection BasicBlockCollection => _basicBlocks;
|
||||
|
||||
public bool TryGetParameterInfo(Instruction inst, out InstructionParameterInfo info)
|
||||
{
|
||||
return _instructionParameterInfos.TryGetValue(inst, out info);
|
||||
}
|
||||
|
||||
public bool TryGetPushResult(Instruction inst, out EvalDataType result)
|
||||
{
|
||||
return _evalStackTopDataTypeAfterInstructions.TryGetValue(inst, out result);
|
||||
}
|
||||
|
||||
public EvalStackState GetEvalStackState(BasicBlock basicBlock)
|
||||
{
|
||||
return _blockEvalStackStates[basicBlock];
|
||||
}
|
||||
|
||||
private void PushStack(List<EvalDataTypeWithSig> datas, TypeSig type)
|
||||
{
|
||||
type = type.RemovePinnedAndModifiers();
|
||||
switch (type.ElementType)
|
||||
{
|
||||
case ElementType.Void: break;
|
||||
case ElementType.Boolean:
|
||||
case ElementType.Char:
|
||||
case ElementType.I1:
|
||||
case ElementType.U1:
|
||||
case ElementType.I2:
|
||||
case ElementType.U2:
|
||||
case ElementType.I4:
|
||||
case ElementType.U4:
|
||||
datas.Add(new EvalDataTypeWithSig(EvalDataType.Int32, null));
|
||||
break;
|
||||
case ElementType.I8:
|
||||
case ElementType.U8:
|
||||
datas.Add(new EvalDataTypeWithSig(EvalDataType.Int64, null));
|
||||
break;
|
||||
case ElementType.R4:
|
||||
datas.Add(new EvalDataTypeWithSig(EvalDataType.Float, null));
|
||||
break;
|
||||
case ElementType.R8:
|
||||
datas.Add(new EvalDataTypeWithSig(EvalDataType.Double, null));
|
||||
break;
|
||||
case ElementType.I:
|
||||
case ElementType.U:
|
||||
case ElementType.Ptr:
|
||||
case ElementType.FnPtr:
|
||||
case ElementType.ByRef:
|
||||
datas.Add(new EvalDataTypeWithSig(EvalDataType.I, null));
|
||||
break;
|
||||
case ElementType.String:
|
||||
case ElementType.Class:
|
||||
case ElementType.Array:
|
||||
case ElementType.SZArray:
|
||||
case ElementType.Object:
|
||||
datas.Add(new EvalDataTypeWithSig(EvalDataType.Ref, type));
|
||||
break;
|
||||
case ElementType.ValueType:
|
||||
{
|
||||
TypeDef typeDef = type.ToTypeDefOrRef().ResolveTypeDefThrow();
|
||||
if (typeDef.IsEnum)
|
||||
{
|
||||
PushStack(datas, typeDef.GetEnumUnderlyingType());
|
||||
}
|
||||
else
|
||||
{
|
||||
PushStack(datas, new EvalDataTypeWithSig(EvalDataType.ValueType, type));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ElementType.GenericInst:
|
||||
{
|
||||
GenericInstSig genericInstSig = (GenericInstSig)type;
|
||||
TypeDef typeDef = genericInstSig.GenericType.ToTypeDefOrRef().ResolveTypeDefThrow();
|
||||
if (!typeDef.IsValueType)
|
||||
{
|
||||
PushStack(datas, new EvalDataTypeWithSig(EvalDataType.Ref, type));
|
||||
}
|
||||
else if (typeDef.IsEnum)
|
||||
{
|
||||
PushStack(datas, typeDef.GetEnumUnderlyingType());
|
||||
}
|
||||
else
|
||||
{
|
||||
PushStack(datas, new EvalDataTypeWithSig(EvalDataType.ValueType, type));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ElementType.TypedByRef:
|
||||
{
|
||||
// TypedByRef is a special type used in dynamic method invocation and reflection.
|
||||
// It is treated as a reference type in the evaluation stack.
|
||||
PushStack(datas, new EvalDataTypeWithSig(EvalDataType.ValueType, type));
|
||||
break;
|
||||
}
|
||||
case ElementType.Var:
|
||||
case ElementType.MVar:
|
||||
PushStack(datas, new EvalDataTypeWithSig(EvalDataType.ValueType, type));
|
||||
break;
|
||||
case ElementType.ValueArray:
|
||||
case ElementType.R:
|
||||
case ElementType.CModOpt:
|
||||
case ElementType.CModReqd:
|
||||
case ElementType.Internal:
|
||||
case ElementType.Module:
|
||||
case ElementType.Sentinel:
|
||||
PushStack(datas, EvalDataType.Unknown);
|
||||
break;
|
||||
|
||||
default: throw new Exception($"Unsupported type: {type} in method: {_method.FullName}.");
|
||||
}
|
||||
}
|
||||
|
||||
private void PushStack(List<EvalDataTypeWithSig> datas, ITypeDefOrRef type)
|
||||
{
|
||||
PushStack(datas, type.ToTypeSig());
|
||||
}
|
||||
|
||||
private void PushStack(List<EvalDataTypeWithSig> datas, EvalDataType type)
|
||||
{
|
||||
Assert.IsTrue(type != EvalDataType.ValueType, "Cannot push EvalDataType.Value without type sig onto the stack.");
|
||||
datas.Add(new EvalDataTypeWithSig(type, null));
|
||||
}
|
||||
|
||||
private void PushStack(List<EvalDataTypeWithSig> datas, EvalDataTypeWithSig type)
|
||||
{
|
||||
datas.Add(type);
|
||||
}
|
||||
|
||||
private void PushStackObject(List<EvalDataTypeWithSig> datas)
|
||||
{
|
||||
datas.Add(new EvalDataTypeWithSig(EvalDataType.Ref, _method.Module.CorLibTypes.Object));
|
||||
}
|
||||
|
||||
private EvalDataType CalcBasicBinOpRetType(EvalDataType op1, EvalDataType op2)
|
||||
{
|
||||
switch (op1)
|
||||
{
|
||||
case EvalDataType.Int32:
|
||||
{
|
||||
switch (op2)
|
||||
{
|
||||
case EvalDataType.Int32: return EvalDataType.Int32;
|
||||
case EvalDataType.Int64: return EvalDataType.Int64;
|
||||
case EvalDataType.I: return EvalDataType.I;
|
||||
default: throw new Exception($"Unsupported operand type: {op2} for {op1} in binary operation.");
|
||||
}
|
||||
}
|
||||
case EvalDataType.Int64:
|
||||
{
|
||||
switch (op2)
|
||||
{
|
||||
case EvalDataType.Int32: return EvalDataType.Int64;
|
||||
case EvalDataType.Int64:
|
||||
case EvalDataType.I:
|
||||
return EvalDataType.Int64;
|
||||
default: throw new Exception($"Unsupported operand type: {op2} for {op1} in binary operation.");
|
||||
}
|
||||
}
|
||||
case EvalDataType.I:
|
||||
{
|
||||
switch (op2)
|
||||
{
|
||||
case EvalDataType.Int32: return EvalDataType.I;
|
||||
case EvalDataType.Int64: return EvalDataType.Int64;
|
||||
case EvalDataType.I: return EvalDataType.I;
|
||||
default: throw new Exception($"Unsupported operand type: {op2} for {op1} in binary operation.");
|
||||
}
|
||||
}
|
||||
case EvalDataType.Float:
|
||||
{
|
||||
switch (op2)
|
||||
{
|
||||
case EvalDataType.Float: return EvalDataType.Float;
|
||||
case EvalDataType.Double: return EvalDataType.Double;
|
||||
default: throw new Exception($"Unsupported operand type: {op2} for {op1} in binary operation.");
|
||||
}
|
||||
}
|
||||
case EvalDataType.Double:
|
||||
{
|
||||
switch (op2)
|
||||
{
|
||||
case EvalDataType.Float:
|
||||
case EvalDataType.Double: return EvalDataType.Double;
|
||||
default: throw new Exception($"Unsupported operand type: {op2} for {op1} in binary operation.");
|
||||
}
|
||||
}
|
||||
default: throw new Exception($"Unsupported operand type: {op1} in binary operation.");
|
||||
}
|
||||
}
|
||||
|
||||
private void SimulateRunAllBlocks()
|
||||
{
|
||||
bool methodHasReturnValue = !MetaUtil.IsVoidType(_method.ReturnType);
|
||||
|
||||
CilBody body = _method.Body;
|
||||
if (body.HasExceptionHandlers)
|
||||
{
|
||||
foreach (ExceptionHandler handler in body.ExceptionHandlers)
|
||||
{
|
||||
if (handler.IsFilter)
|
||||
{
|
||||
BasicBlock bb = _basicBlocks.GetBasicBlockByInstruction(handler.FilterStart);
|
||||
var inputStackDatas = _blockEvalStackStates[bb].inputStackDatas;
|
||||
if (inputStackDatas.Count == 0)
|
||||
{
|
||||
inputStackDatas.Add(new EvalDataTypeWithSig(EvalDataType.Ref, handler.CatchType.ToTypeSig()));
|
||||
}
|
||||
}
|
||||
if (handler.IsCatch || handler.IsFilter)
|
||||
{
|
||||
BasicBlock bb = _basicBlocks.GetBasicBlockByInstruction(handler.HandlerStart);
|
||||
var inputStackDatas = _blockEvalStackStates[bb].inputStackDatas;
|
||||
if (inputStackDatas.Count == 0)
|
||||
{
|
||||
inputStackDatas.Add(new EvalDataTypeWithSig(EvalDataType.Ref, handler.CatchType.ToTypeSig()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var newPushedDatas = new List<EvalDataTypeWithSig>();
|
||||
IList<TypeSig> methodTypeGenericArgument = _method.DeclaringType.GenericParameters.Count > 0
|
||||
? (IList<TypeSig>)_method.DeclaringType.GenericParameters.Select(p => (TypeSig)new GenericVar(p.Number)).ToList()
|
||||
: null;
|
||||
IList<TypeSig> methodMethodGenericArgument = _method.GenericParameters.Count > 0
|
||||
? (IList<TypeSig>)_method.GenericParameters.Select(p => (TypeSig)new GenericMVar(p.Number)).ToList()
|
||||
: null;
|
||||
var gac = new GenericArgumentContext(methodTypeGenericArgument, methodMethodGenericArgument);
|
||||
var corLibTypes = _method.Module.CorLibTypes;
|
||||
|
||||
var blockWalkStack = new Stack<BasicBlock>(_basicBlocks.Blocks.Reverse());
|
||||
while (blockWalkStack.Count > 0)
|
||||
{
|
||||
BasicBlock block = blockWalkStack.Pop();
|
||||
EvalStackState state = _blockEvalStackStates[block];
|
||||
if (state.visited)
|
||||
continue;
|
||||
state.visited = true;
|
||||
state.runStackDatas.AddRange(state.inputStackDatas);
|
||||
List<EvalDataTypeWithSig> stackDatas = state.runStackDatas;
|
||||
foreach (var inst in block.instructions)
|
||||
{
|
||||
int stackSize = stackDatas.Count;
|
||||
newPushedDatas.Clear();
|
||||
switch (inst.OpCode.Code)
|
||||
{
|
||||
case Code.Nop: break;
|
||||
case Code.Break: break;
|
||||
case Code.Ldarg_0:
|
||||
case Code.Ldarg_1:
|
||||
case Code.Ldarg_2:
|
||||
case Code.Ldarg_3:
|
||||
case Code.Ldarg:
|
||||
case Code.Ldarg_S:
|
||||
{
|
||||
PushStack(newPushedDatas, inst.GetParameter(_method.Parameters).Type);
|
||||
break;
|
||||
}
|
||||
case Code.Ldarga:
|
||||
case Code.Ldarga_S:
|
||||
{
|
||||
PushStack(newPushedDatas, EvalDataType.I);
|
||||
break;
|
||||
}
|
||||
case Code.Ldloc_0:
|
||||
case Code.Ldloc_1:
|
||||
case Code.Ldloc_2:
|
||||
case Code.Ldloc_3:
|
||||
case Code.Ldloc:
|
||||
case Code.Ldloc_S:
|
||||
{
|
||||
PushStack(newPushedDatas, inst.GetLocal(body.Variables).Type);
|
||||
break;
|
||||
}
|
||||
case Code.Ldloca:
|
||||
case Code.Ldloca_S:
|
||||
{
|
||||
PushStack(newPushedDatas, EvalDataType.I);
|
||||
break;
|
||||
}
|
||||
case Code.Stloc_0:
|
||||
case Code.Stloc_1:
|
||||
case Code.Stloc_2:
|
||||
case Code.Stloc_3:
|
||||
case Code.Stloc:
|
||||
case Code.Stloc_S:
|
||||
{
|
||||
Assert.IsTrue(stackSize > 0);
|
||||
break;
|
||||
}
|
||||
case Code.Starg:
|
||||
case Code.Starg_S:
|
||||
{
|
||||
Assert.IsTrue(stackSize > 0);
|
||||
break;
|
||||
}
|
||||
case Code.Ldnull:
|
||||
{
|
||||
PushStackObject(newPushedDatas);
|
||||
break;
|
||||
}
|
||||
case Code.Ldc_I4_M1:
|
||||
case Code.Ldc_I4_0:
|
||||
case Code.Ldc_I4_1:
|
||||
case Code.Ldc_I4_2:
|
||||
case Code.Ldc_I4_3:
|
||||
case Code.Ldc_I4_4:
|
||||
case Code.Ldc_I4_5:
|
||||
case Code.Ldc_I4_6:
|
||||
case Code.Ldc_I4_7:
|
||||
case Code.Ldc_I4_8:
|
||||
case Code.Ldc_I4:
|
||||
case Code.Ldc_I4_S:
|
||||
{
|
||||
PushStack(newPushedDatas, EvalDataType.Int32);
|
||||
break;
|
||||
}
|
||||
case Code.Ldc_I8:
|
||||
{
|
||||
PushStack(newPushedDatas, EvalDataType.Int64);
|
||||
break;
|
||||
}
|
||||
case Code.Ldc_R4:
|
||||
{
|
||||
PushStack(newPushedDatas, EvalDataType.Float);
|
||||
break;
|
||||
}
|
||||
case Code.Ldc_R8:
|
||||
{
|
||||
PushStack(newPushedDatas, EvalDataType.Double);
|
||||
break;
|
||||
}
|
||||
case Code.Dup:
|
||||
{
|
||||
Assert.IsTrue(stackSize > 0);
|
||||
EvalDataTypeWithSig type = stackDatas[stackSize - 1];
|
||||
PushStack(newPushedDatas, type);
|
||||
PushStack(newPushedDatas, type);
|
||||
break;
|
||||
}
|
||||
case Code.Pop:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case Code.Jmp:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case Code.Call:
|
||||
case Code.Callvirt:
|
||||
{
|
||||
IMethod calledMethod = (IMethod)inst.Operand;
|
||||
MethodSig methodSig = MetaUtil.GetInflatedMethodSig(calledMethod, gac);
|
||||
PushStack(newPushedDatas, methodSig.RetType);
|
||||
break;
|
||||
}
|
||||
case Code.Calli:
|
||||
{
|
||||
MethodSig methodSig = (MethodSig)inst.Operand;
|
||||
PushStack(newPushedDatas, methodSig.RetType);
|
||||
break;
|
||||
}
|
||||
case Code.Ret:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case Code.Br:
|
||||
case Code.Br_S:
|
||||
case Code.Brfalse:
|
||||
case Code.Brfalse_S:
|
||||
case Code.Brtrue:
|
||||
case Code.Brtrue_S:
|
||||
case Code.Beq:
|
||||
case Code.Beq_S:
|
||||
case Code.Bge:
|
||||
case Code.Bge_S:
|
||||
case Code.Bge_Un:
|
||||
case Code.Bge_Un_S:
|
||||
case Code.Bgt:
|
||||
case Code.Bgt_S:
|
||||
case Code.Bgt_Un:
|
||||
case Code.Bgt_Un_S:
|
||||
case Code.Ble:
|
||||
case Code.Ble_S:
|
||||
case Code.Ble_Un:
|
||||
case Code.Ble_Un_S:
|
||||
case Code.Blt:
|
||||
case Code.Blt_S:
|
||||
case Code.Blt_Un:
|
||||
case Code.Blt_Un_S:
|
||||
case Code.Bne_Un:
|
||||
case Code.Bne_Un_S:
|
||||
{
|
||||
// Branch instructions do not change the stack.
|
||||
break;
|
||||
}
|
||||
case Code.Ceq:
|
||||
case Code.Cgt:
|
||||
case Code.Cgt_Un:
|
||||
case Code.Clt:
|
||||
case Code.Clt_Un:
|
||||
{
|
||||
Assert.IsTrue(stackSize >= 2);
|
||||
EvalDataType op2 = stackDatas[stackSize - 1].type;
|
||||
EvalDataType op1 = stackDatas[stackSize - 2].type;
|
||||
EvalDataType ret = EvalDataType.Int32;
|
||||
_instructionParameterInfos.Add(inst, new InstructionParameterInfo(op1, op2, ret));
|
||||
PushStack(newPushedDatas, ret);
|
||||
break;
|
||||
}
|
||||
case Code.Switch:
|
||||
{
|
||||
// Switch instruction does not change the stack.
|
||||
break;
|
||||
}
|
||||
case Code.Ldind_I1:
|
||||
case Code.Ldind_U1:
|
||||
case Code.Ldind_I2:
|
||||
case Code.Ldind_U2:
|
||||
case Code.Ldind_I4:
|
||||
case Code.Ldind_U4:
|
||||
{
|
||||
Assert.IsTrue(stackSize > 0);
|
||||
PushStack(newPushedDatas, EvalDataType.Int32);
|
||||
break;
|
||||
}
|
||||
case Code.Ldind_I8:
|
||||
{
|
||||
Assert.IsTrue(stackSize > 0);
|
||||
PushStack(newPushedDatas, EvalDataType.Int64);
|
||||
break;
|
||||
}
|
||||
case Code.Ldind_I:
|
||||
{
|
||||
Assert.IsTrue(stackSize > 0);
|
||||
PushStack(newPushedDatas, EvalDataType.I);
|
||||
break;
|
||||
}
|
||||
case Code.Ldind_Ref:
|
||||
{
|
||||
Assert.IsTrue(stackSize > 0);
|
||||
PushStackObject(newPushedDatas);
|
||||
break;
|
||||
}
|
||||
case Code.Ldind_R4:
|
||||
{
|
||||
Assert.IsTrue(stackSize > 0);
|
||||
PushStack(newPushedDatas, EvalDataType.Float);
|
||||
break;
|
||||
}
|
||||
case Code.Ldind_R8:
|
||||
{
|
||||
Assert.IsTrue(stackSize > 0);
|
||||
PushStack(newPushedDatas, EvalDataType.Double);
|
||||
break;
|
||||
}
|
||||
case Code.Stind_I1:
|
||||
case Code.Stind_I2:
|
||||
case Code.Stind_I4:
|
||||
case Code.Stind_I8:
|
||||
case Code.Stind_I:
|
||||
case Code.Stind_R4:
|
||||
case Code.Stind_R8:
|
||||
case Code.Stind_Ref:
|
||||
{
|
||||
Assert.IsTrue(stackSize >= 2);
|
||||
break;
|
||||
}
|
||||
|
||||
case Code.Add:
|
||||
case Code.Add_Ovf:
|
||||
case Code.Add_Ovf_Un:
|
||||
case Code.Sub:
|
||||
case Code.Sub_Ovf:
|
||||
case Code.Sub_Ovf_Un:
|
||||
case Code.Mul:
|
||||
case Code.Mul_Ovf:
|
||||
case Code.Mul_Ovf_Un:
|
||||
case Code.Div:
|
||||
case Code.Div_Un:
|
||||
case Code.Rem:
|
||||
case Code.Rem_Un:
|
||||
|
||||
case Code.And:
|
||||
case Code.Or:
|
||||
case Code.Xor:
|
||||
{
|
||||
Assert.IsTrue(stackSize >= 2);
|
||||
EvalDataType op2 = stackDatas[stackSize - 1].type;
|
||||
EvalDataType op1 = stackDatas[stackSize - 2].type;
|
||||
EvalDataType ret = CalcBasicBinOpRetType(op1, op2);
|
||||
_instructionParameterInfos.Add(inst, new InstructionParameterInfo(op1, op2, ret));
|
||||
PushStack(newPushedDatas, ret);
|
||||
break;
|
||||
}
|
||||
case Code.Shl:
|
||||
case Code.Shr:
|
||||
case Code.Shr_Un:
|
||||
{
|
||||
Assert.IsTrue(stackSize >= 2);
|
||||
EvalDataType op2 = stackDatas[stackSize - 1].type;
|
||||
EvalDataType op1 = stackDatas[stackSize - 2].type;
|
||||
if (op1 != EvalDataType.Int32 && op1 != EvalDataType.Int64 && op1 != EvalDataType.I)
|
||||
throw new Exception($"Unsupported operand type: {op1} in shift operation.");
|
||||
if (op2 != EvalDataType.Int32 && op2 != EvalDataType.Int64)
|
||||
throw new Exception($"Unsupported operand type: {op2} for {op1} in shift operation.");
|
||||
EvalDataType ret = op1;
|
||||
_instructionParameterInfos.Add(inst, new InstructionParameterInfo(op1, op2, ret));
|
||||
PushStack(newPushedDatas, ret);
|
||||
break;
|
||||
}
|
||||
case Code.Neg:
|
||||
{
|
||||
Assert.IsTrue(stackSize > 0);
|
||||
EvalDataType op = stackDatas[stackSize - 1].type;
|
||||
EvalDataType ret = op;
|
||||
switch (op)
|
||||
{
|
||||
case EvalDataType.Int32:
|
||||
case EvalDataType.Int64:
|
||||
case EvalDataType.I:
|
||||
case EvalDataType.Float:
|
||||
case EvalDataType.Double:
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Unsupported operand type: {op} in unary operation.");
|
||||
}
|
||||
_instructionParameterInfos.Add(inst, new InstructionParameterInfo(op, EvalDataType.None, ret));
|
||||
PushStack(newPushedDatas, ret);
|
||||
break;
|
||||
}
|
||||
case Code.Not:
|
||||
{
|
||||
Assert.IsTrue(stackSize > 0);
|
||||
EvalDataType op = stackDatas[stackSize - 1].type;
|
||||
EvalDataType ret = op;
|
||||
if (op != EvalDataType.Int32 && op != EvalDataType.Int64 && op != EvalDataType.I)
|
||||
throw new Exception($"Unsupported operand type: {op} in unary operation.");
|
||||
_instructionParameterInfos.Add(inst, new InstructionParameterInfo(op, EvalDataType.None, ret));
|
||||
PushStack(newPushedDatas, ret);
|
||||
break;
|
||||
}
|
||||
case Code.Conv_I1:
|
||||
case Code.Conv_U1:
|
||||
case Code.Conv_I2:
|
||||
case Code.Conv_U2:
|
||||
case Code.Conv_I4:
|
||||
case Code.Conv_U4:
|
||||
{
|
||||
PushStack(newPushedDatas, EvalDataType.Int32);
|
||||
break;
|
||||
}
|
||||
case Code.Conv_I8:
|
||||
case Code.Conv_U8:
|
||||
{
|
||||
PushStack(newPushedDatas, EvalDataType.Int64);
|
||||
break;
|
||||
}
|
||||
case Code.Conv_I:
|
||||
case Code.Conv_U:
|
||||
{
|
||||
PushStack(newPushedDatas, EvalDataType.I);
|
||||
break;
|
||||
}
|
||||
case Code.Conv_R4:
|
||||
{
|
||||
PushStack(newPushedDatas, EvalDataType.Float);
|
||||
break;
|
||||
}
|
||||
case Code.Conv_R8:
|
||||
{
|
||||
PushStack(newPushedDatas, EvalDataType.Double);
|
||||
break;
|
||||
}
|
||||
case Code.Conv_Ovf_I1:
|
||||
case Code.Conv_Ovf_I1_Un:
|
||||
case Code.Conv_Ovf_U1:
|
||||
case Code.Conv_Ovf_U1_Un:
|
||||
case Code.Conv_Ovf_I2:
|
||||
case Code.Conv_Ovf_I2_Un:
|
||||
case Code.Conv_Ovf_U2:
|
||||
case Code.Conv_Ovf_U2_Un:
|
||||
case Code.Conv_Ovf_I4:
|
||||
case Code.Conv_Ovf_I4_Un:
|
||||
case Code.Conv_Ovf_U4:
|
||||
case Code.Conv_Ovf_U4_Un:
|
||||
{
|
||||
PushStack(newPushedDatas, EvalDataType.Int32);
|
||||
break;
|
||||
}
|
||||
case Code.Conv_Ovf_I8:
|
||||
case Code.Conv_Ovf_I8_Un:
|
||||
case Code.Conv_Ovf_U8:
|
||||
case Code.Conv_Ovf_U8_Un:
|
||||
{
|
||||
PushStack(newPushedDatas, EvalDataType.Int64);
|
||||
break;
|
||||
}
|
||||
case Code.Conv_Ovf_I:
|
||||
case Code.Conv_Ovf_I_Un:
|
||||
case Code.Conv_Ovf_U:
|
||||
case Code.Conv_Ovf_U_Un:
|
||||
{
|
||||
PushStack(newPushedDatas, EvalDataType.I);
|
||||
break;
|
||||
}
|
||||
case Code.Conv_R_Un:
|
||||
{
|
||||
PushStack(newPushedDatas, EvalDataType.Double);
|
||||
break;
|
||||
}
|
||||
case Code.Cpobj:
|
||||
case Code.Initobj:
|
||||
case Code.Stobj:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case Code.Ldobj:
|
||||
{
|
||||
PushStack(newPushedDatas, (ITypeDefOrRef)inst.Operand);
|
||||
break;
|
||||
}
|
||||
case Code.Ldstr:
|
||||
{
|
||||
PushStack(newPushedDatas, new EvalDataTypeWithSig(EvalDataType.Ref, corLibTypes.String));
|
||||
break;
|
||||
}
|
||||
case Code.Newobj:
|
||||
{
|
||||
IMethod ctor = (IMethod)inst.Operand;
|
||||
PushStack(newPushedDatas, ctor.DeclaringType);
|
||||
break;
|
||||
}
|
||||
case Code.Castclass:
|
||||
{
|
||||
PushStack(newPushedDatas, (ITypeDefOrRef)inst.Operand);
|
||||
break;
|
||||
}
|
||||
case Code.Isinst:
|
||||
{
|
||||
Assert.IsTrue(stackSize > 0);
|
||||
var obj = stackDatas[stackSize - 1];
|
||||
Assert.IsTrue(obj.type == EvalDataType.Ref);
|
||||
PushStack(newPushedDatas, obj);
|
||||
break;
|
||||
}
|
||||
case Code.Unbox:
|
||||
{
|
||||
Assert.IsTrue(stackSize > 0);
|
||||
PushStack(newPushedDatas, EvalDataType.I);
|
||||
break;
|
||||
}
|
||||
case Code.Unbox_Any:
|
||||
{
|
||||
Assert.IsTrue(stackSize > 0);
|
||||
PushStack(newPushedDatas, (ITypeDefOrRef)inst.Operand);
|
||||
break;
|
||||
}
|
||||
case Code.Box:
|
||||
{
|
||||
Assert.IsTrue(stackSize > 0);
|
||||
PushStackObject(newPushedDatas);
|
||||
break;
|
||||
}
|
||||
case Code.Throw:
|
||||
{
|
||||
// Throw instruction does not change the stack.
|
||||
break;
|
||||
}
|
||||
case Code.Rethrow:
|
||||
{
|
||||
// Rethrow instruction does not change the stack.
|
||||
break;
|
||||
}
|
||||
case Code.Ldfld:
|
||||
case Code.Ldsfld:
|
||||
{
|
||||
IField field = (IField)inst.Operand;
|
||||
TypeSig fieldType = MetaUtil.InflateFieldSig(field, gac);
|
||||
PushStack(newPushedDatas, fieldType);
|
||||
break;
|
||||
}
|
||||
case Code.Ldflda:
|
||||
case Code.Ldsflda:
|
||||
{
|
||||
PushStack(newPushedDatas, EvalDataType.I);
|
||||
break;
|
||||
}
|
||||
case Code.Stfld:
|
||||
case Code.Stsfld:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case Code.Newarr:
|
||||
{
|
||||
Assert.IsTrue(stackSize > 0);
|
||||
PushStack(newPushedDatas, new SZArraySig(((ITypeDefOrRef)inst.Operand).ToTypeSig()));
|
||||
break;
|
||||
}
|
||||
case Code.Ldlen:
|
||||
{
|
||||
Assert.IsTrue(stackSize > 0);
|
||||
PushStack(newPushedDatas, EvalDataType.I);
|
||||
break;
|
||||
}
|
||||
case Code.Ldelema:
|
||||
{
|
||||
Assert.IsTrue(stackSize >= 2);
|
||||
PushStack(newPushedDatas, EvalDataType.I);
|
||||
break;
|
||||
}
|
||||
case Code.Ldelem_I1:
|
||||
case Code.Ldelem_U1:
|
||||
case Code.Ldelem_I2:
|
||||
case Code.Ldelem_U2:
|
||||
case Code.Ldelem_I4:
|
||||
case Code.Ldelem_U4:
|
||||
{
|
||||
Assert.IsTrue(stackSize >= 2);
|
||||
PushStack(newPushedDatas, EvalDataType.Int32);
|
||||
break;
|
||||
}
|
||||
case Code.Ldelem_I8:
|
||||
{
|
||||
Assert.IsTrue(stackSize >= 2);
|
||||
PushStack(newPushedDatas, EvalDataType.Int64);
|
||||
break;
|
||||
}
|
||||
case Code.Ldelem_I:
|
||||
{
|
||||
Assert.IsTrue(stackSize >= 2);
|
||||
PushStack(newPushedDatas, EvalDataType.I);
|
||||
break;
|
||||
}
|
||||
case Code.Ldelem_R4:
|
||||
{
|
||||
Assert.IsTrue(stackSize >= 2);
|
||||
PushStack(newPushedDatas, EvalDataType.Float);
|
||||
break;
|
||||
}
|
||||
case Code.Ldelem_R8:
|
||||
{
|
||||
Assert.IsTrue(stackSize >= 2);
|
||||
PushStack(newPushedDatas, EvalDataType.Double);
|
||||
break;
|
||||
}
|
||||
case Code.Ldelem_Ref:
|
||||
{
|
||||
Assert.IsTrue(stackSize >= 2);
|
||||
PushStackObject(newPushedDatas);
|
||||
break;
|
||||
}
|
||||
case Code.Ldelem:
|
||||
{
|
||||
Assert.IsTrue(stackSize >= 2);
|
||||
PushStack(newPushedDatas, (ITypeDefOrRef)inst.Operand);
|
||||
break;
|
||||
}
|
||||
case Code.Stelem_I1:
|
||||
case Code.Stelem_I2:
|
||||
case Code.Stelem_I4:
|
||||
case Code.Stelem_I8:
|
||||
case Code.Stelem_I:
|
||||
case Code.Stelem_R4:
|
||||
case Code.Stelem_R8:
|
||||
case Code.Stelem_Ref:
|
||||
case Code.Stelem:
|
||||
{
|
||||
Assert.IsTrue(stackSize >= 3);
|
||||
break;
|
||||
}
|
||||
case Code.Mkrefany:
|
||||
{
|
||||
PushStack(newPushedDatas, new EvalDataTypeWithSig(EvalDataType.ValueType, _method.Module.CorLibTypes.TypedReference));
|
||||
break;
|
||||
}
|
||||
case Code.Refanytype:
|
||||
{
|
||||
PushStack(newPushedDatas, EvalDataType.Token);
|
||||
break;
|
||||
}
|
||||
case Code.Refanyval:
|
||||
{
|
||||
Assert.IsTrue(stackSize > 0);
|
||||
PushStack(newPushedDatas, EvalDataType.I);
|
||||
break;
|
||||
}
|
||||
case Code.Ldtoken:
|
||||
{
|
||||
PushStack(newPushedDatas, EvalDataType.Token);
|
||||
break;
|
||||
}
|
||||
case Code.Endfinally:
|
||||
case Code.Leave:
|
||||
case Code.Leave_S:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case Code.Endfilter:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case Code.Arglist:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case Code.Ldftn:
|
||||
case Code.Ldvirtftn:
|
||||
{
|
||||
PushStack(newPushedDatas, EvalDataType.Unknown);
|
||||
break;
|
||||
}
|
||||
case Code.Localloc:
|
||||
{
|
||||
PushStack(newPushedDatas, EvalDataType.I);
|
||||
break;
|
||||
}
|
||||
case Code.Unaligned:
|
||||
case Code.Volatile:
|
||||
case Code.Tailcall:
|
||||
case Code.No:
|
||||
case Code.Readonly:
|
||||
case Code.Constrained:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case Code.Cpblk:
|
||||
case Code.Initblk:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case Code.Sizeof:
|
||||
{
|
||||
PushStack(newPushedDatas, EvalDataType.Int32);
|
||||
break;
|
||||
}
|
||||
default: throw new Exception($"not supported opcode: {inst} in method: {_method.FullName}.");
|
||||
}
|
||||
|
||||
inst.CalculateStackUsage(methodHasReturnValue, out var pushed, out var pops);
|
||||
if (pushed != newPushedDatas.Count)
|
||||
{
|
||||
throw new Exception($"Instruction {inst} in method {_method.FullName} pushed {newPushedDatas.Count} items, but expected {pushed} items.");
|
||||
}
|
||||
if (pops == -1)
|
||||
{
|
||||
stackDatas.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (stackSize < pops)
|
||||
{
|
||||
throw new Exception($"Instruction {inst} in method {_method.FullName} pops {pops} items, but only {stackSize} items are available on the stack.");
|
||||
}
|
||||
stackDatas.RemoveRange(stackDatas.Count - pops, pops);
|
||||
stackDatas.AddRange(newPushedDatas);
|
||||
Assert.AreEqual(stackSize + pushed - pops, stackDatas.Count);
|
||||
}
|
||||
if (pushed > 0 && stackDatas.Count > 0)
|
||||
{
|
||||
_evalStackTopDataTypeAfterInstructions[inst] = stackDatas.Last().type;
|
||||
}
|
||||
}
|
||||
foreach (BasicBlock outBb in block.outBlocks)
|
||||
{
|
||||
EvalStackState outState = _blockEvalStackStates[outBb];
|
||||
if (outState.visited)
|
||||
{
|
||||
if (stackDatas.Count != outState.inputStackDatas.Count)
|
||||
{
|
||||
throw new Exception($"Block {block} in method {_method.FullName} has inconsistent stack data. Expected {outState.inputStackDatas.Count}, but got {stackDatas.Count}.");
|
||||
}
|
||||
}
|
||||
else if (outState.inputStackDatas.Count != stackDatas.Count)
|
||||
{
|
||||
if (outState.inputStackDatas.Count > 0)
|
||||
{
|
||||
throw new Exception($"Block {outBb} in method {_method.FullName} has inconsistent stack data. Expected {outState.inputStackDatas.Count}, but got {stackDatas.Count}.");
|
||||
}
|
||||
outState.inputStackDatas.AddRange(stackDatas);
|
||||
blockWalkStack.Push(outBb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f25425a3077f6db41873dee4223d0abc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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; }
|
||||
|
||||
public 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,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0bfcb2b5a87851f469d201fc8978c109
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 955da34fbde179641a94108ec53405ce
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fec4187cc1b96d5439ff908bcecd988f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,25 +0,0 @@
|
||||
namespace Obfuz.EncryptionVM
|
||||
{
|
||||
public class EncryptionInstructionWithOpCode
|
||||
{
|
||||
public readonly ushort code;
|
||||
|
||||
public readonly IEncryptionInstruction function;
|
||||
|
||||
public EncryptionInstructionWithOpCode(ushort code, IEncryptionInstruction function)
|
||||
{
|
||||
this.code = code;
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
public int Encrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
return function.Encrypt(value, secretKey, salt);
|
||||
}
|
||||
|
||||
public int Decrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
return function.Decrypt(value, secretKey, salt);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ca9bd232ed2583f4bb5f330886a329e6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,24 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.EncryptionVM
|
||||
{
|
||||
public interface IEncryptionInstruction
|
||||
{
|
||||
int Encrypt(int value, int[] secretKey, int salt);
|
||||
|
||||
int Decrypt(int value, int[] secretKey, int salt);
|
||||
|
||||
void GenerateEncryptCode(List<string> lines, string indent);
|
||||
|
||||
void GenerateDecryptCode(List<string> lines, string indent);
|
||||
}
|
||||
|
||||
public abstract class EncryptionInstructionBase : IEncryptionInstruction
|
||||
{
|
||||
public abstract int Encrypt(int value, int[] secretKey, int salt);
|
||||
public abstract int Decrypt(int value, int[] secretKey, int salt);
|
||||
|
||||
public abstract void GenerateEncryptCode(List<string> lines, string indent);
|
||||
public abstract void GenerateDecryptCode(List<string> lines, string indent);
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7b9d087de770a5488a9069ddf697c2f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 981355cf75a9d234883b2a15c446f478
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,35 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.EncryptionVM.Instructions
|
||||
{
|
||||
public class AddInstruction : EncryptionInstructionBase
|
||||
{
|
||||
private readonly int _addValue;
|
||||
private readonly int _opKeyIndex;
|
||||
|
||||
public AddInstruction(int addValue, int opKeyIndex)
|
||||
{
|
||||
_addValue = addValue;
|
||||
_opKeyIndex = opKeyIndex;
|
||||
}
|
||||
public override int Encrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
return ((value + secretKey[_opKeyIndex]) ^ salt) + _addValue;
|
||||
}
|
||||
|
||||
public override int Decrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
return ((value - _addValue) ^ salt) - secretKey[_opKeyIndex];
|
||||
}
|
||||
|
||||
public override void GenerateEncryptCode(List<string> lines, string indent)
|
||||
{
|
||||
lines.Add(indent + $"value = ((value + _secretKey[{_opKeyIndex}]) ^ salt) + {_addValue};");
|
||||
}
|
||||
|
||||
public override void GenerateDecryptCode(List<string> lines, string indent)
|
||||
{
|
||||
lines.Add(indent + $"value = ((value - {_addValue}) ^ salt) - _secretKey[{_opKeyIndex}];");
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6bdbdc5fd983f044a87e7b8ab8647aeb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,62 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.EncryptionVM.Instructions
|
||||
{
|
||||
public class AddRotateXorInstruction : EncryptionInstructionBase
|
||||
{
|
||||
// x = x + p1 + secretKey[index1];
|
||||
// x = Rotate(x, p2)
|
||||
// x = x ^ p3 ^ salt;
|
||||
|
||||
private readonly int _addValue;
|
||||
private readonly int _index1;
|
||||
private readonly int _rotateBitNum;
|
||||
private readonly int _xorValue;
|
||||
|
||||
public AddRotateXorInstruction(int addValue, int index1, int rotateBitNum, int xorValue)
|
||||
{
|
||||
_addValue = addValue;
|
||||
_index1 = index1;
|
||||
_rotateBitNum = rotateBitNum;
|
||||
_xorValue = xorValue;
|
||||
}
|
||||
|
||||
public override int Encrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
value += _addValue + secretKey[_index1];
|
||||
uint part1 = (uint)value << _rotateBitNum;
|
||||
uint part2 = (uint)value >> (32 - _rotateBitNum);
|
||||
value = (int)(part1 | part2);
|
||||
value ^= _xorValue ^ salt;
|
||||
return value;
|
||||
}
|
||||
|
||||
public override int Decrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
value ^= _xorValue ^ salt;
|
||||
uint value2 = (uint)value >> _rotateBitNum;
|
||||
uint part1 = (uint)value << (32 - _rotateBitNum);
|
||||
value = (int)(value2 | part1);
|
||||
value -= _addValue + secretKey[_index1];
|
||||
return value;
|
||||
}
|
||||
|
||||
public override void GenerateEncryptCode(List<string> lines, string indent)
|
||||
{
|
||||
lines.Add(indent + $"value += {_addValue} + _secretKey[{_index1}];");
|
||||
lines.Add(indent + $"uint part1 = (uint)value << {_rotateBitNum};");
|
||||
lines.Add(indent + $"uint part2 = (uint)value >> (32 - {_rotateBitNum});");
|
||||
lines.Add(indent + $"value = (int)(part1 | part2);");
|
||||
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
|
||||
}
|
||||
|
||||
public override void GenerateDecryptCode(List<string> lines, string indent)
|
||||
{
|
||||
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
|
||||
lines.Add(indent + $"uint value2 = (uint)value >> {_rotateBitNum};");
|
||||
lines.Add(indent + $"uint part1 = (uint)value << (32 - {_rotateBitNum});");
|
||||
lines.Add(indent + $"value = (int)(value2 | part1);");
|
||||
lines.Add(indent + $"value -= {_addValue} + _secretKey[{_index1}];");
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cda67c0dd0cadd24ea02c2988e34281a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,62 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.EncryptionVM.Instructions
|
||||
{
|
||||
public class AddXorRotateInstruction : EncryptionInstructionBase
|
||||
{
|
||||
// x = x + p1 + secretKey[index1];
|
||||
// x = x ^ p3 ^ salt;
|
||||
// x = Rotate(x, p2)
|
||||
|
||||
private readonly int _addValue;
|
||||
private readonly int _index1;
|
||||
private readonly int _rotateBitNum;
|
||||
private readonly int _xorValue;
|
||||
|
||||
public AddXorRotateInstruction(int addValue, int index1, int xorValue, int rotateBitNum)
|
||||
{
|
||||
_addValue = addValue;
|
||||
_index1 = index1;
|
||||
_rotateBitNum = rotateBitNum;
|
||||
_xorValue = xorValue;
|
||||
}
|
||||
|
||||
public override int Encrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
value += _addValue + secretKey[_index1];
|
||||
value ^= _xorValue ^ salt;
|
||||
uint part1 = (uint)value << _rotateBitNum;
|
||||
uint part2 = (uint)value >> (32 - _rotateBitNum);
|
||||
value = (int)(part1 | part2);
|
||||
return value;
|
||||
}
|
||||
|
||||
public override int Decrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
uint value2 = (uint)value >> _rotateBitNum;
|
||||
uint part1 = (uint)value << (32 - _rotateBitNum);
|
||||
value = (int)(value2 | part1);
|
||||
value ^= _xorValue ^ salt;
|
||||
value -= _addValue + secretKey[_index1];
|
||||
return value;
|
||||
}
|
||||
|
||||
public override void GenerateEncryptCode(List<string> lines, string indent)
|
||||
{
|
||||
lines.Add(indent + $"value += {_addValue} + _secretKey[{_index1}];");
|
||||
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
|
||||
lines.Add(indent + $"uint part1 = (uint)value << {_rotateBitNum};");
|
||||
lines.Add(indent + $"uint part2 = (uint)value >> (32 - {_rotateBitNum});");
|
||||
lines.Add(indent + $"value = (int)(part1 | part2);");
|
||||
}
|
||||
|
||||
public override void GenerateDecryptCode(List<string> lines, string indent)
|
||||
{
|
||||
lines.Add(indent + $"uint part1 = (uint)value >> {_rotateBitNum};");
|
||||
lines.Add(indent + $"uint part2 = (uint)value << (32 - {_rotateBitNum});");
|
||||
lines.Add(indent + $"value = (int)(part1 | part2);");
|
||||
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
|
||||
lines.Add(indent + $"value -= {_addValue} + _secretKey[{_index1}];");
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d806305e627be06469fb2d2c2cf98816
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,46 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.EncryptionVM.Instructions
|
||||
{
|
||||
public class BitRotateInstruction : EncryptionInstructionBase
|
||||
{
|
||||
private readonly int _rotateBitNum;
|
||||
private readonly int _opKeyIndex;
|
||||
|
||||
public BitRotateInstruction(int rotateBitNum, int opKeyIndex)
|
||||
{
|
||||
_rotateBitNum = rotateBitNum;
|
||||
_opKeyIndex = opKeyIndex;
|
||||
}
|
||||
|
||||
public override int Encrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
uint part1 = (uint)value << _rotateBitNum;
|
||||
uint part2 = (uint)value >> (32 - _rotateBitNum);
|
||||
return ((int)(part1 | part2) ^ secretKey[_opKeyIndex]) + salt;
|
||||
}
|
||||
|
||||
public override int Decrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
uint value2 = (uint)((value - salt) ^ secretKey[_opKeyIndex]);
|
||||
uint part1 = value2 >> _rotateBitNum;
|
||||
uint part2 = value2 << (32 - _rotateBitNum);
|
||||
return (int)(part1 | part2);
|
||||
}
|
||||
|
||||
public override void GenerateEncryptCode(List<string> lines, string indent)
|
||||
{
|
||||
lines.Add(indent + $"uint part1 = (uint)value << {_rotateBitNum};");
|
||||
lines.Add(indent + $"uint part2 = (uint)value >> (32 - {_rotateBitNum});");
|
||||
lines.Add(indent + $"value = ((int)(part1 | part2) ^ _secretKey[{_opKeyIndex}]) + salt;");
|
||||
}
|
||||
|
||||
public override void GenerateDecryptCode(List<string> lines, string indent)
|
||||
{
|
||||
lines.Add(indent + $"uint value2 = (uint)((value - salt) ^ _secretKey[{_opKeyIndex}]);");
|
||||
lines.Add(indent + $"uint part1 = value2 >> {_rotateBitNum};");
|
||||
lines.Add(indent + $"uint part2 = value2 << (32 - {_rotateBitNum});");
|
||||
lines.Add(indent + $"value = (int)(part1 | part2);");
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bccff31b9f07fcf4f821cee671f82caf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,44 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.EncryptionVM.Instructions
|
||||
{
|
||||
|
||||
public class EncryptFunction : EncryptionInstructionBase
|
||||
{
|
||||
private readonly IEncryptionInstruction[] _instructions;
|
||||
|
||||
public EncryptFunction(IEncryptionInstruction[] instructions)
|
||||
{
|
||||
_instructions = instructions;
|
||||
}
|
||||
|
||||
public override int Encrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
foreach (var instruction in _instructions)
|
||||
{
|
||||
value = instruction.Encrypt(value, secretKey, salt);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public override int Decrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
for (int i = _instructions.Length - 1; i >= 0; i--)
|
||||
{
|
||||
value = _instructions[i].Decrypt(value, secretKey, salt);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public override void GenerateEncryptCode(List<string> lines, string indent)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GenerateDecryptCode(List<string> lines, string indent)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: feafdb30f7b6d5143a89c7659bc16171
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,46 +0,0 @@
|
||||
using Obfuz.Utils;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.EncryptionVM.Instructions
|
||||
{
|
||||
public class MultipleInstruction : EncryptionInstructionBase
|
||||
{
|
||||
private readonly int _multiValue;
|
||||
private readonly int _revertMultiValue;
|
||||
private readonly int _opKeyIndex;
|
||||
|
||||
public MultipleInstruction(int addValue, int opKeyIndex)
|
||||
{
|
||||
_multiValue = addValue;
|
||||
_opKeyIndex = opKeyIndex;
|
||||
_revertMultiValue = MathUtil.ModInverse32(addValue);
|
||||
Verify();
|
||||
}
|
||||
|
||||
private void Verify()
|
||||
{
|
||||
int a = 1122334;
|
||||
UnityEngine.Assertions.Assert.AreEqual(a, a * _multiValue * _revertMultiValue);
|
||||
}
|
||||
|
||||
public override int Encrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
return value * _multiValue + secretKey[_opKeyIndex] + salt;
|
||||
}
|
||||
|
||||
public override int Decrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
return (value - secretKey[_opKeyIndex] - salt) * _revertMultiValue;
|
||||
}
|
||||
|
||||
public override void GenerateEncryptCode(List<string> lines, string indent)
|
||||
{
|
||||
lines.Add(indent + $"value = value * {_multiValue} + _secretKey[{_opKeyIndex}] + salt;");
|
||||
}
|
||||
|
||||
public override void GenerateDecryptCode(List<string> lines, string indent)
|
||||
{
|
||||
lines.Add(indent + $"value = (value - _secretKey[{_opKeyIndex}] - salt) * {_revertMultiValue};");
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd5fdfad694e0ae469bf6ca04c913220
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,65 +0,0 @@
|
||||
using Obfuz.Utils;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.EncryptionVM.Instructions
|
||||
{
|
||||
public class MultipleRotateXorInstruction : EncryptionInstructionBase
|
||||
{
|
||||
// x = x * p1 + secretKey[index1];
|
||||
// x = Rotate(x, p2)
|
||||
// x = x ^ p3 ^ salt;
|
||||
|
||||
private readonly int _multipleValue;
|
||||
private readonly int _revertMultipleValue;
|
||||
private readonly int _index1;
|
||||
private readonly int _rotateBitNum;
|
||||
private readonly int _xorValue;
|
||||
|
||||
public MultipleRotateXorInstruction(int multipleValue, int index1, int rotateBitNum, int xorValue)
|
||||
{
|
||||
_multipleValue = multipleValue;
|
||||
_revertMultipleValue = MathUtil.ModInverse32(multipleValue);
|
||||
_index1 = index1;
|
||||
_rotateBitNum = rotateBitNum;
|
||||
_xorValue = xorValue;
|
||||
}
|
||||
|
||||
public override int Encrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
value = value * _multipleValue + secretKey[_index1];
|
||||
uint part1 = (uint)value << _rotateBitNum;
|
||||
uint part2 = (uint)value >> (32 - _rotateBitNum);
|
||||
value = (int)(part1 | part2);
|
||||
value ^= _xorValue ^ salt;
|
||||
return value;
|
||||
}
|
||||
|
||||
public override int Decrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
value ^= _xorValue ^ salt;
|
||||
uint value2 = (uint)value >> _rotateBitNum;
|
||||
uint part1 = (uint)value << (32 - _rotateBitNum);
|
||||
value = (int)(value2 | part1);
|
||||
value = (value - secretKey[_index1]) * _revertMultipleValue;
|
||||
return value;
|
||||
}
|
||||
|
||||
public override void GenerateEncryptCode(List<string> lines, string indent)
|
||||
{
|
||||
lines.Add(indent + $"value = value * {_multipleValue} + _secretKey[{_index1}];");
|
||||
lines.Add(indent + $"uint part1 = (uint)value << {_rotateBitNum};");
|
||||
lines.Add(indent + $"uint part2 = (uint)value >> (32 - {_rotateBitNum});");
|
||||
lines.Add(indent + $"value = (int)(part1 | part2);");
|
||||
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
|
||||
}
|
||||
|
||||
public override void GenerateDecryptCode(List<string> lines, string indent)
|
||||
{
|
||||
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
|
||||
lines.Add(indent + $"uint value2 = (uint)value >> {_rotateBitNum};");
|
||||
lines.Add(indent + $"uint part1 = (uint)value << (32 - {_rotateBitNum});");
|
||||
lines.Add(indent + $"value = (int)(value2 | part1);");
|
||||
lines.Add(indent + $"value = (value - _secretKey[{_index1}]) * {_revertMultipleValue};");
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3c8b55b35ff1554489fa657a714f485
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,65 +0,0 @@
|
||||
using Obfuz.Utils;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.EncryptionVM.Instructions
|
||||
{
|
||||
public class MultipleXorRotateInstruction : EncryptionInstructionBase
|
||||
{
|
||||
// x = x * p1 + secretKey[index1];
|
||||
// x = x ^ p3 ^ salt;
|
||||
// x = Rotate(x, p2)
|
||||
|
||||
private readonly int _multipleValue;
|
||||
private readonly int _revertMultipleValue;
|
||||
private readonly int _index1;
|
||||
private readonly int _rotateBitNum;
|
||||
private readonly int _xorValue;
|
||||
|
||||
public MultipleXorRotateInstruction(int multipleValue, int index1, int xorValue, int rotateBitNum)
|
||||
{
|
||||
_multipleValue = multipleValue;
|
||||
_revertMultipleValue = MathUtil.ModInverse32(multipleValue);
|
||||
_index1 = index1;
|
||||
_rotateBitNum = rotateBitNum;
|
||||
_xorValue = xorValue;
|
||||
}
|
||||
|
||||
public override int Encrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
value = value * _multipleValue + secretKey[_index1];
|
||||
value ^= _xorValue ^ salt;
|
||||
uint part1 = (uint)value << _rotateBitNum;
|
||||
uint part2 = (uint)value >> (32 - _rotateBitNum);
|
||||
value = (int)(part1 | part2);
|
||||
return value;
|
||||
}
|
||||
|
||||
public override int Decrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
uint value2 = (uint)value >> _rotateBitNum;
|
||||
uint part1 = (uint)value << (32 - _rotateBitNum);
|
||||
value = (int)(value2 | part1);
|
||||
value ^= _xorValue ^ salt;
|
||||
value = (value - secretKey[_index1]) * _revertMultipleValue;
|
||||
return value;
|
||||
}
|
||||
|
||||
public override void GenerateEncryptCode(List<string> lines, string indent)
|
||||
{
|
||||
lines.Add(indent + $"value = value * {_multipleValue} + _secretKey[{_index1}];");
|
||||
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
|
||||
lines.Add(indent + $"uint part1 = (uint)value << {_rotateBitNum};");
|
||||
lines.Add(indent + $"uint part2 = (uint)value >> (32 - {_rotateBitNum});");
|
||||
lines.Add(indent + $"value = (int)(part1 | part2);");
|
||||
}
|
||||
|
||||
public override void GenerateDecryptCode(List<string> lines, string indent)
|
||||
{
|
||||
lines.Add(indent + $"uint value2 = (uint)value >> {_rotateBitNum};");
|
||||
lines.Add(indent + $"uint part1 = (uint)value << (32 - {_rotateBitNum});");
|
||||
lines.Add(indent + $"value = (int)(value2 | part1);");
|
||||
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
|
||||
lines.Add(indent + $"value = (value - _secretKey[{_index1}]) * {_revertMultipleValue};");
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: adc3dcde66795744fa4bdc753a2c599f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,62 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.EncryptionVM.Instructions
|
||||
{
|
||||
public class XorAddRotateInstruction : EncryptionInstructionBase
|
||||
{
|
||||
// x = x ^ p3 ^ salt;
|
||||
// x = x + p1 + secretKey[index1];
|
||||
// x = Rotate(x, p2)
|
||||
|
||||
private readonly int _addValue;
|
||||
private readonly int _index1;
|
||||
private readonly int _rotateBitNum;
|
||||
private readonly int _xorValue;
|
||||
|
||||
public XorAddRotateInstruction(int xorValue, int addValue, int index1, int rotateBitNum)
|
||||
{
|
||||
_addValue = addValue;
|
||||
_index1 = index1;
|
||||
_rotateBitNum = rotateBitNum;
|
||||
_xorValue = xorValue;
|
||||
}
|
||||
|
||||
public override int Encrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
value ^= _xorValue ^ salt;
|
||||
value += _addValue + secretKey[_index1];
|
||||
uint part1 = (uint)value << _rotateBitNum;
|
||||
uint part2 = (uint)value >> (32 - _rotateBitNum);
|
||||
value = (int)(part1 | part2);
|
||||
return value;
|
||||
}
|
||||
|
||||
public override int Decrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
uint value2 = (uint)value >> _rotateBitNum;
|
||||
uint part1 = (uint)value << (32 - _rotateBitNum);
|
||||
value = (int)(value2 | part1);
|
||||
value -= _addValue + secretKey[_index1];
|
||||
value ^= _xorValue ^ salt;
|
||||
return value;
|
||||
}
|
||||
|
||||
public override void GenerateEncryptCode(List<string> lines, string indent)
|
||||
{
|
||||
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
|
||||
lines.Add(indent + $"value += {_addValue} + _secretKey[{_index1}];");
|
||||
lines.Add(indent + $"uint part1 = (uint)value << {_rotateBitNum};");
|
||||
lines.Add(indent + $"uint part2 = (uint)value >> (32 - {_rotateBitNum});");
|
||||
lines.Add(indent + $"value = (int)(part1 | part2);");
|
||||
}
|
||||
|
||||
public override void GenerateDecryptCode(List<string> lines, string indent)
|
||||
{
|
||||
lines.Add(indent + $"uint value2 = (uint)value >> {_rotateBitNum};");
|
||||
lines.Add(indent + $"uint part1 = (uint)value << (32 - {_rotateBitNum});");
|
||||
lines.Add(indent + $"value = (int)(value2 | part1);");
|
||||
lines.Add(indent + $"value -= {_addValue} + _secretKey[{_index1}];");
|
||||
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ad8f4dd724d7ff845b0dd65861054d37
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,36 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.EncryptionVM.Instructions
|
||||
{
|
||||
public class XorInstruction : EncryptionInstructionBase
|
||||
{
|
||||
private readonly int _xorValue;
|
||||
private readonly int _opKeyIndex;
|
||||
|
||||
public XorInstruction(int xorValue, int opKeyIndex)
|
||||
{
|
||||
_xorValue = xorValue;
|
||||
_opKeyIndex = opKeyIndex;
|
||||
}
|
||||
|
||||
public override int Encrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
return ((value ^ secretKey[_opKeyIndex]) + salt) ^ _xorValue;
|
||||
}
|
||||
|
||||
public override int Decrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
return ((value ^ _xorValue) - salt) ^ secretKey[_opKeyIndex];
|
||||
}
|
||||
|
||||
public override void GenerateEncryptCode(List<string> lines, string indent)
|
||||
{
|
||||
lines.Add(indent + $"value = ((value ^ _secretKey[{_opKeyIndex}]) + salt) ^ {_xorValue};");
|
||||
}
|
||||
|
||||
public override void GenerateDecryptCode(List<string> lines, string indent)
|
||||
{
|
||||
lines.Add(indent + $"value = ((value ^ {_xorValue}) - salt) ^ _secretKey[{_opKeyIndex}];");
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f16dd868e4473b45bfa9daaf7fabaf8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,65 +0,0 @@
|
||||
using Obfuz.Utils;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.EncryptionVM.Instructions
|
||||
{
|
||||
public class XorMultipleRotateInstruction : EncryptionInstructionBase
|
||||
{
|
||||
// x = x ^ p3 ^ salt;
|
||||
// x = x * p1 + secretKey[index1];
|
||||
// x = Rotate(x, p2)
|
||||
|
||||
private readonly int _multipleValue;
|
||||
private readonly int _revertMultipleValue;
|
||||
private readonly int _index1;
|
||||
private readonly int _rotateBitNum;
|
||||
private readonly int _xorValue;
|
||||
|
||||
public XorMultipleRotateInstruction(int xorValue, int multipleValue, int index1, int rotateBitNum)
|
||||
{
|
||||
_multipleValue = multipleValue;
|
||||
_revertMultipleValue = MathUtil.ModInverse32(multipleValue);
|
||||
_index1 = index1;
|
||||
_rotateBitNum = rotateBitNum;
|
||||
_xorValue = xorValue;
|
||||
}
|
||||
|
||||
public override int Encrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
value ^= _xorValue ^ salt;
|
||||
value = value * _multipleValue + secretKey[_index1];
|
||||
uint part1 = (uint)value << _rotateBitNum;
|
||||
uint part2 = (uint)value >> (32 - _rotateBitNum);
|
||||
value = (int)(part1 | part2);
|
||||
return value;
|
||||
}
|
||||
|
||||
public override int Decrypt(int value, int[] secretKey, int salt)
|
||||
{
|
||||
uint value2 = (uint)value >> _rotateBitNum;
|
||||
uint part1 = (uint)value << (32 - _rotateBitNum);
|
||||
value = (int)(value2 | part1);
|
||||
value = (value - secretKey[_index1]) * _revertMultipleValue;
|
||||
value ^= _xorValue ^ salt;
|
||||
return value;
|
||||
}
|
||||
|
||||
public override void GenerateEncryptCode(List<string> lines, string indent)
|
||||
{
|
||||
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
|
||||
lines.Add(indent + $"value = value * {_multipleValue} + _secretKey[{_index1}];");
|
||||
lines.Add(indent + $"uint part1 = (uint)value << {_rotateBitNum};");
|
||||
lines.Add(indent + $"uint part2 = (uint)value >> (32 - {_rotateBitNum});");
|
||||
lines.Add(indent + $"value = (int)(part1 | part2);");
|
||||
}
|
||||
|
||||
public override void GenerateDecryptCode(List<string> lines, string indent)
|
||||
{
|
||||
lines.Add(indent + $"uint value2 = (uint)value >> {_rotateBitNum};");
|
||||
lines.Add(indent + $"uint part1 = (uint)value << (32 - {_rotateBitNum});");
|
||||
lines.Add(indent + $"value = (int)(value2 | part1);");
|
||||
lines.Add(indent + $"value = (value - _secretKey[{_index1}]) * {_revertMultipleValue};");
|
||||
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3eb7e6d475cfc14459d3850c5964ba52
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,17 +0,0 @@
|
||||
namespace Obfuz.EncryptionVM
|
||||
{
|
||||
public class VirtualMachine
|
||||
{
|
||||
public const int SecretKeyLength = 1024;
|
||||
|
||||
public readonly int version;
|
||||
public readonly string codeGenerationSecretKey;
|
||||
public readonly EncryptionInstructionWithOpCode[] opCodes;
|
||||
|
||||
public VirtualMachine(int version, string codeGenerationSecretKey, EncryptionInstructionWithOpCode[] opCodes)
|
||||
{
|
||||
this.codeGenerationSecretKey = codeGenerationSecretKey;
|
||||
this.opCodes = opCodes;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c6970e037654dcb49912783a40f3e1ba
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,203 +0,0 @@
|
||||
using Obfuz.Utils;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obfuz.EncryptionVM
|
||||
{
|
||||
public class VirtualMachineCodeGenerator
|
||||
{
|
||||
private readonly int _opCodeCount;
|
||||
private readonly int _opCodeBits;
|
||||
private readonly VirtualMachine _vm;
|
||||
|
||||
public VirtualMachineCodeGenerator(string vmCodeGenerateSecretKey, int opCodeCount)
|
||||
{
|
||||
_opCodeCount = opCodeCount;
|
||||
_opCodeBits = EncryptionUtil.GetBitCount(opCodeCount - 1);
|
||||
_vm = new VirtualMachineCreator(vmCodeGenerateSecretKey).CreateVirtualMachine(opCodeCount);
|
||||
}
|
||||
|
||||
public VirtualMachineCodeGenerator(VirtualMachine vm)
|
||||
{
|
||||
_opCodeCount = vm.opCodes.Length;
|
||||
_opCodeBits = EncryptionUtil.GetBitCount(_opCodeCount - 1);
|
||||
_vm = vm;
|
||||
}
|
||||
|
||||
|
||||
public bool ValidateMatch(string outputFile)
|
||||
{
|
||||
if (!File.Exists(outputFile))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
string oldCode = NormalizeText(File.ReadAllText(outputFile, Encoding.UTF8));
|
||||
string newCode = NormalizeText(GenerateCode());
|
||||
return oldCode == newCode;
|
||||
}
|
||||
|
||||
private static string NormalizeText(string input)
|
||||
{
|
||||
return Regex.Replace(input, @"\s+", string.Empty);
|
||||
}
|
||||
|
||||
public void Generate(string outputFile)
|
||||
{
|
||||
FileUtil.CreateParentDir(outputFile);
|
||||
|
||||
string code = GenerateCode();
|
||||
|
||||
File.WriteAllText(outputFile, code, Encoding.UTF8);
|
||||
Debug.Log($"Generate EncryptionVM code to {outputFile}");
|
||||
}
|
||||
|
||||
private string GenerateCode()
|
||||
{
|
||||
var lines = new List<string>();
|
||||
AppendHeader(lines);
|
||||
AppendEncryptCodes(lines);
|
||||
AppendDecryptCodes(lines);
|
||||
AppendTailer(lines);
|
||||
return string.Join("\n", lines);
|
||||
}
|
||||
|
||||
private void AppendEncryptCodes(List<string> lines)
|
||||
{
|
||||
lines.Add(@"
|
||||
private int ExecuteEncrypt(int value, int opCode, int salt)
|
||||
{
|
||||
switch (opCode)
|
||||
{");
|
||||
foreach (var opCode in _vm.opCodes)
|
||||
{
|
||||
lines.Add($@" case {opCode.code}:
|
||||
{{
|
||||
// {opCode.function.GetType().Name}");
|
||||
AppendEncryptCode(lines, opCode.function);
|
||||
lines.Add(@" return value;
|
||||
}");
|
||||
}
|
||||
|
||||
lines.Add(@"
|
||||
default:
|
||||
throw new System.Exception($""Invalid opCode:{opCode}"");
|
||||
}
|
||||
}");
|
||||
}
|
||||
|
||||
private void AppendDecryptCodes(List<string> lines)
|
||||
{
|
||||
lines.Add(@"
|
||||
private int ExecuteDecrypt(int value, int opCode, int salt)
|
||||
{
|
||||
switch (opCode)
|
||||
{");
|
||||
foreach (var opCode in _vm.opCodes)
|
||||
{
|
||||
lines.Add($@" case {opCode.code}:
|
||||
{{
|
||||
// {opCode.function.GetType().Name}");
|
||||
AppendDecryptCode(lines, opCode.function);
|
||||
lines.Add(@" return value;
|
||||
}");
|
||||
}
|
||||
|
||||
lines.Add(@"
|
||||
default:
|
||||
throw new System.Exception($""Invalid opCode:{opCode}"");
|
||||
}
|
||||
}");
|
||||
}
|
||||
|
||||
private void AppendHeader(List<string> lines)
|
||||
{
|
||||
|
||||
lines.Add($"/// This file is auto-generated by Obfuz. Do not modify it.");
|
||||
lines.Add($"///");
|
||||
//lines.Add($"/// Created Time: {DateTime.Now}");
|
||||
|
||||
lines.Add($"/// Version: {_vm.version}");
|
||||
lines.Add($"/// SecretKey: {_vm.codeGenerationSecretKey}");
|
||||
lines.Add($"/// OpCodeCount: {_vm.opCodes.Length}");
|
||||
|
||||
lines.Add(@"
|
||||
namespace Obfuz.EncryptionVM
|
||||
{
|
||||
public class GeneratedEncryptionVirtualMachine : Obfuz.EncryptorBase
|
||||
{");
|
||||
lines.Add($@"
|
||||
private const int kOpCodeBits = {_opCodeBits};
|
||||
|
||||
private const int kOpCodeCount = {_opCodeCount};
|
||||
|
||||
private const int kOpCodeMask = {_opCodeCount - 1};
|
||||
");
|
||||
lines.Add(@"
|
||||
|
||||
private readonly int[] _secretKey;
|
||||
|
||||
public GeneratedEncryptionVirtualMachine(byte[] secretKey)
|
||||
{
|
||||
this._secretKey = ConvertToIntKey(secretKey);
|
||||
}
|
||||
|
||||
public override int OpCodeCount => kOpCodeCount;
|
||||
|
||||
public override int Encrypt(int value, int opts, int salt)
|
||||
{
|
||||
uint uopts = (uint)opts;
|
||||
uint revertOps = 0;
|
||||
while (uopts != 0)
|
||||
{
|
||||
uint opCode = uopts & kOpCodeMask;
|
||||
revertOps <<= kOpCodeBits;
|
||||
revertOps |= opCode;
|
||||
uopts >>= kOpCodeBits;
|
||||
}
|
||||
|
||||
while (revertOps != 0)
|
||||
{
|
||||
uint opCode = revertOps & kOpCodeMask;
|
||||
value = ExecuteEncrypt(value, (int)opCode, salt);
|
||||
revertOps >>= kOpCodeBits;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public override int Decrypt(int value, int opts, int salt)
|
||||
{
|
||||
uint uopts = (uint)opts;
|
||||
while (uopts != 0)
|
||||
{
|
||||
uint opCode = uopts & kOpCodeMask;
|
||||
value = ExecuteDecrypt(value, (int)opCode, salt);
|
||||
uopts >>= kOpCodeBits;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
");
|
||||
}
|
||||
|
||||
private void AppendTailer(List<string> lines)
|
||||
{
|
||||
lines.Add(@"
|
||||
}
|
||||
}
|
||||
|
||||
");
|
||||
}
|
||||
|
||||
private void AppendEncryptCode(List<string> lines, IEncryptionInstruction instruction)
|
||||
{
|
||||
instruction.GenerateEncryptCode(lines, " ");
|
||||
}
|
||||
|
||||
private void AppendDecryptCode(List<string> lines, IEncryptionInstruction instruction)
|
||||
{
|
||||
instruction.GenerateDecryptCode(lines, " ");
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2246e9d3369eb3c45bc19ae0748d76ba
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,68 +0,0 @@
|
||||
using Obfuz.EncryptionVM.Instructions;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.EncryptionVM
|
||||
{
|
||||
public class VirtualMachineCreator
|
||||
{
|
||||
private readonly string _vmGenerationSecretKey;
|
||||
private readonly IRandom _random;
|
||||
|
||||
public const int CodeGenerationSecretKeyLength = 1024;
|
||||
|
||||
public const int VirtualMachineVersion = 1;
|
||||
|
||||
public VirtualMachineCreator(string vmGenerationSecretKey)
|
||||
{
|
||||
_vmGenerationSecretKey = vmGenerationSecretKey;
|
||||
byte[] byteGenerationSecretKey = KeyGenerator.GenerateKey(vmGenerationSecretKey, CodeGenerationSecretKeyLength);
|
||||
int[] intGenerationSecretKey = KeyGenerator.ConvertToIntKey(byteGenerationSecretKey);
|
||||
_random = new RandomWithKey(intGenerationSecretKey, 0);
|
||||
}
|
||||
|
||||
private readonly List<Func<IRandom, int, EncryptionInstructionBase>> _instructionCreators = new List<Func<IRandom, int, EncryptionInstructionBase>>
|
||||
{
|
||||
(r, len) => new AddInstruction(r.NextInt(), r.NextInt(len)),
|
||||
(r, len) => new XorInstruction(r.NextInt(), r.NextInt(len)),
|
||||
(r, len) => new BitRotateInstruction(r.NextInt(32), r.NextInt(len)),
|
||||
(r, len) => new MultipleInstruction(r.NextInt() | 0x1, r.NextInt(len)),
|
||||
(r, len) => new AddRotateXorInstruction(r.NextInt(), r.NextInt(len), r.NextInt(32), r.NextInt()),
|
||||
(r, len) => new AddXorRotateInstruction(r.NextInt(), r.NextInt(len), r.NextInt(), r.NextInt(32)),
|
||||
(r, len) => new XorAddRotateInstruction(r.NextInt(), r.NextInt(), r.NextInt(len), r.NextInt(32)),
|
||||
(r, len) => new MultipleRotateXorInstruction(r.NextInt() | 0x1, r.NextInt(len), r.NextInt(32), r.NextInt()),
|
||||
(r, len) => new MultipleXorRotateInstruction(r.NextInt() | 0x1, r.NextInt(len), r.NextInt(), r.NextInt(32)),
|
||||
(r, len) => new XorMultipleRotateInstruction(r.NextInt(), r.NextInt() | 0x1, r.NextInt(len), r.NextInt(32)),
|
||||
};
|
||||
|
||||
private IEncryptionInstruction CreateRandomInstruction(int intSecretKeyLength)
|
||||
{
|
||||
return _instructionCreators[_random.NextInt(_instructionCreators.Count)](_random, intSecretKeyLength);
|
||||
}
|
||||
|
||||
private EncryptionInstructionWithOpCode CreateEncryptOpCode(ushort code)
|
||||
{
|
||||
IEncryptionInstruction inst = CreateRandomInstruction(VirtualMachine.SecretKeyLength / sizeof(int));
|
||||
return new EncryptionInstructionWithOpCode(code, inst);
|
||||
}
|
||||
|
||||
public VirtualMachine CreateVirtualMachine(int opCodeCount)
|
||||
{
|
||||
if (opCodeCount < 64)
|
||||
{
|
||||
throw new System.Exception("OpCode count should be >= 64");
|
||||
}
|
||||
if ((opCodeCount & (opCodeCount - 1)) != 0)
|
||||
{
|
||||
throw new System.Exception("OpCode count should be power of 2");
|
||||
}
|
||||
var opCodes = new EncryptionInstructionWithOpCode[opCodeCount];
|
||||
for (int i = 0; i < opCodes.Length; i++)
|
||||
{
|
||||
opCodes[i] = CreateEncryptOpCode((ushort)i);
|
||||
}
|
||||
return new VirtualMachine(VirtualMachineVersion, _vmGenerationSecretKey, opCodes);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 77d95ff5cf0b3aa4e96a055e37c381ba
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,90 +0,0 @@
|
||||
using Obfuz.Utils;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace Obfuz.EncryptionVM
|
||||
{
|
||||
|
||||
public class VirtualMachineSimulator : EncryptorBase
|
||||
{
|
||||
private readonly EncryptionInstructionWithOpCode[] _opCodes;
|
||||
private readonly int[] _secretKey;
|
||||
|
||||
public override int OpCodeCount => _opCodes.Length;
|
||||
|
||||
public VirtualMachineSimulator(VirtualMachine vm, byte[] byteSecretKey)
|
||||
{
|
||||
_opCodes = vm.opCodes;
|
||||
_secretKey = KeyGenerator.ConvertToIntKey(byteSecretKey);
|
||||
|
||||
VerifyInstructions();
|
||||
}
|
||||
|
||||
private void VerifyInstructions()
|
||||
{
|
||||
int value = 0x11223344;
|
||||
for (int i = 0; i < _opCodes.Length; i++)
|
||||
{
|
||||
int encryptedValue = _opCodes[i].Encrypt(value, _secretKey, i);
|
||||
int decryptedValue = _opCodes[i].Decrypt(encryptedValue, _secretKey, i);
|
||||
//Debug.Log($"instruction type:{_opCodes[i].function.GetType()}");
|
||||
Assert.AreEqual(value, decryptedValue);
|
||||
}
|
||||
|
||||
int ops = 11223344;
|
||||
int salt = 789;
|
||||
Assert.AreEqual(1, Decrypt(Encrypt(1, ops, salt), ops, salt));
|
||||
Assert.AreEqual(1L, Decrypt(Encrypt(1L, ops, salt), ops, salt));
|
||||
Assert.AreEqual(1.0f, Decrypt(Encrypt(1.0f, ops, salt), ops, salt));
|
||||
Assert.AreEqual(1.0, Decrypt(Encrypt(1.0, ops, salt), ops, salt));
|
||||
|
||||
byte[] strBytes = Encrypt("abcdef", ops, salt);
|
||||
Assert.AreEqual("abcdef", DecryptString(strBytes, 0, strBytes.Length, ops, salt));
|
||||
var arr = new byte[100];
|
||||
for (int i = 0; i < arr.Length; i++)
|
||||
{
|
||||
arr[i] = (byte)i;
|
||||
}
|
||||
EncryptBlock(arr, ops, salt);
|
||||
DecryptBlock(arr, ops, salt);
|
||||
for (int i = 0; i < arr.Length; i++)
|
||||
{
|
||||
Assert.AreEqual(i, arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private List<uint> DecodeOps(uint ops)
|
||||
{
|
||||
var codes = new List<uint>();
|
||||
while (ops != 0)
|
||||
{
|
||||
uint code = ops % (uint)_opCodes.Length;
|
||||
codes.Add(code);
|
||||
ops /= (uint)_opCodes.Length;
|
||||
}
|
||||
return codes;
|
||||
}
|
||||
|
||||
public override int Encrypt(int value, int ops, int salt)
|
||||
{
|
||||
var codes = DecodeOps((uint)ops);
|
||||
for (int i = codes.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var opCode = _opCodes[codes[i]];
|
||||
value = opCode.Encrypt(value, _secretKey, salt);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public override int Decrypt(int value, int ops, int salt)
|
||||
{
|
||||
var codes = DecodeOps((uint)ops);
|
||||
foreach (var code in codes)
|
||||
{
|
||||
var opCode = _opCodes[code];
|
||||
value = opCode.Decrypt(value, _secretKey, salt);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f86f4d6faf49764a915d5c675091375
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f47f2abd9eb7ba8469ba5cb1bb085d33
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 327cb4a465ff23944a5fea30bf3beeeb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,92 +0,0 @@
|
||||
using Obfuz.Settings;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
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,22 +0,0 @@
|
||||
using Obfuz.Settings;
|
||||
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,15 +0,0 @@
|
||||
using Obfuz.ObfusPasses;
|
||||
|
||||
namespace Obfuz
|
||||
{
|
||||
public interface IObfuscationPass
|
||||
{
|
||||
ObfuscationPassType Type { get; }
|
||||
|
||||
void Start();
|
||||
|
||||
void Stop();
|
||||
|
||||
void Process();
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7003f9503025794b8aa775d9ade335c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 120b2dcffd582e84dbb92003240824d1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,53 +0,0 @@
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Emit;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.ObfusPasses
|
||||
{
|
||||
public abstract class BasicBlockObfuscationPassBase : ObfuscationMethodPassBase
|
||||
{
|
||||
protected virtual bool ComputeBlockInLoop => true;
|
||||
|
||||
protected abstract bool TryObfuscateInstruction(MethodDef callingMethod, Instruction inst, BasicBlock block, int instructionIndex,
|
||||
IList<Instruction> globalInstructions, List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions);
|
||||
|
||||
protected override void ObfuscateData(MethodDef method)
|
||||
{
|
||||
BasicBlockCollection bbc = new BasicBlockCollection(method, ComputeBlockInLoop);
|
||||
|
||||
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];
|
||||
BasicBlock block = bbc.GetBasicBlockByInstruction(inst);
|
||||
outputInstructions.Clear();
|
||||
if (TryObfuscateInstruction(method, inst, block, i, instructions, 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,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae83aaf003665614092aabceabff3cf8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf68e45551825c547b137f6e5189937e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
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 readonly SpecialWhiteListMethodCalculator _specialWhiteListMethodCache;
|
||||
|
||||
private IObfuscator _dynamicProxyObfuscator;
|
||||
private IObfuscationPolicy _dynamicProxyPolicy;
|
||||
|
||||
public override ObfuscationPassType Type => ObfuscationPassType.CallObfus;
|
||||
|
||||
public CallObfusPass(CallObfuscationSettingsFacade settings)
|
||||
{
|
||||
_settings = settings;
|
||||
CurrentSettings = settings;
|
||||
|
||||
_specialWhiteListMethodCache = new SpecialWhiteListMethodCalculator(settings.obfuscateCallToMethodInMscorlib);
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
_dynamicProxyObfuscator.Done();
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
var ctx = ObfuscationPassContext.Current;
|
||||
_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,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 112178b770868274fb8119a4997a3420
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,300 +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.CallObfus
|
||||
{
|
||||
public class ConfigurableObfuscationPolicy : ObfuscationPolicyBase
|
||||
{
|
||||
class WhiteListAssembly
|
||||
{
|
||||
public string name;
|
||||
public NameMatcher nameMatcher;
|
||||
public bool? obfuscate;
|
||||
public List<WhiteListType> types = new List<WhiteListType>();
|
||||
}
|
||||
|
||||
class WhiteListType
|
||||
{
|
||||
public string name;
|
||||
public NameMatcher nameMatcher;
|
||||
public bool? obfuscate;
|
||||
public List<WhiteListMethod> methods = new List<WhiteListMethod>();
|
||||
}
|
||||
|
||||
class WhiteListMethod
|
||||
{
|
||||
public string name;
|
||||
public NameMatcher nameMatcher;
|
||||
public bool? obfuscate;
|
||||
}
|
||||
|
||||
class ObfuscationRule : IRule<ObfuscationRule>
|
||||
{
|
||||
public ObfuscationLevel? obfuscationLevel;
|
||||
|
||||
public void InheritParent(ObfuscationRule parentRule)
|
||||
{
|
||||
if (obfuscationLevel == null)
|
||||
obfuscationLevel = parentRule.obfuscationLevel;
|
||||
}
|
||||
}
|
||||
|
||||
class AssemblySpec : AssemblyRuleBase<TypeSpec, MethodSpec, ObfuscationRule>
|
||||
{
|
||||
}
|
||||
|
||||
class TypeSpec : TypeRuleBase<MethodSpec, ObfuscationRule>
|
||||
{
|
||||
}
|
||||
|
||||
class MethodSpec : MethodRuleBase<ObfuscationRule>
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private static readonly ObfuscationRule s_default = new ObfuscationRule()
|
||||
{
|
||||
obfuscationLevel = ObfuscationLevel.Basic,
|
||||
};
|
||||
|
||||
private readonly XmlAssemblyTypeMethodRuleParser<AssemblySpec, TypeSpec, MethodSpec, ObfuscationRule> _configParser;
|
||||
|
||||
private ObfuscationRule _global;
|
||||
private readonly List<WhiteListAssembly> _whiteListAssemblies = new List<WhiteListAssembly>();
|
||||
|
||||
private readonly CachedDictionary<IMethod, bool> _whiteListMethodCache;
|
||||
private readonly Dictionary<MethodDef, ObfuscationRule> _methodRuleCache = new Dictionary<MethodDef, ObfuscationRule>();
|
||||
|
||||
public ConfigurableObfuscationPolicy(List<string> toObfuscatedAssemblyNames, List<string> xmlConfigFiles)
|
||||
{
|
||||
_whiteListMethodCache = new CachedDictionary<IMethod, bool>(MethodEqualityComparer.CompareDeclaringTypes, this.ComputeIsInWhiteList);
|
||||
_configParser = new XmlAssemblyTypeMethodRuleParser<AssemblySpec, TypeSpec, MethodSpec, ObfuscationRule>(toObfuscatedAssemblyNames,
|
||||
ParseObfuscationRule, ParseGlobalElement);
|
||||
LoadConfigs(xmlConfigFiles);
|
||||
}
|
||||
|
||||
private void LoadConfigs(List<string> configFiles)
|
||||
{
|
||||
_configParser.LoadConfigs(configFiles);
|
||||
|
||||
if (_global == null)
|
||||
{
|
||||
_global = s_default;
|
||||
}
|
||||
else
|
||||
{
|
||||
_global.InheritParent(s_default);
|
||||
}
|
||||
_configParser.InheritParentRules(_global);
|
||||
InheritWhitelistRules();
|
||||
}
|
||||
|
||||
private void InheritWhitelistRules()
|
||||
{
|
||||
foreach (var ass in _whiteListAssemblies)
|
||||
{
|
||||
foreach (var type in ass.types)
|
||||
{
|
||||
if (type.obfuscate == null)
|
||||
{
|
||||
type.obfuscate = ass.obfuscate;
|
||||
}
|
||||
foreach (var method in type.methods)
|
||||
{
|
||||
if (method.obfuscate == null)
|
||||
{
|
||||
method.obfuscate = type.obfuscate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseGlobalElement(string configFile, XmlElement ele)
|
||||
{
|
||||
switch (ele.Name)
|
||||
{
|
||||
case "global": _global = ParseObfuscationRule(configFile, ele); break;
|
||||
case "whitelist": ParseWhitelist(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 void ParseWhitelist(XmlElement ruleEle)
|
||||
{
|
||||
foreach (XmlNode xmlNode in ruleEle.ChildNodes)
|
||||
{
|
||||
if (!(xmlNode is XmlElement childEle))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
switch (childEle.Name)
|
||||
{
|
||||
case "assembly":
|
||||
{
|
||||
var ass = ParseWhiteListAssembly(childEle);
|
||||
_whiteListAssemblies.Add(ass);
|
||||
break;
|
||||
}
|
||||
default: throw new Exception($"Invalid xml file, unknown node {childEle.Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private WhiteListAssembly ParseWhiteListAssembly(XmlElement element)
|
||||
{
|
||||
var ass = new WhiteListAssembly();
|
||||
ass.name = element.GetAttribute("name");
|
||||
ass.nameMatcher = new NameMatcher(ass.name);
|
||||
|
||||
ass.obfuscate = ConfigUtil.ParseNullableBool(element.GetAttribute("obfuscate")) ?? false;
|
||||
|
||||
foreach (XmlNode node in element.ChildNodes)
|
||||
{
|
||||
if (!(node is XmlElement ele))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
switch (ele.Name)
|
||||
{
|
||||
case "type":
|
||||
ass.types.Add(ParseWhiteListType(ele));
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Invalid xml file, unknown node {ele.Name}");
|
||||
}
|
||||
}
|
||||
return ass;
|
||||
}
|
||||
|
||||
private WhiteListType ParseWhiteListType(XmlElement element)
|
||||
{
|
||||
var type = new WhiteListType();
|
||||
type.name = element.GetAttribute("name");
|
||||
type.nameMatcher = new NameMatcher(type.name);
|
||||
type.obfuscate = ConfigUtil.ParseNullableBool(element.GetAttribute("obfuscate"));
|
||||
|
||||
foreach (XmlNode node in element.ChildNodes)
|
||||
{
|
||||
if (!(node is XmlElement ele))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
switch (ele.Name)
|
||||
{
|
||||
case "method":
|
||||
{
|
||||
type.methods.Add(ParseWhiteListMethod(ele));
|
||||
break;
|
||||
}
|
||||
default: throw new Exception($"Invalid xml file, unknown node {ele.Name}");
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
private WhiteListMethod ParseWhiteListMethod(XmlElement element)
|
||||
{
|
||||
var method = new WhiteListMethod();
|
||||
method.name = element.GetAttribute("name");
|
||||
method.nameMatcher = new NameMatcher(method.name);
|
||||
method.obfuscate = ConfigUtil.ParseNullableBool(element.GetAttribute("obfuscate"));
|
||||
return method;
|
||||
}
|
||||
|
||||
private ObfuscationRule GetMethodObfuscationRule(MethodDef method)
|
||||
{
|
||||
if (!_methodRuleCache.TryGetValue(method, out var rule))
|
||||
{
|
||||
rule = _configParser.GetMethodRule(method, _global);
|
||||
_methodRuleCache[method] = rule;
|
||||
}
|
||||
return rule;
|
||||
}
|
||||
|
||||
public override bool NeedObfuscateCallInMethod(MethodDef method)
|
||||
{
|
||||
ObfuscationRule rule = GetMethodObfuscationRule(method);
|
||||
return rule.obfuscationLevel != null && rule.obfuscationLevel.Value >= ObfuscationLevel.Basic;
|
||||
}
|
||||
|
||||
private bool ComputeIsInWhiteList(IMethod calledMethod)
|
||||
{
|
||||
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();
|
||||
|
||||
string assName = typeDef.Module.Assembly.Name;
|
||||
string typeFullName = typeDef.FullName;
|
||||
string methodName = calledMethod.Name;
|
||||
foreach (var ass in _whiteListAssemblies)
|
||||
{
|
||||
if (!ass.nameMatcher.IsMatch(assName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foreach (var type in ass.types)
|
||||
{
|
||||
if (!type.nameMatcher.IsMatch(typeFullName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foreach (var method in type.methods)
|
||||
{
|
||||
if (method.nameMatcher.IsMatch(methodName))
|
||||
{
|
||||
return !method.obfuscate.Value;
|
||||
}
|
||||
}
|
||||
return !type.obfuscate.Value;
|
||||
}
|
||||
return !ass.obfuscate.Value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool NeedObfuscateCalledMethod(MethodDef callerMethod, IMethod calledMethod, bool callVir)
|
||||
{
|
||||
if (_whiteListMethodCache.GetValue(calledMethod))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d9ea12b16c4b296459db8a60fb1615d6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,263 +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>();
|
||||
|
||||
public DelegateProxyAllocator()
|
||||
{
|
||||
_delegateTypes = new CachedDictionary<MethodSig, TypeDef>(SignatureEqualityComparer.Instance, CreateDelegateForSignature);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
_delegateInstanceHolderType = CreateDelegateInstanceHolderTypeDef();
|
||||
}
|
||||
|
||||
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, 4);
|
||||
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,83 +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.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,273 +0,0 @@
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Editor;
|
||||
using Obfuz.Emit;
|
||||
using Obfuz.Settings;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MethodImplAttributes = dnlib.DotNet.MethodImplAttributes;
|
||||
using TypeAttributes = dnlib.DotNet.TypeAttributes;
|
||||
|
||||
namespace Obfuz.ObfusPasses.CallObfus
|
||||
{
|
||||
|
||||
public struct ProxyCallMethodData
|
||||
{
|
||||
public readonly MethodDef proxyMethod;
|
||||
public readonly int encryptOps;
|
||||
public readonly int salt;
|
||||
public readonly int encryptedIndex;
|
||||
public readonly int index;
|
||||
|
||||
public ProxyCallMethodData(MethodDef proxyMethod, int encryptOps, int salt, int encryptedIndex, int index)
|
||||
{
|
||||
this.proxyMethod = proxyMethod;
|
||||
this.encryptOps = encryptOps;
|
||||
this.salt = salt;
|
||||
this.encryptedIndex = encryptedIndex;
|
||||
this.index = index;
|
||||
}
|
||||
}
|
||||
|
||||
class ModuleDispatchProxyAllocator : GroupByModuleEntityBase
|
||||
{
|
||||
private bool _done;
|
||||
private CallObfuscationSettingsFacade _settings;
|
||||
|
||||
|
||||
class MethodProxyInfo
|
||||
{
|
||||
public MethodDef proxyMethod;
|
||||
|
||||
public int index;
|
||||
public int encryptedOps;
|
||||
public int salt;
|
||||
public int encryptedIndex;
|
||||
}
|
||||
|
||||
private readonly Dictionary<MethodKey, MethodProxyInfo> _methodProxys = new Dictionary<MethodKey, MethodProxyInfo>();
|
||||
|
||||
class CallInfo
|
||||
{
|
||||
public string id;
|
||||
public IMethod method;
|
||||
public bool callVir;
|
||||
}
|
||||
|
||||
class DispatchMethodInfo
|
||||
{
|
||||
public MethodDef methodDef;
|
||||
public List<CallInfo> methods = new List<CallInfo>();
|
||||
}
|
||||
|
||||
private readonly Dictionary<MethodSig, List<DispatchMethodInfo>> _dispatchMethods = new Dictionary<MethodSig, List<DispatchMethodInfo>>(SignatureEqualityComparer.Instance);
|
||||
|
||||
|
||||
private TypeDef _proxyTypeDef;
|
||||
|
||||
public ModuleDispatchProxyAllocator()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
_settings = CallObfusPass.CurrentSettings;
|
||||
}
|
||||
|
||||
private TypeDef CreateProxyTypeDef()
|
||||
{
|
||||
ModuleDef mod = Module;
|
||||
using (var scope = new DisableTypeDefFindCacheScope(mod))
|
||||
{
|
||||
var typeDef = new TypeDefUser($"{ConstValues.ObfuzInternalSymbolNamePrefix}ProxyCall", mod.CorLibTypes.Object.ToTypeDefOrRef());
|
||||
typeDef.Attributes = TypeAttributes.NotPublic | TypeAttributes.Sealed;
|
||||
mod.Types.Add(typeDef);
|
||||
return typeDef;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly HashSet<string> _uniqueMethodNames = new HashSet<string>();
|
||||
|
||||
|
||||
private string ToUniqueMethodName(string originalName)
|
||||
{
|
||||
if (_uniqueMethodNames.Add(originalName))
|
||||
{
|
||||
return originalName;
|
||||
}
|
||||
for (int index = 1; ; index++)
|
||||
{
|
||||
string uniqueName = $"{originalName}${index}";
|
||||
if (_uniqueMethodNames.Add(uniqueName))
|
||||
{
|
||||
return uniqueName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string CreateDispatchMethodName(MethodSig methodSig, int index)
|
||||
{
|
||||
// use a stable name for the dispatch method, so that we can reuse it across different modules
|
||||
// this is important for cross-module calls
|
||||
return ToUniqueMethodName($"{ConstValues.ObfuzInternalSymbolNamePrefix}Dispatch_{HashUtil.ComputeHash(methodSig.Params) & 0xFFFF}_{HashUtil.ComputeHash(methodSig.RetType) & 0xFFFFFF}");
|
||||
}
|
||||
|
||||
private MethodDef CreateDispatchMethodInfo(MethodSig methodSig, int index)
|
||||
{
|
||||
if (_proxyTypeDef == null)
|
||||
{
|
||||
_proxyTypeDef = CreateProxyTypeDef();
|
||||
}
|
||||
MethodDef methodDef = new MethodDefUser(CreateDispatchMethodName(methodSig, index), methodSig,
|
||||
MethodImplAttributes.IL | MethodImplAttributes.Managed,
|
||||
MethodAttributes.Static | MethodAttributes.Public);
|
||||
methodDef.DeclaringType = _proxyTypeDef;
|
||||
return methodDef;
|
||||
}
|
||||
|
||||
private MethodSig CreateDispatchMethodSig(IMethod method)
|
||||
{
|
||||
ModuleDef mod = Module;
|
||||
MethodSig methodSig = MetaUtil.ToSharedMethodSig(mod.CorLibTypes, MetaUtil.GetInflatedMethodSig(method, null));
|
||||
//MethodSig methodSig = MetaUtil.GetInflatedMethodSig(method).Clone();
|
||||
//methodSig.Params
|
||||
switch (MetaUtil.GetThisArgType(method))
|
||||
{
|
||||
case ThisArgType.Class:
|
||||
{
|
||||
methodSig.Params.Insert(0, mod.CorLibTypes.Object);
|
||||
break;
|
||||
}
|
||||
case ThisArgType.ValueType:
|
||||
{
|
||||
methodSig.Params.Insert(0, mod.CorLibTypes.IntPtr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// extra param for index
|
||||
methodSig.Params.Add(mod.CorLibTypes.Int32);
|
||||
return MethodSig.CreateStatic(methodSig.RetType, methodSig.Params.ToArray());
|
||||
}
|
||||
|
||||
private int GenerateSalt(IRandom random)
|
||||
{
|
||||
return random.NextInt();
|
||||
}
|
||||
|
||||
private int GenerateEncryptOps(IRandom random)
|
||||
{
|
||||
return EncryptionUtil.GenerateEncryptionOpCodes(random, EncryptionScope.encryptor, _settings.obfuscationLevel);
|
||||
}
|
||||
|
||||
private DispatchMethodInfo GetDispatchMethod(IMethod method)
|
||||
{
|
||||
MethodSig methodSig = CreateDispatchMethodSig(method);
|
||||
if (!_dispatchMethods.TryGetValue(methodSig, out var dispatchMethods))
|
||||
{
|
||||
dispatchMethods = new List<DispatchMethodInfo>();
|
||||
_dispatchMethods.Add(methodSig, dispatchMethods);
|
||||
}
|
||||
if (dispatchMethods.Count == 0 || dispatchMethods.Last().methods.Count >= _settings.maxProxyMethodCountPerDispatchMethod)
|
||||
{
|
||||
var newDispatchMethodInfo = new DispatchMethodInfo
|
||||
{
|
||||
methodDef = CreateDispatchMethodInfo(methodSig, dispatchMethods.Count),
|
||||
};
|
||||
dispatchMethods.Add(newDispatchMethodInfo);
|
||||
}
|
||||
return dispatchMethods.Last();
|
||||
}
|
||||
|
||||
private IRandom CreateRandomForMethod(IMethod method, bool callVir)
|
||||
{
|
||||
int seed = MethodEqualityComparer.CompareDeclaringTypes.GetHashCode(method);
|
||||
return EncryptionScope.localRandomCreator(seed);
|
||||
}
|
||||
|
||||
public ProxyCallMethodData Allocate(IMethod method, bool callVir)
|
||||
{
|
||||
if (_done)
|
||||
{
|
||||
throw new Exception("can't Allocate after done");
|
||||
}
|
||||
var key = new MethodKey(method, callVir);
|
||||
if (!_methodProxys.TryGetValue(key, out var proxyInfo))
|
||||
{
|
||||
var methodDispatcher = GetDispatchMethod(method);
|
||||
|
||||
int index = methodDispatcher.methods.Count;
|
||||
IRandom localRandom = CreateRandomForMethod(method, callVir);
|
||||
int encryptOps = GenerateEncryptOps(localRandom);
|
||||
int salt = GenerateSalt(localRandom);
|
||||
int encryptedIndex = EncryptionScope.encryptor.Encrypt(index, encryptOps, salt);
|
||||
proxyInfo = new MethodProxyInfo()
|
||||
{
|
||||
proxyMethod = methodDispatcher.methodDef,
|
||||
index = index,
|
||||
encryptedOps = encryptOps,
|
||||
salt = salt,
|
||||
encryptedIndex = encryptedIndex,
|
||||
};
|
||||
methodDispatcher.methods.Add(new CallInfo { id = $"{method}{(callVir ? "" : "v")}", method = method, callVir = callVir });
|
||||
_methodProxys.Add(key, proxyInfo);
|
||||
}
|
||||
return new ProxyCallMethodData(proxyInfo.proxyMethod, proxyInfo.encryptedOps, proxyInfo.salt, proxyInfo.encryptedIndex, proxyInfo.index);
|
||||
}
|
||||
|
||||
public override void Done()
|
||||
{
|
||||
if (_done)
|
||||
{
|
||||
throw new Exception("Already done");
|
||||
}
|
||||
_done = true;
|
||||
if (_proxyTypeDef == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// for stable order, we sort methods by name
|
||||
var methodWithNamePairList = _proxyTypeDef.Methods.Select(m => (m, m.ToString())).ToList();
|
||||
methodWithNamePairList.Sort((a, b) => a.Item2.CompareTo(b.Item2));
|
||||
_proxyTypeDef.Methods.Clear();
|
||||
foreach (var methodPair in methodWithNamePairList)
|
||||
{
|
||||
methodPair.Item1.DeclaringType = _proxyTypeDef;
|
||||
}
|
||||
|
||||
foreach (DispatchMethodInfo dispatchMethod in _dispatchMethods.Values.SelectMany(ms => ms))
|
||||
{
|
||||
var methodDef = dispatchMethod.methodDef;
|
||||
var methodSig = methodDef.MethodSig;
|
||||
|
||||
|
||||
var body = new CilBody();
|
||||
methodDef.Body = body;
|
||||
var ins = body.Instructions;
|
||||
|
||||
foreach (Parameter param in methodDef.Parameters)
|
||||
{
|
||||
ins.Add(Instruction.Create(OpCodes.Ldarg, param));
|
||||
}
|
||||
|
||||
var switchCases = new List<Instruction>();
|
||||
var switchInst = Instruction.Create(OpCodes.Switch, switchCases);
|
||||
ins.Add(switchInst);
|
||||
var ret = Instruction.Create(OpCodes.Ret);
|
||||
|
||||
// sort methods by signature to ensure stable order
|
||||
//dispatchMethod.methods.Sort((a, b) => a.id.CompareTo(b.id));
|
||||
foreach (CallInfo ci in dispatchMethod.methods)
|
||||
{
|
||||
var callTargetMethod = Instruction.Create(ci.callVir ? OpCodes.Callvirt : OpCodes.Call, ci.method);
|
||||
switchCases.Add(callTargetMethod);
|
||||
ins.Add(callTargetMethod);
|
||||
ins.Add(Instruction.Create(OpCodes.Br, ret));
|
||||
}
|
||||
ins.Add(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16b960455f093854d927c2dbd47a4826
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,53 +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.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,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e13ba01b03439e049af0e09367825cde
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6af3cd881fdefd14d9a55b77088dd5a4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user