Skip to content

Instantly share code, notes, and snippets.

@alexanderankin
Created March 22, 2023 15:06
Show Gist options
  • Select an option

  • Save alexanderankin/143f49820d12d3e76397fcbfd722bc8b to your computer and use it in GitHub Desktop.

Select an option

Save alexanderankin/143f49820d12d3e76397fcbfd722bc8b to your computer and use it in GitHub Desktop.
import lombok.*;
import lombok.experimental.Accessors;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class ElfReader {
@SneakyThrows
public static void main(String[] args) {
ByteBuffer byteBuffer = ByteBuffer.wrap(Files.readAllBytes(Path.of("/usr/bin/cat")));
ElfHeader elfHeader = new ElfReader()
.parseHeader(byteBuffer);
// System.out.println(elfHeader);
System.out.println(elfHeader.toReport());
}
static byte[] copy(ByteBuffer byteBuffer) {
if (!byteBuffer.hasArray()) {
int position = byteBuffer.position();
byteBuffer.position(0);
byte[] copy = new byte[byteBuffer.remaining()];
byteBuffer.get(copy);
byteBuffer.position(position);
return copy;
}
return Arrays.copyOfRange(byteBuffer.array(),
byteBuffer.arrayOffset(),
byteBuffer.limit());
}
ElfHeader parseHeader(ByteBuffer byteBuffer) {
ByteBuffer eIdent = byteBuffer.slice(0, 16);
ElfIdent elfIdent = new ElfIdent().setValue(eIdent);
if (elfIdent.eiClass() == null) throw new UnsupportedOperationException("unknown value");
ElfHeaderBody body = switch (elfIdent.eiClass()) {
case CLASS64 -> new ElfHeaderBody64().setByteBuffer(byteBuffer.slice(16, 36));
case CLASS32 -> new ElfHeaderBody32().setByteBuffer(byteBuffer.slice(16, 36));
case NONE -> throw new UnsupportedOperationException("not sure how to handle");
};
// ElfHeaderBody64 body1 = (ElfHeaderBody64) body;
// ByteBuffer.wrap(copy(body1.byteBuffer)).order(ByteOrder.LITTLE_ENDIAN).getShort(2);
// short i = body1.byteBuffer.getShort(4);
// ElfMachine elfMachine = ElfMachine.valueOf(i);
// System.out.println(elfMachine);
return new ElfHeader()
.setElfIdent(elfIdent)
.setElfHeaderBody(body)
;
}
/**
* This member identifies the object file type.
*/
@AllArgsConstructor
@ToString
@Getter
enum ElfType {
//<editor-fold desc="members">
/**
* No file type
*/
ET_NONE(0),
/**
* Relocatable file
*/
ET_REL(1),
/**
* Executable file
*/
ET_EXEC(2),
/**
* Shared object file
*/
ET_DYN(3),
/**
* Core file
*/
ET_CORE(4),
/**
* Operating system-specific
*/
ET_LOOS(0xfe00),
/**
* Operating system-specific
*/
ET_HIOS(0xfeff),
/**
* Processor-specific
*/
ET_LOPROC(0xff00),
/**
* Processor-specific
*/
ET_HIPROC(0xffff),
//</editor-fold>
;
private static final Map<Integer, ElfType> byCode =
Arrays.stream(values())
.collect(Collectors.toMap(ElfType::getCode,
Function.identity()));
private final int code;
public static ElfType valueOf(int code) {
return byCode.get(code);
}
}
/**
* This member's value specifies the required architecture for an individual file.
*/
@AllArgsConstructor
@ToString
@Getter
enum ElfMachine {
//<editor-fold desc="members">
/**
* No machine
*/
EM_NONE(0),
/**
* AT&T WE 32100
*/
EM_M32(1),
/**
* SPARC
*/
EM_SPARC(2),
/**
* Intel 80386
*/
EM_386(3),
/**
* Motorola 68000
*/
EM_68K(4),
/**
* Motorola 88000
*/
EM_88K(5),
/**
* Intel 80860
*/
EM_860(7),
/**
* MIPS I Architecture
*/
EM_MIPS(8),
/**
* IBM System/370 Processor
*/
EM_S370(9),
/**
* MIPS RS3000 Little-endian
*/
EM_MIPS_RS3_LE(10),
/**
* Hewlett-Packard PA-RISC
*/
EM_PARISC(15),
/**
* Fujitsu VPP500
*/
EM_VPP500(17),
/**
* Enhanced instruction set SPARC
*/
EM_SPARC32PLUS(18),
/**
* Intel 80960
*/
EM_960(19),
/**
* PowerPC
*/
EM_PPC(20),
/**
* 64-bit PowerPC
*/
EM_PPC64(21),
/**
* IBM System/390 Processor
*/
EM_S390(22),
/**
* NEC V800
*/
EM_V800(36),
/**
* Fujitsu FR20
*/
EM_FR20(37),
/**
* TRW RH-32
*/
EM_RH32(38),
/**
* Motorola RCE
*/
EM_RCE(39),
/**
* Advanced RISC Machines ARM
*/
EM_ARM(40),
/**
* Digital Alpha
*/
EM_ALPHA(41),
/**
* Hitachi SH
*/
EM_SH(42),
/**
* SPARC Version 9
*/
EM_SPARCV9(43),
/**
* Siemens TriCore embedded processor
*/
EM_TRICORE(44),
/**
* Argonaut RISC Core, Argonaut Technologies Inc.
*/
EM_ARC(45),
/**
* Hitachi H8/300
*/
EM_H8_300(46),
/**
* Hitachi H8/300H
*/
EM_H8_300H(47),
/**
* Hitachi H8S
*/
EM_H8S(48),
/**
* Hitachi H8/500
*/
EM_H8_500(49),
/**
* Intel IA-64 processor architecture
*/
EM_IA_64(50),
/**
* Stanford MIPS-X
*/
EM_MIPS_X(51),
/**
* Motorola ColdFire
*/
EM_COLDFIRE(52),
/**
* Motorola M68HC12
*/
EM_68HC12(53),
/**
* Fujitsu MMA Multimedia Accelerator
*/
EM_MMA(54),
/**
* Siemens PCP
*/
EM_PCP(55),
/**
* Sony nCPU embedded RISC processor
*/
EM_NCPU(56),
/**
* Denso NDR1 microprocessor
*/
EM_NDR1(57),
/**
* Motorola Star*Core processor
*/
EM_STARCORE(58),
/**
* Toyota ME16 processor
*/
EM_ME16(59),
/**
* STMicroelectronics ST100 processor
*/
EM_ST100(60),
/**
* Advanced Logic Corp. TinyJ embedded processor family
*/
EM_TINYJ(61),
/**
* AMD x86-64 architecture
*/
EM_X86_64(62),
/**
* Sony DSP Processor
*/
EM_PDSP(63),
/**
* Digital Equipment Corp. PDP-10
*/
EM_PDP10(64),
/**
* Digital Equipment Corp. PDP-11
*/
EM_PDP11(65),
/**
* Siemens FX66 microcontroller
*/
EM_FX66(66),
/**
* STMicroelectronics ST9+ 8/16 bit microcontroller
*/
EM_ST9PLUS(67),
/**
* STMicroelectronics ST7 8-bit microcontroller
*/
EM_ST7(68),
/**
* Motorola MC68HC16 Microcontroller
*/
EM_68HC16(69),
/**
* Motorola MC68HC11 Microcontroller
*/
EM_68HC11(70),
/**
* Motorola MC68HC08 Microcontroller
*/
EM_68HC08(71),
/**
* Motorola MC68HC05 Microcontroller
*/
EM_68HC05(72),
/**
* Silicon Graphics SVx
*/
EM_SVX(73),
/**
* STMicroelectronics ST19 8-bit microcontroller
*/
EM_ST19(74),
/**
* Digital VAX
*/
EM_VAX(75),
/**
* Axis Communications 32-bit embedded processor
*/
EM_CRIS(76),
/**
* Infineon Technologies 32-bit embedded processor
*/
EM_JAVELIN(77),
/**
* Element 14 64-bit DSP Processor
*/
EM_FIREPATH(78),
/**
* LSI Logic 16-bit DSP Processor
*/
EM_ZSP(79),
/**
* Donald Knuth's educational 64-bit processor
*/
EM_MMIX(80),
/**
* Harvard University machine-independent object files
*/
EM_HUANY(81),
/**
* SiTera Prism
*/
EM_PRISM(82),
/**
* Atmel AVR 8-bit microcontroller
*/
EM_AVR(83),
/**
* Fujitsu FR30
*/
EM_FR30(84),
/**
* Mitsubishi D10V
*/
EM_D10V(85),
/**
* Mitsubishi D30V
*/
EM_D30V(86),
/**
* NEC v850
*/
EM_V850(87),
/**
* Mitsubishi M32R
*/
EM_M32R(88),
/**
* Matsushita MN10300
*/
EM_MN10300(89),
/**
* Matsushita MN10200
*/
EM_MN10200(90),
/**
* picoJava
*/
EM_PJ(91),
/**
* OpenRISC 32-bit embedded processor
*/
EM_OPENRISC(92),
/**
* ARC Cores Tangent-A5
*/
EM_ARC_A5(93),
/**
* Tensilica Xtensa Architecture
*/
EM_XTENSA(94),
/**
* Alphamosaic VideoCore processor
*/
EM_VIDEOCORE(95),
/**
* Thompson Multimedia General Purpose Processor
*/
EM_TMM_GPP(96),
/**
* National Semiconductor 32000 series
*/
EM_NS32K(97),
/**
* Tenor Network TPC processor
*/
EM_TPC(98),
/**
* Trebia SNP 1000 processor
*/
EM_SNP1K(99),
/**
* STMicroelectronics (www.st.com) ST200 microcontroller
*/
EM_ST200(100),
//</editor-fold>
/**
* Reserved for future use (was EM_486)
*/
RESERVED_WAS_EM_486(6),
/**
* Reserved for future use
*/
reserved(null),
;
private static final Map<Integer, ElfMachine> values =
Arrays.stream(values())
.filter(e -> null != e.getCode())
.collect(Collectors.toMap(ElfMachine::getCode,
Function.identity()));
final Integer code;
public static ElfMachine valueOf(int code) {
ElfMachine elfMachine = values.get(code);
if (elfMachine != null) return elfMachine;
if (code >= 11 && code <= 14) return reserved;
if (code == 16) return reserved;
if (code >= 23 && code <= 35) return reserved;
return null;
}
}
interface ElfHeaderBody {
ByteBuffer getByteBuffer();
}
/**
* <pre>
* #define EI_NIDENT 16
*
* typedef struct {
* unsigned char e_ident[EI_NIDENT];
* Elf32_Half e_type;
* Elf32_Half e_machine;
* Elf32_Word e_version;
* Elf32_Addr e_entry;
* Elf32_Off e_phoff;
* Elf32_Off e_shoff;
* Elf32_Word e_flags;
* Elf32_Half e_ehsize;
* Elf32_Half e_phentsize;
* Elf32_Half e_phnum;
* Elf32_Half e_shentsize;
* Elf32_Half e_shnum;
* Elf32_Half e_shstrndx;
* } Elf32_Ehdr;
*
* typedef struct {
* unsigned char e_ident[EI_NIDENT];
* Elf64_Half e_type;
* Elf64_Half e_machine;
* Elf64_Word e_version;
* Elf64_Addr e_entry;
* Elf64_Off e_phoff;
* Elf64_Off e_shoff;
* Elf64_Word e_flags;
* Elf64_Half e_ehsize;
* Elf64_Half e_phentsize;
* Elf64_Half e_phnum;
* Elf64_Half e_shentsize;
* Elf64_Half e_shnum;
* Elf64_Half e_shstrndx;
* } Elf64_Ehdr;
* </pre>
*/
@Accessors(chain = true)
@Data
static class ElfHeader {
ElfIdent elfIdent;
ElfHeaderBody elfHeaderBody;
public String toReport() {
var _class = elfIdent.eiClass();
var data = elfIdent.eiData();
var version = elfIdent.eiVersion();
var osAbi = elfIdent.eiOsAbi();
var abiVersion = elfIdent.eiAbiVersion();
var type = elfHeaderBody.getByteBuffer();
var machine = "null";
var versionNum = "null";
var entryPointAddress = "null";
var startOfProgramHeaders = "null";
var startOfSectionHeaders = "null";
var flags = "null";
var sizeOfThisHeader = "null";
var sizeOfProgramHeaders = "null";
var numberOfProgramHeaders = "null";
var sizeOfSectionHeaders = "null";
var numberOfSectionHeaders = "null";
var sectionHeaderStringTableIndex = "null";
String magic = elfIdent.bytesHex();
return """
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Magic: %s
Class: ELF64
Class: %s
Data: 2's complement, little endian
Data: %s
Version: 1 (current)
Version: %s
OS/ABI: UNIX - System V
OS/ABI: %s
ABI Version: 0
ABI Version: %s
Type: DYN (Shared object file)
Type: %s
Machine: Advanced Micro Devices X86-64
Machine: %s
Version: 0x1
Version: %s
Entry point address: 0x31f0
Entry point address: %s
Start of program headers: 64 (bytes into file)
Start of program headers: %s
Start of section headers: 41496 (bytes into file)
Start of section headers: %s
Flags: 0x0
Flags: %s
Size of this header: 64 (bytes)
Size of this header: %s
Size of program headers: 56 (bytes)
Size of program headers: %s
Number of program headers: 13
Number of program headers: %s
Size of section headers: 64 (bytes)
Size of section headers: %s
Number of section headers: 30
Number of section headers: %s
Section header string table index: 29
Section header string table index: %s
""".formatted(
magic,
_class,
data,
version,
osAbi,
abiVersion,
type,
machine,
versionNum,
entryPointAddress,
startOfProgramHeaders,
startOfSectionHeaders,
flags,
sizeOfThisHeader,
sizeOfProgramHeaders,
numberOfProgramHeaders,
sizeOfSectionHeaders,
numberOfSectionHeaders,
sectionHeaderStringTableIndex);
}
}
@Accessors(chain = true)
@Data
static abstract class ElfHeaderBodyBase implements ElfHeaderBody {
protected ByteBuffer byteBuffer;
}
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@Data
static class ElfHeaderBody32 extends ElfHeaderBodyBase {
}
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@Data
static class ElfHeaderBody64 extends ElfHeaderBodyBase {
@ToString.Include
short e_type() {
return byteBuffer.getShort(0);
}
@ToString.Include
short e_machine() {
return byteBuffer.getShort(2);
}
@ToString.Include /*Word*/ int e_version() {
return byteBuffer.getInt(4);
}
@ToString.Include /*Addr*/ int e_entry() {
return byteBuffer.getInt(8);
}
@ToString.Include /*Off*/ int e_phoff() {
return byteBuffer.getInt(12);
}
@ToString.Include /*Off*/ int e_shoff() {
return byteBuffer.getInt(16);
}
@ToString.Include /*Word*/ int e_flags() {
return byteBuffer.getInt(20);
}
@ToString.Include
short e_ehsize() {
return byteBuffer.getShort(24);
}
@ToString.Include
short e_phentsize() {
return byteBuffer.getShort(26);
}
@ToString.Include
short e_phnum() {
return byteBuffer.getShort(28);
}
@ToString.Include
short e_shentsize() {
return byteBuffer.getShort(30);
}
@ToString.Include
short e_shnum() {
return byteBuffer.getShort(32);
}
@ToString.Include
short e_shstrndx() {
return byteBuffer.getShort(34);
}
}
@Accessors(chain = true)
@Data
static class ElfIdent {
public static final int EI_NIDENT = 16;
public static final byte[] ELF_MAGIC_NUMBER = new byte[]{127, 'E', 'L', 'F'};
// 16 chars
ByteBuffer value;
byte[] magicNumber() {
return copy(value.slice(0, 4));
}
boolean magicNumberIsElf() {
return Arrays.equals(ELF_MAGIC_NUMBER, magicNumber());
}
public String bytesHex() {
StringBuilder magic = new StringBuilder();
ByteBuffer mn = value.slice();
while (mn.hasRemaining()) {
String part = Integer.toHexString(mn.get());
magic
.append(part.length() == 1 ? "0" : "")
.append(part)
.append(" ");
}
return magic.toString();
}
byte eiClassValue() {
return value.get(4);
}
@ToString.Include
EiClass eiClass() {
return EiClass.valueOf(eiClassValue());
}
byte eiDataValue() {
return value.get(5);
}
@ToString.Include
EiData eiData() {
return EiData.valueOf(eiDataValue());
}
/**
* Byte e_ident[EI_VERSION] specifies the ELF header version number.
* Currently, this value must be EV_CURRENT, as explained above
* for e_version.
*
* @return version
*/
@ToString.Include
byte eiVersion() {
return value.get(6);
}
byte eiOsAbiValue() {
return value.get(7);
}
@ToString.Include
EiOsAbi eiOsAbi() {
return EiOsAbi.valueOf(eiOsAbiValue());
}
/**
* Byte e_ident[EI_ABIVERSION] identifies the version of the ABI to
* which the object is targeted. This field is used to
* distinguish among incompatible versions of an ABI. The
* interpretation of this version number is dependent on the ABI
* identified by the EI_OSABI field. If no values are specified
* for the EI_OSABI field by the processor supplement or no
* version values are specified for the ABI determined by a
* particular value of the EI_OSABI byte, the value 0 shall be
* used for the EI_ABIVERSION byte; it indicates unspecified.
*/
@ToString.Include
byte eiAbiVersion() {
return value.get(8);
}
/**
* This value marks the beginning of the unused bytes in e_ident.
* These bytes are reserved and set to zero; programs that read
* object files should ignore them. The value of EI_PAD will
* change in the future if currently unused bytes are given meanings.
*/
byte eiPad() {
return value.get(9);
}
/**
* EI_CLASS
* <p>
* The next byte, e_ident[EI_CLASS], identifies the file's class, or capacity.
*/
@AllArgsConstructor
@Getter
enum EiClass {
/**
* Invalid class
*/
NONE(0, "NONE"),
/**
* 32-bit objects
*/
CLASS32(1, "ELF32"),
/**
* 64-bit objects
*/
CLASS64(2, "ELF64"),
;
private static final Map<Integer, EiClass> byCode =
Arrays.stream(values())
.collect(Collectors.toMap(EiClass::getCode,
Function.identity()));
final int code;
final String binUtilsName;
public static EiClass valueOf(int code) {
return byCode.get(code);
}
@Override
public String toString() {
return binUtilsName;
}
}
/**
* EI_DATA
* <p>
* Byte e_ident[EI_DATA] specifies the encoding of both the data
* structures used by object file container and data contained in
* object file sections. The following encodings are currently defined.
* <p>
* Other values are reserved and will be assigned to new encodings as necessary.
*/
@AllArgsConstructor
@Getter
enum EiData {
/**
* Invalid class
*/
NONE(0, "NONE"),
/**
* Encoding ELFDATA2LSB specifies 2's complement values,
* with the least significant byte occupying the lowest address.
*/
LSB(1, "2's complement, little endian"),
/**
* Encoding ELFDATA2MSB specifies 2's complement values,
* with the most significant byte occupying the lowest address.
*/
MSB(2, "2's complement, big endian"),
;
private static final Map<Integer, EiData> byCode =
Arrays.stream(values())
.collect(Collectors.toMap(EiData::getCode,
Function.identity()));
final int code;
final String binUtilsName;
public static EiData valueOf(int code) {
return byCode.get(code);
}
@Override
public String toString() {
return binUtilsName;
}
}
/**
* EI_OSABI
* <p>
* Byte e_ident[EI_OSABI] identifies the OS- or ABI-specific ELF
* extensions used by this file. Some fields in other ELF
* structures have flags and values that have operating system
* and/or ABI specific meanings; the interpretation of those
* fields is determined by the value of this byte. If the object
* file does not use any extensions, it is recommended that this
* byte be set to 0. If the value for this byte is 64 through
* 255, its meaning depends on the value of the e_machine header
* member. The ABI processor supplement for an architecture can
* define its own associated set of values for this byte in this
* range. If the processor supplement does not specify a set of
* values, one of the following values shall be used, where 0 can
* also be taken to mean unspecified.
*/
@AllArgsConstructor
@ToString
@Getter
enum EiOsAbi {
//<editor-fold desc="members">
/**
* No extensions or unspecified
*/
NONE(0),
/**
* Hewlett-Packard HP-UX
*/
HPUX(1),
/**
* NetBSD
*/
NETBSD(2),
/**
* Linux
*/
LINUX(3),
/**
* Sun Solaris
*/
SOLARIS(6),
/**
* AIX
*/
AIX(7),
/**
* IRIX
*/
IRIX(8),
/**
* FreeBSD
*/
FREEBSD(9),
/**
* Compaq TRU64 UNIX
*/
TRU64(10),
/**
* Novell Modesto
*/
MODESTO(11),
/**
* Open BSD
*/
OPENBSD(12),
/**
* Open VMS
*/
OPENVMS(13),
/**
* Hewlett-Packard Non-Stop Kernel
*/
NSK(14),
//</editor-fold>
/**
* Architecture-specific value range 64-255
*/
ARCH_SPECIFIC(null),
;
private static final Map<Integer, EiOsAbi> byCode =
Arrays.stream(values())
.filter(e -> e.getCode() != null)
.collect(Collectors.toMap(EiOsAbi::getCode,
Function.identity()));
final Integer code;
public static EiOsAbi valueOf(int code) {
EiOsAbi eiData = byCode.get(code);
if (eiData != null) return eiData;
if (code >= 64 && code <= 255) return ARCH_SPECIFIC;
return null;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment