added reference proxy protection
This commit is contained in:
Родитель
65d1aff0d3
Коммит
a10c2c8657
|
@ -24,7 +24,8 @@
|
|||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DocumentationFile>..\Debug\bin\Confuser.CLI.XML</DocumentationFile>
|
||||
<DocumentationFile>
|
||||
</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
|
@ -34,7 +35,8 @@
|
|||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DocumentationFile>..\Release\bin\Confuser.CLI.XML</DocumentationFile>
|
||||
<DocumentationFile>
|
||||
</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
|
|
|
@ -159,7 +159,7 @@ namespace Confuser.CLI
|
|||
{
|
||||
DateTime now = DateTime.Now;
|
||||
string timeString = string.Format(
|
||||
"at {0}, {1}:{2} elapsed.",
|
||||
"at {0}, {1}:{2:d2} elapsed.",
|
||||
now.ToShortTimeString(),
|
||||
(int)now.Subtract(begin).TotalMinutes,
|
||||
now.Subtract(begin).Seconds);
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
<Compile Include="ConfuserComponent.cs" />
|
||||
<Compile Include="DnlibUtils.cs" />
|
||||
<Compile Include="Helpers\InjectHelper.cs" />
|
||||
<Compile Include="Helpers\MutationHelper.cs" />
|
||||
<Compile Include="ILogger.cs" />
|
||||
<Compile Include="Marker.cs" />
|
||||
<Compile Include="MarkerResult.cs" />
|
||||
|
|
|
@ -125,6 +125,27 @@ namespace Confuser.Core
|
|||
return fullName == "System.Delegate" || fullName == "System.MulticastDelegate";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified type is inherited from a base type in corlib.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="baseType">The full name of base type.</param>
|
||||
/// <returns><c>true</c> if the specified type is inherited from a base type; otherwise, <c>false</c>.</returns>
|
||||
public static bool InheritsFromCorlib(this TypeDef type, string baseType)
|
||||
{
|
||||
if (type.BaseType == null)
|
||||
return false;
|
||||
|
||||
TypeDef bas = type;
|
||||
do
|
||||
{
|
||||
bas = bas.BaseType.ResolveTypeDefThrow();
|
||||
if (bas.ReflectionFullName == baseType)
|
||||
return true;
|
||||
} while (bas.BaseType != null && bas.BaseType.DefinitionAssembly.IsCorLib());
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified type implements the specified interface.
|
||||
/// </summary>
|
||||
|
|
|
@ -46,7 +46,7 @@ namespace Confuser.Core.Helpers
|
|||
{
|
||||
OriginModule = module;
|
||||
TargetModule = target;
|
||||
importer = new Importer(target);
|
||||
importer = new Importer(target, ImporterOptions.TryToUseTypeDefs);
|
||||
importer.Resolver = this;
|
||||
}
|
||||
|
||||
|
@ -282,6 +282,20 @@ namespace Confuser.Core.Helpers
|
|||
return (TypeDef)ctx.Map[typeDef];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Injects the specified MethodDef to another module.
|
||||
/// </summary>
|
||||
/// <param name="methodDef">The source MethodDef.</param>
|
||||
/// <param name="target">The target module.</param>
|
||||
/// <returns>The injected MethodDef.</returns>
|
||||
public static MethodDef Inject(MethodDef methodDef, ModuleDef target)
|
||||
{
|
||||
InjectContext ctx = new InjectContext(methodDef.Module, target);
|
||||
ctx.Map[methodDef] = Clone(methodDef);
|
||||
CopyMethodDef(methodDef, ctx);
|
||||
return (MethodDef)ctx.Map[methodDef];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Injects the members of specified TypeDef to another module.
|
||||
/// </summary>
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Confuser.Core.Services;
|
||||
|
||||
namespace Confuser.Core.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods to mutated injected methods.
|
||||
/// </summary>
|
||||
public static class MutationHelper
|
||||
{
|
||||
const string mutationType = "Mutation";
|
||||
static Dictionary<string, int> field2index = new Dictionary<string, int>()
|
||||
{
|
||||
{ "KeyI0", 0 },
|
||||
{ "KeyI1", 1 },
|
||||
{ "KeyI2", 2 },
|
||||
{ "KeyI3", 3 },
|
||||
{ "KeyI4", 4 },
|
||||
{ "KeyI5", 5 },
|
||||
{ "KeyI6", 6 },
|
||||
{ "KeyI7", 7 },
|
||||
{ "KeyI8", 8 },
|
||||
{ "KeyI9", 9 },
|
||||
{ "KeyI10", 10 },
|
||||
{ "KeyI11", 11 },
|
||||
{ "KeyI12", 12 },
|
||||
{ "KeyI13", 13 },
|
||||
{ "KeyI14", 14 },
|
||||
{ "KeyI15", 15 }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the mutation key placeholder in method with actual key.
|
||||
/// </summary>
|
||||
/// <param name="method">The method to process.</param>
|
||||
/// <param name="keyId">The mutation key ID.</param>
|
||||
/// <param name="key">The actual key.</param>
|
||||
public static void InjectKey(MethodDef method, int keyId, int key)
|
||||
{
|
||||
foreach (var instr in method.Body.Instructions)
|
||||
{
|
||||
if (instr.OpCode == OpCodes.Ldsfld)
|
||||
{
|
||||
IField field = (IField)instr.Operand;
|
||||
int _keyId;
|
||||
if (field.DeclaringType.FullName == mutationType &&
|
||||
field2index.TryGetValue(field.Name, out _keyId) &&
|
||||
_keyId == keyId)
|
||||
{
|
||||
instr.OpCode = OpCodes.Ldc_I4;
|
||||
instr.Operand = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the mutation key placeholders in method with actual keys.
|
||||
/// </summary>
|
||||
/// <param name="method">The method to process.</param>
|
||||
/// <param name="keyId">The mutation key IDs.</param>
|
||||
/// <param name="key">The actual keys.</param>
|
||||
public static void InjectKeys(MethodDef method, int[] keyIds, int[] keys)
|
||||
{
|
||||
foreach (var instr in method.Body.Instructions)
|
||||
{
|
||||
if (instr.OpCode == OpCodes.Ldsfld)
|
||||
{
|
||||
IField field = (IField)instr.Operand;
|
||||
int _keyIndex;
|
||||
if (field.DeclaringType.FullName == mutationType &&
|
||||
field2index.TryGetValue(field.Name, out _keyIndex) &&
|
||||
(_keyIndex = Array.IndexOf(keyIds, _keyIndex)) != -1)
|
||||
{
|
||||
instr.OpCode = OpCodes.Ldc_I4;
|
||||
instr.Operand = keys[_keyIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the placeholder call in method with actual instruction sequence.
|
||||
/// </summary>
|
||||
/// <param name="method">The methodto process.</param>
|
||||
/// <param name="repl">The function replacing the argument of placeholder call with actual instruction sequence.</param>
|
||||
public static void ReplacePlaceholder(MethodDef method, Func<Instruction[], Instruction[]> repl)
|
||||
{
|
||||
MethodTrace trace = new MethodTrace(method).Trace();
|
||||
for (int i = 0; i < method.Body.Instructions.Count; i++)
|
||||
{
|
||||
var instr = method.Body.Instructions[i];
|
||||
if (instr.OpCode == OpCodes.Call)
|
||||
{
|
||||
IMethod operand = (IMethod)instr.Operand;
|
||||
if (operand.DeclaringType.FullName == mutationType &&
|
||||
operand.Name == "Placeholder")
|
||||
{
|
||||
var argIndexes = trace.TraceArguments(instr);
|
||||
if (argIndexes == null)
|
||||
throw new ArgumentException("Failed to trace placeholder argument.");
|
||||
|
||||
var argIndex = argIndexes[0];
|
||||
Instruction[] arg = method.Body.Instructions.Skip(argIndex).Take(i - argIndex).ToArray();
|
||||
for (int j = 0; j < arg.Length; j++)
|
||||
method.Body.Instructions.RemoveAt(argIndex);
|
||||
method.Body.Instructions.RemoveAt(argIndex);
|
||||
arg = repl(arg);
|
||||
for (int j = arg.Length - 1; j >= 0; j--)
|
||||
method.Body.Instructions.Insert(argIndex, arg[j]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -116,7 +116,6 @@ namespace Confuser.Core
|
|||
{
|
||||
context.Logger.InfoFormat("Loading '{0}'...", module.Path);
|
||||
ModuleDefMD modDef = module.Resolve(proj.BaseDirectory, context.Resolver.DefaultModuleContext);
|
||||
modDef.EnableTypeDefFindCache = true;
|
||||
var rules = ParseRules(proj, module, context);
|
||||
|
||||
context.Annotations.Set(modDef, SNKey, LoadSNKey(context, module.SNKeyPath, module.SNKeyPassword));
|
||||
|
|
|
@ -106,6 +106,19 @@ namespace Confuser.Core.Services
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random byte.
|
||||
/// </summary>
|
||||
/// <returns>Requested random byte.</returns>
|
||||
public byte NextByte()
|
||||
{
|
||||
byte ret = state[32 - stateFilled];
|
||||
stateFilled--;
|
||||
if (stateFilled == 0)
|
||||
NextState();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a buffer of random bytes with the specified length.
|
||||
/// </summary>
|
||||
|
@ -126,6 +139,7 @@ namespace Confuser.Core.Services
|
|||
{
|
||||
return BitConverter.ToInt32(NextBytes(4), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nonnegative random integer that is less than the specified maximum.
|
||||
/// </summary>
|
||||
|
@ -135,6 +149,7 @@ namespace Confuser.Core.Services
|
|||
{
|
||||
return (int)(NextUInt32() % max);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random integer that is within a specified range.
|
||||
/// </summary>
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace Confuser.Core.Services
|
|||
{
|
||||
if (method == null)
|
||||
throw new ArgumentNullException("method");
|
||||
return cache.GetValueOrDefaultLazy(method, m => cache[m] = new MethodTrace(this, m)).Trace();
|
||||
return cache.GetValueOrDefaultLazy(method, m => cache[m] = new MethodTrace(m)).Trace();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,17 +51,14 @@ namespace Confuser.Core.Services
|
|||
/// </summary>
|
||||
public class MethodTrace
|
||||
{
|
||||
TraceService service;
|
||||
MethodDef method;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MethodTrace"/> class.
|
||||
/// </summary>
|
||||
/// <param name="service">The trace service.</param>
|
||||
/// <param name="method">The method to trace.</param>
|
||||
internal MethodTrace(TraceService service, MethodDef method)
|
||||
internal MethodTrace(MethodDef method)
|
||||
{
|
||||
this.service = service;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
|
@ -222,7 +219,7 @@ namespace Confuser.Core.Services
|
|||
/// <exception cref="InvalidMethodException">The method body is invalid.</exception>
|
||||
public int[] TraceArguments(Instruction instr)
|
||||
{
|
||||
if (instr.OpCode.Code != Code.Call && instr.OpCode.Code != Code.Callvirt)
|
||||
if (instr.OpCode.Code != Code.Call && instr.OpCode.Code != Code.Callvirt && instr.OpCode.Code != Code.Newobj)
|
||||
throw new ArgumentException("Invalid call instruction.", "instr");
|
||||
|
||||
int push, pop;
|
||||
|
@ -259,12 +256,12 @@ namespace Confuser.Core.Services
|
|||
index--;
|
||||
}
|
||||
if (index < 0)
|
||||
throw new InvalidMethodException("Empty evaluation stack.");
|
||||
return null;
|
||||
|
||||
if (beginInstrIndex == -1)
|
||||
beginInstrIndex = index;
|
||||
else if (beginInstrIndex != index)
|
||||
throw new InvalidMethodException("Stack depth not matched.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Trace the index of arguments
|
||||
|
@ -279,9 +276,9 @@ namespace Confuser.Core.Services
|
|||
int index = tuple.Item1;
|
||||
Stack<int> evalStack = tuple.Item2;
|
||||
|
||||
while (index != instrIndex)
|
||||
while (index != instrIndex && index < method.Body.Instructions.Count)
|
||||
{
|
||||
Instruction currentInstr = method.Body.Instructions[index];
|
||||
Instruction currentInstr = Instructions[index];
|
||||
currentInstr.CalculateStackUsage(out push, out pop);
|
||||
int stackUsage = pop - push;
|
||||
if (stackUsage < 0)
|
||||
|
@ -291,6 +288,9 @@ namespace Confuser.Core.Services
|
|||
}
|
||||
else
|
||||
{
|
||||
if (evalStack.Count < stackUsage)
|
||||
return null;
|
||||
|
||||
for (int i = 0; i < stackUsage; i++)
|
||||
evalStack.Pop();
|
||||
}
|
||||
|
@ -319,17 +319,17 @@ namespace Confuser.Core.Services
|
|||
}
|
||||
|
||||
if (evalStack.Count != argCount)
|
||||
throw new InvalidMethodException("Cannot find argument index.");
|
||||
return null;
|
||||
else if (ret != null && !evalStack.SequenceEqual(ret))
|
||||
throw new InvalidMethodException("Stack depths mismatched.");
|
||||
return null;
|
||||
else
|
||||
ret = evalStack.ToArray();
|
||||
}
|
||||
|
||||
Array.Reverse(ret);
|
||||
if (ret == null)
|
||||
throw new InvalidMethodException("Cannot find argument index.");
|
||||
return ret;
|
||||
|
||||
Array.Reverse(ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace Confuser.DynCipher.Elements
|
|||
break;
|
||||
case CryptoNumOps.Mul:
|
||||
Key = random.NextUInt32() | 1;
|
||||
InverseKey = Utils.modInv(Key);
|
||||
InverseKey = MathsUtils.modInv(Key);
|
||||
break;
|
||||
case CryptoNumOps.Xnor:
|
||||
Key = random.NextUInt32();
|
||||
|
|
|
@ -51,7 +51,7 @@ namespace Confuser.DynCipher.Elements
|
|||
Target = a
|
||||
}).Emit(new AssignmentStatement()
|
||||
{
|
||||
Value = tmp * (LiteralExpression)Utils.modInv(Key),
|
||||
Value = tmp * (LiteralExpression)MathsUtils.modInv(Key),
|
||||
Target = b
|
||||
});
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ namespace Confuser.DynCipher.Elements
|
|||
Target = a
|
||||
}).Emit(new AssignmentStatement()
|
||||
{
|
||||
Value = (b & notMask) | (tmp * (LiteralExpression)Utils.modInv(Key)),
|
||||
Value = (b & notMask) | (tmp * (LiteralExpression)MathsUtils.modInv(Key)),
|
||||
Target = b
|
||||
});
|
||||
}
|
||||
|
|
|
@ -158,7 +158,7 @@ namespace Confuser.DynCipher.Generation
|
|||
{
|
||||
Debug.Assert(constExp is LiteralExpression);
|
||||
uint val = ((LiteralExpression)constExp).Value;
|
||||
val = Utils.modInv(val);
|
||||
val = MathsUtils.modInv(val);
|
||||
result = new BinOpExpression()
|
||||
{
|
||||
Operation = BinOps.Mul,
|
||||
|
|
|
@ -5,10 +5,10 @@ using System.Text;
|
|||
|
||||
namespace Confuser.DynCipher
|
||||
{
|
||||
static class Utils
|
||||
public static class MathsUtils
|
||||
{
|
||||
const ulong MODULO32 = 0x100000000;
|
||||
static ulong modInv(ulong num, ulong mod)
|
||||
public static ulong modInv(ulong num, ulong mod)
|
||||
{
|
||||
ulong a = mod, b = num % mod;
|
||||
ulong p0 = 0, p1 = 1;
|
||||
|
@ -31,5 +31,10 @@ namespace Confuser.DynCipher
|
|||
{
|
||||
return (uint)modInv(num, MODULO32);
|
||||
}
|
||||
|
||||
public static byte modInv(byte num)
|
||||
{
|
||||
return (byte)modInv(num, 0x100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,6 +116,9 @@ namespace Confuser.Protections
|
|||
|
||||
foreach (var member in members)
|
||||
{
|
||||
marker.Mark(member);
|
||||
name.Analyze(member);
|
||||
|
||||
bool ren = true;
|
||||
if (member is MethodDef)
|
||||
{
|
||||
|
@ -126,6 +129,7 @@ namespace Confuser.Protections
|
|||
method.IsSpecialName = false;
|
||||
else
|
||||
ren = false;
|
||||
|
||||
var ca = method.CustomAttributes.Find(attrName);
|
||||
if (ca != null)
|
||||
ca.Constructor = attr.FindMethod(".ctor");
|
||||
|
@ -146,9 +150,6 @@ namespace Confuser.Protections
|
|||
member.Name = name.ObfuscateName(member.Name, RenameMode.Unicode);
|
||||
name.SetCanRename(member, false);
|
||||
}
|
||||
|
||||
marker.Mark(member);
|
||||
name.Analyze(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace Confuser.Protections
|
|||
|
||||
public override ProtectionPreset Preset
|
||||
{
|
||||
get { return ProtectionPreset.Aggressive; }
|
||||
get { return ProtectionPreset.Maximum; }
|
||||
}
|
||||
|
||||
class AntiDumpPhase : ProtectionPhase
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<OutputPath>..\Debug\bin\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
|
@ -27,7 +27,7 @@
|
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<OutputPath>..\Release\bin\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
|
@ -57,8 +57,11 @@
|
|||
<Compile Include="AntiDumpProtection.cs" />
|
||||
<Compile Include="AntiDebugProtection.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ReferenceProxy\NormalEncoding.cs" />
|
||||
<Compile Include="ReferenceProxy\MildMode.cs" />
|
||||
<Compile Include="ReferenceProxy\IRPEncoding.cs" />
|
||||
<Compile Include="ReferenceProxy\IRPMode.cs" />
|
||||
<Compile Include="ReferenceProxy\RPMode.cs" />
|
||||
<Compile Include="ReferenceProxy\StrongMode.cs" />
|
||||
<Compile Include="ReferenceProxy\ReferenceProxyPhase.cs" />
|
||||
<Compile Include="ReferenceProxy\ReferenceProxyProtection.cs" />
|
||||
<Compile Include="ReferenceProxy\RPContext.cs" />
|
||||
|
|
|
@ -56,9 +56,29 @@ namespace Confuser.Protections.ControlFlow
|
|||
break;
|
||||
|
||||
case 2: // Take that, de4dot + ILSpy :)
|
||||
instrs.Add(Instruction.Create(OpCodes.Ldc_I4, Random.NextBoolean() ? 0 : 1));
|
||||
instrs.Add(Instruction.Create(OpCodes.Box, Method.Module.CorLibTypes.Int32.TypeDefOrRef));
|
||||
bool addDefOk = false;
|
||||
if (Random.NextBoolean())
|
||||
{
|
||||
TypeDef randomType;
|
||||
randomType = Method.Module.Types[Random.NextInt32(Method.Module.Types.Count)];
|
||||
|
||||
if (randomType.HasMethods)
|
||||
{
|
||||
instrs.Add(Instruction.Create(OpCodes.Ldtoken, randomType.Methods[Random.NextInt32(randomType.Methods.Count)]));
|
||||
instrs.Add(Instruction.Create(OpCodes.Box, Method.Module.CorLibTypes.GetTypeRef("System", "RuntimeMethodHandle")));
|
||||
addDefOk = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!addDefOk)
|
||||
{
|
||||
instrs.Add(Instruction.Create(OpCodes.Ldc_I4, Random.NextBoolean() ? 0 : 1));
|
||||
instrs.Add(Instruction.Create(OpCodes.Box, Method.Module.CorLibTypes.Int32.TypeDefOrRef));
|
||||
}
|
||||
var pop = Instruction.Create(OpCodes.Pop);
|
||||
instrs.Add(Instruction.Create(OpCodes.Brfalse, instrs[0]));
|
||||
instrs.Add(Instruction.Create(OpCodes.Ldc_I4, Random.NextBoolean() ? 0 : 1));
|
||||
instrs.Add(pop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -89,6 +109,7 @@ namespace Confuser.Protections.ControlFlow
|
|||
instrs.Add(Instruction.Create(OpCodes.Ldloc, new Local(null) { Index = 0xff }));
|
||||
break;
|
||||
case 5:
|
||||
instrs.Add(Instruction.Create(OpCodes.Ldtoken, Method));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ using dnlib.DotNet.Emit;
|
|||
using Confuser.Core.Services;
|
||||
using Confuser.DynCipher;
|
||||
using Confuser.Renamer;
|
||||
using dnlib.DotNet.Writer;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Confuser.Protections.ControlFlow
|
||||
{
|
||||
|
@ -23,7 +25,7 @@ namespace Confuser.Protections.ControlFlow
|
|||
get { return ProtectionTargets.Methods; }
|
||||
}
|
||||
|
||||
static CFContext ParseParameters(MethodDef method, ConfuserContext context, ProtectionParameters parameters, RandomGenerator random)
|
||||
static CFContext ParseParameters(MethodDef method, ConfuserContext context, ProtectionParameters parameters, RandomGenerator random, bool disableOpti)
|
||||
{
|
||||
CFContext ret = new CFContext();
|
||||
ret.Type = parameters.GetParameter<CFType>(context, method, "type", CFType.Switch);
|
||||
|
@ -31,9 +33,9 @@ namespace Confuser.Protections.ControlFlow
|
|||
|
||||
int rawIntensity = parameters.GetParameter<int>(context, method, "intensity", 60);
|
||||
ret.Intensity = rawIntensity / 100.0;
|
||||
ret.Depth = parameters.GetParameter<int>(context, method, "depth", 5);
|
||||
ret.Depth = parameters.GetParameter<int>(context, method, "depth", 4);
|
||||
|
||||
ret.JunkCode = parameters.GetParameter<bool>(context, method, "junk", false);
|
||||
ret.JunkCode = parameters.GetParameter<bool>(context, method, "junk", false) && !disableOpti;
|
||||
|
||||
ret.Random = random;
|
||||
ret.Method = method;
|
||||
|
@ -43,16 +45,38 @@ namespace Confuser.Protections.ControlFlow
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool DisabledOptimization(ModuleDef module)
|
||||
{
|
||||
bool disableOpti = false;
|
||||
var debugAttr = module.Assembly.CustomAttributes.Find("System.Diagnostics.DebuggableAttribute");
|
||||
if (debugAttr != null)
|
||||
{
|
||||
if (debugAttr.ConstructorArguments.Count == 1)
|
||||
disableOpti |= ((DebuggableAttribute.DebuggingModes)(int)debugAttr.ConstructorArguments[0].Value & DebuggableAttribute.DebuggingModes.DisableOptimizations) != 0;
|
||||
else
|
||||
disableOpti |= (bool)debugAttr.ConstructorArguments[1].Value;
|
||||
}
|
||||
debugAttr = module.CustomAttributes.Find("System.Diagnostics.DebuggableAttribute");
|
||||
if (debugAttr != null)
|
||||
{
|
||||
if (debugAttr.ConstructorArguments.Count == 1)
|
||||
disableOpti |= ((DebuggableAttribute.DebuggingModes)(int)debugAttr.ConstructorArguments[0].Value & DebuggableAttribute.DebuggingModes.DisableOptimizations) != 0;
|
||||
else
|
||||
disableOpti |= (bool)debugAttr.ConstructorArguments[1].Value;
|
||||
}
|
||||
return disableOpti;
|
||||
}
|
||||
|
||||
protected override void Execute(ConfuserContext context, ProtectionParameters parameters)
|
||||
{
|
||||
bool disabledOpti = DisabledOptimization(context.CurrentModule);
|
||||
var random = context.Registry.GetService<IRandomService>().GetRandomGenerator(ControlFlowProtection._FullId);
|
||||
|
||||
foreach (var method in parameters.Targets.OfType<MethodDef>())
|
||||
if (method.HasBody && method.Body.Instructions.Count > 0)
|
||||
{
|
||||
ProcessMethod(method.Body, ParseParameters(method, context, parameters, random));
|
||||
ProcessMethod(method.Body, ParseParameters(method, context, parameters, random, disabledOpti));
|
||||
}
|
||||
|
||||
context.CurrentModuleWriterOptions.MetaDataOptions.Flags |= dnlib.DotNet.Writer.MetaDataFlags.KeepOldMaxStack;
|
||||
}
|
||||
|
||||
static readonly JumpMangler Jump = new JumpMangler();
|
||||
|
@ -68,6 +92,13 @@ namespace Confuser.Protections.ControlFlow
|
|||
|
||||
void ProcessMethod(CilBody body, CFContext ctx)
|
||||
{
|
||||
uint maxStack;
|
||||
if (!MaxStackCalculator.GetMaxStack(body.Instructions, body.ExceptionHandlers, out maxStack))
|
||||
{
|
||||
ctx.Context.Logger.Error("Failed to calcuate maxstack.");
|
||||
throw new ConfuserException(null);
|
||||
}
|
||||
body.MaxStack = (ushort)maxStack;
|
||||
ScopeBlock root = BlockParser.ParseBody(body);
|
||||
|
||||
GetMangler(ctx.Type).Mangle(body, root, ctx);
|
||||
|
@ -79,6 +110,7 @@ namespace Confuser.Protections.ControlFlow
|
|||
eh.TryEnd = body.Instructions[body.Instructions.IndexOf(eh.TryEnd) + 1];
|
||||
eh.HandlerEnd = body.Instructions[body.Instructions.IndexOf(eh.HandlerEnd) + 1];
|
||||
}
|
||||
body.KeepOldMaxStack = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,12 @@ using Confuser.Protections.ControlFlow;
|
|||
|
||||
namespace Confuser.Protections
|
||||
{
|
||||
class ControlFlowProtection : Protection
|
||||
public interface IControlFlowService
|
||||
{
|
||||
void ExcludeMethod(ConfuserContext context, MethodDef method);
|
||||
}
|
||||
|
||||
class ControlFlowProtection : Protection, IControlFlowService
|
||||
{
|
||||
public const string _Id = "ctrl flow";
|
||||
public const string _FullId = "Ki.ControlFlow";
|
||||
|
@ -16,7 +21,7 @@ namespace Confuser.Protections
|
|||
|
||||
protected override void Initialize(ConfuserContext context)
|
||||
{
|
||||
//
|
||||
context.Registry.RegisterService(_ServiceId, typeof(IControlFlowService), this);
|
||||
}
|
||||
|
||||
protected override void PopulatePipeline(ProtectionPipeline pipeline)
|
||||
|
@ -48,5 +53,10 @@ namespace Confuser.Protections
|
|||
{
|
||||
get { return ProtectionPreset.Normal; }
|
||||
}
|
||||
|
||||
public void ExcludeMethod(ConfuserContext context, MethodDef method)
|
||||
{
|
||||
ProtectionParameters.GetParameters(context, method).Remove(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ namespace Confuser.Protections.ControlFlow
|
|||
|
||||
invCompiled = new List<Instruction>();
|
||||
new CodeGen(stateVar, ctx, invCompiled).GenerateCIL(inverse);
|
||||
body.MaxStack += (ushort)ctx.Depth;
|
||||
}
|
||||
|
||||
bool inited = false;
|
||||
|
|
|
@ -73,7 +73,7 @@ namespace Confuser.Protections.ControlFlow
|
|||
{
|
||||
MethodTrace trace = ctx.Context.Registry.GetService<ITraceService>().Trace(ctx.Method);
|
||||
|
||||
body.MaxStack++;
|
||||
body.MaxStack += 2;
|
||||
IPredicate predicate = null;
|
||||
if (ctx.Predicate == PredicateType.Expression)
|
||||
predicate = new ExpressionPredicate(ctx);
|
||||
|
|
|
@ -33,9 +33,12 @@ namespace Confuser.Protections.ControlFlow
|
|||
Variable result = new Variable("{RESULT}");
|
||||
|
||||
var int32 = ctx.Method.Module.CorLibTypes.Int32;
|
||||
native = new MethodDefUser(ctx.Context.Registry.GetService<INameService>().RandomName(), MethodSig.CreateStatic(int32, int32),
|
||||
MethodAttributes.PinvokeImpl | MethodAttributes.PrivateScope | MethodAttributes.Static);
|
||||
native = new MethodDefUser(ctx.Context.Registry.GetService<INameService>().RandomName(), MethodSig.CreateStatic(int32, int32), MethodAttributes.PinvokeImpl | MethodAttributes.PrivateScope | MethodAttributes.Static);
|
||||
native.ImplAttributes = MethodImplAttributes.Native | MethodImplAttributes.Unmanaged | MethodImplAttributes.PreserveSig;
|
||||
// Attempt to improve performance --- failed with StackOverflowException... :/
|
||||
//var suppressAttr = ctx.Method.Module.CorLibTypes.GetTypeRef("System.Security", "SuppressUnmanagedCodeSecurityAttribute").ResolveThrow();
|
||||
//native.CustomAttributes.Add(new CustomAttribute((MemberRef)ctx.Method.Module.Import(suppressAttr.FindDefaultConstructor())));
|
||||
//native.HasSecurity = true;
|
||||
ctx.Method.Module.GlobalType.Methods.Add(native);
|
||||
|
||||
ctx.Context.Registry.GetService<IMarkerService>().Mark(native);
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using dnlib.DotNet.Emit;
|
||||
using dnlib.DotNet;
|
||||
|
||||
namespace Confuser.Protections.ReferenceProxy
|
||||
{
|
||||
interface IRPEncoding
|
||||
{
|
||||
Instruction[] EmitDecode(MethodDef init, RPContext ctx, Instruction[] arg);
|
||||
int Encode(MethodDef init, RPContext ctx, int value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using dnlib.DotNet.Emit;
|
||||
using dnlib.DotNet;
|
||||
using System.Diagnostics;
|
||||
using Confuser.Core;
|
||||
using Confuser.Core.Services;
|
||||
using Confuser.Renamer;
|
||||
|
||||
namespace Confuser.Protections.ReferenceProxy
|
||||
{
|
||||
class MildMode : RPMode
|
||||
{
|
||||
// proxy method, { opCode, calling type, target method}
|
||||
Dictionary<Tuple<Code, TypeDef, IMethod>, MethodDef> proxies = new Dictionary<Tuple<Code, TypeDef, IMethod>, MethodDef>();
|
||||
|
||||
public override void ProcessCall(RPContext ctx, int instrIndex)
|
||||
{
|
||||
Instruction invoke = ctx.Body.Instructions[instrIndex];
|
||||
IMethod target = (IMethod)invoke.Operand;
|
||||
|
||||
// Value type proxy is not supported in mild mode.
|
||||
if (target.DeclaringType.ResolveTypeDefThrow().IsValueType)
|
||||
return;
|
||||
// Skipping visibility is not supported in mild mode.
|
||||
if (!target.ResolveThrow().IsPublic && !target.ResolveThrow().IsAssembly)
|
||||
return;
|
||||
|
||||
var key = Tuple.Create(invoke.OpCode.Code, ctx.Method.DeclaringType, target);
|
||||
MethodDef proxy;
|
||||
if (!proxies.TryGetValue(key, out proxy))
|
||||
{
|
||||
MethodSig sig = CreateProxySignature(ctx, target, invoke.OpCode.Code == Code.Newobj);
|
||||
|
||||
proxy = new MethodDefUser(ctx.Name.RandomName(), sig);
|
||||
proxy.Attributes = MethodAttributes.PrivateScope | MethodAttributes.Static;
|
||||
proxy.ImplAttributes = MethodImplAttributes.Managed | MethodImplAttributes.IL;
|
||||
ctx.Method.DeclaringType.Methods.Add(proxy);
|
||||
|
||||
// Fix peverify --- Non-virtual call to virtual methods must be done on this pointer
|
||||
if (invoke.OpCode.Code == Code.Call && target.ResolveThrow().IsVirtual)
|
||||
{
|
||||
proxy.IsStatic = false;
|
||||
sig.HasThis = true;
|
||||
sig.Params.RemoveAt(0);
|
||||
}
|
||||
|
||||
ctx.Marker.Mark(proxy);
|
||||
ctx.Name.Analyze(proxy);
|
||||
ctx.Name.SetCanRename(proxy, false);
|
||||
|
||||
proxy.Body = new CilBody();
|
||||
for (int i = 0; i < proxy.Parameters.Count; i++)
|
||||
proxy.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg, proxy.Parameters[i]));
|
||||
proxy.Body.Instructions.Add(Instruction.Create(invoke.OpCode, target));
|
||||
proxy.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
||||
|
||||
proxies[key] = proxy;
|
||||
}
|
||||
|
||||
invoke.OpCode = OpCodes.Call;
|
||||
if (ctx.Method.DeclaringType.HasGenericParameters)
|
||||
{
|
||||
var genArgs = new GenericVar[ctx.Method.DeclaringType.GenericParameters.Count];
|
||||
for (int i = 0; i < genArgs.Length; i++)
|
||||
genArgs[i] = new GenericVar(i);
|
||||
|
||||
invoke.Operand = new MemberRefUser(
|
||||
ctx.Module,
|
||||
proxy.Name,
|
||||
proxy.MethodSig,
|
||||
new GenericInstSig((ClassOrValueTypeSig)ctx.Method.DeclaringType.ToTypeSig(), genArgs).ToTypeDefOrRef());
|
||||
}
|
||||
else
|
||||
invoke.Operand = proxy;
|
||||
}
|
||||
|
||||
public override void Finalize(RPContext ctx)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using dnlib.DotNet.Emit;
|
||||
using dnlib.DotNet;
|
||||
using Confuser.Core;
|
||||
using Confuser.Core.Services;
|
||||
using Confuser.DynCipher;
|
||||
|
||||
namespace Confuser.Protections.ReferenceProxy
|
||||
{
|
||||
class NormalEncoding : IRPEncoding
|
||||
{
|
||||
Dictionary<MethodDef, Tuple<int, int>> keys = new Dictionary<MethodDef, Tuple<int, int>>();
|
||||
|
||||
Tuple<int, int> GetKey(RandomGenerator random, MethodDef init)
|
||||
{
|
||||
Tuple<int, int> ret;
|
||||
if (!keys.TryGetValue(init, out ret))
|
||||
{
|
||||
int key = random.NextInt32() | 1;
|
||||
keys[init] = ret = Tuple.Create(key, (int)MathsUtils.modInv((uint)key));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Instruction[] EmitDecode(MethodDef init, RPContext ctx, Instruction[] arg)
|
||||
{
|
||||
var key = GetKey(ctx.Random, init);
|
||||
List<Instruction> ret = new List<Instruction>();
|
||||
if (ctx.Random.NextBoolean())
|
||||
{
|
||||
ret.Add(Instruction.Create(OpCodes.Ldc_I4, key.Item1));
|
||||
ret.AddRange(arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.AddRange(arg);
|
||||
ret.Add(Instruction.Create(OpCodes.Ldc_I4, key.Item1));
|
||||
}
|
||||
ret.Add(Instruction.Create(OpCodes.Mul));
|
||||
return ret.ToArray();
|
||||
}
|
||||
|
||||
public int Encode(MethodDef init, RPContext ctx, int value)
|
||||
{
|
||||
var key = GetKey(ctx.Random, init);
|
||||
return value * key.Item2;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Confuser.Core.Services;
|
||||
using Confuser.Core;
|
||||
using dnlib.DotNet;
|
||||
using Confuser.DynCipher;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Confuser.Renamer;
|
||||
|
||||
namespace Confuser.Protections.ReferenceProxy
|
||||
{
|
||||
enum Mode
|
||||
{
|
||||
Mild,
|
||||
Strong,
|
||||
Ftn
|
||||
}
|
||||
enum EncodingType
|
||||
{
|
||||
Normal,
|
||||
Expression,
|
||||
x86
|
||||
}
|
||||
class RPContext
|
||||
{
|
||||
public RandomGenerator Random;
|
||||
public ConfuserContext Context;
|
||||
public ModuleDef Module;
|
||||
public MethodDef Method;
|
||||
public HashSet<Instruction> BranchTargets;
|
||||
public CilBody Body;
|
||||
public IMarkerService Marker;
|
||||
public IDynCipherService DynCipher;
|
||||
public INameService Name;
|
||||
|
||||
public Mode Mode;
|
||||
public EncodingType Encoding;
|
||||
public bool TypeErasure;
|
||||
public bool InternalAlso;
|
||||
|
||||
public RPMode ModeHandler;
|
||||
public IRPEncoding EncodingHandler;
|
||||
|
||||
public Dictionary<MethodSig, TypeDef> Delegates;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using dnlib.DotNet.Emit;
|
||||
using dnlib.DotNet;
|
||||
using System.Diagnostics;
|
||||
using Confuser.Core;
|
||||
using Confuser.Renamer.References;
|
||||
|
||||
namespace Confuser.Protections.ReferenceProxy
|
||||
{
|
||||
abstract class RPMode
|
||||
{
|
||||
public abstract void ProcessCall(RPContext ctx, int instrIndex);
|
||||
public abstract void Finalize(RPContext ctx);
|
||||
|
||||
static ITypeDefOrRef Import(RPContext ctx, TypeDef typeDef)
|
||||
{
|
||||
var retTypeRef = new Importer(ctx.Module, ImporterOptions.TryToUseTypeDefs).Import(typeDef);
|
||||
if (typeDef.Module != ctx.Module && ctx.Context.Modules.Contains((ModuleDefMD)typeDef.Module))
|
||||
ctx.Name.AddReference(typeDef, new TypeRefReference((TypeRef)retTypeRef, typeDef));
|
||||
return retTypeRef;
|
||||
}
|
||||
|
||||
protected static MethodSig CreateProxySignature(RPContext ctx, IMethod method, bool newObj)
|
||||
{
|
||||
var module = ctx.Module;
|
||||
if (newObj)
|
||||
{
|
||||
Debug.Assert(method.MethodSig.HasThis);
|
||||
Debug.Assert(method.Name == ".ctor");
|
||||
TypeSig[] paramTypes = method.MethodSig.Params.Select(type =>
|
||||
{
|
||||
if (ctx.TypeErasure && type.IsClassSig && method.MethodSig.HasThis)
|
||||
return module.CorLibTypes.Object;
|
||||
return type;
|
||||
}).ToArray();
|
||||
|
||||
TypeSig retType;
|
||||
if (ctx.TypeErasure) // newobj will not be used with value types
|
||||
retType = module.CorLibTypes.Object;
|
||||
else
|
||||
{
|
||||
var declType = method.DeclaringType.ResolveTypeDefThrow();
|
||||
retType = Import(ctx, declType).ToTypeSig();
|
||||
}
|
||||
return MethodSig.CreateStatic(retType, paramTypes);
|
||||
}
|
||||
else
|
||||
{
|
||||
var paramTypes = method.MethodSig.Params.Select(type =>
|
||||
{
|
||||
if (ctx.TypeErasure && type.IsClassSig && method.MethodSig.HasThis)
|
||||
return module.CorLibTypes.Object;
|
||||
return type;
|
||||
});
|
||||
if (method.MethodSig.HasThis && !method.MethodSig.ExplicitThis)
|
||||
{
|
||||
var declType = method.DeclaringType.ResolveTypeDefThrow();
|
||||
if (ctx.TypeErasure && !declType.IsValueType)
|
||||
paramTypes = new[] { module.CorLibTypes.Object }.Concat(paramTypes);
|
||||
else
|
||||
paramTypes = new[] { Import(ctx, declType).ToTypeSig() }.Concat(paramTypes);
|
||||
}
|
||||
TypeSig retType = method.MethodSig.RetType;
|
||||
if (ctx.TypeErasure && retType.IsClassSig)
|
||||
retType = module.CorLibTypes.Object;
|
||||
return MethodSig.CreateStatic(retType, paramTypes.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
protected static TypeDef GetDelegateType(RPContext ctx, MethodSig sig)
|
||||
{
|
||||
TypeDef ret;
|
||||
if (ctx.Delegates.TryGetValue(sig, out ret))
|
||||
return ret;
|
||||
|
||||
ret = new TypeDefUser(ctx.Name.ObfuscateName(ctx.Method.DeclaringType.Namespace, Renamer.RenameMode.Unicode), ctx.Name.RandomName(), ctx.Module.CorLibTypes.GetTypeRef("System", "MulticastDelegate"));
|
||||
ret.Attributes = TypeAttributes.NotPublic | TypeAttributes.Sealed;
|
||||
|
||||
var ctor = new MethodDefUser(".ctor", MethodSig.CreateInstance(ctx.Module.CorLibTypes.Void, ctx.Module.CorLibTypes.Object, ctx.Module.CorLibTypes.IntPtr));
|
||||
ctor.Attributes = MethodAttributes.Assembly | MethodAttributes.HideBySig | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName;
|
||||
ctor.ImplAttributes = MethodImplAttributes.Runtime;
|
||||
ret.Methods.Add(ctor);
|
||||
|
||||
var invoke = new MethodDefUser("Invoke", sig.Clone());
|
||||
invoke.MethodSig.HasThis = true;
|
||||
invoke.Attributes = MethodAttributes.Assembly | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.NewSlot;
|
||||
invoke.ImplAttributes = MethodImplAttributes.Runtime;
|
||||
ret.Methods.Add(invoke);
|
||||
|
||||
ctx.Module.Types.Add(ret);
|
||||
|
||||
foreach (var def in ret.FindDefinitions())
|
||||
{
|
||||
ctx.Marker.Mark(def);
|
||||
ctx.Name.SetCanRename(def, false);
|
||||
}
|
||||
|
||||
ctx.Delegates[sig] = ret;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Confuser.Core;
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Confuser.Core.Services;
|
||||
using Confuser.Renamer;
|
||||
using Confuser.DynCipher;
|
||||
|
||||
namespace Confuser.Protections.ReferenceProxy
|
||||
{
|
||||
class ReferenceProxyPhase : ProtectionPhase
|
||||
{
|
||||
public ReferenceProxyPhase(ReferenceProxyProtection parent)
|
||||
: base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override ProtectionTargets Targets
|
||||
{
|
||||
get { return ProtectionTargets.Methods; }
|
||||
}
|
||||
|
||||
class RPStore
|
||||
{
|
||||
public RandomGenerator random;
|
||||
|
||||
public MildMode mild;
|
||||
public StrongMode strong;
|
||||
|
||||
public NormalEncoding normal;
|
||||
|
||||
class MethodSigComparer : IEqualityComparer<MethodSig>
|
||||
{
|
||||
public bool Equals(MethodSig x, MethodSig y)
|
||||
{
|
||||
return new SigComparer().Equals(x, y);
|
||||
}
|
||||
|
||||
public int GetHashCode(MethodSig obj)
|
||||
{
|
||||
return new SigComparer().GetHashCode(obj);
|
||||
}
|
||||
}
|
||||
public Dictionary<MethodSig, TypeDef> delegates = new Dictionary<MethodSig, TypeDef>(new MethodSigComparer());
|
||||
}
|
||||
|
||||
static RPContext ParseParameters(MethodDef method, ConfuserContext context, ProtectionParameters parameters, RPStore store)
|
||||
{
|
||||
RPContext ret = new RPContext();
|
||||
ret.Mode = parameters.GetParameter<Mode>(context, method, "mode", Mode.Mild);
|
||||
ret.Encoding = parameters.GetParameter<EncodingType>(context, method, "encoding", EncodingType.Normal);
|
||||
ret.InternalAlso = parameters.GetParameter<bool>(context, method, "internal", false);
|
||||
ret.TypeErasure = parameters.GetParameter<bool>(context, method, "typeErasure", false);
|
||||
|
||||
ret.Module = method.Module;
|
||||
ret.Method = method;
|
||||
ret.Body = method.Body;
|
||||
ret.BranchTargets = new HashSet<Instruction>(
|
||||
method.Body.Instructions
|
||||
.Select(instr => instr.Operand as Instruction)
|
||||
.Concat(method.Body.Instructions
|
||||
.Where(instr => instr.Operand is Instruction[])
|
||||
.SelectMany(instr => (Instruction[])instr.Operand))
|
||||
.Where(target => target != null));
|
||||
|
||||
ret.Random = store.random;
|
||||
ret.Context = context;
|
||||
ret.Marker = context.Registry.GetService<IMarkerService>();
|
||||
ret.DynCipher = context.Registry.GetService<IDynCipherService>();
|
||||
ret.Name = context.Registry.GetService<INameService>();
|
||||
|
||||
ret.Delegates = store.delegates;
|
||||
|
||||
switch (ret.Mode)
|
||||
{
|
||||
case Mode.Mild:
|
||||
ret.ModeHandler = store.mild ?? (store.mild = new MildMode());
|
||||
break;
|
||||
case Mode.Strong:
|
||||
ret.ModeHandler = store.strong ?? (store.strong = new StrongMode());
|
||||
break;
|
||||
default:
|
||||
throw new UnreachableException();
|
||||
}
|
||||
|
||||
switch (ret.Encoding)
|
||||
{
|
||||
case EncodingType.Normal:
|
||||
ret.EncodingHandler = store.normal ?? (store.normal = new NormalEncoding());
|
||||
break;
|
||||
default:
|
||||
throw new UnreachableException();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static RPContext ParseParameters(ModuleDef module, ConfuserContext context, ProtectionParameters parameters, RPStore store)
|
||||
{
|
||||
RPContext ret = new RPContext();
|
||||
|
||||
ret.Random = store.random;
|
||||
ret.Module = module;
|
||||
ret.Context = context;
|
||||
ret.Marker = context.Registry.GetService<IMarkerService>();
|
||||
ret.DynCipher = context.Registry.GetService<IDynCipherService>();
|
||||
ret.Name = context.Registry.GetService<INameService>();
|
||||
|
||||
ret.Delegates = store.delegates;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected override void Execute(ConfuserContext context, ProtectionParameters parameters)
|
||||
{
|
||||
var random = context.Registry.GetService<IRandomService>().GetRandomGenerator(ReferenceProxyProtection._FullId);
|
||||
|
||||
RPStore store = new RPStore() { random = random };
|
||||
|
||||
foreach (var method in parameters.Targets.OfType<MethodDef>())
|
||||
if (method.HasBody && method.Body.Instructions.Count > 0)
|
||||
{
|
||||
ProcessMethod(ParseParameters(method, context, parameters, store));
|
||||
}
|
||||
|
||||
var ctx = ParseParameters(context.CurrentModule, context, parameters, store);
|
||||
|
||||
if (store.mild != null)
|
||||
store.mild.Finalize(ctx);
|
||||
|
||||
if (store.strong != null)
|
||||
store.strong.Finalize(ctx);
|
||||
}
|
||||
|
||||
void ProcessMethod(RPContext ctx)
|
||||
{
|
||||
for (int i = 0; i < ctx.Body.Instructions.Count; i++)
|
||||
{
|
||||
var instr = ctx.Body.Instructions[i];
|
||||
if (instr.OpCode.Code == Code.Call || instr.OpCode.Code == Code.Callvirt || instr.OpCode.Code == Code.Newobj)
|
||||
{
|
||||
IMethod operand = (IMethod)instr.Operand;
|
||||
// Call constructor
|
||||
if (instr.OpCode.Code != Code.Newobj && operand.Name == ".ctor")
|
||||
continue;
|
||||
// Internal reference option
|
||||
if (operand is MethodDef && !ctx.InternalAlso)
|
||||
continue;
|
||||
// No generic methods
|
||||
if (operand is MethodSpec)
|
||||
continue;
|
||||
// No generic types / array types
|
||||
if (operand.DeclaringType is TypeSpec)
|
||||
continue;
|
||||
var declType = operand.DeclaringType.ResolveTypeDefThrow();
|
||||
// No delegates
|
||||
if (declType.IsDelegate())
|
||||
continue;
|
||||
// No instance value type methods
|
||||
if (declType.IsValueType && operand.MethodSig.HasThis)
|
||||
return;
|
||||
// No prefixed call
|
||||
if (i - 1 >= 0 && ctx.Body.Instructions[i - 1].OpCode.OpCodeType == OpCodeType.Prefix)
|
||||
continue;
|
||||
|
||||
ctx.ModeHandler.ProcessCall(ctx, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Confuser.Core;
|
||||
using dnlib.DotNet;
|
||||
using Confuser.Protections.ReferenceProxy;
|
||||
|
||||
namespace Confuser.Protections
|
||||
{
|
||||
public interface IReferenceProxyService
|
||||
{
|
||||
void ExcludeMethod(ConfuserContext context, MethodDef method);
|
||||
}
|
||||
|
||||
[AfterProtection("Ki.AntiDebug", "Ki.AntiDump")]
|
||||
[BeforeProtection("Ki.ControlFlow")]
|
||||
class ReferenceProxyProtection : Protection, IReferenceProxyService
|
||||
{
|
||||
public const string _Id = "ref proxy";
|
||||
public const string _FullId = "Ki.RefProxy";
|
||||
public const string _ServiceId = "Ki.RefProxy";
|
||||
|
||||
protected override void Initialize(ConfuserContext context)
|
||||
{
|
||||
context.Registry.RegisterService(_ServiceId, typeof(IReferenceProxyService), this);
|
||||
}
|
||||
|
||||
protected override void PopulatePipeline(ProtectionPipeline pipeline)
|
||||
{
|
||||
pipeline.InsertPostStage(PipelineStage.BeginModule, new ReferenceProxyPhase(this));
|
||||
}
|
||||
|
||||
public override string Name
|
||||
{
|
||||
get { return "Reference Proxy Protection"; }
|
||||
}
|
||||
|
||||
public override string Description
|
||||
{
|
||||
get { return "This protection encodes and hides references to type/method/fields."; }
|
||||
}
|
||||
|
||||
public override string Id
|
||||
{
|
||||
get { return _Id; }
|
||||
}
|
||||
|
||||
public override string FullId
|
||||
{
|
||||
get { return _FullId; }
|
||||
}
|
||||
|
||||
public override ProtectionPreset Preset
|
||||
{
|
||||
get { return ProtectionPreset.Normal; }
|
||||
}
|
||||
|
||||
public void ExcludeMethod(ConfuserContext context, MethodDef method)
|
||||
{
|
||||
ProtectionParameters.GetParameters(context, method).Remove(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,451 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using dnlib.DotNet.Emit;
|
||||
using dnlib.DotNet;
|
||||
using Confuser.Core.Services;
|
||||
using System.Diagnostics;
|
||||
using Confuser.Core;
|
||||
using Confuser.Renamer;
|
||||
using Confuser.Core.Helpers;
|
||||
using Confuser.DynCipher.AST;
|
||||
using Confuser.DynCipher.Generation;
|
||||
using dnlib.DotNet.Writer;
|
||||
using Confuser.DynCipher;
|
||||
|
||||
namespace Confuser.Protections.ReferenceProxy
|
||||
{
|
||||
class StrongMode : RPMode
|
||||
{
|
||||
static int? TraceBeginning(RPContext ctx, int index, int argCount)
|
||||
{
|
||||
if (ctx.BranchTargets.Contains(ctx.Body.Instructions[index]))
|
||||
return null;
|
||||
|
||||
int currentStack = argCount;
|
||||
int currentIndex = index;
|
||||
while (currentStack > 0)
|
||||
{
|
||||
currentIndex--;
|
||||
Instruction currentInstr = ctx.Body.Instructions[currentIndex];
|
||||
|
||||
// Disrupt stack analysis :/ Used by array initializer
|
||||
if (currentInstr.OpCode == OpCodes.Pop || currentInstr.OpCode == OpCodes.Dup)
|
||||
return null;
|
||||
|
||||
// No branch instr.
|
||||
switch (currentInstr.OpCode.FlowControl)
|
||||
{
|
||||
case FlowControl.Call:
|
||||
case FlowControl.Break:
|
||||
case FlowControl.Meta:
|
||||
case FlowControl.Next:
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
int push, pop;
|
||||
currentInstr.CalculateStackUsage(out push, out pop);
|
||||
currentStack += pop;
|
||||
currentStack -= push;
|
||||
|
||||
// No branch target
|
||||
if (ctx.BranchTargets.Contains(currentInstr) && currentStack != 0)
|
||||
return null;
|
||||
}
|
||||
if (currentStack < 0)
|
||||
return null;
|
||||
return currentIndex;
|
||||
}
|
||||
|
||||
public override void ProcessCall(RPContext ctx, int instrIndex)
|
||||
{
|
||||
Instruction invoke = ctx.Body.Instructions[instrIndex];
|
||||
|
||||
var declType = ((IMethod)invoke.Operand).DeclaringType.ResolveTypeDefThrow();
|
||||
if (!declType.Module.IsILOnly) // Reflection doesn't like mixed mode modules.
|
||||
return;
|
||||
if (declType.IsGlobalModuleType) // Reflection doesn't like global methods too.
|
||||
return;
|
||||
|
||||
int push, pop;
|
||||
invoke.CalculateStackUsage(out push, out pop);
|
||||
int? begin = TraceBeginning(ctx, instrIndex, pop);
|
||||
// Fail to trace the arguments => fall back to bridge method
|
||||
bool fallBack = begin == null;
|
||||
|
||||
if (fallBack)
|
||||
{
|
||||
ProcessBridge(ctx, instrIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessInvoke(ctx, instrIndex, begin.Value);
|
||||
}
|
||||
}
|
||||
|
||||
// { key attribute, encoding }
|
||||
Tuple<TypeDef, Func<int, int>>[] keyAttrs;
|
||||
|
||||
// { invoke opCode, invoke target, encoding}, { proxy field, bridge method }
|
||||
Dictionary<Tuple<Code, IMethod, IRPEncoding>, Tuple<FieldDef, MethodDef>> fields = new Dictionary<Tuple<Code, IMethod, IRPEncoding>, Tuple<FieldDef, MethodDef>>();
|
||||
|
||||
class InitMethodDesc
|
||||
{
|
||||
public MethodDef Method;
|
||||
public int[] TokenNameOrder;
|
||||
public int[] TokenByteOrder;
|
||||
public int OpCodeIndex;
|
||||
public IRPEncoding Encoding;
|
||||
}
|
||||
|
||||
Dictionary<IRPEncoding, InitMethodDesc[]> inits = new Dictionary<IRPEncoding, InitMethodDesc[]>();
|
||||
|
||||
void ProcessBridge(RPContext ctx, int instrIndex)
|
||||
{
|
||||
var instr = ctx.Body.Instructions[instrIndex];
|
||||
IMethod target = (IMethod)instr.Operand;
|
||||
|
||||
var declType = target.DeclaringType.ResolveTypeDefThrow();
|
||||
if (!declType.Module.IsILOnly) // Reflection doesn't like mixed mode modules.
|
||||
return;
|
||||
if (declType.IsGlobalModuleType) // Reflection doesn't like global methods too.
|
||||
return;
|
||||
|
||||
var key = Tuple.Create(instr.OpCode.Code, target, ctx.EncodingHandler);
|
||||
Tuple<FieldDef, MethodDef> proxy;
|
||||
if (fields.TryGetValue(key, out proxy))
|
||||
{
|
||||
if (proxy.Item2 != null)
|
||||
{
|
||||
instr.OpCode = OpCodes.Call;
|
||||
instr.Operand = proxy.Item2;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
proxy = new Tuple<FieldDef, MethodDef>(null, null);
|
||||
|
||||
MethodSig sig = CreateProxySignature(ctx, target, instr.OpCode.Code == Code.Newobj);
|
||||
var delegateType = GetDelegateType(ctx, sig);
|
||||
|
||||
// Create proxy field
|
||||
if (proxy.Item1 == null)
|
||||
proxy.Item1 = CreateField(ctx, delegateType);
|
||||
|
||||
// Create proxy bridge
|
||||
Debug.Assert(proxy.Item2 == null);
|
||||
|
||||
proxy.Item2 = CreateBridge(ctx, delegateType, proxy.Item1, sig);
|
||||
fields[key] = proxy;
|
||||
|
||||
// Replace instruction
|
||||
instr.OpCode = OpCodes.Call;
|
||||
instr.Operand = proxy.Item2;
|
||||
}
|
||||
|
||||
void ProcessInvoke(RPContext ctx, int instrIndex, int argBeginIndex)
|
||||
{
|
||||
var instr = ctx.Body.Instructions[instrIndex];
|
||||
IMethod target = (IMethod)instr.Operand;
|
||||
|
||||
MethodSig sig = CreateProxySignature(ctx, target, instr.OpCode.Code == Code.Newobj);
|
||||
var delegateType = GetDelegateType(ctx, sig);
|
||||
|
||||
var key = Tuple.Create(instr.OpCode.Code, target, ctx.EncodingHandler);
|
||||
Tuple<FieldDef, MethodDef> proxy;
|
||||
if (!fields.TryGetValue(key, out proxy))
|
||||
{
|
||||
// Create proxy field
|
||||
proxy = new Tuple<FieldDef, MethodDef>(CreateField(ctx, delegateType), null);
|
||||
fields[key] = proxy;
|
||||
}
|
||||
|
||||
// Insert field load & replace instruction
|
||||
if (argBeginIndex == instrIndex)
|
||||
{
|
||||
ctx.Body.Instructions.Insert(instrIndex + 1,
|
||||
new Instruction(OpCodes.Call, delegateType.FindMethod("Invoke")));
|
||||
instr.OpCode = OpCodes.Ldsfld;
|
||||
instr.Operand = proxy.Item1;
|
||||
}
|
||||
else
|
||||
{
|
||||
var argBegin = ctx.Body.Instructions[argBeginIndex];
|
||||
ctx.Body.Instructions.Insert(argBeginIndex + 1,
|
||||
new Instruction(argBegin.OpCode, argBegin.Operand));
|
||||
argBegin.OpCode = OpCodes.Ldsfld;
|
||||
argBegin.Operand = proxy.Item1;
|
||||
|
||||
instr.OpCode = OpCodes.Call;
|
||||
instr.Operand = delegateType.FindMethod("Invoke");
|
||||
}
|
||||
}
|
||||
|
||||
MethodDef CreateBridge(RPContext ctx, TypeDef delegateType, FieldDef field, MethodSig sig)
|
||||
{
|
||||
var method = new MethodDefUser(ctx.Name.RandomName(), sig);
|
||||
method.Attributes = MethodAttributes.PrivateScope | MethodAttributes.Static;
|
||||
method.ImplAttributes = MethodImplAttributes.Managed | MethodImplAttributes.IL;
|
||||
|
||||
method.Body = new CilBody();
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Ldsfld, field));
|
||||
for (int i = 0; i < method.Parameters.Count; i++)
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg, method.Parameters[i]));
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Call, delegateType.FindMethod("Invoke")));
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
||||
|
||||
delegateType.Methods.Add(method);
|
||||
|
||||
ctx.Context.Registry.GetService<IMarkerService>().Mark(method);
|
||||
ctx.Name.SetCanRename(method, false);
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
FieldDef CreateField(RPContext ctx, TypeDef delegateType)
|
||||
{
|
||||
// Details will be filled in during metadata writing
|
||||
TypeDef randomType;
|
||||
do
|
||||
{
|
||||
randomType = ctx.Module.Types[ctx.Random.NextInt32(ctx.Module.Types.Count)];
|
||||
} while (randomType.HasGenericParameters || randomType.IsGlobalModuleType || randomType.IsDelegate());
|
||||
|
||||
TypeSig fieldType = new CModOptSig(randomType, delegateType.ToTypeSig());
|
||||
|
||||
var field = new FieldDefUser("", new FieldSig(fieldType), FieldAttributes.Static | FieldAttributes.Assembly);
|
||||
field.CustomAttributes.Add(new CustomAttribute(GetKeyAttr(ctx).FindInstanceConstructors().First()));
|
||||
delegateType.Fields.Add(field);
|
||||
|
||||
ctx.Marker.Mark(field);
|
||||
ctx.Name.SetCanRename(field, false);
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
class CodeGen : CILCodeGen
|
||||
{
|
||||
Instruction[] arg;
|
||||
public CodeGen(Instruction[] arg, MethodDef method, IList<Instruction> instrs)
|
||||
: base(method, instrs)
|
||||
{
|
||||
this.arg = arg;
|
||||
}
|
||||
protected override void LoadVar(Variable var)
|
||||
{
|
||||
if (var.Name == "{RESULT}")
|
||||
{
|
||||
foreach (var instr in arg)
|
||||
base.Emit(instr);
|
||||
}
|
||||
else
|
||||
base.LoadVar(var);
|
||||
}
|
||||
}
|
||||
|
||||
TypeDef GetKeyAttr(RPContext ctx)
|
||||
{
|
||||
if (keyAttrs == null)
|
||||
keyAttrs = new Tuple<TypeDef, Func<int, int>>[0x10];
|
||||
|
||||
int index = (int)ctx.Random.NextInt32(keyAttrs.Length);
|
||||
if (keyAttrs[index] == null)
|
||||
{
|
||||
TypeDef rtType = ctx.Context.Registry.GetService<IRuntimeService>().GetRuntimeType("Confuser.Runtime.RefProxyKey");
|
||||
var injectedAttr = InjectHelper.Inject(rtType, ctx.Module);
|
||||
injectedAttr.Name = ctx.Name.RandomName();
|
||||
injectedAttr.Namespace = string.Empty;
|
||||
|
||||
Expression expression, inverse;
|
||||
Variable var = new Variable("{VAR}");
|
||||
Variable result = new Variable("{RESULT}");
|
||||
|
||||
ctx.DynCipher.GenerateExpressionPair(
|
||||
ctx.Random,
|
||||
new VariableExpression() { Variable = var }, new VariableExpression() { Variable = result },
|
||||
3, out expression, out inverse);
|
||||
|
||||
var expCompiled = new DMCodeGen(typeof(int), new[] { Tuple.Create("{VAR}", typeof(int)) })
|
||||
.GenerateCIL(expression)
|
||||
.Compile<Func<int, int>>();
|
||||
|
||||
MethodDef ctor = injectedAttr.FindMethod(".ctor");
|
||||
MutationHelper.ReplacePlaceholder(ctor, arg =>
|
||||
{
|
||||
var invCompiled = new List<Instruction>();
|
||||
new CodeGen(arg, ctor, invCompiled).GenerateCIL(inverse);
|
||||
return invCompiled.ToArray();
|
||||
});
|
||||
keyAttrs[index] = Tuple.Create(injectedAttr, expCompiled);
|
||||
|
||||
ctx.Module.AddAsNonNestedType(injectedAttr);
|
||||
|
||||
foreach (var def in injectedAttr.FindDefinitions())
|
||||
{
|
||||
ctx.Marker.Mark(def);
|
||||
if (def is FieldDef)
|
||||
def.Name = ctx.Name.RandomName();
|
||||
ctx.Name.SetCanRename(def, false);
|
||||
}
|
||||
}
|
||||
return keyAttrs[index].Item1;
|
||||
}
|
||||
|
||||
InitMethodDesc GetInitMethod(RPContext ctx, IRPEncoding encoding)
|
||||
{
|
||||
InitMethodDesc[] initDescs;
|
||||
if (!inits.TryGetValue(encoding, out initDescs))
|
||||
inits[encoding] = initDescs = new InitMethodDesc[0x10];
|
||||
|
||||
int index = (int)ctx.Random.NextInt32(initDescs.Length);
|
||||
if (initDescs[index] == null)
|
||||
{
|
||||
TypeDef rtType = ctx.Context.Registry.GetService<IRuntimeService>().GetRuntimeType("Confuser.Runtime.RefProxyStrong");
|
||||
var injectedMethod = InjectHelper.Inject(rtType.FindMethod("Initialize"), ctx.Module);
|
||||
ctx.Module.GlobalType.Methods.Add(injectedMethod);
|
||||
|
||||
injectedMethod.Access = MethodAttributes.PrivateScope;
|
||||
injectedMethod.Name = ctx.Name.RandomName();
|
||||
ctx.Name.SetCanRename(injectedMethod, false);
|
||||
ctx.Marker.Mark(injectedMethod);
|
||||
|
||||
InitMethodDesc desc = new InitMethodDesc() { Method = injectedMethod };
|
||||
|
||||
// Field name has five bytes, each bytes has different order & meaning
|
||||
int[] order = Enumerable.Range(0, 5).ToArray();
|
||||
ctx.Random.Shuffle(order);
|
||||
desc.OpCodeIndex = order[4];
|
||||
|
||||
desc.TokenNameOrder = new int[4];
|
||||
Array.Copy(order, 0, desc.TokenNameOrder, 0, 4);
|
||||
desc.TokenByteOrder = Enumerable.Range(0, 4).Select(x => x * 8).ToArray();
|
||||
ctx.Random.Shuffle(desc.TokenByteOrder);
|
||||
|
||||
int[] keyInjection = new int[9];
|
||||
Array.Copy(desc.TokenNameOrder, 0, keyInjection, 0, 4);
|
||||
Array.Copy(desc.TokenByteOrder, 0, keyInjection, 4, 4);
|
||||
keyInjection[8] = desc.OpCodeIndex;
|
||||
MutationHelper.InjectKeys(injectedMethod, Enumerable.Range(0, 9).ToArray(), keyInjection);
|
||||
|
||||
// Encoding
|
||||
MutationHelper.ReplacePlaceholder(injectedMethod, arg =>
|
||||
{
|
||||
return encoding.EmitDecode(injectedMethod, ctx, arg);
|
||||
});
|
||||
desc.Encoding = encoding;
|
||||
|
||||
initDescs[index] = desc;
|
||||
}
|
||||
return initDescs[index];
|
||||
}
|
||||
|
||||
class FieldDesc
|
||||
{
|
||||
public FieldDef Field;
|
||||
public Code OpCode;
|
||||
public IMethod Method;
|
||||
public InitMethodDesc InitDesc;
|
||||
public byte OpKey;
|
||||
}
|
||||
List<FieldDesc> fieldDescs = new List<FieldDesc>();
|
||||
|
||||
public override void Finalize(RPContext ctx)
|
||||
{
|
||||
foreach (var field in fields)
|
||||
{
|
||||
var init = GetInitMethod(ctx, field.Key.Item3);
|
||||
byte opKey;
|
||||
do
|
||||
{
|
||||
// No zero bytes
|
||||
opKey = ctx.Random.NextByte();
|
||||
} while (opKey == (byte)field.Key.Item1);
|
||||
|
||||
var delegateType = field.Value.Item1.DeclaringType;
|
||||
|
||||
var cctor = delegateType.FindOrCreateStaticConstructor();
|
||||
cctor.Body.Instructions.Insert(0, Instruction.Create(OpCodes.Call, init.Method));
|
||||
cctor.Body.Instructions.Insert(0, Instruction.CreateLdcI4(opKey));
|
||||
cctor.Body.Instructions.Insert(0, Instruction.Create(OpCodes.Ldtoken, field.Value.Item1));
|
||||
|
||||
fieldDescs.Add(new FieldDesc()
|
||||
{
|
||||
Field = field.Value.Item1,
|
||||
OpCode = field.Key.Item1,
|
||||
Method = field.Key.Item2,
|
||||
OpKey = opKey,
|
||||
InitDesc = init
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var delegateType in ctx.Delegates.Values)
|
||||
{
|
||||
var cctor = delegateType.FindOrCreateStaticConstructor();
|
||||
ctx.Marker.Mark(cctor);
|
||||
ctx.Name.SetCanRename(cctor, false);
|
||||
}
|
||||
|
||||
ctx.Context.CurrentModuleWriterListener.OnWriterEvent += EncodeField;
|
||||
encodeCtx = ctx;
|
||||
}
|
||||
|
||||
RPContext encodeCtx;
|
||||
void EncodeField(object sender, ModuleWriterListenerEventArgs e)
|
||||
{
|
||||
ModuleWriter writer = (ModuleWriter)sender;
|
||||
if (e.WriterEvent == ModuleWriterEvent.MDMemberDefRidsAllocated)
|
||||
{
|
||||
var keyFuncs = keyAttrs
|
||||
.Where(entry => entry != null)
|
||||
.ToDictionary(entry => entry.Item1, entry => entry.Item2);
|
||||
foreach (var desc in fieldDescs)
|
||||
{
|
||||
uint token = writer.MetaData.GetToken(desc.Method).Raw;
|
||||
uint key = encodeCtx.Random.NextUInt32() | 1;
|
||||
|
||||
// CA
|
||||
var ca = desc.Field.CustomAttributes[0];
|
||||
int encodedKey = keyFuncs[(TypeDef)ca.AttributeType]((int)MathsUtils.modInv(key));
|
||||
ca.ConstructorArguments.Add(new CAArgument(encodeCtx.Module.CorLibTypes.Int32, encodedKey));
|
||||
token *= key;
|
||||
|
||||
// Encoding
|
||||
token = (uint)desc.InitDesc.Encoding.Encode(desc.InitDesc.Method, encodeCtx, (int)token);
|
||||
|
||||
// Field name
|
||||
char[] name = new char[5];
|
||||
name[desc.InitDesc.OpCodeIndex] = (char)((byte)desc.OpCode ^ desc.OpKey);
|
||||
|
||||
byte[] nameKey = encodeCtx.Random.NextBytes(4);
|
||||
uint encodedNameKey = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
// No zero bytes
|
||||
while (nameKey[i] == 0)
|
||||
nameKey[i] = encodeCtx.Random.NextByte();
|
||||
name[desc.InitDesc.TokenNameOrder[i]] = (char)nameKey[i];
|
||||
encodedNameKey |= (uint)nameKey[i] << desc.InitDesc.TokenByteOrder[i];
|
||||
}
|
||||
desc.Field.Name = new string(name);
|
||||
|
||||
// Field sig
|
||||
FieldSig sig = desc.Field.FieldSig;
|
||||
uint encodedToken = (token - writer.MetaData.GetToken(((CModOptSig)sig.Type).Modifier).Raw) ^ encodedNameKey;
|
||||
|
||||
|
||||
byte[] extra = new byte[8];
|
||||
extra[0] = 0xc0;
|
||||
extra[3] = (byte)(encodedToken >> desc.InitDesc.TokenByteOrder[3]);
|
||||
extra[4] = 0xc0;
|
||||
extra[5] = (byte)(encodedToken >> desc.InitDesc.TokenByteOrder[2]);
|
||||
extra[6] = (byte)(encodedToken >> desc.InitDesc.TokenByteOrder[1]);
|
||||
extra[7] = (byte)(encodedToken >> desc.InitDesc.TokenByteOrder[0]);
|
||||
sig.ExtraData = extra;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -102,9 +102,9 @@ namespace Confuser.Renamer
|
|||
service.SetCanRename(type, false);
|
||||
}
|
||||
|
||||
if (type.BaseType != null && type.BaseType.DefinitionAssembly.IsCorLib() && type.BaseType.FullName == "System.Attribute")
|
||||
if (type.InheritsFromCorlib("System.Attribute"))
|
||||
{
|
||||
service.SetRenameMode(type, RenameMode.Letters);
|
||||
service.ReduceRenameMode(type, RenameMode.ASCII);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -73,10 +73,13 @@ namespace Confuser.Renamer.Analyzers
|
|||
TypeSig typeSig = (TypeSig)arg.Value;
|
||||
foreach (var typeRef in typeSig.FindTypeRefs())
|
||||
{
|
||||
if (!(typeRef is TypeRef)) continue;
|
||||
TypeDef typeDef = typeRef.ResolveTypeDefThrow();
|
||||
if (context.Modules.Contains((ModuleDefMD)typeDef.Module))
|
||||
service.AddReference(typeDef, new TypeRefReference((TypeRef)typeRef, typeDef));
|
||||
{
|
||||
if (typeRef is TypeRef)
|
||||
service.AddReference(typeDef, new TypeRefReference((TypeRef)typeRef, typeDef));
|
||||
service.ReduceRenameMode(typeDef, RenameMode.ASCII);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (arg.Value is CAArgument[])
|
||||
|
|
|
@ -69,6 +69,13 @@ namespace Confuser.Renamer.Analyzers
|
|||
foreach (var instrInfo in dpRegInstrs)
|
||||
{
|
||||
int[] args = trace.TraceArguments(instrInfo.Item2);
|
||||
if (args == null)
|
||||
{
|
||||
if (!erred)
|
||||
context.Logger.WarnFormat("Failed to extract dependency property name in '{0}'.", method.FullName);
|
||||
erred = true;
|
||||
continue;
|
||||
}
|
||||
Instruction ldstr = method.Body.Instructions[args[0]];
|
||||
if (ldstr.OpCode.Code != Code.Ldstr)
|
||||
{
|
||||
|
@ -129,6 +136,13 @@ namespace Confuser.Renamer.Analyzers
|
|||
foreach (var instr in routedEvtRegInstrs)
|
||||
{
|
||||
int[] args = trace.TraceArguments(instr);
|
||||
if (args == null)
|
||||
{
|
||||
if (!erred)
|
||||
context.Logger.WarnFormat("Failed to extract routed event name in '{0}'.", method.FullName);
|
||||
erred = true;
|
||||
continue;
|
||||
}
|
||||
Instruction ldstr = method.Body.Instructions[args[0]];
|
||||
if (ldstr.OpCode.Code != Code.Ldstr)
|
||||
{
|
||||
|
|
|
@ -302,7 +302,7 @@ namespace Confuser.Renamer.BAML
|
|||
var typeDef = type.ResolveTypeDefThrow();
|
||||
if (context.Modules.Contains((ModuleDefMD)typeDef.Module))
|
||||
{
|
||||
service.SetRenameMode(typeDef, RenameMode.Letters);
|
||||
service.ReduceRenameMode(typeDef, RenameMode.Letters);
|
||||
service.AddReference(typeDef, reference);
|
||||
}
|
||||
}
|
||||
|
@ -475,7 +475,7 @@ namespace Confuser.Renamer.BAML
|
|||
{
|
||||
var reference = new BAMLConverterMemberReference(xmlnsCtx, sig, property, rec);
|
||||
AddTypeSigReference(sig, reference);
|
||||
service.SetRenameMode(property, RenameMode.Letters);
|
||||
service.ReduceRenameMode(property, RenameMode.Letters);
|
||||
service.AddReference(property, reference);
|
||||
}
|
||||
FieldDef field = typeDef.FindField(cmdName);
|
||||
|
@ -483,7 +483,7 @@ namespace Confuser.Renamer.BAML
|
|||
{
|
||||
var reference = new BAMLConverterMemberReference(xmlnsCtx, sig, field, rec);
|
||||
AddTypeSigReference(sig, reference);
|
||||
service.SetRenameMode(field, RenameMode.Letters);
|
||||
service.ReduceRenameMode(field, RenameMode.Letters);
|
||||
service.AddReference(field, reference);
|
||||
}
|
||||
if (property == null && field == null)
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<OutputPath>..\Debug\bin\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
|
@ -25,7 +25,7 @@
|
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<OutputPath>..\Release\bin\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
|
|
|
@ -20,9 +20,11 @@ namespace Confuser.Renamer
|
|||
|
||||
RenameMode GetRenameMode(object obj);
|
||||
void SetRenameMode(object obj, RenameMode val);
|
||||
void ReduceRenameMode(object obj, RenameMode val);
|
||||
|
||||
string ObfuscateName(string name, RenameMode mode);
|
||||
string RandomName();
|
||||
string RandomName(RenameMode mode);
|
||||
|
||||
void RegisterRenamer(IRenamer renamer);
|
||||
void AddReference<T>(T obj, INameReference<T> reference);
|
||||
|
@ -83,6 +85,12 @@ namespace Confuser.Renamer
|
|||
{
|
||||
context.Annotations.Set<RenameMode>(obj, RenameModeKey, val);
|
||||
}
|
||||
public void ReduceRenameMode(object obj, RenameMode val)
|
||||
{
|
||||
RenameMode original = GetRenameMode(obj);
|
||||
if (original < val)
|
||||
context.Annotations.Set<RenameMode>(obj, RenameModeKey, val);
|
||||
}
|
||||
|
||||
static readonly object ReferencesKey = new object();
|
||||
public void AddReference<T>(T obj, INameReference<T> reference)
|
||||
|
@ -110,7 +118,10 @@ namespace Confuser.Renamer
|
|||
}
|
||||
|
||||
#region Charsets
|
||||
static char[] asciiCharset = Enumerable.Range(32, 95).Select(ord => (char)ord).ToArray();
|
||||
static char[] asciiCharset = Enumerable.Range(32, 95)
|
||||
.Select(ord => (char)ord)
|
||||
.Except(new [] { '.' })
|
||||
.ToArray();
|
||||
static char[] letterCharset = Enumerable.Range(0, 26)
|
||||
.SelectMany(ord => new[] { (char)('a' + ord), (char)('A' + ord) })
|
||||
.ToArray();
|
||||
|
@ -126,6 +137,9 @@ namespace Confuser.Renamer
|
|||
|
||||
public string ObfuscateName(string name, RenameMode mode)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
return string.Empty;
|
||||
|
||||
if (mode == RenameMode.Empty)
|
||||
return "";
|
||||
else if (mode == RenameMode.Debug)
|
||||
|
@ -148,7 +162,11 @@ namespace Confuser.Renamer
|
|||
}
|
||||
public string RandomName()
|
||||
{
|
||||
return ObfuscateName(Utils.ToHexString(random.NextBytes(16)), RenameMode.Unicode);
|
||||
return RandomName(RenameMode.Unicode);
|
||||
}
|
||||
public string RandomName(RenameMode mode)
|
||||
{
|
||||
return ObfuscateName(Utils.ToHexString(random.NextBytes(16)), mode);
|
||||
}
|
||||
|
||||
static readonly object OriginalNameKey = new object();
|
||||
|
|
|
@ -7,7 +7,7 @@ using Confuser.Core;
|
|||
|
||||
namespace Confuser.Renamer.References
|
||||
{
|
||||
class MemberRefReference : INameReference<IDefinition>
|
||||
public class MemberRefReference : INameReference<IDefinition>
|
||||
{
|
||||
MemberRef memberRef;
|
||||
IDefinition memberDef;
|
||||
|
|
|
@ -27,14 +27,16 @@ namespace Confuser.Renamer.References
|
|||
{
|
||||
var declType = (GenericInstSig)baseSlot.DeclaringType;
|
||||
target = new MemberRefUser(method.Module, baseSlot.MethodDef.Name, baseSlot.MethodDef.MethodSig, declType.ToTypeDefOrRef());
|
||||
target = (IMethodDefOrRef)method.Module.Import(target);
|
||||
target = (IMethodDefOrRef)new Importer(method.Module, ImporterOptions.TryToUseTypeDefs).Import(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
target = baseSlot.MethodDef;
|
||||
if (target.Module != method.Module)
|
||||
target = (IMethodDefOrRef)method.Module.Import(baseSlot.MethodDef);
|
||||
target = (IMethodDefOrRef)new Importer(method.Module, ImporterOptions.TryToUseTypeDefs).Import(baseSlot.MethodDef);
|
||||
}
|
||||
if(target is MemberRef)
|
||||
service.AddReference(baseSlot.MethodDef, new MemberRefReference((MemberRef)target, baseSlot.MethodDef));
|
||||
|
||||
if (method.Overrides.Any(impl =>
|
||||
new SigComparer().Equals(impl.MethodDeclaration.MethodSig, target.MethodSig) &&
|
||||
|
|
|
@ -7,7 +7,7 @@ using Confuser.Core;
|
|||
|
||||
namespace Confuser.Renamer.References
|
||||
{
|
||||
class TypeRefReference : INameReference<TypeDef>
|
||||
public class TypeRefReference : INameReference<TypeDef>
|
||||
{
|
||||
TypeRef typeRef;
|
||||
TypeDef typeDef;
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace Confuser.Renamer
|
|||
typeDef.Namespace = service.ObfuscateName(typeDef.Namespace, mode);
|
||||
}
|
||||
|
||||
foreach (var refer in references)
|
||||
foreach (var refer in references.ToList())
|
||||
{
|
||||
if (!refer.UpdateNameReference(context, service))
|
||||
{
|
||||
|
@ -63,9 +63,6 @@ namespace Confuser.Renamer
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var module in context.Modules)
|
||||
module.ResetTypeDefFindCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,9 +41,9 @@
|
|||
<Compile Include="antinet\AntiManagedProfiler.cs" />
|
||||
<Compile Include="antinet\HandleProcessCorruptedStateExceptionsAttribute.cs" />
|
||||
<Compile Include="antinet\PEInfo.cs" />
|
||||
<Compile Include="Mutation.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="RefProxy.Strong.cs" />
|
||||
<Compile Include="AntiDebug.Safe.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
class Mutation
|
||||
{
|
||||
public static readonly int KeyI0 = 0;
|
||||
public static readonly int KeyI1 = 1;
|
||||
public static readonly int KeyI2 = 2;
|
||||
public static readonly int KeyI3 = 3;
|
||||
public static readonly int KeyI4 = 4;
|
||||
public static readonly int KeyI5 = 5;
|
||||
public static readonly int KeyI6 = 6;
|
||||
public static readonly int KeyI7 = 7;
|
||||
public static readonly int KeyI8 = 8;
|
||||
public static readonly int KeyI9 = 9;
|
||||
public static readonly int KeyI10 = 10;
|
||||
public static readonly int KeyI11 = 11;
|
||||
public static readonly int KeyI12 = 12;
|
||||
public static readonly int KeyI13 = 13;
|
||||
public static readonly int KeyI14 = 14;
|
||||
public static readonly int KeyI15 = 15;
|
||||
|
||||
public static int Placeholder(int val)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Confuser.Runtime
|
||||
{
|
||||
class RefProxyKey : Attribute
|
||||
{
|
||||
int key;
|
||||
public RefProxyKey(int key)
|
||||
{
|
||||
this.key = Mutation.Placeholder(key);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
static class RefProxyStrong
|
||||
{
|
||||
internal static void Initialize(RuntimeFieldHandle field, byte opKey)
|
||||
{
|
||||
FieldInfo fieldInfo = FieldInfo.GetFieldFromHandle(field);
|
||||
var sig = fieldInfo.Module.ResolveSignature(fieldInfo.MetadataToken);
|
||||
int len = sig.Length;
|
||||
int key = fieldInfo.GetOptionalCustomModifiers()[0].MetadataToken;
|
||||
|
||||
key += (fieldInfo.Name[Mutation.KeyI0] ^ sig[--len]) << Mutation.KeyI4;
|
||||
key += (fieldInfo.Name[Mutation.KeyI1] ^ sig[--len]) << Mutation.KeyI5;
|
||||
key += (fieldInfo.Name[Mutation.KeyI2] ^ sig[--len]) << Mutation.KeyI6;
|
||||
len--;
|
||||
key += (fieldInfo.Name[Mutation.KeyI3] ^ sig[--len]) << Mutation.KeyI7;
|
||||
|
||||
int token = Mutation.Placeholder(key);
|
||||
token *= fieldInfo.GetCustomAttributes(false)[0].GetHashCode();
|
||||
|
||||
var method = fieldInfo.Module.ResolveMethod(token);
|
||||
Type delegateType = fieldInfo.FieldType;
|
||||
if (method.IsStatic)
|
||||
fieldInfo.SetValue(null, Delegate.CreateDelegate(delegateType, (MethodInfo)method));
|
||||
|
||||
else
|
||||
{
|
||||
DynamicMethod dm = null;
|
||||
Type[] argTypes = null;
|
||||
|
||||
foreach (var invoke in fieldInfo.FieldType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance))
|
||||
if (invoke.DeclaringType == delegateType)
|
||||
{
|
||||
var paramTypes = invoke.GetParameters();
|
||||
argTypes = new Type[paramTypes.Length];
|
||||
for (int i = 0; i < argTypes.Length; i++)
|
||||
argTypes[i] = paramTypes[i].ParameterType;
|
||||
|
||||
var declType = method.DeclaringType;
|
||||
dm = new DynamicMethod("", invoke.ReturnType, argTypes, (declType.IsInterface || declType.IsArray) ? delegateType : declType, true);
|
||||
break;
|
||||
}
|
||||
|
||||
var info = dm.GetDynamicILInfo();
|
||||
info.SetLocalSignature(new byte[] { 0x7, 0x0 });
|
||||
byte[] code = new byte[2 * argTypes.Length + 6];
|
||||
int index = 0;
|
||||
for (int i = 0; i < argTypes.Length; i++)
|
||||
{
|
||||
code[index++] = 0x0e;
|
||||
code[index++] = (byte)i;
|
||||
}
|
||||
code[index++] = (byte)((byte)fieldInfo.Name[Mutation.KeyI8] ^ opKey);
|
||||
int dmToken = info.GetTokenFor(method.MethodHandle);
|
||||
code[index++] = (byte)dmToken;
|
||||
code[index++] = (byte)(dmToken >> 8);
|
||||
code[index++] = (byte)(dmToken >> 16);
|
||||
code[index++] = (byte)(dmToken >> 24);
|
||||
code[index] = 0x2a;
|
||||
info.SetCode(code, argTypes.Length + 1);
|
||||
|
||||
fieldInfo.SetValue(null, dm.CreateDelegate(delegateType));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
2
dnlib
2
dnlib
|
@ -1 +1 @@
|
|||
Subproject commit 1473092e24b2b08e58e7e50f32f2c266d8919dbe
|
||||
Subproject commit 03b22aba6e6fe9bf2c7e0be0b5fb425414f80809
|
Загрузка…
Ссылка в новой задаче