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); } }
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); }
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); } } } } }
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); }
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) ); }
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); } } } }
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); } }
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); } }
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) ); } } }
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"); } } } } }
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("]"); } } }
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(); } }
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; }