实现 ExprObfus,暂时只支持BasicObfuscator

dev
walon 2025-06-17 20:21:28 +08:00
parent b84d158fac
commit 8288042e94
38 changed files with 2222 additions and 98 deletions

View File

@ -104,6 +104,7 @@ namespace Obfuz.Emit
{
splitPoints.Add(nextInst);
}
splitPoints.Add((Instruction)curInst.Operand);
break;
}
case FlowControl.Cond_Branch:

View File

@ -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));
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>));
_dynamicDefaultEncryptionServiceMetadataImporter = new EncryptionServiceMetadataImporter(mod, typeof(EncryptionService<DefaultDynamicEncryptionScope>));
if (_encryptionScopeProvider.IsDynamicSecretAssembly(mod))
@ -174,6 +263,51 @@ namespace Obfuz.Emit
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 CastLongAsDouble => _castLongAsDouble;
public IMethod CastFloatAsInt => _castFloatAsInt;
@ -209,5 +343,50 @@ namespace Obfuz.Emit
public IMethod DecryptFromRvaString => _defaultEncryptionServiceMetadataImporter.DecryptFromRvaString;
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;
}
}

View File

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f25425a3077f6db41873dee4223d0abc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 955da34fbde179641a94108ec53405ce
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -6,49 +6,14 @@ using System.Linq;
namespace Obfuz.ObfusPasses
{
public abstract class BasicBlockObfuscationPassBase : ObfuscationPassBase
public abstract class BasicBlockObfuscationPassBase : ObfuscationMethodPassBase
{
protected abstract bool NeedObfuscateMethod(MethodDef method);
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,
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);

View File

@ -157,7 +157,7 @@ namespace Obfuz.ObfusPasses.CallObfus
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.Params
switch (MetaUtil.GetThisArgType(method))

View File

@ -31,7 +31,7 @@ namespace Obfuz.ObfusPasses.CallObfus
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);
DefaultMetadataImporter importer = _moduleEntityManager.GetDefaultModuleMetadataImporter(callerMethod.Module, _encryptionScopeProvider);

View File

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5f820a225c981b8499016958e6c69747
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,16 +1,47 @@
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using Obfuz.Emit;
using Obfuz.ObfusPasses.ExprObfus.Obfuscators;
using Obfuz.Settings;
using Obfuz.Utils;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
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 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()
@ -20,12 +51,90 @@ namespace Obfuz.ObfusPasses.ExprObfus
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;
}
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);
}
}
}
}

View File

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a88981a87bcd9e84b883e39c81cfbf44
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4c5dc8736831c9f4b934c69f7894a412
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,12 @@
using Obfuz.Emit;
namespace Obfuz.ObfusPasses.ExprObfus.Obfuscators
{
class AdvancedObfuscator : BasicObfuscator
{
public AdvancedObfuscator(EncryptionScopeProvider encryptionScopeProvider, GroupByModuleEntityManager moduleEntityManager)
: base(encryptionScopeProvider, moduleEntityManager)
{
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ef717515402ca2f41a52db7ea1300f32
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 578caeae17526b54c9ff1979d897feb7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,12 @@
using Obfuz.Emit;
namespace Obfuz.ObfusPasses.ExprObfus.Obfuscators
{
class MostAdvancedObfuscator : AdvancedObfuscator
{
public MostAdvancedObfuscator(EncryptionScopeProvider encryptionScopeProvider, GroupByModuleEntityManager moduleEntityManager)
: base(encryptionScopeProvider, moduleEntityManager)
{
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: af5946ac6cb0a8b4fa75321439785133
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ca85b81393732984a9019bdbe5d9e9a7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -5,50 +5,12 @@ using System.Linq;
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,
List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions);
private void ObfuscateData(MethodDef method)
protected override void ObfuscateData(MethodDef method)
{
IList<Instruction> instructions = method.Body.Instructions;
var outputInstructions = new List<Instruction>();

View File

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 84b0592af70b0cc41b546cf8ac39f889
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -173,14 +173,14 @@ namespace Obfuz
{
builder.AddPass(new FieldEncryptPass(settings.fieldEncryptSettings.ToFacade()));
}
if (obfuscationPasses.HasFlag(ObfuscationPassType.ExprObfus))
{
builder.AddPass(new ExprObfusPass(settings.exprObfusSettings.ToFacade()));
}
if (obfuscationPasses.HasFlag(ObfuscationPassType.CallObfus))
{
builder.AddPass(new CallObfusPass(settings.callObfusSettings.ToFacade()));
}
if (obfuscationPasses.HasFlag(ObfuscationPassType.ExprObfus))
{
builder.AddPass(new ExprObfusPass());
}
if (obfuscationPasses.HasFlag(ObfuscationPassType.SymbolObfus))
{
builder.AddPass(new SymbolObfusPass(settings.symbolObfusSettings.ToFacade()));

View File

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d63d19f2b1a9f7c4b9812577d215c367
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -34,6 +34,9 @@ namespace Obfuz.Settings
[Tooltip("call obfuscation settings")]
public CallObfuscationSettings callObfusSettings;
[Tooltip("expression obfuscation settings")]
public ExprObfuscationSettings exprObfusSettings;
public string ObfuzRootDir => $"Library/Obfuz";
public string GetObfuscatedAssemblyOutputPath(BuildTarget target)

View File

@ -34,6 +34,7 @@ namespace Obfuz.Settings
private SerializedProperty _constEncryptSettings;
private SerializedProperty _fieldEncryptSettings;
private SerializedProperty _callObfusSettings;
private SerializedProperty _exprObfusSettings;
public ObfuzSettingsProvider() : base("Project/Obfuz", SettingsScope.Project)
{
@ -64,6 +65,7 @@ namespace Obfuz.Settings
_symbolObfusSettings = _serializedObject.FindProperty("symbolObfusSettings");
_constEncryptSettings = _serializedObject.FindProperty("constEncryptSettings");
_exprObfusSettings = _serializedObject.FindProperty("exprObfusSettings");
_fieldEncryptSettings = _serializedObject.FindProperty("fieldEncryptSettings");
_callObfusSettings = _serializedObject.FindProperty("callObfusSettings");
}
@ -86,6 +88,7 @@ namespace Obfuz.Settings
EditorGUILayout.PropertyField(_symbolObfusSettings);
EditorGUILayout.PropertyField(_constEncryptSettings);
EditorGUILayout.PropertyField(_exprObfusSettings);
EditorGUILayout.PropertyField(_fieldEncryptSettings);
EditorGUILayout.PropertyField(_callObfusSettings);

View File

@ -7,8 +7,8 @@ namespace Obfuz.Utils
{
public sealed class GenericArgumentContext
{
List<TypeSig> typeArgsStack;
List<TypeSig> methodArgsStack;
public readonly List<TypeSig> typeArgsStack;
public readonly List<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)
{
if (args == null)
{
throw new ArgumentNullException(nameof(args));
}
return args[(int)number];
}
}

View File

@ -9,5 +9,9 @@
int NextInt();
long NextLong();
float NextFloat();
bool NextInPercentage(float percentage);
}
}

View File

@ -518,13 +518,22 @@ namespace Obfuz.Utils
public static TypeSig Inflate(TypeSig sig, GenericArgumentContext ctx)
{
if (!sig.ContainsGenericParameter)
if (ctx == null || !sig.ContainsGenericParameter)
{
return 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)
{
@ -564,7 +573,29 @@ namespace Obfuz.Utils
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)
{
@ -572,24 +603,40 @@ namespace Obfuz.Utils
}
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)
{
var genericInstMethodSig = methodSpec.GenericInstMethodSig;
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)
{
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}");
}
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)
{
if (!method.MethodSig.HasThis)

View File

@ -47,5 +47,15 @@
{
return ((long)NextInt() << 32) | (uint)NextInt();
}
public float NextFloat()
{
return (float)((double)NextInt() / int.MaxValue);
}
public bool NextInPercentage(float percentage)
{
return NextFloat() < percentage;
}
}
}

View File

@ -1,4 +1,6 @@
namespace Obfuz
using System;
namespace Obfuz
{
public static class EncryptionService<T> where T : IEncryptionScope
@ -85,25 +87,25 @@
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);
}
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);
}
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);
}
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);
}

215
Runtime/ExprUtility.cs Normal file
View File

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9aba5050818a0224696fcf73752fb225
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: