Ejemplo n.º 1
0
 private static void DumpILAstTree(DevirtualisationContext context, VirtualisedMethod method)
 {
     using (var fs = File.CreateText(Path.Combine(context.Options.OutputOptions.ILAstDumpsDirectory,
                                                  $"function_{method.Function.EntrypointAddress:X4}_tree.dot")))
     {
         WriteHeader(fs, method);
         var writer = new DotWriter(fs, new BasicBlockSerializer());
         writer.Write(method.ILCompilationUnit.ConvertToGraphViz(method.CallerMethod));
     }
 }
Ejemplo n.º 2
0
 private static void DumpControlFlowGraph(DevirtualisationContext context, VirtualisedMethod method)
 {
     using (var fs = File.CreateText(Path.Combine(
                                         context.Options.OutputOptions.ILDumpsDirectory,
                                         $"function_{method.Function.EntrypointAddress:X4}.dot")))
     {
         var writer = new DotWriter(fs, new BasicBlockSerializer());
         writer.Write(method.ControlFlowGraph.ConvertToGraphViz(ILBasicBlock.BasicBlockProperty));
     }
 }
Ejemplo n.º 3
0
        public void Run(DevirtualisationContext context)
        {
            if (context.Constants.ConstantFields.Count == 0)
            {
                context.Logger.Warning(Tag, "Finding opcode handlers using custom constant mapping is unsupported.");
                return;
            }

            context.OpCodeMapping = ResolveOpCodeLookupTable(context);
        }
Ejemplo n.º 4
0
        private static void DumpCil(DevirtualisationContext context, VirtualisedMethod method)
        {
            var methodBody = method.CallerMethod.CilMethodBody;
            var formatter  = new CilInstructionFormatter(methodBody);

            using (var fs = File.CreateText(Path.Combine(context.Options.OutputOptions.CilDumpsDirectory, $"function_{method.Function.EntrypointAddress:X4}.il")))
            {
                WriteBasicInfo(fs, method);

                // Dump variables.
                var variables =
                    ((LocalVariableSignature)methodBody.Signature?.Signature)?.Variables
                    ?? Array.Empty <VariableSignature>();

                if (variables.Count > 0)
                {
                    fs.WriteLine("// Variables: ");
                    for (int i = 0; i < variables.Count; i++)
                    {
                        var variable = variables[i];
                        fs.WriteLine($"//    {i}: {variable.VariableType}");
                    }
                    fs.WriteLine();
                }

                // Dump EHs.
                if (methodBody.ExceptionHandlers.Count > 0)
                {
                    fs.WriteLine("// Exception handlers:");
                    for (int i = 0; i < methodBody.ExceptionHandlers.Count; i++)
                    {
                        var eh = methodBody.ExceptionHandlers[i];
                        fs.WriteLine(
                            "//    {0, 2}: EHType: {1, -10} TryStart: {2, -10} TryEnd: {3, -10} HandlerStart: {4, -10} HandlerEnd: {5, -10} FilterStart: {6, -10} CatchType: {7}",
                            i.ToString(),
                            eh.HandlerType,
                            eh.TryStart != null ? $"IL_{eh.TryStart.Offset:X4}" : "<null>",
                            eh.TryEnd != null ? $"IL_{eh.TryEnd.Offset:X4}" : "<null>",
                            eh.HandlerStart != null ? $"IL_{eh.HandlerStart.Offset:X4}" : "<null>",
                            eh.HandlerEnd != null ? $"IL_{eh.HandlerEnd.Offset:X4}" : "<null>",
                            eh.FilterStart != null ? $"IL_{eh.FilterStart.Offset:X4}" : "<null>",
                            eh.CatchType?.FullName ?? "<null>");
                    }

                    fs.WriteLine();
                }

                // Dump instructions.
                foreach (var instruction in methodBody.Instructions)
                {
                    fs.WriteLine(formatter.FormatInstruction(instruction));
                }
            }
        }
Ejemplo n.º 5
0
        private MethodSignature VMSignatureToMethodSignature(DevirtualisationContext context, VMFunctionSignature signature)
        {
            var returnType     = GetTypeSig(context, signature.ReturnToken);
            var parameterTypes = signature.ParameterTokens.Select(x => GetTypeSig(context, x));

            bool hasThis = (signature.Flags & context.Constants.FlagInstance) != 0;

            return(new MethodSignature(parameterTypes.Skip(hasThis ? 1 : 0), returnType)
            {
                HasThis = hasThis
            });
        }
 public void Run(DevirtualisationContext context)
 {
     if (context.Options.OverrideConstants)
     {
         context.Logger.Debug(Tag, "Using pre-defined constants.");
     }
     else
     {
         context.Logger.Debug(Tag, "Attempting to auto-detect constants...");
         context.Constants = AutoDetectConstants(context);
     }
 }
Ejemplo n.º 7
0
        private void MapVMExportsToMethods(DevirtualisationContext context)
        {
            int matchedMethods = 0;

            // Go over all methods in the assembly and detect whether it is virtualised by looking for a call
            // to the VMEntry.Run method. If it is, also detect the export ID associated to it to define a mapping
            // between VMExport and physical method.
            foreach (var type in context.TargetModule.Assembly.Modules[0].GetAllTypes())
            {
                foreach (var method in type.Methods)
                {
                    if (!context.Options.SelectedMethods.Contains(method.MetadataToken.Rid))
                    {
                        continue;
                    }

                    var matchingVmMethods = GetMatchingVirtualisedMethods(context, method);

                    if (matchingVmMethods.Count > 0 &&
                        method.CilMethodBody != null &&
                        TryExtractExportTypeFromMethodBody(context, method.CilMethodBody, out int exportId))
                    {
                        context.Logger.Debug(Tag, $"Detected call to export {exportId} in {method}.");
                        var vmMethod = matchingVmMethods.FirstOrDefault(x => x.ExportId == exportId);
                        if (vmMethod != null)
                        {
                            vmMethod.CallerMethod = method;
                        }
                        else
                        {
                            context.Logger.Debug(Tag, $"Ignoring call to export {exportId} in {method}.");
                        }
                        matchedMethods++;
                    }
                }
            }

            // There could be more exports defined in the #Koi md stream than we were able to directly match
            // with methods in the target assembly. It is expected that the HELPER_INIT method is not matched to a
            // physical method definition, but it could also be that we missed one due to some other form of
            // obfuscation applied to it (maybe a fork of the vanilla version).

            // These missing physical methods will later be added, together with the internal functions.

            // Warn if there are more than one method not directly mapped to a physical method definition.
            if (matchedMethods < context.VirtualisedMethods.Count - 1)
            {
                context.Logger.Warning(Tag, $"Not all VM exports were mapped to physical method definitions "
                                       + $"({matchedMethods} out of {context.VirtualisedMethods.Count} were mapped). "
                                       + "Dummies will be added to the assembly for the remaining exports.");
            }
        }
Ejemplo n.º 8
0
 private void ConvertFunctionSignatures(DevirtualisationContext context)
 {
     foreach (var entry in context.KoiStream.Exports.Where(x => !x.Value.IsSignatureOnly))
     {
         context.Logger.Debug(Tag, $"Converting VM signature of export {entry.Key} to method signature...");
         context.VirtualisedMethods.Add(
             new VirtualisedMethod(new VMFunction(entry.Value.EntrypointAddress, entry.Value.EntryKey), entry.Key,
                                   entry.Value)
         {
             MethodSignature = VMSignatureToMethodSignature(context, entry.Value.Signature)
         });
     }
 }
Ejemplo n.º 9
0
        public void Run(DevirtualisationContext context)
        {
            context.Logger.Debug(Tag, "Parsing #Koi stream...");
            context.KoiStream = context.TargetImage.Header.GetStream <KoiStream>();

            if (context.KoiStream == null)
            {
                throw new DevirtualisationException(
                          "Koi stream was not found in the target PE. This could be because the input file is " +
                          "not protected with KoiVM, or the metadata stream uses a name that is different " +
                          "from the one specified in the input parameters.");
            }
        }
Ejemplo n.º 10
0
        private static MethodSignature CreateMethodSignature(DevirtualisationContext context, IFrameLayout layout)
        {
            var flags          = layout.HasThis ? CallingConventionAttributes.HasThis : 0;
            var returnType     = layout.ReturnType ?? context.TargetModule.CorLibTypeFactory.Object;
            var parameterTypes = new List <TypeSignature>();

            // Add parameters.
            for (int i = 0; i < layout.Parameters.Count; i++)
            {
                parameterTypes.Add(layout.Parameters[i].Type ?? context.TargetModule.CorLibTypeFactory.Object);
            }

            return(new MethodSignature(flags, returnType, parameterTypes));
        }
Ejemplo n.º 11
0
        public void Run(DevirtualisationContext context)
        {
            foreach (var method in context.VirtualisedMethods)
            {
                if (method.IsExport &&
                    !context.Options.SelectedExports.Contains(method.ExportId.Value, method.ExportInfo))
                {
                    continue;
                }

                context.Logger.Debug(Tag, $"Building IL AST for function_{method.Function.EntrypointAddress:X4}...");

                // Create builder.
                var builder = new ILAstBuilder(context.TargetImage)
                {
                    Logger = context.Logger
                };

                // Subscribe to progress events if user specified it in the options.
                if (context.Options.OutputOptions.DumpAllControlFlowGraphs)
                {
                    int step = 1;
                    builder.InitialAstBuilt += (sender, args) =>
                    {
                        context.Logger.Debug(Tag, $"Dumping initial IL AST for function_{method.Function.EntrypointAddress:X4}...");
                        method.ILCompilationUnit = args;
                        DumpILAst(context, method, $" (0. Initial)");
                    };

                    builder.TransformEnd += (sender, args) =>
                    {
                        context.Logger.Debug(Tag, $"Dumping tentative IL AST for function_{method.Function.EntrypointAddress:X4}...");
                        method.ILCompilationUnit = args.Unit;
                        DumpILAst(context, method, $" ({step++}. {args.Transform.Name}-{args.Iteration})");
                    };
                }

                // Build the AST.
                method.ILCompilationUnit = builder.BuildAst(method.ControlFlowGraph, method.Function.FrameLayout, context.Constants);

                // Dump graphs if user specified it in the options.
                if (context.Options.OutputOptions.DumpControlFlowGraphs)
                {
                    context.Logger.Log(Tag, $"Dumping IL AST for function_{method.Function.EntrypointAddress:X4}...");
                    DumpILAst(context, method);

                    DumpILAstTree(context, method);
                }
            }
        }
Ejemplo n.º 12
0
        private VMEntryInfo TryExtractVMEntryInfoFromType(DevirtualisationContext context, TypeDefinition type)
        {
            var info = new VMEntryInfo
            {
                VMEntryType = type
            };

            foreach (var method in type.Methods)
            {
                var parameters = method.Signature.Parameters;
                switch (parameters.Count)
                {
                case 3:
                {
                    if (parameters[0].ParameterType.IsTypeOf("System", "RuntimeTypeHandle") &&
                        parameters[1].ParameterType.IsTypeOf("System", "UInt32") &&
                        parameters[2].ParameterType is SzArrayTypeSignature arrayType &&
                        arrayType.BaseType.IsTypeOf("System", "Object"))
                    {
                        info.RunMethod1 = method;
                    }

                    break;
                }

                case 4:
                {
                    if (parameters[0].ParameterType.IsTypeOf("System", "RuntimeTypeHandle") &&
                        parameters[1].ParameterType.IsTypeOf("System", "UInt32") &&
                        parameters[2].ParameterType is SzArrayTypeSignature arrayType &&
                        arrayType.BaseType is PointerTypeSignature pointerType1 &&
                        pointerType1.BaseType.IsTypeOf("System", "Void") &&
                        parameters[3].ParameterType is PointerTypeSignature pointerType2 &&
                        pointerType2.BaseType.IsTypeOf("System", "Void"))
                    {
                        info.RunMethod2 = method;
                    }

                    break;
                }
                }
            }

            if (info.RunMethod1 == null || info.RunMethod2 == null)
            {
                return(null);
            }

            return(info);
        }
Ejemplo n.º 13
0
        private static OpCodeMapping ResolveOpCodeLookupTable(DevirtualisationContext context)
        {
            context.Logger.Debug(Tag, "Locating opcode interface...");
            var infos = LocateOpCodeInterfaces(context);

            if (infos.Count == 0)
            {
                throw new DevirtualisationException("Could not locate opcode interfaces.");
            }
            context.Logger.Debug(Tag,
                                 $"Opcode interfaces found ({string.Join(", ", infos.Select(x => x.InterfaceType.MetadataToken))}).");

            context.Logger.Debug(Tag, "Resolving opcode lookup table...");
            return(MatchOpCodeTypes(context, infos));
        }
Ejemplo n.º 14
0
        private MethodSignature VMSignatureToMethodSignature(DevirtualisationContext context, VMFunctionSignature signature)
        {
            var module = context.TargetModule;

            var returnType     = ((ITypeDescriptor)module.LookupMember(signature.ReturnToken)).ToTypeSignature();
            var parameterTypes = signature.ParameterTokens
                                 .Select(x => ((ITypeDescriptor)module.LookupMember(x)).ToTypeSignature());

            var hasThis = (signature.Flags & context.Constants.FlagInstance) != 0;

            return(new MethodSignature(
                       hasThis ? CallingConventionAttributes.HasThis : 0,
                       returnType,
                       parameterTypes.Skip(hasThis ? 1 : 0)));
        }
Ejemplo n.º 15
0
        private static MethodSignature CreateMethodSignature(DevirtualisationContext context, IFrameLayout layout)
        {
            var methodSignature = new MethodSignature(layout.ReturnType ?? context.TargetImage.TypeSystem.Object);

            // Add parameters.
            for (int i = 0; i < layout.Parameters.Count; i++)
            {
                methodSignature.Parameters.Add(
                    new ParameterSignature(layout.Parameters[i].Type ?? context.TargetImage.TypeSystem.Object));
            }

            methodSignature.HasThis = layout.HasThis;

            return(methodSignature);
        }
Ejemplo n.º 16
0
        private ICollection <VirtualisedMethod> GetMatchingVirtualisedMethods(
            DevirtualisationContext context,
            MethodDefinition methodToMatch)
        {
            var matches = new List <VirtualisedMethod>();

            foreach (var vmMethod in context.VirtualisedMethods.Where(x => x.CallerMethod == null))
            {
                if (Comparer.Equals(methodToMatch.Signature, vmMethod.MethodSignature))
                {
                    matches.Add(vmMethod);
                }
            }

            return(matches);
        }
Ejemplo n.º 17
0
        public void Run(DevirtualisationContext context)
        {
            if (context.Options.OverrideConstants)
            {
                context.Logger.Debug(Tag, "Using pre-defined constants.");
                context.Constants = context.Options.Constants;
            }
            else
            {
                context.Logger.Debug(Tag, "Attempting to auto-detect constants...");
                context.Constants = AutoDetectConstants(context);
            }

            context.Logger.Debug(Tag, "Attempting to extract key scalar value...");
            context.Constants.KeyScalar = FindKeyScalarValue(context);
        }
Ejemplo n.º 18
0
        public void Run(DevirtualisationContext context)
        {
            var flagHelper = VmHelperGenerator.ImportFlagHelper(context.TargetImage, context.Constants);

            foreach (var method in context.VirtualisedMethods)
            {
                if (method.IsExport &&
                    !context.Options.SelectedExports.Contains(method.ExportId.Value, method.ExportInfo))
                {
                    continue;
                }

                RecompileToCilAst(context, method);
                GenerateCil(context, method, flagHelper);
            }
        }
Ejemplo n.º 19
0
        private VMEntryInfo SearchVMEntryType(DevirtualisationContext context)
        {
            foreach (var type in context.RuntimeModule.Assembly.Modules[0].TopLevelTypes)
            {
                // TODO: maybe a better matching criteria is required here.
                if (type.Methods.Count >= 5)
                {
                    var info = TryExtractVMEntryInfoFromType(context, type);
                    if (info != null)
                    {
                        return(info);
                    }
                }
            }

            return(null);
        }
Ejemplo n.º 20
0
        private static TypeDefinition TryInferDeclaringTypeFromThisParameter(
            DevirtualisationContext context,
            MethodDefinition dummy)
        {
            if (dummy.Signature.ParameterTypes.Count == 0)
            {
                context.Logger.Warning(Tag,
                                       $"Method {dummy.Name} is marked as an instance method but does " +
                                       "not define any parameters. The method will be made static instead.");
                return(null);
            }

            var thisType = dummy.Signature.ParameterTypes[0];

            return(thisType.Scope == context.TargetModule.Assembly.Modules[0]
                ? thisType.Resolve()
                : null);
        }
Ejemplo n.º 21
0
        private static void GenerateCil(DevirtualisationContext context, VirtualisedMethod method, TypeDefinition flagHelper)
        {
            // Generate final CIL code.
            context.Logger.Debug(Tag, $"Generating CIL for function_{method.Function.EntrypointAddress:X4}...");

            var generator = new CilMethodBodyGenerator(context.Constants, flagHelper)
            {
                EnableStackVerification          = !context.Options.EnableSalvageMode,
                EnableExceptionHandlerValidation = !context.Options.EnableSalvageMode
            };

            method.CallerMethod.CilMethodBody = generator.Compile(method.CallerMethod, method.CilCompilationUnit);
            if (context.Options.OutputOptions.DumpRecompiledCil)
            {
                context.Logger.Log(Tag, $"Dumping CIL of function_{method.Function.EntrypointAddress:X4}...");
                DumpCil(context, method);
            }
        }
Ejemplo n.º 22
0
        private static void RecompileToCilAst(DevirtualisationContext context, VirtualisedMethod method)
        {
            context.Logger.Debug(Tag, $"Recompiling function_{method.Function.EntrypointAddress:X4} to CIL AST...");

            var recompiler = new ILToCilRecompiler(method.CallerMethod.CilMethodBody, context.TargetModule, context)
            {
                Logger = context.Logger,
                InferParameterTypes = method.IsMethodSignatureInferred
            };

            // Subscribe to progress events if specified in the options.
            if (context.Options.OutputOptions.DumpAllControlFlowGraphs)
            {
                int step = 1;
                recompiler.InitialAstBuilt +=
                    (sender, args) =>
                {
                    context.Logger.Debug2(Tag,
                                          $"Dumping initial CIL AST of function_{method.Function.EntrypointAddress:X4}...");
                    method.CilCompilationUnit = args;
                    DumpCilAst(context, method, $" (0. Initial)");
                };
                recompiler.TransformEnd +=
                    (sender, args) =>
                {
                    context.Logger.Debug2(Tag,
                                          $"Dumping tentative CIL AST of function_{method.Function.EntrypointAddress:X4}...");
                    method.CilCompilationUnit = args.Unit;
                    DumpCilAst(context, method, $" ({step++}. {args.Transform.Name})");
                };
            }

            // Recompile!
            method.CilCompilationUnit = recompiler.Recompile(method.ILCompilationUnit);

            // Dump AST if specified in the options.
            if (context.Options.OutputOptions.DumpControlFlowGraphs)
            {
                context.Logger.Log(Tag, $"Dumping CIL AST of function_{method.Function.EntrypointAddress:X4}...");
                DumpCilAst(context, method);
                DumpCilAstTree(context, method);
            }
        }
Ejemplo n.º 23
0
        private static OpCodeMapping ResolveOpCodeLookupTable(DevirtualisationContext context)
        {
            context.Logger.Debug(Tag, "Locating opcode interface...");
            var infos = LocateOpCodeInterfaces(context);

            if (infos.Count == 0)
            {
                // Since finding the opcode handlers is not really used (yet) in the devirtualizer, except for debugging
                // purposes and renaming of symbols, it is enough to just warn the user instead of throwing an exception.
                context.Logger.Warning(Tag, "Could not locate opcode interfaces.");
                return(null);
            }

            context.Logger.Debug(Tag,
                                 $"Opcode interfaces found ({string.Join(", ", infos.Select(x => x.InterfaceType.MetadataToken))}).");

            context.Logger.Debug(Tag, "Resolving opcode lookup table...");
            return(MatchOpCodeTypes(context, infos));
        }
Ejemplo n.º 24
0
        private IDictionary <FieldDefinition, byte> FindConstantFieldsAndValues(DevirtualisationContext context)
        {
            context.Logger.Debug(Tag, "Locating constants type...");
            var constantsType = LocateConstantsType(context);

            if (constantsType == null)
            {
                throw new DevirtualisationException("Could not locate constants type!");
            }
            context.Logger.Debug(Tag, $"Found constants type ({constantsType.MetadataToken}).");

            if (context.Options.RenameSymbols)
            {
                constantsType.Namespace = "KoiVM.Runtime.Dynamic";
                constantsType.Name      = "Constants";
            }

            context.Logger.Debug(Tag, $"Resolving constants table...");
            return(ParseConstantValues(constantsType));
        }
Ejemplo n.º 25
0
        private static TypeDefinition LocateConstantsType(DevirtualisationContext context)
        {
            TypeDefinition constantsType = null;

            if (context.Options.OverrideVMConstantsToken)
            {
                context.Logger.Debug(Tag, $"Using token {context.Options.VMConstantsToken} for constants type.");
                constantsType = (TypeDefinition)context.RuntimeModule.LookupMember(context.Options.VMConstantsToken.Value);
            }
            else
            {
                // Constants type contains a lot of public static byte fields, and only those byte fields.
                // Therefore we pattern match on this signature, by finding the type with the most public
                // static byte fields.

                // It is unlikely that any other type has that many byte fields, although it is possible.
                // This could be improved later on.

                int max = 0;
                foreach (var type in context.RuntimeModule.Assembly.Modules[0].TopLevelTypes)
                {
                    // Optimisation: Check first count of all fields. We need at least the amount of opcodes of fields.
                    if (type.Fields.Count < (int)ILCode.Max)
                    {
                        continue;
                    }

                    // Count public static byte fields.
                    int byteFields = type.Fields.Count(x =>
                                                       x.IsPublic && x.IsStatic && x.Signature.FieldType.IsTypeOf("System", "Byte"));

                    if (byteFields == type.Fields.Count && max < byteFields)
                    {
                        constantsType = type;
                        max           = byteFields;
                    }
                }
            }

            return(constantsType);
        }
Ejemplo n.º 26
0
        private static uint FindKeyScalarValue(DevirtualisationContext context)
        {
            context.Logger.Debug(Tag, "Locating VMContext type...");
            var vmCtxType = LocateVmContextType(context.RuntimeModule);

            if (vmCtxType is null)
            {
                context.Logger.Warning(Tag, "Could not locate VMContext type, using default scalar value!");
                return(7);
            }
            context.Logger.Debug(Tag, $"Found VMContext type ({vmCtxType.MetadataToken}).");

            if (context.Options.RenameSymbols)
            {
                vmCtxType.Namespace = "KoiVM.Runtime.Execution";
                vmCtxType.Name      = "VMContext";
            }

            var readByteMethod = vmCtxType.Methods.First(x => x.Signature.ReturnType.IsTypeOf("System", "Byte"));

            if (context.Options.RenameSymbols)
            {
                readByteMethod.Name = "ReadByte";
            }

            var instructions = readByteMethod.CilMethodBody.Instructions;

            for (int i = 0; i < instructions.Count; i++)
            {
                var instr = instructions[i];
                if (instr.IsLdcI4() && instructions[i + 1].OpCode.Code == CilCode.Mul)
                {
                    return((uint)instr.GetLdcI4Constant());
                }
            }

            context.Logger.Warning(Tag, "Could not locate scalar value, using default!");
            return(7);
        }
Ejemplo n.º 27
0
        public void Run(DevirtualisationContext context)
        {
            var flagHelper = VmHelperGenerator.ImportFlagHelper(context.TargetModule, context.Constants);

            foreach (var method in context.VirtualisedMethods)
            {
                try
                {
                    if (method.ILCompilationUnit == null ||
                        method.IsExport && !context.Options.SelectedExports.Contains(method.ExportId.Value))
                    {
                        continue;
                    }

                    RecompileToCilAst(context, method);
                    GenerateCil(context, method, flagHelper);
                }
                catch (Exception e) when(context.Options.EnableSalvageMode)
                {
                    context.Logger.Error(Tag, $"Failed to recompile function_{method.Function.EntrypointAddress:X4}. {e.Message}");
                }
            }
        }
Ejemplo n.º 28
0
        private static IList <OpCodeInterfaceInfo> LocateOpCodeInterfaces(DevirtualisationContext context)
        {
            var result = new List <OpCodeInterfaceInfo>();

            foreach (var type in context.RuntimeImage.Assembly.Modules[0].TopLevelTypes.Where(x => x.IsInterface && x.Methods.Count == 2))
            {
                MethodDefinition getter = null;
                MethodDefinition run    = null;

                foreach (var method in type.Methods)
                {
                    var signature = method.Signature;

                    if (signature.Parameters.Count == 0 && signature.ReturnType.IsTypeOf("System", "Byte"))
                    {
                        // Matched signature byte get_Code():
                        getter = method;
                    }
                    else if (signature.Parameters.Count == 2 &&
                             (method.Parameters.FirstOrDefault(x => x.Sequence == 2)?.Attributes
                              .HasFlag(ParameterAttributes.Out) ?? false) &&
                             signature.ReturnType.IsTypeOf("System", "Void"))
                    {
                        // Matched signature of void Run(VMContext, out ExecutionStage).
                        run = method;
                    }
                }

                if (getter != null && run != null)
                {
                    result.Add(new OpCodeInterfaceInfo(type, getter, run));
                }
            }

            return(result);
        }
Ejemplo n.º 29
0
        private bool TryExtractExportTypeFromMethodBody(DevirtualisationContext context, CilMethodBody methodBody, out int exportId)
        {
            exportId = 0;

            var instructions = methodBody.Instructions;
            var runCall      = instructions.FirstOrDefault(x =>
                                                           x.OpCode.Code == CilCode.Call &&
                                                           x.Operand is IMethodDefOrRef methodOperand &&
                                                           (Comparer.Equals(context.VMEntryInfo.RunMethod1, methodOperand) ||
                                                            Comparer.Equals(context.VMEntryInfo.RunMethod2, methodOperand)
                                                           ));

            if (runCall != null)
            {
                // Do a very minimal emulation of the method body, we are only interested in ldc.i4 values that push
                // the export ID. All other values on the stack will have a placeholder of -1.

                // Note that this strategy only works for variants of KoiVM that have exactly one constant integer
                // pushed on the stack upon calling the run method. It does NOT detect the export ID when the constant
                // is masked behind some obfuscation, or when there are multiple integer parameters pushed on the stack.

                var stack = new Stack <int>();
                foreach (var instr in instructions)
                {
                    if (instr.Offset == runCall.Offset)
                    {
                        // We reached the call to the run method, obtain the integer value corresponding to the export id.

                        int argCount = instr.GetStackPopCount(methodBody);
                        for (int i = 0; i < argCount; i++)
                        {
                            int value = stack.Pop();
                            if (value != -1)
                            {
                                exportId = value;
                                return(true);
                            }
                        }

                        return(false);
                    }

                    if (instr.IsLdcI4())
                    {
                        // Push the ldc.i4 value if we reach one.
                        stack.Push(instr.GetLdcI4Constant());
                    }
                    else
                    {
                        // Pop the correct amount of values from the stack, and push placeholders.
                        for (int i = 0; i < instr.GetStackPopCount(methodBody); i++)
                        {
                            stack.Pop();
                        }
                        for (int i = 0; i < instr.GetStackPushCount(); i++)
                        {
                            stack.Push(-1);
                        }
                    }
                }
            }

            return(false);
        }
Ejemplo n.º 30
0
        private static void AddPhysicalMethod(DevirtualisationContext context, VirtualisedMethod method)
        {
            bool isHelperInit = method.ExportId == context.Constants.HelperInit;

            // Decide on a name of the new method.
            string name;

            if (!method.IsExport)
            {
                name = "__VMFUNCTION__" + method.Function.EntrypointAddress.ToString("X4");
            }
            else if (isHelperInit)
            {
                name = "__VMHELPER_INIT__";
            }
            else
            {
                name = "__VMEXPORT__" + method.ExportId;
            }

            // Create new physical method.
            var dummy = new MethodDefinition(name,
                                             MethodAttributes.Public,
                                             method.MethodSignature);

            dummy.IsStatic      = !method.MethodSignature.HasThis;
            dummy.CilMethodBody = new CilMethodBody(dummy);
            method.CallerMethod = dummy;

            // We perform a heuristic analysis on all private members that are accessed in the code, as these are only
            // able to be accessed if the caller is in the same type.

            var inferredDeclaringType = TryInferDeclaringTypeFromMemberAccesses(context, method);

            // If that fails (e.g. there are no private member accesses), we check if the method is an instance member.
            // If it is, the this parameter is always the declaring type, EXCEPT for one scenario:
            //
            // When the method is intra-linked and only referenced through a LDFTN function, the this parameter type
            // can be inaccurate. KoiVM does not care for the type of the this object, as everything is done through
            // reflection, so it reuses the method signature for things like instance event handlers (object, EventArgs),
            // even though the hidden this parameter might have had a different type.

            if (inferredDeclaringType == null && !dummy.IsStatic)
            {
                inferredDeclaringType = TryInferDeclaringTypeFromThisParameter(context, dummy);
            }

            if (inferredDeclaringType != null)
            {
                // We found a declaring type!
                context.Logger.Debug(Tag,
                                     $"Inferred declaring type of function_{method.Function.EntrypointAddress:X4} ({inferredDeclaringType}).");

                // Remove this parameter from the method signature if necessary.
                if (!dummy.IsStatic)
                {
                    dummy.Signature.Parameters.RemoveAt(0);
                }
            }
            else
            {
                // Fallback method: Add to <Module> and make static.
                context.Logger.Debug(Tag, isHelperInit
                    ? $"Adding HELPER_INIT to <Module>."
                    : $"Could not infer declaring type of function_{method.Function.EntrypointAddress:X4}. Adding to <Module> instead.");

                dummy.IsStatic = true;
                method.MethodSignature.HasThis = false;
                inferredDeclaringType          = context.TargetImage.Assembly.Modules[0].TopLevelTypes[0];
            }

            inferredDeclaringType.Methods.Add(dummy);
        }