mirror of
https://github.com/NinjaCheetah/libWiiPy.git
synced 2025-04-27 22:01:01 -04:00
Add IOSPatcher to apply patches to IOS WADs loaded into a Title()
Also fixes a MAJOR bug with WAD packing where changes to the content records would be dropped when dumping a WAD (!!)
This commit is contained in:
parent
930e09828e
commit
7daba7ec86
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
from .content import *
|
from .content import *
|
||||||
from .crypto import *
|
from .crypto import *
|
||||||
|
from .iospatcher import *
|
||||||
from .nus import *
|
from .nus import *
|
||||||
from .ticket import *
|
from .ticket import *
|
||||||
from .title import *
|
from .title import *
|
||||||
|
168
src/libWiiPy/title/iospatcher.py
Normal file
168
src/libWiiPy/title/iospatcher.py
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
# "title/iospatcher.py" from libWiiPy by NinjaCheetah & Contributors
|
||||||
|
# https://github.com/NinjaCheetah/libWiiPy
|
||||||
|
#
|
||||||
|
# Module for applying patches to IOS WADs via a Title().
|
||||||
|
|
||||||
|
import io
|
||||||
|
from .title import Title
|
||||||
|
|
||||||
|
|
||||||
|
class IOSPatcher:
|
||||||
|
"""
|
||||||
|
An IOSPatcher object that allows for applying patches to IOS WADs loaded into Title objects.
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self.title: Title = Title()
|
||||||
|
self.es_module_index: int = -1
|
||||||
|
|
||||||
|
def load(self, title: Title) -> None:
|
||||||
|
"""
|
||||||
|
Loads a Title object containing an IOS WAD and locates the content containing the ES module that needs to be
|
||||||
|
patched.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
title : Title
|
||||||
|
A Title object containing the IOS to be patched.
|
||||||
|
"""
|
||||||
|
# Check to ensure that this Title contains IOS. IOS always has a TID high of 00000001, and any TID low after
|
||||||
|
# 00000002.
|
||||||
|
tid = title.tmd.title_id
|
||||||
|
if tid[:8] != "00000001" or tid[8:] == "00000001" or tid[8:] == "00000002":
|
||||||
|
raise ValueError("This Title does not contain an IOS! Cannot load Title for patching.")
|
||||||
|
|
||||||
|
# Now that we know this is IOS, we need to go ahead and check all of its contents until we find the one that
|
||||||
|
# contains the ES module, since that's what we're patching.
|
||||||
|
es_content_index = -1
|
||||||
|
for content in range(len(title.content.content_records)):
|
||||||
|
target_content = title.get_content_by_index(title.content.content_records[content].index)
|
||||||
|
es_offset = target_content.find(b'\x45\x53\x3A') # This is looking for "ES:"
|
||||||
|
if es_offset != -1:
|
||||||
|
es_content_index = title.content.content_records[content].index
|
||||||
|
break
|
||||||
|
|
||||||
|
# If we get here with no content index, then ES wasn't found. That probably means that this isn't IOS.
|
||||||
|
if es_content_index == -1:
|
||||||
|
raise Exception("ES module could not be found! Please ensure that this is an intact copy of an IOS.")
|
||||||
|
|
||||||
|
self.title = title
|
||||||
|
self.es_module_index = es_content_index
|
||||||
|
|
||||||
|
def dump(self) -> Title:
|
||||||
|
"""
|
||||||
|
Returns the patched Title object.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Title
|
||||||
|
The patched Title object.
|
||||||
|
"""
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
def patch_all(self) -> None:
|
||||||
|
"""
|
||||||
|
Applies all patches to patch in fakesigning, ES_Identify access, /dev/flash access, and the version patch.
|
||||||
|
"""
|
||||||
|
self.patch_fakesigning()
|
||||||
|
self.patch_es_identify()
|
||||||
|
self.patch_nand_access()
|
||||||
|
self.patch_version_patch()
|
||||||
|
|
||||||
|
def patch_fakesigning(self) -> None:
|
||||||
|
"""
|
||||||
|
Patches the trucha/fakesigning bug back into the IOS' ES module to allow it to accept fakesigned TMDs and
|
||||||
|
Tickets.
|
||||||
|
"""
|
||||||
|
if self.es_module_index == -1:
|
||||||
|
raise Exception("No valid IOS is loaded! Patching cannot continue.")
|
||||||
|
|
||||||
|
target_content = self.title.get_content_by_index(self.es_module_index)
|
||||||
|
|
||||||
|
patch_count = 0
|
||||||
|
patch_sequences = [b'\x20\x07\x23\xa2', b'\x20\x07\x4b\x0b']
|
||||||
|
for sequence in patch_sequences:
|
||||||
|
start_offset = target_content.find(sequence)
|
||||||
|
if start_offset != -1:
|
||||||
|
with io.BytesIO(target_content) as content_data:
|
||||||
|
content_data.seek(start_offset + 1)
|
||||||
|
content_data.write(b'\x00')
|
||||||
|
content_data.seek(0)
|
||||||
|
target_content = content_data.read()
|
||||||
|
patch_count += 1
|
||||||
|
|
||||||
|
# If neither structure was found, then no patches could be applied, so return an error.
|
||||||
|
if patch_count == 0:
|
||||||
|
raise Exception("No patches could be applied because the required data could not be found!")
|
||||||
|
|
||||||
|
self.title.set_content(target_content, self.es_module_index)
|
||||||
|
|
||||||
|
def patch_es_identify(self) -> None:
|
||||||
|
"""
|
||||||
|
Patches the ability to call ES_Identify back into the IOS' ES module to allow for changing the permissions of a
|
||||||
|
title.
|
||||||
|
"""
|
||||||
|
if self.es_module_index == -1:
|
||||||
|
raise Exception("No valid IOS is loaded! Patching cannot continue.")
|
||||||
|
|
||||||
|
target_content = self.title.get_content_by_index(self.es_module_index)
|
||||||
|
|
||||||
|
patch_sequence = b'\x28\x03\xd1\x23'
|
||||||
|
start_offset = target_content.find(patch_sequence)
|
||||||
|
if start_offset != -1:
|
||||||
|
with io.BytesIO(target_content) as content_data:
|
||||||
|
content_data.seek(start_offset + 2)
|
||||||
|
content_data.write(b'\x00\x00')
|
||||||
|
content_data.seek(0)
|
||||||
|
target_content = content_data.read()
|
||||||
|
else:
|
||||||
|
raise Exception("No patches could be applied because the required data could not be found!")
|
||||||
|
|
||||||
|
self.title.set_content(target_content, self.es_module_index)
|
||||||
|
|
||||||
|
def patch_nand_access(self) -> None:
|
||||||
|
"""
|
||||||
|
Patches the ability to directly access /dev/flash back into the IOS' ES module to allow for raw access to the
|
||||||
|
Wii's filesystem.
|
||||||
|
"""
|
||||||
|
if self.es_module_index == -1:
|
||||||
|
raise Exception("No valid IOS is loaded! Patching cannot continue.")
|
||||||
|
|
||||||
|
target_content = self.title.get_content_by_index(self.es_module_index)
|
||||||
|
|
||||||
|
patch_sequence = b'\x42\x8b\xd0\x01\x25\x66'
|
||||||
|
start_offset = target_content.find(patch_sequence)
|
||||||
|
if start_offset != -1:
|
||||||
|
with io.BytesIO(target_content) as content_data:
|
||||||
|
content_data.seek(start_offset + 2)
|
||||||
|
content_data.write(b'\xe0')
|
||||||
|
content_data.seek(0)
|
||||||
|
target_content = content_data.read()
|
||||||
|
else:
|
||||||
|
raise Exception("No patches could be applied because the required data could not be found!")
|
||||||
|
|
||||||
|
self.title.set_content(target_content, self.es_module_index)
|
||||||
|
|
||||||
|
def patch_version_patch(self) -> None:
|
||||||
|
"""
|
||||||
|
Patches the ability to idk man do something I guess back into IOS' ES module. (Awaiting a real explanation)
|
||||||
|
"""
|
||||||
|
if self.es_module_index == -1:
|
||||||
|
raise Exception("No valid IOS is loaded! Patching cannot continue.")
|
||||||
|
|
||||||
|
target_content = self.title.get_content_by_index(self.es_module_index)
|
||||||
|
|
||||||
|
patch_sequence = b'\xd2\x01\x4e\x56'
|
||||||
|
start_offset = target_content.find(patch_sequence)
|
||||||
|
if start_offset != -1:
|
||||||
|
with io.BytesIO(target_content) as content_data:
|
||||||
|
content_data.seek(start_offset)
|
||||||
|
content_data.write(b'\xe0')
|
||||||
|
content_data.seek(0)
|
||||||
|
target_content = content_data.read()
|
||||||
|
else:
|
||||||
|
raise Exception("No patches could be applied because the required data could not be found!")
|
||||||
|
|
||||||
|
self.title.set_content(target_content, self.es_module_index)
|
@ -74,6 +74,7 @@ class Title:
|
|||||||
if self.tmd.title_id == "0000000100000001":
|
if self.tmd.title_id == "0000000100000001":
|
||||||
self.wad.wad_type = "ib"
|
self.wad.wad_type = "ib"
|
||||||
# Dump the TMD and set it in the WAD.
|
# Dump the TMD and set it in the WAD.
|
||||||
|
self.tmd.content_records = self.content.content_records
|
||||||
self.wad.set_tmd_data(self.tmd.dump())
|
self.wad.set_tmd_data(self.tmd.dump())
|
||||||
# Dump the Ticket and set it in the WAD.
|
# Dump the Ticket and set it in the WAD.
|
||||||
self.wad.set_ticket_data(self.ticket.dump())
|
self.wad.set_ticket_data(self.ticket.dump())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user