Example #1
0
        public override void Mangle(CilBody body, ScopeBlock root, CFContext ctx)
        {
            Trace trace = new Trace(body, ctx.Method.ReturnType.RemoveModifiers().ElementType != ElementType.Void);
            var local = new Local(ctx.Method.Module.CorLibTypes.UInt32);
            body.Variables.Add(local);
            body.InitLocals = true;

            body.MaxStack += 2;
            IPredicate predicate = null;
            if (ctx.Predicate == PredicateType.Normal) {
                predicate = new NormalPredicate(ctx);
            }
            else if (ctx.Predicate == PredicateType.Expression) {
                predicate = new ExpressionPredicate(ctx);
            }
            else if (ctx.Predicate == PredicateType.x86) {
                predicate = new x86Predicate(ctx);
            }

            foreach (InstrBlock block in GetAllBlocks(root)) {
                LinkedList<Instruction[]> statements = SpiltStatements(block, trace, ctx);

                // Make sure .ctor is executed before switch
                if (ctx.Method.IsInstanceConstructor) {
                    var newStatement = new List<Instruction>();
                    while (statements.First != null) {
                        newStatement.AddRange(statements.First.Value);
                        Instruction lastInstr = statements.First.Value.Last();
                        statements.RemoveFirst();
                        if (lastInstr.OpCode == OpCodes.Call && ((IMethod)lastInstr.Operand).Name == ".ctor")
                            break;
                    }
                    statements.AddFirst(newStatement.ToArray());
                }

                if (statements.Count < 3) continue;

                int i;

                var keyId = Enumerable.Range(0, statements.Count).ToArray();
                ctx.Random.Shuffle(keyId);
                var key = new int[keyId.Length];
                for (i = 0; i < key.Length; i++) {
                    var q = ctx.Random.NextInt32() & 0x7fffffff;
                    key[i] = q - q % statements.Count + keyId[i];
                }

                var statementKeys = new Dictionary<Instruction, int>();
                LinkedListNode<Instruction[]> current = statements.First;
                i = 0;
                while (current != null) {
                    if (i != 0)
                        statementKeys[current.Value[0]] = key[i];
                    i++;
                    current = current.Next;
                }

                var statementLast = new HashSet<Instruction>(statements.Select(st => st.Last()));

                Func<IList<Instruction>, bool> hasUnknownSource;
                hasUnknownSource = instrs => instrs.Any(instr => {
                    if (trace.HasMultipleSources(instr.Offset))
                        return true;
                    List<Instruction> srcs;
                    if (trace.BrRefs.TryGetValue(instr.Offset, out srcs)) {
                        // Target of switch => assume unknown
                        if (srcs.Any(src => src.Operand is Instruction[]))
                            return true;

                        // Not within current instruction block / targeted in first statement
                        if (srcs.Any(src => src.Offset <= statements.First.Value.Last().Offset ||
                                            src.Offset >= block.Instructions.Last().Offset))
                            return true;

                        // Not targeted by the last of statements
                        if (srcs.Any(src => statementLast.Contains(src)))
                            return true;
                    }
                    return false;
                });

                var switchInstr = new Instruction(OpCodes.Switch);
                var switchHdr = new List<Instruction>();

                if (predicate != null) {
                    predicate.Init(body);
                    switchHdr.Add(Instruction.CreateLdcI4(predicate.GetSwitchKey(key[1])));
                    predicate.EmitSwitchLoad(switchHdr);
                }
                else {
                    switchHdr.Add(Instruction.CreateLdcI4(key[1]));
                }

                switchHdr.Add(Instruction.Create(OpCodes.Dup));
                switchHdr.Add(Instruction.Create(OpCodes.Stloc, local));
                switchHdr.Add(Instruction.Create(OpCodes.Ldc_I4, statements.Count));
                switchHdr.Add(Instruction.Create(OpCodes.Rem_Un));
                switchHdr.Add(switchInstr);

                if (trace.BeforeStack[statements.Last.Value[0].Offset] == 0)
                {
                    ctx.AddJump(switchHdr, statements.Last.Value[0]);
                }
                else
                {
                    ctx.AddJump(switchHdr, switchHdr[0]);
                }
                ctx.AddJunk(switchHdr);

                var operands = new Instruction[statements.Count];
                current = statements.First;
                i = 0;
                while (current.Next != null) {
                    var newStatement = new List<Instruction>(current.Value);

                    if (i != 0) {
                        // Convert to switch
                        bool converted = false;

                        if (newStatement.Last().IsBr()) {
                            // Unconditional

                            var target = (Instruction)newStatement.Last().Operand;
                            int brKey;
                            if (!trace.IsBranchTarget(newStatement.Last().Offset) &&
                                statementKeys.TryGetValue(target, out brKey)) {

                                if (trace.BeforeStack[target.Offset] == 0)
                                {
                                    var targetKey = predicate != null ? predicate.GetSwitchKey(brKey) : brKey;
                                    var unkSrc = hasUnknownSource(newStatement);

                                    var initialBeforeStack = trace.BeforeStack[newStatement[0].Offset];
                                    newStatement.RemoveAt(newStatement.Count - 1);

                                    if (unkSrc || initialBeforeStack != 0) {
                                        newStatement.Add(Instruction.Create(OpCodes.Ldc_I4, targetKey));
                                    }
                                    else {
                                        var thisKey = key[i];
                                        var r = ctx.Random.NextInt32();
                                        newStatement.Add(Instruction.Create(OpCodes.Ldloc, local));
                                        newStatement.Add(Instruction.CreateLdcI4(r));
                                        newStatement.Add(Instruction.Create(OpCodes.Mul));
                                        newStatement.Add(Instruction.Create(OpCodes.Ldc_I4, (thisKey * r) ^ targetKey));
                                        newStatement.Add(Instruction.Create(OpCodes.Xor));
                                    }

                                    ctx.AddJump(newStatement, switchHdr[1]);
                                    ctx.AddJunk(newStatement);
                                }

                                if (trace.BeforeStack[newStatement[0].Offset] == 0)
                                {
                                    operands[keyId[i]] = newStatement[0];
                                }

                                converted = true;
                            }
                        }
                        else if (newStatement.Last().IsConditionalBranch()) {
                            // Conditional

                            var target = (Instruction)newStatement.Last().Operand;
                            int brKey;
                            if (!trace.IsBranchTarget(newStatement.Last().Offset) &&
                                statementKeys.TryGetValue(target, out brKey)) {

                                if (trace.BeforeStack[target.Offset] == 0)
                                {
                                    bool unkSrc = hasUnknownSource(newStatement);
                                    int nextKey = key[i + 1];
                                    OpCode condBr = newStatement.Last().OpCode;
                                    newStatement.RemoveAt(newStatement.Count - 1);

                                    if (ctx.Random.NextBoolean()) {
                                        condBr = InverseBranch(condBr);
                                        int tmp = brKey;
                                        brKey = nextKey;
                                        nextKey = tmp;
                                    }

                                    var thisKey = key[i];
                                    int r = 0, xorKey = 0;
                                    if (!unkSrc) {
                                        r = ctx.Random.NextInt32();
                                        xorKey = thisKey * r;
                                    }

                                    Instruction brKeyInstr = Instruction.CreateLdcI4(xorKey ^ (predicate != null ? predicate.GetSwitchKey(brKey) : brKey));
                                    Instruction nextKeyInstr = Instruction.CreateLdcI4(xorKey ^ (predicate != null ? predicate.GetSwitchKey(nextKey) : nextKey));
                                    Instruction pop = Instruction.Create(OpCodes.Pop);

                                    newStatement.Add(Instruction.Create(condBr, brKeyInstr));
                                    newStatement.Add(nextKeyInstr);
                                    newStatement.Add(Instruction.Create(OpCodes.Dup));
                                    newStatement.Add(Instruction.Create(OpCodes.Br, pop));
                                    newStatement.Add(brKeyInstr);
                                    newStatement.Add(Instruction.Create(OpCodes.Dup));
                                    newStatement.Add(pop);

                                    if (!unkSrc) {
                                        newStatement.Add(Instruction.Create(OpCodes.Ldloc, local));
                                        newStatement.Add(Instruction.CreateLdcI4(r));
                                        newStatement.Add(Instruction.Create(OpCodes.Mul));
                                        newStatement.Add(Instruction.Create(OpCodes.Xor));
                                    }

                                    ctx.AddJump(newStatement, switchHdr[1]);
                                    ctx.AddJunk(newStatement);

                                    converted = true;
                                }

                                if (trace.BeforeStack[newStatement[0].Offset] == 0)
                                {
                                    operands[keyId[i]] = newStatement[0];
                                }
                            }
                        }

                        if (!converted) {
                            // Normal

                            if (newStatement[newStatement.Count - 1].OpCode != OpCodes.Ret)
                            {
                                var nextStatement = current.Next;
                                if (trace.BeforeStack[nextStatement.Value[0].Offset] == 0)
                                {
                                    var targetKey = predicate != null ? predicate.GetSwitchKey(key[i + 1]) : key[i + 1];
                                    if (!hasUnknownSource(newStatement) && trace.BeforeStack[newStatement[0].Offset] == 0)
                                    {
                                        var thisKey = key[i];
                                        var r = ctx.Random.NextInt32();
                                        newStatement.Add(Instruction.Create(OpCodes.Ldloc, local));
                                        newStatement.Add(Instruction.CreateLdcI4(r));
                                        newStatement.Add(Instruction.Create(OpCodes.Mul));
                                        newStatement.Add(Instruction.Create(OpCodes.Ldc_I4, (thisKey*r) ^ targetKey));
                                        newStatement.Add(Instruction.Create(OpCodes.Xor));
                                    }
                                    else
                                    {
                                        newStatement.Add(Instruction.Create(OpCodes.Ldc_I4, targetKey));
                                    }

                                    ctx.AddJump(newStatement, switchHdr[1]);
                                    ctx.AddJunk(newStatement);
                                }
                                else
                                {
                                    newStatement.Add(Instruction.Create(OpCodes.Br, nextStatement.Value[0]));
                                }
                            }

                            if (trace.BeforeStack[newStatement[0].Offset] == 0)
                            {
                                operands[keyId[i]] = newStatement[0];
                            }
                        }
                    }
                    else
                        operands[keyId[i]] = switchHdr[0];

                    current.Value = newStatement.ToArray();
                    current = current.Next;
                    i++;
                }

                if (trace.BeforeStack[current.Value[0].Offset] == 0)
                {
                    operands[keyId[i]] = current.Value[0];
                }

                for (i = 0; i < operands.Length; i++)
                {
                    if (operands[i] == null)
                    {
                        operands[i] = switchHdr[0];
                    }
                }

                switchInstr.Operand = operands;

                Instruction[] first = statements.First.Value;
                statements.RemoveFirst();
                Instruction[] last = statements.Last.Value;
                statements.RemoveLast();

                List<Instruction[]> newStatements = statements.Where(s => trace.BeforeStack[s[0].Offset] == 0).ToList();
                ctx.Random.Shuffle(newStatements);

                List<Instruction[]> newOrderedStatements = statements.Where(s => trace.BeforeStack[s[0].Offset] != 0).ToList();

                block.Instructions.Clear();
                block.Instructions.AddRange(first);
                block.Instructions.AddRange(switchHdr);
                foreach (var statement in newStatements)
                    block.Instructions.AddRange(statement);
                foreach (var statement in newOrderedStatements)
                    block.Instructions.AddRange(statement);
                block.Instructions.AddRange(last);
            }
        }