Exemple #1
0
        public BlockStatement ConvertBlock(Instruction[] instructions, Stack <Expression> stack, out int consumed)
        {
            var statements = new List <Statement>();

            for (consumed = 0; consumed < instructions.Length; consumed++)
            {
                Instruction instruction = instructions[consumed];

                if (instruction == null)
                {
                    Console.WriteLine($"Unimplemented instruction! Stack: {stack.Count}");
                    continue;
                }

                if (Jumper.IsValid(instruction.OP))
                {
                    throw new NotImplementedException("Control Flow handling is still under construction.");
                }

                Statement stmt = instruction.AcceptVisitor(this, stack);

                if (stmt != null)
                {
                    statements.Add(stmt);
                }
            }
            return(new BlockStatement(statements));
        }
Exemple #2
0
        public void RemoveRange(int index, int count)
        {
            if ((index + count) <= _instructions.Count)
            {
                for (int i = 0; i < count; i++)
                {
                    ASInstruction instruction = _instructions[index];
                    _indices.Remove(instruction);
                    _instructions.RemoveAt(index);

                    List <ASInstruction> group = _opGroups[instruction.OP];
                    if (group.Count == 1)
                    {
                        _opGroups.Remove(instruction.OP);
                    }
                    else
                    {
                        group.Remove(instruction);
                    }

                    // TODO: Recalibrate switch exits.
                    Jumper entry = GetJumperEntry(instruction);
                    if (entry != null)
                    {
                        if (index != _instructions.Count)
                        {
                            ASInstruction exit = _instructions[index];
                            JumpExits[entry] = exit;
                        }
                        else
                        {
                            JumpExits[entry] = null;
                        }
                    }

                    if (Jumper.IsValid(instruction.OP))
                    {
                        JumpExits.Remove((Jumper)instruction);
                    }
                    else if (instruction.OP == OPCode.LookUpSwitch)
                    {
                        SwitchExits.Remove((LookUpSwitchIns)instruction);
                    }
                }
                for (int i = index; i < _indices.Count; i++)
                {
                    ASInstruction toPull = _instructions[i];
                    _indices[toPull] -= count;
                }
            }
        }
        public static ControlFlowGraph Create(LingoCode code)
        {
            //TODO: Remove temp comment rambling
            //TODO: For later tranforms, check BBs which are "pure" and only exist as a exit and as "a jumper". BB.Body.Count == 1
            //TODO: Number the blocks topologically or nah? BB.Ordinal?

            var builder = new ControlFlowGraphBuilder();

            var forwardBranches = new Dictionary <Instruction, BasicBlock>(code.JumpExits.Count);
            var backEdges       = new Dictionary <Jumper, BasicBlock>();

            Range currentBlockRange = default; //0..0

            for (int i = 0; i < code.Count; i++)
            {
                Instruction instruction = code[i];

                //We are hunting here for those dangling JumpExits. If we find one which is a back-edge exit, we break and leave that to be targeted for future.
                if (code.JumpExits.ContainsValue(instruction)) //TODO: if the jumpExit OP = Return -> BBKind.Exit, or have another round for transformations like that? //TODO: Check how much ContainsValue slows this down
                {
                    currentBlockRange = currentBlockRange.End..i;

                    if (!currentBlockRange.Equals(i..i))
                    {
                        builder.CurrentBlock.Body = code[currentBlockRange];
                    }

                    if (forwardBranches.TryGetValue(instruction, out var successor))
                    {
                        //Resolving the forward-edge
                        if (builder.CurrentBlock.FallThrough == null) //TODO: Doesn't feel right? Maybe abuse the shit out of CurrentBlock getter-setter?
                        {
                            builder.AppendBlock(successor);           //If
                        }
                        else
                        {
                            builder.AddBlock(successor);  //usually If-Else
                        }
                    }
                    else
                    {
                        //Back-edge target
                        var nextBlock = new BasicBlock();

                        backEdges.Add(code.GetJumperEntry(instruction), nextBlock);
                        builder.AppendBlock(nextBlock);
                    }
                }

                if (Jumper.IsValid(instruction.OP))
                {
                    Jumper jumper = (Jumper)instruction;

                    currentBlockRange         = currentBlockRange.End..(i + 1);
                    builder.CurrentBlock.Body = code[currentBlockRange];

                    if (code.IsBackwardsJump(jumper))
                    {
                        //Fallthrough back-edge jumper
                        builder.LinkBlocks(builder.CurrentBlock, backEdges[jumper]);
                    }
                    else
                    {
                        //Forward jump to successor
                        if (!forwardBranches.TryGetValue(code.JumpExits[jumper], out BasicBlock successor))
                        {
                            forwardBranches.Add(code.JumpExits[jumper], successor = new BasicBlock());
                        }

                        builder.LinkBlocks(builder.CurrentBlock, successor);

                        if (Jumper.IsConditional(instruction.OP))
                        {
                            builder.AppendBlock(new BasicBlock(), isConditionalBranch: true);
                        }
                    }
                }
            }

            currentBlockRange         = currentBlockRange.End..code.Count;
            builder.CurrentBlock.Body = code[currentBlockRange];

            builder.AppendBlock(new BasicBlock(BasicBlockKind.Exit)); //TODO:

            return(new ControlFlowGraph()
            {
                Blocks = builder.Blocks
            });
        }
Exemple #4
0
        public override void WriteTo(FlashWriter output)
        {
            var marks            = new Dictionary <ASInstruction, long>();
            var sharedExits      = new Dictionary <ASInstruction, List <ASInstruction> >();
            var forwardCaseExits = new Dictionary <ASInstruction, Tuple <LookUpSwitchIns, int> >();

            foreach (ASInstruction instruction in _instructions)
            {
                long previousPosition = output.Position;
                marks.Add(instruction, previousPosition);
                instruction.WriteTo(output);

                List <ASInstruction> jumpers = null;
                if (sharedExits.TryGetValue(instruction, out jumpers))
                {
                    foreach (ASInstruction jumper in jumpers)
                    {
                        long position    = marks[jumper];
                        var  fixedOffset = (uint)(previousPosition - (position + 4));

                        ((Jumper)jumper).Offset = fixedOffset;
                        Rewrite(output, jumper, position);
                    }
                    sharedExits.Remove(instruction);
                }

                Tuple <LookUpSwitchIns, int> switchIdentity = null;
                if (forwardCaseExits.TryGetValue(instruction, out switchIdentity))
                {
                    int             caseIndex    = switchIdentity.Item2;
                    LookUpSwitchIns lookUpSwitch = switchIdentity.Item1;

                    long position    = marks[lookUpSwitch];
                    var  fixedOffset = (uint)(previousPosition - position);

                    if (caseIndex == lookUpSwitch.CaseOffsets.Count)
                    {
                        lookUpSwitch.DefaultOffset = fixedOffset;
                    }
                    else
                    {
                        lookUpSwitch.CaseOffsets[caseIndex] = fixedOffset;
                    }
                    Rewrite(output, lookUpSwitch, position);
                }

                if (instruction.OP == OPCode.LookUpSwitch)
                {
                    bool            requiresRewrite = false;
                    var             lookUpSwitch    = (LookUpSwitchIns)instruction;
                    ASInstruction[] cases           = SwitchExits[lookUpSwitch];
                    for (int i = 0; i < cases.Length; i++)
                    {
                        ASInstruction exit = cases[i];
                        if (exit.OP != OPCode.Label)
                        {
                            forwardCaseExits.Add(exit, Tuple.Create(lookUpSwitch, i));
                        }
                        else
                        {
                            requiresRewrite = true;
                            long exitPosition = marks[exit];
                            long jumpCount    = (previousPosition - (exitPosition + 1));

                            uint fixedOffset = (uint)(uint.MaxValue - jumpCount);
                            if (i == (cases.Length - 1))
                            {
                                lookUpSwitch.DefaultOffset = fixedOffset;
                            }
                            else
                            {
                                lookUpSwitch.CaseOffsets[i] = fixedOffset;
                            }
                        }
                    }
                    if (requiresRewrite)
                    {
                        Rewrite(output, lookUpSwitch, previousPosition);
                    }
                }
                else if (Jumper.IsValid(instruction.OP))
                {
                    var jumper = (Jumper)instruction;
                    if (jumper.Offset == 0)
                    {
                        continue;
                    }

                    ASInstruction exit = null;
                    if (!JumpExits.TryGetValue(jumper, out exit))
                    {
                        // An exit for this jump instruction could not be found, perhaps its' offset exceeds the index limit?
                        continue;
                    }
                    else if (exit.OP != OPCode.Label || !marks.ContainsKey(exit)) // Forward jumps can have a label for an exit, as long as it is located ahead.
                    {
                        jumpers = null;
                        if (!sharedExits.TryGetValue(exit, out jumpers))
                        {
                            jumpers = new List <ASInstruction>();
                            sharedExits.Add(exit, jumpers);
                        }
                        jumpers.Add(jumper);
                    }
                    else // Backward jumps must always have a label for an exit, and must be behind this instruction.
                    {
                        long exitPosition = marks[exit];
                        long jumpCount    = (output.Position - (exitPosition + 1));

                        var fixedOffset = (uint)(uint.MaxValue - jumpCount);
                        jumper.Offset = fixedOffset;

                        Rewrite(output, jumper, previousPosition);
                    }
                }
            }
        }
Exemple #5
0
        private void LoadInstructions()
        {
            var caseIndices      = new Dictionary <long, int>();
            var marks            = new Dictionary <long, ASInstruction>();
            var sharedExits      = new Dictionary <long, List <Jumper> >();
            var forwardCaseExits = new Dictionary <long, LookUpSwitchIns>();

            using (var input = new FlashReader(_body.Code))
            {
                while (input.IsDataAvailable)
                {
                    long previousPosition = input.Position;
                    var  instruction      = ASInstruction.Create(_abc, input);
                    marks[previousPosition] = instruction;

                    _indices.Add(instruction, _indices.Count);
                    _instructions.Add(instruction);

                    List <ASInstruction> instructions = null;
                    if (!_opGroups.TryGetValue(instruction.OP, out instructions))
                    {
                        instructions = new List <ASInstruction>();
                        _opGroups.Add(instruction.OP, instructions);
                    }
                    instructions.Add(instruction);

                    List <Jumper> jumpers = null;
                    if (sharedExits.TryGetValue(previousPosition, out jumpers))
                    {
                        // This is an exit position for a jump instruction, or more.
                        foreach (Jumper jumper in jumpers)
                        {
                            JumpExits.Add(jumper, instruction);
                        }
                        sharedExits.Remove(previousPosition);
                    }

                    LookUpSwitchIns lookUpSwitch = null;
                    if (forwardCaseExits.TryGetValue(previousPosition, out lookUpSwitch))
                    {
                        int index = caseIndices[previousPosition];
                        SwitchExits[lookUpSwitch][index] = instruction;

                        caseIndices.Remove(previousPosition);
                        forwardCaseExits.Remove(previousPosition);
                    }

                    if (instruction.OP == OPCode.LookUpSwitch)
                    {
                        lookUpSwitch = (LookUpSwitchIns)instruction;
                        var offsets = new List <uint>(lookUpSwitch.CaseOffsets);
                        offsets.Add(lookUpSwitch.DefaultOffset);

                        var cases = new ASInstruction[offsets.Count];
                        for (int i = 0; i < offsets.Count; i++)
                        {
                            ASInstruction exit         = null;
                            long          exitPosition = (previousPosition + offsets[i]);
                            if (exitPosition <= input.Length)
                            {
                                caseIndices.Add(exitPosition, i);
                                forwardCaseExits.Add(exitPosition, lookUpSwitch);
                            }
                            else
                            {
                                cases[i] = exit;
                                exit     = marks[(exitPosition - uint.MaxValue) - 1];
                            }
                        }
                        SwitchExits.Add(lookUpSwitch, cases);
                    }
                    else if (Jumper.IsValid(instruction.OP))
                    {
                        var jumper = (Jumper)instruction;
                        if (jumper.Offset == 0)
                        {
                            continue;
                        }

                        long exitPosition = (input.Position + jumper.Offset);
                        if (exitPosition == input.Length)
                        {
                            // Jump exit does not exist at this (non-existent)index, do not look for exit.
                            continue;
                        }
                        else if (exitPosition < input.Length) // Forward jump.
                        {
                            jumpers = null;
                            if (!sharedExits.TryGetValue(exitPosition, out jumpers))
                            {
                                jumpers = new List <Jumper>();
                                sharedExits.Add(exitPosition, jumpers);
                            }
                            jumpers.Add(jumper);
                        }
                        else // Backwards jump.
                        {
                            var label = (LabelIns)marks[(exitPosition - uint.MaxValue) - 1];
                            JumpExits.Add(jumper, label);
                        }
                    }
                }
            }
        }
Exemple #6
0
        public void Deobfuscate()
        {
            var machine         = new ASMachine(this, _body.LocalCount);
            var cleaned         = new List <ASInstruction>(_instructions.Count);
            var valuePushers    = new Stack <ASInstruction>(_body.MaxStack);
            var localReferences = new Dictionary <int, List <ASInstruction> >();
            var swappedValues   = new Dictionary <ASInstruction, ASInstruction[]>();

            KeyValuePair <Jumper, ASInstruction>[]            jumpExits   = JumpExits.ToArray();
            KeyValuePair <LookUpSwitchIns, ASInstruction[]>[] switchExits = SwitchExits.ToArray();
            for (int i = 0; i < _instructions.Count; i++)
            {
                ASInstruction instruction = _instructions[i];
                if (Jumper.IsValid(instruction.OP))
                {
                    i += GetFinalJumpCount(machine,
                                           (Jumper)instruction, cleaned, localReferences, valuePushers);
                }
                else
                {
                    instruction.Execute(machine);
                    #region Arithmetic Optimization
                    if (Computation.IsValid(instruction.OP))
                    {
                        object        result      = machine.Values.Pop();
                        ASInstruction rightPusher = valuePushers.Pop();
                        ASInstruction leftPusher  = valuePushers.Pop();
                        if (!Local.IsValid(leftPusher.OP) && !Local.IsValid(rightPusher.OP) && result != null)
                        {
                            // Constant values found, push result instead of having it do the calculation everytime.
                            cleaned.Remove(leftPusher);
                            cleaned.Remove(rightPusher);
                            instruction = Primitive.Create(_abc, result);

                            foreach (KeyValuePair <Jumper, ASInstruction> jumpExit in jumpExits)
                            {
                                if (leftPusher == jumpExit.Value)
                                {
                                    JumpExits[jumpExit.Key] = instruction;
                                    // Do not break, another jump instruction can share the same exit.
                                }
                            }
                            foreach (KeyValuePair <LookUpSwitchIns, ASInstruction[]> switchExit in switchExits)
                            {
                                ASInstruction[] cases = switchExit.Value;
                                for (int j = 0; j < cases.Length; j++)
                                {
                                    ASInstruction exit = cases[j];
                                    if (leftPusher == exit)
                                    {
                                        cases[j] = instruction;
                                    }
                                }
                            }
                            instruction.Execute(machine);
                        }
                        else
                        {
                            // Do not attempt to optimize when a local is being use, as these values can change.
                            valuePushers.Push(leftPusher);
                            valuePushers.Push(rightPusher);
                            machine.Values.Push(result);
                        }
                    }
                    #endregion
                    cleaned.Add(instruction);

                    ASInstruction[]      swaps      = null;
                    List <ASInstruction> references = null;
                    if (Local.IsValid(instruction.OP))
                    {
                        var local = (Local)instruction;
                        if (!localReferences.TryGetValue(local.Register, out references))
                        {
                            references = new List <ASInstruction>();
                            localReferences[local.Register] = references;
                        }
                        references.Add(instruction);
                    }
                    else if (instruction.OP == OPCode.Swap)
                    {
                        swaps = new ASInstruction[2];
                        swappedValues[instruction] = swaps;
                    }

                    int popCount = instruction.GetPopCount();
                    for (int j = 0; j < popCount; j++)
                    {
                        ASInstruction pusher = valuePushers.Pop();
                        references?.Add(pusher);
                        if (swaps != null)
                        {
                            swaps[j] = pusher;
                        }
                    }
                    int pushCount = instruction.GetPushCount();
                    for (int j = 0; j < pushCount; j++)
                    {
                        valuePushers.Push(instruction);
                    }
                }
            }

            // Remove dead locals.
            foreach (KeyValuePair <int, List <ASInstruction> > local in localReferences)
            {
                if (local.Key == 0)
                {
                    continue;                 // Scope
                }
                if (local.Key <= (_body.Method.Parameters.Count))
                {
                    continue;                                               // Register == Param #(Non-zero based)
                }
                List <ASInstruction> references = localReferences[local.Key];

                bool isNeeded = false;
                foreach (ASInstruction reference in references)
                {
                    // This checks if the local is being referenced by something else that retreives the value.
                    if (Local.IsValid(reference.OP) && !Local.IsSetLocal(reference.OP))
                    {
                        isNeeded = true;
                        break;
                    }
                }
                if (!isNeeded)
                {
                    foreach (ASInstruction reference in references)
                    {
                        if (reference.OP == OPCode.Swap)
                        {
                            ASInstruction[] swaps = swappedValues[reference];
                            foreach (ASInstruction swap in swaps)
                            {
                                cleaned.Remove(swap);
                            }
                        }
                        cleaned.Remove(reference);
                    }
                }
            }

            jumpExits = JumpExits.ToArray(); // Global property could have been updated.
            foreach (KeyValuePair <Jumper, ASInstruction> jumpExit in jumpExits)
            {
                if (IndexOf(jumpExit.Key) >
                    IndexOf(jumpExit.Value))
                {
                    // This is not a forward jump instruction, since the exit appears before the jump.
                    continue;
                }

                // True if it still has the jump instruction, but the instruction
                // needed to determine the final instruction to jump is not present.
                if (cleaned.Contains(jumpExit.Key) && !cleaned.Contains(jumpExit.Value))
                {
                    // Start at the index of the instruction that should come after the final instruction to jump.
                    for (int j = (_indices[jumpExit.Value] + 1); j < _instructions.Count; j++)
                    {
                        ASInstruction afterEnd  = _instructions[j];
                        int           exitIndex = cleaned.IndexOf(afterEnd);
                        if (exitIndex != -1) // Does this instruction exist in the cleaned output?; Otherwise, get instruction that comes after it.
                        {
                            JumpExits[jumpExit.Key] = cleaned[exitIndex];
                            break;
                        }
                    }
                }
            }

            _instructions.Clear();
            _instructions.AddRange(cleaned);
            Recalibrate();
        }