重构 MemEncrypt为FieldEncrypt
parent
3c6cc385a4
commit
f0bec3fab9
|
@ -0,0 +1,19 @@
|
|||
using dnlib.DotNet;
|
||||
|
||||
namespace Obfuz.ObfusPasses.FieldEncrypt
|
||||
{
|
||||
public class ConfigurableEncryptPolicy : EncryptPolicyBase
|
||||
{
|
||||
|
||||
public override bool NeedEncrypt(FieldDef field)
|
||||
{
|
||||
TypeDef type = field.DeclaringType;
|
||||
// TODO
|
||||
if (type.Name == "EncryptField" || type.Name == "EncryptProperty")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Emit;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace Obfuz.ObfusPasses.FieldEncrypt
|
||||
{
|
||||
public class DefaultFieldEncryptor : FieldEncryptorBase
|
||||
{
|
||||
private readonly IRandom _random;
|
||||
private readonly IEncryptor _encryptor;
|
||||
|
||||
public DefaultFieldEncryptor(IRandom random, IEncryptor encryptor)
|
||||
{
|
||||
_random = random;
|
||||
_encryptor = encryptor;
|
||||
}
|
||||
|
||||
private DefaultModuleMetadataImporter GetMetadataImporter(MethodDef method)
|
||||
{
|
||||
return MetadataImporter.Instance.GetDefaultModuleMetadataImporter(method.Module);
|
||||
}
|
||||
|
||||
class FieldEncryptInfo
|
||||
{
|
||||
public int encryptOps;
|
||||
public int salt;
|
||||
public ElementType fieldType;
|
||||
public long xorValueForZero;
|
||||
}
|
||||
|
||||
private readonly Dictionary<FieldDef, FieldEncryptInfo> _fieldEncryptInfoCache = new Dictionary<FieldDef, FieldEncryptInfo>();
|
||||
|
||||
|
||||
private long CalcXorValueForZero(ElementType type, int encryptOps, int salt)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ElementType.I4:
|
||||
case ElementType.U4:
|
||||
case ElementType.R4:
|
||||
return _encryptor.Encrypt(0, encryptOps, salt);
|
||||
case ElementType.I8:
|
||||
case ElementType.U8:
|
||||
case ElementType.R8:
|
||||
return _encryptor.Encrypt(0L, encryptOps, salt);
|
||||
default:
|
||||
throw new NotSupportedException($"Unsupported field type: {type} for encryption");
|
||||
}
|
||||
}
|
||||
|
||||
private FieldEncryptInfo GetFieldEncryptInfo(FieldDef field)
|
||||
{
|
||||
if (_fieldEncryptInfoCache.TryGetValue(field, out var info))
|
||||
{
|
||||
return info;
|
||||
}
|
||||
|
||||
int encryptOps = _random.NextInt();
|
||||
int salt = _random.NextInt();
|
||||
ElementType fieldType = field.FieldSig.Type.RemovePinnedAndModifiers().ElementType;
|
||||
long xorValueForZero = CalcXorValueForZero(fieldType, encryptOps, salt);
|
||||
|
||||
info = new FieldEncryptInfo
|
||||
{
|
||||
encryptOps = encryptOps,
|
||||
salt = salt,
|
||||
fieldType = fieldType,
|
||||
xorValueForZero = xorValueForZero,
|
||||
};
|
||||
_fieldEncryptInfoCache[field] = info;
|
||||
return info;
|
||||
}
|
||||
|
||||
public override void Encrypt(MethodDef method, FieldDef field, List<Instruction> outputInstructions, Instruction currentInstruction)
|
||||
{
|
||||
DefaultModuleMetadataImporter importer = GetMetadataImporter(method);
|
||||
FieldEncryptInfo fei = GetFieldEncryptInfo(field);
|
||||
if (fei.fieldType == ElementType.I4 || fei.fieldType == ElementType.U4 || fei.fieldType == ElementType.R4)
|
||||
{
|
||||
// value has been put on stack
|
||||
|
||||
// encrypt
|
||||
outputInstructions.Add(Instruction.CreateLdcI4(fei.encryptOps));
|
||||
outputInstructions.Add(Instruction.CreateLdcI4(fei.salt));
|
||||
outputInstructions.Add(Instruction.Create(OpCodes.Call, importer.EncryptInt));
|
||||
// xor
|
||||
outputInstructions.Add(Instruction.CreateLdcI4((int)fei.xorValueForZero));
|
||||
outputInstructions.Add(Instruction.Create(OpCodes.Xor));
|
||||
}
|
||||
else if (fei.fieldType == ElementType.I8 || fei.fieldType == ElementType.U8 || fei.fieldType == ElementType.R8)
|
||||
{
|
||||
// value has been put on stack
|
||||
|
||||
// encrypt
|
||||
outputInstructions.Add(Instruction.CreateLdcI4(fei.encryptOps));
|
||||
outputInstructions.Add(Instruction.CreateLdcI4(fei.salt));
|
||||
outputInstructions.Add(Instruction.Create(OpCodes.Call, importer.EncryptLong));
|
||||
// xor
|
||||
outputInstructions.Add(Instruction.Create(OpCodes.Ldc_I8, fei.xorValueForZero));
|
||||
outputInstructions.Add(Instruction.Create(OpCodes.Xor));
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.IsTrue(false, $"Unsupported field type: {fei.fieldType} for encryption");
|
||||
}
|
||||
|
||||
outputInstructions.Add(currentInstruction.Clone());
|
||||
}
|
||||
|
||||
public override void Decrypt(MethodDef method, FieldDef field, List<Instruction> outputInstructions, Instruction currentInstruction)
|
||||
{
|
||||
outputInstructions.Add(currentInstruction.Clone());
|
||||
DefaultModuleMetadataImporter importer = GetMetadataImporter(method);
|
||||
FieldEncryptInfo fei = GetFieldEncryptInfo(field);
|
||||
if (fei.fieldType == ElementType.I4 || fei.fieldType == ElementType.U4 || fei.fieldType == ElementType.R4)
|
||||
{
|
||||
// value has been put on stack
|
||||
// xor
|
||||
outputInstructions.Add(Instruction.CreateLdcI4((int)fei.xorValueForZero));
|
||||
outputInstructions.Add(Instruction.Create(OpCodes.Xor));
|
||||
|
||||
// decrypt
|
||||
outputInstructions.Add(Instruction.CreateLdcI4(fei.encryptOps));
|
||||
outputInstructions.Add(Instruction.CreateLdcI4(fei.salt));
|
||||
outputInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptInt));
|
||||
}
|
||||
else if (fei.fieldType == ElementType.I8 || fei.fieldType == ElementType.U8 || fei.fieldType == ElementType.R8)
|
||||
{
|
||||
// value has been put on stack
|
||||
// xor
|
||||
outputInstructions.Add(Instruction.Create(OpCodes.Ldc_I8, fei.xorValueForZero));
|
||||
outputInstructions.Add(Instruction.Create(OpCodes.Xor));
|
||||
|
||||
// decrypt
|
||||
outputInstructions.Add(Instruction.CreateLdcI4(fei.encryptOps));
|
||||
outputInstructions.Add(Instruction.CreateLdcI4(fei.salt));
|
||||
outputInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptLong));
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.IsTrue(false, $"Unsupported field type: {fei.fieldType} for decryption");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +1,21 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz;
|
||||
using Obfuz.ObfusPasses.MemEncrypt;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obfuz.ObfusPasses.MemEncrypt
|
||||
namespace Obfuz.ObfusPasses.FieldEncrypt
|
||||
{
|
||||
|
||||
public class MemEncryptPass : InstructionObfuscationPassBase
|
||||
public class FieldEncryptPass : InstructionObfuscationPassBase
|
||||
{
|
||||
private readonly IEncryptPolicy _encryptionPolicy = new ConfigEncryptionPolicy();
|
||||
private readonly IMemEncryptor _memoryEncryptor = new DefaultMemoryEncryptor();
|
||||
private readonly IEncryptPolicy _encryptionPolicy = new ConfigurableEncryptPolicy();
|
||||
private IFieldEncryptor _memoryEncryptor;
|
||||
|
||||
public override void Start(ObfuscationPassContext ctx)
|
||||
{
|
||||
_memoryEncryptor = new DefaultFieldEncryptor(ctx.random, ctx.encryptor);
|
||||
|
||||
}
|
||||
|
||||
|
@ -29,32 +29,31 @@ namespace Obfuz.ObfusPasses.MemEncrypt
|
|||
return true;
|
||||
}
|
||||
|
||||
private FieldDef TryResolveFieldDef(IField field)
|
||||
private bool IsSupportedFieldType(TypeSig type)
|
||||
{
|
||||
if (field is FieldDef fieldDef)
|
||||
type = type.RemovePinnedAndModifiers();
|
||||
switch (type.ElementType)
|
||||
{
|
||||
return fieldDef;
|
||||
case ElementType.I4:
|
||||
case ElementType.I8:
|
||||
case ElementType.U4:
|
||||
case ElementType.U8:
|
||||
case ElementType.R4:
|
||||
case ElementType.R8:
|
||||
return true;
|
||||
default: return false;
|
||||
}
|
||||
if (field is MemberRef memberRef)
|
||||
{
|
||||
return memberRef.ResolveFieldDef();
|
||||
}
|
||||
throw new System.Exception($"Cannot resolve field: {field}");
|
||||
}
|
||||
|
||||
protected override bool TryObfuscateInstruction(MethodDef callingMethod, Instruction inst, IList<Instruction> instructions, int instructionIndex, List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions)
|
||||
{
|
||||
Code code = inst.OpCode.Code;
|
||||
if (!(inst.Operand is IField field))
|
||||
if (!(inst.Operand is IField field) || !field.IsField)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FieldDef fieldDef = TryResolveFieldDef(field);
|
||||
if (fieldDef == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!_encryptionPolicy.NeedEncrypt(fieldDef))
|
||||
FieldDef fieldDef = field.ResolveFieldDefThrow();
|
||||
if (!IsSupportedFieldType(fieldDef.FieldSig.Type) || !_encryptionPolicy.NeedEncrypt(fieldDef))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -67,22 +66,22 @@ namespace Obfuz.ObfusPasses.MemEncrypt
|
|||
{
|
||||
case Code.Ldfld:
|
||||
{
|
||||
_memoryEncryptor.Decrypt(fieldDef, outputInstructions, ctx);
|
||||
_memoryEncryptor.Decrypt(callingMethod, fieldDef, outputInstructions, inst);
|
||||
break;
|
||||
}
|
||||
case Code.Stfld:
|
||||
{
|
||||
_memoryEncryptor.Encrypt(fieldDef, outputInstructions, ctx);
|
||||
_memoryEncryptor.Encrypt(callingMethod, fieldDef, outputInstructions, inst);
|
||||
break;
|
||||
}
|
||||
case Code.Ldsfld:
|
||||
{
|
||||
_memoryEncryptor.Decrypt(fieldDef, outputInstructions, ctx);
|
||||
_memoryEncryptor.Decrypt(callingMethod, fieldDef, outputInstructions, inst);
|
||||
break;
|
||||
}
|
||||
case Code.Stsfld:
|
||||
{
|
||||
_memoryEncryptor.Encrypt(fieldDef, outputInstructions, ctx);
|
||||
_memoryEncryptor.Encrypt(callingMethod, fieldDef, outputInstructions, inst);
|
||||
break;
|
||||
}
|
||||
case Code.Ldflda:
|
|
@ -5,7 +5,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Obfuz.ObfusPasses.MemEncrypt
|
||||
namespace Obfuz.ObfusPasses.FieldEncrypt
|
||||
{
|
||||
public interface IEncryptPolicy
|
||||
{
|
|
@ -0,0 +1,29 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Obfuz.ObfusPasses.FieldEncrypt
|
||||
{
|
||||
public class MemoryEncryptionContext
|
||||
{
|
||||
public ModuleDef module;
|
||||
|
||||
public Instruction currentInstruction;
|
||||
}
|
||||
|
||||
public interface IFieldEncryptor
|
||||
{
|
||||
void Encrypt(MethodDef method, FieldDef field, List<Instruction> outputInstructions, Instruction currentInstruction);
|
||||
|
||||
void Decrypt(MethodDef method, FieldDef field, List<Instruction> outputInstructions, Instruction currentInstruction);
|
||||
}
|
||||
|
||||
public abstract class FieldEncryptorBase : IFieldEncryptor
|
||||
{
|
||||
public abstract void Encrypt(MethodDef method, FieldDef field, List<Instruction> outputInstructions, Instruction currentInstruction);
|
||||
public abstract void Decrypt(MethodDef method, FieldDef field, List<Instruction> outputInstructions, Instruction currentInstruction);
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
|
||||
namespace Obfuz.ObfusPasses.MemEncrypt
|
||||
{
|
||||
public class ConfigEncryptionPolicy : EncryptPolicyBase
|
||||
{
|
||||
|
||||
private bool IsSupportedFieldType(TypeSig type)
|
||||
{
|
||||
type = type.RemovePinnedAndModifiers();
|
||||
switch (type.ElementType)
|
||||
{
|
||||
case ElementType.I4:
|
||||
case ElementType.I8:
|
||||
case ElementType.U4:
|
||||
case ElementType.U8:
|
||||
case ElementType.R4:
|
||||
case ElementType.R8:
|
||||
return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool NeedEncrypt(FieldDef field)
|
||||
{
|
||||
TypeDef type = field.DeclaringType;
|
||||
if (!IsSupportedFieldType(field.FieldType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// TODO
|
||||
if (type.Name == "EncryptField" || type.Name == "EncryptProperty")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Emit;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace Obfuz.ObfusPasses.MemEncrypt
|
||||
{
|
||||
public class DefaultMemoryEncryptor : MemEncryptorBase
|
||||
{
|
||||
|
||||
private class ModuleDefaultMemoryEncryptor
|
||||
{
|
||||
private readonly ModuleDef _module;
|
||||
|
||||
public ModuleDefaultMemoryEncryptor(ModuleDef module)
|
||||
{
|
||||
_module = module;
|
||||
InitMetadatas(module);
|
||||
}
|
||||
|
||||
private static IMethod s_castIntAsFloat;
|
||||
private static IMethod s_castLongAsDouble;
|
||||
private static IMethod s_castFloatAsInt;
|
||||
private static IMethod s_castDoubleAsLong;
|
||||
|
||||
private void InitMetadatas(ModuleDef mod)
|
||||
{
|
||||
if (s_castFloatAsInt != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var constUtilityType = typeof(ConstUtility);
|
||||
|
||||
s_castIntAsFloat = mod.Import(constUtilityType.GetMethod("CastIntAsFloat"));
|
||||
Assert.IsNotNull(s_castIntAsFloat, "CastIntAsFloat not found");
|
||||
s_castLongAsDouble = mod.Import(constUtilityType.GetMethod("CastLongAsDouble"));
|
||||
Assert.IsNotNull(s_castLongAsDouble, "CastLongAsDouble not found");
|
||||
s_castFloatAsInt = mod.Import(constUtilityType.GetMethod("CastFloatAsInt"));
|
||||
Assert.IsNotNull(s_castFloatAsInt, "CastFloatAsInt not found");
|
||||
s_castDoubleAsLong = mod.Import(constUtilityType.GetMethod("CastDoubleAsLong"));
|
||||
Assert.IsNotNull(s_castDoubleAsLong, "CastDoubleAsLong not found");
|
||||
}
|
||||
|
||||
|
||||
private VariableEncryption CreateEncryption(ElementType type)
|
||||
{
|
||||
IRandom random = new RandomWithKey(new byte[16], 1234);
|
||||
switch (type)
|
||||
{
|
||||
case ElementType.I4:
|
||||
return new VariableEncryption(DataNodeType.Int32, random);
|
||||
case ElementType.I8:
|
||||
return new VariableEncryption(DataNodeType.Int64, random);
|
||||
case ElementType.R4:
|
||||
return new VariableEncryption(DataNodeType.Float32, random);
|
||||
case ElementType.R8:
|
||||
return new VariableEncryption(DataNodeType.Float64, random);
|
||||
default: throw new Exception($"Unsupported type {type} for MemoryEncryptor");
|
||||
}
|
||||
}
|
||||
|
||||
public void Encrypt(FieldDef field, List<Instruction> outputInstructions, MemoryEncryptionContext ctx)
|
||||
{
|
||||
|
||||
ElementType type = field.FieldType.RemovePinnedAndModifiers().ElementType;
|
||||
var encryption = CreateEncryption(type);
|
||||
|
||||
encryption.EmitTransform(outputInstructions, new EncryptionCompileContext { module = _module });
|
||||
|
||||
outputInstructions.Add(ctx.currentInstruction.Clone());
|
||||
}
|
||||
|
||||
public void Decrypt(FieldDef field, List<Instruction> outputInstructions, MemoryEncryptionContext ctx)
|
||||
{
|
||||
outputInstructions.Add(ctx.currentInstruction.Clone());
|
||||
ElementType type = field.FieldType.RemovePinnedAndModifiers().ElementType;
|
||||
var encryption = CreateEncryption(type);
|
||||
|
||||
encryption.EmitRevertTransform(outputInstructions, new EncryptionCompileContext { module = _module });
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Dictionary<ModuleDef, ModuleDefaultMemoryEncryptor> _moduleEncryptors = new Dictionary<ModuleDef, ModuleDefaultMemoryEncryptor>();
|
||||
|
||||
private ModuleDefaultMemoryEncryptor GetModuleEncryptor(ModuleDef module)
|
||||
{
|
||||
if (!_moduleEncryptors.TryGetValue(module, out var encryptor))
|
||||
{
|
||||
encryptor = new ModuleDefaultMemoryEncryptor(module);
|
||||
_moduleEncryptors.Add(module, encryptor);
|
||||
}
|
||||
return encryptor;
|
||||
}
|
||||
|
||||
public override void Encrypt(FieldDef field, List<Instruction> outputInstructions, MemoryEncryptionContext ctx)
|
||||
{
|
||||
GetModuleEncryptor(ctx.module).Encrypt(field, outputInstructions, ctx);
|
||||
}
|
||||
|
||||
public override void Decrypt(FieldDef field, List<Instruction> outputInstructions, MemoryEncryptionContext ctx)
|
||||
{
|
||||
GetModuleEncryptor(ctx.module).Decrypt(field, outputInstructions, ctx);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Obfuz.ObfusPasses.MemEncrypt
|
||||
{
|
||||
public class MemoryEncryptionContext
|
||||
{
|
||||
public ModuleDef module;
|
||||
|
||||
public Instruction currentInstruction;
|
||||
}
|
||||
|
||||
public interface IMemEncryptor
|
||||
{
|
||||
void Encrypt(FieldDef field, List<Instruction> outputInstructions, MemoryEncryptionContext ctx);
|
||||
|
||||
void Decrypt(FieldDef field, List<Instruction> outputInstructions, MemoryEncryptionContext ctx);
|
||||
}
|
||||
|
||||
public abstract class MemEncryptorBase : IMemEncryptor
|
||||
{
|
||||
public abstract void Encrypt(FieldDef field, List<Instruction> outputInstructions, MemoryEncryptionContext ctx);
|
||||
public abstract void Decrypt(FieldDef field, List<Instruction> outputInstructions, MemoryEncryptionContext ctx);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
using Obfuz.ObfusPasses;
|
||||
using Obfuz.ObfusPasses.CallObfus;
|
||||
using Obfuz.ObfusPasses.CleanUp;
|
||||
using Obfuz.ObfusPasses.ConstEncrypt;
|
||||
using Obfuz.ObfusPasses.ExprObfus;
|
||||
using Obfuz.ObfusPasses.MemEncrypt;
|
||||
using Obfuz.ObfusPasses.FieldEncrypt;
|
||||
using Obfuz.ObfusPasses.SymbolObfus;
|
||||
using Obfuz.Settings;
|
||||
using System.Collections.Generic;
|
||||
|
@ -77,7 +76,7 @@ namespace Obfuz
|
|||
ObfuscationPassType obfuscationPasses = settings.enabledObfuscationPasses;
|
||||
if (obfuscationPasses.HasFlag(ObfuscationPassType.MemoryEncryption))
|
||||
{
|
||||
builder.AddPass(new MemEncryptPass());
|
||||
builder.AddPass(new FieldEncryptPass());
|
||||
}
|
||||
if (obfuscationPasses.HasFlag(ObfuscationPassType.CallProxy))
|
||||
{
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obfuz.Settings
|
||||
{
|
||||
[Serializable]
|
||||
public class FieldEncryptSettings
|
||||
{
|
||||
[Tooltip("config xml files")]
|
||||
public string[] configFiles;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue