Skip to content

Instantly share code, notes, and snippets.

@prozacchiwawa
Created September 11, 2024 06:07
Show Gist options
  • Save prozacchiwawa/cd7b3d4cd9a4721868337cef9ecf0f93 to your computer and use it in GitHub Desktop.
Save prozacchiwawa/cd7b3d4cd9a4721868337cef9ecf0f93 to your computer and use it in GitHub Desktop.
Make an elf file that pretends to have specific functions at specific addresses
#!/usr/bin/env python
from elftools.elf.elffile import ELFFile
from pathlib import Path
import struct
import subprocess
# Steps
#
# 0) Read definitions with addresses
# 1) Write C source for all of our definitions
# 2) Compile C source to object file
# 3) Open elf object file
# 4) Set type to ET_EXEC
# 5) Iterate names from definitions:
# 6) Find symbol
# 7) Set st_value and st_shndx according to definitions.
# 8) Write!
#
# { "fff00100": { "name": ..., "info": ..., "decl": "int test_proto(void *x, int y)" },
# "StructDef": "struct StructDef { ... }"
# }
def isxdigit(s):
return all(map(lambda x: x in '0123456789abcdefABCDEF', s))
def create_elf(outelf, outc, address, json):
c_text = []
by_name = {}
top_address = None
for key, value in json.items():
if not isxdigit(key):
c_text.append(value)
c_text.append(';\n')
for key, value in json.items():
if isxdigit(key):
addr = int(key, 16)
if top_address is None or top_address < addr:
top_address = addr + 4
by_name[value['name']] = addr
# Is an address with a definition
if 'decl' in value:
# Has a declaration
c_text.append(value['decl'])
c_text.append(' { }\n')
else:
c_text.append(f'void *{value["name"]}()')
c_text.append(';\n')
with open(outc, 'w') as c_file:
for s in c_text:
c_file.write(s)
# Compile to object file
out = subprocess.check_output(['powerpc-linux-gnu-gcc','-c','-g','-gdwarf',outc])
p = Path(outc)
outo = p.with_suffix('.o')
patches = []
symtab_section_id = None
text_section_id = None
# Read the elf file
with ELFFile(open(outo,'rb')) as elf:
for i in range(elf.num_sections()):
ts = elf.get_section(i)
if ts.name == '.text':
text_section_id = i
symtab = elf.get_section_by_name('.symtab')
symtab_offset = symtab.header.sh_offset
symtab_entsize = symtab.header.sh_entsize
for i in range(symtab.num_symbols()):
sym = symtab.get_symbol(i)
if sym.name in by_name:
symaddr = by_name[sym.name]
patches.append({"offset":symtab_offset + (i * symtab_entsize), "shndx": text_section_id, "value":symaddr - address})
with open(outo,'rb+') as elf:
elf_header_bytes = elf.read(52)
elf_header = struct.unpack(
'>16sHHIIIIIHHHHHH', elf_header_bytes
)
(e_ident, e_type, e_machine, e_version, e_entry, e_phoff, e_shoff, e_flags, e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum, e_shstrndx) = elf_header
offset_of_text_section_entry = e_shoff + (text_section_id * e_shentsize)
# Write ET_EXEC
elf.seek(16)
elf.write(struct.pack('>H', 2))
# Change section size and type for .text
elf.seek(offset_of_text_section_entry + 4)
elf.write(struct.pack('>I', 8))
elf.seek(offset_of_text_section_entry + 20)
elf.write(struct.pack('>I', top_address - address))
# For each patch in patches, write it out
for p in patches:
elf.seek(p['offset'] + 4)
elf.write(struct.pack('>I', p['value']))
elf.seek(p['offset'] + 8)
elf.write(struct.pack('>I', 4))
elf.seek(p['offset'] + 14)
elf.write(struct.pack('>H', p['shndx']))
if __name__ == '__main__':
import sys
import json
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('address')
parser.add_argument('json')
args = parser.parse_args()
p = Path(args.json)
outelf = p.with_suffix('.elf')
outc = p.with_suffix('.c')
create_elf(outelf, outc, int(args.address, 16), json.load(open(args.json)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment