259 lines
6.2 KiB
Ruby
259 lines
6.2 KiB
Ruby
#!/usr/bin/env ruby
|
|
|
|
# $Id$
|
|
|
|
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
|