This commit is contained in:
ApfelTeeSaft
2026-05-30 20:36:46 +02:00
parent 2b7c82ccb0
commit e756a5c887
+243
View File
@@ -0,0 +1,243 @@
import struct
import idaapi
import ida_idp
import ida_loader
import ida_segment
import ida_entry
import ida_name
import ida_ua
import ida_bytes
DOL_FORMAT_NAME = "Nintendo GameCube/Wii DOL"
PPC_PROCESSOR = "ppc"
TEXT_COUNT = 7
DATA_COUNT = 11
DOL_HEADER_SIZE = 0x100
def read_u32be(li):
b = li.read(4)
if len(b) != 4:
raise EOFError("Unexpected EOF")
return struct.unpack(">I", b)[0]
def get_file_size(li):
cur = li.tell()
li.seek(0, 2)
size = li.tell()
li.seek(cur, 0)
return size
def is_mem_addr(addr):
# GameCube/Wii MEM1 + common Wii MEM2 range.
return (
0x80000000 <= addr < 0x81800000 or
0x90000000 <= addr < 0x94000000
)
def parse_dol(li):
li.seek(0)
text_offsets = [read_u32be(li) for _ in range(TEXT_COUNT)]
data_offsets = [read_u32be(li) for _ in range(DATA_COUNT)]
text_addrs = [read_u32be(li) for _ in range(TEXT_COUNT)]
data_addrs = [read_u32be(li) for _ in range(DATA_COUNT)]
text_sizes = [read_u32be(li) for _ in range(TEXT_COUNT)]
data_sizes = [read_u32be(li) for _ in range(DATA_COUNT)]
bss_addr = read_u32be(li)
bss_size = read_u32be(li)
entry = read_u32be(li)
return {
"text_offsets": text_offsets,
"data_offsets": data_offsets,
"text_addrs": text_addrs,
"data_addrs": data_addrs,
"text_sizes": text_sizes,
"data_sizes": data_sizes,
"bss_addr": bss_addr,
"bss_size": bss_size,
"entry": entry,
}
def validate_dol(li):
size = get_file_size(li)
if size < DOL_HEADER_SIZE:
return False
li.seek(0)
magic = li.read(4)
# Reject obvious non-DOL containers/files.
if magic == b"\x55\xAA\x38\x2D": # U8 archive
return False
if magic in (b"Yaz0", b"LZ77", b"RARC", b"RIFF", b"\x89PNG"):
return False
if magic[:2] in (b"PK", b"BM"):
return False
try:
h = parse_dol(li)
except Exception:
return False
sections = []
for i in range(TEXT_COUNT):
off = h["text_offsets"][i]
addr = h["text_addrs"][i]
sz = h["text_sizes"][i]
if sz == 0:
continue
if off < DOL_HEADER_SIZE or off >= size:
return False
if off + sz > size:
return False
if off % 4 != 0:
return False
if not is_mem_addr(addr):
return False
if not is_mem_addr(addr + sz - 1):
return False
sections.append(("text", i, off, addr, sz))
for i in range(DATA_COUNT):
off = h["data_offsets"][i]
addr = h["data_addrs"][i]
sz = h["data_sizes"][i]
if sz == 0:
continue
if off < DOL_HEADER_SIZE or off >= size:
return False
if off + sz > size:
return False
if off % 4 != 0:
return False
if not is_mem_addr(addr):
return False
if not is_mem_addr(addr + sz - 1):
return False
sections.append(("data", i, off, addr, sz))
if len(sections) < 2:
return False
# Entry must point into a text section.
entry_ok = False
for kind, _idx, _off, addr, sz in sections:
if kind == "text" and addr <= h["entry"] < addr + sz:
entry_ok = True
break
if not entry_ok:
return False
# File ranges should not overlap.
ranges = sorted((off, off + sz) for _kind, _idx, off, _addr, sz in sections)
for a, b in zip(ranges, ranges[1:]):
if a[1] > b[0]:
return False
return True
def accept_file(li, filename):
if validate_dol(li):
return {
"format": DOL_FORMAT_NAME,
"processor": PPC_PROCESSOR,
}
return 0
def add_segment(start, end, name, seg_class):
ida_segment.add_segm(0, start, end, name, seg_class)
def load_file(li, neflags, fmt):
print("[DOL] Loading Nintendo GameCube/Wii DOL")
ida_idp.set_processor_type(PPC_PROCESSOR, ida_idp.SETPROC_LOADER)
h = parse_dol(li)
for i in range(TEXT_COUNT):
off = h["text_offsets"][i]
addr = h["text_addrs"][i]
sz = h["text_sizes"][i]
if sz == 0:
continue
end = addr + sz
name = ".text%d" % i
print("[DOL] text%d file=0x%X addr=0x%X size=0x%X" % (i, off, addr, sz))
add_segment(addr, end, name, "CODE")
li.file2base(off, addr, end, True)
seg = ida_segment.getseg(addr)
if seg:
seg.perm = ida_segment.SEGPERM_READ | ida_segment.SEGPERM_EXEC
ida_segment.update_segm(seg)
ida_ua.create_insn(addr)
for i in range(DATA_COUNT):
off = h["data_offsets"][i]
addr = h["data_addrs"][i]
sz = h["data_sizes"][i]
if sz == 0:
continue
end = addr + sz
name = ".data%d" % i
print("[DOL] data%d file=0x%X addr=0x%X size=0x%X" % (i, off, addr, sz))
add_segment(addr, end, name, "DATA")
li.file2base(off, addr, end, True)
seg = ida_segment.getseg(addr)
if seg:
seg.perm = ida_segment.SEGPERM_READ | ida_segment.SEGPERM_WRITE
ida_segment.update_segm(seg)
if h["bss_size"] != 0 and is_mem_addr(h["bss_addr"]):
bss_start = h["bss_addr"]
bss_end = h["bss_addr"] + h["bss_size"]
print("[DOL] bss addr=0x%X size=0x%X" % (bss_start, h["bss_size"]))
add_segment(bss_start, bss_end, ".bss", "BSS")
seg = ida_segment.getseg(bss_start)
if seg:
seg.perm = ida_segment.SEGPERM_READ | ida_segment.SEGPERM_WRITE
ida_segment.update_segm(seg)
ida_entry.add_entry(h["entry"], h["entry"], "entry", True)
ida_name.set_name(h["entry"], "entry", ida_name.SN_CHECK)
print("[DOL] Entry point: 0x%08X" % h["entry"])
return 1