obfuz/Editor/Data/RvaDataAllocator.cs

341 lines
12 KiB
C#
Raw Normal View History

2025-04-22 19:34:46 +08:00
using dnlib.DotNet;
2025-04-22 22:53:51 +08:00
using dnlib.DotNet.Emit;
2025-05-07 19:39:09 +08:00
using Obfuz.Emit;
2025-04-22 19:34:46 +08:00
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine.Assertions;
2025-05-07 22:55:07 +08:00
namespace Obfuz.Data
2025-04-22 19:34:46 +08:00
{
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 ModuleRvaDataAllocator : GroupByModuleEntityBase
2025-04-22 19:34:46 +08:00
{
// randomized
2025-05-09 20:18:24 +08:00
const int maxRvaDataSize = 0x1000;
2025-04-22 19:34:46 +08:00
2025-05-09 20:18:24 +08:00
private ModuleDef _module;
2025-04-22 19:34:46 +08:00
private readonly IRandom _random;
2025-04-23 18:58:44 +08:00
private readonly IEncryptor _encryptor;
2025-04-22 19:34:46 +08:00
class RvaField
{
public FieldDef holderDataField;
public FieldDef runtimeValueField;
2025-05-11 10:37:42 +08:00
public int encryptionOps;
2025-04-22 19:34:46 +08:00
public uint size;
public List<byte> bytes;
public int salt;
2025-04-22 19:34:46 +08:00
public void FillPaddingToSize(int newSize)
{
for (int i = bytes.Count; i < newSize; i++)
{
bytes.Add(0xAB);
}
}
public void FillPaddingToEnd()
2025-04-22 19:34:46 +08:00
{
// fill with random value
for (int i = bytes.Count; i < size; i++)
{
bytes.Add(0xAB);
}
}
}
private readonly List<RvaField> _rvaFields = new List<RvaField>();
private RvaField _currentField;
private TypeDef _rvaTypeDef;
2025-04-22 22:53:51 +08:00
private readonly Dictionary<int, TypeDef> _dataHolderTypeBySizes = new Dictionary<int, TypeDef>();
2025-04-22 19:34:46 +08:00
2025-05-09 20:18:24 +08:00
public ModuleRvaDataAllocator(IRandom random, IEncryptor encryptor)
2025-04-22 19:34:46 +08:00
{
_random = random;
2025-04-23 18:58:44 +08:00
_encryptor = encryptor;
2025-04-22 19:34:46 +08:00
}
2025-05-07 19:39:09 +08:00
public override void Init(ModuleDef mod)
{
2025-05-09 20:18:24 +08:00
_module = mod;
2025-05-07 19:39:09 +08:00
}
2025-04-22 22:53:51 +08:00
private (FieldDef, FieldDef) CreateDataHolderRvaField(TypeDef dataHolderType)
2025-04-22 19:34:46 +08:00
{
if (_rvaTypeDef == null)
{
2025-04-22 22:53:51 +08:00
_module.EnableTypeDefFindCache = false;
2025-05-04 19:24:14 +08:00
//_rvaTypeDef = _module.Find("$ObfuzRVA$", true);
2025-04-22 19:34:46 +08:00
//if (_rvaTypeDef != null)
//{
// throw new Exception($"can't obfuscate a obfuscated assembly");
//}
2025-04-22 22:53:51 +08:00
ITypeDefOrRef objectTypeRef = _module.Import(typeof(object));
_rvaTypeDef = new TypeDefUser("$Obfuz$RVA$", objectTypeRef);
_module.Types.Add(_rvaTypeDef);
_module.EnableTypeDefFindCache = true;
2025-04-22 19:34:46 +08:00
}
2025-04-22 22:53:51 +08:00
var holderField = new FieldDefUser($"$RVA_Data{_rvaTypeDef.Fields.Count}", new FieldSig(dataHolderType.ToTypeSig()), FieldAttributes.InitOnly | FieldAttributes.Static | FieldAttributes.HasFieldRVA);
2025-04-22 19:34:46 +08:00
holderField.DeclaringType = _rvaTypeDef;
2025-04-22 22:53:51 +08:00
var runtimeValueField = new FieldDefUser($"$RVA_Value{_rvaTypeDef.Fields.Count}", new FieldSig(new SZArraySig(_module.CorLibTypes.Byte)), FieldAttributes.Static);
2025-04-22 19:34:46 +08:00
runtimeValueField.DeclaringType = _rvaTypeDef;
return (holderField, runtimeValueField);
}
2025-04-22 22:53:51 +08:00
private TypeDef GetDataHolderType(int size)
2025-04-22 19:34:46 +08:00
{
size = (size + 15) & ~15; // align to 6 bytes
2025-04-22 22:53:51 +08:00
if (_dataHolderTypeBySizes.TryGetValue(size, out var type))
2025-04-22 19:34:46 +08:00
return type;
2025-04-22 22:53:51 +08:00
var dataHolderType = new TypeDefUser($"$ObfuzRVA$DataHolder{size}", _module.Import(typeof(ValueType)));
2025-04-22 19:34:46 +08:00
dataHolderType.Layout = TypeAttributes.ExplicitLayout;
dataHolderType.PackingSize = 1;
dataHolderType.ClassSize = (uint)size;
2025-04-22 22:53:51 +08:00
_dataHolderTypeBySizes.Add(size, dataHolderType);
_module.Types.Add(dataHolderType);
2025-04-22 19:34:46 +08:00
return dataHolderType;
}
private static int AlignTo(int size, int alignment)
{
return (size + alignment - 1) & ~(alignment - 1);
}
2025-04-22 22:53:51 +08:00
private RvaField CreateRvaField(int size)
2025-04-22 19:34:46 +08:00
{
2025-04-22 22:53:51 +08:00
TypeDef dataHolderType = GetDataHolderType(size);
var (holderDataField, runtimeValueField) = CreateDataHolderRvaField(dataHolderType);
2025-04-22 19:34:46 +08:00
var newRvaField = new RvaField
{
holderDataField = holderDataField,
runtimeValueField = runtimeValueField,
size = dataHolderType.ClassSize,
bytes = new List<byte>((int)dataHolderType.ClassSize),
2025-05-11 10:37:42 +08:00
encryptionOps = _random.NextInt(),
salt = _random.NextInt(),
2025-04-22 19:34:46 +08:00
};
_rvaFields.Add(newRvaField);
return newRvaField;
}
2025-04-22 22:53:51 +08:00
private RvaField GetRvaField(int preservedSize, int alignment)
2025-04-22 19:34:46 +08:00
{
Assert.IsTrue(preservedSize % alignment == 0);
// for big size, create a new field
if (preservedSize >= maxRvaDataSize)
{
2025-04-22 22:53:51 +08:00
return CreateRvaField(preservedSize);
2025-04-22 19:34:46 +08:00
}
if (_currentField != null)
{
int offset = AlignTo(_currentField.bytes.Count, alignment);
int expectedSize = offset + preservedSize;
if (expectedSize <= _currentField.size)
{
_currentField.FillPaddingToSize(offset);
2025-04-22 19:34:46 +08:00
return _currentField;
}
_currentField.FillPaddingToEnd();
2025-04-22 19:34:46 +08:00
}
2025-04-22 22:53:51 +08:00
_currentField = CreateRvaField(maxRvaDataSize);
2025-04-22 19:34:46 +08:00
return _currentField;
}
2025-04-22 22:53:51 +08:00
public RvaData Allocate(int value)
2025-04-22 19:34:46 +08:00
{
2025-04-22 22:53:51 +08:00
RvaField field = GetRvaField(4, 4);
2025-04-22 19:34:46 +08:00
int offset = field.bytes.Count;
Assert.IsTrue(offset % 4 == 0);
field.bytes.AddRange(BitConverter.GetBytes(value));
return new RvaData(field.runtimeValueField, offset, 4);
}
2025-04-22 22:53:51 +08:00
public RvaData Allocate(long value)
2025-04-22 19:34:46 +08:00
{
2025-04-22 22:53:51 +08:00
RvaField field = GetRvaField(8, 8);
2025-04-22 19:34:46 +08:00
int offset = field.bytes.Count;
Assert.IsTrue(offset % 8 == 0);
field.bytes.AddRange(BitConverter.GetBytes(value));
return new RvaData(field.runtimeValueField, offset, 8);
}
2025-04-22 22:53:51 +08:00
public RvaData Allocate(float value)
2025-04-22 19:34:46 +08:00
{
2025-04-22 22:53:51 +08:00
RvaField field = GetRvaField(4, 4);
2025-04-22 19:34:46 +08:00
int offset = field.bytes.Count;
Assert.IsTrue(offset % 4 == 0);
field.bytes.AddRange(BitConverter.GetBytes(value));
return new RvaData(field.runtimeValueField, offset, 4);
}
2025-04-22 22:53:51 +08:00
public RvaData Allocate(double value)
2025-04-22 19:34:46 +08:00
{
2025-04-22 22:53:51 +08:00
RvaField field = GetRvaField(8, 8);
2025-04-22 19:34:46 +08:00
int offset = field.bytes.Count;
Assert.IsTrue(offset % 8 == 0);
field.bytes.AddRange(BitConverter.GetBytes(value));
return new RvaData(field.runtimeValueField, offset, 8);
}
2025-04-22 22:53:51 +08:00
public RvaData Allocate(string value)
2025-04-22 19:34:46 +08:00
{
byte[] bytes = Encoding.UTF8.GetBytes(value);
2025-04-22 22:53:51 +08:00
return Allocate(bytes);
2025-04-22 19:34:46 +08:00
}
2025-04-22 22:53:51 +08:00
public RvaData Allocate(byte[] value)
2025-04-22 19:34:46 +08:00
{
2025-04-22 22:53:51 +08:00
RvaField field = GetRvaField(value.Length, 1);
2025-04-22 19:34:46 +08:00
int offset = field.bytes.Count;
field.bytes.AddRange(value);
return new RvaData(field.runtimeValueField, offset, value.Length);
}
2025-04-22 22:53:51 +08:00
private void CreateCCtorOfRvaTypeDef()
{
if (_rvaTypeDef == null)
{
return;
}
ModuleDef mod = _rvaTypeDef.Module;
var cctorMethod = new MethodDefUser(".cctor",
2025-04-22 22:53:51 +08:00
MethodSig.CreateStatic(_module.CorLibTypes.Void),
MethodImplAttributes.IL | MethodImplAttributes.Managed,
MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Private);
cctorMethod.DeclaringType = _rvaTypeDef;
2025-04-22 22:53:51 +08:00
//_rvaTypeDef.Methods.Add(cctor);
var body = new CilBody();
cctorMethod.Body = body;
2025-04-22 22:53:51 +08:00
var ins = body.Instructions;
2025-05-11 08:53:48 +08:00
DefaultMetadataImporter importer = GroupByModuleEntityManager.Ins.GetDefaultModuleMetadataImporter(mod);
2025-04-22 22:53:51 +08:00
foreach (var field in _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));
2025-04-23 18:43:14 +08:00
ins.Add(Instruction.Create(OpCodes.Dup));
2025-04-22 22:53:51 +08:00
ins.Add(Instruction.Create(OpCodes.Stsfld, field.runtimeValueField));
ins.Add(Instruction.Create(OpCodes.Ldtoken, field.holderDataField));
2025-05-07 19:39:09 +08:00
ins.Add(Instruction.Create(OpCodes.Call, importer.InitializedArrayMethod));
// EncryptionService.DecryptBlock(array, field.encryptionOps, field.salt);
2025-05-11 10:37:42 +08:00
ins.Add(Instruction.CreateLdcI4(field.encryptionOps));
ins.Add(Instruction.Create(OpCodes.Ldc_I4, field.salt));
2025-05-07 19:39:09 +08:00
ins.Add(Instruction.Create(OpCodes.Call, importer.DecryptBlock));
2025-04-22 22:53:51 +08:00
}
ins.Add(Instruction.Create(OpCodes.Ret));
}
private void SetFieldsRVA()
2025-04-22 19:34:46 +08:00
{
foreach (var field in _rvaFields)
{
Assert.IsTrue(field.bytes.Count <= field.size);
if (field.bytes.Count < field.size)
{
field.FillPaddingToEnd();
2025-04-22 19:34:46 +08:00
}
2025-04-23 18:58:44 +08:00
byte[] data = field.bytes.ToArray();
_encryptor.EncryptBlock(data, field.encryptionOps, field.salt);
2025-04-23 18:58:44 +08:00
field.holderDataField.InitialValue = data;
2025-04-22 19:34:46 +08:00
}
}
2025-04-22 22:53:51 +08:00
public void Done()
{
SetFieldsRVA();
CreateCCtorOfRvaTypeDef();
}
}
public class RvaDataAllocator
{
private readonly IRandom _random;
2025-04-23 18:58:44 +08:00
private readonly IEncryptor _encryptor;
2025-04-22 22:53:51 +08:00
2025-04-23 18:58:44 +08:00
public RvaDataAllocator(IRandom random, IEncryptor encryptor)
2025-04-22 22:53:51 +08:00
{
_random = random;
2025-04-23 18:58:44 +08:00
_encryptor = encryptor;
2025-04-22 22:53:51 +08:00
}
private ModuleRvaDataAllocator GetModuleRvaDataAllocator(ModuleDef mod)
{
2025-05-11 08:53:48 +08:00
return GroupByModuleEntityManager.Ins.GetEntity<ModuleRvaDataAllocator>(mod, () => new ModuleRvaDataAllocator(_random, _encryptor));
2025-04-22 22:53:51 +08:00
}
public RvaData Allocate(ModuleDef mod, int value)
{
return GetModuleRvaDataAllocator(mod).Allocate(value);
}
public RvaData Allocate(ModuleDef mod, long value)
{
return GetModuleRvaDataAllocator(mod).Allocate(value);
}
public RvaData Allocate(ModuleDef mod, float value)
{
return GetModuleRvaDataAllocator(mod).Allocate(value);
}
public RvaData Allocate(ModuleDef mod, double value)
{
return GetModuleRvaDataAllocator(mod).Allocate(value);
}
public RvaData Allocate(ModuleDef mod, string value)
{
return GetModuleRvaDataAllocator(mod).Allocate(value);
}
public RvaData Allocate(ModuleDef mod, byte[] value)
{
return GetModuleRvaDataAllocator(mod).Allocate(value);
}
public void Done()
{
2025-05-11 08:53:48 +08:00
foreach (var allocator in GroupByModuleEntityManager.Ins.GetEntities<ModuleRvaDataAllocator>())
2025-04-22 22:53:51 +08:00
{
allocator.Done();
}
}
2025-04-22 19:34:46 +08:00
}
}