mirror of
https://github.com/claunia/libexeinfo.git
synced 2025-12-16 19:14:24 +00:00
Support ELF files without sections but with program headers.
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user