using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; using System.Text; using System.Xml; namespace R2RDump { class TextDumper : Dumper { public TextDumper(R2RReader r2r, TextWriter writer, Disassembler disassembler, DumpOptions options) : base(r2r, writer, disassembler, options) { } internal override void Begin() { if (!_options.Normalize) { _writer.WriteLine($"Filename: {_r2r.Filename}"); _writer.WriteLine($"OS: {_r2r.OS}"); _writer.WriteLine($"Machine: {_r2r.Machine}"); _writer.WriteLine($"ImageBase: 0x{_r2r.ImageBase:X8}"); SkipLine(); } } internal override void End() { _writer.WriteLine("============================================================="); SkipLine(); } internal override void WriteDivider(string title) { int len = 61 - title.Length - 2; _writer.WriteLine(new String('=', len / 2) + " " + title + " " + new String('=', (int)Math.Ceiling(len / 2.0))); SkipLine(); } internal override void WriteSubDivider() { _writer.WriteLine("_______________________________________________"); SkipLine(); } internal override void SkipLine() { _writer.WriteLine(); } /// /// Dumps the R2RHeader and all the sections in the header /// internal override void DumpHeader(bool dumpSections) { _writer.WriteLine(_r2r.R2RHeader.ToString()); if (_options.Raw) { DumpBytes(_r2r.R2RHeader.RelativeVirtualAddress, (uint)_r2r.R2RHeader.Size); } SkipLine(); if (dumpSections) { WriteDivider("R2R Sections"); _writer.WriteLine($"{_r2r.R2RHeader.Sections.Count} sections"); SkipLine(); foreach (R2RSection section in NormalizedSections()) { DumpSection(section, parentNode: null); } } SkipLine(); } /// /// Dumps one R2RSection /// internal override void DumpSection(R2RSection section, XmlNode parentNode = null) { WriteSubDivider(); section.WriteTo(_writer, _options); if (_options.Raw) { DumpBytes(section.RelativeVirtualAddress, (uint)section.Size); SkipLine(); } if (_options.SectionContents) { DumpSectionContents(section, parentNode); SkipLine(); } } internal override void DumpEntryPoints() { WriteDivider($@"R2R Entry Points"); foreach (R2RMethod method in NormalizedMethods()) { _writer.WriteLine(method.SignatureString); } } internal override void DumpAllMethods() { WriteDivider("R2R Methods"); _writer.WriteLine($"{_r2r.R2RMethods.Count} methods"); SkipLine(); foreach (R2RMethod method in NormalizedMethods()) { DumpMethod(method); } } /// /// Dumps one R2RMethod. /// internal override void DumpMethod(R2RMethod method, XmlNode parentNode = null) { WriteSubDivider(); method.WriteTo(_writer, _options); if (_options.GC && method.GcInfo != null) { _writer.WriteLine("GcInfo:"); _writer.Write(method.GcInfo); if (_options.Raw) { DumpBytes(method.GcInfo.Offset, (uint)method.GcInfo.Size, null, "", false); } } SkipLine(); foreach (RuntimeFunction runtimeFunction in method.RuntimeFunctions) { DumpRuntimeFunction(runtimeFunction); } } /// /// Dumps one runtime function. /// internal override void DumpRuntimeFunction(RuntimeFunction rtf, XmlNode parentNode = null) { _writer.WriteLine(rtf.Method.SignatureString); rtf.WriteTo(_writer, _options); if (_options.Disasm) { DumpDisasm(rtf, _r2r.GetOffset(rtf.StartAddress)); } if (_options.Raw) { _writer.WriteLine("Raw Bytes:"); DumpBytes(rtf.StartAddress, (uint)rtf.Size); } if (_options.Unwind) { _writer.WriteLine("UnwindInfo:"); _writer.Write(rtf.UnwindInfo); if (_options.Raw) { DumpBytes(rtf.UnwindRVA, (uint)rtf.UnwindInfo.Size); } } SkipLine(); } /// /// Dumps disassembly and register liveness /// internal override void DumpDisasm(RuntimeFunction rtf, int imageOffset, XmlNode parentNode = null) { 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 && ((Amd64.UnwindInfo)rtf.UnwindInfo).UnwindCodes.ContainsKey(codeOffset)) { List codes = ((Amd64.UnwindInfo)rtf.UnwindInfo).UnwindCodes[codeOffset]; foreach (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 != 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; } } /// /// Prints a formatted string containing a block of bytes from the relative virtual address and size /// internal override void DumpBytes(int rva, uint size, XmlNode parentNode = null, string name = "Raw", bool convertToOffset = true) { int start = rva; if (convertToOffset) start = _r2r.GetOffset(rva); if (start > _r2r.Image.Length || start + size > _r2r.Image.Length) { throw new IndexOutOfRangeException(); } _writer.Write(" "); if (rva % 16 != 0) { int floor = rva / 16 * 16; _writer.Write($"{floor:X8}:"); _writer.Write(new String(' ', (rva - floor) * 3)); } for (uint i = 0; i < size; i++) { if ((rva + i) % 16 == 0) { _writer.Write($"{rva + i:X8}:"); } _writer.Write($" {_r2r.Image[start + i]:X2}"); if ((rva + i) % 16 == 15 && i != size - 1) { SkipLine(); _writer.Write(" "); } } SkipLine(); } internal override void DumpSectionContents(R2RSection section, XmlNode parentNode = null) { switch (section.Type) { case R2RSection.SectionType.READYTORUN_SECTION_AVAILABLE_TYPES: if (!_options.Naked) { uint availableTypesSectionOffset = (uint)_r2r.GetOffset(section.RelativeVirtualAddress); NativeParser availableTypesParser = new NativeParser(_r2r.Image, availableTypesSectionOffset); NativeHashtable availableTypes = new NativeHashtable(_r2r.Image, availableTypesParser, (uint)(availableTypesSectionOffset + section.Size)); _writer.WriteLine(availableTypes.ToString()); } foreach (string name in _r2r.AvailableTypes) { _writer.WriteLine(name); } break; case R2RSection.SectionType.READYTORUN_SECTION_METHODDEF_ENTRYPOINTS: if (!_options.Naked) { NativeArray methodEntryPoints = new NativeArray(_r2r.Image, (uint)_r2r.GetOffset(section.RelativeVirtualAddress)); _writer.Write(methodEntryPoints.ToString()); } break; case R2RSection.SectionType.READYTORUN_SECTION_INSTANCE_METHOD_ENTRYPOINTS: if (!_options.Naked) { uint instanceSectionOffset = (uint)_r2r.GetOffset(section.RelativeVirtualAddress); NativeParser instanceParser = new NativeParser(_r2r.Image, instanceSectionOffset); NativeHashtable instMethodEntryPoints = new NativeHashtable(_r2r.Image, instanceParser, (uint)(instanceSectionOffset + section.Size)); _writer.Write(instMethodEntryPoints.ToString()); _writer.WriteLine(); } foreach (InstanceMethod instanceMethod in _r2r.InstanceMethods) { _writer.WriteLine($@"0x{instanceMethod.Bucket:X2} -> {instanceMethod.Method.SignatureString}"); } break; case R2RSection.SectionType.READYTORUN_SECTION_RUNTIME_FUNCTIONS: int rtfOffset = _r2r.GetOffset(section.RelativeVirtualAddress); int rtfEndOffset = rtfOffset + section.Size; int rtfIndex = 0; while (rtfOffset < rtfEndOffset) { int startRva = NativeReader.ReadInt32(_r2r.Image, ref rtfOffset); int endRva = -1; if (_r2r.Machine == Machine.Amd64) { endRva = NativeReader.ReadInt32(_r2r.Image, ref rtfOffset); } int unwindRva = NativeReader.ReadInt32(_r2r.Image, ref rtfOffset); _writer.WriteLine($"Index: {rtfIndex}"); _writer.WriteLine($"\tStartRva: 0x{startRva:X8}"); if (endRva != -1) _writer.WriteLine($"\tEndRva: 0x{endRva:X8}"); _writer.WriteLine($"\tUnwindRva: 0x{unwindRva:X8}"); rtfIndex++; } break; case R2RSection.SectionType.READYTORUN_SECTION_COMPILER_IDENTIFIER: _writer.WriteLine(_r2r.CompilerIdentifier); break; case R2RSection.SectionType.READYTORUN_SECTION_IMPORT_SECTIONS: if (_options.Naked) { DumpNakedImportSections(); } else { foreach (R2RImportSection importSection in _r2r.ImportSections) { importSection.WriteTo(_writer); if (_options.Raw && importSection.Entries.Count != 0) { if (importSection.SectionRVA != 0) { _writer.WriteLine("Section Bytes:"); DumpBytes(importSection.SectionRVA, (uint)importSection.SectionSize); } if (importSection.SignatureRVA != 0) { _writer.WriteLine("Signature Bytes:"); DumpBytes(importSection.SignatureRVA, (uint)importSection.Entries.Count * sizeof(int)); } if (importSection.AuxiliaryDataRVA != 0 && importSection.AuxiliaryDataSize != 0) { _writer.WriteLine("AuxiliaryData Bytes:"); DumpBytes(importSection.AuxiliaryDataRVA, (uint)importSection.AuxiliaryDataSize); } } foreach (R2RImportSection.ImportSectionEntry entry in importSection.Entries) { entry.WriteTo(_writer, _options); _writer.WriteLine(); } _writer.WriteLine(); } } break; case R2RSection.SectionType.READYTORUN_SECTION_MANIFEST_METADATA: int assemblyRefCount = _r2r.MetadataReader.GetTableRowCount(TableIndex.AssemblyRef); _writer.WriteLine($"MSIL AssemblyRef's ({assemblyRefCount} entries):"); for (int assemblyRefIndex = 1; assemblyRefIndex <= assemblyRefCount; assemblyRefIndex++) { AssemblyReference assemblyRef = _r2r.MetadataReader.GetAssemblyReference(MetadataTokens.AssemblyReferenceHandle(assemblyRefIndex)); string assemblyRefName = _r2r.MetadataReader.GetString(assemblyRef.Name); _writer.WriteLine($"[ID 0x{assemblyRefIndex:X2}]: {assemblyRefName}"); } _writer.WriteLine($"Manifest metadata AssemblyRef's ({_r2r.ManifestReferenceAssemblies.Count} entries):"); for (int manifestAsmIndex = 0; manifestAsmIndex < _r2r.ManifestReferenceAssemblies.Count; manifestAsmIndex++) { _writer.WriteLine($"[ID 0x{manifestAsmIndex + assemblyRefCount + 2:X2}]: {_r2r.ManifestReferenceAssemblies[manifestAsmIndex]}"); } break; case R2RSection.SectionType.READYTORUN_SECTION_ATTRIBUTEPRESENCE: int attributesStartOffset = _r2r.GetOffset(section.RelativeVirtualAddress); int attributesEndOffset = attributesStartOffset + section.Size; NativeCuckooFilter attributes = new NativeCuckooFilter(_r2r.Image, attributesStartOffset, attributesEndOffset); _writer.WriteLine("Attribute presence filter"); _writer.WriteLine(attributes.ToString()); break; } } private void DumpNakedImportSections() { List entries = new List(); foreach (R2RImportSection importSection in _r2r.ImportSections) { entries.AddRange(importSection.Entries); } entries.Sort((e1, e2) => e1.Signature.CompareTo(e2.Signature)); foreach (R2RImportSection.ImportSectionEntry entry in entries) { entry.WriteTo(_writer, _options); _writer.WriteLine(); } } internal override XmlNode DumpQueryCount(string q, string title, int count) { _writer.WriteLine(count + " result(s) for \"" + q + "\""); SkipLine(); return null; } } }