2020-12-10 06:58:40 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
import macresources
|
|
|
|
import sys
|
|
|
|
import struct
|
|
|
|
import string
|
|
|
|
|
|
|
|
|
2021-09-20 01:36:03 +00:00
|
|
|
OKCHARS = string.ascii_letters + string.digits + "_%"
|
2021-09-20 00:14:15 +00:00
|
|
|
|
|
|
|
|
|
|
|
def addr(seg):
|
|
|
|
return 0x100000 * seg
|
|
|
|
|
|
|
|
|
2020-12-10 06:58:40 +00:00
|
|
|
parser = argparse.ArgumentParser()
|
2021-09-19 12:22:04 +00:00
|
|
|
parser.add_argument("src", help="rdump file")
|
|
|
|
parser.add_argument("dest", help="binary dest (may also create .txt file)")
|
2020-12-10 06:58:40 +00:00
|
|
|
args = parser.parse_args()
|
|
|
|
|
2021-09-19 12:22:04 +00:00
|
|
|
with open(args.src, "rb") as f:
|
|
|
|
d = f.read()
|
|
|
|
if f.name.endswith(".rdump"):
|
|
|
|
resources = list(macresources.parse_rez_code(d, original_file=f.name))
|
|
|
|
else:
|
|
|
|
resources = list(macresources.parse_file(d))
|
2020-12-10 06:58:40 +00:00
|
|
|
|
2021-09-19 12:22:04 +00:00
|
|
|
resources = [r for r in resources if r.type == b"CODE" and r.id >= 0]
|
2020-12-10 06:58:40 +00:00
|
|
|
resources.sort(key=lambda r: r.id)
|
|
|
|
|
2021-09-19 11:44:06 +00:00
|
|
|
if not resources or resources[0].id != 0:
|
2021-09-19 12:22:04 +00:00
|
|
|
sys.exit("CODE 0 not found in %s" % args.src)
|
2020-12-10 06:58:40 +00:00
|
|
|
|
|
|
|
jt_resource, *other_resources = resources
|
|
|
|
|
|
|
|
bigboy = bytearray()
|
2021-09-20 00:14:15 +00:00
|
|
|
for r in other_resources:
|
|
|
|
bigboy.extend(bytes(addr(r.id) - len(bigboy)))
|
2020-12-10 06:58:40 +00:00
|
|
|
bigboy.extend(r)
|
|
|
|
|
2021-09-19 12:22:04 +00:00
|
|
|
with open(args.dest, "wb") as f:
|
2020-12-10 06:58:40 +00:00
|
|
|
f.write(bigboy)
|
|
|
|
|
2021-09-20 00:14:15 +00:00
|
|
|
jump_table = [] # (a5_ofs, segnum, seg_ofs)
|
|
|
|
(jt_size, a5_offset_of_jt) = struct.unpack_from(">LL", jt_resource, 8)
|
2021-10-04 02:15:11 +00:00
|
|
|
|
|
|
|
thirtytwo = False
|
|
|
|
|
2021-09-20 00:14:15 +00:00
|
|
|
for jt_ofs in range(16, 16 + jt_size, 8):
|
2021-10-04 02:15:11 +00:00
|
|
|
if jt_resource[jt_ofs : jt_ofs + 8] == b"\x00\x00\xff\xff\x00\x00\x00\x00":
|
|
|
|
thirtytwo = True
|
|
|
|
continue
|
|
|
|
|
|
|
|
if thirtytwo:
|
|
|
|
segnum, be_a9f0, ofs = struct.unpack_from(">HHL", jt_resource, jt_ofs)
|
|
|
|
|
|
|
|
if be_a9f0 != 0xA9F0:
|
|
|
|
raise ValueError("32-bit jt")
|
|
|
|
|
|
|
|
rbyid = {r.id: r for r in resources}[segnum]
|
|
|
|
(adjust,) = struct.unpack_from(">L", r, 0x20)
|
|
|
|
# ofs += adjust
|
|
|
|
|
|
|
|
jump_table.append((jt_ofs - 16 + a5_offset_of_jt + 2, segnum, ofs))
|
|
|
|
|
|
|
|
else:
|
|
|
|
ofs, be_3f3c, segnum, be_a9f0 = struct.unpack_from(">HHHH", jt_resource, jt_ofs)
|
|
|
|
|
|
|
|
if be_3f3c != 0x3F3C or be_a9f0 != 0xA9F0:
|
|
|
|
raise ValueError("16-bit jt")
|
|
|
|
|
|
|
|
jump_table.append((jt_ofs - 16 + a5_offset_of_jt + 2, segnum, ofs + 4))
|
2021-09-20 00:14:15 +00:00
|
|
|
|
|
|
|
|
2022-03-31 01:49:21 +00:00
|
|
|
# From https://github.com/elliotnunn/mps/blob/master/stacktrace.go
|
|
|
|
def macsbug_syms(blob):
|
|
|
|
bigmv = memoryview(blob)
|
|
|
|
|
|
|
|
for i in range(0, len(blob) - 2, 2):
|
|
|
|
try:
|
|
|
|
mv = bigmv[i:]
|
|
|
|
# RTS or JMP(A0)
|
|
|
|
if mv[:2] == b"\x4e\x75" or mv[:2] == b"\x4e\xd0":
|
|
|
|
j = i + 2
|
|
|
|
|
|
|
|
# RTD #<word>
|
|
|
|
elif mv[:3] == b"\x4e\x74\x00":
|
|
|
|
j = i + 4
|
|
|
|
|
|
|
|
# Not the end of a procedure
|
|
|
|
else:
|
|
|
|
continue
|
|
|
|
|
|
|
|
mv = bigmv[j:]
|
|
|
|
# "Old style" MacsBug symbol format
|
|
|
|
# Haven't done the MacApp format
|
|
|
|
if 0x20 <= mv[0] & 0x7F <= 0x7F and 0x20 <= mv[1] <= 0x7F:
|
|
|
|
k = j + 1
|
|
|
|
ln = 8
|
|
|
|
|
|
|
|
# Apple Compiler symbol
|
|
|
|
elif mv[0] == 0x80 and mv[1] != 0:
|
|
|
|
k = j + 2
|
|
|
|
ln = mv[1]
|
|
|
|
|
|
|
|
# Apple Compiler symbol
|
|
|
|
elif 0x81 <= mv[0] <= 0x9F:
|
|
|
|
k = j + 1
|
|
|
|
ln = mv[0] & 0x7F
|
|
|
|
|
|
|
|
# No MacsBug string
|
|
|
|
else:
|
|
|
|
continue
|
|
|
|
|
|
|
|
s = bytes(bigmv[k : k + ln])
|
|
|
|
# Sanitise the string
|
|
|
|
if is_mxbg_str(s):
|
|
|
|
yield i, k, s.decode("ascii")
|
|
|
|
|
|
|
|
except IndexError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def is_mxbg_str(s):
|
|
|
|
for c in s:
|
|
|
|
if not ((ord("a") <= c <= ord("z")) or (ord("A") <= c <= ord("Z")) or (ord("0") <= c <= ord("9")) or c in (ord(" "), ord("%"), ord("_"))):
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
2021-09-19 12:22:04 +00:00
|
|
|
with open(args.dest + ".py", "w") as idascript:
|
2020-12-10 06:58:40 +00:00
|
|
|
# Find MacsBug symbols
|
|
|
|
namedict = {}
|
2021-09-20 00:14:15 +00:00
|
|
|
for r in other_resources:
|
|
|
|
targets = set(ofs for _, seg, ofs in jump_table if seg == r.id)
|
|
|
|
|
2022-03-31 01:49:21 +00:00
|
|
|
for rtsoffset, stringoffset, name in macsbug_syms(r):
|
|
|
|
print(f"idaapi.make_ascii_string({addr(r.id)+stringoffset:#X}, {(len(name)+2)&~1}, ASCSTR_C)", file=idascript)
|
|
|
|
print(f"set_cmt({addr(r.id)+stringoffset:#X}, 'MacsBug symbol', 0)", file=idascript)
|
2021-09-20 00:14:15 +00:00
|
|
|
|
2022-03-31 01:49:21 +00:00
|
|
|
for funcoffset in reversed(range(0, rtsoffset, 2)):
|
|
|
|
inst = bytes(r[funcoffset : funcoffset + 2])
|
|
|
|
if inst in (b"\x4E\x75", b"\x4E\x74", b"\x4E\xD0"):
|
|
|
|
break # encountered a func-end
|
2021-09-20 00:14:15 +00:00
|
|
|
|
2022-03-31 01:49:21 +00:00
|
|
|
if funcoffset in targets or inst == b"NV":
|
|
|
|
namedict[addr(r.id) + funcoffset] = name
|
|
|
|
break
|
2021-09-20 00:14:15 +00:00
|
|
|
|
|
|
|
interseg_calls = {}
|
|
|
|
for r in other_resources:
|
|
|
|
for i in range(0, len(r) - 3, 2):
|
2021-09-20 01:25:08 +00:00
|
|
|
if r[i : i + 2] in (b"\x4e\xad", b"\x48\x6d") or (r[i] == 0x41 and r[i + 1] & 0xF8 == 0xE8):
|
2021-09-20 00:14:15 +00:00
|
|
|
(targ,) = struct.unpack_from(">h", r, i + 2)
|
|
|
|
if targ > 0:
|
|
|
|
interseg_calls.setdefault(targ, []).append(addr(r.id) + i)
|
2020-12-10 06:58:40 +00:00
|
|
|
|
|
|
|
# Make some neat names for the segments...
|
|
|
|
segnames = {}
|
|
|
|
for r in other_resources:
|
|
|
|
if r.name:
|
2021-09-19 12:22:04 +00:00
|
|
|
segnames[r.id] = "".join(c for c in r.name if c in (string.ascii_letters + string.digits))
|
2020-12-10 06:58:40 +00:00
|
|
|
else:
|
2021-09-19 12:22:04 +00:00
|
|
|
segnames[r.id] = f"seg_{r.id:X}"
|
2020-12-10 06:58:40 +00:00
|
|
|
|
2021-09-20 00:14:15 +00:00
|
|
|
for a5_ofs, segnum, ofs in jump_table:
|
|
|
|
bigboy_ofs = addr(segnum) + ofs
|
2020-12-10 06:58:40 +00:00
|
|
|
|
2021-09-20 00:14:15 +00:00
|
|
|
cool_name = f"{segnames[segnum]}$"
|
2020-12-10 06:58:40 +00:00
|
|
|
if bigboy_ofs in namedict:
|
|
|
|
cool_name += namedict[bigboy_ofs]
|
|
|
|
del namedict[bigboy_ofs]
|
|
|
|
else:
|
2021-09-19 12:22:04 +00:00
|
|
|
cool_name += f"{bigboy_ofs:X}"
|
2020-12-10 06:58:40 +00:00
|
|
|
|
|
|
|
print(f'MakeFunction(0x{bigboy_ofs:X}); MakeName(0x{bigboy_ofs:X}, "{cool_name}")', file=idascript)
|
|
|
|
|
2021-09-20 00:14:15 +00:00
|
|
|
for caller in interseg_calls.get(a5_ofs, []):
|
|
|
|
print(f'MakeCode(0x{caller:X}); op_man(0x{caller:X}, 0, "{cool_name}")', file=idascript)
|
2020-12-10 06:58:40 +00:00
|
|
|
|
|
|
|
for bigboy_ofs, name in sorted(namedict.items()):
|
2021-09-20 00:14:15 +00:00
|
|
|
cool_name = f"{segnames[bigboy_ofs >> 20]}${name}"
|
2020-12-10 06:58:40 +00:00
|
|
|
print(f'MakeFunction(0x{bigboy_ofs:X}); MakeName(0x{bigboy_ofs:X}, "{cool_name}")', file=idascript)
|