protected bool CheckSameMethods(IMethod method, MethodDef methodToInline, int ignoreLastMethodToInlineArgs)
        {
            var  methodToInlineArgs = methodToInline.Parameters;
            var  methodArgs         = DotNetUtils.GetArgs(method);
            bool hasImplicitThis    = method.MethodSig.ImplicitThis;

            if (methodToInlineArgs.Count - ignoreLastMethodToInlineArgs != methodArgs.Count)
            {
                return(false);
            }
            for (int i = 0; i < methodArgs.Count; i++)
            {
                var methodArg         = methodArgs[i];
                var methodToInlineArg = GetArgType(methodToInline, methodToInlineArgs[i].Type);
                if (!IsCompatibleType(i, methodArg, methodToInlineArg))
                {
                    if (i != 0 || !hasImplicitThis)
                    {
                        return(false);
                    }
                    if (!IsCompatibleValueThisPtr(methodArg, methodToInlineArg))
                    {
                        return(false);
                    }
                }
            }

            return(true);
        }
        bool FindArgs(CallResult callResult)
        {
            var block      = callResult.block;
            var method     = callResult.GetMethodRef();
            var methodArgs = DotNetUtils.GetArgs(method);
            int numArgs    = methodArgs.Count;
            var args       = new object[numArgs];

            int instrIndex = callResult.callEndIndex - 1;

            for (int i = numArgs - 1; i >= 0; i--)
            {
                object arg = null;
                if (!GetArg(method, block, ref arg, ref instrIndex))
                {
                    return(false);
                }
                if (arg is int)
                {
                    arg = FixIntArg(methodArgs[i], (int)arg);
                }
                else if (arg is long)
                {
                    arg = FixIntArg(methodArgs[i], (long)arg);
                }
                args[i] = arg;
            }

            callResult.args           = args;
            callResult.callStartIndex = instrIndex + 1;
            return(true);
        }
        protected InstructionPatcher TryInlineOtherMethod(int patchIndex, MethodDef methodToInline, Instruction instr, int instrIndex, int popLastArgs)
        {
            int  loadIndex       = 0;
            int  methodArgsCount = DotNetUtils.GetArgsCount(methodToInline);
            bool foundLdarga     = false;

            while (instr != null && loadIndex < methodArgsCount)
            {
                bool isLdarg = true;
                switch (instr.OpCode.Code)
                {
                case Code.Ldarg:
                case Code.Ldarg_S:
                case Code.Ldarg_0:
                case Code.Ldarg_1:
                case Code.Ldarg_2:
                case Code.Ldarg_3:
                    break;

                case Code.Ldarga:
                case Code.Ldarga_S:
                    foundLdarga = true;
                    break;

                default:
                    isLdarg = false;
                    break;
                }
                if (!isLdarg)
                {
                    break;
                }

                if (instr.GetParameterIndex() != loadIndex)
                {
                    return(null);
                }
                loadIndex++;
                instr = DotNetUtils.GetInstruction(methodToInline.Body.Instructions, ref instrIndex);
                instr = OnAfterLoadArg(methodToInline, instr, ref instrIndex);
            }
            if (instr == null || loadIndex != methodArgsCount - popLastArgs)
            {
                return(null);
            }

            switch (instr.OpCode.Code)
            {
            case Code.Call:
            case Code.Callvirt:
                if (foundLdarga)
                {
                    return(null);
                }
                var callInstr    = instr;
                var calledMethod = callInstr.Operand as IMethod;
                if (calledMethod == null)
                {
                    return(null);
                }

                if (!IsCompatibleType(-1, calledMethod.MethodSig.RetType, methodToInline.MethodSig.RetType))
                {
                    return(null);
                }

                if (!CheckSameMethods(calledMethod, methodToInline, popLastArgs))
                {
                    return(null);
                }

                if (!HasAccessTo(instr.Operand))
                {
                    return(null);
                }

                return(new InstructionPatcher(patchIndex, instrIndex, callInstr));

            case Code.Newobj:
                if (foundLdarga)
                {
                    return(null);
                }
                var newobjInstr = instr;
                var ctor        = newobjInstr.Operand as IMethod;
                if (ctor == null)
                {
                    return(null);
                }

                if (!IsCompatibleType(-1, ctor.DeclaringType, methodToInline.MethodSig.RetType))
                {
                    return(null);
                }

                var methodArgs       = methodToInline.Parameters;
                var calledMethodArgs = DotNetUtils.GetArgs(ctor);
                if (methodArgs.Count + 1 - popLastArgs != calledMethodArgs.Count)
                {
                    return(null);
                }
                for (int i = 1; i < calledMethodArgs.Count; i++)
                {
                    if (!IsCompatibleType(i, calledMethodArgs[i], methodArgs[i - 1].Type))
                    {
                        return(null);
                    }
                }

                if (!HasAccessTo(instr.Operand))
                {
                    return(null);
                }

                return(new InstructionPatcher(patchIndex, instrIndex, newobjInstr));

            case Code.Ldfld:
            case Code.Ldflda:
            case Code.Ldftn:
            case Code.Ldvirtftn:
            case Code.Ldlen:
            case Code.Initobj:
            case Code.Isinst:
            case Code.Castclass:
            case Code.Newarr:
            case Code.Ldtoken:
            case Code.Unbox_Any:
                var ldInstr = instr;
                if (methodArgsCount != 1)
                {
                    return(null);
                }

                if (instr.OpCode.OperandType != OperandType.InlineNone && !HasAccessTo(instr.Operand))
                {
                    return(null);
                }

                return(new InstructionPatcher(patchIndex, instrIndex, ldInstr));

            default:
                return(null);
            }
        }
        bool DeobfuscateFields()
        {
            foreach (var info in fieldWrites.Values)
            {
                info.Clear();
            }

            foreach (var method in allMethods)
            {
                if (method.Body == null)
                {
                    continue;
                }
                var instructions = method.Body.Instructions;
                for (int i = 0; i < instructions.Count; i++)
                {
                    var                 instr     = instructions[i];
                    TypeSig             fieldType = null;
                    TypeInfo <FieldDef> info      = null;
                    IField              field;
                    switch (instr.OpCode.Code)
                    {
                    case Code.Stfld:
                    case Code.Stsfld:
                        field = instr.Operand as IField;
                        if (field == null)
                        {
                            continue;
                        }
                        if (!fieldWrites.TryGetValue(field, out info))
                        {
                            continue;
                        }
                        bool wasNewobj;
                        fieldType = GetLoadedType(info.arg.DeclaringType, method, instructions, i, out wasNewobj);
                        if (fieldType == null)
                        {
                            continue;
                        }
                        info.Add(fieldType, wasNewobj);
                        break;

                    case Code.Call:
                    case Code.Calli:
                    case Code.Callvirt:
                    case Code.Newobj:
                        var pushedArgs   = MethodStack.GetPushedArgInstructions(instructions, i);
                        var calledMethod = instr.Operand as IMethod;
                        if (calledMethod == null)
                        {
                            continue;
                        }
                        var calledMethodDefOrRef = calledMethod as IMethodDefOrRef;
                        var calledMethodSpec     = calledMethod as MethodSpec;
                        if (calledMethodSpec != null)
                        {
                            calledMethodDefOrRef = calledMethodSpec.Method;
                        }
                        if (calledMethodDefOrRef == null)
                        {
                            continue;
                        }

                        IList <TypeSig> calledMethodArgs = DotNetUtils.GetArgs(calledMethodDefOrRef);
                        calledMethodArgs = DotNetUtils.ReplaceGenericParameters(calledMethodDefOrRef.DeclaringType.TryGetGenericInstSig(), calledMethodSpec, calledMethodArgs);
                        for (int j = 0; j < pushedArgs.NumValidArgs; j++)
                        {
                            var pushInstr = pushedArgs.GetEnd(j);
                            if (pushInstr.OpCode.Code != Code.Ldfld && pushInstr.OpCode.Code != Code.Ldsfld)
                            {
                                continue;
                            }

                            field = pushInstr.Operand as IField;
                            if (field == null)
                            {
                                continue;
                            }
                            if (!fieldWrites.TryGetValue(field, out info))
                            {
                                continue;
                            }
                            fieldType = calledMethodArgs[calledMethodArgs.Count - 1 - j];
                            if (!IsValidType(info.arg.DeclaringType, fieldType))
                            {
                                continue;
                            }
                            info.Add(fieldType);
                        }
                        break;

                    default:
                        continue;
                    }
                }
            }

            bool modified    = false;
            var  removeThese = new List <FieldDef>();

            foreach (var info in fieldWrites.Values)
            {
                if (info.UpdateNewType(module))
                {
                    removeThese.Add(info.arg);
                    GetUpdatedField(info.arg).newFieldType = info.newType;
                    info.arg.FieldSig.Type = info.newType;
                    modified = true;
                }
            }
            foreach (var field in removeThese)
            {
                fieldWrites.Remove(field);
            }
            return(modified);
        }
        void DeobfuscateMethod(MethodDef method)
        {
            if (!method.IsStatic || method.Body == null)
            {
                return;
            }

            bool fixReturnType = IsUnknownType(method.MethodSig.GetRetType());

            argInfos.Clear();
            foreach (var arg in method.Parameters)
            {
                if (arg.IsHiddenThisParameter)
                {
                    continue;
                }
                if (!IsUnknownType(arg))
                {
                    continue;
                }
                argInfos[arg] = new TypeInfo <Parameter>(arg);
            }
            if (argInfos.Count == 0 && !fixReturnType)
            {
                return;
            }

            var        methodParams = method.Parameters;
            PushedArgs pushedArgs;
            var        instructions = method.Body.Instructions;

            for (int i = 0; i < instructions.Count; i++)
            {
                var instr = instructions[i];
                switch (instr.OpCode.Code)
                {
                case Code.Ret:
                    if (!fixReturnType)
                    {
                        break;
                    }
                    bool wasNewobj;
                    var  type = GetLoadedType(method, method, instructions, i, out wasNewobj);
                    if (type == null)
                    {
                        break;
                    }
                    methodReturnInfo.Add(type);
                    break;

                case Code.Call:
                case Code.Calli:
                case Code.Callvirt:
                case Code.Newobj:
                    pushedArgs = MethodStack.GetPushedArgInstructions(instructions, i);
                    var calledMethod = instr.Operand as IMethod;
                    if (calledMethod == null)
                    {
                        break;
                    }
                    var calledMethodParams = DotNetUtils.GetArgs(calledMethod);
                    for (int j = 0; j < pushedArgs.NumValidArgs; j++)
                    {
                        int calledMethodParamIndex = calledMethodParams.Count - j - 1;
                        var ldInstr = pushedArgs.GetEnd(j);
                        switch (ldInstr.OpCode.Code)
                        {
                        case Code.Ldarg:
                        case Code.Ldarg_S:
                        case Code.Ldarg_0:
                        case Code.Ldarg_1:
                        case Code.Ldarg_2:
                        case Code.Ldarg_3:
                            AddMethodArgType(method, GetParameter(methodParams, ldInstr), DotNetUtils.GetArg(calledMethodParams, calledMethodParamIndex));
                            break;

                        default:
                            break;
                        }
                    }
                    break;

                case Code.Castclass:
                    pushedArgs = MethodStack.GetPushedArgInstructions(instructions, i);
                    if (pushedArgs.NumValidArgs < 1)
                    {
                        break;
                    }
                    AddMethodArgType(method, GetParameter(methodParams, pushedArgs.GetEnd(0)), instr.Operand as ITypeDefOrRef);
                    break;

                case Code.Stloc:
                case Code.Stloc_S:
                case Code.Stloc_0:
                case Code.Stloc_1:
                case Code.Stloc_2:
                case Code.Stloc_3:
                    pushedArgs = MethodStack.GetPushedArgInstructions(instructions, i);
                    if (pushedArgs.NumValidArgs < 1)
                    {
                        break;
                    }
                    AddMethodArgType(method, GetParameter(methodParams, pushedArgs.GetEnd(0)), instr.GetLocal(method.Body.Variables));
                    break;

                case Code.Stsfld:
                    pushedArgs = MethodStack.GetPushedArgInstructions(instructions, i);
                    if (pushedArgs.NumValidArgs < 1)
                    {
                        break;
                    }
                    AddMethodArgType(method, GetParameter(methodParams, pushedArgs.GetEnd(0)), instr.Operand as IField);
                    break;

                case Code.Stfld:
                    pushedArgs = MethodStack.GetPushedArgInstructions(instructions, i);
                    if (pushedArgs.NumValidArgs >= 1)
                    {
                        var field = instr.Operand as IField;
                        AddMethodArgType(method, GetParameter(methodParams, pushedArgs.GetEnd(0)), field);
                        if (pushedArgs.NumValidArgs >= 2 && field != null)
                        {
                            AddMethodArgType(method, GetParameter(methodParams, pushedArgs.GetEnd(1)), field.DeclaringType);
                        }
                    }
                    break;

                case Code.Ldfld:
                case Code.Ldflda:
                    pushedArgs = MethodStack.GetPushedArgInstructions(instructions, i);
                    if (pushedArgs.NumValidArgs < 1)
                    {
                        break;
                    }
                    AddMethodArgType(method, GetParameter(methodParams, pushedArgs.GetEnd(0)), instr.Operand as IField);
                    break;

                //TODO: For better results, these should be checked:
                case Code.Starg:
                case Code.Starg_S:

                case Code.Ldelema:
                case Code.Ldelem:
                case Code.Ldelem_I:
                case Code.Ldelem_I1:
                case Code.Ldelem_I2:
                case Code.Ldelem_I4:
                case Code.Ldelem_I8:
                case Code.Ldelem_R4:
                case Code.Ldelem_R8:
                case Code.Ldelem_Ref:
                case Code.Ldelem_U1:
                case Code.Ldelem_U2:
                case Code.Ldelem_U4:

                case Code.Ldind_I:
                case Code.Ldind_I1:
                case Code.Ldind_I2:
                case Code.Ldind_I4:
                case Code.Ldind_I8:
                case Code.Ldind_R4:
                case Code.Ldind_R8:
                case Code.Ldind_Ref:
                case Code.Ldind_U1:
                case Code.Ldind_U2:
                case Code.Ldind_U4:

                case Code.Ldobj:

                case Code.Stelem:
                case Code.Stelem_I:
                case Code.Stelem_I1:
                case Code.Stelem_I2:
                case Code.Stelem_I4:
                case Code.Stelem_I8:
                case Code.Stelem_R4:
                case Code.Stelem_R8:
                case Code.Stelem_Ref:

                case Code.Stind_I:
                case Code.Stind_I1:
                case Code.Stind_I2:
                case Code.Stind_I4:
                case Code.Stind_I8:
                case Code.Stind_R4:
                case Code.Stind_R8:
                case Code.Stind_Ref:

                case Code.Stobj:
                default:
                    break;
                }
            }
        }