From 51f2591a82e6fe9a878f13812dd5476f4a55a974 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Wed, 14 Mar 2018 23:13:24 +0000 Subject: [PATCH] Support ELF files without sections but with program headers. --- libexeinfo/ELF/ELF.cs | 158 ++++++++++++++++++++++++++++++++------ libexeinfo/ELF/Enums.cs | 67 ++++++++++++++++ libexeinfo/ELF/Info.cs | 2 + libexeinfo/ELF/Structs.cs | 76 ++++++++++++++++-- 4 files changed, 275 insertions(+), 28 deletions(-) diff --git a/libexeinfo/ELF/ELF.cs b/libexeinfo/ELF/ELF.cs index d1df916..f599239 100644 --- a/libexeinfo/ELF/ELF.cs +++ b/libexeinfo/ELF/ELF.cs @@ -13,6 +13,7 @@ namespace libexeinfo Elf64_Ehdr Header; string interpreter; Dictionary notes; + Elf64_Phdr[] programHeaders; string[] sectionNames; Elf64_Shdr[] sections; @@ -107,7 +108,92 @@ namespace libexeinfo List strings = new List(); - if(Header.e_shnum == 0) return; + if(Header.e_phnum == 0 && Header.e_shnum == 0) return; + + buffer = new byte[Header.e_phentsize]; + BaseStream.Position = (long)Header.e_phoff; + programHeaders = new Elf64_Phdr[Header.e_phnum]; + for(int i = 0; i < programHeaders.Length; i++) + { + BaseStream.Read(buffer, 0, buffer.Length); + switch(Header.ei_class) + { + case eiClass.ELFCLASS32: + programHeaders[i] = UpBitsProgramHeader(buffer, Header.ei_data == eiData.ELFDATA2MSB); + break; + case eiClass.ELFCLASS64: + if(Header.ei_data == eiData.ELFDATA2MSB) + { + programHeaders[i] = + BigEndianMarshal.ByteArrayToStructureBigEndian(buffer); + programHeaders[i].p_type = (phType)Swapping.Swap((uint)programHeaders[i].p_type); + programHeaders[i].p_flags = (phFlags)Swapping.Swap((uint)programHeaders[i].p_flags); + } + else programHeaders[i] = BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer); + + break; + default: return; + } + } + + for(int i = 0; i < programHeaders.Length; i++) + { + if(programHeaders[i].p_type != phType.PT_INTERP) continue; + + buffer = new byte[programHeaders[i].p_filesz]; + BaseStream.Position = (long)programHeaders[i].p_offset; + BaseStream.Read(buffer, 0, buffer.Length); + interpreter = StringHandlers.CToString(buffer); + } + + Segment[] segments; + + if(Header.e_shnum == 0) + { + segments = new Segment[programHeaders.Length]; + for(int i = 0; i < programHeaders.Length; i++) + { + segments[i] = new Segment + { + Flags = $"{programHeaders[i].p_flags}", + Offset = (long)programHeaders[i].p_offset, + Size = (long)programHeaders[i].p_filesz + }; + + switch(programHeaders[i].p_type) + { + case phType.PT_NULL: break; + case phType.PT_LOAD: + segments[i].Name = ".text"; + break; + case phType.PT_DYNAMIC: + segments[i].Name = ".dynamic"; + break; + case phType.PT_INTERP: + segments[i].Name = ".interp"; + break; + case phType.PT_NOTE: + segments[i].Name = ".note"; + break; + case phType.PT_SHLIB: + segments[i].Name = ".shlib"; + break; + case phType.PT_PHDR: break; + case phType.PT_TLS: + segments[i].Name = ".tls"; + break; + default: + segments[i].Name = programHeaders[i].p_flags.HasFlag(phFlags.PF_X) + ? ".text" + : programHeaders[i].p_flags.HasFlag(phFlags.PF_W) + ? ".data" + : ".rodata"; + break; + } + } + + return; + } BaseStream.Position = (long)Header.e_shoff; buffer = new byte[Header.e_shentsize]; @@ -138,7 +224,7 @@ namespace libexeinfo buffer = new byte[sections[Header.e_shstrndx].sh_size]; BaseStream.Read(buffer, 0, buffer.Length); sectionNames = new string[sections.Length]; - Segment[] segments = new Segment[sections.Length]; + segments = new Segment[sections.Length]; int len = 0; int pos; @@ -444,27 +530,7 @@ namespace libexeinfo default: throw new ArgumentOutOfRangeException(); } else if(!string.IsNullOrEmpty(interpreter)) - switch(interpreter) - { - case "/usr/lib/ldqnx.so.2": - RequiredOperatingSystem = new OperatingSystem {Name = "QNX", MajorVersion = 6}; - break; - case "/usr/dglib/libc.so.1": - RequiredOperatingSystem = new OperatingSystem {Name = "DG/UX"}; - break; - case "/shlib/ld-bsdi.so": - RequiredOperatingSystem = new OperatingSystem {Name = "BSD/OS"}; - break; - case "/usr/lib/libc.so.1" when skyos: - RequiredOperatingSystem = new OperatingSystem {Name = "SkyOS"}; - break; - case "/usr/lib/ld.so.1" when solaris: - RequiredOperatingSystem = new OperatingSystem {Name = "Solaris"}; - break; - default: - RequiredOperatingSystem = new OperatingSystem {Name = interpreter}; - break; - } + RequiredOperatingSystem = GetOsByInterpreter(interpreter, skyos, solaris); else if(beos) RequiredOperatingSystem = new OperatingSystem {Name = "BeOS", MajorVersion = 4}; else if(haiku) RequiredOperatingSystem = new OperatingSystem {Name = "Haiku"}; else if(lynxos) RequiredOperatingSystem = new OperatingSystem {Name = "LynxOS"}; @@ -502,6 +568,34 @@ namespace libexeinfo Segments = segments; } + static OperatingSystem GetOsByInterpreter(string interpreter, bool skyos, bool solaris) + { + switch(interpreter) + { + case "/usr/lib/ldqnx.so.2": + return new OperatingSystem {Name = "QNX", MajorVersion = 6}; + + break; + case "/usr/dglib/libc.so.1": + return new OperatingSystem {Name = "DG/UX"}; + + break; + case "/shlib/ld-bsdi.so": + return new OperatingSystem {Name = "BSD/OS"}; + + break; + case "/usr/lib/libc.so.1" when skyos: + return new OperatingSystem {Name = "SkyOS"}; + + break; + case "/usr/lib/ld.so.1" when solaris: + return new OperatingSystem {Name = "Solaris"}; + + break; + default: return new OperatingSystem {Name = interpreter}; + } + } + static Elf64_Ehdr UpBits(byte[] buffer, bool bigEndian) { Elf32_Ehdr ehdr32 = bigEndian @@ -552,6 +646,24 @@ namespace libexeinfo }; } + static Elf64_Phdr UpBitsProgramHeader(byte[] buffer, bool bigEndian) + { + Elf32_Phdr phdr32 = bigEndian + ? BigEndianMarshal.ByteArrayToStructureBigEndian(buffer) + : BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer); + return new Elf64_Phdr + { + p_offset = phdr32.p_offset, + p_type = (phType)(bigEndian ? Swapping.Swap((uint)phdr32.p_type) : (uint)phdr32.p_type), + p_vaddr = phdr32.p_vaddr, + p_paddr = phdr32.p_paddr, + p_filesz = phdr32.p_filesz, + p_memsz = phdr32.p_memsz, + p_flags = phdr32.p_flags, + p_align = phdr32.p_align + }; + } + /// /// Identifies if the specified executable is an Executable and Linkable Format /// diff --git a/libexeinfo/ELF/Enums.cs b/libexeinfo/ELF/Enums.cs index 0dc382f..44e3b8b 100644 --- a/libexeinfo/ELF/Enums.cs +++ b/libexeinfo/ELF/Enums.cs @@ -953,5 +953,72 @@ namespace libexeinfo kNetBSD = 4, Syllable = 5 } + + enum phType : uint + { + /// + /// The array element is unused; other members' values are undefined. This type lets the program header table have + /// ignored entries. + /// + PT_NULL = 0, + /// + /// The array element specifies a loadable segment, described by p_filesz and p_memsz. The bytes from the file are + /// mapped to the beginning of the memory segment. If the segment's memory size (p_memsz) is larger than the file size + /// (p_filesz), the ``extra'' bytes are defined to hold the value 0 and to follow the segment's initialized area. The + /// file size may not be larger than the memory size. Loadable segment entries in the program header table appear in + /// ascending order, sorted on the p_vaddr member. + /// + PT_LOAD = 1, + /// The array element specifies dynamic linking information. + PT_DYNAMIC = 2, + /// The array element specifies the location and size of a null-terminated path name to invoke as an interpreter. + PT_INTERP = 3, + /// The array element specifies the location and size of auxiliary information. + PT_NOTE = 4, + /// + /// This segment type is reserved but has unspecified semantics. Programs that contain an array element of this + /// type do not conform to the ABI. + /// + PT_SHLIB = 5, + /// + /// The array element, if present, specifies the location and size of the program header table itself, both in the + /// file and in the memory image of the program. This segment type may not occur more than once in a file. Moreover, it + /// may occur only if the program header table is part of the memory image of the program. If it is present, it must + /// precede any loadable segment entry. + /// + PT_PHDR = 6, + /// + /// The array element specifies the Thread-Local Storage template. Implementations need not support this program + /// table entry. + /// + PT_TLS = 7, + /// + /// Values from this to are reserved for operating system-specific semantics. + /// + PT_LOOS = 0x60000000, + /// + /// Values from to this are reserved for operating system-specific semantics. + /// + PT_HIOS = 0x6fffffff, + /// + /// Values from this to are reserved for processor-specific semantics. + /// + PT_LOPROC = 0x70000000, + /// + /// Values from to this are reserved for processor-specific semantics. + /// + PT_HIPROC = 0x7fffffff + } + + [Flags] + enum phFlags : uint + { + /// Executable + PF_X, + /// Writable + PF_W, + /// Readable + PF_R + } } } \ No newline at end of file diff --git a/libexeinfo/ELF/Info.cs b/libexeinfo/ELF/Info.cs index 2cc36c4..b5db88a 100644 --- a/libexeinfo/ELF/Info.cs +++ b/libexeinfo/ELF/Info.cs @@ -57,6 +57,8 @@ namespace libexeinfo if(!string.IsNullOrEmpty(interpreter)) sb.AppendFormat("\tInterpreter: {0}", interpreter).AppendLine(); + if(sections == null || sections.Length == 1) return sb.ToString(); + for(int i = 1; i < sections.Length; i++) { sb.AppendFormat("\tSection {0}:", i).AppendLine(); diff --git a/libexeinfo/ELF/Structs.cs b/libexeinfo/ELF/Structs.cs index 803f00e..292de66 100644 --- a/libexeinfo/ELF/Structs.cs +++ b/libexeinfo/ELF/Structs.cs @@ -372,17 +372,17 @@ namespace libexeinfo struct ElfNote { - public string name; - public uint type; + public string name; + public uint type; public byte[] contents; } class GnuAbiTag { + public uint major; + public uint minor; + public uint revision; public GnuAbiSystem system; - public uint major; - public uint minor; - public uint revision; } struct FreeBSDTag @@ -391,5 +391,71 @@ namespace libexeinfo public uint minor; public uint revision; } + + struct Elf32_Phdr + { + /// + /// This member tells what kind of segment this array element describes or how to interpret the array element's + /// information. + /// + public phType p_type; + /// This member gives the offset from the beginning of the file at which the first byte of the segment resides. + public uint p_offset; + /// This member gives the virtual address at which the first byte of the segment resides in memory. + public uint p_vaddr; + /// + /// On systems for which physical addressing is relevant, this member is reserved for the segment's physical + /// address. Because System V ignores physical addressing for application programs, this member has unspecified + /// contents for executable files and shared objects. + /// + public uint p_paddr; + /// This member gives the number of bytes in the file image of the segment; it may be zero. + public uint p_filesz; + /// This member gives the number of bytes in the memory image of the segment; it may be zero. + public uint p_memsz; + /// This member gives flags relevant to the segment. + public phFlags p_flags; + /// + /// As ``Program Loading'' describes in this chapter of the processor supplement, loadable process segments must + /// have congruent values for and , modulo the page size. This member + /// gives the value to which the segments are aligned in memory and in the file. Values 0 and 1 mean no alignment is + /// required. Otherwise, should be a positive, integral power of 2, and + /// should equal , modulo . + /// + public uint p_align; + } + + struct Elf64_Phdr + { + /// + /// This member tells what kind of segment this array element describes or how to interpret the array element's + /// information. + /// + public phType p_type; + /// This member gives flags relevant to the segment. + public phFlags p_flags; + /// This member gives the offset from the beginning of the file at which the first byte of the segment resides. + public ulong p_offset; + /// This member gives the virtual address at which the first byte of the segment resides in memory. + public ulong p_vaddr; + /// + /// On systems for which physical addressing is relevant, this member is reserved for the segment's physical + /// address. Because System V ignores physical addressing for application programs, this member has unspecified + /// contents for executable files and shared objects. + /// + public ulong p_paddr; + /// This member gives the number of bytes in the file image of the segment; it may be zero. + public ulong p_filesz; + /// This member gives the number of bytes in the memory image of the segment; it may be zero. + public ulong p_memsz; + /// + /// As ``Program Loading'' describes in this chapter of the processor supplement, loadable process segments must + /// have congruent values for and , modulo the page size. This member + /// gives the value to which the segments are aligned in memory and in the file. Values 0 and 1 mean no alignment is + /// required. Otherwise, should be a positive, integral power of 2, and + /// should equal , modulo . + /// + public ulong p_align; + } } } \ No newline at end of file