/// <summary> /// Parse a single instruction and return the RVA of the next instruction. /// </summary> /// <param name="rtf">Runtime function to parse</param> /// <param name="imageOffset">Offset within the PE image byte array</param> /// <param name="rtfOffset">Instruction offset within the runtime function</param> /// <param name="instruction">Output text representation of the instruction</param> /// <returns>Instruction size in bytes - i.o.w. the next instruction starts at rtfOffset + (the return value)</returns> public int GetInstruction(RuntimeFunction rtf, int imageOffset, int rtfOffset, out string instruction) { if (_disasm == IntPtr.Zero) { instruction = ""; return(rtf.Size); } int instrSize = CoreDisTools.GetInstruction(_disasm, rtf, imageOffset, rtfOffset, _reader.Image, out instruction); instruction = instruction.Replace('\t', ' '); switch (_reader.Machine) { case Machine.Amd64: case Machine.IA64: ProbeX64Quirks(rtf, imageOffset, rtfOffset, instrSize, ref instruction); break; case Machine.I386: break; case Machine.ArmThumb2: case Machine.Thumb: break; case Machine.Arm64: break; default: throw new NotImplementedException(); } return(instrSize); }
/// <summary> /// Store the R2R reader and construct the disassembler for the appropriate architecture. /// </summary> /// <param name="reader"></param> public Disassembler(ReadyToRunReader reader, DumpOptions options) { _reader = reader; _options = options; _disasm = CoreDisTools.GetDisasm(_reader.Machine); SetIndentations(); }
public int GetInstruction(RuntimeFunction rtf, int imageOffset, int rtfOffset, out string instruction) { int instrSize = CoreDisTools.GetInstruction(_disasm, rtf, imageOffset, rtfOffset, _image, out instruction); switch (_machine) { case Machine.Amd64: case Machine.IA64: ProbeX64Quirks(rtf, imageOffset, rtfOffset, instrSize, ref instruction); break; case Machine.I386: break; case Machine.ArmThumb2: case Machine.Thumb: break; case Machine.Arm64: break; default: throw new NotImplementedException(); } return(instrSize); }
/// <summary> /// Shut down the native disassembler interface. /// </summary> public void Dispose() { if (_disasm != IntPtr.Zero) { CoreDisTools.FinishDisasm(_disasm); } }
/// <summary> /// Dumps one runtime function. /// </summary> private void DumpRuntimeFunction(R2RReader r2r, RuntimeFunction rtf) { if (_disasm) { _writer.WriteLine($"Id: {rtf.Id}"); _writer.Write(CoreDisTools.GetCodeBlock(_disassembler, rtf.StartAddress, r2r.GetOffset(rtf.StartAddress), r2r.Image, rtf.Size)); } else { _writer.Write($"{rtf}"); } if (_raw) { _writer.WriteLine("Raw Bytes:"); DumpBytes(r2r, rtf.StartAddress, (uint)rtf.Size); } if (_unwind) { _writer.WriteLine("UnwindInfo:"); _writer.Write(rtf.UnwindInfo); if (_raw) { DumpBytes(r2r, rtf.UnwindRVA, (uint)rtf.UnwindInfo.Size); } } _writer.WriteLine(); }
/// <summary> /// Dumps disassembly and register liveness /// </summary> internal override void DumpDisasm(RuntimeFunction rtf, int imageOffset, XmlNode parentNode) { int rtfOffset = 0; int codeOffset = rtf.CodeOffset; while (rtfOffset < rtf.Size) { string instr; int instrSize = _disassembler.GetInstruction(rtf, imageOffset, rtfOffset, out instr); AddXMLNode("offset" + codeOffset, instr, parentNode, $"{codeOffset}"); if (rtf.Method.GcInfo != null && rtf.Method.GcInfo.Transitions.ContainsKey(codeOffset)) { foreach (BaseGcTransition transition in rtf.Method.GcInfo.Transitions[codeOffset]) { AddXMLNode("Transition", transition.ToString(), parentNode, $"{codeOffset}"); } } CoreDisTools.ClearOutputBuffer(); rtfOffset += instrSize; codeOffset += instrSize; } }
private int Run(string[] args) { ArgumentSyntax syntax = ParseCommandLine(args); if (_help) { _writer.WriteLine(syntax.GetHelpText()); return(0); } if (_inputFilenames.Count == 0) { throw new ArgumentException("Input filename must be specified (--in <file>)"); } // open output stream if (_outputFilename != null) { _writer = File.CreateText(_outputFilename); } else { _writer = Console.Out; } try { foreach (string filename in _inputFilenames) { R2RReader r2r = new R2RReader(filename); if (_disasm) { _disassembler = CoreDisTools.GetDisasm(r2r.Machine); } Dump(r2r); if (_disasm) { CoreDisTools.FinishDisasm(_disassembler); } } } catch (Exception e) { Console.WriteLine("Error: " + e.ToString()); return(1); } finally { // close output stream _writer.Close(); } return(0); }
/// <summary> /// Dumps one runtime function. /// </summary> private void DumpRuntimeFunction(R2RReader r2r, RuntimeFunction rtf) { if (_disasm) { _writer.WriteLine($"Id: {rtf.Id}"); CoreDisTools.DumpCodeBlock(_disassembler, rtf.StartAddress, r2r.GetOffset(rtf.StartAddress), r2r.Image, rtf.Size); } else { _writer.Write($"{rtf}"); } if (_raw) { DumpBytes(r2r, rtf.StartAddress, (uint)rtf.Size); } _writer.WriteLine(); }
/// <summary> /// Dumps disassembly and register liveness /// </summary> internal override void DumpDisasm(RuntimeFunction rtf, int imageOffset) { int indent = (_options.Naked ? 11 : 32); string indentString = new string(' ', indent); int rtfOffset = 0; int codeOffset = rtf.CodeOffset; while (rtfOffset < rtf.Size) { string instr; int instrSize = _disassembler.GetInstruction(rtf, imageOffset, rtfOffset, out instr); if (_r2r.Machine == Machine.Amd64 && ((ILCompiler.Reflection.ReadyToRun.Amd64.UnwindInfo)rtf.UnwindInfo).UnwindCodes.ContainsKey(codeOffset)) { List <ILCompiler.Reflection.ReadyToRun.Amd64.UnwindCode> codes = ((ILCompiler.Reflection.ReadyToRun.Amd64.UnwindInfo)rtf.UnwindInfo).UnwindCodes[codeOffset]; foreach (ILCompiler.Reflection.ReadyToRun.Amd64.UnwindCode code in codes) { _writer.Write($"{indentString}{code.UnwindOp} {code.OpInfoStr}"); if (code.NextFrameOffset != -1) { _writer.WriteLine($"{indentString}{code.NextFrameOffset}"); } _writer.WriteLine(); } } if (rtf.Method.GcInfo?.Transitions != null && rtf.Method.GcInfo.Transitions.ContainsKey(codeOffset)) { foreach (BaseGcTransition transition in rtf.Method.GcInfo.Transitions[codeOffset]) { _writer.WriteLine($"{indentString}{transition}"); } } /* According to https://msdn.microsoft.com/en-us/library/ck9asaa9.aspx and src/vm/gcinfodecoder.cpp * UnwindCode and GcTransition CodeOffsets are encoded with a -1 adjustment (that is, it's the offset of the start of the next instruction) */ _writer.Write(instr); CoreDisTools.ClearOutputBuffer(); rtfOffset += instrSize; codeOffset += instrSize; } }
internal unsafe override void DumpDisasm(IntPtr Disasm, RuntimeFunction rtf, int imageOffset, byte[] image, XmlNode parentNode = null) { int rtfOffset = 0; int codeOffset = rtf.CodeOffset; while (rtfOffset < rtf.Size) { string instr; int instrSize = CoreDisTools.GetInstruction(Disasm, rtf, imageOffset, rtfOffset, image, out instr); _writer.Write(instr); if (rtf.Method.GcInfo != null && rtf.Method.GcInfo.Transitions.ContainsKey(codeOffset)) { _writer.WriteLine($"\t\t\t\t{rtf.Method.GcInfo.Transitions[codeOffset].GetSlotState(rtf.Method.GcInfo.SlotTable)}"); } CoreDisTools.ClearOutputBuffer(); rtfOffset += instrSize; codeOffset += instrSize; } }
/// <summary> /// Dumps disassembly and register liveness /// </summary> internal override void DumpDisasm(RuntimeFunction rtf, int imageOffset, XmlNode parentNode = null) { int rtfOffset = 0; int codeOffset = rtf.CodeOffset; while (rtfOffset < rtf.Size) { string instr; int instrSize = _disassembler.GetInstruction(rtf, imageOffset, rtfOffset, out instr); _writer.Write(instr); if (_r2r.Machine == Machine.Amd64 && ((Amd64.UnwindInfo)rtf.UnwindInfo).UnwindCodes.ContainsKey(codeOffset)) { List <Amd64.UnwindCode> codes = ((Amd64.UnwindInfo)rtf.UnwindInfo).UnwindCodes[codeOffset]; foreach (Amd64.UnwindCode code in codes) { _writer.Write($"\t\t\t\t{code.UnwindOp} {code.OpInfoStr}"); if (code.NextFrameOffset != -1) { _writer.WriteLine($" - {code.NextFrameOffset}"); } _writer.WriteLine(); } } if (rtf.Method.GcInfo != null && rtf.Method.GcInfo.Transitions.ContainsKey(codeOffset)) { foreach (BaseGcTransition transition in rtf.Method.GcInfo.Transitions[codeOffset]) { _writer.WriteLine($"\t\t\t\t{transition.ToString()}"); } } CoreDisTools.ClearOutputBuffer(); rtfOffset += instrSize; codeOffset += instrSize; } }
internal unsafe override void DumpDisasm(IntPtr Disasm, RuntimeFunction rtf, int imageOffset, byte[] image, XmlNode parentNode) { int rtfOffset = 0; int codeOffset = rtf.CodeOffset; Dictionary <int, GcInfo.GcTransition> transitions = rtf.Method.GcInfo.Transitions; GcSlotTable slotTable = rtf.Method.GcInfo.SlotTable; while (rtfOffset < rtf.Size) { string instr; int instrSize = CoreDisTools.GetInstruction(Disasm, rtf, imageOffset, rtfOffset, image, out instr); AddXMLNode("offset" + codeOffset, instr, parentNode, $"{codeOffset}"); if (transitions.ContainsKey(codeOffset)) { AddXMLNode("Transition", transitions[codeOffset].GetSlotState(slotTable), parentNode, $"{codeOffset}"); } CoreDisTools.ClearOutputBuffer(); rtfOffset += instrSize; codeOffset += instrSize; } }
/// <summary> /// Dumps disassembly and register liveness /// </summary> internal override void DumpDisasm(RuntimeFunction rtf, int imageOffset, XmlNode parentNode = null) { int rtfOffset = 0; int codeOffset = rtf.CodeOffset; while (rtfOffset < rtf.Size) { string instr; int instrSize = _disassembler.GetInstruction(rtf, imageOffset, rtfOffset, out instr); _writer.Write(instr); if (rtf.Method.GcInfo != null && rtf.Method.GcInfo.Transitions.ContainsKey(codeOffset)) { foreach (BaseGcTransition transition in rtf.Method.GcInfo.Transitions[codeOffset]) { _writer.WriteLine($"\t\t\t\t{transition.ToString()}"); } } CoreDisTools.ClearOutputBuffer(); rtfOffset += instrSize; codeOffset += instrSize; } }
public Disassembler(byte[] image, Machine machine) { _disasm = CoreDisTools.GetDisasm(machine); _image = image; _machine = machine; }
/// <summary> /// Parse a single instruction and return the RVA of the next instruction. /// </summary> /// <param name="rtf">Runtime function to parse</param> /// <param name="imageOffset">Offset within the PE image byte array</param> /// <param name="rtfOffset">Instruction offset within the runtime function</param> /// <param name="instruction">Output text representation of the instruction</param> /// <returns>Instruction size in bytes - i.o.w. the next instruction starts at rtfOffset + (the return value)</returns> public int GetInstruction(RuntimeFunction rtf, int imageOffset, int rtfOffset, out string instruction) { if (_disasm == IntPtr.Zero) { instruction = ""; return(rtf.Size); } int instrSize = CoreDisTools.GetInstruction(_disasm, rtf, imageOffset, rtfOffset, _reader.Image, out instruction); CoreDisTools.ClearOutputBuffer(); instruction = instruction.Replace('\t', ' '); if (_options.Naked) { StringBuilder nakedInstruction = new StringBuilder(); foreach (string line in instruction.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)) { int colon = line.IndexOf(':'); if (colon >= 0) { colon += 2; while (colon + 3 <= line.Length && IsXDigit(line[colon]) && IsXDigit(line[colon + 1]) && line[colon + 2] == ' ') { colon += 3; } if (!_options.HideOffsets) { nakedInstruction.Append($"{(rtfOffset + rtf.CodeOffset),8:x4}:"); nakedInstruction.Append(" "); } else { nakedInstruction.Append(" "); } nakedInstruction.Append(line.Substring(colon).TrimStart()); nakedInstruction.Append('\n'); } else { nakedInstruction.Append(' ', 7); nakedInstruction.Append(line.TrimStart()); nakedInstruction.Append('\n'); } } instruction = nakedInstruction.ToString(); } switch (_reader.Machine) { case Machine.Amd64: ProbeX64Quirks(rtf, imageOffset, rtfOffset, instrSize, ref instruction); break; case Machine.I386: ProbeX86Quirks(rtf, imageOffset, rtfOffset, instrSize, ref instruction); break; case Machine.ArmThumb2: case Machine.Thumb: break; case Machine.Arm64: break; default: throw new NotImplementedException(); } instruction = instruction.Replace("\n", Environment.NewLine); return(instrSize); }
/// <summary> /// Store the R2R reader and construct the disassembler for the appropriate architecture. /// </summary> /// <param name="reader"></param> public Disassembler(R2RReader reader) { _reader = reader; _disasm = CoreDisTools.GetDisasm(_reader.Machine); }
/// <summary> /// Parse and dump a single instruction and return its size in bytes. /// </summary> /// <param name="rtf">Runtime function to parse</param> /// <param name="imageOffset">Offset within the PE image byte array</param> /// <param name="rtfOffset">Instruction offset within the runtime function</param> /// <param name="instruction">Output text representation of the instruction</param> /// <returns>Instruction size in bytes - i.o.w. the next instruction starts at rtfOffset + (the return value)</returns> public int GetInstruction(RuntimeFunction rtf, int imageOffset, int rtfOffset, out string instruction) { if (_disasm == IntPtr.Zero) { instruction = ""; return(rtf.Size); } int instrSize = CoreDisTools.GetInstruction(_disasm, rtf, imageOffset, rtfOffset, _reader.Image, out instruction); // CoreDisTools dumps instructions in the following format: // // address: bytes [padding] \t mnemonic [\t operands] \n // // However, due to an LLVM issue regarding instruction prefixes (https://bugs.llvm.org/show_bug.cgi?id=7709), // multiple lines may be returned for a single x86/x64 instruction. var builder = new StringBuilder(); int lineNum = 0; // The start index of the last line in builder int lineStartIndex = 0; // Remove this foreach wrapper and line* variables after the aforementioned LLVM issue is fixed foreach (string line in instruction.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)) { int colonIndex = line.IndexOf(':'); int tab1Index = line.IndexOf('\t'); if ((0 < colonIndex) && (colonIndex < tab1Index)) { // First handle the address and the byte dump if (_options.Naked) { if (!_options.HideOffsets) { // All lines but the last one must represent single-byte prefixes, so add lineNum to the offset builder.Append($"{rtf.CodeOffset + rtfOffset + lineNum,8:x4}:"); } } else { if (_reader.Machine == Machine.Arm64) { // Replace " hh hh hh hh " byte dump with " hhhhhhhh ". // CoreDisTools should be fixed to dump bytes this way for ARM64. uint instructionBytes = BitConverter.ToUInt32(_reader.Image, imageOffset + rtfOffset); builder.Append(line, 0, colonIndex + 1); builder.Append(' '); builder.Append(instructionBytes.ToString("x8")); } else { // Copy the offset and the byte dump int byteDumpEndIndex = tab1Index; do { byteDumpEndIndex--; }while (line[byteDumpEndIndex] == ' '); builder.Append(line, 0, byteDumpEndIndex + 1); } builder.Append(' '); } // Now handle the mnemonic and operands. Ensure proper indentation for the mnemonic. EnsureIndentation(builder, lineStartIndex, MnemonicIndentation); int tab2Index = line.IndexOf('\t', tab1Index + 1); if (tab2Index >= 0) { // Copy everything between the first and the second tabs builder.Append(line, tab1Index + 1, tab2Index - tab1Index - 1); // Ensure proper indentation for the operands EnsureIndentation(builder, lineStartIndex, OperandsIndentation); int afterTab2Index = tab2Index + 1; // Work around an LLVM issue causing an extra space to be output before operands; // see https://reviews.llvm.org/D35946. if ((afterTab2Index < line.Length) && ((line[afterTab2Index] == ' ') || (line[afterTab2Index] == '\t'))) { afterTab2Index++; } // Copy everything after the second tab int savedLength = builder.Length; builder.Append(line, afterTab2Index, line.Length - afterTab2Index); // There should be no extra tabs. Should we encounter them, replace them with a single space. if (line.IndexOf('\t', afterTab2Index) >= 0) { builder.Replace('\t', ' ', savedLength, builder.Length - savedLength); } } else { // Copy everything after the first tab builder.Append(line, tab1Index + 1, line.Length - tab1Index - 1); } } else { // Should not happen. Just replace tabs with spaces. builder.Append(line.Replace('\t', ' ')); } string translatedLine = builder.ToString(lineStartIndex, builder.Length - lineStartIndex); string fixedTranslatedLine = translatedLine; switch (_reader.Machine) { case Machine.Amd64: ProbeX64Quirks(rtf, imageOffset, rtfOffset, instrSize, ref fixedTranslatedLine); break; case Machine.I386: ProbeX86Quirks(rtf, imageOffset, rtfOffset, instrSize, ref fixedTranslatedLine); break; case Machine.Arm64: ProbeArm64Quirks(rtf, imageOffset, rtfOffset, ref fixedTranslatedLine); break; case Machine.ArmThumb2: break; default: break; } // If the translated line has been changed, replace it in the builder if (!object.ReferenceEquals(fixedTranslatedLine, translatedLine)) { builder.Length = lineStartIndex; builder.Append(fixedTranslatedLine); } builder.Append(Environment.NewLine); lineNum++; lineStartIndex = builder.Length; } instruction = builder.ToString(); return(instrSize); }
/// <summary> /// Store the R2R reader and construct the disassembler for the appropriate architecture. /// </summary> /// <param name="reader"></param> public Disassembler(R2RReader reader, DumpOptions options) { _reader = reader; _options = options; _disasm = CoreDisTools.GetDisasm(_reader.Machine); }
private int Run(string[] args) { ArgumentSyntax syntax = ParseCommandLine(args); // open output stream if (_outputFilename != null) { _writer = File.CreateText(_outputFilename); } else { _writer = Console.Out; } if (_help) { _writer.WriteLine(syntax.GetHelpText()); return(0); } try { if (_inputFilenames.Count == 0) { throw new ArgumentException("Input filename must be specified (--in <file>)"); } foreach (string filename in _inputFilenames) { R2RReader r2r = new R2RReader(filename); if (_disasm) { _disassembler = CoreDisTools.GetDisasm(r2r.Machine); } if (_xml) { _dumper = new XmlDumper(_ignoreSensitive, r2r, _writer, _raw, _header, _disasm, _disassembler, _unwind, _gc, _sectionContents); } else { _dumper = new TextDumper(r2r, _writer, _raw, _header, _disasm, _disassembler, _unwind, _gc, _sectionContents); } Dump(r2r); if (_disasm) { CoreDisTools.FinishDisasm(_disassembler); } } } catch (Exception e) { Console.WriteLine("Error: " + e.ToString()); if (e is ArgumentException) { Console.WriteLine(); Console.WriteLine(syntax.GetHelpText()); } if (_xml) { XmlDocument document = new XmlDocument(); XmlNode node = document.CreateNode("element", "Error", ""); node.InnerText = e.Message; document.AppendChild(node); if (_writer != null) { document.Save(_writer); } } return(1); } finally { // close output stream _writer.Close(); } return(0); }