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 LayoutInstanceProperties(HtmlWriter writer, Type type) { var instanceProperties = type.GetProperties(k_AllInstanceBindings); if (instanceProperties.Length == 0) { return; } Array.Sort(instanceProperties, (lhs, rhs) => ReflectionHelper.CompareProperties(type, lhs, rhs)); int split = instanceProperties.Length; for (int i = 0; i < instanceProperties.Length; i++) { if (instanceProperties[i].DeclaringType != type) { split = i; break; } } if (split > 0) { using (writer.ContainerFluid()) { writer.Inline("h6", "// Instance properties"); LayoutProperties(writer, ArrayView(instanceProperties, 0, split)); } } if (split < instanceProperties.Length) { using (writer.ContainerFluid()) { writer.Inline("h6", "// Inherited properties"); LayoutProperties(writer, ArrayView(instanceProperties, split)); } } }
private void LayoutInstanceMethods(HtmlWriter writer, Type type) { var instanceMethods = type.GetMethods(k_AllInstanceBindings); if (instanceMethods.Length == 0) { return; } Array.Sort(instanceMethods, (lhs, rhs) => ReflectionHelper.CompareMethods(type, lhs, rhs)); int split = instanceMethods.Length; for (int i = 0; i < instanceMethods.Length; i++) { if (instanceMethods[i].DeclaringType != type) { split = i; break; } } if (split > 0) { using (writer.ContainerFluid()) { writer.Inline("h6", "// Instance methods"); LayoutMethods(writer, ArrayView(instanceMethods, 0, split).Where(m => !m.IsSpecialName)); } } if (split < instanceMethods.Length) { using (writer.ContainerFluid()) { writer.Inline("h6", "// Inherited methods"); LayoutMethods(writer, ArrayView(instanceMethods, split).Where(m => !m.IsSpecialName)); } } }
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 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); }
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 LayoutNestedTypes(HtmlWriter writer, Type type) { var nested = type.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic); if (nested.Length == 0) { return; } using (writer.ContainerFluid()) { writer.Inline("h6", "// Nested types"); MakeCodeList( writer, nested, t => WriteInlineAttributes(writer, t.GetCustomAttributes(false)), t => WriteTypeDeclaration(writer, t) ); } }
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 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); } }
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"); } } } } }
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(); }
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; }
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 InspectClass(HtmlWriter writer, Type type) { var instanceCtors = type.GetConstructors(k_AllInstanceBindings); Array.Sort(instanceCtors, (lhs, rhs) => ReflectionHelper.CompareConstructors(type, lhs, rhs)); if (instanceCtors.Length > 0) { using (writer.ContainerFluid()) { writer.Inline("h6", "// Constructors"); LayoutCtors(writer, instanceCtors); } } var staticCtor = type.GetConstructors(k_AllStaticBindings); if (staticCtor.Length > 0) { Array.Sort(staticCtor, (lhs, rhs) => ReflectionHelper.CompareConstructors(type, lhs, rhs)); using (writer.ContainerFluid()) { writer.Inline("h6", "// Static constructors"); LayoutCtors(writer, staticCtor); } } LayoutInstanceFields(writer, type); var staticFields = type.GetFields(k_AllStaticBindings); if (staticFields.Length > 0) { Array.Sort(staticFields, (lhs, rhs) => ReflectionHelper.CompareFields(type, lhs, rhs)); using (writer.ContainerFluid()) { writer.Inline("h6", "// Static fields"); LayoutStaticFields(writer, staticFields); } } LayoutInstanceProperties(writer, type); var staticProperties = type.GetProperties(k_AllStaticBindings); if (staticProperties.Length > 0) { Array.Sort(staticProperties, (lhs, rhs) => ReflectionHelper.CompareProperties(type, lhs, rhs)); using (writer.ContainerFluid()) { writer.Inline("h6", "Static properties"); LayoutProperties(writer, staticProperties); } } LayoutInstanceMethods(writer, type); var staticMethods = type.GetMethods(k_AllStaticBindings); if (staticMethods.Length > 0) { Array.Sort(staticMethods, (lhs, rhs) => ReflectionHelper.CompareMethods(type, lhs, rhs)); using (writer.ContainerFluid()) { writer.Inline("h6", "// Static functions"); LayoutMethods(writer, staticMethods.Where(m => !m.IsSpecialName)); } } LayoutNestedTypes(writer, type); }