Example #1
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);
        }
Example #2
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();
        }