├── ELFSharp ├── icon.png ├── sgKey.snk ├── Endianess.cs ├── ELF │ ├── Class.cs │ ├── Sections │ │ ├── INoteSection.cs │ │ ├── SymbolBinding.cs │ │ ├── IProgBitsSection.cs │ │ ├── SpecialSectionIndex.cs │ │ ├── ISymbolTable.cs │ │ ├── IDynamicSection.cs │ │ ├── SymbolVisibility.cs │ │ ├── SectionFlags.cs │ │ ├── SymbolType.cs │ │ ├── SpecialSectionType.cs │ │ ├── IStringTable.cs │ │ ├── ISection.cs │ │ ├── SectionType.cs │ │ ├── ISymbolEntry.cs │ │ ├── IDynamicEntry.cs │ │ ├── NoteSection.cs │ │ ├── DynamicEntry.cs │ │ ├── ProgBitsSection.cs │ │ ├── Section.cs │ │ ├── SymbolEntry.cs │ │ ├── DynamicSection.cs │ │ ├── SymbolTable.cs │ │ ├── SectionHeader.cs │ │ ├── StringTable.cs │ │ ├── DynamicTag.cs │ │ └── NoteData.cs │ ├── FileType.cs │ ├── Segments │ │ ├── SegmentFlags.cs │ │ ├── SegmentType.cs │ │ ├── ISegment.cs │ │ ├── INoteSegment.cs │ │ ├── INoteData.cs │ │ ├── NoteSegment.cs │ │ └── Segment.cs │ ├── Utilities.cs │ ├── Consts.cs │ ├── IELF.cs │ ├── ELFReader.cs │ ├── Machine.cs │ └── ELF.cs ├── MachO │ ├── MachOResult.cs │ ├── Protection.cs │ ├── IdDylib.cs │ ├── LoadDylib.cs │ ├── LoadWeakDylib.cs │ ├── ReexportDylib.cs │ ├── CommandType.cs │ ├── FileType.cs │ ├── Command.cs │ ├── Machine.cs │ ├── EntryPoint.cs │ ├── Symbol.cs │ ├── UUID.cs │ ├── FatArchiveReader.cs │ ├── Dylib.cs │ ├── Section.cs │ ├── SymbolTable.cs │ ├── MachOReader.cs │ ├── MachO.cs │ ├── HeaderFlags.cs │ └── Segment.cs ├── UImage │ ├── UImageResult.cs │ ├── ImageDataResult.cs │ ├── CompressionType.cs │ ├── ImageType.cs │ ├── Architecture.cs │ ├── OS.cs │ ├── UImageReader.cs │ └── UImage.cs ├── Utilities │ ├── Extensions.cs │ ├── SimpleEndianessAwareReader.cs │ └── SubStream.cs └── ELFSharp.csproj ├── .gitignore ├── .gitmodules ├── .github └── workflows │ └── dotnetcore.yml ├── Directory.Build.props ├── Tests ├── UImage │ ├── GzipTests.cs │ ├── MultiFileImageTests.cs │ └── SimpleTests.cs ├── MachO │ ├── UUIDTests.cs │ ├── EntryPointTests.cs │ ├── FileHeaderTests.cs │ ├── SegmentTests.cs │ ├── SymbolTableTests.cs │ ├── DylibTests.cs │ └── OpeningTests.cs ├── ELF │ ├── ProgBitsSectionTests.cs │ ├── StringTableTests.cs │ ├── DynamicSectionTests.cs │ ├── SectionGettingTests.cs │ ├── SymbolTableTests.cs │ ├── SectionHeadersParsingTests.cs │ ├── NoteSectionTests.cs │ ├── WebTests.cs │ ├── SegmentsTests.cs │ └── OpeningTests.cs ├── Tests.csproj ├── Utilities.cs └── StreamWrapper.cs ├── README.md ├── ELFSharp.sln ├── CHANGELOG.md └── LICENSE /ELFSharp/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/konrad-kruczynski/elfsharp/HEAD/ELFSharp/icon.png -------------------------------------------------------------------------------- /ELFSharp/sgKey.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/konrad-kruczynski/elfsharp/HEAD/ELFSharp/sgKey.snk -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | *.userprefs 4 | test-results/ 5 | /packages/*/ 6 | /.vs 7 | .idea/ 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Tests/Binaries"] 2 | path = Tests/Binaries 3 | url = https://github.com/konrad-kruczynski/elfsharp-test-binaries.git 4 | -------------------------------------------------------------------------------- /ELFSharp/Endianess.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp 2 | { 3 | public enum Endianess 4 | { 5 | LittleEndian, 6 | BigEndian 7 | } 8 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Class.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.ELF 2 | { 3 | public enum Class 4 | { 5 | Bit32, 6 | Bit64, 7 | NotELF 8 | } 9 | } -------------------------------------------------------------------------------- /ELFSharp/MachO/MachOResult.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.MachO 2 | { 3 | public enum MachOResult 4 | { 5 | OK, 6 | NotMachO, 7 | FatMachO 8 | } 9 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/INoteSection.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.ELF.Sections 2 | { 3 | public interface INoteSection : ISection 4 | { 5 | string NoteName { get; } 6 | byte[] Description { get; } 7 | } 8 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/SymbolBinding.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.ELF.Sections 2 | { 3 | public enum SymbolBinding 4 | { 5 | Local, 6 | Global, 7 | Weak, 8 | ProcessorSpecific 9 | } 10 | } -------------------------------------------------------------------------------- /ELFSharp/UImage/UImageResult.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.UImage 2 | { 3 | public enum UImageResult 4 | { 5 | OK, 6 | NotUImage, 7 | BadChecksum, 8 | NotSupportedImageType 9 | } 10 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/IProgBitsSection.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.ELF.Sections 2 | { 3 | public interface IProgBitsSection : ISection 4 | { 5 | void WriteContents(byte[] destination, int offset, int length = 0); 6 | } 7 | } -------------------------------------------------------------------------------- /ELFSharp/MachO/Protection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ELFSharp.MachO 4 | { 5 | [Flags] 6 | public enum Protection 7 | { 8 | Read = 1, 9 | Write = 2, 10 | Execute = 4 11 | } 12 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/FileType.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.ELF 2 | { 3 | public enum FileType : ushort 4 | { 5 | None = 0, 6 | Relocatable, 7 | Executable, 8 | SharedObject, 9 | Core 10 | } 11 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/SpecialSectionIndex.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.ELF.Sections 2 | { 3 | public enum SpecialSectionIndex : ushort 4 | { 5 | Absolute = 0, 6 | Common = 0xFFF1, 7 | Undefined = 0xFFF2 8 | } 9 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/ISymbolTable.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ELFSharp.ELF.Sections 4 | { 5 | public interface ISymbolTable : ISection 6 | { 7 | IEnumerable Entries { get; } 8 | } 9 | } -------------------------------------------------------------------------------- /ELFSharp/UImage/ImageDataResult.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.UImage 2 | { 3 | public enum ImageDataResult 4 | { 5 | OK, 6 | BadChecksum, 7 | UnsupportedCompressionFormat, 8 | InvalidIndex 9 | } 10 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/IDynamicSection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ELFSharp.ELF.Sections 4 | { 5 | public interface IDynamicSection : ISection 6 | { 7 | IEnumerable Entries { get; } 8 | } 9 | } -------------------------------------------------------------------------------- /ELFSharp/UImage/CompressionType.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.UImage 2 | { 3 | public enum CompressionType : byte 4 | { 5 | None = 0, 6 | Gzip = 1, 7 | Bzip2 = 2, 8 | Lzma = 3, 9 | Lzo = 4 10 | } 11 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/SymbolVisibility.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.ELF.Sections 2 | { 3 | public enum SymbolVisibility : byte 4 | { 5 | Default = 0, 6 | Internal = 1, 7 | Hidden = 2, 8 | Protected = 3 9 | } 10 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Segments/SegmentFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ELFSharp.ELF.Segments 4 | { 5 | [Flags] 6 | public enum SegmentFlags : uint 7 | { 8 | Execute = 1, 9 | Write = 2, 10 | Read = 4 11 | } 12 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/SectionFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ELFSharp.ELF.Sections 4 | { 5 | [Flags] 6 | public enum SectionFlags 7 | { 8 | Writable = 1, 9 | Allocatable = 2, 10 | Executable = 4 11 | } 12 | } -------------------------------------------------------------------------------- /ELFSharp/UImage/ImageType.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.UImage 2 | { 3 | // here only supported image types are listed 4 | public enum ImageType : byte 5 | { 6 | Standalone = 1, 7 | Kernel = 2, 8 | MultiFileImage = 4 9 | } 10 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/SymbolType.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.ELF.Sections 2 | { 3 | public enum SymbolType 4 | { 5 | NotSpecified, 6 | Object, 7 | Function, 8 | Section, 9 | File, 10 | ProcessorSpecific 11 | } 12 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/SpecialSectionType.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.ELF.Sections 2 | { 3 | public enum SpecialSectionType 4 | { 5 | Null, 6 | ProgBits, 7 | NoBits, 8 | Shlib, 9 | ProcessorSpecific, 10 | UserSpecific 11 | } 12 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/IStringTable.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ELFSharp.ELF.Sections 4 | { 5 | public interface IStringTable : ISection 6 | { 7 | string this[long index] { get; } 8 | IEnumerable Strings { get; } 9 | } 10 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Utilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ELFSharp.ELF 4 | { 5 | internal static class Utilities 6 | { 7 | internal static T To(this object source) 8 | { 9 | return (T)Convert.ChangeType(source, typeof(T)); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Segments/SegmentType.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.ELF.Segments 2 | { 3 | public enum SegmentType : uint 4 | { 5 | Null = 0, 6 | Load, 7 | Dynamic, 8 | Interpreter, 9 | Note, 10 | SharedLibrary, 11 | ProgramHeader 12 | } 13 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/ISection.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.ELF.Sections 2 | { 3 | public interface ISection 4 | { 5 | string Name { get; } 6 | uint NameIndex { get; } 7 | SectionType Type { get; } 8 | SectionFlags Flags { get; } 9 | byte[] GetContents(); 10 | } 11 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Segments/ISegment.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.ELF.Segments 2 | { 3 | public interface ISegment 4 | { 5 | SegmentType Type { get; } 6 | SegmentFlags Flags { get; } 7 | byte[] GetRawHeader(); 8 | byte[] GetFileContents(); 9 | byte[] GetMemoryContents(); 10 | } 11 | } -------------------------------------------------------------------------------- /ELFSharp/MachO/IdDylib.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using ELFSharp.Utilities; 3 | 4 | namespace ELFSharp.MachO 5 | { 6 | public class IdDylib : Dylib 7 | { 8 | public IdDylib(SimpleEndianessAwareReader reader, Stream stream, uint commandSize) : base(reader, stream, 9 | commandSize) 10 | { 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /ELFSharp/MachO/LoadDylib.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using ELFSharp.Utilities; 3 | 4 | namespace ELFSharp.MachO 5 | { 6 | public class LoadDylib : Dylib 7 | { 8 | public LoadDylib(SimpleEndianessAwareReader reader, Stream stream, uint commandSize) : base(reader, stream, 9 | commandSize) 10 | { 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /ELFSharp/MachO/LoadWeakDylib.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using ELFSharp.Utilities; 3 | 4 | namespace ELFSharp.MachO 5 | { 6 | public class LoadWeakDylib : Dylib 7 | { 8 | public LoadWeakDylib(SimpleEndianessAwareReader reader, Stream stream, uint commandSize) : base(reader, stream, 9 | commandSize) 10 | { 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /ELFSharp/MachO/ReexportDylib.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using ELFSharp.Utilities; 3 | 4 | namespace ELFSharp.MachO 5 | { 6 | public class ReexportDylib : Dylib 7 | { 8 | public ReexportDylib(SimpleEndianessAwareReader reader, Stream stream, uint commandSize) : base(reader, stream, 9 | commandSize) 10 | { 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Consts.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.ELF 2 | { 3 | public static class Consts 4 | { 5 | public const string ObjectsStringTableName = ".strtab"; 6 | public const string DynamicStringTableName = ".dynstr"; 7 | public const int SymbolEntrySize32 = 16; 8 | public const int SymbolEntrySize64 = 24; 9 | public const int MinimalELFSize = 16; 10 | } 11 | } -------------------------------------------------------------------------------- /ELFSharp/MachO/CommandType.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.MachO 2 | { 3 | public enum CommandType : uint 4 | { 5 | Segment = 0x1, 6 | SymbolTable = 0x2, 7 | LoadDylib = 0xc, 8 | IdDylib = 0xd, 9 | LoadWeakDylib = 0x80000018u, 10 | Segment64 = 0x19, 11 | ReexportDylib = 0x8000001fu, 12 | Main = 0x80000028u, 13 | UUID = 0x1b 14 | } 15 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/SectionType.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.ELF.Sections 2 | { 3 | public enum SectionType : uint 4 | { 5 | Null = 0, 6 | ProgBits, 7 | SymbolTable, 8 | StringTable, 9 | RelocationAddends, 10 | HashTable, 11 | Dynamic, 12 | Note, 13 | NoBits, 14 | Relocation, 15 | Shlib, 16 | DynamicSymbolTable 17 | } 18 | } -------------------------------------------------------------------------------- /ELFSharp/MachO/FileType.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.MachO 2 | { 3 | public enum FileType : uint 4 | { 5 | Object = 0x1, 6 | Executable = 0x2, 7 | FixedVM = 0x3, 8 | Core = 0x4, 9 | Preload = 0x5, 10 | DynamicLibrary = 0x6, 11 | DynamicLinker = 0x7, 12 | Bundle = 0x8, 13 | DynamicLibraryStub = 0x9, 14 | Debug = 0xA, 15 | Kext = 0xB 16 | } 17 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/ISymbolEntry.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.ELF.Sections 2 | { 3 | public interface ISymbolEntry 4 | { 5 | string Name { get; } 6 | SymbolBinding Binding { get; } 7 | SymbolType Type { get; } 8 | SymbolVisibility Visibility { get; } 9 | bool IsPointedIndexSpecial { get; } 10 | ISection PointedSection { get; } 11 | ushort PointedSectionIndex { get; } 12 | } 13 | } -------------------------------------------------------------------------------- /.github/workflows/dotnetcore.yml: -------------------------------------------------------------------------------- 1 | name: .NET Core 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | with: 13 | submodules: true 14 | - name: Setup .NET Core 15 | uses: actions/setup-dotnet@v1 16 | with: 17 | dotnet-version: 6.0.100 18 | - name: Unit tests 19 | run: dotnet test -f net6.0 Tests/Tests.csproj 20 | -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/IDynamicEntry.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.ELF.Sections 2 | { 3 | /// 4 | /// Represents an entry in the dynamic table. 5 | /// Interface--because this is a union type in C, if we want more detail at some point on the values in the Union type, 6 | /// we can have separate classes. 7 | /// 8 | public interface IDynamicEntry 9 | { 10 | DynamicTag Tag { get; } 11 | } 12 | } -------------------------------------------------------------------------------- /ELFSharp/MachO/Command.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using ELFSharp.Utilities; 3 | 4 | namespace ELFSharp.MachO 5 | { 6 | public class Command 7 | { 8 | protected readonly SimpleEndianessAwareReader Reader; 9 | protected readonly Stream Stream; 10 | 11 | internal Command(SimpleEndianessAwareReader reader, Stream stream) 12 | { 13 | Stream = stream; 14 | Reader = reader; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Segments/INoteSegment.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ELFSharp.ELF.Segments 4 | { 5 | public interface INoteSegment : ISegment 6 | { 7 | string NoteName { get; } 8 | ulong NoteType { get; } 9 | byte[] NoteDescription { get; } 10 | 11 | /// 12 | /// Returns all notes within the segment 13 | /// 14 | IReadOnlyList Notes { get; } 15 | } 16 | } -------------------------------------------------------------------------------- /ELFSharp/MachO/Machine.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.MachO 2 | { 3 | public enum Machine 4 | { 5 | Any = -1, 6 | Vax = 1, 7 | M68k = 6, 8 | X86 = 7, 9 | X86_64 = X86 | MachO.Architecture64, 10 | M98k = 10, 11 | PaRisc = 11, 12 | Arm = 12, 13 | Arm64 = Arm | MachO.Architecture64, 14 | M88k = 13, 15 | Sparc = 14, 16 | I860 = 15, 17 | PowerPc = 18, 18 | PowerPc64 = PowerPc | MachO.Architecture64 19 | } 20 | } -------------------------------------------------------------------------------- /ELFSharp/MachO/EntryPoint.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using ELFSharp.Utilities; 3 | 4 | namespace ELFSharp.MachO 5 | { 6 | public class EntryPoint : Command 7 | { 8 | public EntryPoint(SimpleEndianessAwareReader reader, Stream stream) : base(reader, stream) 9 | { 10 | Value = Reader.ReadInt64(); 11 | StackSize = Reader.ReadInt64(); 12 | } 13 | 14 | public long Value { get; private set; } 15 | 16 | public long StackSize { get; private set; } 17 | } 18 | } -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8.0 4 | True 5 | True 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /ELFSharp/MachO/Symbol.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace ELFSharp.MachO 4 | { 5 | [DebuggerDisplay("Symbol({Name,nq},{Value}) in {Section}")] 6 | public struct Symbol 7 | { 8 | public Symbol(string name, long value, Section section) : this() 9 | { 10 | Name = name; 11 | Value = value; 12 | Section = section; 13 | } 14 | 15 | public string Name { get; private set; } 16 | public long Value { get; private set; } 17 | public Section Section { get; private set; } 18 | } 19 | } -------------------------------------------------------------------------------- /ELFSharp/UImage/Architecture.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.UImage 2 | { 3 | public enum Architecture : byte 4 | { 5 | Invalid = 0, 6 | Alpha = 1, 7 | ARM = 2, 8 | Ix86 = 3, 9 | Itanium = 4, 10 | MIPS = 5, 11 | MIPS64 = 6, 12 | PowerPC = 7, 13 | S390 = 8, 14 | SuperH = 9, 15 | SPARC = 10, 16 | SPARC64 = 11, 17 | M68k = 12, 18 | MicroBlaze = 14, 19 | Nios2 = 15, 20 | Blackfin = 16, 21 | AVR32 = 17, 22 | ST200 = 18, 23 | Sandbox = 19, 24 | NDS32 = 20, 25 | OpenRISC = 21 26 | } 27 | } -------------------------------------------------------------------------------- /ELFSharp/UImage/OS.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.UImage 2 | { 3 | public enum OS : byte 4 | { 5 | Invalid = 0, 6 | OpenBSD = 1, 7 | NetBSD = 2, 8 | FreeBSD = 3, 9 | BSD44 = 4, 10 | Linux = 5, 11 | SVR4 = 6, 12 | Esix = 7, 13 | Solaris = 8, 14 | Irix = 9, 15 | SCO = 10, 16 | Dell = 11, 17 | NCR = 12, 18 | LynxOS = 13, 19 | VxWorks = 14, 20 | PSOS = 15, 21 | QNX = 16, 22 | Firmware = 17, 23 | RTEMS = 18, 24 | ARTOS = 19, 25 | UnityOS = 20, 26 | INTEGRITY = 21, 27 | OSE = 22, 28 | Plan9 = 23 29 | } 30 | } -------------------------------------------------------------------------------- /ELFSharp/Utilities/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace ELFSharp.Utilities 4 | { 5 | internal static class Extensions 6 | { 7 | public static byte[] ReadBytesOrThrow(this Stream stream, int count) 8 | { 9 | var result = new byte[count]; 10 | while (count > 0) 11 | { 12 | var readThisTurn = stream.Read(result, result.Length - count, count); 13 | if (readThisTurn == 0) 14 | throw new EndOfStreamException($"End of stream reached while {count} bytes more expected."); 15 | count -= readThisTurn; 16 | } 17 | 18 | return result; 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /Tests/UImage/GzipTests.cs: -------------------------------------------------------------------------------- 1 | using ELFSharp.UImage; 2 | using NUnit.Framework; 3 | 4 | namespace Tests.UImage 5 | { 6 | [TestFixture] 7 | public class GzipTests 8 | { 9 | [Test] 10 | public void ShouldExtractGzipCompressedUImage() 11 | { 12 | Assert.AreEqual(UImageResult.OK, 13 | UImageReader.TryLoad(Utilities.GetBinaryStream("uImage-gzip"), true, out var image)); 14 | Assert.AreEqual(CompressionType.Gzip, image.Compression); 15 | 16 | CollectionAssert.AreEqual( 17 | Utilities.ReadWholeStream(Utilities.GetBinaryStream("uImage-gzip-extracted")), 18 | image.GetImageData()); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/NoteSection.cs: -------------------------------------------------------------------------------- 1 | using ELFSharp.Utilities; 2 | 3 | namespace ELFSharp.ELF.Sections 4 | { 5 | public sealed class NoteSection : Section, INoteSection where T : struct 6 | { 7 | private readonly NoteData data; 8 | 9 | internal NoteSection(SectionHeader header, SimpleEndianessAwareReader reader) : base(header, reader) 10 | { 11 | data = new NoteData(header.Offset, header.Size, reader); 12 | } 13 | 14 | public T NoteType => data.Type.To(); 15 | 16 | public string NoteName => data.Name; 17 | 18 | public byte[] Description => data.DescriptionBytes; 19 | 20 | public override string ToString() 21 | { 22 | return string.Format("{0}: {2}, Type={1}", Name, NoteType, Type); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/DynamicEntry.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.ELF.Sections 2 | { 3 | /// 4 | /// Dynamic table entries are made up of a 32 bit or 64 bit "tag" 5 | /// and a 32 bit or 64 bit union (val/pointer in 64 bit, val/pointer/offset in 32 bit). 6 | /// See LLVM elf.h file for the C/C++ version. 7 | /// 8 | public class DynamicEntry : IDynamicEntry 9 | { 10 | public DynamicEntry(T tagValue, T value) 11 | { 12 | Tag = (DynamicTag)tagValue.To(); 13 | Value = value; 14 | } 15 | 16 | public T Value { get; } 17 | 18 | public DynamicTag Tag { get; } 19 | 20 | public override string ToString() 21 | { 22 | return string.Format("{0} \t 0x{1:X}", Tag, Value); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Tests/MachO/UUIDTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using ELFSharp.MachO; 3 | using NUnit.Framework; 4 | 5 | namespace Tests.MachO 6 | { 7 | [TestFixture] 8 | public class UUIDTests 9 | { 10 | [Test] 11 | public void ReadsUUID() 12 | { 13 | var archs = MachOReader.LoadFat(Utilities.GetBinaryStream("3dengine_libretro_ios.dylib"), true); 14 | 15 | foreach (var arch in archs) 16 | { 17 | var ids = arch.GetCommandsOfType(); 18 | 19 | // a valid macho can't have 0 LC_UUIDs 20 | Assert.AreNotEqual(ids.Count(), 0); 21 | 22 | foreach (var id in ids) 23 | // for each arch the uuid must be specified 24 | Assert.AreNotEqual(id.ID, null); 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /ELFSharp/MachO/UUID.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using ELFSharp.Utilities; 5 | 6 | namespace ELFSharp.MachO 7 | { 8 | public class UUID : Command 9 | { 10 | internal UUID(SimpleEndianessAwareReader reader, Stream stream) : base(reader, stream) 11 | { 12 | ID = ReadUUID(); 13 | } 14 | 15 | public Guid ID { get; } 16 | 17 | private Guid ReadUUID() 18 | { 19 | var rawBytes = Reader.ReadBytes(16).ToArray(); 20 | 21 | // Deal here with UUID endianess. Switch scheme is 4(r)-2(r)-2(r)-8(o) 22 | // where r is reverse, o is original order. 23 | Array.Reverse(rawBytes, 0, 4); 24 | Array.Reverse(rawBytes, 4, 2); 25 | Array.Reverse(rawBytes, 6, 2); 26 | 27 | var guid = new Guid(rawBytes); 28 | return guid; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/IELF.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ELFSharp.ELF.Sections; 4 | using ELFSharp.ELF.Segments; 5 | 6 | namespace ELFSharp.ELF 7 | { 8 | public interface IELF : IDisposable 9 | { 10 | Endianess Endianess { get; } 11 | Class Class { get; } 12 | FileType Type { get; } 13 | Machine Machine { get; } 14 | bool HasSegmentHeader { get; } 15 | bool HasSectionHeader { get; } 16 | bool HasSectionsStringTable { get; } 17 | IReadOnlyList Segments { get; } 18 | IStringTable SectionsStringTable { get; } 19 | IReadOnlyList Sections { get; } 20 | IEnumerable GetSections() where T : ISection; 21 | bool TryGetSection(string name, out ISection section); 22 | ISection GetSection(string name); 23 | bool TryGetSection(int index, out ISection section); 24 | ISection GetSection(int index); 25 | } 26 | } -------------------------------------------------------------------------------- /Tests/ELF/ProgBitsSectionTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using ELFSharp.ELF; 3 | using ELFSharp.ELF.Sections; 4 | using NUnit.Framework; 5 | 6 | namespace Tests.ELF 7 | { 8 | [TestFixture] 9 | public class ProgBitsSectionTests 10 | { 11 | [Test] 12 | public void ShouldGetLoadAddress32() 13 | { 14 | var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 15 | var sectionsToLoad = elf.GetSections>().Where(x => x.LoadAddress != 0); 16 | Assert.AreEqual(13, sectionsToLoad.Count()); 17 | } 18 | 19 | [Test] 20 | public void ShouldGetLoadAddress64() 21 | { 22 | var elf = ELFReader.Load(Utilities.GetBinaryStream("hello64le"), true); 23 | var sectionsToLoad = elf.GetSections>().Where(x => x.LoadAddress != 0); 24 | Assert.AreEqual(12, sectionsToLoad.Count()); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Segments/INoteData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using System.IO; 3 | 4 | namespace ELFSharp.ELF.Segments 5 | { 6 | public interface INoteData 7 | { 8 | /// 9 | /// Owner of the note. 10 | /// 11 | string Name { get; } 12 | 13 | /// 14 | /// Data contents of the note. The format of this depends on the combination of the Name and Type properties and often 15 | /// corresponds to a struct. 16 | /// For example, see elf.h in the Linux kernel source tree. 17 | /// 18 | ReadOnlyCollection Description { get; } 19 | 20 | /// 21 | /// Data type 22 | /// 23 | ulong Type { get; } 24 | 25 | /// 26 | /// Returns the Description byte[] as a Stream 27 | /// 28 | /// 29 | Stream ToStream(); 30 | } 31 | } -------------------------------------------------------------------------------- /Tests/MachO/EntryPointTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using ELFSharp.MachO; 3 | using NUnit.Framework; 4 | 5 | namespace Tests.MachO 6 | { 7 | [TestFixture] 8 | public class EntryPointTests 9 | { 10 | [Test] 11 | public void ShouldFind32BitEntryPoint() 12 | { 13 | var machO = MachOReader.Load(Utilities.GetBinaryStream("simple-32-mach-o"), true); 14 | var entryPoint = machO.GetCommandsOfType().Single(); 15 | Assert.AreEqual(0xF60, entryPoint.Value); 16 | Assert.AreEqual(0, entryPoint.StackSize); 17 | } 18 | 19 | [Test] 20 | public void ShouldFind64BitEntryPoint() 21 | { 22 | var machO = MachOReader.Load(Utilities.GetBinaryStream("simple-mach-o"), true); 23 | var entryPoint = machO.GetCommandsOfType().Single(); 24 | Assert.AreEqual(0xF6B, entryPoint.Value); 25 | Assert.AreEqual(0, entryPoint.StackSize); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /Tests/MachO/FileHeaderTests.cs: -------------------------------------------------------------------------------- 1 | using ELFSharp.MachO; 2 | using NUnit.Framework; 3 | 4 | namespace Tests.MachO 5 | { 6 | [TestFixture] 7 | public class FileHeaderTests 8 | { 9 | [Test] 10 | public void ShouldLoadFileHeaderHasFlags() 11 | { 12 | var machO = MachOReader.Load(Utilities.GetBinaryStream("simple-mach-o"), true); 13 | var flags = machO.Flags; 14 | Assert.AreEqual(flags.HasFlag(HeaderFlags.NoUndefs), true); 15 | Assert.AreEqual(flags.HasFlag(HeaderFlags.DyldLink), true); 16 | Assert.AreEqual(flags.HasFlag(HeaderFlags.TwoLevel), true); 17 | Assert.AreEqual(flags.HasFlag(HeaderFlags.PIE), true); 18 | } 19 | 20 | [Test] 21 | public void ShouldLoadFileHasNoHeaderFlags() 22 | { 23 | var machO = MachOReader.Load(Utilities.GetBinaryStream("mach-o-dSYM-dwarf"), true); 24 | var flags = machO.Flags; 25 | Assert.AreEqual(flags, (HeaderFlags)0); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/ProgBitsSection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ELFSharp.Utilities; 3 | 4 | namespace ELFSharp.ELF.Sections 5 | { 6 | public sealed class ProgBitsSection : Section, IProgBitsSection where T : struct 7 | { 8 | private const int BufferSize = 10 * 1024; 9 | 10 | internal ProgBitsSection(SectionHeader header, SimpleEndianessAwareReader reader) : base(header, reader) 11 | { 12 | } 13 | 14 | 15 | public void WriteContents(byte[] destination, int offset, int length = 0) 16 | { 17 | SeekToSectionBeginning(); 18 | if (length == 0 || (ulong)length > Header.Size) length = Convert.ToInt32(Header.Size); 19 | var remaining = length; 20 | while (remaining > 0) 21 | { 22 | var buffer = Reader.ReadBytes(Math.Min(BufferSize, remaining)); 23 | buffer.CopyTo(destination, offset + (length - remaining)); 24 | remaining -= buffer.Length; 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /Tests/Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | Tests 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Tests/Utilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Force.Crc32; 4 | using NUnit.Framework; 5 | 6 | namespace Tests 7 | { 8 | [SetUpFixture] 9 | public class Utilities 10 | { 11 | private const string ResourcesPrefix = "Tests.Binaries."; 12 | 13 | public static Stream GetBinaryStream(string name) 14 | { 15 | var result = typeof(Utilities).Assembly.GetManifestResourceStream(ResourcesPrefix + name); 16 | if (result == null) throw new FileNotFoundException($"Could not find resource '{name}'."); 17 | 18 | return result; 19 | } 20 | 21 | public static uint ComputeCrc32(byte[] data) 22 | { 23 | var algorithm = new Crc32Algorithm(); 24 | var crc32AsBytes = algorithm.ComputeHash(data); 25 | return BitConverter.ToUInt32(crc32AsBytes, 0); 26 | } 27 | 28 | public static byte[] ReadWholeStream(Stream stream) 29 | { 30 | var memoryStream = new MemoryStream(); 31 | stream.CopyTo(memoryStream); 32 | return memoryStream.ToArray(); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /ELFSharp/MachO/FatArchiveReader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using ELFSharp.Utilities; 4 | 5 | namespace ELFSharp.MachO 6 | { 7 | internal static class FatArchiveReader 8 | { 9 | public static IEnumerable Enumerate(Stream stream, bool shouldOwnStream) 10 | { 11 | // Fat header is always big endian. 12 | var reader = new SimpleEndianessAwareReader(stream, Endianess.BigEndian, !shouldOwnStream); 13 | 14 | // We assume that fat magic has been already read. 15 | var machOCount = reader.ReadInt32(); 16 | var alreadyRead = 0; 17 | var fatEntriesBegin = stream.Position; 18 | 19 | while (alreadyRead < machOCount) 20 | { 21 | // We're only interested in offset and size. 22 | stream.Seek(fatEntriesBegin + 20 * alreadyRead + 8, SeekOrigin.Begin); 23 | var offset = reader.ReadInt32(); 24 | var size = reader.ReadInt32(); 25 | var substream = new SubStream(stream, offset, size); 26 | yield return MachOReader.Load(substream, false); 27 | 28 | alreadyRead++; 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ELFSharp 2 | 3 | This is ELFSharp, a C# library created by Konrad Kruczyński for reading binary files in ELF, UImage or Mach-O format. This is open source software, for more informations please visit [ELFSharp's webpage](https://elfsharp.it). 4 | 5 | ## Usage 6 | 7 | If you just want to start using it, grab the library from NuGet and look at [examples](https://elfsharp.it/examples.html). 8 | 9 | ## Contributors 10 | 11 | Contributors (in the order of the first contribution) 12 | * Konrad Kruczyński 13 | * Piotr Zierhoffer 14 | * Łukasz Kucharski 15 | * Bastian Eicher 16 | * Cameron 17 | * Frederik Carlier 18 | * Everett Maus 19 | * Fox 20 | * Reuben Olinsky 21 | * Connor Christie 22 | * Rollrat 23 | * Ulysses Wu 24 | * Cédric Luthi 25 | * Yong Yan 26 | * Filip Navara 27 | * Dedmen Miller 28 | * Jerker Olofsson 29 | * Murat Ocaktürk 30 | * Grivus 31 | * Alex E. 32 | * Pavel Vorozheykin 33 | 34 | ## License 35 | You can find license in the LICENSE file. 36 | 37 | This software uses ELF machine constants from the LLVM projects, whose license can be found in the LICENSE file as well. 38 | 39 | This project uses some test binaries from Jonathan Salwan's excellent collection located at https://github.com/JonathanSalwan/binary-samples These also use the MIT license. -------------------------------------------------------------------------------- /Tests/ELF/StringTableTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using ELFSharp.ELF; 3 | using NUnit.Framework; 4 | 5 | namespace Tests.ELF 6 | { 7 | [TestFixture] 8 | public class StringTableTests 9 | { 10 | [Test] 11 | public void ShouldFindAllStrings() 12 | { 13 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 14 | Assert.IsTrue(elf.HasSectionsStringTable, 15 | "Sections string table was not found in 32 bit ELF."); 16 | Assert.AreEqual(29, elf.SectionsStringTable.Strings.Count()); 17 | } 18 | 19 | [Test] 20 | public void ShouldFindAllStrings64() 21 | { 22 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello64le"), true); 23 | Assert.IsTrue(elf.HasSectionsStringTable, 24 | "Sections string table was not found in 64 bit ELF."); 25 | Assert.AreEqual(30, elf.SectionsStringTable.Strings.Count()); 26 | } 27 | 28 | [Test] 29 | public void ShouldFindAllStringsBigEndian() 30 | { 31 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("vmlinuxOpenRisc"), true); 32 | Assert.IsTrue( 33 | elf.HasSectionsStringTable, 34 | "Sections string table was not found in 32 bit big endian ELF." 35 | ); 36 | Assert.AreEqual(28, elf.SectionsStringTable.Strings.Count()); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /ELFSharp/MachO/Dylib.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using ELFSharp.Utilities; 5 | 6 | namespace ELFSharp.MachO 7 | { 8 | public abstract class Dylib : Command 9 | { 10 | internal Dylib(SimpleEndianessAwareReader reader, Stream stream, uint commandSize) : base(reader, stream) 11 | { 12 | var offset = reader.ReadUInt32(); 13 | var timestamp = reader.ReadInt32(); 14 | var currentVersion = reader.ReadUInt32(); 15 | var compatibilityVersion = reader.ReadUInt32(); 16 | Timestamp = DateTimeOffset.FromUnixTimeSeconds(timestamp).UtcDateTime; 17 | CurrentVersion = GetVersion(currentVersion); 18 | CompatibilityVersion = GetVersion(compatibilityVersion); 19 | Name = GetString(reader.ReadBytes((int)(commandSize - offset))); 20 | } 21 | 22 | public string Name { get; } 23 | public DateTime Timestamp { get; } 24 | public Version CurrentVersion { get; } 25 | public Version CompatibilityVersion { get; } 26 | 27 | private static Version GetVersion(uint version) 28 | { 29 | return new Version((int)(version >> 16), (int)((version >> 8) & 0xff), (int)(version & 0xff)); 30 | } 31 | 32 | private static string GetString(byte[] bytes) 33 | { 34 | var nullTerminatorIndex = Array.FindIndex(bytes, e => e == '\0'); 35 | return Encoding.ASCII.GetString(bytes, 0, nullTerminatorIndex >= 0 ? nullTerminatorIndex : bytes.Length); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /ELFSharp/ELFSharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | True 6 | sgKey.snk 7 | 2.17.3.0 8 | 2.0 9 | Konrad Kruczyński, Piotr Zierhoffer, Łukasz Kucharski, Bastian Eicher, Cameron, Everett Maus, Fox, Reuben Olinsky, Connor Christie, Rollrat, Ulysses Wu, Cédric Luthi, Yong Yan, Filip Navara, Dedmen Miller, Murat Ocaktürk, Grivus, Alex E., Pavel Vorozheykin 10 | Konrad Kruczyński 11 | http://elfsharp.it/ 12 | Fixed magic value for the x64 Big Endian Mach-O binaries. Detailed changelog available at: https://github.com/konrad-kruczynski/elfsharp/blob/master/CHANGELOG.md 13 | C# library for reading binary ELF, UImage, Mach-O files 14 | ELF UImage Mach-O 15 | ELFSharp 16 | C# library for reading binary ELF, UImage, Mach-O files 17 | ELFSharp 18 | icon.png 19 | MIT 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Tests/ELF/DynamicSectionTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using ELFSharp.ELF; 3 | using ELFSharp.ELF.Sections; 4 | using NUnit.Framework; 5 | 6 | namespace Tests.ELF 7 | { 8 | [TestFixture] 9 | public class DynamicSectionTests 10 | { 11 | [Test] 12 | public void ShouldReadDynamicSection32() 13 | { 14 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 15 | var dynamicSection = (IDynamicSection)elf.GetSection(".dynamic"); 16 | Assert.AreEqual(SectionType.Dynamic, dynamicSection.Type); 17 | } 18 | 19 | [Test] 20 | public void ShouldReadDynamicSection64() 21 | { 22 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello64le"), true); 23 | var dynamicSection = (IDynamicSection)elf.GetSection(".dynamic"); 24 | Assert.AreEqual(SectionType.Dynamic, dynamicSection.Type); 25 | } 26 | 27 | [Test] 28 | public void ShouldHaveCorrectDynamicEntryCount32() 29 | { 30 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 31 | var dynamicSection = (IDynamicSection)elf.GetSection(".dynamic"); 32 | Assert.AreEqual(25, dynamicSection.Entries.Count()); 33 | } 34 | 35 | [Test] 36 | public void ShouldHaveCorrectDynamicEntryCount64() 37 | { 38 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello64le"), true); 39 | var dynamicSection = (IDynamicSection)elf.GetSection(".dynamic"); 40 | Assert.AreEqual(29, dynamicSection.Entries.Count()); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /ELFSharp.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ELFSharp", "ELFSharp\ELFSharp.csproj", "{DD952741-16CD-4F36-B88D-5F03AD20676A}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{A2D02532-CD4F-45DB-B49A-207251D66D15}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Release|Any CPU = Release|Any CPU 11 | Debug|Any CPU = Debug|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {DD952741-16CD-4F36-B88D-5F03AD20676A}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {DD952741-16CD-4F36-B88D-5F03AD20676A}.Release|Any CPU.Build.0 = Release|Any CPU 16 | {DD952741-16CD-4F36-B88D-5F03AD20676A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {DD952741-16CD-4F36-B88D-5F03AD20676A}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {A2D02532-CD4F-45DB-B49A-207251D66D15}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {A2D02532-CD4F-45DB-B49A-207251D66D15}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {A2D02532-CD4F-45DB-B49A-207251D66D15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {A2D02532-CD4F-45DB-B49A-207251D66D15}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | EndGlobalSection 23 | GlobalSection(MonoDevelopProperties) = preSolution 24 | Policies = $0 25 | $0.TextStylePolicy = $1 26 | $1.inheritsSet = null 27 | $1.scope = text/x-csharp 28 | $0.CSharpFormattingPolicy = $2 29 | $2.SpaceAfterControlFlowStatementKeyword = False 30 | $2.scope = text/x-csharp 31 | EndGlobalSection 32 | EndGlobal 33 | -------------------------------------------------------------------------------- /Tests/ELF/SectionGettingTests.cs: -------------------------------------------------------------------------------- 1 | using ELFSharp.ELF; 2 | using ELFSharp.ELF.Sections; 3 | using NUnit.Framework; 4 | 5 | namespace Tests.ELF 6 | { 7 | [TestFixture] 8 | public class SectionGettingTests 9 | { 10 | [Test] 11 | public void ShouldGetSection() 12 | { 13 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("issue3"), true); 14 | elf.GetSection(".rodata"); 15 | } 16 | 17 | [Test] 18 | public void ShouldHandleNonExistingSection() 19 | { 20 | ISection section; 21 | Assert.IsFalse(ELFReader.Load(Utilities.GetBinaryStream("issue3"), true) 22 | .TryGetSection(".nonexisting", out section)); 23 | } 24 | 25 | [Test] 26 | public void ShouldHandleOutOfRangeSection() 27 | { 28 | ISection section; 29 | Assert.IsFalse(ELFReader.Load(Utilities.GetBinaryStream("issue3"), true).TryGetSection(28, out section)); 30 | } 31 | 32 | // Github issue no 60. 33 | [Test] 34 | public void SectionCountShouldBeAvailable() 35 | { 36 | var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 37 | Assert.AreEqual(29, elf.Sections.Count); 38 | } 39 | 40 | [Test] 41 | public void ShouldGetSectionContents() 42 | { 43 | var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 44 | var section = elf.GetSection(".text"); 45 | var sectionContents = section.GetContents(); 46 | 47 | Assert.AreEqual(0xE14DEBA1, Utilities.ComputeCrc32(sectionContents)); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /ELFSharp/MachO/Section.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace ELFSharp.MachO 5 | { 6 | [DebuggerDisplay("Section({segment.Name,nq},{Name,nq})")] 7 | public sealed class Section 8 | { 9 | private readonly Segment segment; 10 | 11 | public Section(string name, string segmentName, ulong address, ulong size, uint offset, uint alignExponent, 12 | uint relocOffset, uint numberOfReloc, uint flags, Segment segment) 13 | { 14 | Name = name; 15 | SegmentName = segmentName; 16 | Address = address; 17 | Size = size; 18 | Offset = offset; 19 | AlignExponent = alignExponent; 20 | RelocOffset = relocOffset; 21 | RelocCount = numberOfReloc; 22 | Flags = flags; 23 | this.segment = segment; 24 | } 25 | 26 | public string Name { get; private set; } 27 | public string SegmentName { get; private set; } 28 | public ulong Address { get; private set; } 29 | public ulong Size { get; } 30 | public uint Offset { get; } 31 | public uint AlignExponent { get; private set; } 32 | public uint RelocOffset { get; private set; } 33 | public uint RelocCount { get; private set; } 34 | public uint Flags { get; private set; } 35 | 36 | public byte[] GetData() 37 | { 38 | if (Offset < segment.FileOffset || Offset + Size > segment.FileOffset + segment.Size) return new byte[0]; 39 | var result = new byte[Size]; 40 | Array.Copy(segment.GetData(), (int)(Offset - segment.FileOffset), result, 0, (int)Size); 41 | return result; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Tests/MachO/SegmentTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text; 3 | using ELFSharp.MachO; 4 | using NUnit.Framework; 5 | 6 | namespace Tests.MachO 7 | { 8 | [TestFixture] 9 | public class SegmentTests 10 | { 11 | [Test] 12 | public void ShouldFindSectionWithProperData32() 13 | { 14 | var machO = MachOReader.Load(Utilities.GetBinaryStream("simple-32-mach-o"), true); 15 | var segment = machO.GetCommandsOfType().Single(x => x.Name == "__TEXT"); 16 | var data = segment.Sections.Single(x => x.Name == "__cstring").GetData(); 17 | Assert.AreEqual(Encoding.ASCII.GetBytes("Hello world!\n\0"), data); 18 | } 19 | 20 | [Test] 21 | public void ShouldFindSectionWithProperData64() 22 | { 23 | var machO = MachOReader.Load(Utilities.GetBinaryStream("simple-mach-o"), true); 24 | var segment = machO.GetCommandsOfType().Single(x => x.Name == "__TEXT"); 25 | var data = segment.Sections.Single(x => x.Name == "__cstring").GetData(); 26 | Assert.AreEqual(Encoding.ASCII.GetBytes("Hello world\0"), data); 27 | } 28 | 29 | [TestCase("__TEXT", "__text")] 30 | [TestCase("__DATA", "__bss")] 31 | public static void ShouldFindSectionWithDataAndNoDataArm64(string segmentName, string sectionName) 32 | { 33 | var machO = MachOReader.Load(Utilities.GetBinaryStream(@"3dengine_libretro_ios.dylib"), true); 34 | var segment = machO.GetCommandsOfType().Single(x => x.Name == "__TEXT"); 35 | var section = segment.Sections.Single(x => x.Name == "__text"); 36 | section.GetData(); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/Section.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using ELFSharp.Utilities; 4 | 5 | namespace ELFSharp.ELF.Sections 6 | { 7 | public class Section : ISection where T : struct 8 | { 9 | protected readonly SimpleEndianessAwareReader Reader; 10 | 11 | internal Section(SectionHeader header, SimpleEndianessAwareReader reader) 12 | { 13 | Header = header; 14 | Reader = reader; 15 | } 16 | 17 | public T RawFlags => Header.RawFlags.To(); 18 | 19 | public T LoadAddress => Header.LoadAddress.To(); 20 | 21 | public T Alignment => Header.Alignment.To(); 22 | 23 | public T EntrySize => Header.EntrySize.To(); 24 | 25 | public T Size => Header.Size.To(); 26 | 27 | public T Offset => Header.Offset.To(); 28 | 29 | internal SectionHeader Header { get; } 30 | 31 | public virtual byte[] GetContents() 32 | { 33 | if (Type == SectionType.NoBits) return Array.Empty(); 34 | 35 | Reader.BaseStream.Seek((long)Header.Offset, SeekOrigin.Begin); 36 | return Reader.ReadBytes(Convert.ToInt32(Header.Size)); 37 | } 38 | 39 | public string Name => Header.Name; 40 | 41 | public uint NameIndex => Header.NameIndex; 42 | 43 | public SectionType Type => Header.Type; 44 | 45 | public SectionFlags Flags => Header.Flags; 46 | 47 | public override string ToString() 48 | { 49 | return Header.ToString(); 50 | } 51 | 52 | protected void SeekToSectionBeginning() 53 | { 54 | Reader.BaseStream.Seek((long)Header.Offset, SeekOrigin.Begin); 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /Tests/StreamWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Tests 5 | { 6 | public class StreamWrapper : Stream 7 | { 8 | private readonly Action onDispose; 9 | private readonly Stream stream; 10 | 11 | public StreamWrapper(Stream stream, Action onDispose) 12 | { 13 | this.stream = stream; 14 | this.onDispose = onDispose; 15 | } 16 | 17 | public override bool CanRead => stream.CanRead; 18 | 19 | public override bool CanSeek => stream.CanSeek; 20 | 21 | public override bool CanWrite => stream.CanWrite; 22 | 23 | public override long Length => stream.Length; 24 | 25 | public override long Position 26 | { 27 | get => stream.Position; 28 | set => stream.Position = value; 29 | } 30 | 31 | public override void Flush() 32 | { 33 | stream.Flush(); 34 | } 35 | 36 | public override int Read(byte[] buffer, int offset, int count) 37 | { 38 | return stream.Read(buffer, offset, count); 39 | } 40 | 41 | public override long Seek(long offset, SeekOrigin origin) 42 | { 43 | return stream.Seek(offset, origin); 44 | } 45 | 46 | public override void SetLength(long value) 47 | { 48 | stream.SetLength(value); 49 | } 50 | 51 | public override void Write(byte[] buffer, int offset, int count) 52 | { 53 | stream.Write(buffer, offset, count); 54 | } 55 | 56 | protected override void Dispose(bool disposing) 57 | { 58 | onDispose(); 59 | base.Dispose(disposing); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /Tests/UImage/MultiFileImageTests.cs: -------------------------------------------------------------------------------- 1 | using ELFSharp.UImage; 2 | using NUnit.Framework; 3 | 4 | namespace Tests.UImage 5 | { 6 | [TestFixture] 7 | public sealed class MultiFileTests 8 | { 9 | [Test] 10 | public void ShouldOpenMultiFileImage() 11 | { 12 | Assert.AreEqual(UImageResult.OK, 13 | UImageReader.TryLoad(Utilities.GetBinaryStream("mImage"), true, out _)); 14 | } 15 | 16 | [Test] 17 | public void ShouldGetFirstImageFromMultiFileImage() 18 | { 19 | Assert.AreEqual(UImageResult.OK, 20 | UImageReader.TryLoad(Utilities.GetBinaryStream("mImage"), true, out var uImage)); 21 | 22 | var imageData = uImage.GetImageData(); 23 | CollectionAssert.AreEqual(new byte[] { 0x7F, (byte)'E', (byte)'L', (byte)'F' }, imageData[..4]); 24 | } 25 | 26 | [Test] 27 | public void ShouldGetFirstImageFromMultiFileImageOnZeroIndex() 28 | { 29 | Assert.AreEqual(UImageResult.OK, 30 | UImageReader.TryLoad(Utilities.GetBinaryStream("mImage"), true, out var uImage)); 31 | 32 | var imageData = uImage.GetImageData(0); 33 | CollectionAssert.AreEqual(new byte[] { 0x7F, (byte)'E', (byte)'L', (byte)'F' }, imageData[..4]); 34 | } 35 | 36 | [Test] 37 | public void ShouldGetSecondImageFromMultiFileImage() 38 | { 39 | Assert.AreEqual(UImageResult.OK, 40 | UImageReader.TryLoad(Utilities.GetBinaryStream("mImage"), true, out var uImage)); 41 | 42 | var imageData = uImage.GetImageData(1); 43 | CollectionAssert.AreEqual(new byte[] { 0xD0, 0x0D, 0xFE, 0xED }, imageData[..4]); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /Tests/ELF/SymbolTableTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using ELFSharp.ELF; 3 | using ELFSharp.ELF.Sections; 4 | using NUnit.Framework; 5 | 6 | namespace Tests.ELF 7 | { 8 | [TestFixture] 9 | public class SymbolTableTests 10 | { 11 | [Test] 12 | public void ShouldFindAllSymbols32() 13 | { 14 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 15 | var symtab = (ISymbolTable)elf.GetSection(".symtab"); 16 | Assert.AreEqual(64, symtab.Entries.Count()); 17 | } 18 | 19 | [Test] 20 | public void ShouldFindAllSymbols64() 21 | { 22 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello64le"), true); 23 | var symtab = (ISymbolTable)elf.GetSection(".symtab"); 24 | Assert.AreEqual(64, symtab.Entries.Count()); 25 | } 26 | 27 | // Github issue no 24 28 | [Test] 29 | public void ShouldHandleCorruptedNamesInDynSym() 30 | { 31 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("issue24.elf"), true); 32 | var dynamicSymbolSection = (SymbolTable)elf.GetSection(".dynsym"); 33 | dynamicSymbolSection.Entries.Any(x => x.Name == ""); 34 | } 35 | 36 | [Test] 37 | public void SymbolsShouldHaveCorrectVisibility() 38 | { 39 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 40 | var symtab = (ISymbolTable)elf.GetSection(".symtab"); 41 | var visibilites = symtab.Entries.GroupBy(x => x.Visibility).ToDictionary(x => x.Key, x => x.Count()); 42 | Assert.AreEqual(61, visibilites[SymbolVisibility.Default]); 43 | Assert.AreEqual(3, visibilites[SymbolVisibility.Hidden]); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /Tests/MachO/SymbolTableTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using ELFSharp.MachO; 4 | using NUnit.Framework; 5 | 6 | namespace Tests.MachO 7 | { 8 | [TestFixture] 9 | public class SymbolTableTests 10 | { 11 | [OneTimeSetUp] 12 | public void SetUp() 13 | { 14 | var machO = MachOReader.Load(Utilities.GetBinaryStream("3dengine_libretro_ios.dylib"), true); 15 | symbols = machO.GetCommandsOfType().Single().Symbols; 16 | } 17 | 18 | [Test] 19 | public void ShouldListSymbols() 20 | { 21 | var machO = MachOReader.Load(Utilities.GetBinaryStream("shared-32-mach-o"), true); 22 | var symbolTable = machO.GetCommandsOfType().Single(); 23 | CollectionAssert.IsSubsetOf(new[] { "_funkcja", "_inna_funkcja", "_jeszcze_inna_funkcja" }, 24 | symbolTable.Symbols.Select(x => x.Name).ToArray()); 25 | } 26 | 27 | // picked a few symbols with `nm -m 3dengine_libretro_ios.dylib` 28 | [TestCase("__ZL10first_init", "__DATA,__bss")] 29 | [TestCase("_video_cb", "__DATA,__common")] 30 | [TestCase("__ZL11cube_stride", "__DATA,__data")] 31 | [TestCase("GCC_except_table0", "__TEXT,__gcc_except_tab")] 32 | [TestCase("__Z14coll_detectionRN3glm6detail5tvec3IfEES3_", "__TEXT,__text")] 33 | [TestCase("__ZL11vertex_data", "__TEXT,__const")] 34 | [TestCase("__DefaultRuneLocale", null)] 35 | public void ShouldHaveCorrectSection(string symbolName, string section) 36 | { 37 | var symbol = symbols.First(e => e.Name == symbolName); 38 | Assert.AreEqual(section, 39 | symbol.Section == null ? null : $"{symbol.Section.SegmentName},{symbol.Section.Name}"); 40 | } 41 | 42 | private IEnumerable symbols; 43 | } 44 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/SymbolEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ELFSharp.ELF.Sections 4 | { 5 | public class SymbolEntry : ISymbolEntry where T : struct 6 | { 7 | private readonly ELF elf; 8 | 9 | public SymbolEntry(string name, T value, T size, SymbolVisibility visibility, 10 | SymbolBinding binding, SymbolType type, ELF elf, ushort sectionIdx) 11 | { 12 | Name = name; 13 | Value = value; 14 | Size = size; 15 | Binding = binding; 16 | Type = type; 17 | Visibility = visibility; 18 | this.elf = elf; 19 | PointedSectionIndex = sectionIdx; 20 | } 21 | 22 | public T Value { get; } 23 | 24 | public T Size { get; } 25 | 26 | public Section PointedSection => IsPointedIndexSpecial ? null : elf.GetSection(PointedSectionIndex); 27 | 28 | public SpecialSectionIndex SpecialPointedSectionIndex 29 | { 30 | get 31 | { 32 | if (IsPointedIndexSpecial) return (SpecialSectionIndex)PointedSectionIndex; 33 | throw new InvalidOperationException("Given pointed section index does not have special meaning."); 34 | } 35 | } 36 | 37 | public string Name { get; } 38 | 39 | public SymbolBinding Binding { get; } 40 | 41 | public SymbolType Type { get; } 42 | 43 | public SymbolVisibility Visibility { get; } 44 | 45 | public bool IsPointedIndexSpecial => Enum.IsDefined(typeof(SpecialSectionIndex), PointedSectionIndex); 46 | 47 | ISection ISymbolEntry.PointedSection => PointedSection; 48 | 49 | public ushort PointedSectionIndex { get; } 50 | 51 | public override string ToString() 52 | { 53 | return string.Format("[{3} {4} {0}: 0x{1:X}, size: {2}, section idx: {5}]", 54 | Name, Value, Size, Binding, Type, (SpecialSectionIndex)PointedSectionIndex); 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Segments/NoteSegment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ELFSharp.ELF.Sections; 4 | using ELFSharp.Utilities; 5 | 6 | namespace ELFSharp.ELF.Segments 7 | { 8 | public sealed class NoteSegment : Segment, INoteSegment 9 | { 10 | private readonly NoteData data; 11 | 12 | private readonly List notes = new List(); 13 | 14 | internal NoteSegment(long headerOffset, Class elfClass, SimpleEndianessAwareReader reader) 15 | : base(headerOffset, elfClass, reader) 16 | { 17 | var offset = (ulong)Offset; 18 | var fileSize = (ulong)FileSize; 19 | var remainingSize = fileSize; 20 | 21 | // Keep the first NoteData as a property for backwards compatibility 22 | data = new NoteData(offset, remainingSize, reader); 23 | notes.Add(data); 24 | 25 | offset += data.NoteFileSize; 26 | 27 | // Read all additional notes within the segment 28 | // Multiple notes are common in ELF core files 29 | if (data.NoteFileSize < remainingSize) 30 | { 31 | remainingSize -= data.NoteFileSize; 32 | 33 | while (remainingSize > NoteData.NoteDataHeaderSize) 34 | { 35 | var note = new NoteData(offset, remainingSize, reader); 36 | notes.Add(note); 37 | offset += note.NoteFileSize; 38 | if (note.NoteFileSize <= remainingSize) 39 | remainingSize -= note.NoteFileSize; 40 | else 41 | // File is damaged 42 | throw new IndexOutOfRangeException("NoteSegment internal note-data is out of bounds"); 43 | } 44 | } 45 | } 46 | 47 | public string NoteName => data.Name; 48 | 49 | public ulong NoteType => data.Type; 50 | 51 | public byte[] NoteDescription => data.DescriptionBytes; 52 | public IReadOnlyList Notes => notes.AsReadOnly(); 53 | } 54 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/DynamicSection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.ObjectModel; 3 | using System.Linq; 4 | using ELFSharp.Utilities; 5 | 6 | namespace ELFSharp.ELF.Sections 7 | { 8 | public sealed class DynamicSection : Section, IDynamicSection where T : struct 9 | { 10 | private readonly ELF elf; 11 | 12 | private List> entries; 13 | 14 | internal DynamicSection(SectionHeader header, SimpleEndianessAwareReader reader, ELF elf) : base(header, 15 | reader) 16 | { 17 | this.elf = elf; 18 | ReadEntries(); 19 | } 20 | 21 | public IEnumerable> Entries => new ReadOnlyCollection>(entries); 22 | 23 | IEnumerable IDynamicSection.Entries => entries; 24 | 25 | public override string ToString() 26 | { 27 | return string.Format("{0}: {2}, load @0x{4:X}, {5} entries", Name, NameIndex, Type, RawFlags, LoadAddress, 28 | Entries.Count()); 29 | } 30 | 31 | private void ReadEntries() 32 | { 33 | /// "Kind-of" Bug: 34 | /// So, this winds up with "extra" DT_NULL entries for some executables. The issue 35 | /// is basically that sometimes the .dynamic section's size (and # of entries) per the 36 | /// header is higher than the actual # of entries. The extra space gets filled with null 37 | /// entries in all of the ELF files I tested, so we shouldn't end up with any 'incorrect' entries 38 | /// here unless someone is messing with the ELF structure. 39 | 40 | SeekToSectionBeginning(); 41 | var entryCount = elf.Class == Class.Bit32 ? Header.Size / 8 : Header.Size / 16; 42 | entries = new List>(); 43 | 44 | for (ulong i = 0; i < entryCount; i++) 45 | if (elf.Class == Class.Bit32) 46 | entries.Add(new DynamicEntry(Reader.ReadUInt32().To(), Reader.ReadUInt32().To())); 47 | else if (elf.Class == Class.Bit64) 48 | entries.Add(new DynamicEntry(Reader.ReadUInt64().To(), Reader.ReadUInt64().To())); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /Tests/MachO/DylibTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using ELFSharp.MachO; 4 | using NUnit.Framework; 5 | 6 | namespace Tests.MachO 7 | { 8 | [TestFixture] 9 | public class DylibTests 10 | { 11 | [Test] 12 | public void ShouldFindIdDylib() 13 | { 14 | var machO = MachOReader.Load(Utilities.GetBinaryStream("3dengine_libretro_ios.dylib"), true); 15 | var idDylib = machO.GetCommandsOfType().Single(); 16 | 17 | Assert.AreEqual("3dengine_libretro_ios.dylib", idDylib.Name); 18 | Assert.AreEqual(new DateTime(1970, 1, 1, 0, 0, 1, 0, DateTimeKind.Utc), idDylib.Timestamp); 19 | Assert.AreEqual(new Version(0, 0, 0), idDylib.CurrentVersion); 20 | Assert.AreEqual(new Version(0, 0, 0), idDylib.CompatibilityVersion); 21 | } 22 | 23 | [Test] 24 | public void ShouldFindLoadDylibs() 25 | { 26 | var machO = MachOReader.Load(Utilities.GetBinaryStream("3dengine_libretro_ios.dylib"), true); 27 | var loadDylibs = machO.GetCommandsOfType().ToList(); 28 | 29 | Assert.AreEqual(3, loadDylibs.Count); 30 | 31 | Assert.AreEqual("/System/Library/Frameworks/OpenGLES.framework/OpenGLES", loadDylibs[0].Name); 32 | Assert.AreEqual(new DateTime(1970, 1, 1, 0, 0, 2, 0, DateTimeKind.Utc), loadDylibs[0].Timestamp); 33 | Assert.AreEqual(new Version(1, 0, 0), loadDylibs[0].CurrentVersion); 34 | Assert.AreEqual(new Version(1, 0, 0), loadDylibs[0].CompatibilityVersion); 35 | 36 | Assert.AreEqual("/usr/lib/libSystem.B.dylib", loadDylibs[1].Name); 37 | Assert.AreEqual(new DateTime(1970, 1, 1, 0, 0, 2, 0, DateTimeKind.Utc), loadDylibs[1].Timestamp); 38 | Assert.AreEqual(new Version(1281, 0, 0), loadDylibs[1].CurrentVersion); 39 | Assert.AreEqual(new Version(1, 0, 0), loadDylibs[1].CompatibilityVersion); 40 | 41 | Assert.AreEqual("/usr/lib/libc++.1.dylib", loadDylibs[2].Name); 42 | Assert.AreEqual(new DateTime(1970, 1, 1, 0, 0, 2, 0, DateTimeKind.Utc), loadDylibs[2].Timestamp); 43 | Assert.AreEqual(new Version(800, 7, 0), loadDylibs[2].CurrentVersion); 44 | Assert.AreEqual(new Version(1, 0, 0), loadDylibs[2].CompatibilityVersion); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # ELFSharp Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [2.17.3] - 2023-10-13 9 | ### Fixed 10 | - MachO: fixed magic value for the x64 Big Endian binaries. 11 | 12 | ## [2.17.2] - 2023-06-09 13 | ### Fixed 14 | - MachO: fix for reading LC_UUID command. 15 | 16 | ## [2.17.1] - 2023-04-25 17 | ### Fixed 18 | - ELF: fix for `SHN_LORESERVE` edge case. 19 | 20 | ## [2.17] - 2023-04-03 21 | ### Added 22 | - MachO: Added a command to parse LC_UUID and ability to get the UUID. 23 | 24 | ## [2.16.1] - 2023-01-11 25 | ### Fixed 26 | - ELF: `ELFReader.TryLoad` now closes the underlying stream if it owns it and loading fails. 27 | 28 | ## [2.16] - 2022-12-04 29 | ### Added 30 | - Support for multi-file uImage binaries. 31 | 32 | ## [2.15.2] - 2022-11-14 33 | ### Fixed 34 | - ELF: case when section number is larger than `SHN_LORESERVE` and string table is present is now handled properly. 35 | 36 | ## [2.15.1] - 2022-11-12 37 | ### Fixed 38 | - ELF: case when section number is larger than `SHN_LORESERVE` is now handled properly. 39 | 40 | ## [2.15] - 2022-04-11 41 | ### Added 42 | - ELF: multiple note data are now supported in `NoteSegment`. 43 | 44 | ## [2.14] - 2022-04-08 45 | ### Added 46 | - MachO: Symbol now knows to which section it belongs. 47 | - MachO: Added constraint to GetCommandsOfType. 48 | 49 | ### Fixed 50 | - Parameter shouldOwnStream was incorrectly implemented (as its opposite, actually). 51 | 52 | ## [2.13.3] - 2022-02-17 53 | ### Maintenance 54 | - License keyword is now included in the package. 55 | 56 | ## [2.13.2] - 2021-11-10 57 | ### Fixed 58 | - NOBITS ELF file section is now correctly read. 59 | 60 | ## [2.13.1] - 2021-11-02 61 | ### Fixed 62 | - 64-bit big endian Mach-O binaries were treated as 32-bit. This is now fixed. 63 | 64 | ## [2.13] - 2021-07-20 65 | ### Added 66 | - Mach-O file header flags. 67 | 68 | ## [2.12.1] - 2021-06-12 69 | ### Fixed 70 | - InvalidOperationException when parsing intermediate object file. 71 | 72 | ## [2.12] - 2020-12-18 73 | ### Added 74 | - Support for MachO commands: 75 | + LC_ID_DYLIB 76 | + LC_LOAD_DYLIB 77 | + LC_LOAD_WEAK_DYLIB 78 | + LC_REEXPORT_DYLIB 79 | -------------------------------------------------------------------------------- /ELFSharp/Utilities/SimpleEndianessAwareReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | 5 | namespace ELFSharp.Utilities 6 | { 7 | public sealed class SimpleEndianessAwareReader : IDisposable 8 | { 9 | private readonly bool beNonClosing; 10 | 11 | private readonly bool needsAdjusting; 12 | 13 | public SimpleEndianessAwareReader(Stream stream, Endianess endianess, bool beNonClosing = false) 14 | { 15 | this.beNonClosing = beNonClosing; 16 | this.BaseStream = stream; 17 | needsAdjusting = (endianess == Endianess.LittleEndian) ^ BitConverter.IsLittleEndian; 18 | } 19 | 20 | public Stream BaseStream { get; } 21 | 22 | public void Dispose() 23 | { 24 | if (beNonClosing) return; 25 | BaseStream.Dispose(); 26 | } 27 | 28 | public byte[] ReadBytes(int count) 29 | { 30 | return BaseStream.ReadBytesOrThrow(count); 31 | } 32 | 33 | public byte ReadByte() 34 | { 35 | var result = BaseStream.ReadByte(); 36 | if (result == -1) throw new EndOfStreamException("End of stream reached while trying to read one byte."); 37 | return (byte)result; 38 | } 39 | 40 | public short ReadInt16() 41 | { 42 | var value = BitConverter.ToInt16(ReadBytes(2), 0); 43 | if (needsAdjusting) value = IPAddress.NetworkToHostOrder(value); 44 | return value; 45 | } 46 | 47 | public ushort ReadUInt16() 48 | { 49 | return (ushort)ReadInt16(); 50 | } 51 | 52 | public int ReadInt32() 53 | { 54 | var value = BitConverter.ToInt32(ReadBytes(4), 0); 55 | if (needsAdjusting) value = IPAddress.NetworkToHostOrder(value); 56 | return value; 57 | } 58 | 59 | public uint ReadUInt32() 60 | { 61 | return (uint)ReadInt32(); 62 | } 63 | 64 | public long ReadInt64() 65 | { 66 | var value = BitConverter.ToInt64(ReadBytes(8), 0); 67 | if (needsAdjusting) value = IPAddress.NetworkToHostOrder(value); 68 | return value; 69 | } 70 | 71 | public ulong ReadUInt64() 72 | { 73 | return (ulong)ReadInt64(); 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/SymbolTable.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.ObjectModel; 3 | using ELFSharp.Utilities; 4 | 5 | namespace ELFSharp.ELF.Sections 6 | { 7 | public sealed class SymbolTable : Section, ISymbolTable where T : struct 8 | { 9 | private readonly ELF elf; 10 | private readonly IStringTable table; 11 | 12 | private List> entries; 13 | 14 | internal SymbolTable(SectionHeader header, SimpleEndianessAwareReader Reader, IStringTable table, ELF elf) : 15 | base(header, Reader) 16 | { 17 | this.table = table; 18 | this.elf = elf; 19 | ReadSymbols(); 20 | } 21 | 22 | public IEnumerable> Entries => new ReadOnlyCollection>(entries); 23 | 24 | IEnumerable ISymbolTable.Entries => Entries; 25 | 26 | private void ReadSymbols() 27 | { 28 | SeekToSectionBeginning(); 29 | entries = new List>(); 30 | var adder = (ulong)(elf.Class == Class.Bit32 ? Consts.SymbolEntrySize32 : Consts.SymbolEntrySize64); 31 | for (var i = 0UL; i < Header.Size; i += adder) 32 | { 33 | var value = 0UL; 34 | var size = 0UL; 35 | var nameIdx = Reader.ReadUInt32(); 36 | 37 | if (elf.Class == Class.Bit32) 38 | { 39 | value = Reader.ReadUInt32(); 40 | size = Reader.ReadUInt32(); 41 | } 42 | 43 | var info = Reader.ReadByte(); 44 | var other = Reader.ReadByte(); 45 | var visibility = (SymbolVisibility)(other & 3); // Only three lowest bits are meaningful. 46 | var sectionIdx = Reader.ReadUInt16(); 47 | 48 | if (elf.Class == Class.Bit64) 49 | { 50 | value = Reader.ReadUInt64(); 51 | size = Reader.ReadUInt64(); 52 | } 53 | 54 | var name = table == null ? "" : table[nameIdx]; 55 | var binding = (SymbolBinding)(info >> 4); 56 | var type = (SymbolType)(info & 0x0F); 57 | entries.Add(new SymbolEntry(name, value.To(), size.To(), visibility, binding, type, elf, 58 | sectionIdx)); 59 | } 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/SectionHeader.cs: -------------------------------------------------------------------------------- 1 | using ELFSharp.Utilities; 2 | 3 | namespace ELFSharp.ELF.Sections 4 | { 5 | internal sealed class SectionHeader 6 | { 7 | private readonly Class elfClass; 8 | 9 | private readonly SimpleEndianessAwareReader reader; 10 | 11 | private readonly IStringTable table; 12 | 13 | // TODO: make elf consts file with things like SHT_LOUSER 14 | internal SectionHeader(SimpleEndianessAwareReader reader, Class elfClass, IStringTable table = null) 15 | { 16 | this.reader = reader; 17 | this.table = table; 18 | this.elfClass = elfClass; 19 | ReadSectionHeader(); 20 | } 21 | 22 | internal string Name { get; private set; } 23 | internal uint NameIndex { get; private set; } 24 | internal SectionType Type { get; private set; } 25 | internal SectionFlags Flags { get; private set; } 26 | internal ulong RawFlags { get; private set; } 27 | internal ulong LoadAddress { get; private set; } 28 | internal ulong Alignment { get; private set; } 29 | internal ulong EntrySize { get; private set; } 30 | internal ulong Size { get; private set; } 31 | internal ulong Offset { get; private set; } 32 | internal uint Link { get; private set; } 33 | internal uint Info { get; private set; } 34 | 35 | public override string ToString() 36 | { 37 | return string.Format("{0}: {2}, load @0x{4:X}, {5} bytes long", Name, NameIndex, Type, RawFlags, 38 | LoadAddress, Size); 39 | } 40 | 41 | private void ReadSectionHeader() 42 | { 43 | NameIndex = reader.ReadUInt32(); 44 | if (table != null) Name = table[NameIndex]; 45 | Type = (SectionType)reader.ReadUInt32(); 46 | RawFlags = ReadAddress(); 47 | Flags = unchecked((SectionFlags)RawFlags); 48 | LoadAddress = ReadAddress(); 49 | Offset = ReadOffset(); 50 | Size = ReadOffset(); 51 | Link = reader.ReadUInt32(); 52 | Info = reader.ReadUInt32(); 53 | Alignment = ReadAddress(); 54 | EntrySize = ReadAddress(); 55 | } 56 | 57 | private ulong ReadAddress() 58 | { 59 | return elfClass == Class.Bit32 ? reader.ReadUInt32() : reader.ReadUInt64(); 60 | } 61 | 62 | private ulong ReadOffset() 63 | { 64 | return elfClass == Class.Bit32 ? reader.ReadUInt32() : reader.ReadUInt64(); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/StringTable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Text; 5 | using ELFSharp.Utilities; 6 | 7 | namespace ELFSharp.ELF.Sections 8 | { 9 | public sealed class StringTable : Section, IStringTable where T : struct 10 | { 11 | private readonly byte[] stringBlob; 12 | 13 | private readonly Dictionary stringCache; 14 | private bool cachePopulated; 15 | 16 | internal StringTable(SectionHeader header, SimpleEndianessAwareReader reader) : base(header, reader) 17 | { 18 | stringCache = new Dictionary 19 | { 20 | { 0, string.Empty } 21 | }; 22 | stringBlob = ReadStringData(); 23 | } 24 | 25 | public IEnumerable Strings 26 | { 27 | get 28 | { 29 | if (!cachePopulated) PrepopulateCache(); 30 | return stringCache.Values; 31 | } 32 | } 33 | 34 | public string this[long index] 35 | { 36 | get 37 | { 38 | if (stringCache.TryGetValue(index, out var result)) return result; 39 | return HandleUnexpectedIndex(index); 40 | } 41 | } 42 | 43 | private string HandleUnexpectedIndex(long index) 44 | { 45 | var stringStart = (int)index; 46 | for (var i = stringStart; i < stringBlob.Length; ++i) 47 | if (stringBlob[i] == 0) 48 | { 49 | var str = Encoding.UTF8.GetString(stringBlob, stringStart, i - stringStart); 50 | stringCache.Add(stringStart, str); 51 | return str; 52 | } 53 | 54 | throw new IndexOutOfRangeException(); 55 | } 56 | 57 | private void PrepopulateCache() 58 | { 59 | cachePopulated = true; 60 | 61 | var stringStart = 1; 62 | for (var i = 1; i < stringBlob.Length; ++i) 63 | if (stringBlob[i] == 0) 64 | { 65 | if (!stringCache.ContainsKey(stringStart)) 66 | stringCache.Add(stringStart, Encoding.UTF8.GetString(stringBlob, stringStart, i - stringStart)); 67 | stringStart = i + 1; 68 | } 69 | } 70 | 71 | private byte[] ReadStringData() 72 | { 73 | SeekToSectionBeginning(); 74 | var blob = Reader.ReadBytes((int)Header.Size); 75 | Debug.Assert(blob.Length == 0 || (blob[0] == 0 && blob[blob.Length - 1] == 0), 76 | "First and last bytes must be the null character (except for empty string tables)"); 77 | return blob; 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /ELFSharp/MachO/SymbolTable.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Text; 5 | using ELFSharp.Utilities; 6 | 7 | namespace ELFSharp.MachO 8 | { 9 | public class SymbolTable : Command 10 | { 11 | private readonly bool is64; 12 | 13 | private Symbol[] symbols; 14 | 15 | public SymbolTable(SimpleEndianessAwareReader reader, Stream stream, bool is64, IReadOnlyList
sections) 16 | : base(reader, stream) 17 | { 18 | this.is64 = is64; 19 | ReadSymbols(sections); 20 | } 21 | 22 | public IEnumerable Symbols 23 | { 24 | get { return symbols.Select(x => x); } 25 | } 26 | 27 | private void ReadSymbols(IReadOnlyList
sections) 28 | { 29 | var symbolTableOffset = Reader.ReadInt32(); 30 | var numberOfSymbols = Reader.ReadInt32(); 31 | symbols = new Symbol[numberOfSymbols]; 32 | var stringTableOffset = Reader.ReadInt32(); 33 | Reader.ReadInt32(); // string table size 34 | 35 | var streamPosition = Stream.Position; 36 | Stream.Seek(symbolTableOffset, SeekOrigin.Begin); 37 | 38 | try 39 | { 40 | for (var i = 0; i < numberOfSymbols; i++) 41 | { 42 | var nameOffset = Reader.ReadInt32(); 43 | var name = ReadStringFromOffset(stringTableOffset + nameOffset); 44 | var type = Reader.ReadByte(); 45 | var sect = Reader.ReadByte(); 46 | var desc = Reader.ReadInt16(); 47 | var value = is64 ? Reader.ReadInt64() : Reader.ReadInt32(); 48 | var symbol = new Symbol(name, value, 49 | sect > 0 && sect <= sections.Count ? sections[sect - 1] : null); 50 | symbols[i] = symbol; 51 | } 52 | } 53 | finally 54 | { 55 | Stream.Position = streamPosition; 56 | } 57 | } 58 | 59 | private string ReadStringFromOffset(int offset) 60 | { 61 | var streamPosition = Stream.Position; 62 | Stream.Seek(offset, SeekOrigin.Begin); 63 | try 64 | { 65 | var asBytes = new List(); 66 | int readByte; 67 | while ((readByte = Stream.ReadByte()) != 0) 68 | { 69 | if (readByte == -1) 70 | throw new EndOfStreamException("Premature end of the stream while reading string."); 71 | asBytes.Add((byte)readByte); 72 | } 73 | 74 | return Encoding.UTF8.GetString(asBytes.ToArray()); 75 | } 76 | finally 77 | { 78 | Stream.Position = streamPosition; 79 | } 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /Tests/UImage/SimpleTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ELFSharp.UImage; 3 | using NUnit.Framework; 4 | 5 | namespace Tests.UImage 6 | { 7 | [TestFixture] 8 | public class SimpleTests 9 | { 10 | [Test] 11 | public void ShouldOpenUImage() 12 | { 13 | Assert.AreEqual(UImageResult.OK, 14 | UImageReader.TryLoad(Utilities.GetBinaryStream("uImage-panda"), true, out _)); 15 | } 16 | 17 | [Test] 18 | public void ShouldNotOpenNotUImageFile() 19 | { 20 | // not elf, nor uImage 21 | Assert.AreEqual(UImageResult.NotUImage, 22 | UImageReader.TryLoad(Utilities.GetBinaryStream("notelf"), true, out _)); 23 | } 24 | 25 | [Test] 26 | public void ShouldProperlyReadHeader() 27 | { 28 | var uImage = UImageReader.Load(Utilities.GetBinaryStream("uImage-panda"), true); 29 | Assert.AreEqual(3120712, uImage.Size); 30 | Assert.AreEqual(0x80008000, uImage.EntryPoint); 31 | Assert.AreEqual(0x80008000, uImage.LoadAddress); 32 | Assert.AreEqual(0x6C77B32E, uImage.CRC); 33 | Assert.AreEqual("Linux-3.2.0", uImage.Name); 34 | } 35 | 36 | [Test] 37 | public void ShouldProperlyReadTimestamp() 38 | { 39 | var uImage = UImageReader.Load(Utilities.GetBinaryStream("uImage-panda"), true); 40 | Assert.AreEqual(new DateTime(2012, 4, 10, 19, 11, 06, DateTimeKind.Utc).ToLocalTime(), uImage.Timestamp); 41 | } 42 | 43 | [Test] 44 | public void ShouldFailOnImageWithWrongChecksum() 45 | { 46 | Assert.AreEqual(UImageResult.BadChecksum, 47 | UImageReader.TryLoad(Utilities.GetBinaryStream("uImage-panda-wrng-cksm"), true, out _)); 48 | } 49 | 50 | [Test] 51 | public void ShouldFindCorrectImageType() 52 | { 53 | var uImage = UImageReader.Load(Utilities.GetBinaryStream("uImage-panda"), true); 54 | Assert.AreEqual(ImageType.Kernel, uImage.Type); 55 | } 56 | 57 | [Test] 58 | public void ShouldExtractCorrectImage() 59 | { 60 | var extracted = Utilities.ReadWholeStream(Utilities.GetBinaryStream("vexpress-image-extracted")); 61 | 62 | CollectionAssert.AreEqual(extracted, 63 | UImageReader.Load(Utilities.GetBinaryStream("uImage-vexpress"), true).GetImageData()); 64 | } 65 | 66 | [Test] 67 | public void ShouldGetProperOSValue() 68 | { 69 | var uImage = UImageReader.Load(Utilities.GetBinaryStream("uImage-vexpress"), true); 70 | Assert.AreEqual(OS.Linux, uImage.OperatingSystem); 71 | } 72 | 73 | [Test] 74 | public void ShouldGetProperArchitecture() 75 | { 76 | var uImage = UImageReader.Load(Utilities.GetBinaryStream("uImage-vexpress"), true); 77 | Assert.AreEqual(Architecture.ARM, uImage.Architecture); 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Konrad Kruczyński and other contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | This software uses ELF machine constants from the LLVM projects, whose license 23 | is provided below: 24 | 25 | ============================================================================== 26 | LLVM Release License 27 | ============================================================================== 28 | University of Illinois/NCSA 29 | Open Source License 30 | 31 | Copyright (c) 2003-2010 University of Illinois at Urbana-Champaign. 32 | All rights reserved. 33 | 34 | Developed by: 35 | 36 | LLVM Team 37 | 38 | University of Illinois at Urbana-Champaign 39 | 40 | http://llvm.org 41 | 42 | Permission is hereby granted, free of charge, to any person obtaining a copy of 43 | this software and associated documentation files (the "Software"), to deal with 44 | the Software without restriction, including without limitation the rights to 45 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 46 | of the Software, and to permit persons to whom the Software is furnished to do 47 | so, subject to the following conditions: 48 | 49 | * Redistributions of source code must retain the above copyright notice, 50 | this list of conditions and the following disclaimers. 51 | 52 | * Redistributions in binary form must reproduce the above copyright notice, 53 | this list of conditions and the following disclaimers in the 54 | documentation and/or other materials provided with the distribution. 55 | 56 | * Neither the names of the LLVM Team, University of Illinois at 57 | Urbana-Champaign, nor the names of its contributors may be used to 58 | endorse or promote products derived from this Software without specific 59 | prior written permission. 60 | 61 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 62 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 63 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 64 | CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 65 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 66 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE 67 | SOFTWARE. 68 | -------------------------------------------------------------------------------- /Tests/ELF/SectionHeadersParsingTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using ELFSharp.ELF; 3 | using ELFSharp.ELF.Sections; 4 | using NUnit.Framework; 5 | 6 | namespace Tests.ELF 7 | { 8 | [TestFixture] 9 | public class SectionHeadersParsingTests 10 | { 11 | [Test] 12 | public void ShouldFindProperNumberOfSections32() 13 | { 14 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 15 | Assert.AreEqual(29, elf.Sections.Count()); 16 | } 17 | 18 | [Test] 19 | public void ShouldFindProperNumberOfSections64() 20 | { 21 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello64le"), true); 22 | Assert.AreEqual(30, elf.Sections.Count()); 23 | } 24 | 25 | [Test] 26 | public void ShouldFindProperAlignment32() 27 | { 28 | var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 29 | var header = elf.Sections.First(x => x.Name == ".init"); 30 | Assert.AreEqual(4, header.Alignment); 31 | } 32 | 33 | [Test] 34 | public void ShouldFindProperAlignment64() 35 | { 36 | var elf = ELFReader.Load(Utilities.GetBinaryStream("hello64le"), true); 37 | var header = elf.Sections.First(x => x.Name == ".text"); 38 | Assert.AreEqual(16, header.Alignment); 39 | } 40 | 41 | [Test] 42 | public void ShouldFindProperEntrySize32() 43 | { 44 | var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 45 | var header = elf.Sections.First(x => x.Name == ".dynsym"); 46 | Assert.AreEqual(0x10, header.EntrySize); 47 | } 48 | 49 | [Test] 50 | public void ShouldFindProperEntrySize64() 51 | { 52 | var elf = ELFReader.Load(Utilities.GetBinaryStream("hello64le"), true); 53 | var header = elf.Sections.First(x => x.Name == ".dynsym"); 54 | Assert.AreEqual(0x18, header.EntrySize); 55 | } 56 | 57 | [Test] 58 | public void ShouldFindAllNotes32() 59 | { 60 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 61 | var notes = elf.GetSections(); 62 | Assert.AreEqual(2, notes.Count()); 63 | } 64 | 65 | [Test] 66 | public void ShouldFindAllNotes64() 67 | { 68 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello64le"), true); 69 | var notes = elf.GetSections(); 70 | Assert.AreEqual(2, notes.Count()); 71 | } 72 | 73 | [Test] 74 | public void ShouldFindProperOffset() 75 | { 76 | var elf = ELFReader.Load(Utilities.GetBinaryStream("hello64le"), true); 77 | var section = elf.Sections.First(x => x.Name == ".strtab"); 78 | Assert.AreEqual(0x17A0, section.Offset); 79 | } 80 | 81 | [Test] 82 | public void ShouldFindSizeInFirstEntry() 83 | { 84 | var elf = ELFReader.Load(Utilities.GetBinaryStream("large-elf.o"), true); 85 | Assert.AreEqual(68302, elf.Sections.Count()); 86 | Assert.AreEqual(0x10ace, elf.Sections[0].Size); 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /ELFSharp/MachO/MachOReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace ELFSharp.MachO 8 | { 9 | public static class MachOReader 10 | { 11 | private const uint FatMagic = 0xBEBAFECA; 12 | 13 | private const string FatArchiveErrorMessage = 14 | "Given file is a fat archive, contains more than one MachO binary. Use (Try)LoadFat to handle it."; 15 | 16 | private const string NotMachOErrorMessage = "Given file is not a Mach-O file."; 17 | 18 | private static readonly IReadOnlyDictionary MagicToMachOType = 19 | new Dictionary 20 | { 21 | { 0xFEEDFACE, (false, Endianess.LittleEndian) }, 22 | { 0xFEEDFACF, (true, Endianess.LittleEndian) }, 23 | { 0xCEFAEDFE, (false, Endianess.BigEndian) }, 24 | { 0xCFFAEDFE, (true, Endianess.BigEndian) } 25 | }; 26 | 27 | public static MachO Load(string fileName) 28 | { 29 | return Load(File.OpenRead(fileName), true); 30 | } 31 | 32 | public static MachO Load(Stream stream, bool shouldOwnStream) 33 | { 34 | return TryLoad(stream, shouldOwnStream, out var result) switch 35 | { 36 | MachOResult.OK => result, 37 | MachOResult.NotMachO => throw new InvalidOperationException(NotMachOErrorMessage), 38 | MachOResult.FatMachO => throw new InvalidOperationException(FatArchiveErrorMessage), 39 | _ => throw new ArgumentOutOfRangeException() 40 | }; 41 | } 42 | 43 | public static IReadOnlyList LoadFat(Stream stream, bool shouldOwnStream) 44 | { 45 | var result = TryLoadFat(stream, shouldOwnStream, out var machOs); 46 | if (result == MachOResult.OK || result == MachOResult.FatMachO) return machOs; 47 | 48 | throw new InvalidOperationException(NotMachOErrorMessage); 49 | } 50 | 51 | public static MachOResult TryLoad(string fileName, out MachO machO) 52 | { 53 | return TryLoad(File.OpenRead(fileName), true, out machO); 54 | } 55 | 56 | public static MachOResult TryLoad(Stream stream, bool shouldOwnStream, out MachO machO) 57 | { 58 | var result = TryLoadFat(stream, shouldOwnStream, out var machOs); 59 | 60 | if (result == MachOResult.OK) 61 | machO = machOs.SingleOrDefault(); 62 | else 63 | machO = null; 64 | 65 | return result; 66 | } 67 | 68 | public static MachOResult TryLoadFat(Stream stream, bool shouldOwnStream, out IReadOnlyList machOs) 69 | { 70 | machOs = null; 71 | 72 | using var reader = new BinaryReader(stream, Encoding.UTF8, true); 73 | var magic = reader.ReadUInt32(); 74 | 75 | if (magic == FatMagic) 76 | { 77 | machOs = FatArchiveReader.Enumerate(stream, shouldOwnStream).ToArray(); 78 | return MachOResult.FatMachO; 79 | } 80 | 81 | if (!MagicToMachOType.TryGetValue(magic, out var machOType)) return MachOResult.NotMachO; 82 | 83 | var machO = new MachO(stream, machOType.Is64Bit, machOType.Endianess, shouldOwnStream); 84 | machOs = new[] { machO }; 85 | return MachOResult.OK; 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /Tests/ELF/NoteSectionTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using ELFSharp.ELF; 3 | using ELFSharp.ELF.Sections; 4 | using ELFSharp.ELF.Segments; 5 | using NUnit.Framework; 6 | 7 | namespace Tests.ELF 8 | { 9 | [TestFixture] 10 | public class NoteSectionTests 11 | { 12 | [Test] 13 | public void ShouldReadNoteName32() 14 | { 15 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 16 | var noteSection = (INoteSection)elf.GetSection(".note.ABI-tag"); 17 | Assert.AreEqual("GNU", noteSection.NoteName); 18 | } 19 | 20 | [Test] 21 | public void ShouldReadNoteName64() 22 | { 23 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello64le"), true); 24 | var noteSection = (INoteSection)elf.GetSection(".note.ABI-tag"); 25 | Assert.AreEqual("GNU", noteSection.NoteName); 26 | } 27 | 28 | [Test] 29 | public void ShouldReadNoteType32() 30 | { 31 | var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 32 | var noteSection = (NoteSection)elf.GetSection(".note.ABI-tag"); 33 | Assert.AreEqual(1, noteSection.NoteType); 34 | } 35 | 36 | [Test] 37 | public void ShouldReadNoteType64() 38 | { 39 | var elf = ELFReader.Load(Utilities.GetBinaryStream("hello64le"), true); 40 | var noteSection = (NoteSection)elf.GetSection(".note.ABI-tag"); 41 | Assert.AreEqual(1, noteSection.NoteType); 42 | } 43 | 44 | [Test] 45 | public void ShouldNotThrowOnCustomNote() 46 | { 47 | // Not all notes conform to the expected format that the NoteSection 48 | // class uses; this test validates that we can successfully parse a 49 | // binary containing such a section and retrieve the note, all without 50 | // throwing an exception. 51 | const string sectionName = ".note.custom"; 52 | var elf = ELFReader.Load(Utilities.GetBinaryStream("custom-note"), true); 53 | var noteSection = (NoteSection)elf.GetSection(sectionName); 54 | Assert.AreEqual(sectionName, noteSection.Name); 55 | } 56 | 57 | 58 | [Test] 59 | public void ShouldReadMultipleNotesWithinNoteSection() 60 | { 61 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("elf32-core-adbd_3124"), true); 62 | var noteSegment = elf.Segments.Where(x => x.Type == SegmentType.Note).First() as INoteSegment; 63 | Assert.AreEqual(22, noteSegment.Notes.Count); 64 | } 65 | 66 | [Test] 67 | public void ShouldReadMultipleNoteDataName() 68 | { 69 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("elf32-core-adbd_3124"), true); 70 | var noteSegment = elf.Segments.Where(x => x.Type == SegmentType.Note).First() as INoteSegment; 71 | Assert.AreEqual("CORE", noteSegment.Notes[0].Name); 72 | Assert.AreEqual("LINUX", noteSegment.Notes[6].Name); 73 | } 74 | 75 | [Test] 76 | public void ShouldReadMultipleNoteDataType() 77 | { 78 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("elf32-core-adbd_3124"), true); 79 | var noteSegment = elf.Segments.Where(x => x.Type == SegmentType.Note).First() as INoteSegment; 80 | Assert.AreEqual(1ul, noteSegment.Notes[0].Type); 81 | Assert.AreEqual(1024ul, noteSegment.Notes[6].Type); 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /ELFSharp/MachO/MachO.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using ELFSharp.Utilities; 5 | 6 | namespace ELFSharp.MachO 7 | { 8 | public sealed class MachO 9 | { 10 | internal const int Architecture64 = 0x1000000; 11 | private readonly Command[] commands; 12 | 13 | internal MachO(Stream stream, bool is64, Endianess endianess, bool ownsStream) 14 | { 15 | this.Is64 = is64; 16 | 17 | using var reader = new SimpleEndianessAwareReader(stream, endianess, !ownsStream); 18 | 19 | Machine = (Machine)reader.ReadInt32(); 20 | reader.ReadBytes(4); // we don't support the cpu subtype now 21 | FileType = (FileType)reader.ReadUInt32(); 22 | var noOfCommands = reader.ReadInt32(); 23 | reader.ReadInt32(); // size of commands 24 | Flags = (HeaderFlags)reader.ReadUInt32(); 25 | if (is64) reader.ReadBytes(4); // reserved 26 | commands = new Command[noOfCommands]; 27 | ReadCommands(noOfCommands, stream, reader); 28 | } 29 | 30 | public Machine Machine { get; private set; } 31 | 32 | public FileType FileType { get; private set; } 33 | 34 | public HeaderFlags Flags { get; private set; } 35 | 36 | public bool Is64 { get; } 37 | 38 | public IEnumerable GetCommandsOfType() where T : Command 39 | { 40 | return commands.Where(x => x != null).OfType(); 41 | } 42 | 43 | private void ReadCommands(int noOfCommands, Stream stream, SimpleEndianessAwareReader reader) 44 | { 45 | for (var i = 0; i < noOfCommands; i++) 46 | { 47 | var loadCommandType = reader.ReadUInt32(); 48 | var commandSize = reader.ReadUInt32(); 49 | switch ((CommandType)loadCommandType) 50 | { 51 | case CommandType.SymbolTable: 52 | commands[i] = new SymbolTable(reader, stream, Is64, 53 | commands.OfType().SelectMany(e => e.Sections).ToList()); 54 | break; 55 | case CommandType.IdDylib: 56 | commands[i] = new IdDylib(reader, stream, commandSize); 57 | break; 58 | case CommandType.LoadDylib: 59 | commands[i] = new LoadDylib(reader, stream, commandSize); 60 | break; 61 | case CommandType.LoadWeakDylib: 62 | commands[i] = new LoadWeakDylib(reader, stream, commandSize); 63 | break; 64 | case CommandType.ReexportDylib: 65 | commands[i] = new ReexportDylib(reader, stream, commandSize); 66 | break; 67 | case CommandType.Main: 68 | commands[i] = new EntryPoint(reader, stream); 69 | break; 70 | case CommandType.Segment: 71 | case CommandType.Segment64: 72 | commands[i] = new Segment(reader, stream, this); 73 | break; 74 | case CommandType.UUID: 75 | commands[i] = new UUID(reader, stream); 76 | break; 77 | default: 78 | reader.ReadBytes((int)commandSize - 8); // 8 bytes is the size of the common command header 79 | break; 80 | } 81 | } 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /Tests/MachO/OpeningTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using ELFSharp.MachO; 4 | using NUnit.Framework; 5 | 6 | namespace Tests.MachO 7 | { 8 | [TestFixture] 9 | public class OpeningTests 10 | { 11 | [Test] 12 | public void ShouldOpenMachO() 13 | { 14 | Assert.AreEqual(MachOResult.OK, 15 | MachOReader.TryLoad(Utilities.GetBinaryStream("simple-mach-o"), true, out _)); 16 | } 17 | 18 | [Test] 19 | public void ShouldFindCorrectMachine() 20 | { 21 | var machO = MachOReader.Load(Utilities.GetBinaryStream("simple-mach-o"), true); 22 | Assert.AreEqual(Machine.X86_64, machO.Machine); 23 | } 24 | 25 | [Test] 26 | public void ShouldOpen32BitMachO() 27 | { 28 | var machO = MachOReader.Load(Utilities.GetBinaryStream("simple-32-mach-o"), true); 29 | Assert.AreEqual(Machine.X86, machO.Machine); 30 | } 31 | 32 | [Test] 33 | public void ShouldOpenBigEndianMachO() 34 | { 35 | var machO = MachOReader.Load(Utilities.GetBinaryStream("MachO-OSX-ppc-openssl-1.0.1h"), true); 36 | Assert.AreEqual(Machine.PowerPc, machO.Machine); 37 | } 38 | 39 | [Test] 40 | public void ShouldOpenFatMachO() 41 | { 42 | var machOs = MachOReader.LoadFat(Utilities.GetBinaryStream("Undecimus"), true); 43 | Assert.AreEqual(2, machOs.Count); 44 | Assert.AreEqual(Machine.Arm64, machOs[0].Machine); 45 | Assert.AreEqual(Machine.Arm64, machOs[1].Machine); 46 | } 47 | 48 | [Test] 49 | public void ShouldThrowOnFatMachOWhenOpenedAsNotFat() 50 | { 51 | Assert.Throws(() => 52 | MachOReader.Load(Utilities.GetBinaryStream("Undecimus"), true)); 53 | } 54 | 55 | [Test] 56 | public void ShouldRecognizeFatMachO() 57 | { 58 | var result = MachOReader.TryLoad(Utilities.GetBinaryStream("Undecimus"), true, out _); 59 | Assert.AreEqual(MachOResult.FatMachO, result); 60 | } 61 | 62 | [Test] 63 | public void ShouldOpenNotFatMachOUsingFatMethod() 64 | { 65 | var machOs = MachOReader.LoadFat(Utilities.GetBinaryStream("MachO-OSX-ppc-openssl-1.0.1h"), true); 66 | Assert.AreEqual(1, machOs.Count); 67 | Assert.AreEqual(Machine.PowerPc, machOs[0].Machine); 68 | } 69 | 70 | [Test] 71 | public void ShouldOpenMachOObjectFile() 72 | { 73 | // intermediate object file has only 1 segment. 74 | var result = MachOReader.TryLoad(Utilities.GetBinaryStream("simple-mach-o-object.o"), true, out var machO); 75 | Assert.AreEqual(MachOResult.OK, result); 76 | Assert.AreEqual(machO.FileType, FileType.Object); 77 | Assert.AreEqual(machO.GetCommandsOfType().Count(), 1); 78 | } 79 | 80 | [Test] 81 | public void ShouldDisposeStream() 82 | { 83 | var isDisposed = false; 84 | var stream = new StreamWrapper(Utilities.GetBinaryStream("simple-mach-o"), () => isDisposed = true); 85 | MachOReader.Load(stream, true); 86 | Assert.True(isDisposed); 87 | } 88 | 89 | [Test] 90 | public void ShouldNotDisposeStream() 91 | { 92 | var isDisposed = false; 93 | var stream = new StreamWrapper(Utilities.GetBinaryStream("simple-mach-o"), () => isDisposed = true); 94 | MachOReader.Load(stream, false); 95 | Assert.False(isDisposed); 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /ELFSharp/UImage/UImageReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using System.Text; 5 | 6 | namespace ELFSharp.UImage 7 | { 8 | public static class UImageReader 9 | { 10 | private const uint Magic = 0x27051956; 11 | private const uint Polynomial = 0xEDB88320; 12 | private const uint Seed = 0xFFFFFFFF; 13 | 14 | public static UImage Load(string fileName) 15 | { 16 | return Load(File.OpenRead(fileName), true); 17 | } 18 | 19 | public static UImage Load(Stream stream, bool shouldOwnStream) 20 | { 21 | return TryLoad(stream, shouldOwnStream, out var result) switch 22 | { 23 | UImageResult.OK => result, 24 | UImageResult.NotUImage => throw new InvalidOperationException("Given file is not an UBoot image."), 25 | UImageResult.BadChecksum => throw new InvalidOperationException( 26 | "Wrong header checksum of the given UImage file."), 27 | UImageResult.NotSupportedImageType => throw new InvalidOperationException( 28 | "Given image type is not supported."), 29 | _ => throw new ArgumentOutOfRangeException() 30 | }; 31 | } 32 | 33 | public static UImageResult TryLoad(string fileName, out UImage uImage) 34 | { 35 | return TryLoad(File.OpenRead(fileName), true, out uImage); 36 | } 37 | 38 | public static UImageResult TryLoad(Stream stream, bool shouldOwnStream, out UImage uImage) 39 | { 40 | var startingStreamPosition = stream.Position; 41 | 42 | uImage = null; 43 | if (stream.Length < 64) return UImageResult.NotUImage; 44 | 45 | using var reader = new BinaryReader(stream, Encoding.UTF8, true); 46 | 47 | var headerForCrc = reader.ReadBytes(64); 48 | // we need to zero crc part 49 | for (var i = 4; i < 8; i++) headerForCrc[i] = 0; 50 | 51 | stream.Position = startingStreamPosition; 52 | 53 | var magic = reader.ReadUInt32BigEndian(); 54 | if (magic != Magic) return UImageResult.NotUImage; 55 | 56 | var crc = reader.ReadUInt32BigEndian(); 57 | if (crc != GzipCrc32(headerForCrc)) return UImageResult.BadChecksum; 58 | 59 | reader.ReadBytes(22); 60 | var imageType = (ImageType)reader.ReadByte(); 61 | if (!Enum.IsDefined(typeof(ImageType), imageType)) return UImageResult.NotSupportedImageType; 62 | 63 | var multiFileImage = imageType == ImageType.MultiFileImage; 64 | stream.Position = startingStreamPosition; 65 | uImage = new UImage(stream, multiFileImage, shouldOwnStream); 66 | return UImageResult.OK; 67 | } 68 | 69 | internal static uint GzipCrc32(byte[] data) 70 | { 71 | var remainder = Seed; 72 | for (var i = 0; i < data.Length; i++) 73 | { 74 | remainder ^= data[i]; 75 | for (var j = 0; j < 8; j++) 76 | if ((remainder & 1) != 0) 77 | remainder = (remainder >> 1) ^ Polynomial; 78 | else 79 | remainder >>= 1; 80 | } 81 | 82 | return remainder ^ Seed; 83 | } 84 | 85 | internal static uint ReadUInt32BigEndian(this BinaryReader reader) 86 | { 87 | return (uint)IPAddress.HostToNetworkOrder(reader.ReadInt32()); 88 | } 89 | 90 | internal static int ReadInt32BigEndian(this BinaryReader reader) 91 | { 92 | return IPAddress.HostToNetworkOrder(reader.ReadInt32()); 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /ELFSharp/Utilities/SubStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace ELFSharp.Utilities 5 | { 6 | internal sealed class SubStream : Stream 7 | { 8 | private const string NegativeArgumentMessage = "The argument cannot be negative."; 9 | private const string OutsideStreamMessage = "The argument must be within the wrapped stream."; 10 | private readonly long startingPosition; 11 | 12 | private readonly Stream wrappedStream; 13 | 14 | public SubStream(Stream wrappedStream, long startingPosition, long length) 15 | { 16 | if (startingPosition < 0) throw new ArgumentException(nameof(startingPosition), NegativeArgumentMessage); 17 | 18 | if (startingPosition > wrappedStream.Length) 19 | throw new ArgumentException(nameof(startingPosition), OutsideStreamMessage); 20 | 21 | if (length < 0) throw new ArgumentException(nameof(length), NegativeArgumentMessage); 22 | 23 | if (startingPosition + length > wrappedStream.Length) 24 | throw new ArgumentException(nameof(startingPosition), OutsideStreamMessage); 25 | 26 | if (!wrappedStream.CanSeek) 27 | throw new ArgumentException(nameof(wrappedStream), "Wrapped streem has to be seekable."); 28 | ; 29 | this.wrappedStream = wrappedStream; 30 | this.startingPosition = startingPosition; 31 | this.Length = length; 32 | 33 | wrappedStream.Seek(startingPosition, SeekOrigin.Begin); 34 | } 35 | 36 | public override bool CanRead => wrappedStream.CanRead; 37 | 38 | public override bool CanSeek => wrappedStream.CanSeek; 39 | 40 | public override bool CanWrite => false; 41 | 42 | public override long Length { get; } 43 | 44 | public override long Position 45 | { 46 | get => wrappedStream.Position - startingPosition; 47 | 48 | set => wrappedStream.Position = value + startingPosition; 49 | } 50 | 51 | public override void Flush() 52 | { 53 | wrappedStream.Flush(); 54 | } 55 | 56 | public override int Read(byte[] buffer, int offset, int count) 57 | { 58 | count = (int)Math.Min(count, Length - Position); 59 | return wrappedStream.Read(buffer, offset, count); 60 | } 61 | 62 | public override long Seek(long offset, SeekOrigin origin) 63 | { 64 | // All offsets are adjusted to represent a begin-based offset in 65 | // the original stream, so that we can simplify sanity checks. 66 | 67 | var adjustedOffset = origin switch 68 | { 69 | SeekOrigin.Begin => offset + startingPosition, 70 | SeekOrigin.End => wrappedStream.Length - offset, 71 | SeekOrigin.Current => wrappedStream.Position + offset, 72 | _ => throw new InvalidOperationException("Should never reach here.") 73 | }; 74 | 75 | if (adjustedOffset < startingPosition || adjustedOffset > startingPosition + Length) 76 | throw new ArgumentException(nameof(offset), "Effective offset cannot move outside of the substream."); 77 | 78 | return wrappedStream.Seek(adjustedOffset, SeekOrigin.Begin) - startingPosition; 79 | } 80 | 81 | public override void SetLength(long value) 82 | { 83 | throw new NotSupportedException($"Setting length is not available for {nameof(SubStream)}."); 84 | } 85 | 86 | public override void Write(byte[] buffer, int offset, int count) 87 | { 88 | throw new NotSupportedException($"Writing is not available for {nameof(SubStream)}."); 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /Tests/ELF/WebTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using ELFSharp.ELF; 5 | using ELFSharp.ELF.Sections; 6 | using NUnit.Framework; 7 | 8 | namespace Tests.ELF 9 | { 10 | [TestFixture] 11 | public class WebTests 12 | { 13 | [Test] 14 | public void ListELFSectionHeaders() 15 | { 16 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello64le"), true); 17 | var output = new List(); 18 | 19 | foreach (var header in elf.Sections) output.Add(header.ToString()); 20 | var expectedOutput = @": Null, load @0x0, 0 bytes long 21 | .interp: ProgBits, load @0x400200, 28 bytes long 22 | .note.ABI-tag: Note, Type=1 23 | .note.gnu.build-id: Note, Type=3 24 | .gnu.hash: 1879048182, load @0x400260, 28 bytes long 25 | .dynsym: DynamicSymbolTable, load @0x400280, 96 bytes long 26 | .dynstr: StringTable, load @0x4002E0, 61 bytes long 27 | .gnu.version: 1879048191, load @0x40031E, 8 bytes long 28 | .gnu.version_r: 1879048190, load @0x400328, 32 bytes long 29 | .rela.dyn: RelocationAddends, load @0x400348, 24 bytes long 30 | .rela.plt: RelocationAddends, load @0x400360, 72 bytes long 31 | .init: ProgBits, load @0x4003A8, 26 bytes long 32 | .plt: ProgBits, load @0x4003D0, 64 bytes long 33 | .text: ProgBits, load @0x400410, 420 bytes long 34 | .fini: ProgBits, load @0x4005B4, 9 bytes long 35 | .rodata: ProgBits, load @0x4005C0, 13 bytes long 36 | .eh_frame_hdr: ProgBits, load @0x4005D0, 44 bytes long 37 | .eh_frame: ProgBits, load @0x400600, 172 bytes long 38 | .init_array: 14, load @0x6006B0, 8 bytes long 39 | .fini_array: 15, load @0x6006B8, 8 bytes long 40 | .jcr: ProgBits, load @0x6006C0, 8 bytes long 41 | .dynamic: Dynamic, load @0x6006C8, 29 entries 42 | .got: ProgBits, load @0x600898, 8 bytes long 43 | .got.plt: ProgBits, load @0x6008A0, 48 bytes long 44 | .data: ProgBits, load @0x6008D0, 16 bytes long 45 | .bss: NoBits, load @0x6008E0, 8 bytes long 46 | .comment: ProgBits, load @0x0, 56 bytes long 47 | .shstrtab: StringTable, load @0x0, 264 bytes long 48 | .symtab: SymbolTable, load @0x0, 1536 bytes long 49 | .strtab: StringTable, load @0x0, 566 bytes long"; 50 | var expectedOutputAsLines = expectedOutput.Split(new[] { "\n", "\r\n" }, StringSplitOptions.None); 51 | CollectionAssert.AreEqual(expectedOutputAsLines, output); 52 | } 53 | 54 | [Test] 55 | public void GetNamesOfFunctionSymbols() 56 | { 57 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello64le"), true); 58 | var output = new List(); 59 | 60 | var functions = ((ISymbolTable)elf.GetSection(".symtab")).Entries.Where(x => x.Type == SymbolType.Function); 61 | foreach (var f in functions) output.Add(f.Name); 62 | 63 | var expectedOutput = @"deregister_tm_clones 64 | register_tm_clones 65 | __do_global_dtors_aux 66 | frame_dummy 67 | __libc_csu_fini 68 | puts@@GLIBC_2.2.5 69 | _fini 70 | __libc_start_main@@GLIBC_2.2.5 71 | __libc_csu_init 72 | _start 73 | main 74 | _init"; 75 | var expectedOutputAsLines = expectedOutput.Split(new[] { "\n", "\r\n" }, StringSplitOptions.None); 76 | CollectionAssert.AreEqual(expectedOutputAsLines, output); 77 | } 78 | 79 | [Test] 80 | public void WriteAllLoadableProgBitsToArray() 81 | { 82 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello64le"), true); 83 | 84 | var sectionsToLoad = elf.GetSections>() 85 | .Where(x => x.LoadAddress != 0); 86 | foreach (var s in sectionsToLoad) 87 | { 88 | var array = new byte[s.Size]; 89 | s.GetContents().CopyTo(array, 0); 90 | } 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/DynamicTag.cs: -------------------------------------------------------------------------------- 1 | namespace ELFSharp.ELF.Sections 2 | { 3 | /// 4 | /// This enum holds some of the possible values for the DynamicTag value (dropping platform 5 | /// specific contents, such as MIPS flags.) 6 | /// Values are coming from LLVM's elf.h headers. 7 | /// File can be found in LLVM 3.8.1 source at: 8 | /// ../include/llvm/support/elf.h 9 | /// License of the original C code is LLVM license. 10 | /// 11 | public enum DynamicTag : ulong 12 | { 13 | Null = 0, // Marks end of dynamic array. 14 | Needed = 1, // String table offset of needed library. 15 | PLTRelSz = 2, // Size of relocation entries in PLT. 16 | PLTGOT = 3, // Address associated with linkage table. 17 | Hash = 4, // Address of symbolic hash table. 18 | StrTab = 5, // Address of dynamic string table. 19 | SymTab = 6, // Address of dynamic symbol table. 20 | RelA = 7, // Address of relocation table (Rela entries). 21 | RelASz = 8, // Size of Rela relocation table. 22 | RelAEnt = 9, // Size of a Rela relocation entry. 23 | StrSz = 10, // Total size of the string table. 24 | SymEnt = 11, // Size of a symbol table entry. 25 | Init = 12, // Address of initialization function. 26 | Fini = 13, // Address of termination function. 27 | SoName = 14, // String table offset of a shared objects name. 28 | RPath = 15, // String table offset of library search path. 29 | Symbolic = 16, // Changes symbol resolution algorithm. 30 | Rel = 17, // Address of relocation table (Rel entries). 31 | RelSz = 18, // Size of Rel relocation table. 32 | RelEnt = 19, // Size of a Rel relocation entry. 33 | PLTRel = 20, // Type of relocation entry used for linking. 34 | Debug = 21, // Reserved for debugger. 35 | TextRel = 22, // Relocations exist for non-writable segments. 36 | JmpRel = 23, // Address of relocations associated with PLT. 37 | BindNow = 24, // Process all relocations before execution. 38 | InitArray = 25, // Pointer to array of initialization functions. 39 | FiniArray = 26, // Pointer to array of termination functions. 40 | InitArraySz = 27, // Size of DT_INIT_ARRAY. 41 | FiniArraySz = 28, // Size of DT_FINI_ARRAY. 42 | RunPath = 29, // String table offset of lib search path. 43 | Flags = 30, // Flags. 44 | Encoding = 32, // Values from here to DT_LOOS follow the rules for the interpretation of the d_un union. 45 | 46 | PreInitArray = 32, // Pointer to array of preinit functions. 47 | PreInitArraySz = 33, // Size of the DT_PREINIT_ARRAY array. 48 | LoOS = 0x60000000, // Start of environment specific tags. 49 | HiOS = 0x6FFFFFFF, // End of environment specific tags. 50 | LoProc = 0x70000000, // Start of processor specific tags. 51 | HiProc = 0x7FFFFFFF, // End of processor specific tags. 52 | GNUHash = 0x6FFFFEF5, // Reference to the GNU hash table. 53 | TLSDescPLT = 0x6FFFFEF6, // Location of PLT entry for TLS descriptor resolver calls. 54 | TLSDescGOT = 0x6FFFFEF7, // Location of GOT entry used by TLS descriptor resolver PLT entry. 55 | RelACount = 0x6FFFFFF9, // ELF32_Rela count. 56 | RelCount = 0x6FFFFFFA, // ELF32_Rel count. 57 | Flags1 = 0X6FFFFFFB, // Flags_1. 58 | VerSym = 0x6FFFFFF0, // The address of .gnu.version section. 59 | VerDef = 0X6FFFFFFC, // The address of the version definition table. 60 | VerDefNum = 0X6FFFFFFD, // The number of entries in DT_VERDEF. 61 | VerNeed = 0X6FFFFFFE, // The address of the version Dependency table. 62 | VerNeedNum = 0X6FFFFFFF // The number of entries in DT_VERNEED. 63 | } 64 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/ELFReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | 5 | namespace ELFSharp.ELF 6 | { 7 | public static class ELFReader 8 | { 9 | private const string NotELFMessage = "Given stream is not a proper ELF file."; 10 | 11 | private static readonly byte[] Magic = 12 | { 13 | 0x7F, 14 | 0x45, 15 | 0x4C, 16 | 0x46 17 | }; // 0x7F 'E' 'L' 'F' 18 | 19 | public static IELF Load(Stream stream, bool shouldOwnStream) 20 | { 21 | if (!TryLoad(stream, shouldOwnStream, out var elf)) throw new ArgumentException(NotELFMessage); 22 | 23 | return elf; 24 | } 25 | 26 | public static IELF Load(string fileName) 27 | { 28 | return Load(File.OpenRead(fileName), true); 29 | } 30 | 31 | public static bool TryLoad(Stream stream, bool shouldOwnStream, out IELF elf) 32 | { 33 | switch (CheckELFType(stream)) 34 | { 35 | case Class.Bit32: 36 | elf = new ELF(stream, shouldOwnStream); 37 | return true; 38 | case Class.Bit64: 39 | elf = new ELF(stream, shouldOwnStream); 40 | return true; 41 | default: 42 | elf = null; 43 | stream.Close(); 44 | return false; 45 | } 46 | } 47 | 48 | public static bool TryLoad(string fileName, out IELF elf) 49 | { 50 | return TryLoad(File.OpenRead(fileName), true, out elf); 51 | } 52 | 53 | public static Class CheckELFType(Stream stream) 54 | { 55 | var currentStreamPosition = stream.Position; 56 | 57 | if (stream.Length < Consts.MinimalELFSize) return Class.NotELF; 58 | 59 | using (var reader = new BinaryReader(stream, Encoding.UTF8, true)) 60 | { 61 | var magic = reader.ReadBytes(4); 62 | for (var i = 0; i < 4; i++) 63 | if (magic[i] != Magic[i]) 64 | return Class.NotELF; 65 | 66 | var value = reader.ReadByte(); 67 | stream.Position = currentStreamPosition; 68 | return value == 1 ? Class.Bit32 : Class.Bit64; 69 | } 70 | } 71 | 72 | public static Class CheckELFType(string fileName) 73 | { 74 | using (var stream = File.OpenRead(fileName)) 75 | { 76 | return CheckELFType(stream); 77 | } 78 | } 79 | 80 | public static ELF Load(Stream stream, bool shouldOwnStream) where T : struct 81 | { 82 | if (CheckELFType(stream) == Class.NotELF) throw new ArgumentException(NotELFMessage); 83 | 84 | return new ELF(stream, shouldOwnStream); 85 | } 86 | 87 | public static ELF Load(string fileName) where T : struct 88 | { 89 | return Load(File.OpenRead(fileName), true); 90 | } 91 | 92 | public static bool TryLoad(Stream stream, bool shouldOwnStream, out ELF elf) where T : struct 93 | { 94 | switch (CheckELFType(stream)) 95 | { 96 | case Class.Bit32: 97 | case Class.Bit64: 98 | elf = new ELF(stream, shouldOwnStream); 99 | return true; 100 | default: 101 | elf = null; 102 | return false; 103 | } 104 | } 105 | 106 | public static bool TryLoad(string fileName, out ELF elf) where T : struct 107 | { 108 | return TryLoad(File.OpenRead(fileName), true, out elf); 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Sections/NoteData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using System.IO; 4 | using System.Text; 5 | using ELFSharp.ELF.Segments; 6 | using ELFSharp.Utilities; 7 | 8 | namespace ELFSharp.ELF.Sections 9 | { 10 | public class NoteData : INoteData 11 | { 12 | public const ulong NoteDataHeaderSize = 12; // name size + description size + field 13 | 14 | private readonly SimpleEndianessAwareReader reader; 15 | 16 | internal NoteData(ulong sectionOffset, ulong sectionSize, SimpleEndianessAwareReader reader) 17 | { 18 | this.reader = reader; 19 | var sectionEnd = (long)(sectionOffset + sectionSize); 20 | reader.BaseStream.Seek((long)sectionOffset, SeekOrigin.Begin); 21 | var nameSize = ReadSize(); 22 | var descriptionSize = ReadSize(); 23 | Type = ReadField(); 24 | int remainder; 25 | var fields = Math.DivRem(nameSize, FieldSize, out remainder); 26 | var alignedNameSize = FieldSize * (remainder > 0 ? fields + 1 : fields); 27 | 28 | fields = Math.DivRem(descriptionSize, FieldSize, out remainder); 29 | var alignedDescriptionSize = FieldSize * (remainder > 0 ? fields + 1 : fields); 30 | 31 | // We encountered binaries where nameSize and descriptionSize are 32 | // invalid (i.e. significantly larger than the size of the binary itself). 33 | // To avoid throwing on such binaries, we only read in name and description 34 | // if the sizes are within range of the containing section. 35 | if (reader.BaseStream.Position + alignedNameSize <= sectionEnd) 36 | { 37 | var name = reader.ReadBytes(alignedNameSize); 38 | if (nameSize > 0) 39 | Name = Encoding.UTF8.GetString(name, 0, nameSize - 1); // minus one to omit terminating NUL 40 | if (reader.BaseStream.Position + descriptionSize <= sectionEnd) 41 | DescriptionBytes = descriptionSize > 0 ? reader.ReadBytes(descriptionSize) : new byte[0]; 42 | } 43 | 44 | // If there are multiple notes inside one segment, keep track of the end position so we can read them 45 | // all when parsing the segment 46 | NoteOffset = sectionOffset; 47 | NoteFileSize = (ulong)alignedNameSize + (ulong)alignedDescriptionSize + NoteDataHeaderSize; 48 | } 49 | 50 | internal byte[] DescriptionBytes { get; } 51 | 52 | internal ulong NoteOffset { get; } 53 | internal ulong NoteFileSize { get; } 54 | internal ulong NoteFileEnd => NoteOffset + NoteFileSize; 55 | 56 | private int FieldSize => 4; 57 | 58 | public string Name { get; } 59 | 60 | public ReadOnlyCollection Description => new ReadOnlyCollection(DescriptionBytes); 61 | 62 | public ulong Type { get; } 63 | 64 | public Stream ToStream() 65 | { 66 | return new MemoryStream(DescriptionBytes); 67 | } 68 | 69 | public override string ToString() 70 | { 71 | return $"Name={Name} DataSize=0x{DescriptionBytes.Length.ToString("x8")}"; 72 | } 73 | 74 | private int ReadSize() 75 | { 76 | /* 77 | * According to some versions of ELF64 specfication, in 64-bit ELF files words, of which 78 | * such section consists, should have 8 byte length. However, this is not the case in 79 | * some other specifications (some of theme contradicts with themselves like the 64bit MIPS 80 | * one). In real life scenarios I also observed that note sections are identical in both 81 | * ELF classes. There is also only one structure (i.e. Elf_External_Note) in existing and 82 | * well tested GNU tools. 83 | * 84 | * Nevertheless I leave here the whole machinery as it is already written and may be useful 85 | * some day. 86 | */ 87 | return reader.ReadInt32(); 88 | } 89 | 90 | private ulong ReadField() 91 | { 92 | // see comment above 93 | return reader.ReadUInt32(); 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /ELFSharp/MachO/HeaderFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ELFSharp.MachO 4 | { 5 | [Flags] 6 | public enum HeaderFlags : uint 7 | { 8 | NoUndefs = 0x1, /* the object file has no undefined references */ 9 | 10 | IncrLink = 0x2, /* the object file is the output of an incremental link against a base file 11 | * and can't be link edited again */ 12 | 13 | DyldLink = 0x4, /* the object file is input for the dynamic linker and can't be staticly link edited again */ 14 | 15 | BindAtLoad = 0x8, /* the object file's undefined references are bound by the dynamic 16 | * linker when loaded. */ 17 | 18 | Prebound = 0x10, /* the file has its dynamic undefined references prebound. */ 19 | 20 | SplitSeg = 0x20, /* the file has its read-only and read-write segments split */ 21 | 22 | LazyInit = 0x40, /* the shared library init routine is to be run lazily via catching memory 23 | * faults to its writeable segments (obsolete) */ 24 | 25 | TwoLevel = 0x80, /* the image is using two-level name space bindings */ 26 | 27 | ForceFlat = 0x100, /* the executable is forcing all images to use flat name space bindings */ 28 | 29 | NoMultiDefs = 0x200, /* this umbrella guarantees no multiple defintions of symbols in its 30 | * sub-images so the two-level namespace hints can always be used. */ 31 | 32 | NoFixPrebinding = 0x400, /* do not have dyld notify the prebinding agent about this executable*/ 33 | 34 | Prebindable = 0x800, /* the binary is not prebound but can have its prebinding redone. only used 35 | * when MH_PREBOUND is not set. */ 36 | 37 | AllModsBound = 0x1000, /* indicates that this binary binds to all two-level namespace modules of 38 | * its dependent libraries.only used when MH_PREBINDABLE and MH_TWOLEVEL are both set. */ 39 | 40 | SubsectionsViaSymbols = 0x2000, /* safe to divide up the sections into sub-sections via symbols for dead 41 | * code stripping*/ 42 | 43 | Canonical = 0x4000, /* the binary has been canonicalized via the unprebind operation */ 44 | 45 | WeakDefines = 0x8000, /* the final linked image contains external weak symbols*/ 46 | 47 | BindsToWeak = 0x10000, /* the final linked image uses weak symbols */ 48 | 49 | AllowStackExecution = 0x20000, /* When this bit is set, all stacks in the task will be given stack 50 | * execution privilege. Only used in MH_EXECUTE filetypes. */ 51 | 52 | RootSafe = 53 | 0x40000, /* When this bit is set, the binary declares it is safe for use in processes with uid zero */ 54 | 55 | SetuidSafe = 56 | 0x80000, /* When this bit is set, the binary declares it is safe for use in processes when issetugid() is true */ 57 | 58 | NoReexportedDylibs = 0x100000, /* When this bit is set on a dylib, the static linker does not need to 59 | * examine dependent dylibs to see if any are re-exported */ 60 | 61 | PIE = 0x200000, /* When this bit is set, the OS will load the main executable at a random 62 | * address.Only used in MH_EXECUTE filetypes. */ 63 | 64 | DeadStrippableDylib = 0x400000, /* Only for use on dylibs. When linking against a dylib that has this bit 65 | * set, the static linker will automatically not create a LC_LOAD_DYLIB 66 | * load command to the dylib if no symbols are being referenced from the dylib. */ 67 | 68 | HasTLVDescriptors = 0x800000, /* Contains a section of type S_THREAD_LOCAL_VARIABLES*/ 69 | 70 | NoHeapExecution = 0x1000000, /* When this bit is set, the OS will run the main executable with a 71 | * non-executable heap even on platforms (e.g.i386) that don't require 72 | * it. Only used in MH_EXECUTE filetypes. */ 73 | 74 | AppExtensionSafe = 0x02000000 /* The code was linked for use in an application extension. */ 75 | } 76 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Segments/Segment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using ELFSharp.Utilities; 4 | 5 | namespace ELFSharp.ELF.Segments 6 | { 7 | public class Segment : ISegment 8 | { 9 | private readonly Class elfClass; 10 | 11 | private readonly long headerOffset; 12 | private readonly SimpleEndianessAwareReader reader; 13 | 14 | internal Segment(long headerOffset, Class elfClass, SimpleEndianessAwareReader reader) 15 | { 16 | this.reader = reader; 17 | this.headerOffset = headerOffset; 18 | this.elfClass = elfClass; 19 | ReadHeader(); 20 | } 21 | 22 | public T Address { get; private set; } 23 | 24 | public T PhysicalAddress { get; private set; } 25 | 26 | public T Size { get; private set; } 27 | 28 | public T Alignment { get; private set; } 29 | 30 | public long FileSize { get; private set; } 31 | 32 | public long Offset { get; private set; } 33 | 34 | public SegmentType Type { get; private set; } 35 | 36 | public SegmentFlags Flags { get; private set; } 37 | 38 | /// 39 | /// Returns content of the section as it is given in the file. 40 | /// Note that it may be an array of length 0. 41 | /// 42 | /// Segment contents as byte array. 43 | public byte[] GetFileContents() 44 | { 45 | if (FileSize == 0) return new byte[0]; 46 | 47 | SeekTo(Offset); 48 | var result = new byte[checked((int)FileSize)]; 49 | var fileImage = reader.ReadBytes(result.Length); 50 | fileImage.CopyTo(result, 0); 51 | return result; 52 | } 53 | 54 | /// 55 | /// Returns content of the section, possibly padded or truncated to the memory size. 56 | /// Note that it may be an array of length 0. 57 | /// 58 | /// Segment image as a byte array. 59 | public byte[] GetMemoryContents() 60 | { 61 | var sizeAsInt = Size.To(); 62 | if (sizeAsInt == 0) return new byte[0]; 63 | 64 | SeekTo(Offset); 65 | var result = new byte[sizeAsInt]; 66 | var fileImage = reader.ReadBytes(Math.Min(result.Length, checked((int)FileSize))); 67 | fileImage.CopyTo(result, 0); 68 | return result; 69 | } 70 | 71 | public byte[] GetRawHeader() 72 | { 73 | SeekTo(headerOffset); 74 | return reader.ReadBytes(elfClass == Class.Bit32 ? 32 : 56); 75 | } 76 | 77 | public static SegmentType ProbeType(SimpleEndianessAwareReader reader) 78 | { 79 | return (SegmentType)reader.ReadUInt32(); 80 | } 81 | 82 | public override string ToString() 83 | { 84 | return string.Format("{2}: size {3}, @ 0x{0:X}", Address, PhysicalAddress, Type, Size); 85 | } 86 | 87 | private void ReadHeader() 88 | { 89 | SeekTo(headerOffset); 90 | Type = (SegmentType)reader.ReadUInt32(); 91 | if (elfClass == Class.Bit64) Flags = (SegmentFlags)reader.ReadUInt32(); 92 | // TODO: some functions?s 93 | Offset = elfClass == Class.Bit32 ? reader.ReadUInt32() : reader.ReadInt64(); 94 | Address = (elfClass == Class.Bit32 ? reader.ReadUInt32() : reader.ReadUInt64()).To(); 95 | PhysicalAddress = (elfClass == Class.Bit32 ? reader.ReadUInt32() : reader.ReadUInt64()).To(); 96 | FileSize = elfClass == Class.Bit32 ? reader.ReadInt32() : reader.ReadInt64(); 97 | Size = (elfClass == Class.Bit32 ? reader.ReadUInt32() : reader.ReadUInt64()).To(); 98 | if (elfClass == Class.Bit32) Flags = (SegmentFlags)reader.ReadUInt32(); 99 | 100 | Alignment = (elfClass == Class.Bit32 ? reader.ReadUInt32() : reader.ReadUInt64()).To(); 101 | } 102 | 103 | private void SeekTo(long givenOffset) 104 | { 105 | reader.BaseStream.Seek(givenOffset, SeekOrigin.Begin); 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /ELFSharp/MachO/Segment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | using ELFSharp.Utilities; 9 | 10 | namespace ELFSharp.MachO 11 | { 12 | [DebuggerDisplay("{Type}({Name,nq})")] 13 | public sealed class Segment : Command 14 | { 15 | private readonly byte[] data; 16 | 17 | private readonly bool is64; 18 | 19 | public Segment(SimpleEndianessAwareReader reader, Stream stream, MachO machO) : base(reader, stream) 20 | { 21 | is64 = machO.Is64; 22 | Name = ReadSectionOrSegmentName(); 23 | Address = ReadUInt32OrUInt64(); 24 | Size = ReadUInt32OrUInt64(); 25 | FileOffset = ReadUInt32OrUInt64(); 26 | var fileSize = ReadUInt32OrUInt64(); 27 | MaximalProtection = ReadProtection(); 28 | InitialProtection = ReadProtection(); 29 | var numberOfSections = Reader.ReadInt32(); 30 | Reader.ReadInt32(); // we ignore flags for now 31 | 32 | if (fileSize > 0) 33 | { 34 | var streamPosition = Stream.Position; 35 | Stream.Seek((long)FileOffset, SeekOrigin.Begin); 36 | data = new byte[Size]; 37 | var buffer = stream.ReadBytesOrThrow(checked((int)fileSize)); 38 | Array.Copy(buffer, data, buffer.Length); 39 | Stream.Position = streamPosition; 40 | } 41 | 42 | var sections = new List
(); 43 | for (var i = 0; i < numberOfSections; i++) 44 | { 45 | var sectionName = ReadSectionOrSegmentName(); 46 | var segmentName = ReadSectionOrSegmentName(); 47 | 48 | // An intermediate object file contains only one segment. 49 | // This segment name is empty, its sections segment names are not empty. 50 | if (machO.FileType != FileType.Object && segmentName != Name) 51 | throw new InvalidOperationException("Unexpected name of the section's segment."); 52 | 53 | var sectionAddress = ReadUInt32OrUInt64(); 54 | var sectionSize = ReadUInt32OrUInt64(); 55 | var offset = Reader.ReadUInt32(); 56 | var alignExponent = Reader.ReadUInt32(); 57 | var relocOffset = Reader.ReadUInt32(); 58 | var numberOfReloc = Reader.ReadUInt32(); 59 | var flags = Reader.ReadUInt32(); 60 | _ = Reader.ReadUInt32(); // reserved1 61 | _ = Reader.ReadUInt32(); // reserved2 62 | _ = is64 ? Reader.ReadUInt32() : 0; // reserved3 63 | 64 | var section = new Section(sectionName, segmentName, sectionAddress, sectionSize, offset, alignExponent, 65 | relocOffset, numberOfReloc, flags, this); 66 | sections.Add(section); 67 | } 68 | 69 | Sections = new ReadOnlyCollection
(sections); 70 | } 71 | 72 | public string Name { get; private set; } 73 | public ulong Address { get; private set; } 74 | public ulong Size { get; } 75 | public ulong FileOffset { get; } 76 | public Protection InitialProtection { get; private set; } 77 | public Protection MaximalProtection { get; private set; } 78 | public ReadOnlyCollection
Sections { get; private set; } 79 | private CommandType Type => is64 ? CommandType.Segment64 : CommandType.Segment; 80 | 81 | public byte[] GetData() 82 | { 83 | if (data == null) return new byte[Size]; 84 | return data.ToArray(); 85 | } 86 | 87 | private ulong ReadUInt32OrUInt64() 88 | { 89 | return is64 ? Reader.ReadUInt64() : Reader.ReadUInt32(); 90 | } 91 | 92 | private Protection ReadProtection() 93 | { 94 | return (Protection)Reader.ReadInt32(); 95 | } 96 | 97 | private string ReadSectionOrSegmentName() 98 | { 99 | var nameAsBytes = Reader.ReadBytes(16).TakeWhile(x => x != 0).ToArray(); 100 | return Encoding.UTF8.GetString(nameAsBytes); 101 | } 102 | } 103 | } -------------------------------------------------------------------------------- /Tests/ELF/SegmentsTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using ELFSharp.ELF; 3 | using ELFSharp.ELF.Segments; 4 | using NUnit.Framework; 5 | 6 | namespace Tests.ELF 7 | { 8 | [TestFixture] 9 | public class SegmentsTests 10 | { 11 | [Test] 12 | public void ShouldFindAllSegmentsH32LE() 13 | { 14 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 15 | Assert.AreEqual(8, elf.Segments.Count()); 16 | } 17 | 18 | [Test] 19 | public void ShouldFindAllSegmentsOR32BE() 20 | { 21 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("vmlinuxOpenRisc"), true); 22 | Assert.AreEqual(2, elf.Segments.Count()); 23 | } 24 | 25 | [Test] 26 | public void ShouldFindProperFlags32() 27 | { 28 | var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 29 | var segment = elf.Segments.First(x => x.Address == 0x08048034); 30 | Assert.IsTrue(segment.Flags.HasFlag(SegmentFlags.Execute)); 31 | Assert.IsTrue(segment.Flags.HasFlag(SegmentFlags.Read)); 32 | Assert.IsFalse(segment.Flags.HasFlag(SegmentFlags.Write)); 33 | } 34 | 35 | [Test] 36 | public void ShouldFindProperFlags64() 37 | { 38 | var elf = ELFReader.Load(Utilities.GetBinaryStream("hello64le"), true); 39 | var segment = elf.Segments.First(x => x.Address == 0x400000); 40 | Assert.IsTrue(segment.Flags.HasFlag(SegmentFlags.Execute)); 41 | Assert.IsTrue(segment.Flags.HasFlag(SegmentFlags.Read)); 42 | Assert.IsFalse(segment.Flags.HasFlag(SegmentFlags.Write)); 43 | } 44 | 45 | [Test] 46 | public void ShouldFindProperAlignment32() 47 | { 48 | var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 49 | var segment = elf.Segments.First(x => x.Address == 0x08048000); 50 | Assert.AreEqual(0x1000, segment.Alignment); 51 | } 52 | 53 | [Test] 54 | public void ShouldFindProperAlignment64() 55 | { 56 | var elf = ELFReader.Load(Utilities.GetBinaryStream("hello64le"), true); 57 | var segment = elf.Segments.First(x => x.Address == 0x6006c8); 58 | Assert.AreEqual(8, segment.Alignment); 59 | } 60 | 61 | [Test] 62 | public void ShouldGetFileContents() 63 | { 64 | var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 65 | var segment = elf.Segments.Single(x => x.Address == 0x8049F14 && x.Type == SegmentType.Load); 66 | Assert.AreEqual(256, segment.GetFileContents().Length); 67 | } 68 | 69 | [Test] 70 | public void ShouldGetMemoryContents() 71 | { 72 | var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 73 | var segment = elf.Segments.Single(x => x.Address == 0x8049F14 && x.Type == SegmentType.Load); 74 | Assert.AreEqual(264, segment.GetMemoryContents().Length); 75 | } 76 | 77 | // Github issue no 45 78 | [Test] 79 | public void ShouldHaveNullAdditionalBytes() 80 | { 81 | var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 82 | var segment = elf.Segments.Single(x => x.Address == 0x8049F14 && x.Type == SegmentType.Load); 83 | 84 | var memoryContents = segment.GetMemoryContents(); 85 | var endingZeroes = memoryContents.Skip((int)segment.FileSize); 86 | Assert.IsTrue(endingZeroes.All(x => x == 0), "Not all additional bytes were zero."); 87 | } 88 | 89 | // Github issue no 60. 90 | [Test] 91 | public void SegmentsCountShouldBeAvailable() 92 | { 93 | var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 94 | Assert.AreEqual(8, elf.Segments.Count); 95 | } 96 | 97 | [Test] 98 | public void ShouldHandleNoteSegment() 99 | { 100 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 101 | var noteSegment = elf.Segments.OfType().Single(); 102 | 103 | Assert.AreEqual(SegmentType.Note, noteSegment.Type); 104 | Assert.AreEqual("GNU", noteSegment.NoteName); 105 | Assert.AreEqual(1, noteSegment.NoteType); 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /Tests/ELF/OpeningTests.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using ELFSharp; 3 | using ELFSharp.ELF; 4 | using ELFSharp.ELF.Sections; 5 | using NUnit.Framework; 6 | 7 | namespace Tests.ELF 8 | { 9 | [TestFixture] 10 | public class OpeningTests 11 | { 12 | [Test] 13 | public void ShouldChooseGoodClass32() 14 | { 15 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 16 | Assert.AreEqual(Class.Bit32, elf.Class); 17 | } 18 | 19 | [Test] 20 | public void ShouldChooseGoodClass64() 21 | { 22 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("hello64le"), true); 23 | Assert.AreEqual(Class.Bit64, elf.Class); 24 | } 25 | 26 | [Test] 27 | public void ShouldOpenHelloWorld32() 28 | { 29 | ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 30 | } 31 | 32 | [Test] 33 | public void ShouldOpenHelloWorld64() 34 | { 35 | ELFReader.Load(Utilities.GetBinaryStream("hello64le"), true); 36 | } 37 | 38 | [Test] 39 | public void ShouldProperlyParseClass32() 40 | { 41 | var elf32 = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 42 | Assert.AreEqual(Class.Bit32, elf32.Class); 43 | } 44 | 45 | [Test] 46 | public void ShouldProperlyParseClass64() 47 | { 48 | var elf64 = ELFReader.Load(Utilities.GetBinaryStream("hello64le"), true); 49 | Assert.AreEqual(Class.Bit64, elf64.Class); 50 | } 51 | 52 | [Test] 53 | public void ShouldProperlyParseEndianess() 54 | { 55 | using var elfLittleEndian = ELFReader.Load(Utilities.GetBinaryStream("hello32le"), true); 56 | Assert.AreEqual(Endianess.LittleEndian, elfLittleEndian.Endianess); 57 | using var elfBigEndian = ELFReader.Load(Utilities.GetBinaryStream("vmlinuxOpenRisc"), true); 58 | Assert.AreEqual(Endianess.BigEndian, elfBigEndian.Endianess); 59 | } 60 | 61 | [Test] 62 | public void ShouldOpenBigEndian() 63 | { 64 | ELFReader.Load(Utilities.GetBinaryStream("vmlinuxOpenRisc"), true); 65 | } 66 | 67 | // Github issue no 2 68 | [Test] 69 | public void ShouldOpenElfWithNonUniqueSectionNames() 70 | { 71 | ELFReader.Load(Utilities.GetBinaryStream("mpuG890.axf"), true); 72 | } 73 | 74 | // Github issue no 3 75 | [Test] 76 | public void ShouldLoadSharedObjectElfWithProgramHeaders() 77 | { 78 | ELFReader.Load(Utilities.GetBinaryStream("issue3"), true); 79 | } 80 | 81 | [Test] 82 | public void ShouldNotOpenNonELFFile() 83 | { 84 | Assert.IsFalse(ELFReader.TryLoad(Utilities.GetBinaryStream("notelf"), true, out _)); 85 | } 86 | 87 | // Github issue no 9 88 | [Test] 89 | public void ShouldOpenElfWithStripAll() 90 | { 91 | ELFReader.Load(Utilities.GetBinaryStream("stripped-all-binary"), true); 92 | } 93 | 94 | // Github issue no 24 95 | [Test] 96 | public void ShouldHandleCorruptedNamesInDynSym() 97 | { 98 | ELFReader.Load(Utilities.GetBinaryStream("issue24.elf"), true); 99 | } 100 | 101 | // Github issue no 49 102 | [Test] 103 | public void ShouldOpenEmptyStringTableElf() 104 | { 105 | using var elf = ELFReader.Load(Utilities.GetBinaryStream("libcoreclr"), true); 106 | var section = elf.GetSection(".dynstr"); 107 | Assert.AreEqual(SectionType.NoBits, section.Type); 108 | } 109 | 110 | // Github issue no 91 111 | [Test] 112 | public void ShouldCloseOwnedStreamOnNonElf() 113 | { 114 | var stream = new MemoryStream(new byte[] { 0, 1, 2, 3 }); 115 | Assert.IsFalse(ELFReader.TryLoad(stream, true, out _)); 116 | Assert.IsFalse(stream.CanRead); 117 | } 118 | 119 | [Test] 120 | public void ShouldDisposeStream() 121 | { 122 | var isDisposed = false; 123 | var stream = new StreamWrapper(Utilities.GetBinaryStream("hello32le"), () => isDisposed = true); 124 | ELFReader.Load(stream, true).Dispose(); 125 | Assert.True(isDisposed); 126 | } 127 | 128 | [Test] 129 | public void ShouldNotDisposeStream() 130 | { 131 | var isDisposed = false; 132 | var stream = new StreamWrapper(Utilities.GetBinaryStream("hello32le"), () => isDisposed = true); 133 | ELFReader.Load(stream, false).Dispose(); 134 | Assert.False(isDisposed); 135 | } 136 | } 137 | } -------------------------------------------------------------------------------- /ELFSharp/UImage/UImage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.IO.Compression; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace ELFSharp.UImage 9 | { 10 | public sealed class UImage 11 | { 12 | private const int MaximumNameLength = 32; 13 | private readonly List imageSizes; 14 | private readonly byte[] rawImage; 15 | private readonly bool shouldOwnStream; 16 | 17 | internal UImage(Stream stream, bool multiFileImage, bool ownsStream) 18 | { 19 | shouldOwnStream = ownsStream; 20 | imageSizes = new List(); 21 | 22 | using var reader = new BinaryReader(stream, Encoding.UTF8, !ownsStream); 23 | 24 | reader.ReadBytes(8); // magic and CRC, already checked 25 | Timestamp = (new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) + 26 | TimeSpan.FromSeconds(reader.ReadInt32BigEndian())).ToLocalTime(); 27 | Size = reader.ReadUInt32BigEndian(); 28 | LoadAddress = reader.ReadUInt32BigEndian(); 29 | EntryPoint = reader.ReadUInt32BigEndian(); 30 | CRC = reader.ReadUInt32BigEndian(); 31 | OperatingSystem = (OS)reader.ReadByte(); 32 | Architecture = (Architecture)reader.ReadByte(); 33 | Type = (ImageType)reader.ReadByte(); 34 | Compression = (CompressionType)reader.ReadByte(); 35 | var nameAsBytes = reader.ReadBytes(32); 36 | Name = Encoding.UTF8.GetString(nameAsBytes.Reverse().SkipWhile(x => x == 0).Reverse().ToArray()); 37 | 38 | if (multiFileImage) 39 | { 40 | var startingPosition = stream.Position; 41 | 42 | int nextImageSize; 43 | do 44 | { 45 | nextImageSize = reader.ReadInt32BigEndian(); 46 | imageSizes.Add(nextImageSize); 47 | } while (nextImageSize != 0); 48 | 49 | // Last image size is actually a terminator. 50 | imageSizes.RemoveAt(imageSizes.Count - 1); 51 | ImageCount = imageSizes.Count; 52 | stream.Position = startingPosition; 53 | } 54 | 55 | rawImage = reader.ReadBytes((int)Size); 56 | } 57 | 58 | public uint CRC { get; } 59 | public bool IsChecksumOK { get; private set; } 60 | public uint Size { get; } 61 | public uint LoadAddress { get; private set; } 62 | public uint EntryPoint { get; private set; } 63 | public string Name { get; private set; } 64 | public DateTime Timestamp { get; private set; } 65 | public CompressionType Compression { get; } 66 | public ImageType Type { get; private set; } 67 | public OS OperatingSystem { get; private set; } 68 | public Architecture Architecture { get; private set; } 69 | public int ImageCount { get; } 70 | 71 | public ImageDataResult TryGetImageData(int imageIndex, out byte[] result) 72 | { 73 | result = null; 74 | 75 | if (imageIndex > ImageCount - 1 || imageIndex < 0) return ImageDataResult.InvalidIndex; 76 | 77 | if (ImageCount == 1) return TryGetImageData(out result); 78 | 79 | if (Compression != CompressionType.None) 80 | // We only support multi file images without compression 81 | return ImageDataResult.UnsupportedCompressionFormat; 82 | 83 | if (CRC != UImageReader.GzipCrc32(rawImage)) return ImageDataResult.BadChecksum; 84 | 85 | // Images sizes * 4 + terminator (which also takes 4 bytes). 86 | var startingOffset = 4 * (ImageCount + 1) + imageSizes.Take(imageIndex).Sum(); 87 | result = new byte[imageSizes[imageIndex]]; 88 | Array.Copy(rawImage, startingOffset, result, 0, result.Length); 89 | 90 | return ImageDataResult.OK; 91 | } 92 | 93 | public ImageDataResult TryGetImageData(out byte[] result) 94 | { 95 | result = null; 96 | 97 | if (ImageCount > 1) return TryGetImageData(0, out result); 98 | 99 | if (Compression != CompressionType.None && Compression != CompressionType.Gzip) 100 | return ImageDataResult.UnsupportedCompressionFormat; 101 | 102 | if (CRC != UImageReader.GzipCrc32(rawImage)) return ImageDataResult.BadChecksum; 103 | 104 | result = new byte[rawImage.Length]; 105 | Array.Copy(rawImage, result, result.Length); 106 | if (Compression == CompressionType.Gzip) 107 | using (var stream = new GZipStream(new MemoryStream(result), CompressionMode.Decompress)) 108 | { 109 | using (var decompressed = new MemoryStream()) 110 | { 111 | stream.CopyTo(decompressed); 112 | result = decompressed.ToArray(); 113 | } 114 | } 115 | 116 | return ImageDataResult.OK; 117 | } 118 | 119 | public byte[] GetImageData(int imageIndex) 120 | { 121 | byte[] result; 122 | var imageDataResult = TryGetImageData(imageIndex, out result); 123 | return InterpretImageResult(result, imageDataResult); 124 | } 125 | 126 | public byte[] GetImageData() 127 | { 128 | byte[] result; 129 | var imageDataResult = TryGetImageData(out result); 130 | return InterpretImageResult(result, imageDataResult); 131 | } 132 | 133 | private byte[] InterpretImageResult(byte[] result, ImageDataResult imageDataResult) 134 | { 135 | return imageDataResult switch 136 | { 137 | ImageDataResult.OK => result, 138 | ImageDataResult.BadChecksum => throw new InvalidOperationException( 139 | "Bad checksum of the image, probably corrupted image."), 140 | ImageDataResult.UnsupportedCompressionFormat => throw new InvalidOperationException( 141 | string.Format("Unsupported compression format '{0}'.", Compression)), 142 | ImageDataResult.InvalidIndex => throw new ArgumentException("Invalid image index."), 143 | _ => throw new ArgumentOutOfRangeException() 144 | }; 145 | } 146 | 147 | public byte[] GetRawImageData() 148 | { 149 | var result = new byte[rawImage.Length]; 150 | Array.Copy(rawImage, result, result.Length); 151 | return result; 152 | } 153 | } 154 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/Machine.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is based on LLVM's elf.h file. You can find its license 3 | * in the LICENSE file. 4 | * 5 | */ 6 | 7 | namespace ELFSharp.ELF 8 | { 9 | public enum Machine : ushort 10 | { 11 | None = 0, // No machine 12 | M32 = 1, // AT&T WE 32100 13 | SPARC = 2, // SPARC 14 | Intel386 = 3, // Intel 386 15 | M68K = 4, // Motorola 68000 16 | M88K = 5, // Motorola 88000 17 | Intel486 = 6, // Intel 486 (deprecated) 18 | Intel860 = 7, // Intel 80860 19 | MIPS = 8, // MIPS R3000 20 | S370 = 9, // IBM System/370 21 | MIPSRS3LE = 10, // MIPS RS3000 Little-endian 22 | PARISC = 15, // Hewlett-Packard PA-RISC 23 | VPP500 = 17, // Fujitsu VPP500 24 | SPARC32Plus = 18, // Enhanced instruction set SPARC 25 | Intel960 = 19, // Intel 80960 26 | PPC = 20, // PowerPC 27 | PPC64 = 21, // PowerPC64 28 | S390 = 22, // IBM System/390 29 | SPU = 23, // IBM SPU/SPC 30 | V800 = 36, // NEC V800 31 | FR20 = 37, // Fujitsu FR20 32 | RH32 = 38, // TRW RH-32 33 | RCE = 39, // Motorola RCE 34 | ARM = 40, // ARM 35 | Alpha = 41, // DEC Alpha 36 | SuperH = 42, // Hitachi SH 37 | SPARCv9 = 43, // SPARC V9 38 | TriCore = 44, // Siemens TriCore 39 | ARC = 45, // Argonaut RISC Core 40 | H8300 = 46, // Hitachi H8/300 41 | H8300H = 47, // Hitachi H8/300H 42 | H8S = 48, // Hitachi H8S 43 | H8500 = 49, // Hitachi H8/500 44 | IA64 = 50, // Intel IA-64 processor architecture 45 | MIPSX = 51, // Stanford MIPS-X 46 | ColdFire = 52, // Motorola ColdFire 47 | M68HC12 = 53, // Motorola M68HC12 48 | MMA = 54, // Fujitsu MMA Multimedia Accelerator 49 | PCP = 55, // Siemens PCP 50 | NCPU = 56, // Sony nCPU embedded RISC processor 51 | NDR1 = 57, // Denso NDR1 microprocessor 52 | StarCore = 58, // Motorola Star*Core processor 53 | ME16 = 59, // Toyota ME16 processor 54 | ST100 = 60, // STMicroelectronics ST100 processor 55 | TinyJ = 61, // Advanced Logic Corp. TinyJ embedded processor family 56 | AMD64 = 62, // AMD x86-64 architecture 57 | PDSP = 63, // Sony DSP Processor 58 | PDP10 = 64, // Digital Equipment Corp. PDP-10 59 | PDP11 = 65, // Digital Equipment Corp. PDP-11 60 | FX66 = 66, // Siemens FX66 microcontroller 61 | ST9PLUS = 67, // STMicroelectronics ST9+ 8/16 bit microcontroller 62 | ST7 = 68, // STMicroelectronics ST7 8-bit microcontroller 63 | M68HC16 = 69, // Motorola MC68HC16 Microcontroller 64 | M68HC11 = 70, // Motorola MC68HC11 Microcontroller 65 | M68HC08 = 71, // Motorola MC68HC08 Microcontroller 66 | M68HC05 = 72, // Motorola MC68HC05 Microcontroller 67 | SVX = 73, // Silicon Graphics SVx 68 | ST19 = 74, // STMicroelectronics ST19 8-bit microcontroller 69 | VAX = 75, // Digital VAX 70 | CRIS = 76, // Axis Communications 32-bit embedded processor 71 | Javelin = 77, // Infineon Technologies 32-bit embedded processor 72 | FirePath = 78, // Element 14 64-bit DSP Processor 73 | ZSP = 79, // LSI Logic 16-bit DSP Processor 74 | MMIX = 80, // Donald Knuth's educational 64-bit processor 75 | HUANY = 81, // Harvard University machine-independent object files 76 | PRISM = 82, // SiTera Prism 77 | AVR = 83, // Atmel AVR 8-bit microcontroller 78 | FR30 = 84, // Fujitsu FR30 79 | D10V = 85, // Mitsubishi D10V 80 | D30V = 86, // Mitsubishi D30V 81 | V850 = 87, // NEC v850 82 | M32R = 88, // Mitsubishi M32R 83 | MN10300 = 89, // Matsushita MN10300 84 | MN10200 = 90, // Matsushita MN10200 85 | PicoJava = 91, // picoJava 86 | OpenRISC = 92, // OpenRISC 32-bit embedded processor 87 | ARCompact = 93, // ARC International ARCompact processo 88 | Xtensa = 94, // Tensilica Xtensa Architecture 89 | VideoCore = 95, // Alphamosaic VideoCore processor 90 | TMMGPP = 96, // Thompson Multimedia General Purpose Processor 91 | NS32K = 97, // National Semiconductor 32000 series 92 | TPC = 98, // Tenor Network TPC processor 93 | SNP1k = 99, // Trebia SNP 1000 processor 94 | ST200 = 100, // STMicroelectronics (www.st.com) ST200 95 | IP2K = 101, // Ubicom IP2xxx microcontroller family 96 | MAX = 102, // MAX Processor 97 | CompactRISC = 103, // National Semiconductor CompactRISC microprocessor 98 | F2MC16 = 104, // Fujitsu F2MC16 99 | MSP430 = 105, // Texas Instruments embedded microcontroller msp430 100 | Blackfin = 106, // Analog Devices Blackfin (DSP) processor 101 | S1C33 = 107, // S1C33 Family of Seiko Epson processors 102 | SEP = 108, // Sharp embedded microprocessor 103 | ArcaRISC = 109, // Arca RISC Microprocessor 104 | UNICORE = 110, // Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University 105 | Excess = 111, // eXcess: 16/32/64-bit configurable embedded CPU 106 | DXP = 112, // Icera Semiconductor Inc. Deep Execution Processor 107 | AlteraNios2 = 113, // Altera Nios II soft-core processor 108 | CRX = 114, // National Semiconductor CompactRISC CRX 109 | XGATE = 115, // Motorola XGATE embedded processor 110 | C166 = 116, // Infineon C16x/XC16x processor 111 | M16C = 117, // Renesas M16C series microprocessors 112 | DSPIC30F = 118, // Microchip Technology dsPIC30F Digital Signal 113 | 114 | // Controller 115 | EngineRISC = 119, // Freescale Communication Engine RISC core 116 | M32C = 120, // Renesas M32C series microprocessors 117 | TSK3000 = 131, // Altium TSK3000 core 118 | RS08 = 132, // Freescale RS08 embedded processor 119 | SHARC = 133, // Analog Devices SHARC family of 32-bit DSP processors 120 | ECOG2 = 134, // Cyan Technology eCOG2 microprocessor 121 | Score7 = 135, // Sunplus S+core7 RISC processor 122 | DSP24 = 136, // New Japan Radio (NJR) 24-bit DSP Processor 123 | VideoCore3 = 137, // Broadcom VideoCore III processor 124 | LatticeMico32 = 138, // RISC processor for Lattice FPGA architecture 125 | SeikoEpsonC17 = 139, // Seiko Epson C17 family 126 | TIC6000 = 140, // The Texas Instruments TMS320C6000 DSP family 127 | TIC2000 = 141, // The Texas Instruments TMS320C2000 DSP family 128 | TIC5500 = 142, // The Texas Instruments TMS320C55x DSP family 129 | MMDSPPlus = 160, // STMicroelectronics 64bit VLIW Data Signal Processor 130 | CypressM8C = 161, // Cypress M8C microprocessor 131 | R32C = 162, // Renesas R32C series microprocessors 132 | TriMedia = 163, // NXP Semiconductors TriMedia architecture family 133 | Hexagon = 164, // Qualcomm Hexagon processor 134 | Intel8051 = 165, // Intel 8051 and variants 135 | STxP7x = 166, // STMicroelectronics STxP7x family of configurable and extensible RISC processors 136 | NDS32 = 167, // Andes Technology compact code size embedded RISC processor family 137 | ECOG1 = 168, // Cyan Technology eCOG1X family 138 | ECOG1X = 168, // Cyan Technology eCOG1X family 139 | MAXQ30 = 169, // Dallas Semiconductor MAXQ30 Core Micro-controllers 140 | XIMO16 = 170, // New Japan Radio (NJR) 16-bit DSP Processor 141 | MANIK = 171, // M2000 Reconfigurable RISC Microprocessor 142 | CrayNV2 = 172, // Cray Inc. NV2 vector architecture 143 | RX = 173, // Renesas RX family 144 | METAG = 174, // Imagination Technologies META processor architecture 145 | MCSTElbrus = 175, // MCST Elbrus general purpose hardware architecture 146 | ECOG16 = 176, // Cyan Technology eCOG16 family 147 | CR16 = 177, // National Semiconductor CompactRISC CR16 16-bit microprocessor 148 | ETPU = 178, // Freescale Extended Time Processing Unit 149 | SLE9X = 179, // Infineon Technologies SLE9X core 150 | L10M = 180, // Intel L10M 151 | K10M = 181, // Intel K10M 152 | AArch64 = 183, // ARM AArch64 153 | AVR32 = 185, // Atmel Corporation 32-bit microprocessor family 154 | STM8 = 186, // STMicroeletronics STM8 8-bit microcontroller 155 | TILE64 = 187, // Tilera TILE64 multicore architecture family 156 | TILEPro = 188, // Tilera TILEPro multicore architecture family 157 | CUDA = 190, // NVIDIA CUDA architecture 158 | TILEGx = 191, // Tilera TILE-Gx multicore architecture family 159 | CloudShield = 192, // CloudShield architecture family 160 | CoreA1st = 193, // KIPO-KAIST Core-A 1st generation processor family 161 | CoreA2nd = 194, // KIPO-KAIST Core-A 2nd generation processor family 162 | ARCompact2 = 195, // Synopsys ARCompact V2 163 | Open8 = 196, // Open8 8-bit RISC soft processor core 164 | RL78 = 197, // Renesas RL78 family 165 | VideoCore5 = 198, // Broadcom VideoCore V processor 166 | R78KOR = 199, // Renesas 78KOR family 167 | F56800EX = 200 // Freescale 56800EX Digital Signal Controller (DSC) 168 | } 169 | } -------------------------------------------------------------------------------- /ELFSharp/ELF/ELF.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using ELFSharp.ELF.Sections; 6 | using ELFSharp.ELF.Segments; 7 | using ELFSharp.Utilities; 8 | 9 | namespace ELFSharp.ELF 10 | { 11 | public sealed class ELF : IELF where T : struct 12 | { 13 | private const int SectionNameNotUniqueMarker = -1; 14 | private readonly bool ownsStream; 15 | 16 | private readonly SimpleEndianessAwareReader reader; 17 | private Stage currentStage; 18 | private StringTable dynamicStringTable; 19 | private StringTable objectsStringTable; 20 | private uint sectionHeaderEntryCount; 21 | private ushort sectionHeaderEntrySize; 22 | private long sectionHeaderOffset; 23 | private List sectionHeaders; 24 | private Dictionary sectionIndicesByName; 25 | private List> sections; 26 | private ushort segmentHeaderEntryCount; 27 | private ushort segmentHeaderEntrySize; 28 | private long segmentHeaderOffset; 29 | private List> segments; 30 | private uint stringTableIndex; 31 | 32 | internal ELF(Stream stream, bool ownsStream) 33 | { 34 | this.ownsStream = ownsStream; 35 | reader = ObtainEndianessAwareReader(stream); 36 | ReadFields(); 37 | ReadStringTable(); 38 | ReadSections(); 39 | ReadSegmentHeaders(); 40 | } 41 | 42 | public T EntryPoint { get; private set; } 43 | 44 | public T MachineFlags { get; private set; } 45 | 46 | public IReadOnlyList> Segments => segments.AsReadOnly(); 47 | 48 | public IReadOnlyList> Sections => sections.AsReadOnly(); 49 | 50 | public Endianess Endianess { get; private set; } 51 | 52 | public Class Class { get; private set; } 53 | 54 | public FileType Type { get; private set; } 55 | 56 | public Machine Machine { get; private set; } 57 | 58 | public bool HasSegmentHeader => segmentHeaderOffset != 0; 59 | 60 | public bool HasSectionHeader => sectionHeaderOffset != 0; 61 | 62 | public bool HasSectionsStringTable => stringTableIndex != 0; 63 | 64 | IReadOnlyList IELF.Segments => Segments; 65 | 66 | public IStringTable SectionsStringTable { get; private set; } 67 | 68 | IEnumerable IELF.GetSections() 69 | { 70 | return Sections.Where(x => x is TSectionType).Cast(); 71 | } 72 | 73 | IReadOnlyList IELF.Sections => Sections; 74 | 75 | bool IELF.TryGetSection(string name, out ISection section) 76 | { 77 | var result = TryGetSection(name, out var concreteSection); 78 | section = concreteSection; 79 | return result; 80 | } 81 | 82 | ISection IELF.GetSection(string name) 83 | { 84 | return GetSection(name); 85 | } 86 | 87 | bool IELF.TryGetSection(int index, out ISection section) 88 | { 89 | var result = TryGetSection(index, out var sectionConcrete); 90 | section = sectionConcrete; 91 | return result; 92 | } 93 | 94 | ISection IELF.GetSection(int index) 95 | { 96 | return GetSection(index); 97 | } 98 | 99 | public void Dispose() 100 | { 101 | if (ownsStream) reader.BaseStream.Dispose(); 102 | } 103 | 104 | public IEnumerable GetSections() where TSection : Section 105 | { 106 | return Sections.Where(x => x is TSection).Cast(); 107 | } 108 | 109 | public bool TryGetSection(string name, out Section section) 110 | { 111 | return TryGetSectionInner(name, out section) == GetSectionResult.Success; 112 | } 113 | 114 | public Section GetSection(string name) 115 | { 116 | var result = TryGetSectionInner(name, out var section); 117 | 118 | switch (result) 119 | { 120 | case GetSectionResult.Success: 121 | return section; 122 | case GetSectionResult.SectionNameNotUnique: 123 | throw new InvalidOperationException("Given section name is not unique, order is ambigous."); 124 | case GetSectionResult.NoSectionsStringTable: 125 | throw new InvalidOperationException( 126 | "Given ELF does not contain section header string table, therefore names of sections cannot be obtained."); 127 | case GetSectionResult.NoSuchSection: 128 | throw new KeyNotFoundException(string.Format("Given section {0} could not be found in the file.", 129 | name)); 130 | default: 131 | throw new InvalidOperationException("Unhandled error."); 132 | } 133 | } 134 | 135 | public Section GetSection(int index) 136 | { 137 | var result = TryGetSectionInner(index, out var section); 138 | switch (result) 139 | { 140 | case GetSectionResult.Success: 141 | return section; 142 | case GetSectionResult.NoSuchSection: 143 | throw new IndexOutOfRangeException(string.Format("Given section index {0} is out of range.", 144 | index)); 145 | default: 146 | throw new ArgumentOutOfRangeException(); 147 | } 148 | } 149 | 150 | public override string ToString() 151 | { 152 | return string.Format("[ELF: Endianess={0}, Class={1}, Type={2}, Machine={3}, EntryPoint=0x{4:X}, " + 153 | "NumberOfSections={5}, NumberOfSegments={6}]", Endianess, Class, Type, Machine, 154 | EntryPoint, sections.Count, segments.Count); 155 | } 156 | 157 | private bool TryGetSection(int index, out Section section) 158 | { 159 | return TryGetSectionInner(index, out section) == GetSectionResult.Success; 160 | } 161 | 162 | private Section GetSectionFromSectionHeader(SectionHeader header) 163 | { 164 | Section returned; 165 | switch (header.Type) 166 | { 167 | case SectionType.Null: 168 | goto default; 169 | case SectionType.ProgBits: 170 | returned = new ProgBitsSection(header, reader); 171 | break; 172 | case SectionType.SymbolTable: 173 | returned = new SymbolTable(header, reader, objectsStringTable, this); 174 | break; 175 | case SectionType.StringTable: 176 | returned = new StringTable(header, reader); 177 | break; 178 | case SectionType.RelocationAddends: 179 | goto default; 180 | case SectionType.HashTable: 181 | goto default; 182 | case SectionType.Dynamic: 183 | returned = new DynamicSection(header, reader, this); 184 | break; 185 | case SectionType.Note: 186 | returned = new NoteSection(header, reader); 187 | break; 188 | case SectionType.NoBits: 189 | goto default; 190 | case SectionType.Relocation: 191 | goto default; 192 | case SectionType.Shlib: 193 | goto default; 194 | case SectionType.DynamicSymbolTable: 195 | returned = new SymbolTable(header, reader, dynamicStringTable, this); 196 | break; 197 | default: 198 | returned = new Section(header, reader); 199 | break; 200 | } 201 | 202 | return returned; 203 | } 204 | 205 | private void ReadSegmentHeaders() 206 | { 207 | segments = new List>(segmentHeaderEntryCount); 208 | 209 | for (var i = 0u; i < segmentHeaderEntryCount; i++) 210 | { 211 | var seekTo = segmentHeaderOffset + i * segmentHeaderEntrySize; 212 | reader.BaseStream.Seek(seekTo, SeekOrigin.Begin); 213 | var segmentType = Segment.ProbeType(reader); 214 | 215 | Segment segment; 216 | if (segmentType == SegmentType.Note) 217 | segment = new NoteSegment(segmentHeaderOffset + i * segmentHeaderEntrySize, Class, reader); 218 | else 219 | segment = new Segment(segmentHeaderOffset + i * segmentHeaderEntrySize, Class, reader); 220 | 221 | segments.Add(segment); 222 | } 223 | } 224 | 225 | private void ReadSections() 226 | { 227 | sectionHeaders = new List(); 228 | if (HasSectionsStringTable) sectionIndicesByName = new Dictionary(); 229 | 230 | for (var i = 0; i < sectionHeaderEntryCount; i++) 231 | { 232 | var header = ReadSectionHeader(i); 233 | sectionHeaders.Add(header); 234 | if (HasSectionsStringTable) 235 | { 236 | var name = header.Name; 237 | if (!sectionIndicesByName.ContainsKey(name)) 238 | sectionIndicesByName.Add(name, i); 239 | else 240 | sectionIndicesByName[name] = SectionNameNotUniqueMarker; 241 | } 242 | } 243 | 244 | sections = new List>(Enumerable.Repeat>( 245 | null, 246 | sectionHeaders.Count 247 | )); 248 | FindStringTables(); 249 | for (var i = 0; i < sectionHeaders.Count; i++) TouchSection(i); 250 | sectionHeaders = null; 251 | currentStage = Stage.AfterSectionsAreRead; 252 | } 253 | 254 | private void TouchSection(int index) 255 | { 256 | if (currentStage != Stage.Initalizing) 257 | throw new InvalidOperationException("TouchSection invoked in improper state."); 258 | if (sections[index] != null) return; 259 | var section = GetSectionFromSectionHeader(sectionHeaders[index]); 260 | sections[index] = section; 261 | } 262 | 263 | private void FindStringTables() 264 | { 265 | TryGetSection(Consts.ObjectsStringTableName, out var section); 266 | objectsStringTable = (StringTable)section; 267 | TryGetSection(Consts.DynamicStringTableName, out section); 268 | 269 | // It might happen that the section is not really available, represented as a NoBits one. 270 | dynamicStringTable = section as StringTable; 271 | } 272 | 273 | private void ReadStringTable() 274 | { 275 | if (!HasSectionHeader || !HasSectionsStringTable) return; 276 | 277 | var header = ReadSectionHeader(checked((int)stringTableIndex)); 278 | if (header.Type != SectionType.StringTable) 279 | throw new InvalidOperationException( 280 | "Given index of section header does not point at string table which was expected."); 281 | 282 | SectionsStringTable = new StringTable(header, reader); 283 | } 284 | 285 | private SectionHeader ReadSectionHeader(int index, bool ignoreUpperLimit = false) 286 | { 287 | if (index < 0 || (!ignoreUpperLimit && index >= sectionHeaderEntryCount)) 288 | throw new ArgumentOutOfRangeException(nameof(index)); 289 | 290 | reader.BaseStream.Seek( 291 | sectionHeaderOffset + index * sectionHeaderEntrySize, 292 | SeekOrigin.Begin 293 | ); 294 | 295 | return new SectionHeader(reader, Class, SectionsStringTable); 296 | } 297 | 298 | private SimpleEndianessAwareReader ObtainEndianessAwareReader(Stream stream) 299 | { 300 | var reader = new BinaryReader(stream); 301 | reader.ReadBytes(4); // ELF magic 302 | var classByte = reader.ReadByte(); 303 | 304 | Class = classByte switch 305 | { 306 | 1 => Class.Bit32, 307 | 2 => Class.Bit64, 308 | _ => throw new ArgumentException($"Given ELF file is of unknown class {classByte}.") 309 | }; 310 | 311 | var endianessByte = reader.ReadByte(); 312 | 313 | Endianess = endianessByte switch 314 | { 315 | 1 => Endianess.LittleEndian, 316 | 2 => Endianess.BigEndian, 317 | _ => throw new ArgumentException($"Given ELF file uses unknown endianess {endianessByte}.") 318 | }; 319 | 320 | reader.ReadBytes(10); // padding bytes of section e_ident 321 | return new SimpleEndianessAwareReader(stream, Endianess); 322 | } 323 | 324 | private void ReadFields() 325 | { 326 | Type = (FileType)reader.ReadUInt16(); 327 | Machine = (Machine)reader.ReadUInt16(); 328 | var version = reader.ReadUInt32(); 329 | if (version != 1) 330 | throw new ArgumentException(string.Format( 331 | "Given ELF file is of unknown version {0}.", 332 | version 333 | )); 334 | EntryPoint = (Class == Class.Bit32 ? reader.ReadUInt32() : reader.ReadUInt64()).To(); 335 | // TODO: assertions for (u)longs 336 | segmentHeaderOffset = Class == Class.Bit32 ? reader.ReadUInt32() : reader.ReadInt64(); 337 | sectionHeaderOffset = Class == Class.Bit32 ? reader.ReadUInt32() : reader.ReadInt64(); 338 | MachineFlags = reader.ReadUInt32().To(); // TODO: always 32bit? 339 | reader.ReadUInt16(); // elf header size 340 | segmentHeaderEntrySize = reader.ReadUInt16(); 341 | segmentHeaderEntryCount = reader.ReadUInt16(); 342 | sectionHeaderEntrySize = reader.ReadUInt16(); 343 | sectionHeaderEntryCount = reader.ReadUInt16(); 344 | stringTableIndex = reader.ReadUInt16(); 345 | 346 | // If the number of sections is greater than or equal to SHN_LORESERVE (0xff00), this member has the 347 | // value zero and the actual number of section header table entries is contained in the sh_size field 348 | // of the section header at index 0. (Otherwise, the sh_size member of the initial entry contains 0.) 349 | if (sectionHeaderEntryCount == 0) 350 | { 351 | var firstSectionHeader = ReadSectionHeader(0, true); 352 | sectionHeaderEntryCount = checked((uint)firstSectionHeader.Size); 353 | 354 | // If the index of the string table is larger than or equal to SHN_LORESERVE (0xff00), this member holds SHN_XINDEX (0xffff) 355 | // and the real index of the section name string table section is held in the sh_link member of the initial entry in section 356 | // header table. Otherwise, the sh_link member of the initial entry in section header table contains the value zero. 357 | if (stringTableIndex == 0xffff) stringTableIndex = checked(firstSectionHeader.Link); 358 | } 359 | } 360 | 361 | private GetSectionResult TryGetSectionInner(string name, out Section section) 362 | { 363 | section = default; 364 | if (!HasSectionsStringTable) return GetSectionResult.NoSectionsStringTable; 365 | if (!sectionIndicesByName.TryGetValue(name, out var index)) return GetSectionResult.NoSuchSection; 366 | if (index == SectionNameNotUniqueMarker) return GetSectionResult.SectionNameNotUnique; 367 | return TryGetSectionInner(index, out section); 368 | } 369 | 370 | private GetSectionResult TryGetSectionInner(int index, out Section section) 371 | { 372 | section = default; 373 | if (index >= sections.Count) return GetSectionResult.NoSuchSection; 374 | if (sections[index] != null) 375 | { 376 | section = sections[index]; 377 | return GetSectionResult.Success; 378 | } 379 | 380 | if (currentStage != Stage.Initalizing) 381 | throw new InvalidOperationException( 382 | "Assert not met: null section by proper index in not initializing stage."); 383 | TouchSection(index); 384 | section = sections[index]; 385 | return GetSectionResult.Success; 386 | } 387 | 388 | private enum Stage 389 | { 390 | Initalizing, 391 | AfterSectionsAreRead 392 | } 393 | 394 | private enum GetSectionResult 395 | { 396 | Success, 397 | SectionNameNotUnique, 398 | NoSectionsStringTable, 399 | NoSuchSection 400 | } 401 | } 402 | } --------------------------------------------------------------------------------