实现 ExprObfus,暂时只支持BasicObfuscator
parent
b84d158fac
commit
8288042e94
|
@ -104,6 +104,7 @@ namespace Obfuz.Emit
|
||||||
{
|
{
|
||||||
splitPoints.Add(nextInst);
|
splitPoints.Add(nextInst);
|
||||||
}
|
}
|
||||||
|
splitPoints.Add((Instruction)curInst.Operand);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FlowControl.Cond_Branch:
|
case FlowControl.Cond_Branch:
|
||||||
|
|
|
@ -147,6 +147,95 @@ namespace Obfuz.Emit
|
||||||
_obfuscationTypeMapperRegisterType = mod.Import(typeof(ObfuscationTypeMapper).GetMethod("RegisterType", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(string) }, null));
|
_obfuscationTypeMapperRegisterType = mod.Import(typeof(ObfuscationTypeMapper).GetMethod("RegisterType", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(string) }, null));
|
||||||
Assert.IsNotNull(_obfuscationTypeMapperRegisterType, "ObfuscationTypeMapper.RegisterType not found");
|
Assert.IsNotNull(_obfuscationTypeMapperRegisterType, "ObfuscationTypeMapper.RegisterType not found");
|
||||||
|
|
||||||
|
var exprUtilityType = typeof(ExprUtility);
|
||||||
|
_addInt = mod.Import(exprUtilityType.GetMethod("Add", new[] { typeof(int), typeof(int) }));
|
||||||
|
Assert.IsNotNull(_addInt, "ExprUtility.Add(int, int) not found");
|
||||||
|
_addLong = mod.Import(exprUtilityType.GetMethod("Add", new[] { typeof(long), typeof(long) }));
|
||||||
|
Assert.IsNotNull(_addLong, "ExprUtility.Add(long, long) not found");
|
||||||
|
_addFloat = mod.Import(exprUtilityType.GetMethod("Add", new[] { typeof(float), typeof(float) }));
|
||||||
|
Assert.IsNotNull(_addFloat, "ExprUtility.Add(float, float) not found");
|
||||||
|
_addDouble = mod.Import(exprUtilityType.GetMethod("Add", new[] { typeof(double), typeof(double) }));
|
||||||
|
Assert.IsNotNull(_addDouble, "ExprUtility.Add(double, double) not found");
|
||||||
|
_subtractInt = mod.Import(exprUtilityType.GetMethod("Subtract", new[] { typeof(int), typeof(int) }));
|
||||||
|
Assert.IsNotNull(_subtractInt, "ExprUtility.Subtract(int, int) not found");
|
||||||
|
_subtractLong = mod.Import(exprUtilityType.GetMethod("Subtract", new[] { typeof(long), typeof(long) }));
|
||||||
|
Assert.IsNotNull(_subtractLong, "ExprUtility.Subtract(long, long) not found");
|
||||||
|
_subtractFloat = mod.Import(exprUtilityType.GetMethod("Subtract", new[] { typeof(float), typeof(float) }));
|
||||||
|
Assert.IsNotNull(_subtractFloat, "ExprUtility.Subtract(float, float) not found");
|
||||||
|
_subtractDouble = mod.Import(exprUtilityType.GetMethod("Subtract", new[] { typeof(double), typeof(double) }));
|
||||||
|
Assert.IsNotNull(_subtractDouble, "ExprUtility.Subtract(double, double) not found");
|
||||||
|
_multiplyInt = mod.Import(exprUtilityType.GetMethod("Multiply", new[] { typeof(int), typeof(int) }));
|
||||||
|
Assert.IsNotNull(_multiplyInt, "ExprUtility.Multiply(int, int) not found");
|
||||||
|
_multiplyLong = mod.Import(exprUtilityType.GetMethod("Multiply", new[] { typeof(long), typeof(long) }));
|
||||||
|
Assert.IsNotNull(_multiplyLong, "ExprUtility.Multiply(long, long) not found");
|
||||||
|
_multiplyFloat = mod.Import(exprUtilityType.GetMethod("Multiply", new[] { typeof(float), typeof(float) }));
|
||||||
|
Assert.IsNotNull(_multiplyFloat, "ExprUtility.Multiply(float, float) not found");
|
||||||
|
_multiplyDouble = mod.Import(exprUtilityType.GetMethod("Multiply", new[] { typeof(double), typeof(double) }));
|
||||||
|
Assert.IsNotNull(_multiplyDouble, "ExprUtility.Multiply(double, double) not found");
|
||||||
|
_divideInt = mod.Import(exprUtilityType.GetMethod("Divide", new[] {typeof(int), typeof(int) }));
|
||||||
|
Assert.IsNotNull(_divideInt, "ExprUtility.Divide(int, int) not found");
|
||||||
|
_divideLong = mod.Import(exprUtilityType.GetMethod("Divide", new[] { typeof(long), typeof(long) }));
|
||||||
|
Assert.IsNotNull(_divideLong);
|
||||||
|
_divideFloat = mod.Import(exprUtilityType.GetMethod("Divide", new[] { typeof(float), typeof(float) }));
|
||||||
|
Assert.IsNotNull(_divideFloat, "ExprUtility.Divide(float, float) not found");
|
||||||
|
_divideDouble = mod.Import(exprUtilityType.GetMethod("Divide", new[] { typeof(double), typeof(double) }));
|
||||||
|
Assert.IsNotNull(_divideDouble, "ExprUtility.Divide(double, double) not found");
|
||||||
|
_divideUnInt = mod.Import(exprUtilityType.GetMethod("DivideUn", new[] { typeof(int), typeof(int) }));
|
||||||
|
Assert.IsNotNull(_divideUnInt, "ExprUtility.DivideUn(int, int) not found");
|
||||||
|
_divideUnLong = mod.Import(exprUtilityType.GetMethod("DivideUn", new[] { typeof(long), typeof(long) }));
|
||||||
|
Assert.IsNotNull(_divideUnLong, "ExprUtility.DivideUn(long, long) not found");
|
||||||
|
_remInt = mod.Import(exprUtilityType.GetMethod("Rem", new[] { typeof(int), typeof(int) }));
|
||||||
|
Assert.IsNotNull(_remInt, "ExprUtility.Rem(int, int) not found");
|
||||||
|
_remLong = mod.Import(exprUtilityType.GetMethod("Rem", new[] { typeof(long), typeof(long) }));
|
||||||
|
Assert.IsNotNull(_remLong, "ExprUtility.Rem(long, long) not found");
|
||||||
|
_remFloat = mod.Import(exprUtilityType.GetMethod("Rem", new[] { typeof(float), typeof(float) }));
|
||||||
|
Assert.IsNotNull(_remFloat, "ExprUtility.Rem(float, float) not found");
|
||||||
|
_remDouble = mod.Import(exprUtilityType.GetMethod("Rem", new[] { typeof(double), typeof(double) }));
|
||||||
|
Assert.IsNotNull(_remDouble, "ExprUtility.Rem(double, double) not found");
|
||||||
|
_remUnInt = mod.Import(exprUtilityType.GetMethod("RemUn", new[] { typeof(int), typeof(int) }));
|
||||||
|
Assert.IsNotNull(_remUnInt, "ExprUtility.RemUn(int, int) not found");
|
||||||
|
_remUnLong = mod.Import(exprUtilityType.GetMethod("RemUn", new[] { typeof(long), typeof(long) }));
|
||||||
|
Assert.IsNotNull(_remUnLong, "ExprUtility.RemUn(long, long) not found");
|
||||||
|
_negInt = mod.Import(exprUtilityType.GetMethod("Negate", new[] { typeof(int) }));
|
||||||
|
Assert.IsNotNull(_negInt, "ExprUtility.Negate(int) not found");
|
||||||
|
_negLong = mod.Import(exprUtilityType.GetMethod("Negate", new[] { typeof(long) }));
|
||||||
|
Assert.IsNotNull(_negLong, "ExprUtility.Negate(long) not found");
|
||||||
|
_negFloat = mod.Import(exprUtilityType.GetMethod("Negate", new[] { typeof(float) }));
|
||||||
|
Assert.IsNotNull(_negFloat, "ExprUtility.Negate(float) not found");
|
||||||
|
_negDouble = mod.Import(exprUtilityType.GetMethod("Negate", new[] { typeof(double) }));
|
||||||
|
Assert.IsNotNull(_negDouble, "ExprUtility.Negate(double) not found");
|
||||||
|
|
||||||
|
_andInt = mod.Import(exprUtilityType.GetMethod("And", new[] { typeof(int), typeof(int) }));
|
||||||
|
Assert.IsNotNull(_andInt, "ExprUtility.And(int, int) not found");
|
||||||
|
_andLong = mod.Import(exprUtilityType.GetMethod("And", new[] { typeof(long), typeof(long) }));
|
||||||
|
Assert.IsNotNull(_andLong, "ExprUtility.And(long, long) not found");
|
||||||
|
_orInt = mod.Import(exprUtilityType.GetMethod("Or", new[] { typeof(int), typeof(int) }));
|
||||||
|
Assert.IsNotNull(_orInt, "ExprUtility.Or(int, int) not found");
|
||||||
|
_orLong = mod.Import(exprUtilityType.GetMethod("Or", new[] { typeof(long), typeof(long) }));
|
||||||
|
Assert.IsNotNull(_orLong, "ExprUtility.Or(long, long) not found");
|
||||||
|
_xorInt = mod.Import(exprUtilityType.GetMethod("Xor", new[] { typeof(int), typeof(int) }));
|
||||||
|
Assert.IsNotNull(_xorInt, "ExprUtility.Xor(int, int) not found");
|
||||||
|
_xorLong = mod.Import(exprUtilityType.GetMethod("Xor", new[] { typeof(long), typeof(long) }));
|
||||||
|
Assert.IsNotNull(_xorLong, "ExprUtility.Xor(long, long) not found");
|
||||||
|
_notInt = mod.Import(exprUtilityType.GetMethod("Not", new[] { typeof(int) }));
|
||||||
|
Assert.IsNotNull(_notInt, "ExprUtility.Not(int) not found");
|
||||||
|
_notLong = mod.Import(exprUtilityType.GetMethod("Not", new[] { typeof(long) }));
|
||||||
|
Assert.IsNotNull(_notLong, "ExprUtility.Not(long) not found");
|
||||||
|
|
||||||
|
_shlInt = mod.Import(exprUtilityType.GetMethod("ShiftLeft", new[] { typeof(int), typeof(int) }));
|
||||||
|
Assert.IsNotNull(_shlInt, "ExprUtility.ShiftLeft(int, int) not found");
|
||||||
|
_shlLong = mod.Import(exprUtilityType.GetMethod("ShiftLeft", new[] { typeof(long), typeof(int) }));
|
||||||
|
Assert.IsNotNull(_shlLong, "ExprUtility.ShiftLeft(long, int) not found");
|
||||||
|
_shrInt = mod.Import(exprUtilityType.GetMethod("ShiftRight", new[] { typeof(int), typeof(int) }));
|
||||||
|
Assert.IsNotNull(_shrInt, "ExprUtility.ShiftRight(int, int) not found");
|
||||||
|
_shrLong = mod.Import(exprUtilityType.GetMethod("ShiftRight", new[] { typeof(long), typeof(int) }));
|
||||||
|
Assert.IsNotNull(_shrLong, "ExprUtility.ShiftRight(long, int) not found");
|
||||||
|
_shrUnInt = mod.Import(exprUtilityType.GetMethod("ShiftRightUn", new[] { typeof(int), typeof(int) }));
|
||||||
|
Assert.IsNotNull(_shrUnInt, "ExprUtility.ShiftRightUn(int, int) not found");
|
||||||
|
_shrUnLong = mod.Import(exprUtilityType.GetMethod("ShiftRightUn", new[] { typeof(long), typeof(int) }));
|
||||||
|
Assert.IsNotNull(_shrUnLong, "ExprUtility.ShiftRightUn(long, int) not found");
|
||||||
|
|
||||||
|
|
||||||
_staticDefaultEncryptionServiceMetadataImporter = new EncryptionServiceMetadataImporter(mod, typeof(EncryptionService<DefaultStaticEncryptionScope>));
|
_staticDefaultEncryptionServiceMetadataImporter = new EncryptionServiceMetadataImporter(mod, typeof(EncryptionService<DefaultStaticEncryptionScope>));
|
||||||
_dynamicDefaultEncryptionServiceMetadataImporter = new EncryptionServiceMetadataImporter(mod, typeof(EncryptionService<DefaultDynamicEncryptionScope>));
|
_dynamicDefaultEncryptionServiceMetadataImporter = new EncryptionServiceMetadataImporter(mod, typeof(EncryptionService<DefaultDynamicEncryptionScope>));
|
||||||
if (_encryptionScopeProvider.IsDynamicSecretAssembly(mod))
|
if (_encryptionScopeProvider.IsDynamicSecretAssembly(mod))
|
||||||
|
@ -174,6 +263,51 @@ namespace Obfuz.Emit
|
||||||
|
|
||||||
private IMethod _obfuscationTypeMapperRegisterType;
|
private IMethod _obfuscationTypeMapperRegisterType;
|
||||||
|
|
||||||
|
private IMethod _addInt;
|
||||||
|
private IMethod _addLong;
|
||||||
|
private IMethod _addFloat;
|
||||||
|
private IMethod _addDouble;
|
||||||
|
private IMethod _subtractInt;
|
||||||
|
private IMethod _subtractLong;
|
||||||
|
private IMethod _subtractFloat;
|
||||||
|
private IMethod _subtractDouble;
|
||||||
|
private IMethod _multiplyInt;
|
||||||
|
private IMethod _multiplyLong;
|
||||||
|
private IMethod _multiplyFloat;
|
||||||
|
private IMethod _multiplyDouble;
|
||||||
|
private IMethod _divideInt;
|
||||||
|
private IMethod _divideLong;
|
||||||
|
private IMethod _divideFloat;
|
||||||
|
private IMethod _divideDouble;
|
||||||
|
private IMethod _divideUnInt;
|
||||||
|
private IMethod _divideUnLong;
|
||||||
|
private IMethod _remInt;
|
||||||
|
private IMethod _remLong;
|
||||||
|
private IMethod _remFloat;
|
||||||
|
private IMethod _remDouble;
|
||||||
|
private IMethod _remUnInt;
|
||||||
|
private IMethod _remUnLong;
|
||||||
|
private IMethod _negInt;
|
||||||
|
private IMethod _negLong;
|
||||||
|
private IMethod _negFloat;
|
||||||
|
private IMethod _negDouble;
|
||||||
|
|
||||||
|
private IMethod _andInt;
|
||||||
|
private IMethod _andLong;
|
||||||
|
private IMethod _orInt;
|
||||||
|
private IMethod _orLong;
|
||||||
|
private IMethod _xorInt;
|
||||||
|
private IMethod _xorLong;
|
||||||
|
private IMethod _notInt;
|
||||||
|
private IMethod _notLong;
|
||||||
|
|
||||||
|
private IMethod _shlInt;
|
||||||
|
private IMethod _shlLong;
|
||||||
|
private IMethod _shrInt;
|
||||||
|
private IMethod _shrLong;
|
||||||
|
private IMethod _shrUnInt;
|
||||||
|
private IMethod _shrUnLong;
|
||||||
|
|
||||||
public IMethod CastIntAsFloat => _castIntAsFloat;
|
public IMethod CastIntAsFloat => _castIntAsFloat;
|
||||||
public IMethod CastLongAsDouble => _castLongAsDouble;
|
public IMethod CastLongAsDouble => _castLongAsDouble;
|
||||||
public IMethod CastFloatAsInt => _castFloatAsInt;
|
public IMethod CastFloatAsInt => _castFloatAsInt;
|
||||||
|
@ -209,5 +343,50 @@ namespace Obfuz.Emit
|
||||||
public IMethod DecryptFromRvaString => _defaultEncryptionServiceMetadataImporter.DecryptFromRvaString;
|
public IMethod DecryptFromRvaString => _defaultEncryptionServiceMetadataImporter.DecryptFromRvaString;
|
||||||
|
|
||||||
public IMethod DecryptInitializeArray => _defaultEncryptionServiceMetadataImporter.DecryptInitializeArray;
|
public IMethod DecryptInitializeArray => _defaultEncryptionServiceMetadataImporter.DecryptInitializeArray;
|
||||||
|
|
||||||
|
public IMethod AddInt => _addInt;
|
||||||
|
public IMethod AddLong => _addLong;
|
||||||
|
public IMethod AddFloat => _addFloat;
|
||||||
|
public IMethod AddDouble => _addDouble;
|
||||||
|
public IMethod SubtractInt => _subtractInt;
|
||||||
|
public IMethod SubtractLong => _subtractLong;
|
||||||
|
public IMethod SubtractFloat => _subtractFloat;
|
||||||
|
public IMethod SubtractDouble => _subtractDouble;
|
||||||
|
public IMethod MultiplyInt => _multiplyInt;
|
||||||
|
public IMethod MultiplyLong => _multiplyLong;
|
||||||
|
public IMethod MultiplyFloat => _multiplyFloat;
|
||||||
|
public IMethod MultiplyDouble => _multiplyDouble;
|
||||||
|
public IMethod DivideInt => _divideInt;
|
||||||
|
public IMethod DivideLong => _divideLong;
|
||||||
|
public IMethod DivideFloat => _divideFloat;
|
||||||
|
public IMethod DivideDouble => _divideDouble;
|
||||||
|
public IMethod DivideUnInt => _divideUnInt;
|
||||||
|
public IMethod DivideUnLong => _divideUnLong;
|
||||||
|
public IMethod RemInt => _remInt;
|
||||||
|
public IMethod RemLong => _remLong;
|
||||||
|
public IMethod RemFloat => _remFloat;
|
||||||
|
public IMethod RemDouble => _remDouble;
|
||||||
|
public IMethod RemUnInt => _remUnInt;
|
||||||
|
public IMethod RemUnLong => _remUnLong;
|
||||||
|
public IMethod NegInt => _negInt;
|
||||||
|
public IMethod NegLong => _negLong;
|
||||||
|
public IMethod NegFloat => _negFloat;
|
||||||
|
public IMethod NegDouble => _negDouble;
|
||||||
|
public IMethod AndInt => _andInt;
|
||||||
|
public IMethod AndLong => _andLong;
|
||||||
|
public IMethod OrInt => _orInt;
|
||||||
|
public IMethod OrLong => _orLong;
|
||||||
|
public IMethod XorInt => _xorInt;
|
||||||
|
public IMethod XorLong => _xorLong;
|
||||||
|
public IMethod NotInt => _notInt;
|
||||||
|
public IMethod NotLong => _notLong;
|
||||||
|
public IMethod ShlInt => _shlInt;
|
||||||
|
public IMethod ShlLong => _shlLong;
|
||||||
|
public IMethod ShrInt => _shrInt;
|
||||||
|
public IMethod ShrLong => _shrLong;
|
||||||
|
public IMethod ShrUnInt => _shrUnInt;
|
||||||
|
public IMethod ShrUnLong => _shrUnLong;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,888 @@
|
||||||
|
using dnlib.DotNet;
|
||||||
|
using dnlib.DotNet.Emit;
|
||||||
|
using dnlib.DotNet.Writer;
|
||||||
|
using Obfuz.Utils;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Unity.Properties;
|
||||||
|
using UnityEngine.Assertions;
|
||||||
|
|
||||||
|
namespace Obfuz.Emit
|
||||||
|
{
|
||||||
|
enum EvalDataType
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Int32,
|
||||||
|
Int64,
|
||||||
|
Float,
|
||||||
|
Double,
|
||||||
|
I,
|
||||||
|
Ref,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
class InstructionParameterInfo
|
||||||
|
{
|
||||||
|
public readonly EvalDataType op1;
|
||||||
|
public readonly EvalDataType op2;
|
||||||
|
public readonly EvalDataType retType;
|
||||||
|
public InstructionParameterInfo(EvalDataType op1, EvalDataType op2, EvalDataType retType)
|
||||||
|
{
|
||||||
|
this.op1 = op1;
|
||||||
|
this.op2 = op2;
|
||||||
|
this.retType = retType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EvalStackCalculator
|
||||||
|
{
|
||||||
|
private readonly MethodDef _method;
|
||||||
|
private readonly BasicBlockCollection _basicBlocks;
|
||||||
|
private readonly Dictionary<Instruction, InstructionParameterInfo> _instructionParameterInfos = new Dictionary<Instruction, InstructionParameterInfo>();
|
||||||
|
|
||||||
|
public EvalStackCalculator(MethodDef method)
|
||||||
|
{
|
||||||
|
_method = method;
|
||||||
|
_basicBlocks = new BasicBlockCollection(method, false);
|
||||||
|
|
||||||
|
SimulateRunAllBlocks();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetParameterInfo(Instruction inst, out InstructionParameterInfo info)
|
||||||
|
{
|
||||||
|
return _instructionParameterInfos.TryGetValue(inst, out info);
|
||||||
|
}
|
||||||
|
|
||||||
|
class EvalStackState
|
||||||
|
{
|
||||||
|
public bool visited;
|
||||||
|
|
||||||
|
public readonly List<EvalDataType> inputStackDatas = new List<EvalDataType>();
|
||||||
|
public readonly List<EvalDataType> runStackDatas = new List<EvalDataType>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PushStack(List<EvalDataType> datas, TypeSig type)
|
||||||
|
{
|
||||||
|
type = type.RemovePinnedAndModifiers();
|
||||||
|
switch (type.ElementType)
|
||||||
|
{
|
||||||
|
case ElementType.Void: break;
|
||||||
|
case ElementType.Boolean:
|
||||||
|
case ElementType.Char:
|
||||||
|
case ElementType.I1:
|
||||||
|
case ElementType.U1:
|
||||||
|
case ElementType.I2:
|
||||||
|
case ElementType.U2:
|
||||||
|
case ElementType.I4:
|
||||||
|
case ElementType.U4:
|
||||||
|
datas.Add(EvalDataType.Int32);
|
||||||
|
break;
|
||||||
|
case ElementType.I8:
|
||||||
|
case ElementType.U8:
|
||||||
|
datas.Add(EvalDataType.Int64);
|
||||||
|
break;
|
||||||
|
case ElementType.R4:
|
||||||
|
datas.Add(EvalDataType.Float);
|
||||||
|
break;
|
||||||
|
case ElementType.R8:
|
||||||
|
datas.Add(EvalDataType.Double);
|
||||||
|
break;
|
||||||
|
case ElementType.I:
|
||||||
|
case ElementType.U:
|
||||||
|
case ElementType.Ptr:
|
||||||
|
case ElementType.FnPtr:
|
||||||
|
case ElementType.ByRef:
|
||||||
|
datas.Add(EvalDataType.I);
|
||||||
|
break;
|
||||||
|
case ElementType.String:
|
||||||
|
case ElementType.Class:
|
||||||
|
case ElementType.Array:
|
||||||
|
case ElementType.SZArray:
|
||||||
|
case ElementType.Object:
|
||||||
|
datas.Add(EvalDataType.Ref);
|
||||||
|
break;
|
||||||
|
case ElementType.ValueType:
|
||||||
|
{
|
||||||
|
TypeDef typeDef = type.ToTypeDefOrRef().ResolveTypeDefThrow();
|
||||||
|
if (typeDef.IsEnum)
|
||||||
|
{
|
||||||
|
PushStack(datas, typeDef.GetEnumUnderlyingType());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PushStack(datas, EvalDataType.Unknown);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ElementType.GenericInst:
|
||||||
|
{
|
||||||
|
GenericInstSig genericInstSig = (GenericInstSig)type;
|
||||||
|
PushStack(datas, genericInstSig.GenericType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ElementType.Var:
|
||||||
|
case ElementType.MVar:
|
||||||
|
case ElementType.ValueArray:
|
||||||
|
case ElementType.TypedByRef:
|
||||||
|
case ElementType.R:
|
||||||
|
case ElementType.CModOpt:
|
||||||
|
case ElementType.CModReqd:
|
||||||
|
case ElementType.Internal:
|
||||||
|
case ElementType.Module:
|
||||||
|
case ElementType.Sentinel:
|
||||||
|
PushStack(datas, EvalDataType.Unknown);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: throw new Exception($"Unsupported type: {type} in method: {_method.FullName}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PushStack(List<EvalDataType> datas, ITypeDefOrRef type)
|
||||||
|
{
|
||||||
|
PushStack(datas, type.ToTypeSig());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PushStack(List<EvalDataType> datas, EvalDataType type)
|
||||||
|
{
|
||||||
|
datas.Add(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private EvalDataType CalcBasicBinOpRetType(List<EvalDataType> datas, EvalDataType op1, EvalDataType op2)
|
||||||
|
{
|
||||||
|
switch (op1)
|
||||||
|
{
|
||||||
|
case EvalDataType.Int32:
|
||||||
|
{
|
||||||
|
switch (op2)
|
||||||
|
{
|
||||||
|
case EvalDataType.Int32: return EvalDataType.Int32;
|
||||||
|
case EvalDataType.Int64: return EvalDataType.Int64;
|
||||||
|
case EvalDataType.I: return EvalDataType.I;
|
||||||
|
default: throw new Exception($"Unsupported operand type: {op2} for {op1} in binary operation.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case EvalDataType.Int64:
|
||||||
|
{
|
||||||
|
switch (op2)
|
||||||
|
{
|
||||||
|
case EvalDataType.Int32: return EvalDataType.Int64;
|
||||||
|
case EvalDataType.Int64:
|
||||||
|
case EvalDataType.I:
|
||||||
|
return EvalDataType.Int64;
|
||||||
|
default: throw new Exception($"Unsupported operand type: {op2} for {op1} in binary operation.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case EvalDataType.I:
|
||||||
|
{
|
||||||
|
switch (op2)
|
||||||
|
{
|
||||||
|
case EvalDataType.Int32: return EvalDataType.I;
|
||||||
|
case EvalDataType.Int64: return EvalDataType.Int64;
|
||||||
|
case EvalDataType.I: return EvalDataType.I;
|
||||||
|
default: throw new Exception($"Unsupported operand type: {op2} for {op1} in binary operation.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case EvalDataType.Float:
|
||||||
|
{
|
||||||
|
switch (op2)
|
||||||
|
{
|
||||||
|
case EvalDataType.Float: return EvalDataType.Float;
|
||||||
|
default: throw new Exception($"Unsupported operand type: {op2} for {op1} in binary operation.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case EvalDataType.Double:
|
||||||
|
{
|
||||||
|
switch (op2)
|
||||||
|
{
|
||||||
|
case EvalDataType.Double: return EvalDataType.Double;
|
||||||
|
default: throw new Exception($"Unsupported operand type: {op2} for {op1} in binary operation.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: throw new Exception($"Unsupported operand type: {op1} in binary operation.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SimulateRunAllBlocks()
|
||||||
|
{
|
||||||
|
Dictionary<BasicBlock, EvalStackState> blockEvalStackStates = _basicBlocks.Blocks.ToDictionary(b => b, b => new EvalStackState());
|
||||||
|
bool methodHasReturnValue = _method.ReturnType.ElementType != ElementType.Void;
|
||||||
|
|
||||||
|
CilBody body = _method.Body;
|
||||||
|
if (body.HasExceptionHandlers)
|
||||||
|
{
|
||||||
|
foreach (ExceptionHandler handler in body.ExceptionHandlers)
|
||||||
|
{
|
||||||
|
if (handler.IsCatch)
|
||||||
|
{
|
||||||
|
BasicBlock bb = _basicBlocks.GetBasicBlockByInstruction(handler.HandlerStart);
|
||||||
|
blockEvalStackStates[bb].runStackDatas.Add(EvalDataType.Ref); // Exception object is pushed onto the stack.
|
||||||
|
}
|
||||||
|
else if (handler.IsFilter)
|
||||||
|
{
|
||||||
|
BasicBlock bb = _basicBlocks.GetBasicBlockByInstruction(handler.FilterStart);
|
||||||
|
blockEvalStackStates[bb].runStackDatas.Add(EvalDataType.Ref); // Exception object is pushed onto the stack.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var newPushedDatas = new List<EvalDataType>();
|
||||||
|
IList<TypeSig> methodTypeGenericArgument = _method.DeclaringType.GenericParameters.Count > 0
|
||||||
|
? (IList<TypeSig>)_method.DeclaringType.GenericParameters.Select(p => (TypeSig)new GenericVar(p.Number)).ToList()
|
||||||
|
: null;
|
||||||
|
IList<TypeSig> methodMethodGenericArgument = _method.GenericParameters.Count > 0
|
||||||
|
? (IList<TypeSig>)_method.GenericParameters.Select(p => (TypeSig)new GenericMVar(p.Number)).ToList()
|
||||||
|
: null;
|
||||||
|
var gac = new GenericArgumentContext(methodTypeGenericArgument, methodMethodGenericArgument);
|
||||||
|
|
||||||
|
var blockWalkStack = new Stack<BasicBlock>(_basicBlocks.Blocks.Reverse());
|
||||||
|
while (blockWalkStack.TryPop(out BasicBlock block))
|
||||||
|
{
|
||||||
|
EvalStackState state = blockEvalStackStates[block];
|
||||||
|
if (state.visited)
|
||||||
|
continue;
|
||||||
|
state.visited = true;
|
||||||
|
state.runStackDatas.AddRange(state.inputStackDatas);
|
||||||
|
List<EvalDataType> stackDatas = state.runStackDatas;
|
||||||
|
foreach (var inst in block.instructions)
|
||||||
|
{
|
||||||
|
int stackSize = stackDatas.Count;
|
||||||
|
newPushedDatas.Clear();
|
||||||
|
switch (inst.OpCode.Code)
|
||||||
|
{
|
||||||
|
case Code.Nop: break;
|
||||||
|
case Code.Break: break;
|
||||||
|
case Code.Ldarg_0:
|
||||||
|
case Code.Ldarg_1:
|
||||||
|
case Code.Ldarg_2:
|
||||||
|
case Code.Ldarg_3:
|
||||||
|
case Code.Ldarg:
|
||||||
|
case Code.Ldarg_S:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, inst.GetParameter(_method.Parameters).Type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldarga:
|
||||||
|
case Code.Ldarga_S:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.I);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldloc_0:
|
||||||
|
case Code.Ldloc_1:
|
||||||
|
case Code.Ldloc_2:
|
||||||
|
case Code.Ldloc_3:
|
||||||
|
case Code.Ldloc:
|
||||||
|
case Code.Ldloc_S:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, inst.GetLocal(body.Variables).Type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldloca:
|
||||||
|
case Code.Ldloca_S:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.I);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Stloc_0:
|
||||||
|
case Code.Stloc_1:
|
||||||
|
case Code.Stloc_2:
|
||||||
|
case Code.Stloc_3:
|
||||||
|
case Code.Stloc:
|
||||||
|
case Code.Stloc_S:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize > 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Starg:
|
||||||
|
case Code.Starg_S:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize > 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldnull:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.I);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldc_I4_M1:
|
||||||
|
case Code.Ldc_I4_0:
|
||||||
|
case Code.Ldc_I4_1:
|
||||||
|
case Code.Ldc_I4_2:
|
||||||
|
case Code.Ldc_I4_3:
|
||||||
|
case Code.Ldc_I4_4:
|
||||||
|
case Code.Ldc_I4_5:
|
||||||
|
case Code.Ldc_I4_6:
|
||||||
|
case Code.Ldc_I4_7:
|
||||||
|
case Code.Ldc_I4_8:
|
||||||
|
case Code.Ldc_I4:
|
||||||
|
case Code.Ldc_I4_S:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Int32);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldc_I8:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Int64);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldc_R4:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Float);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldc_R8:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Double);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Dup:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize > 0);
|
||||||
|
EvalDataType type = stackDatas[stackSize - 1];
|
||||||
|
PushStack(newPushedDatas, type);
|
||||||
|
PushStack(newPushedDatas, type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Pop:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Jmp:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Call:
|
||||||
|
case Code.Callvirt:
|
||||||
|
{
|
||||||
|
IMethod calledMethod = (IMethod)inst.Operand;
|
||||||
|
MethodSig methodSig = MetaUtil.GetInflatedMethodSig(calledMethod, gac);
|
||||||
|
PushStack(newPushedDatas, methodSig.RetType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Calli:
|
||||||
|
{
|
||||||
|
MethodSig methodSig = (MethodSig)inst.Operand;
|
||||||
|
PushStack(newPushedDatas, methodSig.RetType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ret:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Br:
|
||||||
|
case Code.Br_S:
|
||||||
|
case Code.Brfalse:
|
||||||
|
case Code.Brfalse_S:
|
||||||
|
case Code.Brtrue:
|
||||||
|
case Code.Brtrue_S:
|
||||||
|
case Code.Beq:
|
||||||
|
case Code.Beq_S:
|
||||||
|
case Code.Bge:
|
||||||
|
case Code.Bge_S:
|
||||||
|
case Code.Bge_Un:
|
||||||
|
case Code.Bge_Un_S:
|
||||||
|
case Code.Bgt:
|
||||||
|
case Code.Bgt_S:
|
||||||
|
case Code.Bgt_Un:
|
||||||
|
case Code.Bgt_Un_S:
|
||||||
|
case Code.Ble:
|
||||||
|
case Code.Ble_S:
|
||||||
|
case Code.Ble_Un:
|
||||||
|
case Code.Ble_Un_S:
|
||||||
|
case Code.Blt:
|
||||||
|
case Code.Blt_S:
|
||||||
|
case Code.Blt_Un:
|
||||||
|
case Code.Blt_Un_S:
|
||||||
|
case Code.Bne_Un:
|
||||||
|
case Code.Bne_Un_S:
|
||||||
|
{
|
||||||
|
// Branch instructions do not change the stack.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ceq:
|
||||||
|
case Code.Cgt:
|
||||||
|
case Code.Cgt_Un:
|
||||||
|
case Code.Clt:
|
||||||
|
case Code.Clt_Un:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize >= 2);
|
||||||
|
EvalDataType op2 = stackDatas[stackSize - 1];
|
||||||
|
EvalDataType op1 = stackDatas[stackSize - 2];
|
||||||
|
EvalDataType ret = EvalDataType.Int32;
|
||||||
|
_instructionParameterInfos.Add(inst, new InstructionParameterInfo(op1, op2, ret));
|
||||||
|
PushStack(newPushedDatas, ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Switch:
|
||||||
|
{
|
||||||
|
// Switch instruction does not change the stack.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldind_I1:
|
||||||
|
case Code.Ldind_U1:
|
||||||
|
case Code.Ldind_I2:
|
||||||
|
case Code.Ldind_U2:
|
||||||
|
case Code.Ldind_I4:
|
||||||
|
case Code.Ldind_U4:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize > 0);
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Int32);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldind_I8:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize > 0);
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Int64);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldind_I:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize > 0);
|
||||||
|
PushStack(newPushedDatas, EvalDataType.I);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldind_Ref:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize > 0);
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Ref);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldind_R4:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize > 0);
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Float);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldind_R8:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize > 0);
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Double);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Stind_I1:
|
||||||
|
case Code.Stind_I2:
|
||||||
|
case Code.Stind_I4:
|
||||||
|
case Code.Stind_I8:
|
||||||
|
case Code.Stind_I:
|
||||||
|
case Code.Stind_R4:
|
||||||
|
case Code.Stind_R8:
|
||||||
|
case Code.Stind_Ref:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize >= 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Code.Add:
|
||||||
|
case Code.Add_Ovf:
|
||||||
|
case Code.Add_Ovf_Un:
|
||||||
|
case Code.Sub:
|
||||||
|
case Code.Sub_Ovf:
|
||||||
|
case Code.Sub_Ovf_Un:
|
||||||
|
case Code.Mul:
|
||||||
|
case Code.Mul_Ovf:
|
||||||
|
case Code.Mul_Ovf_Un:
|
||||||
|
case Code.Div:
|
||||||
|
case Code.Div_Un:
|
||||||
|
case Code.Rem:
|
||||||
|
case Code.Rem_Un:
|
||||||
|
|
||||||
|
case Code.And:
|
||||||
|
case Code.Or:
|
||||||
|
case Code.Xor:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize >= 2);
|
||||||
|
EvalDataType op2 = stackDatas[stackSize - 1];
|
||||||
|
EvalDataType op1 = stackDatas[stackSize - 2];
|
||||||
|
EvalDataType ret = CalcBasicBinOpRetType(stackDatas, op1, op2);
|
||||||
|
_instructionParameterInfos.Add(inst, new InstructionParameterInfo(op1, op2, ret));
|
||||||
|
PushStack(newPushedDatas, ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Shl:
|
||||||
|
case Code.Shr:
|
||||||
|
case Code.Shr_Un:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize >= 2);
|
||||||
|
EvalDataType op2 = stackDatas[stackSize - 1];
|
||||||
|
EvalDataType op1 = stackDatas[stackSize - 2];
|
||||||
|
if (op1 != EvalDataType.Int32 && op1 != EvalDataType.Int64 && op1 != EvalDataType.I)
|
||||||
|
throw new Exception($"Unsupported operand type: {op1} in shift operation.");
|
||||||
|
if (op2 != EvalDataType.Int32 && op2 != EvalDataType.Int64)
|
||||||
|
throw new Exception($"Unsupported operand type: {op2} for {op1} in shift operation.");
|
||||||
|
EvalDataType ret = op1;
|
||||||
|
_instructionParameterInfos.Add(inst, new InstructionParameterInfo(op1, op2, ret));
|
||||||
|
PushStack(newPushedDatas, ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Neg:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize > 0);
|
||||||
|
EvalDataType op = stackDatas[stackSize - 1];
|
||||||
|
EvalDataType ret = op;
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case EvalDataType.Int32:
|
||||||
|
case EvalDataType.Int64:
|
||||||
|
case EvalDataType.I:
|
||||||
|
case EvalDataType.Float:
|
||||||
|
case EvalDataType.Double:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception($"Unsupported operand type: {op} in unary operation.");
|
||||||
|
}
|
||||||
|
_instructionParameterInfos.Add(inst, new InstructionParameterInfo(op, EvalDataType.None, ret));
|
||||||
|
PushStack(newPushedDatas, ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Not:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize > 0);
|
||||||
|
EvalDataType op = stackDatas[stackSize - 1];
|
||||||
|
EvalDataType ret = op;
|
||||||
|
if (op != EvalDataType.Int32 && op != EvalDataType.Int64 && op != EvalDataType.I)
|
||||||
|
throw new Exception($"Unsupported operand type: {op} in unary operation.");
|
||||||
|
_instructionParameterInfos.Add(inst, new InstructionParameterInfo(op, EvalDataType.None, ret));
|
||||||
|
PushStack(newPushedDatas, ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Conv_I1:
|
||||||
|
case Code.Conv_U1:
|
||||||
|
case Code.Conv_I2:
|
||||||
|
case Code.Conv_U2:
|
||||||
|
case Code.Conv_I4:
|
||||||
|
case Code.Conv_U4:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Int32);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Conv_I8:
|
||||||
|
case Code.Conv_U8:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Int64);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Conv_I:
|
||||||
|
case Code.Conv_U:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.I);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Conv_R4:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Float);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Conv_R8:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Double);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Conv_Ovf_I1:
|
||||||
|
case Code.Conv_Ovf_I1_Un:
|
||||||
|
case Code.Conv_Ovf_U1:
|
||||||
|
case Code.Conv_Ovf_U1_Un:
|
||||||
|
case Code.Conv_Ovf_I2:
|
||||||
|
case Code.Conv_Ovf_I2_Un:
|
||||||
|
case Code.Conv_Ovf_U2:
|
||||||
|
case Code.Conv_Ovf_U2_Un:
|
||||||
|
case Code.Conv_Ovf_I4:
|
||||||
|
case Code.Conv_Ovf_I4_Un:
|
||||||
|
case Code.Conv_Ovf_U4:
|
||||||
|
case Code.Conv_Ovf_U4_Un:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Int32);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Conv_Ovf_I8:
|
||||||
|
case Code.Conv_Ovf_I8_Un:
|
||||||
|
case Code.Conv_Ovf_U8:
|
||||||
|
case Code.Conv_Ovf_U8_Un:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Int64);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Conv_Ovf_I:
|
||||||
|
case Code.Conv_Ovf_I_Un:
|
||||||
|
case Code.Conv_Ovf_U:
|
||||||
|
case Code.Conv_Ovf_U_Un:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.I);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Conv_R_Un:
|
||||||
|
{
|
||||||
|
//PushStack(newDataTack, EvalDataType.Double);
|
||||||
|
//break;
|
||||||
|
throw new Exception($"unsupported opcode:{inst}");
|
||||||
|
}
|
||||||
|
case Code.Cpobj:
|
||||||
|
case Code.Initobj:
|
||||||
|
case Code.Stobj:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldobj:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, (ITypeDefOrRef)inst.Operand);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldstr:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Ref);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Newobj:
|
||||||
|
{
|
||||||
|
IMethod ctor = (IMethod)inst.Operand;
|
||||||
|
PushStack(newPushedDatas, ctor.DeclaringType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Castclass:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, (ITypeDefOrRef)inst.Operand);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Isinst:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Int32);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Unbox:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize > 0);
|
||||||
|
PushStack(newPushedDatas, EvalDataType.I);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Unbox_Any:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize > 0);
|
||||||
|
PushStack(newPushedDatas, (ITypeDefOrRef)inst.Operand);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Box:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize > 0);
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Ref);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Throw:
|
||||||
|
{
|
||||||
|
// Throw instruction does not change the stack.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Rethrow:
|
||||||
|
{
|
||||||
|
// Rethrow instruction does not change the stack.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldfld:
|
||||||
|
case Code.Ldsfld:
|
||||||
|
{
|
||||||
|
IField field = (IField)inst.Operand;
|
||||||
|
TypeSig fieldType = MetaUtil.InflateFieldSig(field, gac);
|
||||||
|
PushStack(newPushedDatas, field.FieldSig.GetFieldType());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldflda:
|
||||||
|
case Code.Ldsflda:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize > 0);
|
||||||
|
PushStack(newPushedDatas, EvalDataType.I);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Stfld:
|
||||||
|
case Code.Stsfld:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Newarr:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize > 0);
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Ref);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldlen:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize > 0);
|
||||||
|
PushStack(newPushedDatas, EvalDataType.I);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldelema:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize >= 2);
|
||||||
|
PushStack(newPushedDatas, EvalDataType.I);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldelem_I1:
|
||||||
|
case Code.Ldelem_U1:
|
||||||
|
case Code.Ldelem_I2:
|
||||||
|
case Code.Ldelem_U2:
|
||||||
|
case Code.Ldelem_I4:
|
||||||
|
case Code.Ldelem_U4:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize >= 2);
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Int32);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldelem_I8:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize >= 2);
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Int64);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldelem_I:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize >= 2);
|
||||||
|
PushStack(newPushedDatas, EvalDataType.I);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldelem_R4:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize >= 2);
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Float);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldelem_R8:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize >= 2);
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Double);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldelem_Ref:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize >= 2);
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Ref);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldelem:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize >= 2);
|
||||||
|
PushStack(newPushedDatas, (ITypeDefOrRef)inst.Operand);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Stelem_I1:
|
||||||
|
case Code.Stelem_I2:
|
||||||
|
case Code.Stelem_I4:
|
||||||
|
case Code.Stelem_I8:
|
||||||
|
case Code.Stelem_I:
|
||||||
|
case Code.Stelem_R4:
|
||||||
|
case Code.Stelem_R8:
|
||||||
|
case Code.Stelem_Ref:
|
||||||
|
case Code.Stelem:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize >= 3);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Mkrefany:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Unknown);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Refanytype:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Unknown);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Refanyval:
|
||||||
|
{
|
||||||
|
Assert.IsTrue(stackSize > 0);
|
||||||
|
PushStack(newPushedDatas, (ITypeDefOrRef)inst.Operand);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldtoken:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Unknown);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Endfinally:
|
||||||
|
case Code.Leave:
|
||||||
|
case Code.Leave_S:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Endfilter:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Arglist:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Ldftn:
|
||||||
|
case Code.Ldvirtftn:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Unknown);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Localloc:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.I);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Unaligned:
|
||||||
|
case Code.Volatile:
|
||||||
|
case Code.Tailcall:
|
||||||
|
case Code.No:
|
||||||
|
case Code.Readonly:
|
||||||
|
case Code.Constrained:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Cpblk:
|
||||||
|
case Code.Initblk:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Code.Sizeof:
|
||||||
|
{
|
||||||
|
PushStack(newPushedDatas, EvalDataType.Int32);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: throw new Exception($"not supported opcode: {inst} in method: {_method.FullName}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
inst.CalculateStackUsage(methodHasReturnValue, out var pushed, out var pops);
|
||||||
|
if (pushed != newPushedDatas.Count)
|
||||||
|
{
|
||||||
|
throw new Exception($"Instruction {inst} in method {_method.FullName} pushed {newPushedDatas.Count} items, but expected {pushed} items.");
|
||||||
|
}
|
||||||
|
if (pops == -1)
|
||||||
|
{
|
||||||
|
stackDatas.Clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (stackSize < pops)
|
||||||
|
{
|
||||||
|
throw new Exception($"Instruction {inst} in method {_method.FullName} pops {pops} items, but only {stackSize} items are available on the stack.");
|
||||||
|
}
|
||||||
|
stackDatas.RemoveRange(stackDatas.Count - pops, pops);
|
||||||
|
stackDatas.AddRange(newPushedDatas);
|
||||||
|
Assert.AreEqual(stackSize + pushed - pops, stackDatas.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (BasicBlock outBb in block.outBlocks)
|
||||||
|
{
|
||||||
|
EvalStackState outState = blockEvalStackStates[outBb];
|
||||||
|
if (outState.visited)
|
||||||
|
{
|
||||||
|
if (stackDatas.Count != outState.inputStackDatas.Count)
|
||||||
|
{
|
||||||
|
throw new Exception($"Block {block} in method {_method.FullName} has inconsistent stack data. Expected {outState.inputStackDatas.Count}, but got {stackDatas.Count}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (outState.inputStackDatas.Count != stackDatas.Count)
|
||||||
|
{
|
||||||
|
if (outState.inputStackDatas.Count > 0)
|
||||||
|
{
|
||||||
|
throw new Exception($"Block {outBb} in method {_method.FullName} has inconsistent stack data. Expected {outState.inputStackDatas.Count}, but got {stackDatas.Count}.");
|
||||||
|
}
|
||||||
|
outState.inputStackDatas.AddRange(stackDatas);
|
||||||
|
blockWalkStack.Push(outBb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f25425a3077f6db41873dee4223d0abc
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,67 @@
|
||||||
|
using dnlib.DotNet;
|
||||||
|
using dnlib.DotNet.Emit;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Obfuz.Emit
|
||||||
|
{
|
||||||
|
class ScopeLocalVariables : IDisposable
|
||||||
|
{
|
||||||
|
private readonly LocalVariableAllocator _localVariableAllocator;
|
||||||
|
|
||||||
|
private readonly List<Local> _allocatedVars = new List<Local>();
|
||||||
|
|
||||||
|
|
||||||
|
public ScopeLocalVariables(LocalVariableAllocator localVariableAllocator)
|
||||||
|
{
|
||||||
|
_localVariableAllocator = localVariableAllocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Local AllocateLocal(TypeSig type)
|
||||||
|
{
|
||||||
|
var local = _localVariableAllocator.AllocateLocal(type);
|
||||||
|
_allocatedVars.Add(local);
|
||||||
|
return local;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (var local in _allocatedVars)
|
||||||
|
{
|
||||||
|
_localVariableAllocator.ReturnLocal(local);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LocalVariableAllocator
|
||||||
|
{
|
||||||
|
private readonly MethodDef _method;
|
||||||
|
private readonly List<Local> _freeLocals = new List<Local>();
|
||||||
|
|
||||||
|
public LocalVariableAllocator(MethodDef method)
|
||||||
|
{
|
||||||
|
_method = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Local AllocateLocal(TypeSig type)
|
||||||
|
{
|
||||||
|
foreach (var local in _freeLocals)
|
||||||
|
{
|
||||||
|
if (TypeEqualityComparer.Instance.Equals(local.Type, type))
|
||||||
|
{
|
||||||
|
_freeLocals.Remove(local);
|
||||||
|
return local;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var newLocal = new Local(type);
|
||||||
|
// _freeLocals.Add(newLocal);
|
||||||
|
_method.Body.Variables.Add(newLocal);
|
||||||
|
return newLocal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReturnLocal(Local local)
|
||||||
|
{
|
||||||
|
_freeLocals.Add(local);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 955da34fbde179641a94108ec53405ce
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -6,49 +6,14 @@ using System.Linq;
|
||||||
|
|
||||||
namespace Obfuz.ObfusPasses
|
namespace Obfuz.ObfusPasses
|
||||||
{
|
{
|
||||||
public abstract class BasicBlockObfuscationPassBase : ObfuscationPassBase
|
public abstract class BasicBlockObfuscationPassBase : ObfuscationMethodPassBase
|
||||||
{
|
{
|
||||||
protected abstract bool NeedObfuscateMethod(MethodDef method);
|
|
||||||
|
|
||||||
protected virtual bool ComputeBlockInLoop => true;
|
protected virtual bool ComputeBlockInLoop => true;
|
||||||
|
|
||||||
public override void Process()
|
|
||||||
{
|
|
||||||
var ctx = ObfuscationPassContext.Current;
|
|
||||||
ObfuscationMethodWhitelist whiteList = ctx.whiteList;
|
|
||||||
ConfigurablePassPolicy passPolicy = ctx.passPolicy;
|
|
||||||
foreach (ModuleDef mod in ctx.modulesToObfuscate)
|
|
||||||
{
|
|
||||||
if (whiteList.IsInWhiteList(mod))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// ToArray to avoid modify list exception
|
|
||||||
foreach (TypeDef type in mod.GetTypes().ToArray())
|
|
||||||
{
|
|
||||||
if (whiteList.IsInWhiteList(type))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// ToArray to avoid modify list exception
|
|
||||||
foreach (MethodDef method in type.Methods.ToArray())
|
|
||||||
{
|
|
||||||
if (!method.HasBody || ctx.whiteList.IsInWhiteList(method) || !Support(passPolicy.GetMethodObfuscationPasses(method)) || !NeedObfuscateMethod(method))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// TODO if isGeneratedBy Obfuscator, continue
|
|
||||||
ObfuscateData(method);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected abstract bool TryObfuscateInstruction(MethodDef callingMethod, Instruction inst, BasicBlock block, int instructionIndex,
|
protected abstract bool TryObfuscateInstruction(MethodDef callingMethod, Instruction inst, BasicBlock block, int instructionIndex,
|
||||||
IList<Instruction> globalInstructions, List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions);
|
IList<Instruction> globalInstructions, List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions);
|
||||||
|
|
||||||
private void ObfuscateData(MethodDef method)
|
protected override void ObfuscateData(MethodDef method)
|
||||||
{
|
{
|
||||||
BasicBlockCollection bbc = new BasicBlockCollection(method, ComputeBlockInLoop);
|
BasicBlockCollection bbc = new BasicBlockCollection(method, ComputeBlockInLoop);
|
||||||
|
|
||||||
|
|
|
@ -157,7 +157,7 @@ namespace Obfuz.ObfusPasses.CallObfus
|
||||||
|
|
||||||
private MethodSig CreateDispatchMethodSig(IMethod method)
|
private MethodSig CreateDispatchMethodSig(IMethod method)
|
||||||
{
|
{
|
||||||
MethodSig methodSig = MetaUtil.ToSharedMethodSig(_module.CorLibTypes, MetaUtil.GetInflatedMethodSig(method));
|
MethodSig methodSig = MetaUtil.ToSharedMethodSig(_module.CorLibTypes, MetaUtil.GetInflatedMethodSig(method, null));
|
||||||
//MethodSig methodSig = MetaUtil.GetInflatedMethodSig(method).Clone();
|
//MethodSig methodSig = MetaUtil.GetInflatedMethodSig(method).Clone();
|
||||||
//methodSig.Params
|
//methodSig.Params
|
||||||
switch (MetaUtil.GetThisArgType(method))
|
switch (MetaUtil.GetThisArgType(method))
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace Obfuz.ObfusPasses.CallObfus
|
||||||
public override void Obfuscate(MethodDef callerMethod, IMethod calledMethod, bool callVir, bool needCacheCall, List<Instruction> obfuscatedInstructions)
|
public override void Obfuscate(MethodDef callerMethod, IMethod calledMethod, bool callVir, bool needCacheCall, List<Instruction> obfuscatedInstructions)
|
||||||
{
|
{
|
||||||
|
|
||||||
MethodSig sharedMethodSig = MetaUtil.ToSharedMethodSig(calledMethod.Module.CorLibTypes, MetaUtil.GetInflatedMethodSig(calledMethod));
|
MethodSig sharedMethodSig = MetaUtil.ToSharedMethodSig(calledMethod.Module.CorLibTypes, MetaUtil.GetInflatedMethodSig(calledMethod, null));
|
||||||
ProxyCallMethodData proxyCallMethodData = _proxyCallAllocator.Allocate(callerMethod.Module, calledMethod, callVir);
|
ProxyCallMethodData proxyCallMethodData = _proxyCallAllocator.Allocate(callerMethod.Module, calledMethod, callVir);
|
||||||
DefaultMetadataImporter importer = _moduleEntityManager.GetDefaultModuleMetadataImporter(callerMethod.Module, _encryptionScopeProvider);
|
DefaultMetadataImporter importer = _moduleEntityManager.GetDefaultModuleMetadataImporter(callerMethod.Module, _encryptionScopeProvider);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
using dnlib.DotNet;
|
||||||
|
using Obfuz.Conf;
|
||||||
|
using Obfuz.Utils;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Xml;
|
||||||
|
|
||||||
|
namespace Obfuz.ObfusPasses.ExprObfus
|
||||||
|
{
|
||||||
|
|
||||||
|
public interface IObfuscationPolicy
|
||||||
|
{
|
||||||
|
bool NeedObfuscate(MethodDef method);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class ObfuscationPolicyBase : IObfuscationPolicy
|
||||||
|
{
|
||||||
|
public abstract bool NeedObfuscate(MethodDef method);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ConfigurableObfuscationPolicy : ObfuscationPolicyBase
|
||||||
|
{
|
||||||
|
class ObfuscationRule : IRule<ObfuscationRule>
|
||||||
|
{
|
||||||
|
public bool? obfuscate;
|
||||||
|
|
||||||
|
public void InheritParent(ObfuscationRule parentRule)
|
||||||
|
{
|
||||||
|
if (obfuscate == null)
|
||||||
|
obfuscate = parentRule.obfuscate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MethodSpec : MethodRuleBase<ObfuscationRule>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
class TypeSpec : TypeRuleBase<MethodSpec, ObfuscationRule>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
class AssemblySpec : AssemblyRuleBase<TypeSpec, MethodSpec, ObfuscationRule>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly ObfuscationRule s_default = new ObfuscationRule()
|
||||||
|
{
|
||||||
|
obfuscate = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly XmlAssemblyTypeMethodRuleParser<AssemblySpec, TypeSpec, MethodSpec, ObfuscationRule> _xmlParser;
|
||||||
|
|
||||||
|
private readonly Dictionary<MethodDef, ObfuscationRule> _methodRuleCache = new Dictionary<MethodDef, ObfuscationRule>();
|
||||||
|
|
||||||
|
public ConfigurableObfuscationPolicy(List<string> toObfuscatedAssemblyNames, List<string> xmlConfigFiles)
|
||||||
|
{
|
||||||
|
_xmlParser = new XmlAssemblyTypeMethodRuleParser<AssemblySpec, TypeSpec, MethodSpec, ObfuscationRule>(
|
||||||
|
toObfuscatedAssemblyNames, ParseObfuscationRule, null);
|
||||||
|
LoadConfigs(xmlConfigFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadConfigs(List<string> configFiles)
|
||||||
|
{
|
||||||
|
_xmlParser.LoadConfigs(configFiles);
|
||||||
|
_xmlParser.InheritParentRules(s_default);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObfuscationRule ParseObfuscationRule(string configFile, XmlElement ele)
|
||||||
|
{
|
||||||
|
var rule = new ObfuscationRule();
|
||||||
|
if (ele.HasAttribute("obfuscate"))
|
||||||
|
{
|
||||||
|
rule.obfuscate = ConfigUtil.ParseBool(ele.GetAttribute("obfuscate"));
|
||||||
|
}
|
||||||
|
return rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObfuscationRule GetMethodObfuscationRule(MethodDef method)
|
||||||
|
{
|
||||||
|
if (!_methodRuleCache.TryGetValue(method, out var rule))
|
||||||
|
{
|
||||||
|
rule = _xmlParser.GetMethodRule(method, s_default);
|
||||||
|
_methodRuleCache[method] = rule;
|
||||||
|
}
|
||||||
|
return rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool NeedObfuscate(MethodDef method)
|
||||||
|
{
|
||||||
|
ObfuscationRule rule = GetMethodObfuscationRule(method);
|
||||||
|
return rule.obfuscate == true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5f820a225c981b8499016958e6c69747
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -1,16 +1,47 @@
|
||||||
using dnlib.DotNet;
|
using dnlib.DotNet;
|
||||||
using dnlib.DotNet.Emit;
|
using dnlib.DotNet.Emit;
|
||||||
|
using Obfuz.Emit;
|
||||||
|
using Obfuz.ObfusPasses.ExprObfus.Obfuscators;
|
||||||
|
using Obfuz.Settings;
|
||||||
|
using Obfuz.Utils;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Obfuz.ObfusPasses.ExprObfus
|
namespace Obfuz.ObfusPasses.ExprObfus
|
||||||
{
|
{
|
||||||
public class ExprObfusPass : InstructionObfuscationPassBase
|
class ExprObfusPass : ObfuscationMethodPassBase
|
||||||
{
|
{
|
||||||
|
private readonly ExprObfuscationSettingsFacade _settings;
|
||||||
|
private IObfuscationPolicy _obfuscationPolicy;
|
||||||
|
private IObfuscator _obfuscator;
|
||||||
|
|
||||||
|
public ExprObfusPass(ExprObfuscationSettingsFacade settings)
|
||||||
|
{
|
||||||
|
_settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
public override ObfuscationPassType Type => ObfuscationPassType.ExprObfus;
|
public override ObfuscationPassType Type => ObfuscationPassType.ExprObfus;
|
||||||
|
|
||||||
public override void Start()
|
public override void Start()
|
||||||
{
|
{
|
||||||
|
ObfuscationPassContext ctx = ObfuscationPassContext.Current;
|
||||||
|
_obfuscationPolicy = new ConfigurableObfuscationPolicy(
|
||||||
|
ctx.coreSettings.assembliesToObfuscate,
|
||||||
|
_settings.ruleFiles);
|
||||||
|
_obfuscator = CreateObfuscator(ctx.encryptionScopeProvider, ctx.moduleEntityManager, _settings.obfuscationLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IObfuscator CreateObfuscator(EncryptionScopeProvider encryptionScopeProvider, GroupByModuleEntityManager moduleEntityManager, ObfuscationLevel level)
|
||||||
|
{
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case ObfuscationLevel.None: return new NoneObfuscator();
|
||||||
|
case ObfuscationLevel.Basic:return new BasicObfuscator(encryptionScopeProvider, moduleEntityManager);
|
||||||
|
case ObfuscationLevel.Advanced: return new AdvancedObfuscator(encryptionScopeProvider, moduleEntityManager);
|
||||||
|
case ObfuscationLevel.MostAdvanced: return new MostAdvancedObfuscator(encryptionScopeProvider, moduleEntityManager);
|
||||||
|
default: throw new System.ArgumentOutOfRangeException(nameof(level), level, "Unknown obfuscation level");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Stop()
|
public override void Stop()
|
||||||
|
@ -20,12 +51,90 @@ namespace Obfuz.ObfusPasses.ExprObfus
|
||||||
|
|
||||||
protected override bool NeedObfuscateMethod(MethodDef method)
|
protected override bool NeedObfuscateMethod(MethodDef method)
|
||||||
{
|
{
|
||||||
|
return _settings.obfuscationLevel != ObfuscationLevel.None && _obfuscationPolicy.NeedObfuscate(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool TryObfuscateInstruction(MethodDef callingMethod, InstructionParameterInfo pi, LocalVariableAllocator localVariableAllocator, IRandom localRandom, Instruction inst, List<Instruction> outputInstructions)
|
||||||
|
{
|
||||||
|
Debug.Log($"Obfuscating instruction: {inst} in method: {callingMethod.FullName}");
|
||||||
|
switch (inst.OpCode.Code)
|
||||||
|
{
|
||||||
|
case Code.Neg:
|
||||||
|
{
|
||||||
|
return localRandom.NextInPercentage(_settings.obfuscationPercentage) && _obfuscator.ObfuscateBasicUnaryOp(callingMethod, inst, pi.op1, pi.retType, localVariableAllocator, outputInstructions);
|
||||||
|
}
|
||||||
|
case Code.Add:
|
||||||
|
case Code.Sub:
|
||||||
|
case Code.Mul:
|
||||||
|
case Code.Div:
|
||||||
|
case Code.Div_Un:
|
||||||
|
case Code.Rem:
|
||||||
|
case Code.Rem_Un:
|
||||||
|
{
|
||||||
|
return localRandom.NextInPercentage(_settings.obfuscationPercentage) && _obfuscator.ObfuscateBasicBinOp(callingMethod, inst, pi.op1, pi.op2, pi.retType, localVariableAllocator, outputInstructions);
|
||||||
|
}
|
||||||
|
case Code.And:
|
||||||
|
case Code.Or:
|
||||||
|
case Code.Xor:
|
||||||
|
{
|
||||||
|
return localRandom.NextInPercentage(_settings.obfuscationPercentage) && _obfuscator.ObfuscateBinBitwiseOp(callingMethod, inst, pi.op1, pi.op2, pi.retType, localVariableAllocator, outputInstructions);
|
||||||
|
}
|
||||||
|
case Code.Not:
|
||||||
|
{
|
||||||
|
return localRandom.NextInPercentage(_settings.obfuscationPercentage) && _obfuscator.ObfuscateUnaryBitwiseOp(callingMethod, inst, pi.op1, pi.retType, localVariableAllocator, outputInstructions);
|
||||||
|
}
|
||||||
|
case Code.Shl:
|
||||||
|
case Code.Shr:
|
||||||
|
case Code.Shr_Un:
|
||||||
|
{
|
||||||
|
return localRandom.NextInPercentage(_settings.obfuscationPercentage) && _obfuscator.ObfuscateBitShiftOp(callingMethod, inst, pi.op1, pi.op2, pi.retType, localVariableAllocator, outputInstructions);
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool TryObfuscateInstruction(MethodDef callingMethod, Instruction inst, IList<Instruction> instructions, int instructionIndex, List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions)
|
protected override void ObfuscateData(MethodDef method)
|
||||||
{
|
{
|
||||||
return false;
|
Debug.Log($"Obfuscating method: {method.FullName} with ExprObfusPass");
|
||||||
|
var calc = new EvalStackCalculator(method);
|
||||||
|
var localVarAllocator = new LocalVariableAllocator(method);
|
||||||
|
IList<Instruction> instructions = method.Body.Instructions;
|
||||||
|
var outputInstructions = new List<Instruction>();
|
||||||
|
var totalFinalInstructions = new List<Instruction>();
|
||||||
|
var encryptionScope = ObfuscationPassContext.Current.encryptionScopeProvider.GetScope(method.Module);
|
||||||
|
var localRandom = encryptionScope.localRandomCreator(MethodEqualityComparer.CompareDeclaringTypes.GetHashCode(method));
|
||||||
|
for (int i = 0; i < instructions.Count; i++)
|
||||||
|
{
|
||||||
|
Instruction inst = instructions[i];
|
||||||
|
bool add = false;
|
||||||
|
if (calc.TryGetParameterInfo(inst, out InstructionParameterInfo pi))
|
||||||
|
{
|
||||||
|
outputInstructions.Clear();
|
||||||
|
if (TryObfuscateInstruction(method, pi, localVarAllocator, localRandom, inst, outputInstructions))
|
||||||
|
{
|
||||||
|
// current instruction may be the target of control flow instruction, so we can't remove it directly.
|
||||||
|
// we replace it with nop now, then remove it in CleanUpInstructionPass
|
||||||
|
inst.OpCode = outputInstructions[0].OpCode;
|
||||||
|
inst.Operand = outputInstructions[0].Operand;
|
||||||
|
totalFinalInstructions.Add(inst);
|
||||||
|
for (int k = 1; k < outputInstructions.Count; k++)
|
||||||
|
{
|
||||||
|
totalFinalInstructions.Add(outputInstructions[k]);
|
||||||
|
}
|
||||||
|
add = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!add)
|
||||||
|
{
|
||||||
|
totalFinalInstructions.Add(inst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
instructions.Clear();
|
||||||
|
foreach (var obInst in totalFinalInstructions)
|
||||||
|
{
|
||||||
|
instructions.Add(obInst);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
using dnlib.DotNet;
|
||||||
|
using dnlib.DotNet.Emit;
|
||||||
|
using Obfuz.Emit;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Obfuz.ObfusPasses.ExprObfus
|
||||||
|
{
|
||||||
|
interface IObfuscator
|
||||||
|
{
|
||||||
|
bool ObfuscateBasicUnaryOp(MethodDef method, Instruction inst, EvalDataType op, EvalDataType ret, LocalVariableAllocator localVariableAllocator, List<Instruction> outputInsts);
|
||||||
|
|
||||||
|
bool ObfuscateBasicBinOp(MethodDef method, Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, LocalVariableAllocator localVariableAllocator, List<Instruction> outputInsts);
|
||||||
|
|
||||||
|
bool ObfuscateUnaryBitwiseOp(MethodDef method, Instruction inst, EvalDataType op, EvalDataType ret, LocalVariableAllocator localVariableAllocator, List<Instruction> outputInsts);
|
||||||
|
|
||||||
|
bool ObfuscateBinBitwiseOp(MethodDef method, Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, LocalVariableAllocator localVariableAllocator, List<Instruction> outputInsts);
|
||||||
|
|
||||||
|
bool ObfuscateBitShiftOp(MethodDef method, Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, LocalVariableAllocator localVariableAllocator, List<Instruction> outputInsts);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class ObfuscatorBase : IObfuscator
|
||||||
|
{
|
||||||
|
public abstract bool ObfuscateBasicUnaryOp(MethodDef method, Instruction inst, EvalDataType op, EvalDataType ret, LocalVariableAllocator localVariableAllocator, List<Instruction> outputInsts);
|
||||||
|
public abstract bool ObfuscateBasicBinOp(MethodDef method, Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, LocalVariableAllocator localVariableAllocator, List<Instruction> outputInsts);
|
||||||
|
public abstract bool ObfuscateUnaryBitwiseOp(MethodDef method, Instruction inst, EvalDataType op, EvalDataType ret, LocalVariableAllocator localVariableAllocator, List<Instruction> outputInsts);
|
||||||
|
public abstract bool ObfuscateBinBitwiseOp(MethodDef method, Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, LocalVariableAllocator localVariableAllocator, List<Instruction> outputInsts);
|
||||||
|
public abstract bool ObfuscateBitShiftOp(MethodDef method, Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, LocalVariableAllocator localVariableAllocator, List<Instruction> outputInsts);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a88981a87bcd9e84b883e39c81cfbf44
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4c5dc8736831c9f4b934c69f7894a412
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,12 @@
|
||||||
|
using Obfuz.Emit;
|
||||||
|
|
||||||
|
namespace Obfuz.ObfusPasses.ExprObfus.Obfuscators
|
||||||
|
{
|
||||||
|
class AdvancedObfuscator : BasicObfuscator
|
||||||
|
{
|
||||||
|
public AdvancedObfuscator(EncryptionScopeProvider encryptionScopeProvider, GroupByModuleEntityManager moduleEntityManager)
|
||||||
|
: base(encryptionScopeProvider, moduleEntityManager)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ef717515402ca2f41a52db7ea1300f32
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,261 @@
|
||||||
|
using dnlib.DotNet;
|
||||||
|
using dnlib.DotNet.Emit;
|
||||||
|
using Obfuz.Emit;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Obfuz.ObfusPasses.ExprObfus.Obfuscators
|
||||||
|
{
|
||||||
|
|
||||||
|
class BasicObfuscator : ObfuscatorBase
|
||||||
|
{
|
||||||
|
private readonly EncryptionScopeProvider _encryptionScopeProvider;
|
||||||
|
private readonly GroupByModuleEntityManager _moduleEntityManager;
|
||||||
|
|
||||||
|
public BasicObfuscator(EncryptionScopeProvider encryptionScopeProvider, GroupByModuleEntityManager moduleEntityManager)
|
||||||
|
{
|
||||||
|
_encryptionScopeProvider = encryptionScopeProvider;
|
||||||
|
_moduleEntityManager = moduleEntityManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DefaultMetadataImporter GetModuleMetadataImporter(ModuleDef module)
|
||||||
|
{
|
||||||
|
return _moduleEntityManager.GetDefaultModuleMetadataImporter(module, _encryptionScopeProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IMethod GetMethod(DefaultMetadataImporter importer, Code code, EvalDataType op1)
|
||||||
|
{
|
||||||
|
switch (code)
|
||||||
|
{
|
||||||
|
case Code.Add:
|
||||||
|
{
|
||||||
|
switch (op1)
|
||||||
|
{
|
||||||
|
case EvalDataType.Int32: return importer.AddInt;
|
||||||
|
case EvalDataType.Int64: return importer.AddLong;
|
||||||
|
case EvalDataType.Float: return importer.AddFloat;
|
||||||
|
case EvalDataType.Double: return importer.AddDouble;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Code.Sub:
|
||||||
|
{
|
||||||
|
switch (op1)
|
||||||
|
{
|
||||||
|
case EvalDataType.Int32: return importer.SubtractInt;
|
||||||
|
case EvalDataType.Int64: return importer.SubtractLong;
|
||||||
|
case EvalDataType.Float: return importer.SubtractFloat;
|
||||||
|
case EvalDataType.Double: return importer.SubtractDouble;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Code.Mul:
|
||||||
|
{
|
||||||
|
switch (op1)
|
||||||
|
{
|
||||||
|
case EvalDataType.Int32: return importer.MultiplyInt;
|
||||||
|
case EvalDataType.Int64: return importer.MultiplyLong;
|
||||||
|
case EvalDataType.Float: return importer.MultiplyFloat;
|
||||||
|
case EvalDataType.Double: return importer.MultiplyDouble;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Code.Div:
|
||||||
|
{
|
||||||
|
switch (op1)
|
||||||
|
{
|
||||||
|
case EvalDataType.Int32: return importer.DivideInt;
|
||||||
|
case EvalDataType.Int64: return importer.DivideLong;
|
||||||
|
case EvalDataType.Float: return importer.DivideFloat;
|
||||||
|
case EvalDataType.Double: return importer.DivideDouble;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Code.Div_Un:
|
||||||
|
{
|
||||||
|
switch (op1)
|
||||||
|
{
|
||||||
|
case EvalDataType.Int32: return importer.DivideUnInt;
|
||||||
|
case EvalDataType.Int64: return importer.DivideUnLong;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Code.Rem:
|
||||||
|
{
|
||||||
|
switch (op1)
|
||||||
|
{
|
||||||
|
case EvalDataType.Int32: return importer.RemInt;
|
||||||
|
case EvalDataType.Int64: return importer.RemLong;
|
||||||
|
case EvalDataType.Float: return importer.RemFloat;
|
||||||
|
case EvalDataType.Double: return importer.RemDouble;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Code.Rem_Un:
|
||||||
|
{
|
||||||
|
switch (op1)
|
||||||
|
{
|
||||||
|
case EvalDataType.Int32: return importer.RemUnInt;
|
||||||
|
case EvalDataType.Int64: return importer.RemUnLong;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Code.Neg:
|
||||||
|
{
|
||||||
|
switch (op1)
|
||||||
|
{
|
||||||
|
case EvalDataType.Int32: return importer.NegInt;
|
||||||
|
case EvalDataType.Int64: return importer.NegLong;
|
||||||
|
case EvalDataType.Float: return importer.NegFloat;
|
||||||
|
case EvalDataType.Double: return importer.NegDouble;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Code.And:
|
||||||
|
{
|
||||||
|
switch (op1)
|
||||||
|
{
|
||||||
|
case EvalDataType.Int32: return importer.AndInt;
|
||||||
|
case EvalDataType.Int64: return importer.AndLong;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Code.Or:
|
||||||
|
{
|
||||||
|
switch (op1)
|
||||||
|
{
|
||||||
|
case EvalDataType.Int32: return importer.OrInt;
|
||||||
|
case EvalDataType.Int64: return importer.OrLong;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Code.Xor:
|
||||||
|
{
|
||||||
|
switch (op1)
|
||||||
|
{
|
||||||
|
case EvalDataType.Int32: return importer.XorInt;
|
||||||
|
case EvalDataType.Int64: return importer.XorLong;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Code.Not:
|
||||||
|
{
|
||||||
|
switch (op1)
|
||||||
|
{
|
||||||
|
case EvalDataType.Int32: return importer.NotInt;
|
||||||
|
case EvalDataType.Int64: return importer.NotLong;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Code.Shl:
|
||||||
|
{
|
||||||
|
switch (op1)
|
||||||
|
{
|
||||||
|
case EvalDataType.Int32: return importer.ShlInt;
|
||||||
|
case EvalDataType.Int64: return importer.ShlLong;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Code.Shr:
|
||||||
|
{
|
||||||
|
switch (op1)
|
||||||
|
{
|
||||||
|
case EvalDataType.Int32: return importer.ShrInt;
|
||||||
|
case EvalDataType.Int64: return importer.ShrLong;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Code.Shr_Un:
|
||||||
|
{
|
||||||
|
switch (op1)
|
||||||
|
{
|
||||||
|
case EvalDataType.Int32: return importer.ShrUnInt;
|
||||||
|
case EvalDataType.Int64: return importer.ShrUnLong;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ObfuscateBasicUnaryOp(MethodDef method, Instruction inst, EvalDataType op, EvalDataType ret, LocalVariableAllocator localVariableAllocator, List<Instruction> outputInsts)
|
||||||
|
{
|
||||||
|
DefaultMetadataImporter importer = GetModuleMetadataImporter(method.Module);
|
||||||
|
IMethod opMethod = GetMethod(importer, inst.OpCode.Code, op);
|
||||||
|
if (opMethod == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outputInsts.Add(Instruction.Create(OpCodes.Call, opMethod));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ObfuscateBasicBinOp(MethodDef method, Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, LocalVariableAllocator localVariableAllocator, List<Instruction> outputInsts)
|
||||||
|
{
|
||||||
|
if (op1 != op2)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"BasicObfuscator: Cannot obfuscate binary operation {inst.OpCode.Code} with different operand types: op1={op1}, op2={op2}, ret={ret}. This is a limitation of the BasicObfuscator.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DefaultMetadataImporter importer = GetModuleMetadataImporter(method.Module);
|
||||||
|
IMethod opMethod = GetMethod(importer, inst.OpCode.Code, op1);
|
||||||
|
if (opMethod == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outputInsts.Add(Instruction.Create(OpCodes.Call, opMethod));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ObfuscateUnaryBitwiseOp(MethodDef method, Instruction inst, EvalDataType op, EvalDataType ret, LocalVariableAllocator localVariableAllocator, List<Instruction> outputInsts)
|
||||||
|
{
|
||||||
|
DefaultMetadataImporter importer = GetModuleMetadataImporter(method.Module);
|
||||||
|
IMethod opMethod = GetMethod(importer, inst.OpCode.Code, op);
|
||||||
|
if (opMethod == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outputInsts.Add(Instruction.Create(OpCodes.Call, opMethod));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ObfuscateBinBitwiseOp(MethodDef method, Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, LocalVariableAllocator localVariableAllocator, List<Instruction> outputInsts)
|
||||||
|
{
|
||||||
|
if (op1 != op2)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"BasicObfuscator: Cannot obfuscate binary operation {inst.OpCode.Code} with different operand types: op1={op1}, op2={op2}, ret={ret}. This is a limitation of the BasicObfuscator.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DefaultMetadataImporter importer = GetModuleMetadataImporter(method.Module);
|
||||||
|
IMethod opMethod = GetMethod(importer, inst.OpCode.Code, op1);
|
||||||
|
if (opMethod == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outputInsts.Add(Instruction.Create(OpCodes.Call, opMethod));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ObfuscateBitShiftOp(MethodDef method, Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, LocalVariableAllocator localVariableAllocator, List<Instruction> outputInsts)
|
||||||
|
{
|
||||||
|
if (op2 != EvalDataType.Int32)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"BasicObfuscator: Cannot obfuscate binary operation {inst.OpCode.Code} with operand type {op2}. This is a limitation of the BasicObfuscator.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DefaultMetadataImporter importer = GetModuleMetadataImporter(method.Module);
|
||||||
|
IMethod opMethod = GetMethod(importer, inst.OpCode.Code, op1);
|
||||||
|
if (opMethod == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outputInsts.Add(Instruction.Create(OpCodes.Call, opMethod));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 578caeae17526b54c9ff1979d897feb7
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,12 @@
|
||||||
|
using Obfuz.Emit;
|
||||||
|
|
||||||
|
namespace Obfuz.ObfusPasses.ExprObfus.Obfuscators
|
||||||
|
{
|
||||||
|
class MostAdvancedObfuscator : AdvancedObfuscator
|
||||||
|
{
|
||||||
|
public MostAdvancedObfuscator(EncryptionScopeProvider encryptionScopeProvider, GroupByModuleEntityManager moduleEntityManager)
|
||||||
|
: base(encryptionScopeProvider, moduleEntityManager)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: af5946ac6cb0a8b4fa75321439785133
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,35 @@
|
||||||
|
using dnlib.DotNet;
|
||||||
|
using dnlib.DotNet.Emit;
|
||||||
|
using Obfuz.Emit;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Obfuz.ObfusPasses.ExprObfus.Obfuscators
|
||||||
|
{
|
||||||
|
class NoneObfuscator : ObfuscatorBase
|
||||||
|
{
|
||||||
|
public override bool ObfuscateBasicUnaryOp(MethodDef method, Instruction inst, EvalDataType op, EvalDataType ret, LocalVariableAllocator localVariableAllocator, List<Instruction> outputInsts)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ObfuscateBasicBinOp(MethodDef method, Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, LocalVariableAllocator localVariableAllocator, List<Instruction> outputInsts)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ObfuscateUnaryBitwiseOp(MethodDef method, Instruction inst, EvalDataType op, EvalDataType ret, LocalVariableAllocator localVariableAllocator, List<Instruction> outputInsts)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ObfuscateBinBitwiseOp(MethodDef method, Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, LocalVariableAllocator localVariableAllocator, List<Instruction> outputInsts)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ObfuscateBitShiftOp(MethodDef method, Instruction inst, EvalDataType op1, EvalDataType op2, EvalDataType ret, LocalVariableAllocator localVariableAllocator, List<Instruction> outputInsts)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ca85b81393732984a9019bdbe5d9e9a7
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -5,50 +5,12 @@ using System.Linq;
|
||||||
|
|
||||||
namespace Obfuz.ObfusPasses
|
namespace Obfuz.ObfusPasses
|
||||||
{
|
{
|
||||||
public abstract class InstructionObfuscationPassBase : ObfuscationPassBase
|
public abstract class InstructionObfuscationPassBase : ObfuscationMethodPassBase
|
||||||
{
|
{
|
||||||
protected virtual bool ForceProcessAllAssembliesAndIgnoreAllPolicy => false;
|
|
||||||
|
|
||||||
protected abstract bool NeedObfuscateMethod(MethodDef method);
|
|
||||||
|
|
||||||
public override void Process()
|
|
||||||
{
|
|
||||||
var ctx = ObfuscationPassContext.Current;
|
|
||||||
var modules = ForceProcessAllAssembliesAndIgnoreAllPolicy ? ctx.allObfuscationRelativeModules : ctx.modulesToObfuscate;
|
|
||||||
ObfuscationMethodWhitelist whiteList = ctx.whiteList;
|
|
||||||
ConfigurablePassPolicy passPolicy = ctx.passPolicy;
|
|
||||||
foreach (ModuleDef mod in modules)
|
|
||||||
{
|
|
||||||
if (!ForceProcessAllAssembliesAndIgnoreAllPolicy && whiteList.IsInWhiteList(mod))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// ToArray to avoid modify list exception
|
|
||||||
foreach (TypeDef type in mod.GetTypes().ToArray())
|
|
||||||
{
|
|
||||||
if (!ForceProcessAllAssembliesAndIgnoreAllPolicy && whiteList.IsInWhiteList(type))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// ToArray to avoid modify list exception
|
|
||||||
foreach (MethodDef method in type.Methods.ToArray())
|
|
||||||
{
|
|
||||||
if (!method.HasBody || (!ForceProcessAllAssembliesAndIgnoreAllPolicy && (ctx.whiteList.IsInWhiteList(method) || !Support(passPolicy.GetMethodObfuscationPasses(method)) || !NeedObfuscateMethod(method))))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// TODO if isGeneratedBy Obfuscator, continue
|
|
||||||
ObfuscateData(method);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected abstract bool TryObfuscateInstruction(MethodDef callingMethod, Instruction inst, IList<Instruction> instructions, int instructionIndex,
|
protected abstract bool TryObfuscateInstruction(MethodDef callingMethod, Instruction inst, IList<Instruction> instructions, int instructionIndex,
|
||||||
List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions);
|
List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions);
|
||||||
|
|
||||||
private void ObfuscateData(MethodDef method)
|
protected override void ObfuscateData(MethodDef method)
|
||||||
{
|
{
|
||||||
IList<Instruction> instructions = method.Body.Instructions;
|
IList<Instruction> instructions = method.Body.Instructions;
|
||||||
var outputInstructions = new List<Instruction>();
|
var outputInstructions = new List<Instruction>();
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
using dnlib.DotNet;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Obfuz.ObfusPasses
|
||||||
|
{
|
||||||
|
public abstract class ObfuscationMethodPassBase : ObfuscationPassBase
|
||||||
|
{
|
||||||
|
protected virtual bool ForceProcessAllAssembliesAndIgnoreAllPolicy => false;
|
||||||
|
|
||||||
|
protected abstract bool NeedObfuscateMethod(MethodDef method);
|
||||||
|
|
||||||
|
protected abstract void ObfuscateData(MethodDef method);
|
||||||
|
|
||||||
|
public override void Process()
|
||||||
|
{
|
||||||
|
var ctx = ObfuscationPassContext.Current;
|
||||||
|
var modules = ForceProcessAllAssembliesAndIgnoreAllPolicy ? ctx.allObfuscationRelativeModules : ctx.modulesToObfuscate;
|
||||||
|
ObfuscationMethodWhitelist whiteList = ctx.whiteList;
|
||||||
|
ConfigurablePassPolicy passPolicy = ctx.passPolicy;
|
||||||
|
foreach (ModuleDef mod in modules)
|
||||||
|
{
|
||||||
|
if (!ForceProcessAllAssembliesAndIgnoreAllPolicy && whiteList.IsInWhiteList(mod))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// ToArray to avoid modify list exception
|
||||||
|
foreach (TypeDef type in mod.GetTypes().ToArray())
|
||||||
|
{
|
||||||
|
if (!ForceProcessAllAssembliesAndIgnoreAllPolicy && whiteList.IsInWhiteList(type))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// ToArray to avoid modify list exception
|
||||||
|
foreach (MethodDef method in type.Methods.ToArray())
|
||||||
|
{
|
||||||
|
if (!method.HasBody || (!ForceProcessAllAssembliesAndIgnoreAllPolicy && (ctx.whiteList.IsInWhiteList(method) || !Support(passPolicy.GetMethodObfuscationPasses(method)) || !NeedObfuscateMethod(method))))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// TODO if isGeneratedBy Obfuscator, continue
|
||||||
|
ObfuscateData(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 84b0592af70b0cc41b546cf8ac39f889
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -173,14 +173,14 @@ namespace Obfuz
|
||||||
{
|
{
|
||||||
builder.AddPass(new FieldEncryptPass(settings.fieldEncryptSettings.ToFacade()));
|
builder.AddPass(new FieldEncryptPass(settings.fieldEncryptSettings.ToFacade()));
|
||||||
}
|
}
|
||||||
|
if (obfuscationPasses.HasFlag(ObfuscationPassType.ExprObfus))
|
||||||
|
{
|
||||||
|
builder.AddPass(new ExprObfusPass(settings.exprObfusSettings.ToFacade()));
|
||||||
|
}
|
||||||
if (obfuscationPasses.HasFlag(ObfuscationPassType.CallObfus))
|
if (obfuscationPasses.HasFlag(ObfuscationPassType.CallObfus))
|
||||||
{
|
{
|
||||||
builder.AddPass(new CallObfusPass(settings.callObfusSettings.ToFacade()));
|
builder.AddPass(new CallObfusPass(settings.callObfusSettings.ToFacade()));
|
||||||
}
|
}
|
||||||
if (obfuscationPasses.HasFlag(ObfuscationPassType.ExprObfus))
|
|
||||||
{
|
|
||||||
builder.AddPass(new ExprObfusPass());
|
|
||||||
}
|
|
||||||
if (obfuscationPasses.HasFlag(ObfuscationPassType.SymbolObfus))
|
if (obfuscationPasses.HasFlag(ObfuscationPassType.SymbolObfus))
|
||||||
{
|
{
|
||||||
builder.AddPass(new SymbolObfusPass(settings.symbolObfusSettings.ToFacade()));
|
builder.AddPass(new SymbolObfusPass(settings.symbolObfusSettings.ToFacade()));
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Obfuz.Settings
|
||||||
|
{
|
||||||
|
public enum ObfuscationLevel
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Basic = 1,
|
||||||
|
Advanced = 2,
|
||||||
|
MostAdvanced = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ExprObfuscationSettingsFacade
|
||||||
|
{
|
||||||
|
public ObfuscationLevel obfuscationLevel;
|
||||||
|
public float obfuscationPercentage;
|
||||||
|
public List<string> ruleFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class ExprObfuscationSettings
|
||||||
|
{
|
||||||
|
[Tooltip("Obfuscation level")]
|
||||||
|
public ObfuscationLevel obfuscationLevel = ObfuscationLevel.Basic;
|
||||||
|
|
||||||
|
[Tooltip("percentage of obfuscation, 0.0 - 1.0, 0.5 means 50% of expressions will be obfuscated")]
|
||||||
|
[Range(0.1f, 1.0f)]
|
||||||
|
public float obfuscationPercentage = 0.5f;
|
||||||
|
|
||||||
|
[Tooltip("rule config xml files")]
|
||||||
|
public string[] ruleFiles;
|
||||||
|
|
||||||
|
public ExprObfuscationSettingsFacade ToFacade()
|
||||||
|
{
|
||||||
|
return new ExprObfuscationSettingsFacade
|
||||||
|
{
|
||||||
|
obfuscationLevel = obfuscationLevel,
|
||||||
|
obfuscationPercentage = obfuscationPercentage,
|
||||||
|
ruleFiles = new List<string>(ruleFiles),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d63d19f2b1a9f7c4b9812577d215c367
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -34,6 +34,9 @@ namespace Obfuz.Settings
|
||||||
[Tooltip("call obfuscation settings")]
|
[Tooltip("call obfuscation settings")]
|
||||||
public CallObfuscationSettings callObfusSettings;
|
public CallObfuscationSettings callObfusSettings;
|
||||||
|
|
||||||
|
[Tooltip("expression obfuscation settings")]
|
||||||
|
public ExprObfuscationSettings exprObfusSettings;
|
||||||
|
|
||||||
public string ObfuzRootDir => $"Library/Obfuz";
|
public string ObfuzRootDir => $"Library/Obfuz";
|
||||||
|
|
||||||
public string GetObfuscatedAssemblyOutputPath(BuildTarget target)
|
public string GetObfuscatedAssemblyOutputPath(BuildTarget target)
|
||||||
|
|
|
@ -34,6 +34,7 @@ namespace Obfuz.Settings
|
||||||
private SerializedProperty _constEncryptSettings;
|
private SerializedProperty _constEncryptSettings;
|
||||||
private SerializedProperty _fieldEncryptSettings;
|
private SerializedProperty _fieldEncryptSettings;
|
||||||
private SerializedProperty _callObfusSettings;
|
private SerializedProperty _callObfusSettings;
|
||||||
|
private SerializedProperty _exprObfusSettings;
|
||||||
|
|
||||||
public ObfuzSettingsProvider() : base("Project/Obfuz", SettingsScope.Project)
|
public ObfuzSettingsProvider() : base("Project/Obfuz", SettingsScope.Project)
|
||||||
{
|
{
|
||||||
|
@ -64,6 +65,7 @@ namespace Obfuz.Settings
|
||||||
|
|
||||||
_symbolObfusSettings = _serializedObject.FindProperty("symbolObfusSettings");
|
_symbolObfusSettings = _serializedObject.FindProperty("symbolObfusSettings");
|
||||||
_constEncryptSettings = _serializedObject.FindProperty("constEncryptSettings");
|
_constEncryptSettings = _serializedObject.FindProperty("constEncryptSettings");
|
||||||
|
_exprObfusSettings = _serializedObject.FindProperty("exprObfusSettings");
|
||||||
_fieldEncryptSettings = _serializedObject.FindProperty("fieldEncryptSettings");
|
_fieldEncryptSettings = _serializedObject.FindProperty("fieldEncryptSettings");
|
||||||
_callObfusSettings = _serializedObject.FindProperty("callObfusSettings");
|
_callObfusSettings = _serializedObject.FindProperty("callObfusSettings");
|
||||||
}
|
}
|
||||||
|
@ -86,6 +88,7 @@ namespace Obfuz.Settings
|
||||||
|
|
||||||
EditorGUILayout.PropertyField(_symbolObfusSettings);
|
EditorGUILayout.PropertyField(_symbolObfusSettings);
|
||||||
EditorGUILayout.PropertyField(_constEncryptSettings);
|
EditorGUILayout.PropertyField(_constEncryptSettings);
|
||||||
|
EditorGUILayout.PropertyField(_exprObfusSettings);
|
||||||
EditorGUILayout.PropertyField(_fieldEncryptSettings);
|
EditorGUILayout.PropertyField(_fieldEncryptSettings);
|
||||||
EditorGUILayout.PropertyField(_callObfusSettings);
|
EditorGUILayout.PropertyField(_callObfusSettings);
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@ namespace Obfuz.Utils
|
||||||
{
|
{
|
||||||
public sealed class GenericArgumentContext
|
public sealed class GenericArgumentContext
|
||||||
{
|
{
|
||||||
List<TypeSig> typeArgsStack;
|
public readonly List<TypeSig> typeArgsStack;
|
||||||
List<TypeSig> methodArgsStack;
|
public readonly List<TypeSig> methodArgsStack;
|
||||||
|
|
||||||
public GenericArgumentContext(IList<TypeSig> typeArgsStack, IList<TypeSig> methodArgsStack)
|
public GenericArgumentContext(IList<TypeSig> typeArgsStack, IList<TypeSig> methodArgsStack)
|
||||||
{
|
{
|
||||||
|
@ -100,6 +100,10 @@ namespace Obfuz.Utils
|
||||||
|
|
||||||
private TypeSig Resolve(List<TypeSig> args, uint number)
|
private TypeSig Resolve(List<TypeSig> args, uint number)
|
||||||
{
|
{
|
||||||
|
if (args == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(args));
|
||||||
|
}
|
||||||
return args[(int)number];
|
return args[(int)number];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,5 +9,9 @@
|
||||||
int NextInt();
|
int NextInt();
|
||||||
|
|
||||||
long NextLong();
|
long NextLong();
|
||||||
|
|
||||||
|
float NextFloat();
|
||||||
|
|
||||||
|
bool NextInPercentage(float percentage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -518,13 +518,22 @@ namespace Obfuz.Utils
|
||||||
|
|
||||||
public static TypeSig Inflate(TypeSig sig, GenericArgumentContext ctx)
|
public static TypeSig Inflate(TypeSig sig, GenericArgumentContext ctx)
|
||||||
{
|
{
|
||||||
if (!sig.ContainsGenericParameter)
|
if (ctx == null || !sig.ContainsGenericParameter)
|
||||||
{
|
{
|
||||||
return sig;
|
return sig;
|
||||||
}
|
}
|
||||||
return ctx.Resolve(sig);
|
return ctx.Resolve(sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IList<TypeSig> TryInflate(IList<TypeSig> sig, GenericArgumentContext ctx)
|
||||||
|
{
|
||||||
|
if (sig == null || ctx == null)
|
||||||
|
{
|
||||||
|
return sig;
|
||||||
|
}
|
||||||
|
return sig.Select(s => Inflate(s, ctx)).ToList() ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static MethodSig InflateMethodSig(MethodSig methodSig, GenericArgumentContext genericArgumentContext)
|
public static MethodSig InflateMethodSig(MethodSig methodSig, GenericArgumentContext genericArgumentContext)
|
||||||
{
|
{
|
||||||
|
@ -564,7 +573,29 @@ namespace Obfuz.Utils
|
||||||
throw new NotSupportedException($"type:{type}");
|
throw new NotSupportedException($"type:{type}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MethodSig GetInflatedMethodSig(IMethod method)
|
public static GenericArgumentContext GetInflatedMemberRefGenericArgument(IMemberRefParent type, GenericArgumentContext ctx)
|
||||||
|
{
|
||||||
|
if (type is TypeDef typeDef)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (type is TypeRef typeRef)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (type is TypeSpec typeSpec)
|
||||||
|
{
|
||||||
|
GenericInstSig genericInstSig = typeSpec.TypeSig.ToGenericInstSig();
|
||||||
|
if (genericInstSig == null)
|
||||||
|
{
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
return new GenericArgumentContext(TryInflate(genericInstSig.GenericArguments, ctx), null);
|
||||||
|
}
|
||||||
|
throw new NotSupportedException($"type:{type}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MethodSig GetInflatedMethodSig(IMethod method, GenericArgumentContext ctx)
|
||||||
{
|
{
|
||||||
if (method is MethodDef methodDef)
|
if (method is MethodDef methodDef)
|
||||||
{
|
{
|
||||||
|
@ -572,24 +603,40 @@ namespace Obfuz.Utils
|
||||||
}
|
}
|
||||||
if (method is MemberRef memberRef)
|
if (method is MemberRef memberRef)
|
||||||
{
|
{
|
||||||
return InflateMethodSig(memberRef.MethodSig, new GenericArgumentContext(GetGenericArguments(memberRef.Class), null));
|
return InflateMethodSig(memberRef.MethodSig, GetInflatedMemberRefGenericArgument(memberRef.Class, ctx));
|
||||||
}
|
}
|
||||||
if (method is MethodSpec methodSpec)
|
if (method is MethodSpec methodSpec)
|
||||||
{
|
{
|
||||||
var genericInstMethodSig = methodSpec.GenericInstMethodSig;
|
var genericInstMethodSig = methodSpec.GenericInstMethodSig;
|
||||||
if (methodSpec.Method is MethodDef methodDef2)
|
if (methodSpec.Method is MethodDef methodDef2)
|
||||||
{
|
{
|
||||||
return InflateMethodSig(methodDef2.MethodSig, new GenericArgumentContext(null, genericInstMethodSig.GenericArguments));
|
return InflateMethodSig(methodDef2.MethodSig, new GenericArgumentContext(null, TryInflate(genericInstMethodSig.GenericArguments, ctx)));
|
||||||
}
|
}
|
||||||
if (methodSpec.Method is MemberRef memberRef2)
|
if (methodSpec.Method is MemberRef memberRef2)
|
||||||
{
|
{
|
||||||
return InflateMethodSig(memberRef2.MethodSig, new GenericArgumentContext(GetGenericArguments(memberRef2.Class), genericInstMethodSig.GenericArguments));
|
return InflateMethodSig(memberRef2.MethodSig, new GenericArgumentContext(
|
||||||
|
GetInflatedMemberRefGenericArgument(memberRef2.Class, ctx)?.typeArgsStack,
|
||||||
|
TryInflate(genericInstMethodSig.GenericArguments, ctx)));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
throw new NotSupportedException($" method: {method}");
|
throw new NotSupportedException($" method: {method}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static TypeSig InflateFieldSig(IField field, GenericArgumentContext ctx)
|
||||||
|
{
|
||||||
|
if (field is FieldDef fieldDef)
|
||||||
|
{
|
||||||
|
return fieldDef.FieldType;
|
||||||
|
}
|
||||||
|
if (field is MemberRef memberRef)
|
||||||
|
{
|
||||||
|
return Inflate(memberRef.FieldSig.Type, new GenericArgumentContext(TryInflate(GetGenericArguments(memberRef.Class), ctx), null));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception($"unknown field:{field}");
|
||||||
|
}
|
||||||
|
|
||||||
public static ThisArgType GetThisArgType(IMethod method)
|
public static ThisArgType GetThisArgType(IMethod method)
|
||||||
{
|
{
|
||||||
if (!method.MethodSig.HasThis)
|
if (!method.MethodSig.HasThis)
|
||||||
|
|
|
@ -47,5 +47,15 @@
|
||||||
{
|
{
|
||||||
return ((long)NextInt() << 32) | (uint)NextInt();
|
return ((long)NextInt() << 32) | (uint)NextInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float NextFloat()
|
||||||
|
{
|
||||||
|
return (float)((double)NextInt() / int.MaxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool NextInPercentage(float percentage)
|
||||||
|
{
|
||||||
|
return NextFloat() < percentage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
namespace Obfuz
|
using System;
|
||||||
|
|
||||||
|
namespace Obfuz
|
||||||
{
|
{
|
||||||
|
|
||||||
public static class EncryptionService<T> where T : IEncryptionScope
|
public static class EncryptionService<T> where T : IEncryptionScope
|
||||||
|
@ -85,25 +87,25 @@
|
||||||
|
|
||||||
public static int DecryptFromRvaInt(byte[] data, int offset, int ops, int salt)
|
public static int DecryptFromRvaInt(byte[] data, int offset, int ops, int salt)
|
||||||
{
|
{
|
||||||
int encryptedValue = ConstUtility.GetInt(data, offset);
|
int encryptedValue = BitConverter.ToInt32(data, offset);
|
||||||
return Decrypt(encryptedValue, ops, salt);
|
return Decrypt(encryptedValue, ops, salt);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long DecryptFromRvaLong(byte[] data, int offset, int ops, int salt)
|
public static long DecryptFromRvaLong(byte[] data, int offset, int ops, int salt)
|
||||||
{
|
{
|
||||||
long encryptedValue = ConstUtility.GetLong(data, offset);
|
long encryptedValue = BitConverter.ToInt64(data, offset);
|
||||||
return Decrypt(encryptedValue, ops, salt);
|
return Decrypt(encryptedValue, ops, salt);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float DecryptFromRvaFloat(byte[] data, int offset, int ops, int salt)
|
public static float DecryptFromRvaFloat(byte[] data, int offset, int ops, int salt)
|
||||||
{
|
{
|
||||||
float encryptedValue = ConstUtility.GetFloat(data, offset);
|
float encryptedValue = BitConverter.ToSingle(data, offset);
|
||||||
return Decrypt(encryptedValue, ops, salt);
|
return Decrypt(encryptedValue, ops, salt);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double DecryptFromRvaDouble(byte[] data, int offset, int ops, int salt)
|
public static double DecryptFromRvaDouble(byte[] data, int offset, int ops, int salt)
|
||||||
{
|
{
|
||||||
double encryptedValue = ConstUtility.GetDouble(data, offset);
|
double encryptedValue = BitConverter.ToDouble(data, offset);
|
||||||
return Decrypt(encryptedValue, ops, salt);
|
return Decrypt(encryptedValue, ops, salt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,215 @@
|
||||||
|
namespace Obfuz
|
||||||
|
{
|
||||||
|
public static class ExprUtility
|
||||||
|
{
|
||||||
|
public static int Add(int a, int b)
|
||||||
|
{
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long Add(long a, long b)
|
||||||
|
{
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float Add(float a, float b)
|
||||||
|
{
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double Add(double a, double b)
|
||||||
|
{
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int Subtract(int a, int b)
|
||||||
|
{
|
||||||
|
return a - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long Subtract(long a, long b)
|
||||||
|
{
|
||||||
|
return a - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float Subtract(float a, float b)
|
||||||
|
{
|
||||||
|
return a - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double Subtract(double a, double b)
|
||||||
|
{
|
||||||
|
return a - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int Multiply(int a, int b)
|
||||||
|
{
|
||||||
|
return a * b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long Multiply(long a, long b)
|
||||||
|
{
|
||||||
|
return a * b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float Multiply(float a, float b)
|
||||||
|
{
|
||||||
|
return a * b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double Multiply(double a, double b)
|
||||||
|
{
|
||||||
|
return a * b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int Divide(int a, int b)
|
||||||
|
{
|
||||||
|
return a / b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long Divide(long a, long b)
|
||||||
|
{
|
||||||
|
return a / b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float Divide(float a, float b)
|
||||||
|
{
|
||||||
|
return a / b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double Divide(double a, double b)
|
||||||
|
{
|
||||||
|
return a / b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int DivideUn(int a, int b)
|
||||||
|
{
|
||||||
|
return (int)((uint)a / (uint)b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long DivideUn(long a, long b)
|
||||||
|
{
|
||||||
|
return (long)((ulong)a / (ulong)b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int Rem(int a, int b)
|
||||||
|
{
|
||||||
|
return a % b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long Rem(long a, long b)
|
||||||
|
{
|
||||||
|
return a % b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float Rem(float a, float b)
|
||||||
|
{
|
||||||
|
return a % b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double Rem(double a, double b)
|
||||||
|
{
|
||||||
|
return a % b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int RemUn(int a, int b)
|
||||||
|
{
|
||||||
|
return (int)((uint)a % (uint)b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long RemUn(long a, long b)
|
||||||
|
{
|
||||||
|
return (long)((ulong)a % (ulong)b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int Negate(int a)
|
||||||
|
{
|
||||||
|
return -a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long Negate(long a)
|
||||||
|
{
|
||||||
|
return -a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float Negate(float a)
|
||||||
|
{
|
||||||
|
return -a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double Negate(double a)
|
||||||
|
{
|
||||||
|
return -a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int And(int a, int b)
|
||||||
|
{
|
||||||
|
return a & b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long And(long a, long b)
|
||||||
|
{
|
||||||
|
return a & b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int Or(int a, int b)
|
||||||
|
{
|
||||||
|
return a | b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long Or(long a, long b)
|
||||||
|
{
|
||||||
|
return a | b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int Xor(int a, int b)
|
||||||
|
{
|
||||||
|
return a ^ b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long Xor(long a, long b)
|
||||||
|
{
|
||||||
|
return a ^ b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int Not(int a)
|
||||||
|
{
|
||||||
|
return ~a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long Not(long a)
|
||||||
|
{
|
||||||
|
return ~a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int ShiftLeft(int a, int b)
|
||||||
|
{
|
||||||
|
return a << b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long ShiftLeft(long a, int b)
|
||||||
|
{
|
||||||
|
return a << b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int ShiftRight(int a, int b)
|
||||||
|
{
|
||||||
|
return a >> b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long ShiftRight(long a, int b)
|
||||||
|
{
|
||||||
|
return a >> b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int ShiftRightUn(int a, int b)
|
||||||
|
{
|
||||||
|
return (int)((uint)a >> b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long ShiftRightUn(long a, int b)
|
||||||
|
{
|
||||||
|
return (long)((ulong)a >> b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9aba5050818a0224696fcf73752fb225
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Loading…
Reference in New Issue