支持从rva data中读取常量数据
parent
b19959488f
commit
f1b3bd3329
|
@ -0,0 +1,217 @@
|
|||
using dnlib.DotNet;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace Obfuz.Emit
|
||||
{
|
||||
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
|
||||
{
|
||||
// randomized
|
||||
const int maxRvaDataSize = 0x100;
|
||||
|
||||
private readonly IRandom _random;
|
||||
|
||||
class RvaField
|
||||
{
|
||||
public FieldDef holderDataField;
|
||||
public FieldDef runtimeValueField;
|
||||
public uint size;
|
||||
public List<byte> bytes;
|
||||
|
||||
public void FillPadding()
|
||||
{
|
||||
// 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;
|
||||
|
||||
private readonly Dictionary<(ModuleDef, int), TypeDef> _dataHolderTypeBySizes = new Dictionary<(ModuleDef, int), TypeDef>();
|
||||
|
||||
public RvaDataAllocator(IRandom random)
|
||||
{
|
||||
_random = random;
|
||||
}
|
||||
|
||||
private (FieldDef, FieldDef) CreateDataHolderRvaField(ModuleDef mod, TypeDef dataHolderType)
|
||||
{
|
||||
if (_rvaTypeDef == null)
|
||||
{
|
||||
mod.EnableTypeDefFindCache = false;
|
||||
//_rvaTypeDef = mod.Find("$ObfuzRVA$", false);
|
||||
//if (_rvaTypeDef != null)
|
||||
//{
|
||||
// throw new Exception($"can't obfuscate a obfuscated assembly");
|
||||
//}
|
||||
ITypeDefOrRef objectTypeRef = mod.Import(typeof(object));
|
||||
_rvaTypeDef = new TypeDefUser("$Obfuz$RVA$",objectTypeRef);
|
||||
mod.Types.Add(_rvaTypeDef);
|
||||
mod.EnableTypeDefFindCache = true;
|
||||
}
|
||||
|
||||
|
||||
var holderField = new FieldDefUser($"$RVA_Data{_rvaTypeDef.Fields.Count}", new FieldSig(dataHolderType.ToTypeSig()), FieldAttributes.InitOnly | FieldAttributes.Static | FieldAttributes.HasFieldRVA);
|
||||
holderField.DeclaringType = _rvaTypeDef;
|
||||
|
||||
var runtimeValueField = new FieldDefUser($"$RVA_Value{_rvaTypeDef.Fields.Count}", new FieldSig(new SZArraySig(mod.CorLibTypes.Byte)), FieldAttributes.Static);
|
||||
runtimeValueField.DeclaringType = _rvaTypeDef;
|
||||
return (holderField, runtimeValueField);
|
||||
}
|
||||
|
||||
private TypeDef GetDataHolderType(ModuleDef mod, int size)
|
||||
{
|
||||
size = (size + 15) & ~15; // align to 6 bytes
|
||||
if (_dataHolderTypeBySizes.TryGetValue((mod, size), out var type))
|
||||
return type;
|
||||
var dataHolderType = new TypeDefUser($"$ObfuzRVA$DataHolder{size}", mod.Import(typeof(ValueType)));
|
||||
dataHolderType.Layout = TypeAttributes.ExplicitLayout;
|
||||
dataHolderType.PackingSize = 1;
|
||||
dataHolderType.ClassSize = (uint)size;
|
||||
_dataHolderTypeBySizes.Add((mod, size), dataHolderType);
|
||||
mod.Types.Add(dataHolderType);
|
||||
return dataHolderType;
|
||||
}
|
||||
|
||||
private static int AlignTo(int size, int alignment)
|
||||
{
|
||||
return (size + alignment - 1) & ~(alignment - 1);
|
||||
}
|
||||
|
||||
private RvaField CreateRvaField(ModuleDef mod, int size)
|
||||
{
|
||||
TypeDef dataHolderType = GetDataHolderType(mod, size);
|
||||
var (holderDataField, runtimeValueField) = CreateDataHolderRvaField(mod, dataHolderType);
|
||||
var newRvaField = new RvaField
|
||||
{
|
||||
holderDataField = holderDataField,
|
||||
runtimeValueField = runtimeValueField,
|
||||
size = dataHolderType.ClassSize,
|
||||
bytes = new List<byte>((int)dataHolderType.ClassSize),
|
||||
};
|
||||
_rvaFields.Add(newRvaField);
|
||||
return newRvaField;
|
||||
}
|
||||
|
||||
private RvaField GetRvaField(ModuleDef mod, int preservedSize, int alignment)
|
||||
{
|
||||
Assert.IsTrue(preservedSize % alignment == 0);
|
||||
// for big size, create a new field
|
||||
if (preservedSize >= maxRvaDataSize)
|
||||
{
|
||||
return CreateRvaField(mod, preservedSize);
|
||||
}
|
||||
|
||||
if (_currentField != null)
|
||||
{
|
||||
int offset = AlignTo(_currentField.bytes.Count, alignment);
|
||||
|
||||
int expectedSize = offset + preservedSize;
|
||||
if (expectedSize <= _currentField.size)
|
||||
{
|
||||
// insert random padding
|
||||
for (int i = _currentField.bytes.Count; i < offset; i++)
|
||||
{
|
||||
//_currentField.bytes.Add((byte)_random.NextInt(0, 256));
|
||||
// TODO replace with random value
|
||||
_currentField.bytes.Add(0xAB);
|
||||
}
|
||||
return _currentField;
|
||||
}
|
||||
|
||||
_currentField.FillPadding();
|
||||
}
|
||||
_currentField = CreateRvaField(mod, maxRvaDataSize);
|
||||
return _currentField;
|
||||
}
|
||||
|
||||
public RvaData Allocate(ModuleDef mod, int value)
|
||||
{
|
||||
RvaField field = GetRvaField(mod, 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(ModuleDef mod, long value)
|
||||
{
|
||||
RvaField field = GetRvaField(mod, 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(ModuleDef mod, float value)
|
||||
{
|
||||
RvaField field = GetRvaField(mod, 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(ModuleDef mod, double value)
|
||||
{
|
||||
RvaField field = GetRvaField(mod, 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(ModuleDef mod, string value)
|
||||
{
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(value);
|
||||
return Allocate(mod, bytes);
|
||||
}
|
||||
|
||||
public RvaData Allocate(ModuleDef mod, byte[] value)
|
||||
{
|
||||
RvaField field = GetRvaField(mod, value.Length, 1);
|
||||
int offset = field.bytes.Count;
|
||||
field.bytes.AddRange(value);
|
||||
return new RvaData(field.runtimeValueField, offset, value.Length);
|
||||
}
|
||||
|
||||
public void SetFieldsRVA()
|
||||
{
|
||||
foreach (var field in _rvaFields)
|
||||
{
|
||||
Assert.IsTrue(field.bytes.Count <= field.size);
|
||||
if (field.bytes.Count < field.size)
|
||||
{
|
||||
field.FillPadding();
|
||||
}
|
||||
field.holderDataField.InitialValue = field.bytes.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
{
|
||||
"name": "Obfuz",
|
||||
"rootNamespace": "",
|
||||
"references": [],
|
||||
"references": [
|
||||
"GUID:7e4de3067c2ab5c43a03ac49273dfb68"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using NUnit.Framework;
|
||||
using Obfuz.Emit;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obfuz.Virtualization
|
||||
|
@ -9,5 +10,6 @@ namespace Obfuz.Virtualization
|
|||
{
|
||||
public MethodDef method;
|
||||
public List<Instruction> output;
|
||||
public RvaDataAllocator rvaDataAllocator;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Obfuz.Utils;
|
||||
using Obfuz.Emit;
|
||||
using Obfuz.Utils;
|
||||
|
||||
namespace Obfuz.Virtualization
|
||||
{
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Emit;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static UnityEngine.Networking.UnityWebRequest;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Obfuz.Virtualization
|
||||
{
|
||||
[NodeOutput(DataNodeType.Int32)]
|
||||
[NodeOutput(DataNodeType.Int64)]
|
||||
[NodeOutput(DataNodeType.Float32)]
|
||||
[NodeOutput(DataNodeType.Float64)]
|
||||
[NodeOutput(DataNodeType.Bytes)]
|
||||
[NodeOutput(DataNodeType.String)]
|
||||
public class ConstFromFieldRvaDataNode : DataNodeAny
|
||||
{
|
||||
|
||||
private RvaData AllocateRvaData(CompileContext ctx)
|
||||
{
|
||||
ModuleDef mod = ctx.method.Module;
|
||||
RvaDataAllocator allocator = ctx.rvaDataAllocator;
|
||||
switch (Type)
|
||||
{
|
||||
case DataNodeType.Int32: return allocator.Allocate(mod, IntValue);
|
||||
case DataNodeType.Int64: return allocator.Allocate(mod, LongValue);
|
||||
case DataNodeType.Float32: return allocator.Allocate(mod, FloatValue);
|
||||
case DataNodeType.Float64: return allocator.Allocate(mod, DoubleValue);
|
||||
case DataNodeType.Bytes: return allocator.Allocate(mod, BytesValue);
|
||||
case DataNodeType.String: return allocator.Allocate(mod, StringValue);
|
||||
default:
|
||||
throw new NotSupportedException($"Unsupported type: {Type}.");
|
||||
}
|
||||
}
|
||||
|
||||
private static IMethod s_convertInt;
|
||||
private static IMethod s_convertLong;
|
||||
private static IMethod s_convertFloat;
|
||||
private static IMethod s_convertDouble;
|
||||
|
||||
private static IMethod s_convertString;
|
||||
private static IField s_Encoding_Utf8;
|
||||
private static IMethod s_convertBytes;
|
||||
|
||||
private void InitImportMetadatas(ModuleDef mod)
|
||||
{
|
||||
if (s_convertInt != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
s_convertInt = mod.Import(typeof(BitConverter).GetMethod("ToInt32", new[] { typeof(byte[]), typeof(int) }));
|
||||
s_convertLong = mod.Import(typeof(BitConverter).GetMethod("ToInt64", new[] { typeof(byte[]), typeof(int) }));
|
||||
s_convertFloat = mod.Import(typeof(BitConverter).GetMethod("ToSingle", new[] { typeof(byte[]), typeof(int) }));
|
||||
s_convertDouble = mod.Import(typeof(BitConverter).GetMethod("ToDouble", new[] { typeof(byte[]), typeof(int) }));
|
||||
s_convertString = mod.Import(typeof(Encoding).GetMethod("GetString", new[] { typeof(byte[]), typeof(int), typeof(int) }));
|
||||
s_Encoding_Utf8 = mod.Import(typeof(Encoding).GetField("UTF8"));
|
||||
s_convertBytes = mod.Import(typeof(Array).GetMethod("Copy", new[] { typeof(Array), typeof(int), typeof(Array), typeof(int), typeof(int) }));
|
||||
}
|
||||
|
||||
IMethod GetConvertMethod(ModuleDef mod)
|
||||
{
|
||||
InitImportMetadatas(mod);
|
||||
switch (Type)
|
||||
{
|
||||
case DataNodeType.Int32: return s_convertInt;
|
||||
case DataNodeType.Int64: return s_convertLong;
|
||||
case DataNodeType.Float32: return s_convertFloat;
|
||||
case DataNodeType.Float64: return s_convertDouble;
|
||||
case DataNodeType.String: return s_convertString;
|
||||
case DataNodeType.Bytes: return s_convertBytes;
|
||||
default: throw new NotSupportedException($"Unsupported type: {Type}.");
|
||||
}
|
||||
}
|
||||
|
||||
public override void Compile(CompileContext ctx)
|
||||
{
|
||||
// only support Int32, int64, bytes.
|
||||
// string can only create from StringFromBytesNode
|
||||
// x = memcpy array.GetRange(index, length);
|
||||
var output = ctx.output;
|
||||
RvaData rvaData = AllocateRvaData(ctx);
|
||||
ModuleDef mod = ctx.method.Module;
|
||||
IMethod convertMethod = GetConvertMethod(mod);
|
||||
switch (Type)
|
||||
{
|
||||
case DataNodeType.Int32:
|
||||
case DataNodeType.Int64:
|
||||
case DataNodeType.Float32:
|
||||
case DataNodeType.Float64:
|
||||
{
|
||||
ctx.output.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
|
||||
output.Add(Instruction.Create(OpCodes.Ldc_I4, rvaData.offset));
|
||||
output.Add(Instruction.Create(OpCodes.Call, convertMethod));
|
||||
break;
|
||||
}
|
||||
case DataNodeType.String:
|
||||
{
|
||||
|
||||
// Encoding.UTF8.GetString(data, offset, length);
|
||||
ctx.output.Add(Instruction.Create(OpCodes.Ldsfld, s_Encoding_Utf8));
|
||||
ctx.output.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
|
||||
output.Add(Instruction.Create(OpCodes.Ldc_I4, rvaData.offset));
|
||||
output.Add(Instruction.Create(OpCodes.Ldc_I4, rvaData.size));
|
||||
output.Add(Instruction.Create(OpCodes.Call, convertMethod));
|
||||
break;
|
||||
}
|
||||
case DataNodeType.Bytes:
|
||||
{
|
||||
// byte[] result = new byte[length];
|
||||
// Array.Copy(data, offset, result, 0, length);
|
||||
ctx.output.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
|
||||
output.Add(Instruction.Create(OpCodes.Ldc_I4, rvaData.offset));
|
||||
output.Add(Instruction.Create(OpCodes.Ldc_I4, rvaData.size));
|
||||
output.Add(Instruction.Create(OpCodes.Newarr, new SZArraySig(mod.CorLibTypes.Byte).ToTypeDefOrRef()));
|
||||
output.Add(Instruction.Create(OpCodes.Ldc_I4, 0));
|
||||
output.Add(Instruction.Create(OpCodes.Ldc_I4, rvaData.size));
|
||||
output.Add(Instruction.Create(OpCodes.Call, convertMethod));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new NotSupportedException($"Unsupported type: {Type}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
namespace Obfuz.Virtualization
|
||||
using dnlib.DotNet;
|
||||
|
||||
namespace Obfuz.Virtualization
|
||||
{
|
||||
public abstract class DataNodeBase : IDataNode
|
||||
{
|
||||
|
@ -18,6 +20,11 @@
|
|||
|
||||
public byte[] BytesValue => (byte[])Value;
|
||||
|
||||
public virtual void Init(CreateExpressionOptions options)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public abstract void Compile(CompileContext ctx);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,18 +23,24 @@ namespace Obfuz.Virtualization
|
|||
|
||||
public override void Stop(ObfuscatorContext ctx)
|
||||
{
|
||||
|
||||
_dataObfuscator.Stop();
|
||||
}
|
||||
|
||||
public override void Process(ObfuscatorContext ctx)
|
||||
{
|
||||
foreach (var ass in ctx.assemblies)
|
||||
{
|
||||
foreach (TypeDef type in ass.module.GetTypes())
|
||||
// ToArray to avoid modify list exception
|
||||
foreach (TypeDef type in ass.module.GetTypes().ToArray())
|
||||
{
|
||||
foreach (MethodDef method in type.Methods)
|
||||
if (type.Name.StartsWith("$Obfuz$"))
|
||||
{
|
||||
if (!method.HasBody || !_dataObfuscatorPolicy.NeedObfuscateMethod(method))
|
||||
continue;
|
||||
}
|
||||
// ToArray to avoid modify list exception
|
||||
foreach (MethodDef method in type.Methods.ToArray())
|
||||
{
|
||||
if (!method.HasBody || method.Name.StartsWith("$Obfuz$") || !_dataObfuscatorPolicy.NeedObfuscateMethod(method))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Obfuz.Emit;
|
||||
using Obfuz.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
@ -7,7 +9,16 @@ namespace Obfuz.Virtualization
|
|||
{
|
||||
public class DefaultDataObfuscator : IDataObfuscator
|
||||
{
|
||||
private readonly RandomDataNodeCreator _nodeCreator = new RandomDataNodeCreator();
|
||||
private readonly IRandom _random;
|
||||
private readonly RandomDataNodeCreator _nodeCreator;
|
||||
private readonly RvaDataAllocator _rvaDataAllocator;
|
||||
|
||||
public DefaultDataObfuscator()
|
||||
{
|
||||
_random = new RandomWithKey(new byte[] { 0x1, 0x2, 0x3, 0x4 }, 0x5);
|
||||
_nodeCreator = new RandomDataNodeCreator(_random);
|
||||
_rvaDataAllocator = new RvaDataAllocator(_random);
|
||||
}
|
||||
|
||||
public void ObfuscateInt(MethodDef method, int value, List<Instruction> obfuscatedInstructions)
|
||||
{
|
||||
|
@ -16,6 +27,7 @@ namespace Obfuz.Virtualization
|
|||
{
|
||||
method = method,
|
||||
output = obfuscatedInstructions,
|
||||
rvaDataAllocator = _rvaDataAllocator,
|
||||
};
|
||||
node.Compile(ctx);
|
||||
//obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldc_I4, value));
|
||||
|
@ -46,5 +58,10 @@ namespace Obfuz.Virtualization
|
|||
{
|
||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldstr, value));
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_rvaDataAllocator.SetFieldsRVA();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ namespace Obfuz.Virtualization
|
|||
|
||||
object Value { get; }
|
||||
|
||||
void Init(CreateExpressionOptions options);
|
||||
|
||||
void Compile(CompileContext ctx);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,5 +19,6 @@ namespace Obfuz.Virtualization
|
|||
void ObfuscateString(MethodDef method, string value, List<Instruction> obfuscatedInstructions);
|
||||
|
||||
void ObfuscateBytes(MethodDef method, Array value, List<Instruction> obfuscatedInstructions);
|
||||
void Stop();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,10 +9,11 @@ namespace Obfuz.Virtualization
|
|||
{
|
||||
private readonly Dictionary<DataNodeType, List<IFunction>> _functions = new Dictionary<DataNodeType, List<IFunction>>();
|
||||
|
||||
private readonly IRandom _random = new RandomWithKey(new byte[] { 0x1, 0x2, 0x3, 0x4 }, 0x5);
|
||||
private readonly IRandom _random;
|
||||
|
||||
public RandomDataNodeCreator()
|
||||
public RandomDataNodeCreator(IRandom random)
|
||||
{
|
||||
_random = random;
|
||||
var int32Funcs = new List<IFunction>()
|
||||
{
|
||||
new Int32FunctionAdd(),
|
||||
|
@ -29,7 +30,8 @@ namespace Obfuz.Virtualization
|
|||
}
|
||||
if (options.depth >= 2)
|
||||
{
|
||||
return new ConstDataNode() { Type = type, Value = value };
|
||||
//return new ConstDataNode() { Type = type, Value = value };
|
||||
return new ConstFromFieldRvaDataNode() { Type = type, Value = value };
|
||||
}
|
||||
var func = funcs[options.random.NextInt(funcs.Count)];
|
||||
++options.depth;
|
||||
|
|
Loading…
Reference in New Issue