static int InsertStateGetAndUpdate(CFGContext ctx, int index, BlockKeyType type, uint currentState, uint 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; } 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 int InsertStateGetAndUpdate(CFGContext ctx, int index, BlockKeyType type, uint currentState, uint 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; } 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 InsertEmptyStateUpdate(CFGContext ctx, ControlFlowBlock block) { var body = ctx.Graph.Body; var key = ctx.Keys[block.Id]; if (key.EntryState == key.ExitState) { return; } // Cannot use graph.IndexOf because instructions has been modified. int targetIndex = body.Instructions.IndexOf(block.Header); Instruction first; 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; } 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)); } ctx.Graph.Body.ReplaceReference(block.Header, first); }
static void InsertEmptyStateUpdate(CFGContext ctx, ControlFlowBlock block) { var body = ctx.Graph.Body; var key = ctx.Keys[block.Id]; if (key.EntryState == key.ExitState) return; // Cannot use graph.IndexOf because instructions has been modified. int targetIndex = body.Instructions.IndexOf(block.Header); Instruction first; 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; } 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)); } ctx.Graph.Body.ReplaceReference(block.Header, first); }
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, 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); var block = graph.GetContainingBlock(index); SortedList <int, Tuple <Instruction, uint, IMethod> > list; if (!blockReferences.TryGetValue(block.Id, out list)) { list = blockReferences[block.Id] = new SortedList <int, Tuple <Instruction, uint, IMethod> >(); } list.Add(index, instr); } // Update state for blocks not in use for (int i = 0; i < graph.Count; i++) { var block = graph[i]; if (blockReferences.ContainsKey(block.Id)) { continue; } InsertEmptyStateUpdate(cfgCtx, block); } // Update references foreach (var blockRef in blockReferences) { var key = sequence[blockRef.Key]; 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 refEntry = blockRef.Value.Values[i]; 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; } } }
static uint InsertStateGetAndUpdate(CFGContext ctx, ref int index, BlockKeyType type, ref CFGState currentState, CFGState?targetState) { var body = ctx.Graph.Body; if (type == BlockKeyType.Incremental) { // 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); } } }
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); CFGState entry; if (!ctx.StatesMap.TryGetValue(key.EntryState, out entry)) { key.Type = BlockKeyType.Explicit; } if (key.Type == BlockKeyType.Incremental) { // 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]; } } else { // 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 void ReplaceCFG(MethodDef method, List <Tuple <Instruction, uint, IMethod> > instrs, CEContext ctx) { var graph = ControlFlowGraph.Construct(method.Body); var sequence = KeySequence.ComputeKeys(graph, ctx.Random); var cfgCtx = new CFGContext { Graph = graph, Keys = sequence, Random = ctx.Random }; var blockReferences = new Dictionary <int, SortedList <int, Tuple <Instruction, uint, IMethod> > >(); foreach (var instr in instrs) { var index = graph.IndexOf(instr.Item1); var block = graph.GetContainingBlock(index); SortedList <int, Tuple <Instruction, uint, IMethod> > list; if (!blockReferences.TryGetValue(block.Id, out list)) { list = blockReferences[block.Id] = new SortedList <int, Tuple <Instruction, uint, IMethod> >(); } 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)) { continue; } InsertEmptyStateUpdate(cfgCtx, graph[i]); } // Update references foreach (var blockRef in blockReferences) { var key = sequence[blockRef.Key]; var currentState = key.EntryState; 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; 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)); type = BlockKeyType.Incremental; currentState = targetState; } } }
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); 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) { // 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]; } } else { // 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 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, 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); var block = graph.GetContainingBlock(index); SortedList<int, Tuple<Instruction, uint, IMethod>> list; if (!blockReferences.TryGetValue(block.Id, out list)) list = blockReferences[block.Id] = new SortedList<int, Tuple<Instruction, uint, IMethod>>(); list.Add(index, instr); } // Update state for blocks not in use for (int i = 0; i < graph.Count; i++) { if (blockReferences.ContainsKey(i)) continue; InsertEmptyStateUpdate(cfgCtx, graph[i]); } // Update references foreach (var blockRef in blockReferences) { var key = sequence[blockRef.Key]; 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 refEntry = blockRef.Value.Values[i]; 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; } } }
static uint InsertStateGetAndUpdate(CFGContext ctx, ref int index, BlockKeyType type, ref CFGState currentState, CFGState? targetState) { var body = ctx.Graph.Body; if (type == BlockKeyType.Incremental) { // 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; } } }
static void ReplaceCFG(MethodDef method, List<Tuple<Instruction, uint, IMethod>> instrs, CEContext ctx) { var graph = ControlFlowGraph.Construct(method.Body); var sequence = KeySequence.ComputeKeys(graph, ctx.Random); var cfgCtx = new CFGContext { Graph = graph, Keys = sequence, Random = ctx.Random }; var blockReferences = new Dictionary<int, SortedList<int, Tuple<Instruction, uint, IMethod>>>(); foreach (var instr in instrs) { var index = graph.IndexOf(instr.Item1); var block = graph.GetContainingBlock(index); SortedList<int, Tuple<Instruction, uint, IMethod>> list; if (!blockReferences.TryGetValue(block.Id, out list)) list = blockReferences[block.Id] = new SortedList<int, Tuple<Instruction, uint, IMethod>>(); 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)) continue; InsertEmptyStateUpdate(cfgCtx, graph[i]); } // Update references foreach (var blockRef in blockReferences) { var key = sequence[blockRef.Key]; var currentState = key.EntryState; 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; 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)); type = BlockKeyType.Incremental; currentState = targetState; } } }