chainload.py: Cleanup, move Mach-O loader to macho.py

Signed-off-by: Hector Martin <marcan@marcan.st>
This commit is contained in:
Hector Martin 2021-05-01 15:15:43 +09:00
parent a489a646bd
commit 7c2dace0b0
4 changed files with 110 additions and 83 deletions

View file

@ -4,70 +4,27 @@ import argparse, pathlib
parser = argparse.ArgumentParser(description='Mach-O loader for m1n1')
parser.add_argument('payload', type=pathlib.Path)
parser.add_argument('-1', '--el1', action="store_true")
parser.add_argument('-s', '--sepfw', action="store_true")
args = parser.parse_args()
from setup import *
from tgtypes import *
from tgtypes import BootArgs
from macho import MachO
import adt
import asm
payload = args.payload.read_bytes()
macho = MachO(args.payload.read_bytes())
obj = MachO.parse(payload)
vmin, vmax = (1 << 64), 0
entry = None
for cmd in obj.cmds:
if cmd.cmd == LoadCmdType.SEGMENT_64:
vmin = min(vmin, cmd.args.vmaddr)
vmax = max(vmax, cmd.args.vmaddr + cmd.args.vmsize)
elif cmd.cmd == LoadCmdType.UNIXTHREAD:
entry = cmd.args[0].data.pc
memory_size = vmax - vmin
image = bytearray(memory_size)
image = macho.prepare_image()
new_base = u.base
def align(v, a=16384):
return (v + a - 1) & ~(a - 1)
for cmdi, cmd in enumerate(obj.cmds):
is_m1n1 = None
if cmd.cmd == LoadCmdType.SEGMENT_64:
if is_m1n1 is None:
is_m1n1 = cmd.args.segname == "_HDR"
dest = cmd.args.vmaddr - vmin
end = min(len(payload), cmd.args.fileoff + cmd.args.filesize)
size = end - cmd.args.fileoff
print(f"LOAD: {cmd.args.segname} {size} bytes from {cmd.args.fileoff:x} to {dest + new_base:x}")
image[dest:dest + size] = payload[cmd.args.fileoff:end]
if cmd.args.vmsize > size:
clearsize = cmd.args.vmsize - size
if cmd.args.segname == "PYLD":
print("SKIP: %d bytes from 0x%x to 0x%x" % (clearsize, dest + new_base + size, dest + new_base + size + clearsize))
memory_size -= clearsize - 4 # leave a payload end marker
image = image[:memory_size]
else:
print("ZERO: %d bytes from 0x%x to 0x%x" % (clearsize, dest + new_base + size, dest + new_base + size + clearsize))
image[dest + size:dest + cmd.args.vmsize] = bytes(clearsize)
entry -= vmin
entry = macho.entry
entry -= macho.vmin
entry += new_base
if args.sepfw:
adt_base = u.ba.devtree - u.ba.virt_base + u.ba.phys_base
adt_size = u.ba.devtree_size
print(f"Fetching ADT ({adt_size} bytes)...")
adt = adt.load_adt(iface.readmem(adt_base, u.ba.devtree_size))
sepfw_start, sepfw_length = adt["chosen"]["memory-map"].SEPFW
sepfw_start, sepfw_length = u.adt["chosen"]["memory-map"].SEPFW
else:
sepfw_start, sepfw_length = 0, 0
@ -90,9 +47,12 @@ if args.sepfw:
print(f"Setting up bootargs...")
tba = u.ba.copy()
#tba.phys_base = new_base
#tba.virt_base = 0xfffffe0010000000 + new_base & (32 * 1024 * 1024 - 1)
tba.top_of_kdata = new_base + image_size
if args.sepfw:
tba.top_of_kernel_data = new_base + image_size
else:
# SEP firmware is in here somewhere, keep top_of_kdata high so we hopefully don't clobber it
tba.top_of_kernel_data = max(tba.top_of_kernel_data, new_base + image_size)
iface.writemem(image_addr + bootargs_off, BootArgs.build(tba))
@ -116,18 +76,10 @@ iface.writemem(stub.addr, stub.data)
p.dc_cvau(stub.addr, stub.len)
p.ic_ivau(stub.addr, stub.len)
if args.el1:
print("Setting up EL1 config")
# Enable physical timer for EL1
u.msr(CNTHCTL_EL2, 3 << 10) # EL1PTEN | EL1PCTEN
# Unredirect IRQs/FIQs
u.msr(HCR_EL2, u.mrs(HCR_EL2) & ~(3 << 3)) # ~(IMO | FMO)
print(f"Entry point: 0x{entry:x}")
print(f"Jumping to stub at 0x{stub.addr:x}")
p.reboot(stub.addr, new_base + bootargs_off, image_addr, new_base, image_size, el1=args.el1)
p.reboot(stub.addr, new_base + bootargs_off, image_addr, new_base, image_size)
iface.nop()
print("Proxy is alive again")

47
proxyclient/macho.py Normal file
View file

@ -0,0 +1,47 @@
#!/usr/bin/env python3
from tgtypes import *
from utils import *
class MachO:
def __init__(self, data):
self.data = data
self.obj = MachOFile.parse(data)
self.load_info()
def load_info(self):
self.vmin, self.vmax = (1 << 64), 0
self.entry = None
for cmd in self.obj.cmds:
if cmd.cmd == MachOLoadCmdType.SEGMENT_64:
self.vmin = min(self.vmin, cmd.args.vmaddr)
self.vmax = max(self.vmax, cmd.args.vmaddr + cmd.args.vmsize)
elif cmd.cmd == MachOLoadCmdType.UNIXTHREAD:
self.entry = cmd.args[0].data.pc
def prepare_image(self):
memory_size = self.vmax - self.vmin
image = bytearray(memory_size)
for cmdi, cmd in enumerate(self.obj.cmds):
is_m1n1 = None
if cmd.cmd == MachOLoadCmdType.SEGMENT_64:
if is_m1n1 is None:
is_m1n1 = cmd.args.segname == "_HDR"
dest = cmd.args.vmaddr - self.vmin
end = min(len(self.data), cmd.args.fileoff + cmd.args.filesize)
size = end - cmd.args.fileoff
print(f"LOAD: {cmd.args.segname} {size} bytes from {cmd.args.fileoff:x} to {dest:x}")
image[dest:dest + size] = self.data[cmd.args.fileoff:end]
if cmd.args.vmsize > size:
clearsize = cmd.args.vmsize - size
if cmd.args.segname == "PYLD":
print("SKIP: %d bytes from 0x%x to 0x%x" % (clearsize, dest + size, dest + size + clearsize))
memory_size -= clearsize - 4 # leave a payload end marker
image = image[:memory_size]
else:
print("ZERO: %d bytes from 0x%x to 0x%x" % (clearsize, dest + size, dest + size + clearsize))
image[dest + size:dest + cmd.args.vmsize] = bytes(clearsize)
return image

View file

@ -1,7 +1,7 @@
import serial, os, struct, sys, time, json, os.path, lzma
import serial, os, struct, sys, time, json, os.path, lzma, functools
from proxy import *
from tgtypes import *
import malloc
import malloc, adt
def load_registers():
data = json.load(open(os.path.join(os.path.dirname(__file__), "..", "tools", "arm_regs.json")))
@ -42,6 +42,9 @@ class ProxyUtils(object):
self.code_buffer = self.malloc(0x10000)
self.adt_data = None
self.adt = LazyADT(self)
def mrs(self, reg, silent=False, call=None):
if call is None:
call = self.proxy.call
@ -115,6 +118,31 @@ class ProxyUtils(object):
assert decompressed_size == len(data)
def get_adt(self):
if self.adt_data is not None:
return self.adt_data
adt_base = self.ba.devtree - self.ba.virt_base + self.ba.phys_base
adt_size = self.ba.devtree_size
print(f"Fetching ADT ({adt_size} bytes)...")
self.adt_data = self.iface.readmem(adt_base, self.ba.devtree_size)
return self.adt_data
class LazyADT:
def __init__(self, utils):
self.utils = utils
@functools.cached_property
def _adt(self):
return adt.load_adt(self.utils.get_adt())
def __getitem__(self, item):
return self._adt[item]
def __getattr__(self, attr):
return getattr(self._adt, item)
def __str__(self, t=""):
return gstr(self._adt)
def __iter__(self):
return iter(self._adt)
class RegMonitor(object):
def __init__(self, utils):
self.utils = utils

View file

@ -27,7 +27,7 @@ BootArgs = Struct(
"mem_size_actual" / Hex(Int64ul),
)
LoadCmdType = "LoadCmdType" / Enum(Int32ul,
MachOLoadCmdType = "LoadCmdType" / Enum(Int32ul,
UNIXTHREAD = 0x05,
SEGMENT_64 = 0x19,
UUID = 0x1b,
@ -36,11 +36,11 @@ LoadCmdType = "LoadCmdType" / Enum(Int32ul,
FILESET_ENTRY = 0x80000035,
)
ArmThreadStateFlavor = "ThreadStateFlavor" / Enum(Int32ul,
MachOArmThreadStateFlavor = "ThreadStateFlavor" / Enum(Int32ul,
THREAD64 = 6,
)
MachHeader = Struct(
MachOHeader = Struct(
"magic" / Hex(Int32ul),
"cputype" / Hex(Int32ul),
"cpusubtype" / Hex(Int32ul),
@ -51,16 +51,16 @@ MachHeader = Struct(
"reserved" / Hex(Int32ul),
)
VmProt = FlagsEnum(Int32sl,
MachOVmProt = FlagsEnum(Int32sl,
PROT_READ = 0x01,
PROT_WRITE = 0x02,
PROT_EXECUTE = 0x04,
)
CmdUnixThread = GreedyRange(Struct(
"flavor" / ArmThreadStateFlavor,
MachOCmdUnixThread = GreedyRange(Struct(
"flavor" / MachOArmThreadStateFlavor,
"data" / Prefixed(ExprAdapter(Int32ul, obj_ * 4, obj_ / 4), Switch(this.flavor, {
ArmThreadStateFlavor.THREAD64: Struct(
MachOArmThreadStateFlavor.THREAD64: Struct(
"x" / Array(29, Hex(Int64ul)),
"fp" / Hex(Int64ul),
"lr" / Hex(Int64ul),
@ -73,14 +73,14 @@ CmdUnixThread = GreedyRange(Struct(
))
CmdSegment64 = Struct(
MachOCmdSegment64 = Struct(
"segname" / PaddedString(16, "ascii"),
"vmaddr" / Hex(Int64ul),
"vmsize" / Hex(Int64ul),
"fileoff" / Hex(Int64ul),
"filesize" / Hex(Int64ul),
"maxprot" / VmProt,
"initprot" / VmProt,
"maxprot" / MachOVmProt,
"initprot" / MachOVmProt,
"nsects" / Int32ul,
"flags" / Hex(Int32ul),
"sections" / GreedyRange(Struct(
@ -99,17 +99,17 @@ CmdSegment64 = Struct(
)),
)
Cmd = Struct(
"cmd" / Hex(LoadCmdType),
MachOCmd = Struct(
"cmd" / Hex(MachOLoadCmdType),
"args" / Prefixed(ExprAdapter(Int32ul, obj_ - 8, obj_ + 8), Switch(this.cmd, {
LoadCmdType.UNIXTHREAD: CmdUnixThread,
LoadCmdType.SEGMENT_64: CmdSegment64,
LoadCmdType.UUID: Hex(Bytes(16)),
MachOLoadCmdType.UNIXTHREAD: MachOCmdUnixThread,
MachOLoadCmdType.SEGMENT_64: MachOCmdSegment64,
MachOLoadCmdType.UUID: Hex(Bytes(16)),
}, default=GreedyBytes)),
)
MachO = Struct(
"header" / MachHeader,
"cmds" / Array(this.header.ncmds, Cmd),
MachOFile = Struct(
"header" / MachOHeader,
"cmds" / Array(this.header.ncmds, MachOCmd),
"extradata" / GreedyBytes,
)