Created
October 10, 2023 17:35
-
-
Save fzakaria/62d776353b54d6175f4e35e40404d4b9 to your computer and use it in GitHub Desktop.
ELF Loader in python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import ctypes | |
import mmap | |
import struct | |
# Simplified ELF Header Structure | |
class Elf32_Ehdr(ctypes.Structure): | |
_fields_ = [ | |
("e_ident", ctypes.c_char * 16), | |
("e_type", ctypes.c_uint16), | |
("e_machine", ctypes.c_uint16), | |
("e_version", ctypes.c_uint32), | |
("e_entry", ctypes.c_uint32), | |
("e_phoff", ctypes.c_uint32), | |
("e_shoff", ctypes.c_uint32), | |
("e_flags", ctypes.c_uint32), | |
("e_ehsize", ctypes.c_uint16), | |
("e_phentsize", ctypes.c_uint16), | |
("e_phnum", ctypes.c_uint16), | |
("e_shentsize", ctypes.c_uint16), | |
("e_shnum", ctypes.c_uint16), | |
("e_shstrndx", ctypes.c_uint16), | |
] | |
# Simplified Program Header Structure | |
class Elf32_Phdr(ctypes.Structure): | |
_fields_ = [ | |
("p_type", ctypes.c_uint32), | |
("p_offset", ctypes.c_uint32), | |
("p_vaddr", ctypes.c_uint32), | |
("p_paddr", ctypes.c_uint32), | |
("p_filesz", ctypes.c_uint32), | |
("p_memsz", ctypes.c_uint32), | |
("p_flags", ctypes.c_uint32), | |
("p_align", ctypes.c_uint32), | |
] | |
# Load ELF binary into memory and execute its main function | |
def load_and_execute_elf(file_path): | |
with open(file_path, "rb") as f: | |
# Map the file into memory | |
mmapped_file = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) | |
# Read the ELF header | |
elf_header = Elf32_Ehdr.from_buffer(mmapped_file) | |
# Validate the ELF magic number | |
if elf_header.e_ident[:4] != b'\x7fELF': | |
raise ValueError("Not a valid ELF file") | |
# Find the program header table | |
phoff = elf_header.e_phoff | |
phentsize = elf_header.e_phentsize | |
phnum = elf_header.e_phnum | |
# Iterate through program header entries | |
for i in range(phnum): | |
# Read the program header entry | |
phdr_data = mmapped_file[phoff + i * phentsize : phoff + (i + 1) * phentsize] | |
phdr = Elf32_Phdr.from_buffer_copy(phdr_data) | |
# Check if the segment is loadable | |
if phdr.p_type == 1: # PT_LOAD | |
# Map the segment into memory | |
segment = mmap.mmap(-1, phdr.p_memsz, prot=mmap.PROT_READ | mmap.PROT_WRITE | mmap.PROT_EXEC) | |
segment.write(mmapped_file[phdr.p_offset : phdr.p_offset + phdr.p_filesz]) | |
# Assume the entry point is in this segment | |
if elf_header.e_entry >= phdr.p_vaddr and elf_header.e_entry < phdr.p_vaddr + phdr.p_memsz: | |
entry_offset = elf_header.e_entry - phdr.p_vaddr | |
# Define a ctypes function type for the entry point | |
FUNCTYPE = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_char_p)) | |
# Create a ctypes function pointer | |
entry_func = FUNCTYPE(segment[entry_offset:].tobytes()) | |
# Call the entry function | |
args = (ctypes.c_int(1), ctypes.POINTER(ctypes.c_char_p)(ctypes.c_char_p(b'example'))) | |
result = entry_func(*args) | |
print("Program returned:", result) | |
break | |
# Example usage | |
# load_and_execute_elf("path_to_your_elf_file") |
Author
fzakaria
commented
Oct 10, 2023
import ctypes
import mmap
import struct
# ... [Previous code and structures] ...
# ELF Relocation Entry with Addend
class Elf32_Rela(ctypes.Structure):
_fields_ = [
("r_offset", ctypes.c_uint32),
("r_info", ctypes.c_uint32),
("r_addend", ctypes.c_int32),
]
# ... [Previous code] ...
def perform_relocations(segment, rela_entries, symtab, strtab):
for rela in rela_entries:
# Extract the symbol index and type from r_info
sym_index = rela.r_info >> 8
rel_type = rela.r_info & 0xff
# Find the symbol entry
symbol = symtab[sym_index]
# Find the symbol name
sym_name = strtab[symbol.st_name:].split(b'\0', 1)[0]
# TODO: Find the actual address of the symbol
# This would typically involve looking up the symbol in the dynamic linker's
# symbol table or in the symbol tables of other loaded libraries.
# For simplicity, let's assume the symbol addresses are fixed.
symbol_addr = ... # Replace with actual address
# Perform the relocation
if rel_type == 7: # R_386_JUMP_SLOT
# Replace the function address in the PLT with the actual address
ctypes.memmove(segment + rela.r_offset, ctypes.pointer(ctypes.c_uint32(symbol_addr)), 4)
elif rel_type == 6: # R_386_GLOB_DAT
# Replace the global data address with the actual address
ctypes.memmove(segment + rela.r_offset, ctypes.pointer(ctypes.c_uint32(symbol_addr)), 4)
# TODO: Handle other relocation types
# ... [Previous code] ...
def load_and_execute_elf(file_path):
# ... [Previous code] ...
# Assume the entry point is in this segment
if elf_header.e_entry >= phdr.p_vaddr and elf_header.e_entry < phdr.p_vaddr + phdr.p_memsz:
# ... [Previous code] ...
# TODO: Find the .rela.dyn and .dynsym sections in the ELF file
# and read the relocation entries, symbol table, and string table.
# For simplicity, let's assume we have arrays of the entries.
rela_entries = [...] # Replace with actual entries
symtab = [...] # Replace with actual entries
strtab = b'...' # Replace with actual string table
# Perform relocations
perform_relocations(segment, rela_entries, symtab, strtab)
# ... [Previous code] ...
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment