mirror of
https://github.com/AsahiLinux/m1n1
synced 2024-11-26 00:10:18 +00:00
hv.py: Now gets pretty far into XNU boot
Next up: mmiotrace Signed-off-by: Hector Martin <marcan@marcan.st>
This commit is contained in:
parent
9efa91bf87
commit
aebda5213a
2 changed files with 427 additions and 27 deletions
|
@ -1,16 +1,20 @@
|
|||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import sys
|
||||
import sys, traceback, struct, array, bisect
|
||||
|
||||
from asm import ARMAsm
|
||||
from tgtypes import *
|
||||
from proxy import IODEV, START, EXC, EXC_RET, ExcInfo
|
||||
from utils import *
|
||||
from sysreg import *
|
||||
from macho import MachO
|
||||
from adt import load_adt
|
||||
import xnutools
|
||||
import shell
|
||||
|
||||
PAC_MASK = 0xfffff00000000000
|
||||
|
||||
class HV:
|
||||
PTE_VALID = 1 << 0
|
||||
|
||||
|
@ -23,10 +27,38 @@ class HV:
|
|||
SPTE_MAP = 0 << 50
|
||||
SPTE_HOOK = 1 << 50
|
||||
|
||||
MSR_REDIRECTS = {
|
||||
SCTLR_EL1: SCTLR_EL12,
|
||||
TTBR0_EL1: TTBR0_EL12,
|
||||
TTBR1_EL1: TTBR1_EL12,
|
||||
TCR_EL1: TCR_EL12,
|
||||
ESR_EL1: ESR_EL12,
|
||||
FAR_EL1: FAR_EL12,
|
||||
AFSR0_EL1: AFSR0_EL12,
|
||||
AFSR1_EL1: AFSR1_EL12,
|
||||
MAIR_EL1: MAIR_EL12,
|
||||
AMAIR_EL1: AMAIR_EL12,
|
||||
CONTEXTIDR_EL1: CONTEXTIDR_EL12,
|
||||
ACTLR_EL1: ACTLR_EL12,
|
||||
AMX_CTL_EL1: AMX_CTL_EL12,
|
||||
SPRR_CONFIG_EL1: SPRR_CONFIG_EL12,
|
||||
SPRR_PERM_EL1: SPRR_PERM_EL12,
|
||||
SPRR_PERM_EL0: SPRR_PERM_EL02,
|
||||
SPRR_UNK1_EL1: SPRR_UNK1_EL12,
|
||||
SPRR_UNK2_EL1: SPRR_UNK2_EL12,
|
||||
}
|
||||
|
||||
def __init__(self, iface, proxy, utils):
|
||||
self.iface = iface
|
||||
self.p = proxy
|
||||
self.u = utils
|
||||
self.vbar_el1 = None
|
||||
self.want_vbar = None
|
||||
self.vectors = [None]
|
||||
self.step = False
|
||||
self.sym_offset = 0
|
||||
self.symbols = []
|
||||
self.sysreg = {}
|
||||
|
||||
def unmap(self, ipa, size):
|
||||
assert self.p.hv_map(ipa, 0, size, 0) >= 0
|
||||
|
@ -37,29 +69,232 @@ class HV:
|
|||
def map_sw(self, ipa, pa, size):
|
||||
assert self.p.hv_map(ipa, pa | self.SPTE_MAP, size, 1) >= 0
|
||||
|
||||
def addr(self, addr):
|
||||
unslid_addr = addr + self.sym_offset
|
||||
if addr < self.tba.virt_base or unslid_addr < self.macho.vmin:
|
||||
return f"0x{addr:x}"
|
||||
|
||||
saddr, name = self.sym(addr)
|
||||
|
||||
if name is None:
|
||||
return f"0x{addr:x} (0x{unslid_addr:x})"
|
||||
|
||||
return f"0x{addr:x} ({name}+0x{unslid_addr - saddr:x})"
|
||||
|
||||
|
||||
def sym(self, addr):
|
||||
unslid_addr = addr + self.sym_offset
|
||||
|
||||
if addr < self.tba.virt_base or unslid_addr < self.macho.vmin:
|
||||
return None, None
|
||||
|
||||
idx = bisect.bisect_left(self.symbols, (unslid_addr + 1, "")) - 1
|
||||
if idx < 0 or idx >= len(self.symbols):
|
||||
return f"0x{addr:x} (0x{unslid_addr:x})"
|
||||
|
||||
return self.symbols[idx]
|
||||
|
||||
def handle_msr(self, ctx, iss=None):
|
||||
if iss is None:
|
||||
iss = ctx.esr.ISS
|
||||
iss = ESR_ISS_MSR(iss)
|
||||
enc = iss.Op0, iss.Op1, iss.CRn, iss.CRm, iss.Op2
|
||||
|
||||
if enc in sysreg_rev:
|
||||
name = sysreg_rev[enc]
|
||||
else:
|
||||
name = f"s{iss.Op0}_{iss.Op1}_c{iss.CRn}_c{iss.CRm}_{iss.Op2}"
|
||||
|
||||
skip = set()
|
||||
shadow = {
|
||||
#SPRR_CONFIG_EL1,
|
||||
#SPRR_PERM_EL0,
|
||||
#SPRR_PERM_EL1,
|
||||
VMSA_LOCK_EL1,
|
||||
#SPRR_UNK1_EL1,
|
||||
#SPRR_UNK2_EL1,
|
||||
}
|
||||
ro = {
|
||||
ACC_CFG_EL1,
|
||||
CYC_OVRD_EL1,
|
||||
ACC_OVRD_EL1,
|
||||
}
|
||||
value = 0
|
||||
if enc in shadow:
|
||||
if iss.DIR == MSR_DIR.READ:
|
||||
value = self.sysreg.setdefault(enc, 0)
|
||||
print(f"Shadow: mrs x{iss.Rt}, {name} = {value:x}")
|
||||
if iss.Rt != 31:
|
||||
ctx.regs[iss.Rt] = value
|
||||
else:
|
||||
if iss.Rt != 31:
|
||||
value = ctx.regs[iss.Rt]
|
||||
print(f"Shadow: msr {name}, x{iss.Rt} = {value:x}")
|
||||
self.sysreg[enc] = value
|
||||
elif enc in skip or (enc in ro and iss.DIR == MSR_DIR.WRITE):
|
||||
if iss.DIR == MSR_DIR.READ:
|
||||
print(f"Skip: mrs x{iss.Rt}, {name} = 0")
|
||||
if iss.Rt != 31:
|
||||
ctx.regs[iss.Rt] = 0
|
||||
else:
|
||||
value = ctx.regs[iss.Rt]
|
||||
print(f"Skip: msr {name}, x{iss.Rt} = {value:x}")
|
||||
else:
|
||||
if iss.DIR == MSR_DIR.READ:
|
||||
print(f"Pass: mrs x{iss.Rt}, {name}", end=" ")
|
||||
sys.stdout.flush()
|
||||
value = self.u.mrs(self.MSR_REDIRECTS.get(enc, enc))
|
||||
print(f"= {value:x}")
|
||||
if iss.Rt != 31:
|
||||
ctx.regs[iss.Rt] = value
|
||||
else:
|
||||
if iss.Rt != 31:
|
||||
value = ctx.regs[iss.Rt]
|
||||
print(f"Pass: msr {name}, x{iss.Rt} = {value:x}", end=" ")
|
||||
sys.stdout.flush()
|
||||
self.u.msr(self.MSR_REDIRECTS.get(enc, enc), value, call=self.p.gl2_call)
|
||||
print("(OK)")
|
||||
|
||||
ctx.elr += 4
|
||||
|
||||
self.patch_exception_handling()
|
||||
|
||||
return True
|
||||
|
||||
def handle_impdef(self, ctx):
|
||||
if ctx.esr.ISS == 0x20:
|
||||
return self.handle_msr(ctx, self.u.mrs(AFSR1_EL1))
|
||||
|
||||
start = ctx.elr_phys
|
||||
code = struct.unpack("<I", self.iface.readmem(ctx.elr_phys, 4))
|
||||
c = ARMAsm(".inst " + ",".join(str(i) for i in code), ctx.elr_phys)
|
||||
insn = "; ".join(c.disassemble())
|
||||
|
||||
print(f"IMPDEF exception on: {insn}")
|
||||
|
||||
return False
|
||||
|
||||
def handle_hvc(self, ctx):
|
||||
idx = ctx.esr.ISS
|
||||
if idx == 0:
|
||||
return False
|
||||
|
||||
vector, target = self.vectors[idx]
|
||||
if target is None:
|
||||
print(f"EL1: Exception #{vector} with no target")
|
||||
target = 0
|
||||
ok = False
|
||||
else:
|
||||
ctx.elr = target
|
||||
ctx.elr_phys = self.p.hv_translate(target, False, False)
|
||||
ok = True
|
||||
|
||||
if (vector & 3) == EXC.SYNC:
|
||||
spsr = SPSR(self.u.mrs(SPSR_EL12))
|
||||
esr = ESR(self.u.mrs(ESR_EL12))
|
||||
elr = self.u.mrs(ELR_EL12)
|
||||
elr_phys = self.p.hv_translate(elr, False, False)
|
||||
sp_el1 = self.u.mrs(SP_EL1)
|
||||
sp_el0 = self.u.mrs(SP_EL0)
|
||||
far = None
|
||||
if esr.EC == ESR_EC.DABORT or esr.EC == ESR_EC.IABORT:
|
||||
far = self.u.mrs(FAR_EL12)
|
||||
if self.sym(elr)[1] != "com.apple.kernel:_panic_trap_to_debugger":
|
||||
print("Page fault")
|
||||
return ok
|
||||
|
||||
print(f"EL1: Exception #{vector} ({esr.EC!s}) to {self.addr(target)} from {spsr.M.name}")
|
||||
print(f" ELR={self.addr(elr)} (0x{elr_phys:x})")
|
||||
print(f" SP_EL1=0x{sp_el1:x} SP_EL0=0x{sp_el0:x}")
|
||||
if far is not None:
|
||||
print(f" FAR={self.addr(far)}")
|
||||
if elr_phys:
|
||||
self.u.disassemble_at(elr_phys - 4 * 4, 9 * 4, elr_phys)
|
||||
if self.sym(elr)[1] == "com.apple.kernel:_panic_trap_to_debugger":
|
||||
print("Panic! Trying to decode panic...")
|
||||
try:
|
||||
self.decode_panic_call()
|
||||
except:
|
||||
print("Error decoding panic.")
|
||||
try:
|
||||
self.bt()
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
if esr.EC == ESR_EC.UNKNOWN:
|
||||
instr = self.p.read32(elr_phys)
|
||||
if instr == 0xe7ffdeff:
|
||||
print("Debugger break! Trying to decode panic...")
|
||||
try:
|
||||
self.decode_dbg_panic()
|
||||
except:
|
||||
print("Error decoding panic.")
|
||||
try:
|
||||
self.bt()
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
return False
|
||||
else:
|
||||
elr = self.u.mrs(ELR_EL12)
|
||||
print(f"Guest: {str(EXC(vector & 3))} at {self.addr(elr)}")
|
||||
|
||||
return ok
|
||||
|
||||
def handle_sync(self, ctx):
|
||||
if ctx.esr.EC == ESR_EC.MSR:
|
||||
return self.handle_msr(ctx)
|
||||
|
||||
if ctx.esr.EC == ESR_EC.IMPDEF:
|
||||
return self.handle_impdef(ctx)
|
||||
|
||||
if ctx.esr.EC == ESR_EC.HVC:
|
||||
return self.handle_hvc(ctx)
|
||||
|
||||
def handle_exception(self, reason, code, info):
|
||||
self.ctx = ctx = ExcInfo.parse(self.iface.readmem(info, ExcInfo.sizeof()))
|
||||
|
||||
print(f"Guest exception: {code.name}")
|
||||
handled = False
|
||||
|
||||
self.u.print_exception(code, ctx)
|
||||
try:
|
||||
if code == EXC.SYNC:
|
||||
handled = self.handle_sync(ctx)
|
||||
elif code == EXC.FIQ:
|
||||
self.u.msr(CNTV_CTL_EL0, 0)
|
||||
self.u.print_exception(code, ctx)
|
||||
handled = True
|
||||
except Exception as e:
|
||||
print(f"Python exception while handling guest exception:")
|
||||
traceback.print_exc()
|
||||
|
||||
locals = {
|
||||
"hv": self,
|
||||
"iface": self.iface,
|
||||
"p": self.p,
|
||||
"u": self.u,
|
||||
}
|
||||
if handled:
|
||||
ret = EXC_RET.HANDLED
|
||||
else:
|
||||
print(f"Guest exception: {code.name}")
|
||||
|
||||
for attr in dir(self):
|
||||
locals[attr] = getattr(self, attr)
|
||||
self.u.print_exception(code, ctx)
|
||||
|
||||
ret = shell.run_shell(locals, "Entering debug shell", "Returning from exception")
|
||||
locals = {
|
||||
"hv": self,
|
||||
"iface": self.iface,
|
||||
"p": self.p,
|
||||
"u": self.u,
|
||||
}
|
||||
|
||||
if ret is None:
|
||||
ret = EXC_RET.EXIT_GUEST
|
||||
for attr in dir(self):
|
||||
a = getattr(self, attr)
|
||||
if callable(a):
|
||||
locals[attr] = getattr(self, attr)
|
||||
|
||||
ret = shell.run_shell(locals, "Entering debug shell", "Returning from exception")
|
||||
|
||||
if ret is None:
|
||||
ret = EXC_RET.EXIT_GUEST
|
||||
|
||||
self.iface.writemem(info, ExcInfo.build(self.ctx))
|
||||
|
||||
if ret == EXC_RET.HANDLED and self.step:
|
||||
ret = EXC_RET.STEP
|
||||
self.p.exit(ret)
|
||||
|
||||
def skip(self):
|
||||
|
@ -72,6 +307,94 @@ class HV:
|
|||
def exit(self):
|
||||
raise shell.ExitConsole(EXC_RET.EXIT_GUEST)
|
||||
|
||||
def hvc(self, arg):
|
||||
assert 0 <= arg <= 0xffff
|
||||
return 0xd4000002 | (arg << 5)
|
||||
|
||||
def decode_dbg_panic(self):
|
||||
xnutools.decode_debugger_state(self.u, self.ctx)
|
||||
|
||||
def decode_panic_call(self):
|
||||
xnutools.decode_panic_call(self.u, self.ctx)
|
||||
|
||||
def bt(self, frame=None, lr=None):
|
||||
if frame is None:
|
||||
frame = self.ctx.regs[29]
|
||||
if lr is None:
|
||||
lr = self.ctx.regs[30] | PAC_MASK
|
||||
|
||||
print("Stack trace:")
|
||||
while frame:
|
||||
print(f" - {self.addr(lr - 4)}")
|
||||
lrp = self.p.hv_translate(frame + 8)
|
||||
fpp = self.p.hv_translate(frame)
|
||||
if not fpp:
|
||||
break
|
||||
lr = self.p.read64(lrp) | PAC_MASK
|
||||
frame = self.p.read64(fpp)
|
||||
|
||||
def patch_exception_handling(self):
|
||||
if self.want_vbar is not None:
|
||||
vbar = self.want_vbar
|
||||
else:
|
||||
vbar = self.u.mrs(VBAR_EL12)
|
||||
|
||||
if vbar == self.vbar_el1:
|
||||
return
|
||||
|
||||
if vbar == 0:
|
||||
return
|
||||
|
||||
if self.u.mrs(SCTLR_EL12) & 1:
|
||||
vbar_phys = self.p.hv_translate(vbar, False, False)
|
||||
if vbar_phys == 0:
|
||||
print(f"VBAR vaddr 0x{vbar:x} translation failed!")
|
||||
if self.vbar_el1 is not None:
|
||||
self.want_vbar = vbar
|
||||
self.u.msr(VBAR_EL12, self.vbar_el1)
|
||||
return
|
||||
else:
|
||||
if vbar & (1 << 63):
|
||||
print(f"VBAR vaddr 0x{vbar:x} without translation enabled")
|
||||
if self.vbar_el1 is not None:
|
||||
self.want_vbar = vbar
|
||||
self.u.msr(VBAR_EL12, self.vbar_el1)
|
||||
return
|
||||
|
||||
vbar_phys = vbar
|
||||
|
||||
if self.want_vbar is not None:
|
||||
self.want_vbar = None
|
||||
self.u.msr(VBAR_EL12, vbar)
|
||||
|
||||
print(f"New VBAR paddr: 0x{vbar_phys:x}")
|
||||
|
||||
#for i in range(16):
|
||||
for i in [0, 3, 4, 7, 8, 11, 12, 15]:
|
||||
idx = 0
|
||||
addr = vbar_phys + 0x80 * i
|
||||
orig = self.p.read32(addr)
|
||||
if (orig & 0xfc000000) != 0x14000000:
|
||||
print(f"Unknown vector #{i}:\n")
|
||||
self.u.disassemble_at(addr, 16)
|
||||
else:
|
||||
idx = len(self.vectors)
|
||||
delta = orig & 0x3ffffff
|
||||
if delta == 0:
|
||||
target = None
|
||||
print(f"Vector #{i}: Loop\n")
|
||||
else:
|
||||
target = (delta << 2) + vbar + 0x80 * i
|
||||
print(f"Vector #{i}: 0x{target:x}\n")
|
||||
self.vectors.append((i, target))
|
||||
self.u.disassemble_at(addr, 16)
|
||||
self.p.write32(addr, self.hvc(idx))
|
||||
|
||||
self.p.dc_cvau(vbar_phys, 0x800)
|
||||
self.p.ic_ivau(vbar_phys, 0x800)
|
||||
|
||||
self.vbar_el1 = vbar
|
||||
|
||||
def init(self):
|
||||
self.adt = load_adt(self.u.get_adt())
|
||||
self.iodev = self.p.iodev_whoami()
|
||||
|
@ -87,7 +410,33 @@ class HV:
|
|||
|
||||
self.map_hw(0x2_00000000, 0x2_00000000, 0x5_00000000)
|
||||
|
||||
self.p.hv_map_vuart(0x2_35200000)
|
||||
hcr = HCR(self.u.mrs(HCR_EL2))
|
||||
hcr.TACR = 1
|
||||
hcr.TIDCP = 0
|
||||
hcr.TVM = 0
|
||||
hcr.FMO = 0
|
||||
hcr.IMO = 0
|
||||
self.u.msr(HCR_EL2, hcr.value)
|
||||
|
||||
hacr = HACR(0)
|
||||
hacr.TRAP_CPU_EXT = 1
|
||||
#hacr.TRAP_SPRR = 1
|
||||
#hacr.TRAP_GXF = 1
|
||||
hacr.TRAP_CTRR = 1
|
||||
hacr.TRAP_EHID = 1
|
||||
hacr.TRAP_HID = 1
|
||||
hacr.TRAP_ACC = 1
|
||||
self.u.msr(HACR_EL2, hacr.value)
|
||||
|
||||
amx_ctl = AMX_CTL(self.u.mrs(AMX_CTL_EL1))
|
||||
amx_ctl.EN_EL1 = 1
|
||||
self.u.msr(AMX_CTL_EL1, amx_ctl.value)
|
||||
|
||||
#self.p.hv_map_vuart(0x2_35200000)
|
||||
|
||||
actlr = ACTLR(self.u.mrs(ACTLR_EL12))
|
||||
actlr.EnMDSB = 1
|
||||
self.u.msr(ACTLR_EL12, actlr.value)
|
||||
|
||||
self.setup_adt()
|
||||
|
||||
|
@ -102,13 +451,13 @@ class HV:
|
|||
except KeyError:
|
||||
pass
|
||||
|
||||
for cpu in list(self.adt["cpus"]):
|
||||
if cpu.name != "cpu0":
|
||||
print(f"Removing ADT node {cpu._path}")
|
||||
try:
|
||||
del self.adt["cpus"][cpu.name]
|
||||
except KeyError:
|
||||
pass
|
||||
#for cpu in list(self.adt["cpus"]):
|
||||
#if cpu.name != "cpu0":
|
||||
#print(f"Removing ADT node {cpu._path}")
|
||||
#try:
|
||||
#del self.adt["cpus"][cpu.name]
|
||||
#except KeyError:
|
||||
#pass
|
||||
|
||||
def set_bootargs(self, boot_args):
|
||||
if "-v" in boot_args.split():
|
||||
|
@ -118,11 +467,49 @@ class HV:
|
|||
print(f"Setting boot arguments to {boot_args!r}")
|
||||
self.tba.cmdline = boot_args
|
||||
|
||||
def load_macho(self, data):
|
||||
def load_macho(self, data, symfile=None):
|
||||
if isinstance(data, str):
|
||||
data = open(data, "rb").read()
|
||||
data = open(data, "rb")
|
||||
|
||||
macho = MachO(data)
|
||||
self.macho = macho = MachO(data)
|
||||
symbols = None
|
||||
if symfile is not None:
|
||||
if isinstance(symfile, str):
|
||||
symfile = open(symfile, "rb")
|
||||
syms = MachO(symfile)
|
||||
macho.add_symbols("com.apple.kernel", syms)
|
||||
|
||||
self.symbols = [(v, k) for k, v in macho.symbols.items()]
|
||||
self.symbols.sort()
|
||||
|
||||
def load_hook(data, segname, size, fileoff, dest):
|
||||
if segname != "__TEXT_EXEC":
|
||||
return data
|
||||
|
||||
print(f"Patching segment {segname}...")
|
||||
|
||||
a = array.array("I", data)
|
||||
|
||||
output = []
|
||||
|
||||
p = 0
|
||||
while (p := data.find(b"\x20\x00", p)) != -1:
|
||||
if (p & 3) != 2:
|
||||
p += 1
|
||||
continue
|
||||
|
||||
opcode = a[p // 4]
|
||||
inst = self.hvc((opcode & 0xffff))
|
||||
off = fileoff + (p & ~3)
|
||||
if off >= 0xbfcfc0:
|
||||
print(f" 0x{off:x}: 0x{opcode:04x} -> hvc 0x{opcode:x} (0x{inst:x})")
|
||||
a[p // 4] = inst
|
||||
p += 4
|
||||
|
||||
print("Done.")
|
||||
return a.tobytes()
|
||||
|
||||
#image = macho.prepare_image(load_hook)
|
||||
image = macho.prepare_image()
|
||||
sepfw_start, sepfw_length = self.u.adt["chosen"]["memory-map"].SEPFW
|
||||
tc_start, tc_size = self.u.adt["chosen"]["memory-map"].TrustCache
|
||||
|
@ -137,6 +524,7 @@ class HV:
|
|||
print(f"Total region size: 0x{image_size:x} bytes")
|
||||
|
||||
self.phys_base = phys_base = guest_base = self.u.heap_top
|
||||
guest_base += 16 << 20 # ensure guest starts within a 16MB aligned region of mapped RAM
|
||||
adt_base = guest_base
|
||||
guest_base += align(self.u.ba.devtree_size)
|
||||
tc_base = guest_base
|
||||
|
@ -180,7 +568,9 @@ class HV:
|
|||
self.tba.phys_base = phys_base
|
||||
self.tba.virt_base = 0xfffffe0010000000 + (phys_base & (32 * 1024 * 1024 - 1))
|
||||
self.tba.devtree = adt_base - phys_base + self.tba.virt_base
|
||||
self.tba.top_of_kdata = guest_base + image_size
|
||||
self.tba.top_of_kernel_data = guest_base + image_size
|
||||
|
||||
self.sym_offset = macho.vmin - guest_base + self.tba.phys_base - self.tba.virt_base
|
||||
|
||||
self.iface.writemem(guest_base + self.bootargs_off, BootArgs.build(self.tba))
|
||||
|
||||
|
@ -194,6 +584,12 @@ class HV:
|
|||
print(f"Shutting down framebuffer...")
|
||||
self.p.fb_shutdown()
|
||||
|
||||
print(f"Enabling SPRR...")
|
||||
self.u.msr(SPRR_CONFIG_EL1, 1)
|
||||
|
||||
print(f"Enabling GXF...")
|
||||
self.u.msr(GXF_CONFIG_EL1, 1)
|
||||
|
||||
print(f"Jumping to entrypoint at 0x{self.entry:x}")
|
||||
|
||||
self.iface.dev.timeout = None
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import argparse, pathlib
|
||||
|
||||
parser = argparse.ArgumentParser(description='Run a Mach-O payload under the hypervisor')
|
||||
parser.add_argument('-s', '--symbols', type=pathlib.Path)
|
||||
parser.add_argument('payload', type=pathlib.Path)
|
||||
parser.add_argument('boot_args', default=[], nargs="*")
|
||||
args = parser.parse_args()
|
||||
|
@ -25,5 +26,8 @@ if len(args.boot_args) > 0:
|
|||
boot_args = " ".join(args.boot_args)
|
||||
hv.set_bootargs(boot_args)
|
||||
|
||||
hv.load_macho(args.payload.read_bytes())
|
||||
symfile = None
|
||||
if args.symbols:
|
||||
symfile = args.symbols.open("rb")
|
||||
hv.load_macho(args.payload.open("rb"), symfile=symfile)
|
||||
hv.start()
|
||||
|
|
Loading…
Reference in a new issue