private void DiffR2RMethodsForHeader(ReadyToRunCoreHeader leftHeader, ReadyToRunCoreHeader rightHeader) { if (!leftHeader.Sections.TryGetValue(ReadyToRunSectionType.MethodDefEntryPoints, out ReadyToRunSection leftSection)) { leftSection = new ReadyToRunSection(); } if (!rightHeader.Sections.TryGetValue(ReadyToRunSectionType.MethodDefEntryPoints, out ReadyToRunSection rightSection)) { rightSection = new ReadyToRunSection(); } ShowDiff(GetR2RMethodMap(_leftDumper.Reader, leftSection), GetR2RMethodMap(_rightDumper.Reader, rightSection), "R2R methods"); if (_leftDumper.Reader.Composite && _rightDumper.Reader.Composite) { HashSet <string> allComponentAssemblies = new HashSet <string>(_leftDumper.Reader.ManifestReferenceAssemblies.Keys); allComponentAssemblies.UnionWith(_rightDumper.Reader.ManifestReferenceAssemblies.Keys); foreach (string assemblyName in allComponentAssemblies.OrderBy(name => name)) { int leftIndex = _leftDumper.Reader.ManifestReferenceAssemblies[assemblyName]; int rightIndex = _rightDumper.Reader.ManifestReferenceAssemblies[assemblyName]; if (leftIndex < 0 || !_leftDumper.Reader.ReadyToRunAssemblyHeaders[leftIndex].Sections.TryGetValue(ReadyToRunSectionType.MethodDefEntryPoints, out leftSection)) { leftSection = new ReadyToRunSection(); } if (rightIndex < 0 || !_rightDumper.Reader.ReadyToRunAssemblyHeaders[rightIndex].Sections.TryGetValue(ReadyToRunSectionType.MethodDefEntryPoints, out rightSection)) { rightSection = new ReadyToRunSection(); } ShowDiff(GetR2RMethodMap(_leftDumper.Reader, leftSection), GetR2RMethodMap(_rightDumper.Reader, rightSection), $"{assemblyName}: component R2R methods"); } } }
private void DiffMethodsForModule(ReadyToRunSection leftSection, ReadyToRunSection rightSection) { if (!TryGetMethods(_leftDumper.Reader, leftSection, out IReadOnlyList <ReadyToRunMethod> leftSectionMethods)) { leftSectionMethods = new List <ReadyToRunMethod>(); } if (!TryGetMethods(_rightDumper.Reader, rightSection, out IReadOnlyList <ReadyToRunMethod> rightSectionMethods)) { rightSectionMethods = new List <ReadyToRunMethod>(); } Dictionary <string, ReadyToRunMethod> leftMethods = new Dictionary <string, ReadyToRunMethod>(leftSectionMethods .Select(method => new KeyValuePair <string, ReadyToRunMethod>(method.SignatureString, method))); Dictionary <string, ReadyToRunMethod> rightMethods = new Dictionary <string, ReadyToRunMethod>(rightSectionMethods .Select(method => new KeyValuePair <string, ReadyToRunMethod>(method.SignatureString, method))); Dictionary <string, MethodPair> commonMethods = new Dictionary <string, MethodPair>(leftMethods .Select(kvp => new KeyValuePair <string, MethodPair>(kvp.Key, new MethodPair(kvp.Value, rightMethods.TryGetValue(kvp.Key, out ReadyToRunMethod rightMethod) ? rightMethod : null))) .Where(kvp => kvp.Value.RightMethod != null)); if (_leftDumper.Options.DiffHideSameDisasm) { commonMethods = new Dictionary <string, MethodPair>(HideMethodsWithSameDisassembly(commonMethods)); } DumpCommonMethods(_leftDumper, leftSection, commonMethods); DumpCommonMethods(_rightDumper, rightSection, commonMethods); }
/// <summary> /// Returns true if the name or value of the ReadyToRunSectionType of <param>section</param> matches <param>query</param> /// </summary> /// <remarks>Case-insensitive</remarks> private bool Match(ReadyToRunSection section, string query) { int queryInt; bool isNum = ArgStringToInt(query, out queryInt); string typeName = Enum.GetName(typeof(ReadyToRunSectionType), section.Type); return((isNum && (int)section.Type == queryInt) || typeName.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0); }
public static void WriteTo(this ReadyToRunSection theThis, TextWriter writer, DumpOptions options) { writer.WriteLine($"Type: {Enum.GetName(typeof(ReadyToRunSectionType), theThis.Type)} ({theThis.Type:D})"); if (!options.Naked) { writer.WriteLine($"RelativeVirtualAddress: 0x{theThis.RelativeVirtualAddress:X8}"); } writer.WriteLine($"Size: {theThis.Size} bytes"); }
/// <summary> /// Dump the subset of methods common to both sides of the diff to the given dumper. /// </summary> /// <param name="dumper">Output dumper to use</param> /// <param name="signatureFilter">Set of common signatures of methods to dump</param> private void DumpCommonMethods(Dumper dumper, ReadyToRunSection section, Dictionary <string, MethodPair> signatureFilter) { if (!TryGetMethods(dumper.Reader, section, out IReadOnlyList <ReadyToRunMethod> sectionMethods)) { IEnumerable <ReadyToRunMethod> filteredMethods = sectionMethods .Where(method => signatureFilter.ContainsKey(method.SignatureString)) .OrderBy(method => method.SignatureString); foreach (ReadyToRunMethod method in filteredMethods) { dumper.DumpMethod(method); } } }
private bool TryGetMethods(ReadyToRunReader reader, ReadyToRunSection section, out IReadOnlyList <ReadyToRunMethod> methods) { int assemblyIndex = reader.GetAssemblyIndex(section); if (assemblyIndex == -1) { methods = null; return(false); } else { methods = reader.ReadyToRunAssemblies[assemblyIndex].Methods; return(true); } }
/// <summary> /// Read the R2R method map for a given R2R image. /// </summary> /// <param name="reader">R2R image to scan</param> /// <returns></returns> private Dictionary <string, int> GetR2RMethodMap(ReadyToRunReader reader, ReadyToRunSection section) { Dictionary <string, int> methodMap = new Dictionary <string, int>(); if (TryGetMethods(reader, section, out IReadOnlyList <ReadyToRunMethod> sectionMethods)) { foreach (ReadyToRunMethod method in sectionMethods) { int size = method.RuntimeFunctions.Sum(rf => rf.Size); methodMap.Add(method.SignatureString, size); } } return(methodMap); }
/// <summary> /// Dumps one R2RSection /// </summary> internal override void DumpSection(ReadyToRunSection section) { WriteSubDivider(); section.WriteTo(_writer, _options); if (_options.Raw) { DumpBytes(section.RelativeVirtualAddress, (uint)section.Size); SkipLine(); } if (_options.SectionContents) { DumpSectionContents(section); SkipLine(); } }
/// <summary> /// Public API runs all available diff algorithms in sequence. /// </summary> public void Run() { DiffTitle(); DiffPESections(); DiffR2RSections(); DiffR2RMethods(); if (!_leftDumper.Reader.ReadyToRunHeader.Sections.TryGetValue(ReadyToRunSectionType.MethodDefEntryPoints, out ReadyToRunSection leftSection)) { leftSection = new ReadyToRunSection(); } if (!_rightDumper.Reader.ReadyToRunHeader.Sections.TryGetValue(ReadyToRunSectionType.MethodDefEntryPoints, out ReadyToRunSection rightSection)) { rightSection = new ReadyToRunSection(); } DiffMethodsForModule(leftSection, rightSection); if (_leftDumper.Reader.Composite && _rightDumper.Reader.Composite) { HashSet <string> allComponentAssemblies = new HashSet <string>(_leftDumper.Reader.ManifestReferenceAssemblies.Keys); allComponentAssemblies.UnionWith(_rightDumper.Reader.ManifestReferenceAssemblies.Keys); foreach (string assemblyName in allComponentAssemblies.OrderBy(name => name)) { int leftIndex = _leftDumper.Reader.ManifestReferenceAssemblies[assemblyName]; int rightIndex = _rightDumper.Reader.ManifestReferenceAssemblies[assemblyName]; if (leftIndex < 0 || !_leftDumper.Reader.ReadyToRunAssemblyHeaders[leftIndex].Sections.TryGetValue(ReadyToRunSectionType.MethodDefEntryPoints, out leftSection)) { leftSection = new ReadyToRunSection(); } if (rightIndex < 0 || !_rightDumper.Reader.ReadyToRunAssemblyHeaders[rightIndex].Sections.TryGetValue(ReadyToRunSectionType.MethodDefEntryPoints, out rightSection)) { rightSection = new ReadyToRunSection(); } _writer.WriteLine($@"{assemblyName}: component method diff"); DiffMethodsForModule(leftSection, rightSection); } } }
internal override void DumpSectionContents(ReadyToRunSection section) { switch (section.Type) { case ReadyToRunSectionType.AvailableTypes: 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()); } if (_r2r.AvailableTypes.TryGetValue(section, out List <string> sectionTypes)) { _writer.WriteLine(); foreach (string name in sectionTypes) { _writer.WriteLine(name); } } break; case ReadyToRunSectionType.MethodDefEntryPoints: if (!_options.Naked) { NativeArray methodEntryPoints = new NativeArray(_r2r.Image, (uint)_r2r.GetOffset(section.RelativeVirtualAddress)); _writer.Write(methodEntryPoints.ToString()); } if (_r2r.Methods.TryGetValue(section, out List <ReadyToRunMethod> sectionMethods)) { _writer.WriteLine(); foreach (ReadyToRunMethod method in sectionMethods) { _writer.WriteLine($@"{MetadataTokens.GetToken(method.MethodHandle):X8}: {method.SignatureString}"); } } break; case ReadyToRunSectionType.InstanceMethodEntryPoints: 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 ReadyToRunSectionType.RuntimeFunctions: 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($" StartRva: 0x{startRva:X8}"); if (endRva != -1) { _writer.WriteLine($" EndRva: 0x{endRva:X8}"); } _writer.WriteLine($" UnwindRva: 0x{unwindRva:X8}"); rtfIndex++; } break; case ReadyToRunSectionType.CompilerIdentifier: _writer.WriteLine(_r2r.CompilerIdentifier); break; case ReadyToRunSectionType.ImportSections: if (_options.Naked) { DumpNakedImportSections(); } else { foreach (ReadyToRunImportSection 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 (ReadyToRunImportSection.ImportSectionEntry entry in importSection.Entries) { entry.WriteTo(_writer, _options); _writer.WriteLine(); } _writer.WriteLine(); } } break; case ReadyToRunSectionType.ManifestMetadata: int assemblyRefCount = 0; if (!_r2r.Composite) { MetadataReader globalReader = _r2r.GetGlobalMetadataReader(); assemblyRefCount = globalReader.GetTableRowCount(TableIndex.AssemblyRef) + 1; _writer.WriteLine($"MSIL AssemblyRef's ({assemblyRefCount} entries):"); for (int assemblyRefIndex = 1; assemblyRefIndex < assemblyRefCount; assemblyRefIndex++) { AssemblyReference assemblyRef = globalReader.GetAssemblyReference(MetadataTokens.AssemblyReferenceHandle(assemblyRefIndex)); string assemblyRefName = globalReader.GetString(assemblyRef.Name); _writer.WriteLine($"[ID 0x{assemblyRefIndex:X2}]: {assemblyRefName}"); } } _writer.WriteLine($"Manifest metadata AssemblyRef's ({_r2r.ManifestReferenceAssemblies.Count} entries):"); int manifestAsmIndex = 0; foreach (string manifestReferenceAssembly in _r2r.ManifestReferenceAssemblies.OrderBy(kvp => kvp.Value).Select(kvp => kvp.Key)) { _writer.WriteLine($"[ID 0x{manifestAsmIndex + assemblyRefCount + 1:X2}]: {manifestReferenceAssembly}"); manifestAsmIndex++; } break; case ReadyToRunSectionType.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; case ReadyToRunSectionType.InliningInfo: int iiOffset = _r2r.GetOffset(section.RelativeVirtualAddress); int iiEndOffset = iiOffset + section.Size; InliningInfoSection inliningInfoSection = new InliningInfoSection(_r2r, iiOffset, iiEndOffset); _writer.WriteLine(inliningInfoSection.ToString()); break; case ReadyToRunSectionType.InliningInfo2: int ii2Offset = _r2r.GetOffset(section.RelativeVirtualAddress); int ii2EndOffset = ii2Offset + section.Size; InliningInfoSection2 inliningInfoSection2 = new InliningInfoSection2(_r2r, ii2Offset, ii2EndOffset); _writer.WriteLine(inliningInfoSection2.ToString()); break; case ReadyToRunSectionType.OwnerCompositeExecutable: int oceOffset = _r2r.GetOffset(section.RelativeVirtualAddress); if (_r2r.Image[oceOffset + section.Size - 1] != 0) { R2RDump.WriteWarning("String is not zero-terminated"); } string ownerCompositeExecutable = Encoding.UTF8.GetString(_r2r.Image, oceOffset, section.Size - 1); // exclude the zero terminator _writer.WriteLine("Composite executable: {0}", ownerCompositeExecutable.ToEscapedString()); break; } }
abstract internal void DumpSectionContents(ReadyToRunSection section);
internal override void DumpSectionContents(ReadyToRunSection section) { switch (section.Type) { case ReadyToRunSection.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 ReadyToRunSection.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 ReadyToRunSection.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 ReadyToRunSection.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 ReadyToRunSection.SectionType.READYTORUN_SECTION_COMPILER_IDENTIFIER: _writer.WriteLine(_r2r.CompilerIdentifier); break; case ReadyToRunSection.SectionType.READYTORUN_SECTION_IMPORT_SECTIONS: if (_options.Naked) { DumpNakedImportSections(); } else { foreach (ReadyToRunImportSection 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 (ReadyToRunImportSection.ImportSectionEntry entry in importSection.Entries) { entry.WriteTo(_writer, _options); _writer.WriteLine(); } _writer.WriteLine(); } } break; case ReadyToRunSection.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):"); int manifestAsmIndex = 0; foreach (string manifestReferenceAssembly in _r2r.ManifestReferenceAssemblies) { _writer.WriteLine($"[ID 0x{manifestAsmIndex + assemblyRefCount + 2:X2}]: {manifestReferenceAssembly}"); manifestAsmIndex++; } break; case ReadyToRunSection.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; } }