From 3990ba774f9667c8a5bec4841af3f2fe10feaa80 Mon Sep 17 00:00:00 2001 From: n1073645 Date: Sun, 5 Jun 2022 18:35:02 +0100 Subject: [PATCH] Implemented readelf-like functionality. --- src/core/config/Categories.json | 3 +- src/core/operations/ELFInfo.mjs | 916 +++++++++++++++++++++++++++++ tests/operations/index.mjs | 1 + tests/operations/tests/ELFInfo.mjs | 20 + 4 files changed, 939 insertions(+), 1 deletion(-) create mode 100644 src/core/operations/ELFInfo.mjs create mode 100644 tests/operations/tests/ELFInfo.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index c15ec3d2..5c74c0c2 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -413,7 +413,8 @@ "Extract RGBA", "View Bit Plane", "Randomize Colour Palette", - "Extract LSB" + "Extract LSB", + "ELF Info" ] }, { diff --git a/src/core/operations/ELFInfo.mjs b/src/core/operations/ELFInfo.mjs new file mode 100644 index 00000000..6fa46c54 --- /dev/null +++ b/src/core/operations/ELFInfo.mjs @@ -0,0 +1,916 @@ +/** + * @author n1073645 [n1073645@gmail.com] + * @copyright Crown Copyright 2022 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import Stream from "../lib/Stream.mjs"; +import Utils from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; + +/** + * ELF Info operation + */ +class ELFInfo extends Operation { + + /** + * ELFInfo constructor + */ + constructor() { + super(); + + this.name = "ELF Info"; + this.module = "Default"; + this.description = "Implements readelf-like functionality. This operation will extract the ELF Header, Program Headers, Section Headers and Symbol Table for an ELF file."; + this.infoURL = "https://www.wikipedia.org/wiki/Executable_and_Linkable_Format"; + this.inputType = "ArrayBuffer"; + this.outputType = "string"; + this.args = []; + } + + /** + * @param {ArrayBuffer} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + let phoff = 0; + let phEntries = 0; + let shoff = 0; + let shEntries = 0; + let shentSize = 0; + let entry = 0; + let format = 0; + let endianness = ""; + let shstrtab = 0; + + let namesOffset = 0; + + let symtabOffset = 0; + let symtabSize = 0; + let symtabEntSize = 0; + + let strtabOffset = 0; + const align = 30; + + /** + * This function reads characters until it hits a null terminator. + * + * @param {stream} stream + * @param {integer} namesOffset + * @param {integer} nameOffset + * @returns {string} + */ + function readString(stream, namesOffset, nameOffset) { + const preMove = stream.position; + stream.moveTo(namesOffset + nameOffset); + + let nameResult = ""; + let elem = 0; + while ((elem = stream.readInt(1, endianness)) !== 0) + nameResult += String.fromCharCode(elem); + stream.moveTo(preMove); + return nameResult; + } + + /** + * This function parses and extracts relevant information from the ELF Header. + * + * @param {stream} stream + * @returns {string} + */ + function elfHeader(stream) { + /** + * The ELF Header is comprised of the following structures depending on the binary's format. + * + * e_ident - The Magic Number 0x7F,0x45,0x4c,0x46 + * - Byte set to 1 or 2 to signify 32-bit or 64-bit format, respectively. + * - Byte set to 1 or 2 to signify little of big endianness, respectively. + * - Byte set to 1 for the version of ELF. + * - Byte identifying the target OS ABI. + * - Byte further identifying the OS ABI Version. + * - 7 Padding Bytes. + * e_type - 2 bytes identifying the object file type. + * e_machine - 2 bytes identifying the instruction set architecture. + * e_version - Byte set to 1 for the version of ELF. + * + * 32-bit: + * e_entry - 4 Bytes specifying the entry point. + * e_phoff - 4 Bytes specifying the offset of the Program Header Table. + * e_shoff - 4 Bytes specifying the offset of the Section Header Table. + * + * 64-bit: + * e_entry - 8 Bytes specifying the entry point. + * e_phoff - 8 Bytes specifying the offset of the Program Header Table. + * e_shoff - 8 Bytes specifying the offset of the Section Header Table. + * + * e_flags - 4 Bytes specifying processor specific flags. + * e_ehsize - 2 Bytes specifying the size of the ELF Header. + * e_phentsize - 2 Bytes specifying the size of a Program Header Table Entry. + * e_phnum - 2 Bytes specifying the number of entries in the Program Header Table. + * e_shentsize - 2 Bytes specifying the size of a Section Header Table Entry. + * e_shnum - 2 Bytes specifying the number of entries in the Section Header Table. + * e_shstrndx - 2 Bytes specifying the index of the section containing the section names in the Section Header Table. + */ + const ehResult = []; + + const magic = stream.getBytes(4); + if (magic.join("") !== [0x7f, 0x45, 0x4c, 0x46].join("")) + throw new OperationError("Invalid ELF"); + + ehResult.push("Magic:".padEnd(align) + `${Utils.byteArrayToChars(magic)}`); + + format = stream.readInt(1); + ehResult.push("Format:".padEnd(align) + `${format === 1 ? "32-bit" : "64-bit"}`); + + endianness = stream.readInt(1) === 1 ? "le" : "be"; + ehResult.push("Endianness:".padEnd(align) + `${endianness === 1 ? "Little" : "Big"}`); + + ehResult.push("Version:".padEnd(align) + `${stream.readInt(1).toString()}`); + + let ABI = ""; + switch (stream.readInt(1)) { + case 0x00: + ABI = "System V"; + break; + case 0x01: + ABI = "HP-UX"; + break; + case 0x02: + ABI = "NetBSD"; + break; + case 0x03: + ABI = "Linux"; + break; + case 0x04: + ABI = "GNU Hurd"; + break; + case 0x06: + ABI = "Solaris"; + break; + case 0x07: + ABI = "AIX"; + break; + case 0x08: + ABI = "IRIX"; + break; + case 0x09: + ABI = "FreeBSD"; + break; + case 0x0A: + ABI = "Tru64"; + break; + case 0x0B: + ABI = "Novell Modesto"; + break; + case 0x0C: + ABI = "OpenBSD"; + break; + case 0x0D: + ABI = "OpenVMS"; + break; + case 0x0E: + ABI = "NonStop Kernel"; + break; + case 0x0F: + ABI = "AROS"; + break; + case 0x10: + ABI = "Fenix OS"; + break; + case 0x11: + ABI = "CloudABI"; + break; + case 0x12: + ABI = "Stratus Technologies OpenVOS"; + break; + default: + break; + } + ehResult.push("ABI:".padEnd(align) + ABI); + + // Linux Kernel does not use ABI Version. + const abiVersion = stream.readInt(1).toString(); + if (ABI !== "Linux") + ehResult.push("ABI Version:".padEnd(align) + abiVersion); + + stream.moveForwardsBy(7); + + let eType = ""; + switch (stream.readInt(2, endianness)) { + case 0x0000: + eType = "Unknown"; + break; + case 0x0001: + eType = "Relocatable File"; + break; + case 0x0002: + eType = "Executable File"; + break; + case 0x0003: + eType = "Shared Object"; + break; + case 0x0004: + eType = "Core File"; + break; + case 0xFE00: + eType = "LOOS"; + break; + case 0xFEFF: + eType = "HIOS"; + break; + case 0xFF00: + eType = "LOPROC"; + break; + case 0xFFFF: + eType = "HIPROC"; + break; + default: + break; + } + ehResult.push("Type:".padEnd(align) + eType); + + let ISA = ""; + switch (stream.readInt(2, endianness)) { + case 0x0000: + ISA = "No specific instruction set"; + break; + case 0x0001: + ISA = "AT&T WE 32100"; + break; + case 0x0002: + ISA = "SPARC"; + break; + case 0x0003: + ISA = "x86"; + break; + case 0x0004: + ISA = "Motorola 68000 (M68k)"; + break; + case 0x0005: + ISA = "Motorola 88000 (M88k)"; + break; + case 0x0006: + ISA = "Intel MCU"; + break; + case 0x0007: + ISA = "Intel 80860"; + break; + case 0x0008: + ISA = "MIPS"; + break; + case 0x0009: + ISA = "IBM System/370"; + break; + case 0x000A: + ISA = "MIPS RS3000 Little-endian"; + break; + case 0x000B: + case 0x000C: + case 0x000D: + case 0x000E: + case 0x0018: + case 0x0019: + case 0x001A: + case 0x001B: + case 0x001C: + case 0x001D: + case 0x001E: + case 0x001F: + case 0x0020: + case 0x0021: + case 0x0022: + case 0x0023: + ISA = "Reserved for future use"; + break; + case 0x000F: + ISA = "Hewlett-Packard PA-RISC"; + break; + case 0x0011: + ISA = "Fujitsu VPP500"; + break; + case 0x0012: + ISA = "Enhanced instruction set SPARC"; + break; + case 0x0013: + ISA = "Intel 80960"; + break; + case 0x0014: + ISA = "PowerPC"; + break; + case 0x0015: + ISA = "PowerPC (64-bit)"; + break; + case 0x0016: + ISA = "S390, including S390"; + break; + case 0x0017: + ISA = "IBM SPU/SPC"; + break; + case 0x0024: + ISA = "NEC V800"; + break; + case 0x0025: + ISA = "Fujitsu FR20"; + break; + case 0x0026: + ISA = "TRW RH-32"; + break; + case 0x0027: + ISA = "Motorola RCE"; + break; + case 0x0028: + ISA = "ARM (up to ARMv7/Aarch32)"; + break; + case 0x0029: + ISA = "Digital Alpha"; + break; + case 0x002A: + ISA = "SuperH"; + break; + case 0x002B: + ISA = "SPARC Version 9"; + break; + case 0x002C: + ISA = "Siemens TriCore embedded processor"; + break; + case 0x002D: + ISA = "Argonaut RISC Core"; + break; + case 0x002E: + ISA = "Hitachi H8/300"; + break; + case 0x002F: + ISA = "Hitachi H8/300H"; + break; + case 0x0030: + ISA = "Hitachi H8S"; + break; + case 0x0031: + ISA = "Hitachi H8/500"; + break; + case 0x0032: + ISA = "IA-64"; + break; + case 0x0033: + ISA = "Standford MIPS-X"; + break; + case 0x0034: + ISA = "Motorola ColdFire"; + break; + case 0x0035: + ISA = "Motorola M68HC12"; + break; + case 0x0036: + ISA = "Fujitsu MMA Multimedia Accelerator"; + break; + case 0x0037: + ISA = "Siemens PCP"; + break; + case 0x0038: + ISA = "Sony nCPU embedded RISC processor"; + break; + case 0x0039: + ISA = "Denso NDR1 microprocessor"; + break; + case 0x003A: + ISA = "Motorola Star*Core processor"; + break; + case 0x003B: + ISA = "Toyota ME16 processor"; + break; + case 0x003C: + ISA = "STMicroelectronics ST100 processor"; + break; + case 0x003D: + ISA = "Advanced Logic Corp. TinyJ embedded processor family"; + break; + case 0x003E: + ISA = "AMD x86-64"; + break; + case 0x003F: + ISA = "Sony DSP Processor"; + break; + case 0x0040: + ISA = "Digital Equipment Corp. PDP-10"; + break; + case 0x0041: + ISA = "Digital Equipment Corp. PDP-11"; + break; + case 0x0042: + ISA = "Siemens FX66 microcontroller"; + break; + case 0x0043: + ISA = "STMicroelectronics ST9+ 8/16 bit microcontroller"; + break; + case 0x0044: + ISA = "STMicroelectronics ST7 8-bit microcontroller"; + break; + case 0x0045: + ISA = "Motorola MC68HC16 Microcontroller"; + break; + case 0x0046: + ISA = "Motorola MC68HC11 Microcontroller"; + break; + case 0x0047: + ISA = "Motorola MC68HC08 Microcontroller"; + break; + case 0x0048: + ISA = "Motorola MC68HC05 Microcontroller"; + break; + case 0x0049: + ISA = "Silicon Graphics SVx"; + break; + case 0x004A: + ISA = "STMicroelectronics ST19 8-bit microcontroller"; + break; + case 0x004B: + ISA = "Digital VAX"; + break; + case 0x004C: + ISA = "Axis Communications 32-bit embedded processor"; + break; + case 0x004D: + ISA = "Infineon Technologies 32-bit embedded processor"; + break; + case 0x004E: + ISA = "Element 14 64-bit DSP Processor"; + break; + case 0x004F: + ISA = "LSI Logic 16-bit DSP Processor"; + break; + case 0x0050: + ISA = "Donald Knuth's educational 64-bit processor"; + break; + case 0x0051: + ISA = "Harvard University machine-independent object files"; + break; + case 0x0052: + ISA = "SiTera Prism"; + break; + case 0x0053: + ISA = "Atmel AVR 8-bit microcontroller"; + break; + case 0x0054: + ISA = "Fujitsu FR30"; + break; + case 0x0055: + ISA = "Mitsubishi D10V"; + break; + case 0x0056: + ISA = "Mitsubishi D30V"; + break; + case 0x0057: + ISA = "NEC v850"; + break; + case 0x0058: + ISA = "Mitsubishi M32R"; + break; + case 0x0059: + ISA = "Matsushita MN10300"; + break; + case 0x005A: + ISA = "Matsushita MN10200"; + break; + case 0x005B: + ISA = "picoJava"; + break; + case 0x005C: + ISA = "OpenRISC 32-bit embedded processor"; + break; + case 0x005D: + ISA = "ARC Cores Tangent-A5"; + break; + case 0x005E: + ISA = "Tensilica Xtensa Architecture"; + break; + case 0x005F: + ISA = "Alphamosaic VideoCore processor"; + break; + case 0x0060: + ISA = "Thompson Multimedia General Purpose Processor"; + break; + case 0x0061: + ISA = "National Semiconductor 32000 series"; + break; + case 0x0062: + ISA = "Tenor Network TPC processor"; + break; + case 0x0063: + ISA = "Trebia SNP 1000 processor"; + break; + case 0x0064: + ISA = "STMicroelectronics (www.st.com) ST200 microcontroller"; + break; + case 0x008C: + ISA = "TMS320C6000 Family"; + break; + case 0x00AF: + ISA = "MCST Elbrus e2k"; + break; + case 0x00B7: + ISA = "ARM 64-bits (ARMv8/Aarch64)"; + break; + case 0x00F3: + ISA = "RISC-V"; + break; + case 0x00F7: + ISA = "Berkeley Packet Filter"; + break; + case 0x0101: + ISA = "WDC 65C816"; + break; + default: + ISA = "Unimplemented"; + break; + } + ehResult.push("Instruction Set Architecture:".padEnd(align) + ISA); + + ehResult.push("ELF Version:".padEnd(align) + `${stream.readInt(4, endianness)}`); + + const readSize = format === 1 ? 4 : 8; + entry = stream.readInt(readSize, endianness); + phoff = stream.readInt(readSize, endianness); + shoff = stream.readInt(readSize, endianness); + ehResult.push("Entry Point:".padEnd(align) + `0x${Utils.hex(entry)}`); + ehResult.push("Entry PHOFF:".padEnd(align) + `0x${Utils.hex(phoff)}`); + ehResult.push("Entry SHOFF:".padEnd(align) + `0x${Utils.hex(shoff)}`); + + const flags = stream.readInt(4, endianness); + ehResult.push("Flags:".padEnd(align) + `${Utils.bin(flags)}`); + + ehResult.push("ELF Header Size:".padEnd(align) + `${stream.readInt(2, endianness)} bytes`); + ehResult.push("Program Header Size:".padEnd(align) + `${stream.readInt(2, endianness)} bytes`); + phEntries = stream.readInt(2, endianness); + ehResult.push("Program Header Entries:".padEnd(align) + phEntries); + shentSize = stream.readInt(2, endianness); + ehResult.push("Section Header Size:".padEnd(align) + shentSize + " bytes"); + shEntries = stream.readInt(2, endianness); + ehResult.push("Section Header Entries:".padEnd(align) + shEntries); + shstrtab = stream.readInt(2, endianness); + ehResult.push("Section Header Names:".padEnd(align) + shstrtab); + + return ehResult.join("\n"); + } + + /** + * This function parses and extracts relevant information from a Program Header. + * + * @param {stream} stream + * @returns {string} + */ + function programHeader(stream) { + /** + * A Program Header is comprised of the following structures depending on the binary's format. + * + * p_type - 4 Bytes identifying the type of the segment. + * + * 32-bit: + * p_offset - 4 Bytes specifying the offset of the segment. + * p_vaddr - 4 Bytes specifying the virtual address of the segment in memory. + * p_paddr - 4 Bytes specifying the physical address of the segment in memory. + * p_filesz - 4 Bytes specifying the size in bytes of the segment in the file image. + * p_memsz - 4 Bytes specifying the size in bytes of the segment in memory. + * p_flags - 4 Bytes identifying the segment dependent flags. + * p_align - 4 Bytes set to 0 or 1 for alignment or no alignment, respectively. + * + * 64-bit: + * p_flags - 4 Bytes identifying segment dependent flags. + * p_offset - 8 Bytes specifying the offset of the segment. + * p_vaddr - 8 Bytes specifying the virtual address of the segment in memory. + * p_paddr - 8 Bytes specifying the physical address of the segment in memory. + * p_filesz - 8 Bytes specifying the size in bytes of the segment in the file image. + * p_memsz - 8 Bytes specifying the size in bytes of the segment in memory. + * p_align - 8 Bytes set to 0 or 1 for alignment or no alignment, respectively. + */ + + /** + * This function decodes the flags bitmask for the Program Header. + * + * @param {integer} flags + * @returns {string} + */ + function readFlags(flags) { + const result = []; + if (flags & 0x1) + result.push("Execute"); + if (flags & 0x2) + result.push("Write"); + if (flags & 0x4) + result.push("Read"); + if (flags & 0xf0000000) + result.push("Unspecified"); + return result.join(","); + } + + const phResult = []; + + let pType = ""; + const programHeaderType = stream.readInt(4, endianness); + switch (true) { + case (programHeaderType === 0x00000000): + pType = "Unused"; + break; + case (programHeaderType === 0x00000001): + pType = "Loadable Segment"; + break; + case (programHeaderType === 0x00000002): + pType = "Dynamic linking information"; + break; + case (programHeaderType === 0x00000003): + pType = "Interpreter Information"; + break; + case (programHeaderType === 0x00000004): + pType = "Auxiliary Information"; + break; + case (programHeaderType === 0x00000005): + pType = "Reserved"; + break; + case (programHeaderType === 0x00000006): + pType = "Program Header Table"; + break; + case (programHeaderType === 0x00000007): + pType = "Thread-Local Storage Template"; + break; + case (programHeaderType >= 0x60000000 && programHeaderType <= 0x6FFFFFFF): + pType = "Reserved Inclusive Range. OS Specific"; + break; + case (programHeaderType >= 0x70000000 && programHeaderType <= 0x7FFFFFFF): + pType = "Reserved Inclusive Range. Processor Specific"; + break; + default: + break; + + } + phResult.push("Program Header Type:".padEnd(align) + pType); + + if (format === 2) + phResult.push("Flags:".padEnd(align) + readFlags(stream.readInt(4, endianness))); + + const readSize = format === 1? 4 : 8; + phResult.push("Offset Of Segment:".padEnd(align) + `${stream.readInt(readSize, endianness)}`); + phResult.push("Virtual Address of Segment:".padEnd(align) + `${stream.readInt(readSize, endianness)}`); + phResult.push("Physical Address of Segment:".padEnd(align) + `${stream.readInt(readSize, endianness)}`); + phResult.push("Size of Segment:".padEnd(align) + `${stream.readInt(readSize, endianness)} bytes`); + phResult.push("Size of Segment in Memory:".padEnd(align) + `${stream.readInt(readSize, endianness)} bytes`); + + if (format === 1) + phResult.push("Flags:".padEnd(align) + readFlags(stream.readInt(4, endianness))); + + stream.moveForwardsBy(readSize); + + return phResult.join("\n"); + } + + /** + * This function parses and extracts relevant information from a Section Header. + * + * @param {stream} stream + * @returns {string} + */ + function sectionHeader(stream) { + /** + * A Section Header is comprised of the following structures depending on the binary's format. + * + * sh_name - 4 Bytes identifying the offset into the .shstrtab for the name of this section. + * sh_type - 4 Bytes identifying the type of this header. + * + * 32-bit: + * sh_flags - 4 Bytes identifying section specific flags. + * sh_addr - 4 Bytes identifying the virtual address of the section in memory. + * sh_offset - 4 Bytes identifying the offset of the section in the file. + * sh_size - 4 Bytes specifying the size in bytes of the section in the file image. + * sh_link - 4 Bytes identifying the index of an associated section. + * sh_info - 4 Bytes specifying extra information about the section. + * sh_addralign - 4 Bytes containing the alignment for the section. + * sh_entsize - 4 Bytes specifying the size, in bytes, of each entry in the section. + * + * 64-bit: + * sh_flags - 8 Bytes identifying section specific flags. + * sh_addr - 8 Bytes identifying the virtual address of the section in memory. + * sh_offset - 8 Bytes identifying the offset of the section in the file. + * sh_size - 8 Bytes specifying the size in bytes of the section in the file image. + * sh_link - 4 Bytes identifying the index of an associated section. + * sh_info - 4 Bytes specifying extra information about the section. + * sh_addralign - 8 Bytes containing the alignment for the section. + * sh_entsize - 8 Bytes specifying the size, in bytes, of each entry in the section. + */ + const shResult = []; + + const nameOffset = stream.readInt(4, endianness); + let type = ""; + const shType = stream.readInt(4, endianness); + switch (true) { + case (shType === 0x00000001): + type = "Program Data"; + break; + case (shType === 0x00000002): + type = "Symbol Table"; + break; + case (shType === 0x00000003): + type = "String Table"; + break; + case (shType === 0x00000004): + type = "Relocation Entries with Addens"; + break; + case (shType === 0x00000005): + type = "Symbol Hash Table"; + break; + case (shType === 0x00000006): + type = "Dynamic Linking Information"; + break; + case (shType === 0x00000007): + type = "Notes"; + break; + case (shType === 0x00000008): + type = "Program Space with No Data"; + break; + case (shType === 0x00000009): + type = "Relocation Entries with no Addens"; + break; + case (shType === 0x0000000A): + type = "Reserved"; + break; + case (shType === 0x0000000B): + type = "Dynamic Linker Symbol Table"; + break; + case (shType === 0x0000000E): + type = "Array of Constructors"; + break; + case (shType === 0x0000000F): + type = "Array of Destructors"; + break; + case (shType === 0x00000010): + type = "Array of pre-constructors"; + break; + case (shType === 0x00000011): + type = "Section group"; + break; + case (shType === 0x00000012): + type = "Extended section indices"; + break; + case (shType === 0x00000013): + type = "Number of defined types"; + break; + case (shType >= 0x60000000 && shType <= 0x6fffffff): + type = "OS-specific"; + break; + case (shType >= 0x70000000 && shType <= 0x7fffffff): + type = "Processor-specific"; + break; + case (shType >= 0x80000000 && shType <= 0x8fffffff): + type = "Application-specific"; + break; + default: + type = "Unused"; + break; + } + + shResult.push("Type:".padEnd(align) + type); + + let nameResult = ""; + if (type !== "Unused") { + nameResult = readString(stream, namesOffset, nameOffset); + shResult.push("Section Name: ".padEnd(align) + nameResult); + } + + const readSize = (format === 1) ? 4 : 8; + + const flags = stream.readInt(readSize, endianness); + const shFlags = []; + const bitMasks = [ + [0x00000001, "Writable"], + [0x00000002, "Alloc"], + [0x00000004, "Executable"], + [0x00000010, "Merge"], + [0x00000020, "Strings"], + [0x00000040, "SHT Info Link"], + [0x00000080, "Link Order"], + [0x00000100, "OS Specific Handling"], + [0x00000200, "Group"], + [0x00000400, "Thread Local Data"], + [0x0FF00000, "OS-Specific"], + [0xF0000000, "Processor Specific"], + [0x04000000, "Special Ordering (Solaris)"], + [0x08000000, "Excluded (Solaris)"] + ]; + bitMasks.forEach(elem => { + if (flags & elem[0]) + shFlags.push(elem[1]); + }); + shResult.push("Flags:".padEnd(align) + shFlags); + + const vaddr = stream.readInt(readSize, endianness); + shResult.push("Section Vaddr in memory:".padEnd(align) + vaddr); + + const shoffset = stream.readInt(readSize, endianness); + shResult.push("Offset of the section:".padEnd(align) + shoffset); + + const secSize = stream.readInt(readSize, endianness); + shResult.push("Section Size:".padEnd(align) + secSize); + + const associatedSection = stream.readInt(4, endianness); + shResult.push("Associated Section:".padEnd(align) + associatedSection); + + const extraInfo = stream.readInt(4, endianness); + shResult.push("Section Extra Information:".padEnd(align) + extraInfo); + + // Jump over alignment field. + stream.moveForwardsBy(readSize); + const entSize = stream.readInt(readSize, endianness); + switch (nameResult) { + case ".strtab": + strtabOffset = shoffset; + break; + case ".symtab": + symtabOffset = shoffset; + symtabSize = secSize; + symtabEntSize = entSize; + break; + default: + break; + } + return shResult.join("\n"); + } + + /** + * This function returns the offset of the Section Header Names Section. + * + * @param {stream} stream + */ + function getNamesOffset(stream) { + const preMove = stream.position; + stream.moveTo(shoff + (shentSize * shstrtab)); + if (format === 1) { + stream.moveForwardsBy(0x10); + namesOffset = stream.readInt(4, endianness); + } else { + stream.moveForwardsBy(0x18); + namesOffset = stream.readInt(8, endianness); + } + stream.position = preMove; + } + + /** + * This function returns a symbol's name from the string table. + * + * @param {stream} stream + * @returns {string} + */ + function getSymbols(stream) { + /** + * The Symbol Table is comprised of Symbol Table Entries whose structure depends on the binary's format. + * + * 32-bit: + * st_name - 4 Bytes specifying an index in the files symbol string table. + * st_value - 4 Bytes identifying the value associated with the symbol. + * st_size - 4 Bytes specifying the size associated with the symbol (this is not the size of the symbol). + * st_info - A byte specifying the type and binding of the symbol. + * st_other - A byte specifying the symbol's visibility. + * st_shndx - 2 Bytes identifying the section that this symbol is related to. + * + * 64-bit: + * st_name - 4 Bytes specifying an index in the files symbol string table. + * st_info - A byte specifying the type and binding of the symbol. + * st_other - A byte specifying the symbol's visibility. + * st_shndx - 2 Bytes identifying the section that this symbol is related to. + * st_value - 8 Bytes identifying the value associated with the symbol. + * st_size - 8 Bytes specifying the size associated with the symbol (this is not the size of the symbol). + */ + const nameOffset = stream.readInt(4, endianness); + stream.moveForwardsBy(format === 2 ? 20 : 12); + return readString(stream, strtabOffset, nameOffset); + } + + input = new Uint8Array(input); + const stream = new Stream(input); + const result = ["=".repeat(align) + " ELF Header " + "=".repeat(align)]; + result.push(elfHeader(stream) + "\n"); + + getNamesOffset(stream); + + result.push("=".repeat(align) + " Program Header " + "=".repeat(align)); + stream.moveTo(phoff); + for (let i = 0; i < phEntries; i++) + result.push(programHeader(stream) + "\n"); + + result.push("=".repeat(align) + " Section Header " + "=".repeat(align)); + stream.moveTo(shoff); + for (let i = 0; i < shEntries; i++) + result.push(sectionHeader(stream) + "\n"); + + result.push("=".repeat(align) + " Symbol Table " + "=".repeat(align)); + + stream.moveTo(symtabOffset); + let elem = ""; + for (let i = 0; i < (symtabSize / symtabEntSize); i++) + if ((elem = getSymbols(stream)) !== "") + result.push("Symbol Name:".padEnd(align) + elem); + + return result.join("\n"); + } + +} + +export default ELFInfo; diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index cf32a5ab..b6beb081 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -113,6 +113,7 @@ import "./tests/JA3SFingerprint.mjs"; import "./tests/HASSH.mjs"; import "./tests/GetAllCasings.mjs"; import "./tests/SIGABA.mjs"; +import "./tests/ELFInfo.mjs"; // Cannot test operations that use the File type yet diff --git a/tests/operations/tests/ELFInfo.mjs b/tests/operations/tests/ELFInfo.mjs new file mode 100644 index 00000000..2afcda81 --- /dev/null +++ b/tests/operations/tests/ELFInfo.mjs @@ -0,0 +1,20 @@ +/** + * @author n1073645 [n1073645@gmail.com] + * @copyright Crown Copyright 2022 + * @license Apache-2.0 + */ + import TestRegister from "../../lib/TestRegister.mjs"; + + TestRegister.addTests([ + { + name: "ELF Info invalid ELF.", + input: "\x7f\x00\x00\x00", + expectedOutput: "Invalid ELF", + recipeConfig: [ + { + op: "ELF Info", + args: [], + }, + ], + }, + ]);