diff --git a/CpuPluginEntryIdentifier.py b/CpuPluginEntryIdentifier.py new file mode 100644 index 0000000..dc53ebc --- /dev/null +++ b/CpuPluginEntryIdentifier.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 + +from pefbinary import PEF +from zcpcore import read_rsrc_path + +# FIGURE OUT THE ENTRY POINTS IN A CPU PLUGIN! + +# Paste in what the NanoKernel extruded under the influence of HackLogFirstSIGPs. + +NK_OUTPUT = """ +0 7c0802a6 90010008 9421ffc0 5463043e 480086d9 80410014 39820360 806c0748 38210040 +1 7c0802a6 bf41ffe8 90010008 9421ffb0 3bc20360 549f043e 57e7083c 7d07f214 547d043e +2 7c0802a6 bfa1fff4 90010008 9421ffb0 3bc20360 549f043e 57e5083c 7cc5f214 5463043e +3 7c0802a6 bf81fff0 90010008 9421ffb0 3b820360 549f043e 57e5083c 7cc5e214 547e043e +4 7c0802a6 bf41ffe8 90010008 9421ffb0 3bc20360 549f043e 3b600001 57e5083c 3b400000 +5 7c0802a6 bfc1fff8 90010008 9421ffc0 3bc20360 549f043e 5463043e 480084bd 80410014 +6 7c0802a6 bfc1fff8 90010008 9421ffc0 83e2ffc4 549e043e 5463043e 48008239 80410014 +7 7c0802a6 bfc1fff8 90010008 9421ffc0 83e2ffc4 549e043e 5463043e 4800810d 80410014 +8 7c0802a6 bf41ffe8 90010008 9421ffb0 3bc20360 547c043e 837e074c 7f83e378 8bbe0f82 +9 7c0802a6 bf21ffe4 90010008 9421ffa0 3bc20360 547d043e 839e0b80 57bf083c 3b400000 +10 7c0802a6 bfc1fff8 90010008 9421ffc0 3bc20360 549f043e 5463043e 48007de1 80410014 +11 7c0802a6 90010008 9421ffc0 5463043e 48007d81 80410014 38210040 80010008 7c0803a6 +12 7c0802a6 bee1ffdc 90010008 9421ffa0 90a10080 90c10084 90e10088 9101008c 91210090 +13 7c0802a6 be61ffcc 90010008 9421ff90 83e2ffc4 547a043e 839f0b7c 3b000001 833f0b80 +14 456e7472 79000000 00000000 00000000 4e56ffe2 48e70718 266e0008 7005fe04 28484aae +15 456e7472 79000000 00000000 00000000 4e56ffe2 48e70718 266e0008 7005fe04 28484aae +16 7c0802a6 bf21ffe4 90010008 9421ffa0 90c10084 90e10088 9101008c 91210090 91410094 +17 7c0802a6 90010008 9421ffc0 5463043e 48006fd5 80410014 8062ffc4 38800007 48003409 +""" + +# And this should be the PEF to search. +CPUP_PATH = '/Users/elliotnunn/Nosy/Victims/Extensions/Multiprocessing/Apple CPU Plugins//cpup/4/Core99Plugin/locked' + +KNOWN_SELECTORS = { + 1: 'StartProcessor', + 3: 'StopProcessor', + 4: 'ResetProcessor', + 8: 'SynchClock', +} + +whole_binary = read_rsrc_path(CPUP_PATH) +code_section = PEF(whole_binary).code + +patchpef_args = [] + +for l in NK_OUTPUT.split('\n'): + l = l.rstrip() + if not l: continue + n, _, hx = l.partition(' ') + n = int(n) + hx = bytes.fromhex(hx.replace(' ', '')) + + name = KNOWN_SELECTORS.get(n, 'UnknownSelector') + name = '%s_%d' % (name, n) + + print('Selector %d:' % n, end='') + + loc = code_section.find(hx) + if loc == -1: + print(' not found') + continue + + while loc != -1: + patchpef_args.extend(['0x%X' % loc, ':'+name]) + print(' 0x%X' % loc, end='') + loc = code_section.find(hx, loc+1) + print() + +if patchpef_args: + print() + print('Suggested patchpef arguments:') + print(*patchpef_args) \ No newline at end of file diff --git a/pefbinary.py b/pefbinary.py new file mode 100755 index 0000000..79117b4 --- /dev/null +++ b/pefbinary.py @@ -0,0 +1,88 @@ +import struct + +class PEF: + MAGIC = b'Joy!' + + CONT_HEAD_FMT = '>4s4s4s5I2HI' + CONT_HEAD_LEN = struct.calcsize(CONT_HEAD_FMT) + + SEC_HEAD_FMT = '>i5I4B' + SEC_HED_LEN = struct.calcsize(SEC_HEAD_FMT) + + @classmethod + def read_from(cls, path): + with open(path, 'rb') as f: + return cls(f.read()) + + def __init__(self, data): + (magic, fourcc, arch, ver, + timestamp, old_def_ver, old_imp_ver, cur_ver, + sec_count, inst_sec_count, reserv) = struct.unpack_from(self.CONT_HEAD_FMT, data) + + sec_earliest = len(data) + sec_latest = 0 + + self.sections = [] + self.sectypes = [] + self.headeroffsets = [] + + self.code = None + + for i in range(sec_count): + sh_offset = self.CONT_HEAD_LEN + self.SEC_HED_LEN*i + + (sectionName, sectionAddress, execSize, + initSize, rawSize, containerOffset, + regionKind, shareKind, alignment, reserved) = struct.unpack_from(self.SEC_HEAD_FMT, data, sh_offset) + + the_sec = data[containerOffset : containerOffset + rawSize] + + if regionKind == 0 and execSize == initSize == rawSize: + the_sec = bytearray(the_sec) + self.code = the_sec + + self.sections.append(the_sec) + self.sectypes.append(regionKind) + self.headeroffsets.append(sh_offset) + + sec_earliest = min(sec_earliest, containerOffset) + sec_latest = max(sec_latest, containerOffset + rawSize) + + if any(data[sec_latest:]): + print('nonzero trailing data from', hex(sec_latest), 'to', hex(len(data)), ' ... will cause incorrect output') + + self.padmult = 1 + while len(data) % (self.padmult * 2) == 0: + self.padmult *= 2 + + self.header = data[:sec_earliest] + + def __bytes__(self): + accum = bytearray(self.header) + + for i in range(len(self.sections)): + the_sec = self.sections[i] + hoff = self.headeroffsets[i] + + while len(accum) % 16: + accum.append(0) + + new_off = len(accum) + new_len = len(the_sec) + + accum.extend(the_sec) + + struct.pack_into('>I', accum, hoff + 20, new_off) + + if the_sec is self.code: + for i in range(8, 20, 4): + struct.pack_into('>I', accum, hoff + i, new_len) + + while len(accum) % self.padmult != 0: + accum.extend(b'\x00') + + return bytes(accum) + + def write_to(self, path): + with open(path, 'wb') as f: + f.write(bytes(self)) diff --git a/zcpcore.py b/zcpcore.py new file mode 100644 index 0000000..9422b8a --- /dev/null +++ b/zcpcore.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 + +from subprocess import run, PIPE +from sys import argv +import tempfile + +def read_rsrc_path(path): + BADCHARS = b'\t$" ' + + if '//' not in path: + with open(path, 'rb') as f: + return f.read() + + else: + path, _, rest = path.partition('//') + + rtype, rid, *_ = rest.split('/') + + try: + int(rid) + except ValueError: + rid = '"%s"' % rid + + type_expr = '\'%s\' (%s)' % (rtype, rid) + + rez_code = run(['DeRez', '-only', type_expr, path], stdout=PIPE, check=True).stdout + + if len(rez_code) < 2: + raise FileNotFoundError + + accum = bytearray() + + for l in rez_code.split(b'\n'): + if len(l) >= 6 and l[1:2] == b'$': + hx = l[:43].lstrip(BADCHARS).rstrip(BADCHARS) + accum.extend(bytes.fromhex(hx.decode('ascii'))) + + return bytes(accum) + +def write_rsrc_path(path, data): + STEP = 32 + + if '//' not in path: + with open(path, 'wb') as f: + f.write(data) + + else: + path, _, rest = path.partition('//') + + rtype, rid, *args = rest.split('/') + if args: + rname = ', "%s"' % args[0] + args = args[1:] + else: + rname = "" + rid = int(rid) + + args = ''.join(', %s' % x for x in args) + + + with tempfile.NamedTemporaryFile(mode='w') as f: + print('data \'%s\' (%d%s%s) {\n' % (rtype, rid, rname, args), file=f) + + for o in range(0, len(data), STEP): + chunk = data[o:o+STEP].hex() + print('\t$"%s"' % chunk, file=f) + + print('};', file=f) + + f.flush() + + run(['Rez', '-a', '-o', path, f.name], check=True)