├── .clang-format ├── .gitignore ├── LICENSE ├── Makefile ├── README.md └── source ├── libpatcher.c ├── libpatcher.h └── patches.h /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 4 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 WiiLink24 Developers 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | # Clear the implicit built in rules 3 | #--------------------------------------------------------------------------------- 4 | .SUFFIXES: 5 | #--------------------------------------------------------------------------------- 6 | ifeq ($(strip $(DEVKITPPC)),) 7 | $(error "Please set DEVKITPPC in your environment. export DEVKITPPC=devkitPPC") 8 | endif 9 | 10 | include $(DEVKITPPC)/wii_rules 11 | PORTLIBS_WII := $(DEVKITPRO)/portlibs/wii 12 | 13 | #--------------------------------------------------------------------------------- 14 | # TARGET is the name of the output 15 | # BUILD is the directory where object files & intermediate files will be placed 16 | # SOURCES is a list of directories containing source code 17 | # INCLUDES is a list of directories containing extra header files 18 | #--------------------------------------------------------------------------------- 19 | TARGET := $(notdir $(CURDIR)) 20 | BUILD := build 21 | SOURCES := source 22 | DATA := data 23 | INCLUDES := 24 | 25 | #--------------------------------------------------------------------------------- 26 | # options for code generation 27 | #--------------------------------------------------------------------------------- 28 | 29 | CFLAGS = -g -O2 -Wall $(MACHDEP) $(INCLUDE) 30 | CXXFLAGS = $(CFLAGS) 31 | 32 | LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map 33 | 34 | #--------------------------------------------------------------------------------- 35 | # any extra libraries we wish to link with the project 36 | #--------------------------------------------------------------------------------- 37 | LIBS := -logc 38 | 39 | #--------------------------------------------------------------------------------- 40 | # list of directories containing libraries, this must be the top level containing 41 | # include and lib 42 | #--------------------------------------------------------------------------------- 43 | LIBDIRS := $(PORTLIBS) 44 | 45 | #--------------------------------------------------------------------------------- 46 | # no real need to edit anything past this point unless you need to add additional 47 | # rules for different file extensions 48 | #--------------------------------------------------------------------------------- 49 | ifneq ($(BUILD),$(notdir $(CURDIR))) 50 | #--------------------------------------------------------------------------------- 51 | 52 | export OUTPUT := $(CURDIR)/$(TARGET) 53 | 54 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 55 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 56 | 57 | export DEPSDIR := $(CURDIR)/$(BUILD) 58 | 59 | #--------------------------------------------------------------------------------- 60 | # automatically build a list of object files for our project 61 | #--------------------------------------------------------------------------------- 62 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 63 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 64 | sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 65 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) 66 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 67 | 68 | #--------------------------------------------------------------------------------- 69 | # use CXX for linking C++ projects, CC for standard C 70 | #--------------------------------------------------------------------------------- 71 | ifeq ($(strip $(CPPFILES)),) 72 | export LD := $(CC) 73 | else 74 | export LD := $(CXX) 75 | endif 76 | 77 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 78 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ 79 | $(sFILES:.s=.o) $(SFILES:.S=.o) 80 | 81 | #--------------------------------------------------------------------------------- 82 | # build a list of include paths 83 | #--------------------------------------------------------------------------------- 84 | export INCLUDE := $(foreach dir,$(INCLUDES), -iquote $(CURDIR)/$(dir)) \ 85 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 86 | -I$(CURDIR)/$(BUILD) \ 87 | -I$(LIBOGC_INC) \ 88 | -I$(CURDIR)/source 89 | 90 | #--------------------------------------------------------------------------------- 91 | # build a list of library paths 92 | #--------------------------------------------------------------------------------- 93 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ 94 | -L$(LIBOGC_LIB) 95 | 96 | export OUTPUT := $(CURDIR)/$(TARGET) 97 | .PHONY: $(BUILD) clean 98 | 99 | #--------------------------------------------------------------------------------- 100 | $(BUILD): 101 | @[ -d $@ ] || mkdir -p $@ 102 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 103 | 104 | #--------------------------------------------------------------------------------- 105 | clean: 106 | @echo clean ... 107 | @rm -fr $(BUILD) libpatcher.a 108 | 109 | #--------------------------------------------------------------------------------- 110 | install: build 111 | @echo Installing ... 112 | @mkdir -p $(DESTDIR)$(PORTLIBS_WII)/include/libpatcher $(DESTDIR)$(PORTLIBS_WII)/lib 113 | @install -v -m 644 build/libpatcher.a $(DESTDIR)$(PORTLIBS_WII)/lib 114 | @install -v -m 644 source/libpatcher.h $(DESTDIR)$(PORTLIBS_WII)/include/libpatcher 115 | 116 | #--------------------------------------------------------------------------------- 117 | else 118 | 119 | DEPENDS := $(OFILES:.o=.d) 120 | 121 | #--------------------------------------------------------------------------------- 122 | # main targets 123 | #--------------------------------------------------------------------------------- 124 | libpatcher.a: $(OFILES) 125 | 126 | #--------------------------------------------------------------------------------- 127 | # This rule will create a library with all the object files 128 | #--------------------------------------------------------------------------------- 129 | .a: 130 | @echo Archiving... 131 | @$(AR) $(ARFLAGS) build/libpatcher.a $^ 132 | 133 | -include $(DEPENDS) 134 | 135 | #--------------------------------------------------------------------------------- 136 | endif 137 | #--------------------------------------------------------------------------------- 138 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libpatcher 2 | 3 | libpatcher provides a solution to easily apply several common IOS patches. 4 | It can be used to automate enabling AHBPROT by reloading IOS with the ES ticket patch, 5 | to apply the negation of ISFS permissions, and to disable signature verification within ES. 6 | 7 | For example, an application requiring usage of ES_Identify would require all of the above 8 | in order to load a ticket and TMD manually, and then truly identify. 9 | 10 | Such an application could reduce patching to: 11 | 12 | ```c 13 | int main(void) { 14 | VIDEO_Init(); 15 | // [continue with video initialization] 16 | 17 | bool success = apply_patches(); 18 | if (!success) { 19 | printf("Failed to apply patches!\n"); 20 | } 21 | 22 | WPAD_Init(); 23 | // [...] 24 | } 25 | ``` 26 | 27 | It's recommended to not initialize WPAD and similar IOS-backed functions until after patching, 28 | as IOS upon reload will not be set up to relay input over Bluetooth. 29 | 30 | Alternatively, one can shutdown, patch, and later initialize WPAD and similar libraries. ES will be automatically initialized by libogc upon IOS reload. 31 | -------------------------------------------------------------------------------- /source/libpatcher.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "patches.h" 8 | 9 | #define HW_AHBPROT 0x0d800064 10 | #define MEM2_PROT 0x0d8b420a 11 | 12 | #define AHBPROT_DISABLED read32(HW_AHBPROT) == 0xFFFFFFFF 13 | #define IOS_MEMORY_START (u32 *)0x933E0000 14 | #define IOS_MEMORY_END (u32 *)0x93FFFFFF 15 | 16 | uint8_t in_dolphin = 0xFF; 17 | 18 | // Within Dolphin, we have no IOS to patch. 19 | // Additionally, many patches can cause Dolphin to fail. 20 | bool is_dolphin() { 21 | // We may have detected this before. 22 | if (in_dolphin != 0xFF) { 23 | return (in_dolphin == 1); 24 | } 25 | 26 | s32 fd = IOS_Open("/dev/dolphin", IPC_OPEN_READ); 27 | 28 | // On a real Wii, this returns a negative number as an error. 29 | // Within Dolphin, we acquire a file descriptor starting at 0 or above. 30 | // We should close the handle if this is the case. 31 | if (fd >= 0) { 32 | IOS_Close(fd); 33 | in_dolphin = 1; 34 | return true; 35 | } else { 36 | in_dolphin = 0; 37 | return false; 38 | } 39 | } 40 | 41 | void disable_memory_protections() { write16(MEM2_PROT, 2); } 42 | 43 | bool patch_memory_range(u32 *start, u32 *end, const u16 original_patch[], 44 | const u16 new_patch[], u32 patch_size) { 45 | bool patched = false; 46 | 47 | for (u32 *patchme = start; patchme < end; ++patchme) { 48 | if (memcmp(patchme, original_patch, patch_size) == 0) { 49 | // Copy our new patch over the existing, and flush. 50 | memcpy(patchme, new_patch, patch_size); 51 | DCFlushRange(patchme, patch_size); 52 | 53 | // While this realistically won't do anything for some parts, 54 | // it's worth a try... 55 | ICInvalidateRange(patchme, patch_size); 56 | 57 | patched = true; 58 | } 59 | } 60 | 61 | return patched; 62 | } 63 | 64 | bool patch_ios_range(const u16 original_patch[], const u16 new_patch[], 65 | u32 patch_size) { 66 | // Consider our changes successful under Dolphin. 67 | if (is_dolphin()) { 68 | return true; 69 | } 70 | 71 | return patch_memory_range(IOS_MEMORY_START, IOS_MEMORY_END, original_patch, 72 | new_patch, patch_size); 73 | } 74 | 75 | bool patch_ahbprot_reset_for_ver(s32 ios_version) { 76 | // Under Dolphin, we do not need to disable AHBPROT. 77 | if (is_dolphin()) { 78 | return true; 79 | } 80 | 81 | // We'll need to disable MEM2 protections in order to write over IOS. 82 | disable_memory_protections(); 83 | 84 | // Attempt to patch IOS. 85 | bool patched = patch_ios_range(ticket_check_old, ticket_check_patch, 86 | TICKET_CHECK_SIZE); 87 | if (!patched) { 88 | printf("unable to find and patch ES memory!\n"); 89 | return false; 90 | } 91 | 92 | s32 ios_result = IOS_ReloadIOS(ios_version); 93 | if (ios_result < 0) { 94 | printf("unable to reload IOS version! (error %d)\n", ios_result); 95 | return false; 96 | } 97 | 98 | // Keep memory protections disabled. 99 | disable_memory_protections(); 100 | 101 | if (AHBPROT_DISABLED) { 102 | return true; 103 | } else { 104 | printf("unable to preserve AHBPROT after IOS reload!\n"); 105 | return false; 106 | } 107 | } 108 | 109 | bool patch_ahbprot_reset() { 110 | s32 current_ios = IOS_GetVersion(); 111 | if (current_ios < 0) { 112 | printf("unable to get current IOS version! (error %d)\n", current_ios); 113 | return false; 114 | } 115 | 116 | return patch_ahbprot_reset_for_ver(current_ios); 117 | } 118 | 119 | bool patch_isfs_permissions() { 120 | return patch_ios_range(isfs_permissions_old, isfs_permissions_patch, 121 | ISFS_PERMISSIONS_SIZE); 122 | } 123 | 124 | bool patch_es_identify() { 125 | return patch_ios_range(es_identify_old, es_identify_patch, 126 | ES_IDENTIFY_SIZE); 127 | } 128 | 129 | bool patch_ios_verify() { 130 | return patch_ios_range(ios_verify_old, ios_verify_patch, IOS_VERIFY_SIZE); 131 | } 132 | 133 | bool apply_patches() { 134 | bool ahbprot_fix = patch_ahbprot_reset(); 135 | if (!ahbprot_fix) { 136 | // patch_ahbprot_reset should log its own errors. 137 | return false; 138 | } 139 | 140 | if (!patch_isfs_permissions()) { 141 | printf("unable to find and patch ISFS permissions!\n"); 142 | return false; 143 | } 144 | 145 | if (!patch_es_identify()) { 146 | printf("unable to find and patch ES_Identify!\n"); 147 | return false; 148 | } 149 | 150 | if (!patch_ios_verify()) { 151 | printf("unable to find and patch IOSC_VerifyPublicKeySign!\n"); 152 | return false; 153 | } 154 | 155 | return true; 156 | } 157 | -------------------------------------------------------------------------------- /source/libpatcher.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | bool patch_memory_range(u32 *start, u32 *end, const u16 original_patch[], 4 | const u16 new_patch[], u32 patch_size); 5 | 6 | bool patch_ios_range(const u16 original_patch[], const u16 new_patch[], 7 | u32 patch_size); 8 | 9 | // Applies specific patches. 10 | bool patch_ahbprot_reset_for_ver(s32 ios_version); 11 | bool patch_ahbprot_reset(); 12 | 13 | // These functions expect AHBPROT has already been disabled via the above. 14 | bool patch_isfs_permissions(); 15 | bool patch_es_identify(); 16 | bool patch_ios_verify(); 17 | 18 | // Applies all patches. 19 | bool apply_patches(); 20 | -------------------------------------------------------------------------------- /source/patches.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define HW_AHBPROT 0x0d800064 4 | #define MEM2_PROT 0x0d8b420a 5 | 6 | // This patch allows us to read tickets/TMDs/so forth. 7 | static const u16 isfs_permissions_old[] = {0x428B, 0xD001, 0x2566}; 8 | static const u16 isfs_permissions_patch[] = {0x428B, 0xE001, 0x2566}; 9 | 10 | // This patch is used to allow us to identify regardless of our UID. 11 | // We patch the start of this in order to be IOS-agnostic, as immediately 12 | // following is a branch whose address is not guaranteed to always be the same. 13 | // For this reason, we may also patch ES_DiVerifyWithTicketView's condition 14 | // within the main ES Ioctlv handler. 15 | // (No issue there. It cannot hurt anything.) 16 | static const u16 es_identify_old[] = { 17 | 0x68cc, // ldr r4, [r1, #0xc] 18 | 0x69a6, // ldr r6, [r4, #0x18] 19 | 0x6868, // ldr r0, [r5, #0x4] ; context->UID, 3 seems to be DI 20 | 0x2803 // cmp r0, #0x3 21 | }; 22 | static const u16 es_identify_patch[] = { 23 | 0x68cc, // ldr r4, [r1, #0xc] 24 | 0x69a6, // ldr r6, [r4, #0x18] 25 | 0x2003, // mov r0, #0x3 ; if you can't beat them, set yourself to them(?) 26 | 0x2803 // cmp r0, #0x3 27 | }; 28 | 29 | // This patch allows us to gain access to the AHBPROT register. 30 | static const u16 ticket_check_old[] = { 31 | 0x685B, // ldr r3,[r3,#4] ; get TMD pointer 32 | 0x22EC, 0x0052, // movls r2, 0x1D8 33 | 0x189B, // adds r3, r3, r2; add offset of access rights field in TMD 34 | 0x681B, // ldr r3, [r3] ; load access rights (haxxme!) 35 | 0x4698, // mov r8, r3 ; store it for the DVD video bitcheck later 36 | 0x07DB // lsls r3, r3, #31; check AHBPROT bit 37 | }; 38 | static const u16 ticket_check_patch[] = { 39 | 0x685B, // ldr r3,[r3,#4] ; get TMD pointer 40 | 0x22EC, 0x0052, // movls r2, 0x1D8 41 | 0x189B, // adds r3, r3, r2; add offset of access rights field in TMD 42 | 0x23FF, // li r3, 0xFF ; <--- 0xFF gives us all access bits 43 | 0x4698, // mov r8, r3 ; store it for the DVD video bitcheck later 44 | 0x07DB // lsls r3, r3, #31; check AHBPROT bit 45 | }; 46 | 47 | // This patch returns success to all signatures. 48 | static const u16 ios_verify_old[] = { 49 | 0xb5f0, // push { r4, r5, r6, r7, lr } 50 | 0x4657, // mov r7, r10 51 | 0x464e, // mov r6, r9 52 | 0x4645, // mov r5, r8 53 | 0xb4e0, // push { r5, r6, r7 } 54 | 0xb083, // sub sp, #0xc 55 | 0x2400 // mov r4, #0x0 56 | }; 57 | static const u16 ios_verify_patch[] = { 58 | 0x2000, // mov r0, #0x0 59 | 0x4770, // bx lr 60 | 0xb000, // nop 61 | 0xb000, // nop 62 | 0xb000, // nop 63 | 0xb000, // nop 64 | 0xb000 // nop 65 | }; 66 | 67 | // If a new IOS patch is added, please update accordingly. 68 | #define ISFS_PERMISSIONS_SIZE sizeof(isfs_permissions_patch) 69 | #define ES_IDENTIFY_SIZE sizeof(es_identify_patch) 70 | #define IOS_VERIFY_SIZE sizeof(ios_verify_patch) 71 | #define TICKET_CHECK_SIZE sizeof(ticket_check_patch) 72 | --------------------------------------------------------------------------------