重构 MemEncrypt为FieldEncrypt

backup
walon 2025-05-10 19:09:44 +08:00
parent 3c6cc385a4
commit f0bec3fab9
10 changed files with 241 additions and 204 deletions

View File

@ -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;
}
}
}

View File

@ -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");
}
}
}
}

View File

@ -1,21 +1,21 @@
using dnlib.DotNet; using dnlib.DotNet;
using dnlib.DotNet.Emit; using dnlib.DotNet.Emit;
using Obfuz; using Obfuz;
using Obfuz.ObfusPasses.MemEncrypt;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; 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 IEncryptPolicy _encryptionPolicy = new ConfigurableEncryptPolicy();
private readonly IMemEncryptor _memoryEncryptor = new DefaultMemoryEncryptor(); private IFieldEncryptor _memoryEncryptor;
public override void Start(ObfuscationPassContext ctx) public override void Start(ObfuscationPassContext ctx)
{ {
_memoryEncryptor = new DefaultFieldEncryptor(ctx.random, ctx.encryptor);
} }
@ -29,32 +29,31 @@ namespace Obfuz.ObfusPasses.MemEncrypt
return true; 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) protected override bool TryObfuscateInstruction(MethodDef callingMethod, Instruction inst, IList<Instruction> instructions, int instructionIndex, List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions)
{ {
Code code = inst.OpCode.Code; Code code = inst.OpCode.Code;
if (!(inst.Operand is IField field)) if (!(inst.Operand is IField field) || !field.IsField)
{ {
return false; return false;
} }
FieldDef fieldDef = TryResolveFieldDef(field); FieldDef fieldDef = field.ResolveFieldDefThrow();
if (fieldDef == null) if (!IsSupportedFieldType(fieldDef.FieldSig.Type) || !_encryptionPolicy.NeedEncrypt(fieldDef))
{
return false;
}
if (!_encryptionPolicy.NeedEncrypt(fieldDef))
{ {
return false; return false;
} }
@ -67,22 +66,22 @@ namespace Obfuz.ObfusPasses.MemEncrypt
{ {
case Code.Ldfld: case Code.Ldfld:
{ {
_memoryEncryptor.Decrypt(fieldDef, outputInstructions, ctx); _memoryEncryptor.Decrypt(callingMethod, fieldDef, outputInstructions, inst);
break; break;
} }
case Code.Stfld: case Code.Stfld:
{ {
_memoryEncryptor.Encrypt(fieldDef, outputInstructions, ctx); _memoryEncryptor.Encrypt(callingMethod, fieldDef, outputInstructions, inst);
break; break;
} }
case Code.Ldsfld: case Code.Ldsfld:
{ {
_memoryEncryptor.Decrypt(fieldDef, outputInstructions, ctx); _memoryEncryptor.Decrypt(callingMethod, fieldDef, outputInstructions, inst);
break; break;
} }
case Code.Stsfld: case Code.Stsfld:
{ {
_memoryEncryptor.Encrypt(fieldDef, outputInstructions, ctx); _memoryEncryptor.Encrypt(callingMethod, fieldDef, outputInstructions, inst);
break; break;
} }
case Code.Ldflda: case Code.Ldflda:

View File

@ -5,7 +5,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Obfuz.ObfusPasses.MemEncrypt namespace Obfuz.ObfusPasses.FieldEncrypt
{ {
public interface IEncryptPolicy public interface IEncryptPolicy
{ {

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -1,9 +1,8 @@
using Obfuz.ObfusPasses; using Obfuz.ObfusPasses;
using Obfuz.ObfusPasses.CallObfus; using Obfuz.ObfusPasses.CallObfus;
using Obfuz.ObfusPasses.CleanUp;
using Obfuz.ObfusPasses.ConstEncrypt; using Obfuz.ObfusPasses.ConstEncrypt;
using Obfuz.ObfusPasses.ExprObfus; using Obfuz.ObfusPasses.ExprObfus;
using Obfuz.ObfusPasses.MemEncrypt; using Obfuz.ObfusPasses.FieldEncrypt;
using Obfuz.ObfusPasses.SymbolObfus; using Obfuz.ObfusPasses.SymbolObfus;
using Obfuz.Settings; using Obfuz.Settings;
using System.Collections.Generic; using System.Collections.Generic;
@ -77,7 +76,7 @@ namespace Obfuz
ObfuscationPassType obfuscationPasses = settings.enabledObfuscationPasses; ObfuscationPassType obfuscationPasses = settings.enabledObfuscationPasses;
if (obfuscationPasses.HasFlag(ObfuscationPassType.MemoryEncryption)) if (obfuscationPasses.HasFlag(ObfuscationPassType.MemoryEncryption))
{ {
builder.AddPass(new MemEncryptPass()); builder.AddPass(new FieldEncryptPass());
} }
if (obfuscationPasses.HasFlag(ObfuscationPassType.CallProxy)) if (obfuscationPasses.HasFlag(ObfuscationPassType.CallProxy))
{ {

View File

@ -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;
}
}