예제 #1
0
        public void Clear()
        {
            _indices.Clear();
            _opGroups.Clear();
            _instructions.Clear();

            JumpExits.Clear();
            SwitchExits.Clear();
        }
예제 #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;
                }
            }
        }
예제 #3
0
        public void InsertRange(int index, IEnumerable <ASInstruction> collection)
        {
            if (index <= _instructions.Count && collection.Any())
            {
                var deadJumps = new Stack <Jumper>(JumpExits
                                                   .Where(je => je.Value == null)
                                                   .Select(je => je.Key));

                int count = _instructions.Count;
                if (index == _instructions.Count)
                {
                    _instructions.AddRange(collection);
                }
                else
                {
                    _instructions.InsertRange(index, collection);
                }
                int collectionCount = (_instructions.Count - count);

                while (deadJumps.Count > 0)
                {
                    JumpExits[deadJumps.Pop()] = collection.First();
                }

                // We'll try to keep the dictionary organized, even though it really doesn't matter.
                // Looks nice though.
                for (int i = ((index + collectionCount) - 1); i >= index; i--)
                {
                    ASInstruction instruction = _instructions[i];
                    _indices.Add(instruction, i);

                    List <ASInstruction> instructions = null;
                    if (!_opGroups.TryGetValue(instruction.OP, out instructions))
                    {
                        instructions = new List <ASInstruction>();
                        _opGroups.Add(instruction.OP, instructions);
                    }
                    instructions.Add(instruction);
                }
                for (int i = (index + collectionCount); i < _instructions.Count; i++)
                {
                    ASInstruction toPush = _instructions[i];
                    _indices[toPush] += collectionCount;
                }
            }
        }
예제 #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);
                    }
                }
            }
        }
예제 #5
0
        private int GetFinalJumpCount(ASMachine machine, Jumper jumper, List <ASInstruction> cleaned, Dictionary <int, List <ASInstruction> > localReferences, Stack <ASInstruction> valuePushers)
        {
            var  magicCount = 0;
            var  locals     = new List <Local>();
            var  pushers    = new List <ASInstruction>();
            bool?isJumping  = jumper.RunCondition(machine);

            // Get the instructions that pushed the values the jump instruction used.
            int popCount = jumper.GetPopCount();

            for (int i = 0; i < popCount; i++)
            {
                ASInstruction pusher = valuePushers.Pop();
                if (!pushers.Contains(pusher))
                {
                    pushers.Add(pusher);
                    // Get the instructions that were pushed by a GetLocal/N.
                    // These are used to determine whether the jump should be kept, since a local register could change within the jump body.
                    if (Local.IsValid(pusher.OP))
                    {
                        locals.Add((Local)pusher);
                    }
                    else if (Primitive.IsValid(pusher.OP) ||
                             pusher.OP == OPCode.Dup)
                    {
                        magicCount++;
                    }
                }
            }

            // Output is not known, keep the instruction.
            if (isJumping == null)
            {
                cleaned.Add(jumper);
                return(0);
            }

            if (pushers.Count != (magicCount + locals.Count))
            {
                // One or more push instructions are wildcards, they have a 'history' of being modified.
                // Keep this jump instruction, result could change.
                cleaned.Add(jumper);
                return(0);
            }

            // Gather information about the jump instruction, and it's 'block' of instructions that are being jumped over(if 'isJumping = true').
            ASInstruction exit                = null;
            bool          isBackwardsJump     = false;
            IEnumerable <ASInstruction> block = null;

            if (!JumpExits.TryGetValue(jumper, out exit))
            {
                // This jump instruction should not be 'cleaned', keep it.
                cleaned.Add(jumper);
                return(0);
            }
            if (IsBackwardsJump(jumper))
            {
                isBackwardsJump = true;

                block = cleaned
                        .Skip(cleaned.IndexOf(exit) + 1)
                        .TakeWhile(i => i != jumper);
            }
            else
            {
                block = (jumper.Offset > 0 ? GetJumpBlock(jumper) : null);
            }


            if (isJumping == true && block != null)
            {
                if (isBackwardsJump)
                {
                    // Check if any of the locals used by the jump instruction is being set within the body.
                    // If the anwser is yes, removing the jump instruction is a bad idea, since the output of the condition could change.
                    foreach (Local local in locals)
                    {
                        foreach (ASInstruction instruction in block)
                        {
                            if (!Local.IsValid(instruction.OP))
                            {
                                continue;
                            }
                            if (Local.IsGetLocal(instruction.OP))
                            {
                                continue;
                            }
                            var bodyLocal = (Local)instruction;

                            if (bodyLocal.Register == local.Register)
                            {
                                // Do not remove the jump instruction, condition result may change.
                                cleaned.Add(jumper);
                                return(0);
                            }
                        }
                    }
                }

                foreach (KeyValuePair <Jumper, ASInstruction> jumpExit in JumpExits)
                {
                    if (jumpExit.Key == jumper)
                    {
                        continue;
                    }

                    bool hasEntry = block.Contains(jumpExit.Key);   // Does a jump instruction begin somewhere in the block?
                    bool hasExit  = block.Contains(jumpExit.Value); // Does the jump instruction end somewhere in the block?

                    ASInstruction afterLast       = _instructions[_indices[block.Last()] + 1];
                    bool          isExitAfterLast = (jumpExit.Value == afterLast); // Does the exit of the jump that is in the block come after the final instruction of the current block?

                    if (hasEntry && !hasExit && !isExitAfterLast ||
                        hasExit && !hasEntry)
                    {
                        // Keep the jump instruction, since it will corrupt the other jump instruction that is using it.
                        cleaned.Add(jumper);
                        return(0);
                    }
                }
                foreach (KeyValuePair <LookUpSwitchIns, ASInstruction[]> switchExit in SwitchExits)
                {
                    foreach (ASInstruction caseExit in switchExit.Value)
                    {
                        if (block.Contains(caseExit))
                        {
                            cleaned.Add(jumper);
                            return(0);
                        }
                    }
                }
            }

            foreach (Local local in locals)
            {
                List <ASInstruction> references = localReferences[local.Register];
                references.Remove(local);
            }

            // Remove the instructions that pushed values for the jump instruction that is to be removed.
            foreach (ASInstruction pusher in pushers)
            {
                cleaned.Remove(pusher);
            }

            if (isJumping == false || isBackwardsJump)
            {
                block = null;
            }
            JumpExits.Remove(jumper);
            return(block?.Count() ?? 0);
        }
예제 #6
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);
                        }
                    }
                }
            }
        }
예제 #7
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();
        }