metasploit-framework/lib/rex/elfparsey/elfbase.rb

257 lines
6.5 KiB
Ruby

# -*- coding: binary -*-
require 'rex/struct2'
module Rex
module ElfParsey
class ElfBase
# ELF Header
ELF_HEADER_SIZE = 52
EI_NIDENT = 16
ELF32_EHDR_LSB = Rex::Struct2::CStructTemplate.new(
[ 'string', 'e_ident', EI_NIDENT, '' ],
[ 'uint16v', 'e_type', 0 ],
[ 'uint16v', 'e_machine', 0 ],
[ 'uint32v', 'e_version', 0 ],
[ 'uint32v', 'e_entry', 0 ],
[ 'uint32v', 'e_phoff', 0 ],
[ 'uint32v', 'e_shoff', 0 ],
[ 'uint32v', 'e_flags', 0 ],
[ 'uint16v', 'e_ehsize', 0 ],
[ 'uint16v', 'e_phentsize', 0 ],
[ 'uint16v', 'e_phnum', 0 ],
[ 'uint16v', 'e_shentsize', 0 ],
[ 'uint16v', 'e_shnum', 0 ],
[ 'uint16v', 'e_shstrndx', 0 ]
)
ELF32_EHDR_MSB = Rex::Struct2::CStructTemplate.new(
[ 'string', 'e_ident', EI_NIDENT, '' ],
[ 'uint16n', 'e_type', 0 ],
[ 'uint16n', 'e_machine', 0 ],
[ 'uint32n', 'e_version', 0 ],
[ 'uint32n', 'e_entry', 0 ],
[ 'uint32n', 'e_phoff', 0 ],
[ 'uint32n', 'e_shoff', 0 ],
[ 'uint32n', 'e_flags', 0 ],
[ 'uint16n', 'e_ehsize', 0 ],
[ 'uint16n', 'e_phentsize', 0 ],
[ 'uint16n', 'e_phnum', 0 ],
[ 'uint16n', 'e_shentsize', 0 ],
[ 'uint16n', 'e_shnum', 0 ],
[ 'uint16n', 'e_shstrndx', 0 ]
)
# e_type This member identifies the object file type
ET_NONE = 0 # No file type
ET_REL = 1 # Relocatable file
ET_EXEC = 2 # Executable file
ET_DYN = 3 # Shared object file
ET_CORE = 4 # Core file
ET_LOPROC = 0xff00 # Processor-specific
ET_HIPROC = 0xffff # Processor-specific
#
# e_machine This member's value specifies the required architecture for an
# individual file.
#
# ET_NONE = 0 # No machine
EM_M32 = 1 # AT&T WE 32100
EM_SPARC = 2 # SPARC
EM_386 = 3 # Intel Architecture
EM_68K = 4 # Motorola 68000
EM_88K = 5 # Motorola 88000
EM_860 = 7 # Intel 80860
EM_MIPS = 8 # MIPS RS3000 Big-Endian
EM_MIPS_RS4_BE = 10 # MIPS RS4000 Big-Endian
# e_version This member identifies the object file version
EV_NONE = 0 # Invalid version
EV_CURRENT = 1 # Current version
# ELF Identification
# e_ident[] Identification indexes
EI_MAG0 = 0 # File identification
EI_MAG1 = 1 # File identification
EI_MAG2 = 2 # File identification
EI_MAG3 = 3 # File identification
EI_CLASS = 4 # File class
EI_DATA = 5 # Data encoding
EI_VERSION = 6 # File version
EI_PAD = 7 # Start of padding bytes
# EI_NIDENT = 16 # Size of e_ident[]
#
# EI_MAG0 to EI_MAG3 A file's first 4 bytes hold a "magic number",
# identifying the file as an ELF object file.
#
ELFMAG0 = 0x7f # e_ident[EI_MAG0]
ELFMAG1 = ?E # e_ident[EI_MAG1]
ELFMAG2 = ?L # e_ident[EI_MAG2]
ELFMAG3 = ?F # e_ident[EI_MAG3]
ELFMAG = ELFMAG0.chr + ELFMAG1.chr + ELFMAG2.chr + ELFMAG3.chr
# EI_CLASS Identifies the file's class, or capacity
ELFCLASSNONE = 0 # Invalid class
ELFCLASS32 = 1 # 32-bit objects
ELFCLASS64 = 2 # 64-bit objects
#
# EI_DATA Specifies the data encoding of the processor-specific data in
# the object file. The following encodings are currently defined.
#
ELFDATANONE = 0 # Invalid data encoding
ELFDATA2LSB = 1 # Least significant byte first
ELFDATA2MSB = 2 # Most significant byte first
class GenericStruct
attr_accessor :struct
def initialize(_struct)
self.struct = _struct
end
# The following methods are just pass-throughs for struct
# Access a value
def v
struct.v
end
# Access a value by array
def [](*args)
struct[*args]
end
# Obtain an array of all fields
def keys
struct.keys
end
def method_missing(meth, *args)
v[meth.to_s] || (raise NoMethodError.new, meth)
end
end
class GenericHeader < GenericStruct
end
class ElfHeader < GenericHeader
def initialize(rawdata)
# Identify the data encoding and parse ELF Header
elf_header = ELF32_EHDR_LSB.make_struct
if !elf_header.from_s(rawdata)
raise ElfHeaderError, "Couldn't parse ELF Header", caller
end
if elf_header.v['e_ident'][EI_DATA,1].unpack('C')[0] == ELFDATA2MSB
elf_header = ELF32_EHDR_MSB.make_struct
if !elf_header.from_s(rawdata)
raise ElfHeaderError, "Couldn't parse ELF Header", caller
end
end
unless [ ELFDATA2LSB, ELFDATA2MSB ].include?(
elf_header.v['e_ident'][EI_DATA,1].unpack('C')[0])
raise ElfHeaderError, "Invalid data encoding", caller
end
# Identify the file as an ELF object file
unless elf_header.v['e_ident'][EI_MAG0, 4] == ELFMAG
raise ElfHeaderError, 'Invalid magic number', caller
end
self.struct = elf_header
end
def e_ident
struct.v['e_ident']
end
end
# Program Header
PROGRAM_HEADER_SIZE = 32
ELF32_PHDR_LSB = Rex::Struct2::CStructTemplate.new(
[ 'uint32v', 'p_type', 0 ],
[ 'uint32v', 'p_offset', 0 ],
[ 'uint32v', 'p_vaddr', 0 ],
[ 'uint32v', 'p_paddr', 0 ],
[ 'uint32v', 'p_filesz', 0 ],
[ 'uint32v', 'p_memsz', 0 ],
[ 'uint32v', 'p_flags', 0 ],
[ 'uint32v', 'p_align', 0 ]
)
ELF32_PHDR_MSB = Rex::Struct2::CStructTemplate.new(
[ 'uint32n', 'p_type', 0 ],
[ 'uint32n', 'p_offset', 0 ],
[ 'uint32n', 'p_vaddr', 0 ],
[ 'uint32n', 'p_paddr', 0 ],
[ 'uint32n', 'p_filesz', 0 ],
[ 'uint32n', 'p_memsz', 0 ],
[ 'uint32n', 'p_flags', 0 ],
[ 'uint32n', 'p_align', 0 ]
)
#
# p_type This member tells what kind of segment this array element
# describes or how to interpret the array element's information.
#
# Segment Types
PT_NULL = 0
PT_LOAD = 1
PT_DYNAMIC = 2
PT_INTERP = 3
PT_NOTE = 4
PT_SHLIB = 5
PT_PHDR = 6
PT_LOPROC = 0x70000000
PT_HIPROC = 0x7fffffff
class ProgramHeader < GenericHeader
def initialize(rawdata, ei_data)
# Identify the data encoding and parse Program Header
if ei_data == ELFDATA2LSB
program_header = ELF32_PHDR_LSB.make_struct
elsif ei_data == ELFDATA2MSB
program_header = ELF32_PHDR_MSB.make_struct
else
raise ElfHeaderError, "Invalid data encoding", caller
end
if !program_header.from_s(rawdata)
raise ProgramHeaderError, "Couldn't parse Program Header", caller
end
self.struct = program_header
end
end
end
end
end