added basic control flow protection

This commit is contained in:
yck1509 2014-03-05 00:03:12 +08:00
Родитель bf53857c6b
Коммит ad552fd420
18 изменённых файлов: 612 добавлений и 18 удалений

Просмотреть файл

@ -43,9 +43,6 @@
<Private>True</Private>
<HintPath>..\packages\TaskParallelLibrary.1.0.2856.0\lib\Net35\System.Threading.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -137,6 +137,10 @@ namespace Confuser.Core
{
context.Logger.ErrorException("Failed to resolve a member, check if all dependencies are of correct version.", ex);
}
catch (IOException ex)
{
context.Logger.ErrorException("An IO error occured, check if all input/output locations are read/writable.", ex);
}
catch (OperationCanceledException)
{
context.Logger.Error("Operation is canceled.");
@ -320,6 +324,8 @@ namespace Confuser.Core
static void WriteModule(ConfuserContext context)
{
context.Logger.InfoFormat("Writing module '{0}'...", context.CurrentModule.Name);
MemoryStream ms = new MemoryStream();
if (context.CurrentModuleWriterOptions is ModuleWriterOptions)
context.CurrentModule.Write(ms, (ModuleWriterOptions)context.CurrentModuleWriterOptions);

Просмотреть файл

@ -280,6 +280,9 @@ namespace Confuser.Core
}
/// <summary>
/// <see cref="Stream"/> wrapper of <see cref="IImageStream"/>.
/// </summary>
public class ImageStream : Stream
{
/// <summary>

Просмотреть файл

@ -148,6 +148,7 @@ namespace Confuser.Core
/// <summary>
/// Parses the rules' patterns.
/// </summary>
/// <param name="proj">The project.</param>
/// <param name="module">The module description.</param>
/// <param name="context">The working context.</param>
/// <returns>Parsed rule patterns.</returns>

Просмотреть файл

@ -47,7 +47,7 @@ namespace Confuser.Core
/// <returns>The value of the parameter.</returns>
public T GetParameter<T>(ConfuserContext context, IDefinition target, string name, T defValue = default(T))
{
var objParams = context.Annotations.Get<ProtectionSettings>(comp, ParametersKey);
var objParams = context.Annotations.Get<ProtectionSettings>(target, ParametersKey);
if (objParams == null)
return defValue;
Dictionary<string, string> protParams;
@ -55,7 +55,13 @@ namespace Confuser.Core
return defValue;
string ret;
if (protParams.TryGetValue(name, out ret))
return (T)Convert.ChangeType(ret, typeof(T));
{
Type paramType = typeof(T);
if (paramType.IsEnum)
return (T)Enum.Parse(paramType, ret, true);
else
return (T)Convert.ChangeType(ret, typeof(T));
}
else
return defValue;
}

Просмотреть файл

@ -98,8 +98,8 @@ namespace Confuser.Core.Services
else
{
Buffer.BlockCopy(state, 32 - stateFilled, buffer, offset, length);
length = 0;
stateFilled -= length;
length = 0;
}
if (stateFilled == 0)
NextState();
@ -155,6 +155,15 @@ namespace Confuser.Core.Services
return BitConverter.ToUInt32(NextBytes(4), 0);
}
/// <summary>
/// Returns a random double floating pointer number from 0 (inclusive) to 1 (exclusive).
/// </summary>
/// <returns>Requested random number.</returns>
public double NextDouble()
{
return NextUInt32() / ((double)uint.MaxValue + 1);
}
/// <summary>
/// Returns a random boolean value.
/// </summary>
@ -167,6 +176,22 @@ namespace Confuser.Core.Services
NextState();
return s % 2 == 0;
}
/// <summary>
/// Shuffles the element in the specified list.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list">The list to shuffle.</param>
public void Shuffle<T>(IList<T> list)
{
for (int i = list.Count - 1; i > 1; i--)
{
int k = NextInt32(i + 1);
T tmp = list[k];
list[k] = list[i];
list[i] = tmp;
}
}
}
/// <summary>

Просмотреть файл

@ -40,7 +40,7 @@ namespace Confuser.Core.Services
/// Trace the stack of the specified method.
/// </summary>
/// <param name="method">The method to trace.</param>
/// <exception cref="InvalidMethodException"><paramref name="member"/> has invalid body.</exception>
/// <exception cref="InvalidMethodException"><paramref name="method"/> has invalid body.</exception>
/// <exception cref="System.ArgumentNullException"><paramref name="method"/> is <c>null</c>.</exception>
MethodTrace Trace(MethodDef method);
}
@ -104,7 +104,7 @@ namespace Confuser.Core.Services
for (int i = 0; i < instrs.Count; i++)
{
offset2index.Add(instrs[i].Offset, i);
depths[i] = -1;
depths[i] = int.MinValue;
}
foreach (var eh in body.ExceptionHandlers)
@ -121,7 +121,7 @@ namespace Confuser.Core.Services
{
var instr = instrs[i];
if (depths[i] != -1) // Already set due to being target of a branch / beginning of EHs.
if (depths[i] != int.MinValue) // Already set due to being target of a branch / beginning of EHs.
currentStack = depths[i];
switch (instr.OpCode.FlowControl)
@ -160,7 +160,6 @@ namespace Confuser.Core.Services
currentStack = 0;
break;
case FlowControl.Throw:
currentStack = 0;
break;
default:
throw new UnreachableException();
@ -170,7 +169,7 @@ namespace Confuser.Core.Services
depths[i] = currentStack;
}
foreach (var stackDepth in depths)
if (stackDepth == -1)
if (stackDepth == int.MinValue)
throw new InvalidMethodException("Bad method body.");
StackDepths = depths;

Просмотреть файл

@ -11,7 +11,6 @@ namespace Confuser.Protections
{
public const string _Id = "anti ildasm";
public const string _FullId = "Ki.AntiILDasm";
public const string _ServiceId = "Ki.AntiILDasm";
protected override void Initialize(ConfuserContext context)
{

Просмотреть файл

@ -43,8 +43,13 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AntiILDasmProtection.cs" />
<Compile Include="ControlFlow\CFContext.cs" />
<Compile Include="ControlFlow\ControlFlowPhase.cs" />
<Compile Include="ControlFlow\ControlFlowProtection.cs" />
<Compile Include="ControlFlow\BlockParser.cs" />
<Compile Include="ControlFlow\Blocks.cs" />
<Compile Include="ControlFlow\JumpMangler.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ReflectorTrapProtection.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
@ -63,6 +68,7 @@
<Name>dnlib</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">

Просмотреть файл

@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using dnlib.DotNet.Emit;
using Confuser.Core;
using System.Diagnostics;
namespace Confuser.Protections.ControlFlow
{
static class BlockParser
{
public static ScopeBlock ParseBody(CilBody body)
{
var ehScopes = new Dictionary<ExceptionHandler, Tuple<ScopeBlock, ScopeBlock, ScopeBlock>>();
foreach (var eh in body.ExceptionHandlers)
{
ScopeBlock tryBlock = new ScopeBlock(BlockType.Try, eh);
BlockType handlerType = BlockType.Handler;
if (eh.HandlerType == ExceptionHandlerType.Finally)
handlerType = BlockType.Finally;
else if (eh.HandlerType == ExceptionHandlerType.Fault)
handlerType = BlockType.Fault;
ScopeBlock handlerBlock = new ScopeBlock(handlerType, eh);
if (eh.FilterStart != null)
{
ScopeBlock filterBlock = new ScopeBlock(BlockType.Filter, eh);
ehScopes[eh] = Tuple.Create(tryBlock, handlerBlock, filterBlock);
}
else
ehScopes[eh] = Tuple.Create(tryBlock, handlerBlock, (ScopeBlock)null);
}
ScopeBlock root = new ScopeBlock(BlockType.Normal, null);
Stack<ScopeBlock> scopeStack = new Stack<ScopeBlock>();
scopeStack.Push(root);
foreach (var instr in body.Instructions)
{
foreach (var eh in body.ExceptionHandlers)
{
var ehScope = ehScopes[eh];
if (instr == eh.TryEnd)
scopeStack.Pop();
if (instr == eh.HandlerEnd)
scopeStack.Pop();
if (eh.FilterStart != null && instr == eh.HandlerStart)
{
// Filter must precede handler immediately
Debug.Assert(scopeStack.Peek().Type == BlockType.Filter);
scopeStack.Pop();
}
}
foreach (var eh in body.ExceptionHandlers.Reverse())
{
var ehScope = ehScopes[eh];
var parent = scopeStack.Count > 0 ? scopeStack.Peek() : null;
if (instr == eh.TryStart)
{
if (parent != null)
parent.Children.Add(ehScope.Item1);
scopeStack.Push(ehScope.Item1);
}
if (instr == eh.HandlerStart)
{
if (parent != null)
parent.Children.Add(ehScope.Item2);
scopeStack.Push(ehScope.Item2);
}
if (instr == eh.FilterStart)
{
if (parent != null)
parent.Children.Add(ehScope.Item3);
scopeStack.Push(ehScope.Item3);
}
}
ScopeBlock scope = scopeStack.Peek();
InstrBlock block = scope.Children.LastOrDefault() as InstrBlock;
if (block == null)
scope.Children.Add(block = new InstrBlock());
block.Instructions.Add(instr);
}
Debug.Assert(scopeStack.Count == 1);
return root;
}
}
}

Просмотреть файл

@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using dnlib.DotNet.Emit;
namespace Confuser.Protections.ControlFlow
{
abstract class BlockBase
{
public BlockBase(BlockType type)
{
Type = type;
}
public ScopeBlock Parent { get; private set; }
public abstract void ToBody(CilBody body);
public BlockType Type { get; private set; }
}
enum BlockType
{
Normal,
Try,
Handler,
Finally,
Filter,
Fault
}
class ScopeBlock : BlockBase
{
public ScopeBlock(BlockType type, ExceptionHandler handler)
: base(type)
{
this.Handler = handler;
this.Children = new List<BlockBase>();
}
public ExceptionHandler Handler { get; private set; }
public List<BlockBase> Children { get; set; }
public override string ToString()
{
StringBuilder ret = new StringBuilder();
if (Type == BlockType.Try)
ret.Append("try ");
else if (Type == BlockType.Handler)
ret.Append("handler ");
else if (Type == BlockType.Finally)
ret.Append("finally ");
else if (Type == BlockType.Fault)
ret.Append("fault ");
ret.AppendLine("{");
foreach (var child in Children)
ret.Append(child.ToString());
ret.AppendLine("}");
return ret.ToString();
}
public Instruction GetFirstInstr()
{
BlockBase firstBlock = Children.First();
if (firstBlock is ScopeBlock)
return ((ScopeBlock)firstBlock).GetFirstInstr();
else
return ((InstrBlock)firstBlock).Instructions.First();
}
public Instruction GetLastInstr()
{
BlockBase firstBlock = Children.Last();
if (firstBlock is ScopeBlock)
return ((ScopeBlock)firstBlock).GetLastInstr();
else
return ((InstrBlock)firstBlock).Instructions.Last();
}
public override void ToBody(CilBody body)
{
if (Type != BlockType.Normal)
{
if (Type == BlockType.Try)
{
Handler.TryStart = GetFirstInstr();
Handler.TryEnd = GetLastInstr();
}
else if (Type == BlockType.Filter)
{
Handler.FilterStart = GetFirstInstr();
}
else
{
Handler.HandlerStart = GetFirstInstr();
Handler.HandlerEnd = GetLastInstr();
}
}
foreach (var block in Children)
block.ToBody(body);
}
}
class InstrBlock : BlockBase
{
public InstrBlock()
: base(BlockType.Normal)
{
this.Instructions = new List<Instruction>();
}
public List<Instruction> Instructions { get; set; }
public override string ToString()
{
StringBuilder ret = new StringBuilder();
foreach (var instr in Instructions)
ret.AppendLine(instr.ToString());
return ret.ToString();
}
public override void ToBody(CilBody body)
{
foreach (var instr in Instructions)
body.Instructions.Add(instr);
}
}
}

Просмотреть файл

@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Confuser.Core.Services;
using dnlib.DotNet.Emit;
using dnlib.DotNet;
namespace Confuser.Protections.ControlFlow
{
enum CFType
{
Switch,
Jump,
}
class CFContext
{
public RandomGenerator Random;
public MethodDef Method;
public CFType Type;
public double Intensity;
public bool JunkCode;
public bool FakeBranch;
public void AddJump(IList<Instruction> instrs, Instruction target, IList<Instruction> candidates)
{
if (!Method.Module.IsClr40 && JunkCode &&
!Method.DeclaringType.HasGenericParameters && !Method.HasGenericParameters &&
(instrs[0].OpCode.FlowControl == FlowControl.Call || instrs[0].OpCode.FlowControl == FlowControl.Next))
{
switch (Random.NextInt32(3))
{
case 0:
instrs.Add(Instruction.Create(OpCodes.Ldc_I4_0));
instrs.Add(Instruction.Create(OpCodes.Brtrue, instrs[0]));
break;
case 1:
instrs.Add(Instruction.Create(OpCodes.Ldc_I4_1));
instrs.Add(Instruction.Create(OpCodes.Brfalse, instrs[0]));
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));
instrs.Add(Instruction.Create(OpCodes.Brfalse, instrs[0]));
break;
}
}
instrs.Add(Instruction.Create(OpCodes.Br, target));
}
public void AddJunk(IList<Instruction> instrs)
{
if (Method.Module.IsClr40 || !JunkCode)
return;
switch (Random.NextInt32(6))
{
case 0:
instrs.Add(Instruction.Create(OpCodes.Pop));
break;
case 1:
instrs.Add(Instruction.Create(OpCodes.Dup));
break;
case 2:
instrs.Add(Instruction.Create(OpCodes.Throw));
break;
case 3:
instrs.Add(Instruction.Create(OpCodes.Ldarg, new Parameter(0xff)));
break;
case 4:
instrs.Add(Instruction.Create(OpCodes.Ldloc, new Local(null) { Index = 0xff }));
break;
case 5:
break;
}
}
}
}

Просмотреть файл

@ -0,0 +1,69 @@
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;
namespace Confuser.Protections.ControlFlow
{
class ControlFlowPhase : ProtectionPhase
{
public ControlFlowPhase(ControlFlowProtection parent)
: base(parent)
{
}
public override ProtectionTargets Targets
{
get { return ProtectionTargets.Methods; }
}
CFContext ParseParameter(
MethodDef method, ConfuserContext context,
ProtectionParameters parameters, RandomGenerator random)
{
CFContext ret = new CFContext();
ret.Type = parameters.GetParameter<CFType>(context, method, "type", CFType.Switch);
int rawIntensity = parameters.GetParameter<int>(context, method, "intensity", 60);
ret.Intensity = rawIntensity / 100.0;
ret.JunkCode = parameters.GetParameter<bool>(context, method, "junk", false);
ret.FakeBranch = parameters.GetParameter<bool>(context, method, "fakeBr", false);
ret.Random = random;
ret.Method = method;
return ret;
}
protected override void Execute(ConfuserContext context, ProtectionParameters parameters)
{
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, ParseParameter(method, context, parameters, random));
}
context.CurrentModuleWriterOptions.MetaDataOptions.Flags |= dnlib.DotNet.Writer.MetaDataFlags.KeepOldMaxStack;
}
void ProcessMethod(CilBody body, CFContext ctx)
{
ScopeBlock root = BlockParser.ParseBody(body);
JumpMangler.Mangle(body, root, ctx);
body.Instructions.Clear();
root.ToBody(body);
foreach (var eh in body.ExceptionHandlers)
{
eh.TryEnd = body.Instructions[body.Instructions.IndexOf(eh.TryEnd) + 1];
eh.HandlerEnd = body.Instructions[body.Instructions.IndexOf(eh.HandlerEnd) + 1];
}
}
}
}

Просмотреть файл

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Confuser.Core;
using dnlib.DotNet;
using Confuser.Protections.ControlFlow;
namespace Confuser.Protections
{
class ControlFlowProtection : Protection
{
public const string _Id = "ctrl flow";
public const string _FullId = "Ki.ControlFlow";
public const string _ServiceId = "Ki.ControlFlow";
protected override void Initialize(ConfuserContext context)
{
//
}
protected override void PopulatePipeline(ProtectionPipeline pipeline)
{
pipeline.InsertPreStage(PipelineStage.OptimizeMethods, new ControlFlowPhase(this));
}
public override string Name
{
get { return "Control Flow Protection"; }
}
public override string Description
{
get { return "This protection mangles the code in the methods so that decompilers cannot decompile the methods."; }
}
public override string Id
{
get { return _Id; }
}
public override string FullId
{
get { return _FullId; }
}
public override ProtectionPreset Preset
{
get { return ProtectionPreset.Aggressive; }
}
}
}

Просмотреть файл

@ -0,0 +1,124 @@
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.Protections.ControlFlow
{
class JumpMangler
{
static IEnumerable<InstrBlock> GetAllBlocks(ScopeBlock scope)
{
foreach (var child in scope.Children)
{
if (child is InstrBlock)
yield return (InstrBlock)child;
else
{
foreach (var block in GetAllBlocks((ScopeBlock)child))
yield return block;
}
}
}
static LinkedList<Instruction[]> SpiltFragments(InstrBlock block, CFContext ctx)
{
LinkedList<Instruction[]> fragments = new LinkedList<Instruction[]>();
List<Instruction> currentFragment = new List<Instruction>();
int skipCount = -1;
for (int i = 0; i < block.Instructions.Count; i++)
{
if (skipCount != -1)
{
if (skipCount > 0)
{
currentFragment.Add(block.Instructions[i]);
skipCount--;
continue;
}
else
{
fragments.AddLast(currentFragment.ToArray());
currentFragment.Clear();
skipCount = -1;
}
}
if (block.Instructions[i].OpCode.OpCodeType == OpCodeType.Prefix)
{
skipCount = 1;
currentFragment.Add(block.Instructions[i]);
continue;
}
else if (i + 2 < block.Instructions.Count &&
block.Instructions[i + 0].OpCode.Code == Code.Dup &&
block.Instructions[i + 1].OpCode.Code == Code.Ldvirtftn &&
block.Instructions[i + 2].OpCode.Code == Code.Newobj)
{
skipCount = 2;
currentFragment.Add(block.Instructions[i]);
continue;
}
else if (i + 1 < block.Instructions.Count &&
block.Instructions[i + 0].OpCode.Code == Code.Ldftn &&
block.Instructions[i + 1].OpCode.Code == Code.Newobj)
{
skipCount = 1;
currentFragment.Add(block.Instructions[i]);
continue;
}
else
{
currentFragment.Add(block.Instructions[i]);
if (ctx.Intensity > ctx.Random.NextDouble())
{
fragments.AddLast(currentFragment.ToArray());
currentFragment.Clear();
}
}
}
if (currentFragment.Count > 0)
fragments.AddLast(currentFragment.ToArray());
return fragments;
}
public static void Mangle(CilBody body, ScopeBlock root, CFContext ctx)
{
body.MaxStack++;
foreach (var block in GetAllBlocks(root))
{
var fragments = SpiltFragments(block, ctx);
if (fragments.Count < 4) continue;
var current = fragments.First;
while (current.Next != null)
{
List<Instruction> newFragment = new List<Instruction>(current.Value);
ctx.AddJump(newFragment, current.Next.Value[0], block.Instructions);
ctx.AddJunk(newFragment);
current.Value = newFragment.ToArray();
current = current.Next;
}
Instruction[] first = fragments.First.Value;
fragments.RemoveFirst();
Instruction[] last = fragments.Last.Value;
fragments.RemoveLast();
var newFragments = fragments.ToList();
ctx.Random.Shuffle(newFragments);
block.Instructions = first
.Concat(newFragments.SelectMany(fragment => fragment))
.Concat(last).ToList();
}
}
}
}

Просмотреть файл

@ -33,9 +33,6 @@
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -35,7 +35,7 @@ namespace Confuser.Renamer
public NameService(ConfuserContext context)
{
this.context = context;
this.random = context.Registry.GetService<IRandomService>().GetRandomGenerator(NameProtection._ServiceId);
this.random = context.Registry.GetService<IRandomService>().GetRandomGenerator(NameProtection._FullId);
this.nameSeed = random.NextBytes(20);
this.Renamers = new List<IRenamer>()

2
dnlib

@ -1 +1 @@
Subproject commit a1bf585fee79a4a7bf709b79c8c27c33cf37a168
Subproject commit 99c04ee6a1ffa4fd826d8386fdf6321a1cab9f5f