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;
|
Elf64_Ehdr Header;
|
||||||
string interpreter;
|
string interpreter;
|
||||||
Dictionary<string, ElfNote> notes;
|
Dictionary<string, ElfNote> notes;
|
||||||
|
Elf64_Phdr[] programHeaders;
|
||||||
string[] sectionNames;
|
string[] sectionNames;
|
||||||
Elf64_Shdr[] sections;
|
Elf64_Shdr[] sections;
|
||||||
|
|
||||||
@@ -107,7 +108,92 @@ namespace libexeinfo
|
|||||||
|
|
||||||
List<string> strings = new List<string>();
|
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;
|
BaseStream.Position = (long)Header.e_shoff;
|
||||||
buffer = new byte[Header.e_shentsize];
|
buffer = new byte[Header.e_shentsize];
|
||||||
@@ -138,7 +224,7 @@ namespace libexeinfo
|
|||||||
buffer = new byte[sections[Header.e_shstrndx].sh_size];
|
buffer = new byte[sections[Header.e_shstrndx].sh_size];
|
||||||
BaseStream.Read(buffer, 0, buffer.Length);
|
BaseStream.Read(buffer, 0, buffer.Length);
|
||||||
sectionNames = new string[sections.Length];
|
sectionNames = new string[sections.Length];
|
||||||
Segment[] segments = new Segment[sections.Length];
|
segments = new Segment[sections.Length];
|
||||||
|
|
||||||
int len = 0;
|
int len = 0;
|
||||||
int pos;
|
int pos;
|
||||||
@@ -444,27 +530,7 @@ namespace libexeinfo
|
|||||||
default: throw new ArgumentOutOfRangeException();
|
default: throw new ArgumentOutOfRangeException();
|
||||||
}
|
}
|
||||||
else if(!string.IsNullOrEmpty(interpreter))
|
else if(!string.IsNullOrEmpty(interpreter))
|
||||||
switch(interpreter)
|
RequiredOperatingSystem = GetOsByInterpreter(interpreter, skyos, solaris);
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
else if(beos) RequiredOperatingSystem = new OperatingSystem {Name = "BeOS", MajorVersion = 4};
|
else if(beos) RequiredOperatingSystem = new OperatingSystem {Name = "BeOS", MajorVersion = 4};
|
||||||
else if(haiku) RequiredOperatingSystem = new OperatingSystem {Name = "Haiku"};
|
else if(haiku) RequiredOperatingSystem = new OperatingSystem {Name = "Haiku"};
|
||||||
else if(lynxos) RequiredOperatingSystem = new OperatingSystem {Name = "LynxOS"};
|
else if(lynxos) RequiredOperatingSystem = new OperatingSystem {Name = "LynxOS"};
|
||||||
@@ -502,6 +568,34 @@ namespace libexeinfo
|
|||||||
Segments = segments;
|
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)
|
static Elf64_Ehdr UpBits(byte[] buffer, bool bigEndian)
|
||||||
{
|
{
|
||||||
Elf32_Ehdr ehdr32 = 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>
|
/// <summary>
|
||||||
/// Identifies if the specified executable is an Executable and Linkable Format
|
/// Identifies if the specified executable is an Executable and Linkable Format
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -953,5 +953,72 @@ namespace libexeinfo
|
|||||||
kNetBSD = 4,
|
kNetBSD = 4,
|
||||||
Syllable = 5
|
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(!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++)
|
for(int i = 1; i < sections.Length; i++)
|
||||||
{
|
{
|
||||||
sb.AppendFormat("\tSection {0}:", i).AppendLine();
|
sb.AppendFormat("\tSection {0}:", i).AppendLine();
|
||||||
|
|||||||
@@ -379,10 +379,10 @@ namespace libexeinfo
|
|||||||
|
|
||||||
class GnuAbiTag
|
class GnuAbiTag
|
||||||
{
|
{
|
||||||
public GnuAbiSystem system;
|
|
||||||
public uint major;
|
public uint major;
|
||||||
public uint minor;
|
public uint minor;
|
||||||
public uint revision;
|
public uint revision;
|
||||||
|
public GnuAbiSystem system;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FreeBSDTag
|
struct FreeBSDTag
|
||||||
@@ -391,5 +391,71 @@ namespace libexeinfo
|
|||||||
public uint minor;
|
public uint minor;
|
||||||
public uint revision;
|
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