Example #1
0
        private void InspectType(HtmlWriter writer, Assembly assembly, Type type)
        {
            var asm = assembly;

            using (writer.Tag("small")) {
                AssemblyLink(writer, asm);
                writer.Write(" | ");
                NamespaceLink(writer, asm.FindNamespace(type.Namespace), type.Namespace ?? "<root>");
                // see whether this is a nested type
                if (type.DeclaringType != null)
                {
                    writer.Write(" | ");
                    TypeLink(writer, type.DeclaringType);
                }
            }
            var attr = type.GetCustomAttributes(true);

            if (attr.Length > 0)
            {
                writer.Break();
                writer.Break();
                WriteAttributes(writer, attr);
            }

            using (writer.Tag("h2")) {
                writer.Write(type.PrettyName(TypeExt.NameMode.WithNamespace));
                if (type.IsGenericType)
                {
                    WriteGenericArguments(writer, type.GetGenericArguments(), TypeExt.NameMode.WithNamespace);
                }
            }

            if (type.IsGenericType)
            {
                TypeLink(writer, type.GetGenericTypeDefinition(), "go to generic definition " + type.Name);
                writer.Break();
            }
            WriteTypeDeclaration(writer, type);
            if (type.IsClass || type.IsValueType)
            {
                InspectClass(writer, type);
            }
            else if (type.IsInterface)
            {
                InspectInterface(writer, type);
            }
            else if (type.IsEnum)
            {
                InspectEnum(writer, type);
            }
        }
Example #2
0
        private void InspectCtor(HtmlWriter writer, Assembly assembly, Type type, ConstructorInfo ctor)
        {
            LayoutMethodHeader(writer, assembly, type, ctor);
            using (writer.Tag("code"))
                using (writer.Tag("h5"))
                {
                    WriteCtorPrefix(writer, ctor);
                    writer.Write(" ");
                    WriteCtorDeclaration(writer, ctor);
                }

            using (writer.ContainerFluid())
                WriteDissassembly(writer, ctor);
        }
Example #3
0
 private void MakeTable <T>(HtmlWriter writer, IEnumerable <T> ts, params Action <T>[] inner)
 {
     using (writer.Tag("table")) {
         foreach (var t in ts)
         {
             using (writer.Tag("tr")) {
                 foreach (var cell in inner)
                 {
                     using (writer.Tag("td"))
                         cell(t);
                 }
             }
         }
     }
 }
Example #4
0
        private void InspectMethod(HtmlWriter writer, Assembly assembly, Type type, MethodInfo method)
        {
            LayoutMethodHeader(writer, assembly, type, method);
            using (writer.Tag("code"))
                using (writer.Tag("h5"))
                {
                    WriteMethodPrefix(writer, method);
                    writer.Write(" ");
                    WriteMethodReturnType(writer, method);
                    writer.Write(" ");
                    WriteMethodDeclaration(writer, method);
                }

            using (writer.ContainerFluid())
                WriteDissassembly(writer, method);
        }
Example #5
0
        private void InspectNamespace(HtmlWriter writer, Namespace ns)
        {
            using (writer.Tag("small"))
            {
                DomainLink(writer);
                writer.Write(" | ");
                AssemblyLink(writer, ns.Assembly);
            }

            using (writer.ContainerFluid())
            {
                using (writer.Tag("code"))
                    using (writer.Tag("h5"))
                    {
                        writer.Write("namespace ");
                        NamespaceLink(writer, ns, ns.PrettyName);
                    }

                WriteNamespaceMembers(writer, ns);
            }
        }
 private void InspectNamespace(HtmlWriter writer, Namespace ns)
 {
     using (writer.Tag("small")) {
         AssemblyLink(writer, ns.Assembly);
     }
     writer.Inline("h2", ns.FullName);
     MakeTable(
         writer,
         ns.Types,
         t => WriteShortTypeDeclaration(writer, t)
         );
 }
Example #7
0
        private void InspectAssembly(HtmlWriter writer, Assembly asm)
        {
            using (writer.Tag("small")) {
                DomainLink(writer);
            }
            writer.Inline("h5", asm.Name);

            using (writer.ContainerFluid())
            {
                var namespaces = asm.Namespaces.Where(s => s.Name.Length > 0).ToList();
                if (namespaces.Count > 0)
                {
                    using (writer.ContainerFluid())
                        using (writer.Tag("code"))
                        {
                            writer.Inline("h6", "// namespaces");
                            MakeCodeList(
                                writer,
                                namespaces.OrderBy(n => n.Name),
                                n =>
                            {
                                writer.Write("namespace ");
                                NamespaceLink(writer, n, n.RelativeName);
                            });
                        }
                }

                var unnamed = asm.FindNamespace("");
                if (unnamed != null)
                {
                    using (writer.ContainerFluid())
                        using (writer.Tag("code"))
                        {
                            writer.Inline("h6", "// Root namespace");
                            WriteNamespaceMembers(writer, unnamed);
                        }
                }
            }
        }
Example #8
0
        private void LayoutMethodHeader(HtmlWriter writer, Assembly asm, Type type, MethodBase method)
        {
            using (writer.Tag("small")) {
                AssemblyLink(writer, asm);
                writer.Write(" | ");
                NamespaceLink(writer, asm.FindNamespace(type.Namespace), type.Namespace ?? "<root>");
                writer.Write(" | ");
                TypeLink(writer, type);
            }
            var attr = method.GetCustomAttributes(true);

            if (attr.Length > 0)
            {
                writer.Break();
                WriteAttributes(writer, attr);
            }
        }
Example #9
0
 void WriteNamespaceMembers(HtmlWriter writer, Namespace ns)
 {
     using (writer.Tag("code"))
     {
         foreach (var group in ns.Types.Where(t => t.DeclaringType == null).GroupBy(t => TypeKinds.Classify(t)).OrderBy(group => group.Key))
         {
             using (writer.ContainerFluid())
             {
                 writer.Inline("h6", "// " + group.Key.KindName());
                 MakeCodeList(
                     writer,
                     group.OrderBy(t => t.Name),
                     t => WriteInlineAttributes(writer, t.GetCustomAttributes(false)),
                     t => WriteShortTypeDeclaration(writer, t)
                     );
             }
         }
     }
 }
        private void InspectAssembly(HtmlWriter writer, Assembly asm)
        {
            using (writer.Tag("small")) {
                writer.AHref("Domain", _completePrefix);
            }
            writer.Inline("h2", "Assembly: " + asm.Name);
            writer.Inline("h4", "Namespaces");
            MakeTable(
                writer,
                asm.Namespaces,
                n => NamespaceLink(writer, n, n.RelativeName)
                );
            var unnamed = asm.FindNamespace("");

            if (unnamed != null)
            {
                writer.Inline("h4", "Types");
                InspectNamespace(writer, unnamed);
            }
        }
Example #11
0
        void InspectDomain(HtmlWriter writer)
        {
            writer.Inline("h5", AppDomain.CurrentDomain.FriendlyName);
            var assemblies = m_Explorer.Assemblies.ToArray();

            Array.Sort(assemblies, (lhs, rhs) => lhs.FullName.CompareTo(rhs.FullName));
            if (assemblies.Length > 0)
            {
                using (writer.ContainerFluid())
                    using (writer.Tag("code"))
                    {
                        writer.Inline("h6", "// assemblies");
                        MakeCodeList(
                            writer,
                            assemblies,
                            a => AssemblyLink(writer, a),
                            a => writer.Write("    // " + a.FullName)
                            );
                    }
            }
        }
Example #12
0
        private static void WriteStyle(HtmlWriter writer)
        {
            using (writer.Tag("style")) {
                writer.Write(
                    @"table {
    font-family: arial, sans-serif;
    border-collapse: collapse;
    width: 100%;
}

td, th {
    border: 1px solid #dddddd;
    text-align: left;
    padding: 8px;
}

tr:nth-child(even) {
    background-color: #dddddd;
}"
                    );
            }
        }
 public static HtmlWriter.TagHandle ContainerFluid(this HtmlWriter writer)
 {
     return(writer.Tag("div", "class", "container-fluid"));
 }
        void ExecuteLookup(HtmlWriter writer, HttpListenerRequest request)
        {
            const string addresses = nameof(addresses);

            using (writer.Tag("form", "action", CommandUrl("lookup"), "method", "post"))
                using (writer.ContainerFluid())
                    using (writer.Tag("div", "class", "form-group"))
                    {
                        writer.Inline("h2", "Paste addresses to look up (hex)");
                        writer.Tag("textarea", "class", "form-control", "name", addresses, "rows", "10").Dispose();
                        writer.Break();
                        writer.InlineTag("input", "type", "submit", "value", "Submit");
                    }

            NameValueCollection postValues;

            using (StreamReader reader = new StreamReader(request.InputStream, request.ContentEncoding))
                postValues = System.Web.HttpUtility.ParseQueryString(reader.ReadToEnd());
            if (postValues[addresses] != null)
            {
                var modules = Process.GetCurrentProcess().Modules;
                string FindModule(long address)
                {
                    for (int i = 0; i < modules.Count; i++)
                    {
                        var  m           = modules[i];
                        long baseAddress = m.BaseAddress.ToInt64();
                        if (baseAddress <= address && address < baseAddress + m.ModuleMemorySize)
                        {
                            return(modules[i].ModuleName);
                        }
                    }
                    return("unknown module");
                }

                using (writer.ContainerFluid())
                {
                    writer.Inline("h4", "Results");
                    var lines = postValues[addresses].Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
                    using (writer.Tag("textarea", "class", "form-control", "rows", "10"))
                    {
                        foreach (var line in lines)
                        {
                            writer.Write(line);
                            writer.Write(", ");
                            int start = line.IndexOf("0x");
                            if (start >= 0)
                            {
                                int end = line.IndexOf(',', start);
                                if (end < 0)
                                {
                                    end = line.Length;
                                }
                                var numberString = line.Substring(start + 2, end - start - 2);
                                if (long.TryParse(numberString, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out long address))
                                {
                                    var jitInfo = Mono.GetJitInfo(new IntPtr(address));
                                    if (jitInfo.Method == null)
                                    {
                                        writer.Write("unknown method");
                                    }
                                    else if (jitInfo.Method is MethodInfo m)
                                    {
                                        WriteTypeName(writer, m.DeclaringType, true);
                                        writer.Write(".");
                                        WriteMethodDeclaration(writer, m, true);
                                    }
                                    else if (jitInfo.Method is ConstructorInfo c)
                                    {
                                        WriteCtorDeclaration(writer, c, true);
                                    }
                                    writer.Write(", ");
                                    writer.Write(FindModule(address));
                                }
                                else
                                {
                                    writer.Write("failed to parse " + numberString + ",");
                                }
                            }
                            else
                            {
                                writer.Write("failed to parse,");
                            }


                            writer.Write("\n");
                        }
                    }
                }
            }
        }
Example #15
0
        private void WriteDissassembly(HtmlWriter writer, MethodBase method)
        {
            if (method.IsAbstract || method.ContainsGenericParameters)
            {
                writer.Write("Cannot display disassembly for generic or abstract methods.");
                return;
            }
            var jitInfo = Mono.GetJitInfo(method);

            writer.Write("Address: ");
            writer.Write(jitInfo.CodeStart.ToString("X16"));
            writer.Break();
            writer.Write("Code Size in Bytes: ");
            writer.Write(jitInfo.CodeSize.ToString());
            writer.Break();
            if (jitInfo.CodeSize <= 0)
            {
                return;
            }
            using (writer.Tag("pre")) {
                using (writer.Tag("code")) {
                    // some special help for calls using R11 and nops
                    int         nops            = 0;
                    Instruction lastInstruction = null;
                    ulong       r11Register     = 0;
                    lock (_disassemblerLock) {
                        foreach (var inst in GetInstructions(jitInfo))
                        {
                            // abbreviate excessive nopping
                            if (inst.Mnemonic == ud_mnemonic_code.UD_Inop)
                            {
                                nops++;
                                lastInstruction = inst;
                                continue;
                            }
                            if (nops > 0)
                            {
                                if (nops == 1)
                                {
                                    writer.Write(lastInstruction.ToString());
                                }
                                else
                                {
                                    writer.Write("nop (");
                                    writer.Write(nops.ToString());
                                    writer.Write(" bytes)");
                                }
                                nops = 0;
                                writer.Write("\n");
                            }
                            lastInstruction = inst;
                            using (writer.Tag("span").With("id", "X" + Address(inst).ToString("X16"))) {
                                writer.Write(inst.ToString());

                                if (inst.Mnemonic == ud_mnemonic_code.UD_Imov)
                                {
                                    var op0 = inst.Operands[0];
                                    // call targets on x64 are frequently placed in R11, so let's ensure that we catch that.
                                    if (op0.Type == ud_type.UD_OP_REG && op0.Base == ud_type.UD_R_R11)
                                    {
                                        r11Register = inst.Operands[1].LvalUQWord;
                                    }
                                }
                                else if (inst.Mnemonic == ud_mnemonic_code.UD_Icall)
                                {
                                    WriteCallInstruction(writer, inst, r11Register);
                                    r11Register = 0;
                                }
                                else if (IsJump(inst.Mnemonic))
                                {
                                    WriteJumpInstruction(writer, inst, r11Register);
                                }
                            }
                            writer.Write("\n");
                        }
                    }
                }
            }
        }
        void HandleRequest(HttpListenerContext ctxt)
        {
            // decode command
            string url          = ctxt.Request.RawUrl;
            int    commandStart = url.IndexOf('/', 1) + 1;
            string command      = null;

            if (commandStart != -1)
            {
                int commandEnd = url.IndexOf('?', commandStart);
                if (commandEnd == -1)
                {
                    commandEnd = url.Length;
                }

                command = url.Substring(commandStart, commandEnd - commandStart);
            }

            if (string.IsNullOrEmpty(command))
            {
                command = "inspect";
            }

            using (MemoryStream stream = new MemoryStream())
            {
                var sw = new StreamWriter(stream, new UTF8Encoding(false));
                sw.WriteLine("<!doctype html>");
                var writer = new HtmlWriter(sw);
                using (writer.Tag("html", "lang", "en"))
                {
                    using (writer.Tag("head"))
                    {
                        WriteHeaderContent(sw, "ASM Explorer");
                    }

                    using (writer.Tag("body"))
                        using (writer.ContainerFluid())
                        {
                            switch (command)
                            {
                            case "inspect":
                            {
                                ExecuteInspect(writer, ctxt.Request.QueryString);
                                break;
                            }

                            case "lookup":
                            {
                                ExecuteLookup(writer, ctxt.Request);
                                break;
                            }

                            default:
                            {
                                writer.Write($"Invalid command \"{command}\" in {url}");
                                break;
                            }
                            }
                        }

                    sw.WriteLine();
                }

                sw.Flush();
                ctxt.Response.ContentLength64 = stream.Length;
                stream.Position = 0;
                stream.WriteTo(ctxt.Response.OutputStream);
            }

            ctxt.Response.OutputStream.Close();
        }
        void WriteAttributes(HtmlWriter writer, object[] attributes, bool stacked = true)
        {
            using (writer.Tag("code"))
            {
                if (attributes.Length == 0)
                {
                    return;
                }
                if (!stacked)
                {
                    writer.Write("[");
                }
                for (int i = 0; i < attributes.Length; i++)
                {
                    if (stacked)
                    {
                        writer.Write("[");
                    }
                    else if (i > 0)
                    {
                        writer.Write(", ");
                    }
                    var type = attributes[i].GetType();
                    TypeLink(writer, type);
                    var properties = type.GetProperties(k_AllInstanceBindings);
                    if (properties.Length > 0)
                    {
                        bool first = true;
                        for (int j = 0; j < properties.Length; j++)
                        {
                            var prop = properties[j];
                            if (!prop.CanRead || prop.GetIndexParameters().Length > 0 || prop.Name == "TypeId")
                            {
                                continue;
                            }
                            if (!first)
                            {
                                writer.Write(", ");
                            }
                            else
                            {
                                writer.Write("(");
                            }
                            first = false;
                            writer.Write(prop.Name);
                            writer.Write(" = ");
                            var value = prop.GetValue(attributes[i], null);
                            if (value == null)
                            {
                                writer.Write("null");
                            }
                            else if (value is string)
                            {
                                writer.Write("\"");
                                writer.Write((value as string));
                                writer.Write("\"");
                            }
                            else
                            {
                                writer.Write(value.ToString());
                            }
                        }

                        if (!first)
                        {
                            writer.Write(")");
                        }
                    }

                    if (stacked)
                    {
                        writer.Write("]");
                        writer.Break();
                    }
                }

                if (!stacked)
                {
                    writer.Write("]");
                }
            }
        }
Example #18
0
        private void HandleRequest(HttpListenerContext ctxt)
        {
            var settings = new XmlWriterSettings {
                // this is crucial to get rid of the BOM header, see https://stackoverflow.com/questions/1755958/how-can-i-remove-the-bom-from-xmltextwriter-using-c
                Encoding           = new System.Text.UTF8Encoding(false),
                OmitXmlDeclaration = true,
                ConformanceLevel   = ConformanceLevel.Fragment,
                CloseOutput        = false,
            };

            // decode command
            string url          = ctxt.Request.RawUrl;
            int    commandStart = url.IndexOf('/', 1) + 1;
            string command;

            if (commandStart != -1)
            {
                int commandEnd = url.IndexOf('?', commandStart);
                if (commandEnd == -1)
                {
                    commandEnd = url.Length;
                }
                command = url.Substring(commandStart, commandEnd - commandStart);
            }
            else
            {
                command = "inspect";
            }

            using (MemoryStream stream = new MemoryStream()) {
                using (var writer = XmlWriter.Create(stream, settings)) {
                    var htmlWriter = new HtmlWriter(writer);
                    try {
                        writer.WriteStartElement("html");
                        writer.WriteStartElement("head");
                        WriteStyle(htmlWriter);
                        writer.WriteEndElement();
                        writer.WriteStartElement("body");
switchStart:
                        switch (command)
                        {
                        case "inspect":
                            ExecuteInspect(htmlWriter, ctxt.Request.QueryString);
                            break;

                        default:
                            command = "inspect";
                            goto switchStart;
                        }
                        writer.WriteEndElement();
                        writer.WriteEndElement();
                    } catch (Exception ex) {
                        htmlWriter.Write(ex.Message);
                        htmlWriter.Break();
                        using (htmlWriter.Tag("pre"))
                            htmlWriter.Write(ex.StackTrace);
                        writer.WriteFullEndElement();
                    }
                }
                stream.Flush();
                ctxt.Response.ContentLength64 = stream.Length;
                stream.Position = 0;
                stream.WriteTo(ctxt.Response.OutputStream);
            }
            ctxt.Response.OutputStream.Close();
        }
        private void InspectType(HtmlWriter writer, Assembly assembly, Type type)
        {
            var asm = assembly;
            var ns  = asm.FindNamespace(type.Namespace);

            using (writer.Tag("small"))
            {
                DomainLink(writer);
                writer.Write(" | ");
                AssemblyLink(writer, asm);
                writer.Write(" | ");
                NamespaceLink(writer, ns, type.Namespace ?? "<root>");

                // see whether this is a nested type
                if (type.DeclaringType != null)
                {
                    writer.Write(" | ");
                    TypeLink(writer, type.DeclaringType);
                }
            }

            using (writer.ContainerFluid())
                using (writer.Tag("code"))
                {
                    writer.Write("namespace ");
                    NamespaceLink(writer, ns, string.IsNullOrEmpty(ns.FullName) ? "<root>" : ns.FullName);

                    HtmlWriter.TagHandle outerClass = default;
                    if (type.DeclaringType != null)
                    {
                        outerClass = writer.ContainerFluid();
                        WriteTypeDeclaration(writer, type.DeclaringType);
                    }

                    using (writer.ContainerFluid())
                    {
                        var attr = type.GetCustomAttributes(false);
                        if (attr.Length > 0)
                        {
                            WriteAttributes(writer, attr);
                        }

                        using (writer.Tag("h5"))
                            WriteTypeDeclaration(writer, type);

                        if (type.IsGenericType && type.GenericTypeArguments.Length != 0)
                        {
                            writer.Break();
                            writer.Write("// instance of generic definition ");
                            TypeLink(writer, type.GetGenericTypeDefinition(), type.Name);
                        }

                        if (type.IsClass || type.IsValueType)
                        {
                            InspectClass(writer, type);
                        }
                        else if (type.IsInterface)
                        {
                            InspectInterface(writer, type);
                        }
                        else if (type.IsEnum)
                        {
                            InspectEnum(writer, type);
                        }
                    }
                    outerClass.Dispose();
                }
        }
Example #20
0
        private void WriteDissassembly(HtmlWriter writer, MethodBase method)
        {
            if (method.IsAbstract || method.ContainsGenericParameters)
            {
                writer.Write("Cannot display disassembly for generic or abstract methods.");
                return;
            }

            var context = new MethodContext()
            {
                DebugEnabled = MonoDebug.IsEnabled
            };

            var jitInfo = Mono.GetJitInfo(method);

            using (writer.ContainerFluid())
            {
                writer.Write("Address: ");
                writer.Write(jitInfo.CodeStart.ToString("X16"));
                writer.Break();
                writer.Write("Code Size in Bytes: ");
                writer.Write(jitInfo.CodeSize.ToString());
                writer.Break();
                writer.Write("Debug mode: ");
                writer.Write(context.DebugEnabled ? "enabled" : "disabled");
                writer.Break();
            }

            if (jitInfo.CodeSize <= 0)
            {
                return;
            }

            using (writer.Tag("pre"))
            {
                using (writer.Tag("code"))
                {
                    // some special help for calls using R11 and nops
                    int  nops  = 0;
                    bool first = true;
                    lock (_disassemblerLock)
                    {
                        foreach (var inst in GetInstructions(jitInfo))
                        {
                            context.HasLineNote = false;

                            if (first)
                            {
                                context.HasBasePointer =
                                    inst.Mnemonic == ud_mnemonic_code.UD_Ipush &&
                                    inst.Operands[0].Type == ud_type.UD_OP_REG &&
                                    inst.Operands[0].Base == ud_type.UD_R_RBP;
                                first = false;
                            }

                            // abbreviate excessive nopping
                            if (inst.Mnemonic == ud_mnemonic_code.UD_Inop)
                            {
                                nops++;
                                context.LastInstruction = inst;
                                continue;
                            }

                            if (nops > 0)
                            {
                                if (nops == 1)
                                {
                                    writer.Write(context.LastInstruction.ToString());
                                }
                                else
                                {
                                    var str = context.LastInstruction.ToString();
                                    writer.Write(str);
                                    context.LineLength = str.Length;
                                    StartNote(writer, ref context);

                                    writer.Write("repeated nops (");
                                    writer.Write(nops.ToString());
                                    writer.Write(" bytes)");
                                }

                                nops = 0;
                                context.HasLineNote = false;
                                writer.Write("\n");
                            }

                            using (writer.Tag("span", "id", "X" + Address(inst).ToString("X16")))
                            {
                                var str = inst.ToString();
                                context.LineLength = str.Length;
                                writer.Write(str);

                                if (inst.Mnemonic == ud_mnemonic_code.UD_Imov)
                                {
                                    var op0 = inst.Operands[0];
                                    var op1 = inst.Operands[1];

                                    // call targets on x64 are frequently placed in R11, so let's ensure that we catch that.
                                    if (IsR11(op0))
                                    {
                                        context.R11 = op1;
                                    }
                                    if (context.DebugEnabled)
                                    {
                                        if (IsLocalStore(inst) && IsR11(op1) && context.R11 != null && context.R11.Type == ud_type.UD_OP_IMM)
                                        {
                                            if (context.BreakpointTrampolineOffset == 0)
                                            {
                                                context.BreakpointTrampolineOffset = op0.Value;
                                                StartNote(writer, ref context);
                                                writer.Write("write breakpoint trampoline");
                                            }
                                            else if (context.SinglestepTrampolineOffset == 0)
                                            {
                                                context.SinglestepTrampolineOffset = op0.Value;
                                                StartNote(writer, ref context);
                                                writer.Write("write singlestep trampoline");
                                            }
                                        }
                                        else if (IsReadSinglestepTrampoline(inst))
                                        {
                                            StartNote(writer, ref context);
                                            writer.Write("read singlestep trampoline");
                                        }
                                        else if (IsReadBreakpointTrampoline(inst))
                                        {
                                            StartNote(writer, ref context);
                                            writer.Write("read breakpoint trampoline");
                                        }
                                    }
                                }
                                else if (inst.Mnemonic == ud_mnemonic_code.UD_Iadd)
                                {
                                    var op1 = inst.Operands[1];
                                    if (op1.Type == ud_type.UD_OP_IMM)
                                    {
                                        StartNote(writer, ref context);
                                        writer.Write(op1.Value.ToString());
                                    }
                                }
                                else if (inst.Mnemonic == ud_mnemonic_code.UD_Icall)
                                {
                                    WriteCallInstruction(writer, inst, ref context);
                                    context.R11 = null;
                                }
                                else if (IsJump(inst.Mnemonic))
                                {
                                    WriteJumpInstruction(writer, inst, ref context);
                                }
                            }

                            writer.Write("\n");

                            context.LastInstruction = inst;
                        }
                    }
                }
            }

            bool IsR11(Operand op) => op.Type == ud_type.UD_OP_REG && op.Base == ud_type.UD_R_R11;
            bool IsReadSinglestepTrampoline(Instruction inst) => IsLocalLoadOffset(inst, context.SinglestepTrampolineOffset);
            bool IsReadBreakpointTrampoline(Instruction inst) => IsLocalLoadOffset(inst, context.BreakpointTrampolineOffset);
            ud_type StackFrameRegister() => context.HasBasePointer ? ud_type.UD_R_RBP : ud_type.UD_R_RSP;

            bool IsLocalInteraction(Instruction inst, int operand)
            {
                if (inst.Mnemonic != ud_mnemonic_code.UD_Imov)
                {
                    return(false);
                }
                var op = inst.Operands[operand];

                return(op.Type == ud_type.UD_OP_MEM && op.Base == StackFrameRegister());
            }

            bool IsLocalStore(Instruction inst) => IsLocalInteraction(inst, 0);
            bool IsLocalLoad(Instruction inst) => IsLocalInteraction(inst, 1);
            bool IsLocalLoadOffset(Instruction inst, long offset) => IsLocalLoad(inst) && inst.Operands[1].Value == offset;
        }