private void WriteCallInstruction(HtmlWriter writer, Instruction inst, ulong r11) { // we should be able to find the call targets for R11/relative immediate ulong callTarget = GetBranchTarget(inst, r11); writer.Write(" ; "); if (callTarget != 0) { var target = Mono.GetJitInfo((IntPtr)callTarget); if (target.Method != null) { if (target.Method.IsConstructor) { WriteCtorPrefix(writer, target.Method); writer.Write(" "); WriteCtorDeclaration(writer, target.Method as ConstructorInfo); } else if (target.Method != null) { WriteMethodPrefix(writer, target.Method); writer.Write(" "); WriteMethodReturnType(writer, target.Method as MethodInfo); writer.Write(" "); if (target.Method.DeclaringType != null) { TypeLink(writer, target.Method.DeclaringType); writer.Write("."); } WriteMethodDeclaration(writer, target.Method as MethodInfo); } } else { writer.Write("unknown target @ " + callTarget.ToString("X16")); } } else { writer.Write("unsupported call, probably virtual"); } }
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"); } } } } }
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; }