Improve constant CFG references

This commit is contained in:
yck1509 2015-03-09 22:07:51 +08:00
Родитель 31e6deb0d8
Коммит ddeb06fffe
4 изменённых файлов: 407 добавлений и 68 удалений

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

@ -63,7 +63,7 @@ namespace Confuser.Core.Helpers {
/// Computes a key sequence of the given CFG.
/// </summary>
/// <param name="graph">The CFG.</param>
/// <param name="random">The random source.</param>
/// <param name="random">The random source, or <c>null</c> if key id is needed.</param>
/// <returns>The generated key sequence of the CFG.</returns>
public static BlockKey[] ComputeKeys(ControlFlowGraph graph, RandomGenerator random) {
var keys = new BlockKey[graph.Count];
@ -190,20 +190,22 @@ namespace Confuser.Core.Helpers {
}
} while (updated);
// Replace id with actual values
var idMap = new Dictionary<uint, uint>();
for (int i = 0; i < keys.Length; i++) {
BlockKey key = keys[i];
if (random != null) {
// Replace id with actual values
var idMap = new Dictionary<uint, uint>();
for (int i = 0; i < keys.Length; i++) {
BlockKey key = keys[i];
uint entryId = key.EntryState;
if (!idMap.TryGetValue(entryId, out key.EntryState))
key.EntryState = idMap[entryId] = random.NextUInt32();
uint entryId = key.EntryState;
if (!idMap.TryGetValue(entryId, out key.EntryState))
key.EntryState = idMap[entryId] = random.NextUInt32();
uint exitId = key.ExitState;
if (!idMap.TryGetValue(exitId, out key.ExitState))
key.ExitState = idMap[exitId] = random.NextUInt32();
uint exitId = key.ExitState;
if (!idMap.TryGetValue(exitId, out key.ExitState))
key.ExitState = idMap[exitId] = random.NextUInt32();
keys[i] = key;
keys[i] = key;
}
}
}
}

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

@ -32,6 +32,9 @@ namespace Confuser.Protections.Constants {
public INameService Name;
public RandomGenerator Random;
public TypeDef CfgCtxType;
public MethodDef CfgCtxCtor;
public MethodDef CfgCtxNext;
public Dictionary<MethodDef, List<Tuple<Instruction, uint, IMethod>>> ReferenceRepl;
}

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

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Confuser.Core;
using Confuser.Core.Helpers;
using Confuser.Core.Services;
@ -27,86 +28,338 @@ namespace Confuser.Protections.Constants {
}
struct CFGContext {
public CEContext Ctx;
public ControlFlowGraph Graph;
public BlockKey[] Keys;
public RandomGenerator Random;
public Dictionary<uint, CFGState> StatesMap;
public Local StateVariable;
}
struct CFGState {
public uint A;
public uint B;
public uint C;
public uint D;
public CFGState(uint seed) {
A = seed *= 0x21412321;
B = seed *= 0x21412321;
C = seed *= 0x21412321;
D = seed *= 0x21412321;
}
public void UpdateExplicit(int id, uint value) {
switch (id) {
case 0:
A = value;
break;
case 1:
B = value;
break;
case 2:
C = value;
break;
case 3:
D = value;
break;
}
}
public void UpdateIncremental(int id, uint value) {
switch (id) {
case 0:
A *= value;
break;
case 1:
B += value;
break;
case 2:
C ^= value;
break;
case 3:
D -= value;
break;
}
}
public uint GetIncrementalUpdate(int id, uint target) {
switch (id) {
case 0:
return A ^ target;
case 1:
return target - B;
case 2:
return C ^ target;
case 3:
return D - target;
}
throw new UnreachableException();
}
public uint Get(int id) {
switch (id) {
case 0:
return A;
case 1:
return B;
case 2:
return C;
case 3:
return D;
}
throw new UnreachableException();
}
public static byte EncodeFlag(bool exp, int updateId, int getId) {
byte fl = (byte)(exp ? 0x80 : 0);
fl |= (byte)updateId;
fl |= (byte)(getId << 2);
return fl;
}
}
static void InjectStateType(CEContext ctx) {
if (ctx.CfgCtxType == null) {
var type = ctx.Context.Registry.GetService<IRuntimeService>().GetRuntimeType("Confuser.Runtime.CFGCtx");
ctx.CfgCtxType = InjectHelper.Inject(type, ctx.Module);
ctx.Module.Types.Add(ctx.CfgCtxType);
ctx.CfgCtxCtor = ctx.CfgCtxType.FindMethod(".ctor");
ctx.CfgCtxNext = ctx.CfgCtxType.FindMethod("Next");
ctx.Name.MarkHelper(ctx.CfgCtxType, ctx.Marker, ctx.Protection);
foreach (var def in ctx.CfgCtxType.Fields)
ctx.Name.MarkHelper(def, ctx.Marker, ctx.Protection);
foreach (var def in ctx.CfgCtxType.Methods)
ctx.Name.MarkHelper(def, ctx.Marker, ctx.Protection);
}
}
static void InsertEmptyStateUpdate(CFGContext ctx, ControlFlowBlock block) {
var body = ctx.Graph.Body;
var key = ctx.Keys[block.Id];
if (key.EntryState == key.ExitState)
return;
Instruction first = null;
// Cannot use graph.IndexOf because instructions has been modified.
int targetIndex = body.Instructions.IndexOf(block.Header);
Instruction first;
CFGState entry;
if (!ctx.StatesMap.TryGetValue(key.EntryState, out entry)) {
Debug.Assert(key.Type == BlockKeyType.Explicit);
key.Type = BlockKeyType.Explicit;
}
if (key.Type == BlockKeyType.Incremental) {
body.Instructions.Insert(targetIndex + 0, first = Instruction.Create(OpCodes.Ldloc, ctx.StateVariable));
switch (ctx.Random.NextInt32(3)) {
case 0:
body.Instructions.Insert(targetIndex + 1, Instruction.Create(OpCodes.Ldc_I4, (int)(key.EntryState ^ key.ExitState)));
body.Instructions.Insert(targetIndex + 2, Instruction.Create(OpCodes.Xor));
break;
case 1:
body.Instructions.Insert(targetIndex + 1, Instruction.Create(OpCodes.Ldc_I4, (int)(key.ExitState - key.EntryState)));
body.Instructions.Insert(targetIndex + 2, Instruction.Create(OpCodes.Add));
break;
case 2:
body.Instructions.Insert(targetIndex + 1, Instruction.Create(OpCodes.Ldc_I4, (int)(key.EntryState - key.ExitState)));
body.Instructions.Insert(targetIndex + 2, Instruction.Create(OpCodes.Sub));
break;
// Incremental
CFGState exit;
if (!ctx.StatesMap.TryGetValue(key.ExitState, out exit)) {
// Create new exit state
// Update one of the entry states to be exit state
exit = entry;
int updateId = ctx.Random.NextInt32(3);
uint targetValue = ctx.Random.NextUInt32();
exit.UpdateExplicit(updateId, targetValue);
int getId = ctx.Random.NextInt32(3);
var fl = CFGState.EncodeFlag(false, updateId, getId);
var incr = entry.GetIncrementalUpdate(updateId, targetValue);
body.Instructions.Insert(targetIndex++, first = Instruction.Create(OpCodes.Ldloca, ctx.StateVariable));
body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldc_I4_S, (sbyte)fl));
body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldc_I4, (int)incr));
body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Call, ctx.Ctx.CfgCtxNext));
body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Pop));
ctx.StatesMap[key.ExitState] = exit;
}
else {
// Scan for updated state
var headerIndex = targetIndex;
for (int stateId = 0; stateId < 4; stateId++) {
if (entry.Get(stateId) == exit.Get(stateId))
continue;
uint targetValue = exit.Get(stateId);
int getId = ctx.Random.NextInt32(3);
var fl = CFGState.EncodeFlag(false, stateId, getId);
var incr = entry.GetIncrementalUpdate(stateId, targetValue);
body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldloca, ctx.StateVariable));
body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldc_I4_S, (sbyte)fl));
body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldc_I4, (int)incr));
body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Call, ctx.Ctx.CfgCtxNext));
body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Pop));
}
first = body.Instructions[headerIndex];
}
body.Instructions.Insert(targetIndex + 3, Instruction.Create(OpCodes.Stloc, ctx.StateVariable));
}
else {
body.Instructions.Insert(targetIndex + 0, first = Instruction.Create(OpCodes.Ldc_I4, (int)key.ExitState));
body.Instructions.Insert(targetIndex + 1, Instruction.Create(OpCodes.Stloc, ctx.StateVariable));
// Explicit
CFGState exit;
if (!ctx.StatesMap.TryGetValue(key.ExitState, out exit)) {
// Create new exit state from random seed
var seed = ctx.Random.NextUInt32();
exit = new CFGState(seed);
body.Instructions.Insert(targetIndex++, first = Instruction.Create(OpCodes.Ldloca, ctx.StateVariable));
body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldc_I4, (int)seed));
body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Call, ctx.Ctx.CfgCtxCtor));
ctx.StatesMap[key.ExitState] = exit;
}
else {
// Scan for updated state
var headerIndex = targetIndex;
for (int stateId = 0; stateId < 4; stateId++) {
uint targetValue = exit.Get(stateId);
int getId = ctx.Random.NextInt32(3);
var fl = CFGState.EncodeFlag(true, stateId, getId);
body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldloca, ctx.StateVariable));
body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldc_I4_S, (sbyte)fl));
body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Ldc_I4, (int)targetValue));
body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Call, ctx.Ctx.CfgCtxNext));
body.Instructions.Insert(targetIndex++, Instruction.Create(OpCodes.Pop));
}
first = body.Instructions[headerIndex];
}
}
ctx.Graph.Body.ReplaceReference(block.Header, first);
}
static int InsertStateGetAndUpdate(CFGContext ctx, int index, BlockKeyType type, uint currentState, uint targetState) {
static uint InsertStateGetAndUpdate(CFGContext ctx, ref int index, BlockKeyType type, ref CFGState currentState, CFGState? targetState) {
var body = ctx.Graph.Body;
if (type == BlockKeyType.Incremental) {
body.Instructions.Insert(index + 0, Instruction.Create(OpCodes.Ldloc, ctx.StateVariable));
body.Instructions.Insert(index + 1, Instruction.Create(OpCodes.Dup));
switch (ctx.Random.NextInt32(3)) {
case 0:
body.Instructions.Insert(index + 2, Instruction.Create(OpCodes.Ldc_I4, (int)(currentState ^ targetState)));
body.Instructions.Insert(index + 3, Instruction.Create(OpCodes.Xor));
break;
case 1:
body.Instructions.Insert(index + 2, Instruction.Create(OpCodes.Ldc_I4, (int)(targetState - currentState)));
body.Instructions.Insert(index + 3, Instruction.Create(OpCodes.Add));
break;
case 2:
body.Instructions.Insert(index + 2, Instruction.Create(OpCodes.Ldc_I4, (int)(currentState - targetState)));
body.Instructions.Insert(index + 3, Instruction.Create(OpCodes.Sub));
break;
// Incremental
if (targetState == null) {
// Randomly update and get state
int updateId = ctx.Random.NextInt32(3);
uint targetValue = ctx.Random.NextUInt32();
int getId = ctx.Random.NextInt32(3);
var fl = CFGState.EncodeFlag(false, updateId, getId);
var incr = currentState.GetIncrementalUpdate(updateId, targetValue);
currentState.UpdateExplicit(updateId, targetValue);
body.Instructions.Insert(index++, Instruction.Create(OpCodes.Ldloca, ctx.StateVariable));
body.Instructions.Insert(index++, Instruction.Create(OpCodes.Ldc_I4_S, (sbyte)fl));
body.Instructions.Insert(index++, Instruction.Create(OpCodes.Ldc_I4, (int)incr));
body.Instructions.Insert(index++, Instruction.Create(OpCodes.Call, ctx.Ctx.CfgCtxNext));
return currentState.Get(getId);
}
// Scan for updated state
int[] stateIds = { 0, 1, 2, 3 };
ctx.Random.Shuffle(stateIds);
int i = 0;
uint getValue = 0;
foreach (var stateId in stateIds) {
// There must be at least one update&get
if (currentState.Get(stateId) == targetState.Value.Get(stateId) &&
i != stateIds.Length - 1) {
i++;
continue;
}
uint targetValue = targetState.Value.Get(stateId);
int getId = ctx.Random.NextInt32(3);
var fl = CFGState.EncodeFlag(false, stateId, getId);
var incr = currentState.GetIncrementalUpdate(stateId, targetValue);
currentState.UpdateExplicit(stateId, targetValue);
body.Instructions.Insert(index++, Instruction.Create(OpCodes.Ldloca, ctx.StateVariable));
body.Instructions.Insert(index++, Instruction.Create(OpCodes.Ldc_I4_S, (sbyte)fl));
body.Instructions.Insert(index++, Instruction.Create(OpCodes.Ldc_I4, (int)incr));
body.Instructions.Insert(index++, Instruction.Create(OpCodes.Call, ctx.Ctx.CfgCtxNext));
i++;
if (i == stateIds.Length)
getValue = currentState.Get(getId);
else
body.Instructions.Insert(index++, Instruction.Create(OpCodes.Pop));
}
return getValue;
}
else {
// Explicit
if (targetState == null) {
// Create new exit state from random seed
var seed = ctx.Random.NextUInt32();
currentState = new CFGState(seed);
body.Instructions.Insert(index++, Instruction.Create(OpCodes.Ldloca, ctx.StateVariable));
body.Instructions.Insert(index++, Instruction.Create(OpCodes.Dup));
body.Instructions.Insert(index++, Instruction.Create(OpCodes.Ldc_I4, (int)seed));
body.Instructions.Insert(index++, Instruction.Create(OpCodes.Call, ctx.Ctx.CfgCtxCtor));
// Randomly get state
int updateId = ctx.Random.NextInt32(3);
uint targetValue = ctx.Random.NextUInt32();
int getId = ctx.Random.NextInt32(3);
var fl = CFGState.EncodeFlag(false, updateId, getId);
var incr = currentState.GetIncrementalUpdate(updateId, targetValue);
currentState.UpdateExplicit(updateId, targetValue);
body.Instructions.Insert(index++, Instruction.Create(OpCodes.Ldc_I4_S, (sbyte)fl));
body.Instructions.Insert(index++, Instruction.Create(OpCodes.Ldc_I4, (int)incr));
body.Instructions.Insert(index++, Instruction.Create(OpCodes.Call, ctx.Ctx.CfgCtxNext));
return currentState.Get(getId);
}
else {
// Scan for updated state
int[] stateIds = { 0, 1, 2, 3 };
ctx.Random.Shuffle(stateIds);
int i = 0;
uint getValue = 0;
foreach (var stateId in stateIds) {
uint targetValue = targetState.Value.Get(stateId);
int getId = ctx.Random.NextInt32(3);
var fl = CFGState.EncodeFlag(true, stateId, getId);
currentState.UpdateExplicit(stateId, targetValue);
body.Instructions.Insert(index++, Instruction.Create(OpCodes.Ldloca, ctx.StateVariable));
body.Instructions.Insert(index++, Instruction.Create(OpCodes.Ldc_I4_S, (sbyte)fl));
body.Instructions.Insert(index++, Instruction.Create(OpCodes.Ldc_I4, (int)targetValue));
body.Instructions.Insert(index++, Instruction.Create(OpCodes.Call, ctx.Ctx.CfgCtxNext));
i++;
if (i == stateIds.Length)
getValue = targetState.Value.Get(getId);
else
body.Instructions.Insert(index++, Instruction.Create(OpCodes.Pop));
}
return getValue;
}
body.Instructions.Insert(index + 4, Instruction.Create(OpCodes.Stloc, ctx.StateVariable));
return index + 5;
}
body.Instructions.Insert(index + 0, Instruction.Create(OpCodes.Ldc_I4, (int)currentState));
body.Instructions.Insert(index + 1, Instruction.Create(OpCodes.Ldc_I4, (int)targetState));
body.Instructions.Insert(index + 2, Instruction.Create(OpCodes.Stloc, ctx.StateVariable));
return index + 3;
}
static void ReplaceCFG(MethodDef method, List<Tuple<Instruction, uint, IMethod>> instrs, CEContext ctx) {
InjectStateType(ctx);
var graph = ControlFlowGraph.Construct(method.Body);
var sequence = KeySequence.ComputeKeys(graph, ctx.Random);
var sequence = KeySequence.ComputeKeys(graph, null);
var cfgCtx = new CFGContext {
Ctx = ctx,
Graph = graph,
Keys = sequence,
StatesMap = new Dictionary<uint, CFGState>(),
Random = ctx.Random
};
cfgCtx.StateVariable = new Local(ctx.CfgCtxType.ToTypeSig());
method.Body.Variables.Add(cfgCtx.StateVariable);
method.Body.InitLocals = true;
var blockReferences = new Dictionary<int, SortedList<int, Tuple<Instruction, uint, IMethod>>>();
foreach (var instr in instrs) {
var index = graph.IndexOf(instr.Item1);
@ -119,10 +372,6 @@ namespace Confuser.Protections.Constants {
list.Add(index, instr);
}
cfgCtx.StateVariable = new Local(method.Module.CorLibTypes.UInt32);
method.Body.Variables.Add(cfgCtx.StateVariable);
method.Body.InitLocals = true;
// Update state for blocks not in use
for (int i = 0; i < graph.Count; i++) {
if (blockReferences.ContainsKey(i))
@ -133,23 +382,49 @@ namespace Confuser.Protections.Constants {
// Update references
foreach (var blockRef in blockReferences) {
var key = sequence[blockRef.Key];
var currentState = key.EntryState;
CFGState currentState;
if (!cfgCtx.StatesMap.TryGetValue(key.EntryState, out currentState)) {
Debug.Assert((graph[blockRef.Key].Type & ControlFlowBlockType.Entry) != 0);
Debug.Assert(key.Type == BlockKeyType.Explicit);
// Create new entry state
uint blockSeed = ctx.Random.NextUInt32();
currentState = new CFGState(blockSeed);
cfgCtx.StatesMap[key.EntryState] = currentState;
var index = graph.Body.Instructions.IndexOf(graph[blockRef.Key].Header);
Instruction newHeader;
method.Body.Instructions.Insert(index++, newHeader = Instruction.Create(OpCodes.Ldloca, cfgCtx.StateVariable));
method.Body.Instructions.Insert(index++, Instruction.Create(OpCodes.Ldc_I4, (int)blockSeed));
method.Body.Instructions.Insert(index++, Instruction.Create(OpCodes.Call, ctx.CfgCtxCtor));
method.Body.ReplaceReference(graph[blockRef.Key].Header, newHeader);
key.Type = BlockKeyType.Incremental;
}
var type = key.Type;
for (int i = 0; i < blockRef.Value.Count; i++) {
var entry = blockRef.Value.Values[i];
var targetState = i == blockRef.Value.Count - 1 ? key.ExitState : cfgCtx.Random.NextUInt32();
var index = graph.Body.Instructions.IndexOf(entry.Item1);
var id = entry.Item2 ^ currentState;
var refEntry = blockRef.Value.Values[i];
entry.Item1.OpCode = OpCodes.Ldc_I4;
entry.Item1.Operand = (int)id;
index = InsertStateGetAndUpdate(cfgCtx, index + 1, type, currentState, targetState);
method.Body.Instructions.Insert(index + 0, Instruction.Create(OpCodes.Xor));
method.Body.Instructions.Insert(index + 1, Instruction.Create(OpCodes.Call, entry.Item3));
CFGState? targetState = null;
if (i == blockRef.Value.Count - 1) {
CFGState exitState;
if (cfgCtx.StatesMap.TryGetValue(key.ExitState, out exitState))
targetState = exitState;
}
var index = graph.Body.Instructions.IndexOf(refEntry.Item1) + 1;
var value = InsertStateGetAndUpdate(cfgCtx, ref index, type, ref currentState, targetState);
refEntry.Item1.OpCode = OpCodes.Ldc_I4;
refEntry.Item1.Operand = (int)(refEntry.Item2 ^ value);
method.Body.Instructions.Insert(index++, Instruction.Create(OpCodes.Xor));
method.Body.Instructions.Insert(index, Instruction.Create(OpCodes.Call, refEntry.Item3));
if (i == blockRef.Value.Count - 1 && targetState == null) {
cfgCtx.StatesMap[key.ExitState] = currentState;
}
type = BlockKeyType.Incremental;
currentState = targetState;
}
}
}

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

@ -67,4 +67,63 @@ namespace Confuser.Runtime {
return ret;
}
}
internal struct CFGCtx {
uint A;
uint B;
uint C;
uint D;
public CFGCtx(uint seed) {
A = seed *= 0x21412321;
B = seed *= 0x21412321;
C = seed *= 0x21412321;
D = seed *= 0x21412321;
}
public uint Next(byte f, uint q) {
if ((f & 0x80) != 0) {
switch (f & 0x3) {
case 0:
A = q;
break;
case 1:
B = q;
break;
case 2:
C = q;
break;
case 3:
D = q;
break;
}
}
else {
switch (f & 0x3) {
case 0:
A ^= q;
break;
case 1:
B += q;
break;
case 2:
C ^= q;
break;
case 3:
D -= q;
break;
}
}
switch ((f >> 2) & 0x3) {
case 0:
return A;
case 1:
return B;
case 2:
return C;
}
return D;
}
}
}