Support ELF files without sections but with program headers.

This commit is contained in:
2018-03-14 23:13:24 +00:00
parent 60601230e3
commit 51f2591a82
4 changed files with 275 additions and 28 deletions

View File

@@ -13,6 +13,7 @@ namespace libexeinfo
Elf64_Ehdr Header;
string interpreter;
Dictionary<string, ElfNote> notes;
Elf64_Phdr[] programHeaders;
string[] sectionNames;
Elf64_Shdr[] sections;
@@ -107,7 +108,92 @@ namespace libexeinfo
List<string> strings = new List<string>();
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<Elf64_Phdr>(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<Elf64_Phdr>(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<Elf32_Phdr>(buffer)
: BigEndianMarshal.ByteArrayToStructureLittleEndian<Elf32_Phdr>(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
};
}
/// <summary>
/// Identifies if the specified executable is an Executable and Linkable Format
/// </summary>

View File

@@ -953,5 +953,72 @@ namespace libexeinfo
kNetBSD = 4,
Syllable = 5
}
enum phType : uint
{
/// <summary>
/// The array element is unused; other members' values are undefined. This type lets the program header table have
/// ignored entries.
/// </summary>
PT_NULL = 0,
/// <summary>
/// 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.
/// </summary>
PT_LOAD = 1,
/// <summary>The array element specifies dynamic linking information.</summary>
PT_DYNAMIC = 2,
/// <summary>The array element specifies the location and size of a null-terminated path name to invoke as an interpreter.</summary>
PT_INTERP = 3,
/// <summary>The array element specifies the location and size of auxiliary information.</summary>
PT_NOTE = 4,
/// <summary>
/// This segment type is reserved but has unspecified semantics. Programs that contain an array element of this
/// type do not conform to the ABI.
/// </summary>
PT_SHLIB = 5,
/// <summary>
/// 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.
/// </summary>
PT_PHDR = 6,
/// <summary>
/// The array element specifies the Thread-Local Storage template. Implementations need not support this program
/// table entry.
/// </summary>
PT_TLS = 7,
/// <summary>
/// Values from this to <see cref="PT_HIOS" /> are reserved for operating system-specific semantics.
/// </summary>
PT_LOOS = 0x60000000,
/// <summary>
/// Values from <see cref="PT_LOOS" /> to this are reserved for operating system-specific semantics.
/// </summary>
PT_HIOS = 0x6fffffff,
/// <summary>
/// Values from this to <see cref="PT_HIPROC" /> are reserved for processor-specific semantics.
/// </summary>
PT_LOPROC = 0x70000000,
/// <summary>
/// Values from <see cref="PT_LOPROC" /> to this are reserved for processor-specific semantics.
/// </summary>
PT_HIPROC = 0x7fffffff
}
[Flags]
enum phFlags : uint
{
/// <summary>Executable</summary>
PF_X,
/// <summary>Writable</summary>
PF_W,
/// <summary>Readable</summary>
PF_R
}
}
}

View File

@@ -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();

View File

@@ -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
{
/// <summary>
/// This member tells what kind of segment this array element describes or how to interpret the array element's
/// information.
/// </summary>
public phType p_type;
/// <summary>This member gives the offset from the beginning of the file at which the first byte of the segment resides.</summary>
public uint p_offset;
/// <summary>This member gives the virtual address at which the first byte of the segment resides in memory.</summary>
public uint p_vaddr;
/// <summary>
/// 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.
/// </summary>
public uint p_paddr;
/// <summary>This member gives the number of bytes in the file image of the segment; it may be zero.</summary>
public uint p_filesz;
/// <summary>This member gives the number of bytes in the memory image of the segment; it may be zero.</summary>
public uint p_memsz;
/// <summary>This member gives flags relevant to the segment.</summary>
public phFlags p_flags;
/// <summary>
/// As ``Program Loading'' describes in this chapter of the processor supplement, loadable process segments must
/// have congruent values for <see cref="p_vaddr" /> and <see cref="p_offset" />, 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, <see cref="p_align" /> should be a positive, integral power of 2, and <see cref="p_vaddr" />
/// should equal <see cref="p_offset" />, modulo <see cref="p_align" />.
/// </summary>
public uint p_align;
}
struct Elf64_Phdr
{
/// <summary>
/// This member tells what kind of segment this array element describes or how to interpret the array element's
/// information.
/// </summary>
public phType p_type;
/// <summary>This member gives flags relevant to the segment.</summary>
public phFlags p_flags;
/// <summary>This member gives the offset from the beginning of the file at which the first byte of the segment resides.</summary>
public ulong p_offset;
/// <summary>This member gives the virtual address at which the first byte of the segment resides in memory.</summary>
public ulong p_vaddr;
/// <summary>
/// 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.
/// </summary>
public ulong p_paddr;
/// <summary>This member gives the number of bytes in the file image of the segment; it may be zero.</summary>
public ulong p_filesz;
/// <summary>This member gives the number of bytes in the memory image of the segment; it may be zero.</summary>
public ulong p_memsz;
/// <summary>
/// As ``Program Loading'' describes in this chapter of the processor supplement, loadable process segments must
/// have congruent values for <see cref="p_vaddr" /> and <see cref="p_offset" />, 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, <see cref="p_align" /> should be a positive, integral power of 2, and <see cref="p_vaddr" />
/// should equal <see cref="p_offset" />, modulo <see cref="p_align" />.
/// </summary>
public ulong p_align;
}
}
}