mirror of
https://github.com/lief-project/LIEF.git
synced 2026-10-12 02:17:46 +00:00
196 lines
5.7 KiB
Python
Executable File
196 lines
5.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import ctypes
|
|
import sys
|
|
from itertools import islice
|
|
|
|
import lief
|
|
|
|
|
|
def host_info():
|
|
print("Host")
|
|
# lief-doc: host-start
|
|
print(f" Hostname: {lief.runtime.Host.name}")
|
|
print(f" Home: {lief.runtime.Host.home_dir}")
|
|
print(f" cache: {lief.runtime.Host.cache_dir}")
|
|
print(f" temp: {lief.runtime.Host.tmp_dir}")
|
|
print(f" config: {lief.runtime.Host.config_dir}")
|
|
# lief-doc: host-end
|
|
|
|
# lief-doc: host-android-start
|
|
if (sdk := lief.runtime.android.Host.sdk_version) is not None:
|
|
print(f" SDK: {sdk}")
|
|
# lief-doc: host-android-end
|
|
print()
|
|
|
|
|
|
def process_info():
|
|
print("Process")
|
|
# lief-doc: process-start
|
|
print(f" PID: {lief.runtime.Process.pid}")
|
|
print(f" TID: {lief.runtime.Process.tid}")
|
|
print(f" Page size: {lief.runtime.Process.page_size:#06x}")
|
|
print(f" Arch: {lief.runtime.Process.arch}")
|
|
print(f" Platform: {lief.runtime.Process.platform}")
|
|
# lief-doc: process-end
|
|
|
|
# lief-doc: process-android-start
|
|
# Access a named property
|
|
sdk = lief.runtime.android.Process.get_system_property("ro.build.version.sdk")
|
|
if sdk is not None:
|
|
print(f" {sdk.name}: {sdk.value} (serial: {sdk.serial})")
|
|
|
|
# Iterate over all properties
|
|
for prop in lief.runtime.android.Process.properties:
|
|
print(f" {prop.name}: {prop.value} (serial: {prop.serial})")
|
|
# lief-doc: process-android-end
|
|
|
|
|
|
def modules_info():
|
|
print("Module API")
|
|
libc: lief.runtime.android.Module | None = None
|
|
# lief-doc: modules-start
|
|
for mod in lief.runtime.modules():
|
|
print(mod)
|
|
# Look for the Bionic libc
|
|
if mod.name.endswith("libc.so"):
|
|
print(
|
|
f"libc.so found: {mod.path} ([{mod.imagebase:#010x}, {mod.end:#010x}])"
|
|
)
|
|
assert isinstance(mod, lief.runtime.android.Module)
|
|
libc = mod
|
|
# lief-doc: modules-end
|
|
|
|
if libc is None:
|
|
return
|
|
|
|
# lief-doc: modules-android-start
|
|
print(f"libc path: {libc.path}")
|
|
if __cxa_finalize := libc.dlsym("__cxa_finalize"):
|
|
__cxa_finalize_addr = lief.to_int(__cxa_finalize)
|
|
print(f"{libc.name}!__cxa_finalize: {__cxa_finalize_addr:#010x}")
|
|
|
|
if malloc := libc.dlsym("malloc"):
|
|
malloc_addr = lief.to_int(malloc)
|
|
print(f"{libc.name}!malloc: {malloc_addr:#010x}")
|
|
|
|
# With LIEF extended, we can disassemble the function from its
|
|
# absolute memory address:
|
|
if lief.__extended__:
|
|
for inst in islice(lief.runtime.disassemble(malloc_addr), 4):
|
|
print(" ", inst)
|
|
|
|
if (elf_on_disk := libc.parse_from_path()) is not None and (
|
|
__cxa_finalize := elf_on_disk.get_symbol("__cxa_finalize")
|
|
) is not None:
|
|
print(f"__cxa_finalize: {__cxa_finalize.value:#010x}")
|
|
|
|
if liblog := lief.runtime.android.dlopen("liblog.so"):
|
|
print(
|
|
f"liblog loaded: {liblog.path} "
|
|
f"(dlopen handler={lief.to_int(liblog.handle):#010x})"
|
|
)
|
|
|
|
if log_print := liblog.dlsym("__android_log_print"):
|
|
log_print_addr = lief.to_int(log_print)
|
|
print(f"{liblog.name}!__android_log_print: {log_print_addr:#010x}")
|
|
# lief-doc: modules-android-end
|
|
|
|
|
|
def memory_example():
|
|
print("Memory")
|
|
# lief-doc: memory-start
|
|
chunk = lief.runtime.Memory.mmap(
|
|
lief.runtime.Process.page_size,
|
|
lief.runtime.Memory.ANONYMOUS | lief.runtime.Memory.PRIVATE,
|
|
lief.runtime.Memory.READ | lief.runtime.Memory.WRITE | lief.runtime.Memory.EXEC,
|
|
)
|
|
|
|
class Config(lief.assembly.AssemblerConfig):
|
|
def resolve_symbol(self, name: str) -> int | None:
|
|
libc = lief.runtime.android.dlopen("libc.so")
|
|
if libc is None:
|
|
print("Failed to dlopen libc.so", file=sys.stderr)
|
|
return None
|
|
|
|
if name == "write":
|
|
return lief.to_int(libc.dlsym("write"))
|
|
return None
|
|
|
|
config = Config()
|
|
|
|
raw_inst = lief.runtime.assemble(
|
|
chunk.addr,
|
|
r"""
|
|
.text
|
|
.global main
|
|
.align 2
|
|
|
|
main:
|
|
stp x29, x30, [sp, -16]!
|
|
mov x29, sp
|
|
|
|
ldr x8, =write
|
|
|
|
mov x0, 1 // STDOUT_FILENO
|
|
adr x1, msg // buf = &msg
|
|
mov x2, 27 // len = len("LIEF Runtime Extended Demo\n")
|
|
blr x8 // write(STDOUT_FILENO, msg, 27)
|
|
|
|
ldp x29, x30, [sp], 16
|
|
ret
|
|
|
|
msg:
|
|
.ascii "LIEF Runtime Extended Demo\n"
|
|
""",
|
|
config,
|
|
)
|
|
|
|
# Disassemble the JITed stub from memory
|
|
for inst in islice(lief.runtime.disassemble(chunk.addr), 9):
|
|
print(inst)
|
|
|
|
# Dump as raw bytes
|
|
raw_bytes = lief.runtime.Memory.read(chunk.addr, len(raw_inst))
|
|
print(raw_bytes.hex(":"))
|
|
|
|
# Flush the instruction cache before executing the freshly written code
|
|
# (this is required on AArch64).
|
|
chunk.cache_flush()
|
|
|
|
# Change the permission to R-X
|
|
chunk.make_rx()
|
|
|
|
VoidVoidFunc = ctypes.CFUNCTYPE(None) # void(*)()
|
|
hello_jit = VoidVoidFunc(chunk.addr)
|
|
hello_jit()
|
|
|
|
# Change the message
|
|
pos = raw_bytes.find(b"LIEF Runtime")
|
|
chunk.make_rw()
|
|
lief.runtime.Memory.write(b"Hello Python ctypes World!\n", chunk.addr + pos)
|
|
chunk.make_rx()
|
|
chunk.cache_flush()
|
|
|
|
# Print again
|
|
hello_jit()
|
|
|
|
# Don't miss good practices!
|
|
chunk.deallocate()
|
|
# lief-doc: memory-end
|
|
|
|
|
|
def main() -> int:
|
|
if not lief.runtime.enabled:
|
|
print("Error: LIEF's runtime is not enabled", file=sys.stderr)
|
|
return 0
|
|
|
|
host_info()
|
|
process_info()
|
|
modules_info()
|
|
memory_example()
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|