修复$$Obfuz$RVA$的所有rva字段累计长度超出16k引发的在hybridclr下运行出错的问题。此问题是hybridclr的bug,因为ldslfd之类指令要求offset小于16k。
parent
d4133f1e8a
commit
0985b3d06e
|
@ -4,6 +4,7 @@ using Obfuz.Emit;
|
||||||
using Obfuz.Utils;
|
using Obfuz.Utils;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using UnityEngine.Assertions;
|
using UnityEngine.Assertions;
|
||||||
|
@ -26,8 +27,10 @@ namespace Obfuz.Data
|
||||||
|
|
||||||
public class RvaDataAllocator : GroupByModuleEntityBase
|
public class RvaDataAllocator : GroupByModuleEntityBase
|
||||||
{
|
{
|
||||||
// randomized
|
const int maxRvaDataSize = 2 * 1024;
|
||||||
const int maxRvaDataSize = 0x1000;
|
|
||||||
|
// 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;
|
private IRandom _random;
|
||||||
|
|
||||||
|
@ -58,11 +61,23 @@ namespace Obfuz.Data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<RvaField> _rvaFields = new List<RvaField>();
|
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 RvaField _currentField;
|
||||||
|
|
||||||
|
private RvaTypeDefInfo _currentRvaType;
|
||||||
private TypeDef _rvaTypeDef;
|
private readonly List<RvaTypeDefInfo> _rvaTypeDefs = new List<RvaTypeDefInfo>();
|
||||||
|
|
||||||
private readonly Dictionary<int, TypeDef> _dataHolderTypeBySizes = new Dictionary<int, TypeDef>();
|
private readonly Dictionary<int, TypeDef> _dataHolderTypeBySizes = new Dictionary<int, TypeDef>();
|
||||||
private bool _done;
|
private bool _done;
|
||||||
|
@ -78,27 +93,28 @@ namespace Obfuz.Data
|
||||||
|
|
||||||
private (FieldDef, FieldDef) CreateDataHolderRvaField(TypeDef dataHolderType)
|
private (FieldDef, FieldDef) CreateDataHolderRvaField(TypeDef dataHolderType)
|
||||||
{
|
{
|
||||||
if (_rvaTypeDef == null)
|
if (_currentRvaType == null || _currentRvaType.rvaFields.Count >= maxTotalRvaDataFieldSizeInHybridCLR / maxRvaDataSize - 1)
|
||||||
{
|
{
|
||||||
using (var scope = new DisableTypeDefFindCacheScope(Module))
|
using (var scope = new DisableTypeDefFindCacheScope(Module))
|
||||||
{
|
{
|
||||||
_rvaTypeDef = new TypeDefUser("$Obfuz$RVA$", Module.CorLibTypes.Object.ToTypeDefOrRef());
|
var rvaTypeDef = new TypeDefUser($"$Obfuz$RVA${_rvaTypeDefs.Count}", Module.CorLibTypes.Object.ToTypeDefOrRef());
|
||||||
Module.Types.Add(_rvaTypeDef);
|
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 holderField = new FieldDefUser($"$RVA_Data{_rvaFields.Count}", new FieldSig(dataHolderType.ToTypeSig()), FieldAttributes.InitOnly | FieldAttributes.Static | FieldAttributes.HasFieldRVA);
|
var runtimeValueField = new FieldDefUser($"$RVA_Value{_currentRvaType.rvaFields.Count}", new FieldSig(new SZArraySig(Module.CorLibTypes.Byte)), FieldAttributes.Static | FieldAttributes.Public);
|
||||||
holderField.DeclaringType = _rvaTypeDef;
|
runtimeValueField.DeclaringType = _currentRvaType.typeDef;
|
||||||
|
|
||||||
var runtimeValueField = new FieldDefUser($"$RVA_Value{_rvaFields.Count}", new FieldSig(new SZArraySig(Module.CorLibTypes.Byte)), FieldAttributes.Static | FieldAttributes.Public);
|
|
||||||
runtimeValueField.DeclaringType = _rvaTypeDef;
|
|
||||||
return (holderField, runtimeValueField);
|
return (holderField, runtimeValueField);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TypeDef GetDataHolderType(int size)
|
private TypeDef GetDataHolderType(int size)
|
||||||
{
|
{
|
||||||
size = (size + 15) & ~15; // align to 6 bytes
|
size = (size + 15) & ~15; // align to 16 bytes
|
||||||
if (_dataHolderTypeBySizes.TryGetValue(size, out var type))
|
if (_dataHolderTypeBySizes.TryGetValue(size, out var type))
|
||||||
return type;
|
return type;
|
||||||
|
|
||||||
|
@ -133,7 +149,7 @@ namespace Obfuz.Data
|
||||||
encryptionOps = _random.NextInt(),
|
encryptionOps = _random.NextInt(),
|
||||||
salt = _random.NextInt(),
|
salt = _random.NextInt(),
|
||||||
};
|
};
|
||||||
_rvaFields.Add(newRvaField);
|
_currentRvaType.rvaFields.Add(newRvaField);
|
||||||
return newRvaField;
|
return newRvaField;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,16 +254,14 @@ namespace Obfuz.Data
|
||||||
|
|
||||||
private void CreateCCtorOfRvaTypeDef()
|
private void CreateCCtorOfRvaTypeDef()
|
||||||
{
|
{
|
||||||
if (_rvaTypeDef == null)
|
foreach (RvaTypeDefInfo rvaTypeDef in _rvaTypeDefs)
|
||||||
{
|
{
|
||||||
return;
|
ModuleDef mod = rvaTypeDef.typeDef.Module;
|
||||||
}
|
|
||||||
ModuleDef mod = _rvaTypeDef.Module;
|
|
||||||
var cctorMethod = new MethodDefUser(".cctor",
|
var cctorMethod = new MethodDefUser(".cctor",
|
||||||
MethodSig.CreateStatic(Module.CorLibTypes.Void),
|
MethodSig.CreateStatic(Module.CorLibTypes.Void),
|
||||||
MethodImplAttributes.IL | MethodImplAttributes.Managed,
|
MethodImplAttributes.IL | MethodImplAttributes.Managed,
|
||||||
MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Private);
|
MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Private);
|
||||||
cctorMethod.DeclaringType = _rvaTypeDef;
|
cctorMethod.DeclaringType = rvaTypeDef.typeDef;
|
||||||
//_rvaTypeDef.Methods.Add(cctor);
|
//_rvaTypeDef.Methods.Add(cctor);
|
||||||
var body = new CilBody();
|
var body = new CilBody();
|
||||||
cctorMethod.Body = body;
|
cctorMethod.Body = body;
|
||||||
|
@ -255,7 +269,7 @@ namespace Obfuz.Data
|
||||||
|
|
||||||
DefaultMetadataImporter importer = this.GetDefaultModuleMetadataImporter();
|
DefaultMetadataImporter importer = this.GetDefaultModuleMetadataImporter();
|
||||||
AddVerifyCodes(ins, importer);
|
AddVerifyCodes(ins, importer);
|
||||||
foreach (var field in _rvaFields)
|
foreach (var field in rvaTypeDef.rvaFields)
|
||||||
{
|
{
|
||||||
// ldc
|
// ldc
|
||||||
// newarr
|
// newarr
|
||||||
|
@ -279,10 +293,11 @@ namespace Obfuz.Data
|
||||||
}
|
}
|
||||||
ins.Add(Instruction.Create(OpCodes.Ret));
|
ins.Add(Instruction.Create(OpCodes.Ret));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void SetFieldsRVA()
|
private void SetFieldsRVA()
|
||||||
{
|
{
|
||||||
foreach (var field in _rvaFields)
|
foreach (var field in _rvaTypeDefs.SelectMany(t => t.rvaFields))
|
||||||
{
|
{
|
||||||
Assert.IsTrue(field.bytes.Count <= field.size);
|
Assert.IsTrue(field.bytes.Count <= field.size);
|
||||||
if (field.bytes.Count < field.size)
|
if (field.bytes.Count < field.size)
|
||||||
|
|
|
@ -281,7 +281,7 @@ namespace Obfuz.ObfusPasses.ConstEncrypt
|
||||||
fieldDef.InitialValue = encryptedBytes;
|
fieldDef.InitialValue = encryptedBytes;
|
||||||
byte[] decryptedBytes = (byte[])encryptedBytes.Clone();
|
byte[] decryptedBytes = (byte[])encryptedBytes.Clone();
|
||||||
encryptionScope.encryptor.DecryptBlock(decryptedBytes, ops, salt);
|
encryptionScope.encryptor.DecryptBlock(decryptedBytes, ops, salt);
|
||||||
Assert.AreEqual(originalBytes, decryptedBytes, "Decrypted bytes should match the original bytes after encryption and decryption.");
|
AssertUtil.AreArrayEqual(originalBytes, decryptedBytes, "Decrypted bytes should match the original bytes after encryption and decryption.");
|
||||||
}
|
}
|
||||||
return encryptedRvaData;
|
return encryptedRvaData;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ using System.IO;
|
||||||
|
|
||||||
namespace Obfuz.Utils
|
namespace Obfuz.Utils
|
||||||
{
|
{
|
||||||
|
|
||||||
public class AssemblyCache
|
public class AssemblyCache
|
||||||
{
|
{
|
||||||
private readonly IAssemblyResolver _assemblyPathResolver;
|
private readonly IAssemblyResolver _assemblyPathResolver;
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
using UnityEngine.Assertions;
|
||||||
|
|
||||||
|
namespace Obfuz.Utils
|
||||||
|
{
|
||||||
|
public static class AssertUtil
|
||||||
|
{
|
||||||
|
private static bool IsArrayEqual(byte[] a, byte[] b)
|
||||||
|
{
|
||||||
|
if (a.Length != b.Length)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < a.Length; i++)
|
||||||
|
{
|
||||||
|
if (a[i] != b[i])
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AreArrayEqual(byte[] expected, byte[] actual, string message)
|
||||||
|
{
|
||||||
|
Assert.IsTrue(IsArrayEqual(expected, actual), message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: aba0c82c527b6494ab5c9c67c25656ed
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Loading…
Reference in New Issue