Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions example/jitter/resolve_ifunc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
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 RelocOptions, get_ifuncs_x86, apply_ifunc_x86
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()

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)
if elf.executable.Ehdr.type == ET_EXEC:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe clearer in the example if regrouped with the original definition of the base address ? (line 21)

Maybe it can be passed as an argument and put the if statement in the main ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm good with the first change but I'm not sure about the second since it would lead to unnecessarily duplicated code

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair

# static executables can't be rebased
base_addr = 0

return (base_addr, loc_db, elf, myjit)

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("wrapper")
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. `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')",
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`
#
# 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:
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_x86(elf.executable, base_addr, with_syms=False)
for reloc, ifunc_resolver in ifunc_resolvers:
apply_ifunc_x86(reloc, ifunc_resolver, elf.executable, jitter)

res2 = launch(jitter)
log.info(f"Forcing ifunc resolving to func2 returned {res2}.")
assert res2 == "bloups"
Binary file added example/samples/ifunc
Binary file not shown.
34 changes: 34 additions & 0 deletions example/samples/ifunc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Compile with gcc -fno-stack-protector -o ifunc ifunc.c

#include <stdbool.h>
#include <stdint.h>

const char *func_1() {
return "zglorg";
}
const char *func_2() {
return "bloups";
}

static bool use_func2;

char *func() __attribute__((ifunc("resolve_func")));

// Function pointer type matching `func` prototype
typedef const char *(*func_type)();

static func_type resolve_func() {
if (use_func2) {
return func_2;
}
return func_1;
}

char *wrapper() {
return func();
}

int main() {
wrapper();
return 0;
}
14 changes: 10 additions & 4 deletions miasm/analysis/binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -163,15 +164,16 @@ 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, 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
@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
Expand All @@ -189,7 +191,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,
reloc_options=reloc_options,
)
else:
self._executable = elf_init.ELF(data)
Expand All @@ -202,6 +205,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 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 the base address.")
self._entry_point = self._executable.Ehdr.entry + addr
except Exception as error:
raise ContainerParsingException('Cannot read ELF: %s' % error)
Expand Down
Loading
Loading