From 3c20b41be8b721b5e75ee6ff8f02901e58654539 Mon Sep 17 00:00:00 2001 From: kStor2poche <43518872+kStor2poche@users.noreply.github.com> Date: Fri, 11 Sep 2026 15:54:37 +0200 Subject: [PATCH 01/13] Apply relocs for ifuncs --- miasm/jitter/loader/elf.py | 55 +++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/miasm/jitter/loader/elf.py b/miasm/jitter/loader/elf.py index 91d1c18bb..90c536650 100644 --- a/miasm/jitter/loader/elf.py +++ b/miasm/jitter/loader/elf.py @@ -3,6 +3,8 @@ from future.utils import viewitems +from miasm.analysis.machine import Machine +from miasm.core.locationdb import LocationDB from miasm.loader import cstruct from miasm.loader import * import miasm.loader.elf as elf_csts @@ -32,7 +34,7 @@ def get_import_address_elf(e): return import2addr -def preload_elf(vm, e, runtime_lib, patch_vm_imp=True, loc_db=None): +def preload_elf(vm, e, runtime_lib, patch_vm_imp=True, loc_db=None, elf_base_addr: int = 0): # XXX quick hack fa = get_import_address_elf(e) dyn_funcs = {} @@ -47,11 +49,11 @@ def preload_elf(vm, e, runtime_lib, patch_vm_imp=True, loc_db=None): libname_s = canon_libname_libfunc(libname, libfunc) dyn_funcs[libname_s] = ad_libfunc if patch_vm_imp: - log.debug('patch 0x%x 0x%x %s', ad, ad_libfunc, libfunc) + log.debug('patch 0x%x 0x%x %s', ad + elf_base_addr, ad_libfunc, libfunc) set_endianness = { elf_csts.ELFDATA2MSB: ">", elf_csts.ELFDATA2LSB: "<", elf_csts.ELFDATANONE: "" }[e.sex] - vm.set_mem(ad, + vm.set_mem(ad + elf_base_addr, struct.pack(set_endianness + cstruct.size2type[e.size], ad_libfunc)) @@ -163,7 +165,7 @@ def fill_loc_db_with_symbols(elf, loc_db, base_addr=0): loc_db.add_location(name=name, offset=vaddr) -def apply_reloc_x86(elf, vm, section, base_addr, loc_db): +def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB): """Apply relocation for x86 ELF contained in the section @section @elf: miasm.loader's ELF instance @vm: VmMngr instance @@ -206,14 +208,20 @@ def apply_reloc_x86(elf, vm, section, base_addr, loc_db): (32, elf_csts.R_386_RELATIVE), (32, elf_csts.R_386_IRELATIVE), ]: - # B + A - addr = base_addr + r_addend + where = base_addr + r_offset + addr = int.from_bytes(elf.get_virt().get(r_offset, r_offset + elf.size // 8), byteorder="little") + ifunc_syms = [s for s in elf.sh.symtab.symtab if s.value == addr and s.info & elf_csts.STT_GNU_IFUNC == elf_csts.STT_GNU_IFUNC] + if len(ifunc_syms) > 0: + is_ifunc = True + else: + # B + A + addr = base_addr + r_addend elif reloc.type == elf_csts.R_X86_64_64: # S + A - addr_symb = loc_db.get_name_offset(symbol_entry.name) + addr_symb = loc_db.get_name_offset(symbol_entry.name.decode()) if addr_symb is None: - log.warning("Unable to find symbol %r" % symbol_entry.name) + log.warning("Unable to find symbol %r" % symbol_entry.name.decode()) continue addr = addr_symb + r_addend where = base_addr + r_offset @@ -232,7 +240,7 @@ def apply_reloc_x86(elf, vm, section, base_addr, loc_db): (32, elf_csts.R_386_GLOB_DAT), ]: # S - addr = loc_db.get_name_offset(symbol_entry.name) + addr = loc_db.get_name_offset(symbol_entry.name.decode()) if addr is None: log.warning("Unable to find symbol %r" % symbol_entry.name) continue @@ -244,13 +252,28 @@ def apply_reloc_x86(elf, vm, section, base_addr, loc_db): reloc) ) if is_ifunc: - # Resolve at runtime - not implemented for now - log.warning("Relocation for %r (at %x, currently pointing on %x) " - "has to be resolved at runtime", - name, where, sym_addr) - continue - - log.debug("Write %x at %x", addr, where) + # TODO: only relevant for statically and dynamically linked programs, cf. https://sourceware.org/glibc/manual/latest/html_node/Indirect-Functions.html#When-IFUNC-Resolvers-Run + ifunc_machine = Machine(guess_arch(elf)) + ifunc_jitter = ifunc_machine.jitter(loc_db) + + for map_addr, map_mem in vm.get_all_memory().items(): + ifunc_jitter.vm.add_memory_page(map_addr, map_mem["access"], map_mem["data"]) + + ifunc_jitter.init_stack() + end_addr = 0x1337beef + def _code_sentinelle(jitter): + jitter.running = False + return False + ifunc_jitter.add_breakpoint(end_addr, _code_sentinelle) + if elf.size == 32: + ifunc_jitter.push_uint32_t(end_addr) + elif elf.size == 64: + ifunc_jitter.push_uint64_t(end_addr) + + ifunc_jitter.run(addr) + addr = getattr(ifunc_jitter.cpu, ifunc_machine.lifter_model_call(loc_db).ret_reg.name) + + log.debug(f"Write {addr:x} at {where:x}") addr_writer(where, addr) From 4ad7f56414715f0b42c45cd168754385045e9b55 Mon Sep 17 00:00:00 2001 From: kStor2poche <43518872+kStor2poche@users.noreply.github.com> Date: Sat, 19 Sep 2026 11:21:38 +0200 Subject: [PATCH 02/13] Raise error for weird elf sizes --- miasm/jitter/loader/elf.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/miasm/jitter/loader/elf.py b/miasm/jitter/loader/elf.py index 90c536650..606f7c697 100644 --- a/miasm/jitter/loader/elf.py +++ b/miasm/jitter/loader/elf.py @@ -269,6 +269,10 @@ def _code_sentinelle(jitter): ifunc_jitter.push_uint32_t(end_addr) elif elf.size == 64: ifunc_jitter.push_uint64_t(end_addr) + else: + raise ValueError( + f"Cannot apply ifunc relocations on an ELF with {elf.size=}" + ) ifunc_jitter.run(addr) addr = getattr(ifunc_jitter.cpu, ifunc_machine.lifter_model_call(loc_db).ret_reg.name) From 6b932c18bbf15360860c8e042b071a856f583779 Mon Sep 17 00:00:00 2001 From: kStor2poche <43518872+kStor2poche@users.noreply.github.com> Date: Sun, 20 Sep 2026 21:35:45 +0200 Subject: [PATCH 03/13] Basic ifunc example to iterate upon --- example/jitter/apply_ifunc_relocs.py | 29 +++++++++++++++++++++ example/samples/ifunc | Bin 0 -> 20424 bytes example/samples/ifunc.c | 37 +++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 example/jitter/apply_ifunc_relocs.py create mode 100755 example/samples/ifunc create mode 100644 example/samples/ifunc.c diff --git a/example/jitter/apply_ifunc_relocs.py b/example/jitter/apply_ifunc_relocs.py new file mode 100644 index 000000000..467874d7a --- /dev/null +++ b/example/jitter/apply_ifunc_relocs.py @@ -0,0 +1,29 @@ +from argparse import ArgumentParser +from pdb import pm +from miasm.analysis.binary import Container, ContainerELF +from miasm.analysis.machine import Machine +from miasm.core.locationdb import LocationDB + +if __name__ == "__main__": + parser = ArgumentParser(description="x86 ELF ifunc relocs") + parser.add_argument("filename", help="ELF to apply (ifunc) relocs to") + parser.add_argument("-j", "--jitter", + help="Jitter engine (default is 'gcc')", + default="gcc") + parser.add_argument("--verbose", "-v", action="store_true", + help="Verbose mode") + args = parser.parse_args() + loc_db = LocationDB() + + myjit = Machine("x86_64").jitter(loc_db, args.jitter) + myjit.init_stack() + + with open(args.filename, 'rb') as f: + elf: ContainerELF = Container.from_stream(f, loc_db=loc_db, apply_reloc=True, run_ifuncs=True) + assert(isinstance(elf, ContainerELF)) + + run_addr = elf._entry_point + + if args.verbose: + myjit.set_trace_log() + myjit.run(run_addr) diff --git a/example/samples/ifunc b/example/samples/ifunc new file mode 100755 index 0000000000000000000000000000000000000000..bf0ccd7279f32583484f59af24c756823c504cac GIT binary patch literal 20424 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 S9dQBs6Z0&M%hC+ep!iSn1*QoA literal 0 HcmV?d00001 diff --git a/example/samples/ifunc.c b/example/samples/ifunc.c new file mode 100644 index 000000000..c0611f945 --- /dev/null +++ b/example/samples/ifunc.c @@ -0,0 +1,37 @@ +// Compile with gcc -masm=intel -o ifunc ifunc.c + +#include +#include +#include +void pxor_c(uint64_t a[2], uint64_t b[2]) { + printf("in boring ver.\n"); + a[0] ^= b[0]; + a[1] ^= b[1]; +} +void pxor_sse2(uint64_t a[2], uint64_t b[2]) { + printf("in sse2 ver.\n"); + asm("MOVDQA xmm1, [%1]\n\t" + "MOVDQA xmm2, [%2]\n\t" + "PXOR xmm1, xmm2" + : "+r" (a) + : "r" (a), + "r" (b) + ); +} + +void pxor(uint64_t a[2], uint64_t b[2]) __attribute__((ifunc("resolve_pxor"))); + +static void *resolve_pxor() { + printf("sse2 support is %b\n", __builtin_cpu_supports("sse2")); + if (__builtin_cpu_supports("sse2")) { + return pxor_sse2; + } + return pxor_c; +} + +int main(void) { + uint64_t a[2] = {0x1337, 0xBEEF}; + uint64_t b[2] = {0xBABA, 0x15DA}; + pxor(a, b); + return EXIT_SUCCESS; +} From c1e0bd4521ab107fcd1d3ae43dcb89f12af0c49d Mon Sep 17 00:00:00 2001 From: kStor2poche <43518872+kStor2poche@users.noreply.github.com> Date: Sun, 20 Sep 2026 21:40:30 +0200 Subject: [PATCH 04/13] Add run_ifunc switch for vm_load_elf and apply_reloc_x86 --- miasm/jitter/loader/elf.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/miasm/jitter/loader/elf.py b/miasm/jitter/loader/elf.py index 606f7c697..c69b2a2cd 100644 --- a/miasm/jitter/loader/elf.py +++ b/miasm/jitter/loader/elf.py @@ -165,7 +165,7 @@ def fill_loc_db_with_symbols(elf, loc_db, base_addr=0): loc_db.add_location(name=name, offset=vaddr) -def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB): +def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB | None, run_ifuncs=False): """Apply relocation for x86 ELF contained in the section @section @elf: miasm.loader's ELF instance @vm: VmMngr instance @@ -251,7 +251,7 @@ def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB): "Unknown relocation type: %d (%r)" % (reloc.type, reloc) ) - if is_ifunc: + if is_ifunc and run_ifuncs: # TODO: only relevant for statically and dynamically linked programs, cf. https://sourceware.org/glibc/manual/latest/html_node/Indirect-Functions.html#When-IFUNC-Resolvers-Run ifunc_machine = Machine(guess_arch(elf)) ifunc_jitter = ifunc_machine.jitter(loc_db) @@ -282,11 +282,14 @@ def _code_sentinelle(jitter): def vm_load_elf(vm, fdata, name="", base_addr=0, loc_db=None, apply_reloc=False, - **kargs): + run_ifuncs=False, **kargs): """ Very dirty elf loader TODO XXX: implement real loader """ + if run_ifuncs and not apply_reloc: + log.warning("vm_load_elf was called with run_ifuncs=True but they won't be run nor applied since apply_reloc=False.") + elf = elf_init.ELF(fdata, **kargs) i = interval() all_data = {} @@ -335,7 +338,7 @@ def vm_load_elf(vm, fdata, name="", base_addr=0, loc_db=None, apply_reloc=False, sections.append(section) for section in sections: if arch in ["x86_64", "x86_32"]: - apply_reloc_x86(elf, vm, section, base_addr, loc_db) + apply_reloc_x86(elf, vm, section, base_addr, loc_db, run_ifuncs) else: log.debug("Unsupported relocation for arch %r" % arch) From 8330418ef4eb89b343e1b0a84b2346b659fb500d Mon Sep 17 00:00:00 2001 From: kStor2poche <43518872+kStor2poche@users.noreply.github.com> Date: Sun, 20 Sep 2026 21:40:49 +0200 Subject: [PATCH 05/13] Switch to f-strings and tiny optimization --- miasm/jitter/loader/elf.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/miasm/jitter/loader/elf.py b/miasm/jitter/loader/elf.py index c69b2a2cd..18dba32f5 100644 --- a/miasm/jitter/loader/elf.py +++ b/miasm/jitter/loader/elf.py @@ -180,7 +180,7 @@ def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB | None, run_ addr_writer = lambda vaddr, addr: vm.set_mem(vaddr, struct.pack(" 0: symbol_entry = symb_section.symtab[r_info_sym] + symbol_name = symbol_entry.name.decode() r_offset = reloc.offset r_addend = reloc.cstr.sym @@ -219,9 +221,9 @@ def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB | None, run_ addr = base_addr + r_addend elif reloc.type == elf_csts.R_X86_64_64: # S + A - addr_symb = loc_db.get_name_offset(symbol_entry.name.decode()) + addr_symb = loc_db.get_name_offset(symbol_name) if addr_symb is None: - log.warning("Unable to find symbol %r" % symbol_entry.name.decode()) + log.warning(f"Unable to find symbol {symbol_name}") continue addr = addr_symb + r_addend where = base_addr + r_offset @@ -231,7 +233,7 @@ def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB | None, run_ (32, elf_csts.R_386_TLS_TPOFF), ]: # Thread dependent, ignore for now - log.debug("Skip relocation TPOFF64 %r", reloc) + log.debug(f"Skip relocation TPOFF64 {reloc}") continue elif (elf.size, reloc.type) in [ (64, elf_csts.R_X86_64_GLOB_DAT), @@ -240,17 +242,14 @@ def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB | None, run_ (32, elf_csts.R_386_GLOB_DAT), ]: # S - addr = loc_db.get_name_offset(symbol_entry.name.decode()) + addr = loc_db.get_name_offset(symbol_name) if addr is None: - log.warning("Unable to find symbol %r" % symbol_entry.name) + log.warning(f"Unable to find symbol {symbol_name}") continue is_ifunc = symbol_entry.info & 0xF == elf_csts.STT_GNU_IFUNC where = base_addr + r_offset else: - raise ValueError( - "Unknown relocation type: %d (%r)" % (reloc.type, - reloc) - ) + raise ValueError(f"Unknown relocation type: {reloc.type} ({reloc})") if is_ifunc and run_ifuncs: # TODO: only relevant for statically and dynamically linked programs, cf. https://sourceware.org/glibc/manual/latest/html_node/Indirect-Functions.html#When-IFUNC-Resolvers-Run ifunc_machine = Machine(guess_arch(elf)) From 112ad406deb3d5dabbced9e4b975c28e12b5312f Mon Sep 17 00:00:00 2001 From: kStor2poche <43518872+kStor2poche@users.noreply.github.com> Date: Wed, 30 Sep 2026 10:07:01 +0200 Subject: [PATCH 06/13] More accurate reloc addend and parsing of Rela relocation entries --- miasm/jitter/loader/elf.py | 27 ++++++++++++++++++++------- miasm/loader/elf.py | 14 ++++++++++++++ miasm/loader/elf_init.py | 22 ++++++++++++++++++++++ 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/miasm/jitter/loader/elf.py b/miasm/jitter/loader/elf.py index 18dba32f5..057cffa12 100644 --- a/miasm/jitter/loader/elf.py +++ b/miasm/jitter/loader/elf.py @@ -24,11 +24,15 @@ def get_import_address_elf(e): + # TODO: rely on DT_NEEDED and/or .dynsym rather than relocs ? import2addr = defaultdict(set) for sh in e.sh: - if not hasattr(sh, 'rel'): - continue - for k, v in viewitems(sh.rel): + rel = [] + if hasattr(sh, 'rel'): + rel += sh.rel.items() + if hasattr(sh, 'rela'): + rel += sh.rela.items() + for k, v in rel: k = force_str(k) import2addr[('xxx', k)].add(v.offset) return import2addr @@ -183,8 +187,14 @@ def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB | None, run_ raise ValueError(f"Unsupported elf size {elf.size}") symb_section = section.linksection - for reloc in section.reltab: + if hasattr(section, "reltab"): + table = section.reltab + elif hasattr(section, "relatab"): + table = section.relatab + else: + raise ValueError(f"Trying to apply reloc on section without RelTable or RelATable.") + for reloc in table: # Parse relocation info r_info = reloc.info if elf.size == 64: @@ -202,7 +212,10 @@ def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB | None, run_ symbol_name = symbol_entry.name.decode() r_offset = reloc.offset - r_addend = reloc.cstr.sym + if hasattr(reloc, "addend"): + addend = reloc.addend + else: + addend = int.from_bytes(elf.get_virt().get(r_offset, r_offset + elf.size // 8), byteorder="little") if (elf.size, reloc.type) in [ (64, elf_csts.R_X86_64_RELATIVE), @@ -225,7 +238,7 @@ def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB | None, run_ if addr_symb is None: log.warning(f"Unable to find symbol {symbol_name}") continue - addr = addr_symb + r_addend + addr = addr_symb + addend where = base_addr + r_offset elif (elf.size, reloc.type) in [ (64, elf_csts.R_X86_64_TPOFF64), @@ -325,7 +338,7 @@ def vm_load_elf(vm, fdata, name="", base_addr=0, loc_db=None, apply_reloc=False, arch = guess_arch(elf) sections = [] for section in elf.sh: - if not hasattr(section, 'reltab'): + if not (hasattr(section, 'reltab') or hasattr(section, 'relatab')): continue if isinstance(section, elf_init.RelATable): pass diff --git a/miasm/loader/elf.py b/miasm/loader/elf.py index 742587826..6fc05381b 100644 --- a/miasm/loader/elf.py +++ b/miasm/loader/elf.py @@ -486,6 +486,20 @@ class Dynamic(CStruct): DF_1_ENDFILTEE = 0x00004000 # Filtee terminates filters search. DF_1_DISPRELDNE = 0x00008000 # Disp reloc applied at build time. DF_1_DISPRELPND = 0x00010000 # Disp reloc applied at run-time. +DF_1_NODIRECT = 0x00020000 # Object has no-direct binding. +DF_1_IGNMULDEF = 0x00040000 +DF_1_NOKSYMS = 0x00080000 +DF_1_NOHDR = 0x00100000 +DF_1_EDITED = 0x00200000 # Object is modified after built. +DF_1_NORELOC = 0x00400000 +DF_1_SYMINTPOSE = 0x00800000 # Object has individual interposers. +DF_1_GLOBAUDIT = 0x01000000 # Global auditing required. +DF_1_SINGLETON = 0x02000000 # Singleton symbols are used. +DF_1_STUB = 0x04000000 +DF_1_PIE = 0x08000000 +DF_1_KMOD = 0x10000000 +DF_1_WEAKFILTER = 0x20000000 +DF_1_NOCOMMON = 0x40000000 # Flags for the feature selection in DT_FEATURE_1. DTF_1_PARINIT = 0x00000001 diff --git a/miasm/loader/elf_init.py b/miasm/loader/elf_init.py index cffb35758..1b3aed662 100644 --- a/miasm/loader/elf_init.py +++ b/miasm/loader/elf_init.py @@ -489,6 +489,28 @@ def parse_content(self, sex, size): class RelATable(RelTable): sht = elf.SHT_RELA + def parse_content(self, sex, size): + self.sex, self.size = sex, size + if size == 32: + WRela = WRela32 + elif size == 64: + WRela = WRela64 + else: + ValueError('unknown size') + c = self.content + self.relatab = [] + self.rela = {} + sz = self.sh.entsize + + idx = 0 + while len(c) > sz*idx: + s = c[sz*idx:sz*(idx+1)] + idx += 1 + rela = WRela(self, sex, size, s) + self.relatab.append(rela) + if rela.parent.linksection != self.parent.shlist[0]: + self.rela[rela.sym] = rela + # Section List From 7e28ed9607a488a3e93f6cea97cd76615409fd17 Mon Sep 17 00:00:00 2001 From: kStor2poche <43518872+kStor2poche@users.noreply.github.com> Date: Wed, 30 Sep 2026 10:09:21 +0200 Subject: [PATCH 07/13] Enforce base address=0 when loading non-PIE ELFs --- miasm/analysis/binary.py | 4 ++++ miasm/jitter/loader/elf.py | 3 +++ 2 files changed, 7 insertions(+) diff --git a/miasm/analysis/binary.py b/miasm/analysis/binary.py index c278594b1..4cbb76f5b 100644 --- a/miasm/analysis/binary.py +++ b/miasm/analysis/binary.py @@ -3,6 +3,7 @@ from miasm.core.bin_stream import bin_stream_str, bin_stream_elf, bin_stream_pe from miasm.jitter.csts import PAGE_READ +from miasm.loader.elf import ET_EXEC log = logging.getLogger("binary") @@ -202,6 +203,9 @@ def parse(self, data, vm=None, addr=0, apply_reloc=False, **kwargs): # Build the bin_stream instance and set the entry point try: self._bin_stream = bin_stream_elf(self._executable) + if self._executable.Ehdr.type == ET_EXEC: + addr = 0 + log.warning("This elf has Ehdr.type == ET_EXEC meaning it isn't relocatable. Using 0 as base_addr.") self._entry_point = self._executable.Ehdr.entry + addr except Exception as error: raise ContainerParsingException('Cannot read ELF: %s' % error) diff --git a/miasm/jitter/loader/elf.py b/miasm/jitter/loader/elf.py index 057cffa12..2773b34f5 100644 --- a/miasm/jitter/loader/elf.py +++ b/miasm/jitter/loader/elf.py @@ -305,6 +305,9 @@ def vm_load_elf(vm, fdata, name="", base_addr=0, loc_db=None, apply_reloc=False, elf = elf_init.ELF(fdata, **kargs) i = interval() all_data = {} + if elf.Ehdr.type == elf_csts.ET_EXEC and base_addr != 0: + log.warning("This elf has Ehdr.type == ET_EXEC meaning it isn't relocatable. Reversing base_addr to 0.") + base_addr = 0 for p in elf.ph.phlist: if p.ph.type != elf_csts.PT_LOAD: From 7297c8d964b5674f7e85dd715701c0a4a72910a0 Mon Sep 17 00:00:00 2001 From: kStor2poche <43518872+kStor2poche@users.noreply.github.com> Date: Wed, 30 Sep 2026 10:11:36 +0200 Subject: [PATCH 08/13] Cleanup ifunc reloc implementation --- miasm/analysis/binary.py | 5 +- miasm/jitter/loader/elf.py | 203 +++++++++++++++++++++++++++++-------- 2 files changed, 164 insertions(+), 44 deletions(-) diff --git a/miasm/analysis/binary.py b/miasm/analysis/binary.py index 4cbb76f5b..54f6f8e29 100644 --- a/miasm/analysis/binary.py +++ b/miasm/analysis/binary.py @@ -164,7 +164,7 @@ def parse(self, data, vm=None, **kwargs): class ContainerELF(Container): "Container abstraction for ELF" - def parse(self, data, vm=None, addr=0, apply_reloc=False, **kwargs): + def parse(self, data, vm=None, addr=0, apply_reloc=False, run_ifuncs=False, **kwargs): """Load an ELF from @data @data: bytes containing the ELF bytes @vm (optional): VmMngr instance. If set, load the ELF in virtual memory @@ -190,7 +190,8 @@ def parse(self, data, vm=None, addr=0, apply_reloc=False, **kwargs): data, loc_db=self.loc_db, base_addr=addr, - apply_reloc=apply_reloc + apply_reloc=apply_reloc, + run_ifuncs=run_ifuncs ) else: self._executable = elf_init.ELF(data) diff --git a/miasm/jitter/loader/elf.py b/miasm/jitter/loader/elf.py index 2773b34f5..df5606bdf 100644 --- a/miasm/jitter/loader/elf.py +++ b/miasm/jitter/loader/elf.py @@ -37,6 +37,13 @@ def get_import_address_elf(e): import2addr[('xxx', k)].add(v.offset) return import2addr +def get_addr_writer(elf, vm): + if elf.size == 64: + return lambda vaddr, addr: vm.set_mem(vaddr, struct.pack("> 8) & 0xFFFFFF r_info_type = r_info & 0xFF + else: + raise ValueError( + f"Cannot parse relocations on an ELF with {elf.size=}" + ) is_ifunc = False symbol_entry = None @@ -219,19 +228,19 @@ def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB | None, run_ if (elf.size, reloc.type) in [ (64, elf_csts.R_X86_64_RELATIVE), - (64, elf_csts.R_X86_64_IRELATIVE), (32, elf_csts.R_386_RELATIVE), + ]: + # B + A + where = base_addr + r_offset + addr = base_addr + addend + elif (elf.size, reloc.type) in [ + (64, elf_csts.R_X86_64_IRELATIVE), (32, elf_csts.R_386_IRELATIVE), ]: - + # indirect B + A (indirect as in ifunc) where = base_addr + r_offset - addr = int.from_bytes(elf.get_virt().get(r_offset, r_offset + elf.size // 8), byteorder="little") - ifunc_syms = [s for s in elf.sh.symtab.symtab if s.value == addr and s.info & elf_csts.STT_GNU_IFUNC == elf_csts.STT_GNU_IFUNC] - if len(ifunc_syms) > 0: - is_ifunc = True - else: - # B + A - addr = base_addr + r_addend + addr = base_addr + addend + is_ifunc = True elif reloc.type == elf_csts.R_X86_64_64: # S + A addr_symb = loc_db.get_name_offset(symbol_name) @@ -259,35 +268,12 @@ def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB | None, run_ if addr is None: log.warning(f"Unable to find symbol {symbol_name}") continue - is_ifunc = symbol_entry.info & 0xF == elf_csts.STT_GNU_IFUNC where = base_addr + r_offset else: raise ValueError(f"Unknown relocation type: {reloc.type} ({reloc})") if is_ifunc and run_ifuncs: - # TODO: only relevant for statically and dynamically linked programs, cf. https://sourceware.org/glibc/manual/latest/html_node/Indirect-Functions.html#When-IFUNC-Resolvers-Run - ifunc_machine = Machine(guess_arch(elf)) - ifunc_jitter = ifunc_machine.jitter(loc_db) - - for map_addr, map_mem in vm.get_all_memory().items(): - ifunc_jitter.vm.add_memory_page(map_addr, map_mem["access"], map_mem["data"]) - - ifunc_jitter.init_stack() - end_addr = 0x1337beef - def _code_sentinelle(jitter): - jitter.running = False - return False - ifunc_jitter.add_breakpoint(end_addr, _code_sentinelle) - if elf.size == 32: - ifunc_jitter.push_uint32_t(end_addr) - elif elf.size == 64: - ifunc_jitter.push_uint64_t(end_addr) - else: - raise ValueError( - f"Cannot apply ifunc relocations on an ELF with {elf.size=}" - ) - - ifunc_jitter.run(addr) - addr = getattr(ifunc_jitter.cpu, ifunc_machine.lifter_model_call(loc_db).ret_reg.name) + _run_ifunc_resolver_copy((where, addr), elf, vm, loc_db) + continue log.debug(f"Write {addr:x} at {where:x}") addr_writer(where, addr) @@ -359,6 +345,139 @@ def vm_load_elf(vm, fdata, name="", base_addr=0, loc_db=None, apply_reloc=False, return elf +def get_ifuncs(elf, base_addr, with_syms=False): + """ + Returns all ifunc resolvers found in @elf along with their GOT entry and their associated symbols if they exist and @with_syms == True + + @elf: miasm.loader.elf_init.ELF + @return: list[(to_reloc: int, resolver: int, list[miasm.loader.elf_init.WSym(32|64)] if with_syms)] + """ + res = [] + explicit_addend = False + for sh in elf.sh: + if hasattr(sh, "reltab"): + table = sh.reltab + elif hasattr(sh, "relatab"): + table = sh.relatab + explicit_addend = True + else: + continue + for reloc in table: + if (elf.size, reloc.type) in [ + (64, elf_csts.R_X86_64_IRELATIVE), + (32, elf_csts.R_386_IRELATIVE), + ]: + addend = reloc.addend if explicit_addend else int.from_bytes(elf.get_virt().get(reloc.offset, reloc.offset + elf.size // 8), byteorder="little") + + # indirect B + A (indirect as in ifunc) + to_reloc = base_addr + reloc.offset + resolver = base_addr + addend + if with_syms: + ifunc_syms = [s for s in elf.sh.symtab.symtab if s.value == resolver and s.info & elf_csts.STT_GNU_IFUNC == elf_csts.STT_GNU_IFUNC] + res.append((to_reloc, resolver, ifunc_syms)) + else: + res.append((to_reloc, resolver)) + return res + +def run_ifunc_resolvers_copy(resolvers, elf, vm, loc_db): + """ + WARNING: this is only accurate for dynamically-linked ELFs. Static and static-pie executables' ifuncs are loaded at runtime during libc initialization. + Runs all provided ifunc resolvers, starting each ifunc off of a clean copy of the passed vm + + @resolvers: list[(reloc: int, resolved: int)] + @elf: miasm.loader.elf_init.ELF + @vm: VmMngr - with the loaded elf to copy + @loc_db: LocationDB - also with the loaded elf + @return: None + """ + for resolver in resolvers: + _run_ifunc_resolver_copy(resolver, elf, vm, loc_db) + +def run_ifunc_resolvers_mut(resolvers, elf, jitter): + """ + WARNING: this is only accurate for dynamically-linked binaries. Static and static-pie executables' ifuncs are loaded at runtime during libc initialization. + WARNING: this requires the jitter to have an initialized stack + Runs all provided ifunc resolvers + + @resolvers: list[(reloc: int, resolved: int)] + @elf: miasm.loader.elf_init.ELF + @jitter: Jitter - a jitter with the related elf loaded + @return: None + """ + for resolver in resolvers: + _run_ifunc_resolver_mut(resolver, elf, jitter) + +def _run_ifunc_resolver_copy(resolver, elf, vm, loc_db): + """ + WARNING: this is only accurate for dynamically-linked binaries. Static and static-pie executables' ifuncs are loaded at runtime during libc initialization. + Runs all provided ifunc resolvers + + @resolver: (reloc: int, resolved: int) + @elf: miasm.loader.elf_init.ELF + @vm: VmMngr - with the loaded elf to copy + @loc_db: LocationDB - idem + @return: None + """ + addr_writer = get_addr_writer(elf, vm) + ifunc_machine = Machine(guess_arch(elf)) + ifunc_jitter = ifunc_machine.jitter(loc_db) + + last_addr = 0x100 + stack_base_found = False + ifunc_jitter.stack_size = 0x100 + for map_addr, map_mem in vm.get_all_memory().items(): + map_data = map_mem["data"] + # find somewhere for our stack to go + if map_addr - last_addr > ifunc_jitter.stack_size: + ifunc_jitter.stack_base = last_addr + last_addr = float("inf") + stack_base_found = True + else: + last_addr = map_addr + len(map_data) + + # and copy the memory already mapped by the loader (and maybe the user, alas) + ifunc_jitter.vm.add_memory_page(map_addr, map_mem["access"], map_data) + + if stack_base_found: + ifunc_jitter.init_stack() + else: + raise ValueError("Couldn't find enough space to allocate our ifunc runner's stack") + _run_ifunc_resolver_mut(resolver, elf, ifunc_jitter, addr_writer) + +def _run_ifunc_resolver_mut(resolver, elf, jitter, addr_writer=None): + """ + WARNING: this is only accurate for dynamically-linked binaries. Static and static-pie executables' ifuncs are loaded at runtime during libc initialization. + WARNING: this requires the jitter to have an initialized stack + Runs all provided ifunc resolvers + + @resolver: (reloc: int, resolved: int) + @elf: miasm.loader.elf_init.ELF + @jitter: Jitter - a jitter with the related elf loaded + @addr_writer: Function | None - if None, will be set to the writer associated to the given jitter's vm. This is probably what you want if you use this function in your script. + @return: None + """ + if addr_writer is None: + addr_writer = get_addr_writer(elf, jitter.vm) + + end_addr = 0x1337beef + def _code_sentinelle(jitter): + jitter.running = False + return False + jitter.add_breakpoint(end_addr, _code_sentinelle) + if elf.size == 32: + jitter.push_uint32_t(end_addr) + elif elf.size == 64: + jitter.push_uint64_t(end_addr) + else: + raise ValueError( + f"Cannot apply ifunc relocations on an ELF with {elf.size=}" + ) + + jitter.run(resolver[1]) + resolved_funcaddr = getattr(jitter.cpu, "RAX" if elf.size == 64 else "EAX") + log.debug(f"Write {resolver[0]:x} at {resolved_funcaddr:x}") + addr_writer(resolver[0], resolved_funcaddr) + class libimp_elf(libimp): pass From 6af66af8585cb724c7adea353d48a37e26962cef Mon Sep 17 00:00:00 2001 From: kStor2poche <43518872+kStor2poche@users.noreply.github.com> Date: Wed, 30 Sep 2026 10:12:16 +0200 Subject: [PATCH 09/13] Better ifunc example and derived test --- example/jitter/apply_ifunc_relocs.py | 29 -------- example/jitter/resolve_ifunc.py | 97 +++++++++++++++++++++++++++ example/samples/ifunc | Bin 20424 -> 16120 bytes example/samples/ifunc.c | 51 +++++++------- test/test_all.py | 3 +- 5 files changed, 123 insertions(+), 57 deletions(-) delete mode 100644 example/jitter/apply_ifunc_relocs.py create mode 100644 example/jitter/resolve_ifunc.py diff --git a/example/jitter/apply_ifunc_relocs.py b/example/jitter/apply_ifunc_relocs.py deleted file mode 100644 index 467874d7a..000000000 --- a/example/jitter/apply_ifunc_relocs.py +++ /dev/null @@ -1,29 +0,0 @@ -from argparse import ArgumentParser -from pdb import pm -from miasm.analysis.binary import Container, ContainerELF -from miasm.analysis.machine import Machine -from miasm.core.locationdb import LocationDB - -if __name__ == "__main__": - parser = ArgumentParser(description="x86 ELF ifunc relocs") - parser.add_argument("filename", help="ELF to apply (ifunc) relocs to") - parser.add_argument("-j", "--jitter", - help="Jitter engine (default is 'gcc')", - default="gcc") - parser.add_argument("--verbose", "-v", action="store_true", - help="Verbose mode") - args = parser.parse_args() - loc_db = LocationDB() - - myjit = Machine("x86_64").jitter(loc_db, args.jitter) - myjit.init_stack() - - with open(args.filename, 'rb') as f: - elf: ContainerELF = Container.from_stream(f, loc_db=loc_db, apply_reloc=True, run_ifuncs=True) - assert(isinstance(elf, ContainerELF)) - - run_addr = elf._entry_point - - if args.verbose: - myjit.set_trace_log() - myjit.run(run_addr) diff --git a/example/jitter/resolve_ifunc.py b/example/jitter/resolve_ifunc.py new file mode 100644 index 000000000..73dc9fe56 --- /dev/null +++ b/example/jitter/resolve_ifunc.py @@ -0,0 +1,97 @@ +import logging +from argparse import ArgumentParser + +from miasm.analysis.binary import Container, ContainerELF +from miasm.analysis.machine import Machine +from miasm.core.locationdb import LocationDB +from miasm.jitter.loader.elf import get_ifuncs, run_ifunc_resolvers_copy +from miasm.loader.elf import ET_EXEC + + +def code_sentinelle(jitter): + jitter.running = False + jitter.pc = 0 + return False + +def prepare(run_ifuncs: bool): + loc_db = LocationDB() + + myjit = Machine("x86_64").jitter(loc_db, args.jitter) + myjit.init_stack() + base_addr = 0x400000 + + with open(args.filename, 'rb') as f: + elf: ContainerELF = Container.from_stream(f, addr=base_addr, loc_db=loc_db, vm=myjit.vm, apply_reloc=True, run_ifuncs=run_ifuncs) + assert isinstance(elf, ContainerELF) + if elf.executable.Ehdr.type == ET_EXEC: + # static executables can't be rebased + base_addr = 0 + + return (base_addr, loc_db, elf, myjit) + +def launch(jitter): + jitter.push_uint64_t(0x1337beef) + jitter.add_breakpoint(0x1337beef, code_sentinelle) + + if args.verbose >= 2: + jitter.set_trace_log(True, True) + run_at = jitter.lifter.loc_db.get_name_offset("intermediate") + assert run_at is not None + jitter.run(run_at) + return jitter.get_c_str(jitter.cpu.RAX) + +if __name__ == "__main__": + parser = ArgumentParser(description="x86 ELF ifunc relocs (e.g. apsamples/ifunc)") + parser.add_argument("filename", help="ELF to apply (ifunc) relocs to") + parser.add_argument("-j", "--jitter", + help="Jitter engine (default is 'gcc')", + default="gcc") + parser.add_argument("--verbose", "-v", action="count", + help="Verbose mode (-v activates debug logging, -vv adds jitter tracing)", + default=0) + args = parser.parse_args() + + # setup logging + log = logging.getLogger(__name__) + ch = logging.StreamHandler() + formatter = logging.Formatter("[%(levelname)-8s]: %(message)s") + ch.setFormatter(formatter) + if args.verbose >= 1: + log.setLevel(logging.DEBUG) + else: + log.setLevel(logging.WARNING) + log.addHandler(ch) + + + # 1. Run ifunc with default resolver behavior + + _, _, _, jitter = prepare(run_ifuncs=True) + res1 = launch(jitter) + log.info(f"Default ifunc resolving returned {res1}.") + assert res1 == "zglorg" + + + # 2. Run ifunc with alternate resolver behavior + + base_addr, loc_db, elf, jitter = prepare(run_ifuncs=False) + + # The ifunc resolver in ../samples/ifunc uses .bss variable `use_func2` + # to know whether to redirect to `func1` or `func2` when we execute `func` + # which we proceed to set to `true` + use_func2_addr = loc_db.get_name_offset("use_func2") + if use_func2_addr is not None and jitter.vm.is_mapped(use_func2_addr + base_addr, 1): + jitter.vm.set_mem(use_func2_addr + base_addr, b'\x01') + log.info(f"Set symbol use_func2 (@0x{use_func2_addr + base_addr:x}) to 1") + else: + if use_func2_addr is None: + raise ValueError("symbol use_func2 was not found") + else: + raise ValueError(f"use_func2 exists but doesn't seem to be loaded inside our vm ({use_func2_addr+base_addr=:x})\n{jitter.vm}") + + # we can then run our infunc resolver + ifunc_resolvers = get_ifuncs(elf.executable, base_addr, with_syms=False) + run_ifunc_resolvers_copy(ifunc_resolvers, elf.executable, jitter.vm, loc_db) + + res2 = launch(jitter) + log.info(f"Forcing ifunc resolving to func2 returned {res2}.") + assert res2 == "bloups" diff --git a/example/samples/ifunc b/example/samples/ifunc index bf0ccd7279f32583484f59af24c756823c504cac..50cddb390535d26e8b9283bbe14a46d57026bf85 100755 GIT binary patch literal 16120 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 S9dQBs6Z0&M%hC+ep!iSn1*QoA diff --git a/example/samples/ifunc.c b/example/samples/ifunc.c index c0611f945..e3083dd1e 100644 --- a/example/samples/ifunc.c +++ b/example/samples/ifunc.c @@ -1,37 +1,34 @@ -// Compile with gcc -masm=intel -o ifunc ifunc.c +// Compile with gcc -fno-stack-protector ifunc ifunc.c +#include #include -#include -#include -void pxor_c(uint64_t a[2], uint64_t b[2]) { - printf("in boring ver.\n"); - a[0] ^= b[0]; - a[1] ^= b[1]; + +const char *func_1() { + return "zglorg"; } -void pxor_sse2(uint64_t a[2], uint64_t b[2]) { - printf("in sse2 ver.\n"); - asm("MOVDQA xmm1, [%1]\n\t" - "MOVDQA xmm2, [%2]\n\t" - "PXOR xmm1, xmm2" - : "+r" (a) - : "r" (a), - "r" (b) - ); +const char *func_2() { + return "bloups"; } -void pxor(uint64_t a[2], uint64_t b[2]) __attribute__((ifunc("resolve_pxor"))); +static bool use_func2; + +char *func() __attribute__((ifunc("resolve_func"))); + +// Function pointer type matching `func` prototype +typedef const char *(*func_type)(); -static void *resolve_pxor() { - printf("sse2 support is %b\n", __builtin_cpu_supports("sse2")); - if (__builtin_cpu_supports("sse2")) { - return pxor_sse2; +static func_type resolve_func() { + if (use_func2) { + return func_2; } - return pxor_c; + return func_1; +} + +char *intermediate() { + return func(); } -int main(void) { - uint64_t a[2] = {0x1337, 0xBEEF}; - uint64_t b[2] = {0xBABA, 0x15DA}; - pxor(a, b); - return EXIT_SUCCESS; +int main() { + intermediate(); + return 0; } diff --git a/test/test_all.py b/test/test_all.py index 4997bf0ad..57b2e6e4a 100755 --- a/test/test_all.py +++ b/test/test_all.py @@ -831,7 +831,8 @@ class ExampleJitterNoPython(ExampleJitter): "l", "-a", "0"], [test_arml]), (["sandbox_call.py", Example.get_sample("md5_arm")], []), (["sandbox_pe_x86_32.py", Example.get_sample("x86_32_automod_2.bin")], - [test_x86_32_automod_2]) + [test_x86_32_automod_2]), + (["resolve_ifunc.py", Example.get_sample("ifunc")], []), ] + [(["sandbox_pe_x86_32.py", Example.get_sample("x86_32_" + name + ".bin")], [test_box[name]]) From 69b1cb96484520358312bb976ce113caebf5077a Mon Sep 17 00:00:00 2001 From: kStor2poche <43518872+kStor2poche@users.noreply.github.com> Date: Mon, 5 Oct 2026 11:03:49 +0200 Subject: [PATCH 10/13] Improved interface for ifunc resolving and adapted example --- example/jitter/resolve_ifunc.py | 28 +++--- example/samples/ifunc.c | 2 +- miasm/jitter/loader/elf.py | 165 ++++++++++++++------------------ 3 files changed, 87 insertions(+), 108 deletions(-) diff --git a/example/jitter/resolve_ifunc.py b/example/jitter/resolve_ifunc.py index 73dc9fe56..42859fa48 100644 --- a/example/jitter/resolve_ifunc.py +++ b/example/jitter/resolve_ifunc.py @@ -4,7 +4,7 @@ from miasm.analysis.binary import Container, ContainerELF from miasm.analysis.machine import Machine from miasm.core.locationdb import LocationDB -from miasm.jitter.loader.elf import get_ifuncs, run_ifunc_resolvers_copy +from miasm.jitter.loader.elf import get_ifuncs, apply_ifunc from miasm.loader.elf import ET_EXEC @@ -41,7 +41,7 @@ def launch(jitter): return jitter.get_c_str(jitter.cpu.RAX) if __name__ == "__main__": - parser = ArgumentParser(description="x86 ELF ifunc relocs (e.g. apsamples/ifunc)") + parser = ArgumentParser(description="x86 ELF ifunc relocs (e.g. `python resolve_ifunc.py -j llvm -vv ../samples/ifunc`)") parser.add_argument("filename", help="ELF to apply (ifunc) relocs to") parser.add_argument("-j", "--jitter", help="Jitter engine (default is 'gcc')", @@ -78,19 +78,23 @@ def launch(jitter): # The ifunc resolver in ../samples/ifunc uses .bss variable `use_func2` # to know whether to redirect to `func1` or `func2` when we execute `func` # which we proceed to set to `true` + # + # Note that we could've created a separate jitter to run ifuncs on (5th arg + # of apply_ifunc) if we wanted to keep our bss clean for the regular jitter use_func2_addr = loc_db.get_name_offset("use_func2") - if use_func2_addr is not None and jitter.vm.is_mapped(use_func2_addr + base_addr, 1): - jitter.vm.set_mem(use_func2_addr + base_addr, b'\x01') - log.info(f"Set symbol use_func2 (@0x{use_func2_addr + base_addr:x}) to 1") - else: - if use_func2_addr is None: - raise ValueError("symbol use_func2 was not found") - else: - raise ValueError(f"use_func2 exists but doesn't seem to be loaded inside our vm ({use_func2_addr+base_addr=:x})\n{jitter.vm}") - # we can then run our infunc resolver + if use_func2_addr is None: + raise ValueError("symbol use_func2 was not found") + if not jitter.vm.is_mapped(use_func2_addr + base_addr, 1): + raise ValueError(f"use_func2 exists but doesn't seem to be loaded inside our vm ({use_func2_addr+base_addr=:x})\n{jitter.vm}") + + jitter.vm.set_mem(use_func2_addr + base_addr, b'\x01') + log.info(f"Set symbol use_func2 (@0x{use_func2_addr + base_addr:x}) to 1") + + # we can then run our ifunc resolver ifunc_resolvers = get_ifuncs(elf.executable, base_addr, with_syms=False) - run_ifunc_resolvers_copy(ifunc_resolvers, elf.executable, jitter.vm, loc_db) + for reloc, ifunc_resolver in ifunc_resolvers: + apply_ifunc(reloc, ifunc_resolver, elf.executable, jitter) res2 = launch(jitter) log.info(f"Forcing ifunc resolving to func2 returned {res2}.") diff --git a/example/samples/ifunc.c b/example/samples/ifunc.c index e3083dd1e..74aa43699 100644 --- a/example/samples/ifunc.c +++ b/example/samples/ifunc.c @@ -1,4 +1,4 @@ -// Compile with gcc -fno-stack-protector ifunc ifunc.c +// Compile with gcc -fno-stack-protector -o ifunc ifunc.c #include #include diff --git a/miasm/jitter/loader/elf.py b/miasm/jitter/loader/elf.py index df5606bdf..31c8d9b81 100644 --- a/miasm/jitter/loader/elf.py +++ b/miasm/jitter/loader/elf.py @@ -37,14 +37,6 @@ def get_import_address_elf(e): import2addr[('xxx', k)].add(v.offset) return import2addr -def get_addr_writer(elf, vm): - if elf.size == 64: - return lambda vaddr, addr: vm.set_mem(vaddr, struct.pack(" ifunc_jitter.stack_size: + ifunc_jitter.stack_base = last_addr + last_addr = float("inf") + stack_base_found = True + else: + last_addr = map_addr + len(map_data) + + # and copy the memory already mapped by the loader + ifunc_jitter.vm.add_memory_page(map_addr, map_mem["access"], map_data) + + if stack_base_found: + ifunc_jitter.init_stack() + else: + raise ValueError("Couldn't find enough space to allocate our ifunc runner's stack") + for reloc in table: # Parse relocation info r_info = reloc.info @@ -272,11 +288,15 @@ def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB | None, run_ else: raise ValueError(f"Unknown relocation type: {reloc.type} ({reloc})") if is_ifunc and run_ifuncs: - _run_ifunc_resolver_copy((where, addr), elf, vm, loc_db) - continue + addr = _resolve_ifunc(where, addr, elf, ifunc_jitter) log.debug(f"Write {addr:x} at {where:x}") - addr_writer(where, addr) + if elf.size == 64: + return vm.set_u64(where, addr) + elif elf.size == 32: + return vm.set_u32(where, addr) + else: + raise ValueError(f"Unsupported elf size {elf.size}") def vm_load_elf(vm, fdata, name="", base_addr=0, loc_db=None, apply_reloc=False, @@ -379,105 +399,60 @@ def get_ifuncs(elf, base_addr, with_syms=False): res.append((to_reloc, resolver)) return res -def run_ifunc_resolvers_copy(resolvers, elf, vm, loc_db): - """ - WARNING: this is only accurate for dynamically-linked ELFs. Static and static-pie executables' ifuncs are loaded at runtime during libc initialization. - Runs all provided ifunc resolvers, starting each ifunc off of a clean copy of the passed vm - - @resolvers: list[(reloc: int, resolved: int)] - @elf: miasm.loader.elf_init.ELF - @vm: VmMngr - with the loaded elf to copy - @loc_db: LocationDB - also with the loaded elf - @return: None - """ - for resolver in resolvers: - _run_ifunc_resolver_copy(resolver, elf, vm, loc_db) - -def run_ifunc_resolvers_mut(resolvers, elf, jitter): +def _resolve_ifunc(reloc_addr, resolver_addr, elf, run_jitter): """ WARNING: this is only accurate for dynamically-linked binaries. Static and static-pie executables' ifuncs are loaded at runtime during libc initialization. WARNING: this requires the jitter to have an initialized stack - Runs all provided ifunc resolvers + Runs provided ifunc resolver - @resolvers: list[(reloc: int, resolved: int)] + @reloc_addr: int - the address where we want to apply our reloc + @resolver_addr: int - the address of the ifunc resolver @elf: miasm.loader.elf_init.ELF - @jitter: Jitter - a jitter with the related elf loaded + @run_jitter: Jitter to run the ifunc resolver on @return: None """ - for resolver in resolvers: - _run_ifunc_resolver_mut(resolver, elf, jitter) - -def _run_ifunc_resolver_copy(resolver, elf, vm, loc_db): - """ - WARNING: this is only accurate for dynamically-linked binaries. Static and static-pie executables' ifuncs are loaded at runtime during libc initialization. - Runs all provided ifunc resolvers - - @resolver: (reloc: int, resolved: int) - @elf: miasm.loader.elf_init.ELF - @vm: VmMngr - with the loaded elf to copy - @loc_db: LocationDB - idem - @return: None - """ - addr_writer = get_addr_writer(elf, vm) - ifunc_machine = Machine(guess_arch(elf)) - ifunc_jitter = ifunc_machine.jitter(loc_db) - - last_addr = 0x100 - stack_base_found = False - ifunc_jitter.stack_size = 0x100 - for map_addr, map_mem in vm.get_all_memory().items(): - map_data = map_mem["data"] - # find somewhere for our stack to go - if map_addr - last_addr > ifunc_jitter.stack_size: - ifunc_jitter.stack_base = last_addr - last_addr = float("inf") - stack_base_found = True - else: - last_addr = map_addr + len(map_data) - - # and copy the memory already mapped by the loader (and maybe the user, alas) - ifunc_jitter.vm.add_memory_page(map_addr, map_mem["access"], map_data) - - if stack_base_found: - ifunc_jitter.init_stack() + end_addr = 0x1337beef + def _code_sentinelle(j): + j.running = False + return False + run_jitter.add_breakpoint(end_addr, _code_sentinelle) + if elf.size == 32: + run_jitter.push_uint32_t(end_addr) + elif elf.size == 64: + run_jitter.push_uint64_t(end_addr) else: - raise ValueError("Couldn't find enough space to allocate our ifunc runner's stack") - _run_ifunc_resolver_mut(resolver, elf, ifunc_jitter, addr_writer) + raise ValueError( + f"Cannot resolve ifunc on an ELF with unsupported size {elf.size}" + ) + + run_jitter.run(resolver_addr) + resolved_funcaddr = getattr(run_jitter.cpu, "RAX" if elf.size == 64 else "EAX") + return resolved_funcaddr -def _run_ifunc_resolver_mut(resolver, elf, jitter, addr_writer=None): +def apply_ifunc(reloc_addr, resolver_addr, elf, jitter, run_jitter=None): """ WARNING: this is only accurate for dynamically-linked binaries. Static and static-pie executables' ifuncs are loaded at runtime during libc initialization. - WARNING: this requires the jitter to have an initialized stack - Runs all provided ifunc resolvers + Runs and applies provided ifunc resolver - @resolver: (reloc: int, resolved: int) + @reloc_addr: int - the address where we want to apply our reloc + @resolver_addr: int - the address of the ifunc resolver @elf: miasm.loader.elf_init.ELF - @jitter: Jitter - a jitter with the related elf loaded - @addr_writer: Function | None - if None, will be set to the writer associated to the given jitter's vm. This is probably what you want if you use this function in your script. + @jitter: Jitter instance to apply the reloc to + @run_jitter: (optional) Jitter to run the ifunc resolver on in place of @jitter @return: None """ - if addr_writer is None: - addr_writer = get_addr_writer(elf, jitter.vm) + if run_jitter is None: + run_jitter = jitter - end_addr = 0x1337beef - def _code_sentinelle(jitter): - jitter.running = False - return False - jitter.add_breakpoint(end_addr, _code_sentinelle) - if elf.size == 32: - jitter.push_uint32_t(end_addr) - elif elf.size == 64: - jitter.push_uint64_t(end_addr) - else: - raise ValueError( - f"Cannot apply ifunc relocations on an ELF with {elf.size=}" - ) - - jitter.run(resolver[1]) - resolved_funcaddr = getattr(jitter.cpu, "RAX" if elf.size == 64 else "EAX") - log.debug(f"Write {resolver[0]:x} at {resolved_funcaddr:x}") - addr_writer(resolver[0], resolved_funcaddr) + resolved_funcaddr = _resolve_ifunc(reloc_addr, resolver_addr, elf, run_jitter) + log.debug(f"Write {resolved_funcaddr:x} at {reloc_addr:x}") + if elf.size == 64: + return jitter.vm.set_u64(reloc_addr, resolved_funcaddr) + elif elf.size == 32: + return jitter.vm.set_u32(reloc_addr, resolved_funcaddr) + else: + raise ValueError(f"Unsupported elf size {elf.size}") class libimp_elf(libimp): pass From 63bdc0a5b2ab9b55821a609c36bcdff315188847 Mon Sep 17 00:00:00 2001 From: kStor2poche <43518872+kStor2poche@users.noreply.github.com> Date: Tue, 6 Oct 2026 15:27:47 +0200 Subject: [PATCH 11/13] Expose options for apply_reloc_x86's ifunc_jitter through a RelocOption struct --- example/jitter/resolve_ifunc.py | 8 ++- miasm/analysis/binary.py | 5 +- miasm/jitter/loader/elf.py | 94 ++++++++++++++++++++++----------- 3 files changed, 71 insertions(+), 36 deletions(-) diff --git a/example/jitter/resolve_ifunc.py b/example/jitter/resolve_ifunc.py index 42859fa48..bf9c11098 100644 --- a/example/jitter/resolve_ifunc.py +++ b/example/jitter/resolve_ifunc.py @@ -4,7 +4,7 @@ from miasm.analysis.binary import Container, ContainerELF from miasm.analysis.machine import Machine from miasm.core.locationdb import LocationDB -from miasm.jitter.loader.elf import get_ifuncs, apply_ifunc +from miasm.jitter.loader.elf import RelocOptions, get_ifuncs, apply_ifunc from miasm.loader.elf import ET_EXEC @@ -20,8 +20,10 @@ def prepare(run_ifuncs: bool): myjit.init_stack() base_addr = 0x400000 + reloc_options = RelocOptions(run_ifuncs=True, ifunc_jitter_engine=args.jitter) + with open(args.filename, 'rb') as f: - elf: ContainerELF = Container.from_stream(f, addr=base_addr, loc_db=loc_db, vm=myjit.vm, apply_reloc=True, run_ifuncs=run_ifuncs) + elf: ContainerELF = Container.from_stream(f, addr=base_addr, loc_db=loc_db, vm=myjit.vm, apply_reloc=True, reloc_options=reloc_options) assert isinstance(elf, ContainerELF) if elf.executable.Ehdr.type == ET_EXEC: # static executables can't be rebased @@ -81,6 +83,8 @@ def launch(jitter): # # Note that we could've created a separate jitter to run ifuncs on (5th arg # of apply_ifunc) if we wanted to keep our bss clean for the regular jitter + # We also could've prepared a special jitter to pass in RelocOptions to + # achieve the same goal use_func2_addr = loc_db.get_name_offset("use_func2") if use_func2_addr is None: diff --git a/miasm/analysis/binary.py b/miasm/analysis/binary.py index 54f6f8e29..0b6a2b594 100644 --- a/miasm/analysis/binary.py +++ b/miasm/analysis/binary.py @@ -164,12 +164,13 @@ def parse(self, data, vm=None, **kwargs): class ContainerELF(Container): "Container abstraction for ELF" - def parse(self, data, vm=None, addr=0, apply_reloc=False, run_ifuncs=False, **kwargs): + def parse(self, data, vm=None, addr=0, apply_reloc=False, reloc_options=None, **kwargs): """Load an ELF from @data @data: bytes containing the ELF bytes @vm (optional): VmMngr instance. If set, load the ELF in virtual memory @addr (optional): base address the ELF in virtual memory @apply_reloc (optional): if set, apply relocation during ELF loading + @reloc_options (optional): see miasm.jitter.loader.elf.RelocOptions @addr and @apply_reloc are only meaningful in the context of a non-empty @vm @@ -191,7 +192,7 @@ def parse(self, data, vm=None, addr=0, apply_reloc=False, run_ifuncs=False, **kw loc_db=self.loc_db, base_addr=addr, apply_reloc=apply_reloc, - run_ifuncs=run_ifuncs + reloc_options=reloc_options, ) else: self._executable = elf_init.ELF(data) diff --git a/miasm/jitter/loader/elf.py b/miasm/jitter/loader/elf.py index 31c8d9b81..69b361050 100644 --- a/miasm/jitter/loader/elf.py +++ b/miasm/jitter/loader/elf.py @@ -1,6 +1,7 @@ import struct from collections import defaultdict +from typing import Literal from future.utils import viewitems from miasm.analysis.machine import Machine @@ -167,17 +168,39 @@ def fill_loc_db_with_symbols(elf, loc_db, base_addr=0): else: loc_db.add_location(name=name, offset=vaddr) +class RelocOptions(): + def __init__(self, run_ifuncs: bool = False, ifunc_jitter_engine: Literal["python", "gcc", "llvm"] | None = None, ifunc_jitter = None) -> None: + """@run_ifuncs: whether or not to run, resolve and apply ifuncs + @ifunc_jitter_engine: set to "gcc" by default, unless @ifunc_jitter is used. The engine to use for the default ifunc jitter. + @ifunc_jitter: a user-provided jitter to run/resolve ifuncs with. Needs to have an initialized stack. Overrides the default ifunc jitter. + + WARNING: @ifunc_jitter_engine and @ifunc_jitter are mutually exclusive. + """ + if ifunc_jitter_engine is not None and ifunc_jitter is not None: + raise ValueError("ifunc_jitter_engine and ifunc_jitter are mutually exclusive") + self.run_ifuncs = run_ifuncs + if ifunc_jitter_engine is None: + if ifunc_jitter is None: + self.ifunc_jitter_engine = "gcc" + else: + self.ifunc_jitter_engine = ifunc_jitter_engine + + if ifunc_jitter is not None: + self.ifunc_jitter = ifunc_jitter -def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB | None, run_ifuncs=False): +def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB | None, reloc_options: RelocOptions | None = None): """Apply relocation for x86 ELF contained in the section @section @elf: miasm.loader's ELF instance @vm: VmMngr instance @section: elf's section containing relocation to perform @base_addr: addr to reloc to @loc_db: LocationDB used to retrieve symbols'offset - @run_ifuncs: whether or not to run ifuncs and apply their reloc + @reloc_options: Options for which reloc to process and how to process them """ - if run_ifuncs and elf.Ehdr.type == elf_csts.ET_EXEC: + if reloc_options is None: + reloc_options = RelocOptions() + + if reloc_options.run_ifuncs and elf.Ehdr.type == elf_csts.ET_EXEC: log.warning("Running ifuncs as a part of the loading process is only accurate for dynamically-linked executables, as they are normally ran during glibc initialization for static and static-pie executables. See https://sourceware.org/glibc/manual/latest/html_node/Indirect-Functions.html#When-IFUNC-Resolvers-Run.") log.debug(f"Applying relocations for section {section}") @@ -190,30 +213,33 @@ def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB | None, run_ else: raise ValueError(f"Trying to apply reloc on section without RelTable or RelATable.") - if run_ifuncs: - ifunc_machine = Machine(guess_arch(elf)) - ifunc_jitter = ifunc_machine.jitter(loc_db=loc_db) - - last_addr = 0x100 - stack_base_found = False - ifunc_jitter.stack_size = 0x100 - for map_addr, map_mem in vm.get_all_memory().items(): - map_data = map_mem["data"] - # find somewhere for our stack to go - if map_addr - last_addr > ifunc_jitter.stack_size: - ifunc_jitter.stack_base = last_addr - last_addr = float("inf") - stack_base_found = True - else: - last_addr = map_addr + len(map_data) + if reloc_options.run_ifuncs: + if hasattr(reloc_options, "ifunc_jitter"): + ifunc_jitter = reloc_options.ifunc_jitter + else: + ifunc_machine = Machine(guess_arch(elf)) + ifunc_jitter = ifunc_machine.jitter(loc_db, reloc_options.ifunc_jitter_engine) + + last_addr = 0x100 + stack_base_found = False + ifunc_jitter.stack_size = 0x100 + for map_addr, map_mem in vm.get_all_memory().items(): + map_data = map_mem["data"] + # find somewhere for our stack to go + if map_addr - last_addr > ifunc_jitter.stack_size: + ifunc_jitter.stack_base = last_addr + last_addr = float("inf") + stack_base_found = True + else: + last_addr = map_addr + len(map_data) - # and copy the memory already mapped by the loader - ifunc_jitter.vm.add_memory_page(map_addr, map_mem["access"], map_data) + # and copy the memory already mapped by the loader + ifunc_jitter.vm.add_memory_page(map_addr, map_mem["access"], map_data) - if stack_base_found: - ifunc_jitter.init_stack() - else: - raise ValueError("Couldn't find enough space to allocate our ifunc runner's stack") + if stack_base_found: + ifunc_jitter.init_stack() + else: + raise ValueError("Couldn't find enough space to allocate our ifunc runner's stack") for reloc in table: # Parse relocation info @@ -287,7 +313,7 @@ def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB | None, run_ where = base_addr + r_offset else: raise ValueError(f"Unknown relocation type: {reloc.type} ({reloc})") - if is_ifunc and run_ifuncs: + if is_ifunc and reloc_options.run_ifuncs: addr = _resolve_ifunc(where, addr, elf, ifunc_jitter) log.debug(f"Write {addr:x} at {where:x}") @@ -300,13 +326,16 @@ def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB | None, run_ def vm_load_elf(vm, fdata, name="", base_addr=0, loc_db=None, apply_reloc=False, - run_ifuncs=False, **kargs): + reloc_options=None, **kargs): """ Very dirty elf loader TODO XXX: implement real loader """ - if run_ifuncs and not apply_reloc: - log.warning("vm_load_elf was called with run_ifuncs=True but they won't be run nor applied since apply_reloc=False.") + if reloc_options is None: + reloc_options = RelocOptions() + + if reloc_options.run_ifuncs and not apply_reloc: + log.warning("vm_load_elf was called with reloc_options.run_ifuncs=True but they won't be run nor applied since apply_reloc=False.") elf = elf_init.ELF(fdata, **kargs) i = interval() @@ -359,7 +388,7 @@ def vm_load_elf(vm, fdata, name="", base_addr=0, loc_db=None, apply_reloc=False, sections.append(section) for section in sections: if arch in ["x86_64", "x86_32"]: - apply_reloc_x86(elf, vm, section, base_addr, loc_db, run_ifuncs) + apply_reloc_x86(elf, vm, section, base_addr, loc_db, reloc_options) else: log.debug("Unsupported relocation for arch %r" % arch) @@ -401,7 +430,7 @@ def get_ifuncs(elf, base_addr, with_syms=False): def _resolve_ifunc(reloc_addr, resolver_addr, elf, run_jitter): """ - WARNING: this is only accurate for dynamically-linked binaries. Static and static-pie executables' ifuncs are loaded at runtime during libc initialization. + WARNING: this is only accurate for dynamically-linked binaries. Static and static-pie executables' ifuncs are loaded at runtime during glibc initialization. WARNING: this requires the jitter to have an initialized stack Runs provided ifunc resolver @@ -431,7 +460,8 @@ def _code_sentinelle(j): def apply_ifunc(reloc_addr, resolver_addr, elf, jitter, run_jitter=None): """ - WARNING: this is only accurate for dynamically-linked binaries. Static and static-pie executables' ifuncs are loaded at runtime during libc initialization. + WARNING: this is only accurate for dynamically-linked binaries. Static and static-pie executables' ifuncs are loaded at runtime during glibc initialization. + WARNING: this requires the jitter or run_jitter (if present) to have an initialized stack Runs and applies provided ifunc resolver @reloc_addr: int - the address where we want to apply our reloc From 2d07c11d3a881f0b8da8d6cec9e99a617bd446e1 Mon Sep 17 00:00:00 2001 From: kStor2poche <43518872+kStor2poche@users.noreply.github.com> Date: Tue, 6 Oct 2026 16:45:19 +0200 Subject: [PATCH 12/13] Allow default mapping of ifunc_jitter stack after loaded ELF if in addr range --- miasm/jitter/loader/elf.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/miasm/jitter/loader/elf.py b/miasm/jitter/loader/elf.py index 69b361050..c14601296 100644 --- a/miasm/jitter/loader/elf.py +++ b/miasm/jitter/loader/elf.py @@ -236,6 +236,10 @@ def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB | None, relo # and copy the memory already mapped by the loader ifunc_jitter.vm.add_memory_page(map_addr, map_mem["access"], map_data) + if not stack_base_found and last_addr + ifunc_jitter.stack_size < 1 << elf.size: + ifunc_jitter.stack_base = last_addr + stack_base_found = True + if stack_base_found: ifunc_jitter.init_stack() else: From 545fb4914c95ed045a37696fdc2b6171ae897cdc Mon Sep 17 00:00:00 2001 From: kStor2poche <43518872+kStor2poche@users.noreply.github.com> Date: Wed, 7 Oct 2026 22:05:44 +0200 Subject: [PATCH 13/13] Fix example run_ifuncs and some polish --- example/jitter/resolve_ifunc.py | 14 +++++++------- example/samples/ifunc | Bin 16120 -> 16112 bytes example/samples/ifunc.c | 4 ++-- miasm/analysis/binary.py | 8 ++++---- miasm/jitter/loader/elf.py | 12 ++++++------ 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/example/jitter/resolve_ifunc.py b/example/jitter/resolve_ifunc.py index bf9c11098..a3b9660d0 100644 --- a/example/jitter/resolve_ifunc.py +++ b/example/jitter/resolve_ifunc.py @@ -4,7 +4,7 @@ from miasm.analysis.binary import Container, ContainerELF from miasm.analysis.machine import Machine from miasm.core.locationdb import LocationDB -from miasm.jitter.loader.elf import RelocOptions, get_ifuncs, apply_ifunc +from miasm.jitter.loader.elf import RelocOptions, get_ifuncs_x86, apply_ifunc_x86 from miasm.loader.elf import ET_EXEC @@ -18,10 +18,10 @@ def prepare(run_ifuncs: bool): myjit = Machine("x86_64").jitter(loc_db, args.jitter) myjit.init_stack() - base_addr = 0x400000 - reloc_options = RelocOptions(run_ifuncs=True, ifunc_jitter_engine=args.jitter) + reloc_options = RelocOptions(run_ifuncs=run_ifuncs, ifunc_jitter_engine=args.jitter) + base_addr = 0x400000 with open(args.filename, 'rb') as f: elf: ContainerELF = Container.from_stream(f, addr=base_addr, loc_db=loc_db, vm=myjit.vm, apply_reloc=True, reloc_options=reloc_options) assert isinstance(elf, ContainerELF) @@ -31,13 +31,13 @@ def prepare(run_ifuncs: bool): return (base_addr, loc_db, elf, myjit) -def launch(jitter): +def launch(jitter) -> str: jitter.push_uint64_t(0x1337beef) jitter.add_breakpoint(0x1337beef, code_sentinelle) if args.verbose >= 2: jitter.set_trace_log(True, True) - run_at = jitter.lifter.loc_db.get_name_offset("intermediate") + run_at = jitter.lifter.loc_db.get_name_offset("wrapper") assert run_at is not None jitter.run(run_at) return jitter.get_c_str(jitter.cpu.RAX) @@ -96,9 +96,9 @@ def launch(jitter): log.info(f"Set symbol use_func2 (@0x{use_func2_addr + base_addr:x}) to 1") # we can then run our ifunc resolver - ifunc_resolvers = get_ifuncs(elf.executable, base_addr, with_syms=False) + ifunc_resolvers = get_ifuncs_x86(elf.executable, base_addr, with_syms=False) for reloc, ifunc_resolver in ifunc_resolvers: - apply_ifunc(reloc, ifunc_resolver, elf.executable, jitter) + apply_ifunc_x86(reloc, ifunc_resolver, elf.executable, jitter) res2 = launch(jitter) log.info(f"Forcing ifunc resolving to func2 returned {res2}.") diff --git a/example/samples/ifunc b/example/samples/ifunc index 50cddb390535d26e8b9283bbe14a46d57026bf85..939ef890c17ab5fc5b232f8a9d0666a57bfdd6fc 100755 GIT binary patch delta 264 zcmexS`=NG%2BX17&3tAN4#nV;M|02SzMph{#(vd~I-zN-`J2};&(UK{nCxj}GI^?z zuw*zR1A`C)4}+T^0~qi^D2BMn2aUuTvnLw@$*Rd8fn@t+PGfz;X;AegQ1#qU_49z@ z0t`|N3Jy>KD3vlf&{$k@F;rFpO?Lg{Mxg8#sF{{%ZrD9}qp>*Saj+X6PJU=C&h&?I s^DpCc#>si6uI%MSi3J6zMVmL79%kHJVXnZ=$TWGTojIe>i_@% delta 287 zcmexR`=fS(2BXDB&3tB&S-DZSFZ%C`d6Oq%c6>>x?vwsG7dNkAo}HyX_JeU9g diff --git a/example/samples/ifunc.c b/example/samples/ifunc.c index 74aa43699..f942c8936 100644 --- a/example/samples/ifunc.c +++ b/example/samples/ifunc.c @@ -24,11 +24,11 @@ static func_type resolve_func() { return func_1; } -char *intermediate() { +char *wrapper() { return func(); } int main() { - intermediate(); + wrapper(); return 0; } diff --git a/miasm/analysis/binary.py b/miasm/analysis/binary.py index 0b6a2b594..8abd0885f 100644 --- a/miasm/analysis/binary.py +++ b/miasm/analysis/binary.py @@ -172,8 +172,8 @@ def parse(self, data, vm=None, addr=0, apply_reloc=False, reloc_options=None, ** @apply_reloc (optional): if set, apply relocation during ELF loading @reloc_options (optional): see miasm.jitter.loader.elf.RelocOptions - @addr and @apply_reloc are only meaningful in the context of a - non-empty @vm + @addr, @apply_reloc and @reloc_options are only meaningful in the context + of a non-empty @vm """ from miasm.jitter.loader.elf import vm_load_elf, guess_arch, \ fill_loc_db_with_symbols @@ -205,9 +205,9 @@ def parse(self, data, vm=None, addr=0, apply_reloc=False, reloc_options=None, ** # Build the bin_stream instance and set the entry point try: self._bin_stream = bin_stream_elf(self._executable) - if self._executable.Ehdr.type == ET_EXEC: + if addr != 0 and self._executable.Ehdr.type == ET_EXEC: addr = 0 - log.warning("This elf has Ehdr.type == ET_EXEC meaning it isn't relocatable. Using 0 as base_addr.") + log.warning("This elf has Ehdr.type == ET_EXEC meaning it isn't relocatable. Using 0 as the base address.") self._entry_point = self._executable.Ehdr.entry + addr except Exception as error: raise ContainerParsingException('Cannot read ELF: %s' % error) diff --git a/miasm/jitter/loader/elf.py b/miasm/jitter/loader/elf.py index c14601296..79e654f57 100644 --- a/miasm/jitter/loader/elf.py +++ b/miasm/jitter/loader/elf.py @@ -201,7 +201,7 @@ def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB | None, relo reloc_options = RelocOptions() if reloc_options.run_ifuncs and elf.Ehdr.type == elf_csts.ET_EXEC: - log.warning("Running ifuncs as a part of the loading process is only accurate for dynamically-linked executables, as they are normally ran during glibc initialization for static and static-pie executables. See https://sourceware.org/glibc/manual/latest/html_node/Indirect-Functions.html#When-IFUNC-Resolvers-Run.") + log.warning("Running ifuncs as a part of the loading process is only accurate for dynamically-linked executables, as they are normally ran during glibc initialization for static and static-pie executables. See GNU C Library Reference Manual, version 2.44, chapter/section 37.4.2 When IFUNC Resolvers Run.") log.debug(f"Applying relocations for section {section}") @@ -318,7 +318,7 @@ def apply_reloc_x86(elf, vm, section, base_addr, loc_db: LocationDB | None, relo else: raise ValueError(f"Unknown relocation type: {reloc.type} ({reloc})") if is_ifunc and reloc_options.run_ifuncs: - addr = _resolve_ifunc(where, addr, elf, ifunc_jitter) + addr = _resolve_ifunc_x86(addr, elf, ifunc_jitter) log.debug(f"Write {addr:x} at {where:x}") if elf.size == 64: @@ -398,7 +398,7 @@ def vm_load_elf(vm, fdata, name="", base_addr=0, loc_db=None, apply_reloc=False, return elf -def get_ifuncs(elf, base_addr, with_syms=False): +def get_ifuncs_x86(elf, base_addr, with_syms=False): """ Returns all ifunc resolvers found in @elf along with their GOT entry and their associated symbols if they exist and @with_syms == True @@ -432,7 +432,7 @@ def get_ifuncs(elf, base_addr, with_syms=False): res.append((to_reloc, resolver)) return res -def _resolve_ifunc(reloc_addr, resolver_addr, elf, run_jitter): +def _resolve_ifunc_x86(resolver_addr, elf, run_jitter): """ WARNING: this is only accurate for dynamically-linked binaries. Static and static-pie executables' ifuncs are loaded at runtime during glibc initialization. WARNING: this requires the jitter to have an initialized stack @@ -462,7 +462,7 @@ def _code_sentinelle(j): resolved_funcaddr = getattr(run_jitter.cpu, "RAX" if elf.size == 64 else "EAX") return resolved_funcaddr -def apply_ifunc(reloc_addr, resolver_addr, elf, jitter, run_jitter=None): +def apply_ifunc_x86(reloc_addr, resolver_addr, elf, jitter, run_jitter=None): """ WARNING: this is only accurate for dynamically-linked binaries. Static and static-pie executables' ifuncs are loaded at runtime during glibc initialization. WARNING: this requires the jitter or run_jitter (if present) to have an initialized stack @@ -478,7 +478,7 @@ def apply_ifunc(reloc_addr, resolver_addr, elf, jitter, run_jitter=None): if run_jitter is None: run_jitter = jitter - resolved_funcaddr = _resolve_ifunc(reloc_addr, resolver_addr, elf, run_jitter) + resolved_funcaddr = _resolve_ifunc_x86(resolver_addr, elf, run_jitter) log.debug(f"Write {resolved_funcaddr:x} at {reloc_addr:x}") if elf.size == 64: