Compare commits

...

11 Commits

Author SHA1 Message Date
Maschell 6f47f2f9bb Update Dockerfile 2024-04-25 20:05:09 +02:00
Maschell b538deef4d Update github actions 2024-04-25 20:05:09 +02:00
Maschell 42e4a99552 Remove udp logging 2024-04-25 20:05:09 +02:00
Maschell fb12520149 fs: remove raw access patches 2024-04-25 20:05:09 +02:00
GaryOderNichts d9b6c7d1c2 FSA: Disable access to some raw devices for unlocked clients 2024-04-25 20:05:09 +02:00
Maschell ff7a2c5bbf ios_mcp: Rewrite rpx loading to make it more robust 2024-04-25 20:05:09 +02:00
Maschell fe488db5f5 Update Dockerfile 2024-04-25 20:05:09 +02:00
Maschell 1758f7ee0d Fix ios_fs .text assertion 2024-04-25 20:05:09 +02:00
Maschell d4200d781a ios_fs: patch the fat32 driver to improve readdir and stat performance (thanks: @GaryOderNichts) 2024-04-25 20:05:09 +02:00
Maschell 48e1540f7a Avoid exploiting the iosu when mocha is already running 2024-04-25 20:05:09 +02:00
Maschell 962c388fe3 Patch kernel stack check instead of the whole error_handler 2024-04-25 20:05:09 +02:00
19 changed files with 631 additions and 211 deletions

View File

@ -41,7 +41,7 @@ jobs:
- name: zip artifact
run: zip -r ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip *.rpx
- name: Create Release
uses: "softprops/action-gh-release@v1"
uses: "softprops/action-gh-release@v2"
with:
tag_name: ${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }}
draft: false

View File

@ -1,5 +1,5 @@
FROM ghcr.io/wiiu-env/devkitppc:20230621
FROM ghcr.io/wiiu-env/devkitppc:20240423
COPY --from=ghcr.io/wiiu-env/libmocha:20230621 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libmocha:20231127 /artifacts $DEVKITPRO
WORKDIR project

View File

@ -26,5 +26,5 @@ SECTIONS
}
}
ASSERT((SIZEOF(.text)) < 0x7E00, "FS text section is too big");
ASSERT((SIZEOF(.text)) < 0xE00, "FS text section is too big");
ASSERT((SIZEOF(.bss)) < 0x2C4AAC, "FS bss section is too big");

View File

@ -0,0 +1,66 @@
#pragma once
#include <assert.h>
#include <stdint.h>
#define FSA_CAPABILITY_ODD_READ (1llu << 0)
#define FSA_CAPABILITY_ODD_WRITE (1llu << 1)
#define FSA_CAPABILITY_ODD_RAW_OPEN (1llu << 2)
#define FSA_CAPABILITY_ODD_MOUNT (1llu << 3)
#define FSA_CAPABILITY_SLCCMPT_READ (1llu << 4)
#define FSA_CAPABILITY_SLCCMPT_WRITE (1llu << 5)
#define FSA_CAPABILITY_SLCCMPT_RAW_OPEN (1llu << 6)
#define FSA_CAPABILITY_SLCCMPT_MOUNT (1llu << 7)
#define FSA_CAPABILITY_SLC_READ (1llu << 8)
#define FSA_CAPABILITY_SLC_WRITE (1llu << 9)
#define FSA_CAPABILITY_SLC_RAW_OPEN (1llu << 10)
#define FSA_CAPABILITY_SLC_MOUNT (1llu << 11)
#define FSA_CAPABILITY_MLC_READ (1llu << 12)
#define FSA_CAPABILITY_MLC_WRITE (1llu << 13)
#define FSA_CAPABILITY_MLC_RAW_OPEN (1llu << 14)
#define FSA_CAPABILITY_MLC_MOUNT (1llu << 15)
#define FSA_CAPABILITY_SDCARD_READ (1llu << 16)
#define FSA_CAPABILITY_SDCARD_WRITE (1llu << 17)
#define FSA_CAPABILITY_SDCARD_RAW_OPEN (1llu << 18)
#define FSA_CAPABILITY_SDCARD_MOUNT (1llu << 19)
#define FSA_CAPABILITY_HFIO_READ (1llu << 20)
#define FSA_CAPABILITY_HFIO_WRITE (1llu << 21)
#define FSA_CAPABILITY_HFIO_RAW_OPEN (1llu << 22)
#define FSA_CAPABILITY_HFIO_MOUNT (1llu << 23)
#define FSA_CAPABILITY_RAMDISK_READ (1llu << 24)
#define FSA_CAPABILITY_RAMDISK_WRITE (1llu << 25)
#define FSA_CAPABILITY_RAMDISK_RAW_OPEN (1llu << 26)
#define FSA_CAPABILITY_RAMDISK_MOUNT (1llu << 27)
#define FSA_CAPABILITY_USB_READ (1llu << 28)
#define FSA_CAPABILITY_USB_WRITE (1llu << 29)
#define FSA_CAPABILITY_USB_RAW_OPEN (1llu << 30)
#define FSA_CAPABILITY_USB_MOUNT (1llu << 31)
#define FSA_CAPABILITY_OTHER_READ (1llu << 32)
#define FSA_CAPABILITY_OTHER_WRITE (1llu << 33)
#define FSA_CAPABILITY_OTHER_RAW_OPEN (1llu << 34)
#define FSA_CAPABILITY_OTHER_MOUNT (1llu << 35)
typedef struct __attribute__((packed)) {
uint32_t initialized;
uint64_t titleId;
uint32_t processId;
uint32_t groupId;
uint32_t unk0;
uint64_t capabilityMask;
uint8_t unk1[0x4518];
char unk2[0x280];
char unk3[0x280];
void *mutex;
} FSAProcessData;
static_assert(sizeof(FSAProcessData) == 0x4A3C, "FSAProcessData: wrong size");
typedef struct __attribute__((packed)) {
uint32_t opened;
FSAProcessData *processData;
char unk0[0x10];
char unk1[0x90];
uint32_t unk2;
char work_dir[0x280];
uint32_t unk3;
} FSAClientHandle;
static_assert(sizeof(FSAClientHandle) == 0x330, "FSAClientHandle: wrong size");

View File

@ -1,35 +1,15 @@
#include "fsa.h"
#include "ipc_types.h"
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
typedef struct __attribute__((packed)) {
uint32_t initialized;
uint64_t titleId;
uint32_t processId;
uint32_t groupId;
uint32_t unk0;
uint64_t capabilityMask;
uint8_t unk1[0x4518];
char unk2[0x280];
char unk3[0x280];
void *mutex;
} FSAProcessData;
static_assert(sizeof(FSAProcessData) == 0x4A3C, "FSAProcessData: wrong size");
typedef struct __attribute__((packed)) {
uint32_t opened;
FSAProcessData *processData;
char unk0[0x10];
char unk1[0x90];
uint32_t unk2;
char work_dir[0x280];
uint32_t unk3;
} FSAClientHandle;
static_assert(sizeof(FSAClientHandle) == 0x330, "FSAClientHandle: wrong size");
#define PATCHED_CLIENT_HANDLES_MAX_COUNT 0x40
// Disable raw access to every device except ODD and USB
#define DISABLED_CAPABILITIES \
(FSA_CAPABILITY_SLCCMPT_RAW_OPEN | FSA_CAPABILITY_SLC_RAW_OPEN | FSA_CAPABILITY_MLC_RAW_OPEN | FSA_CAPABILITY_SDCARD_RAW_OPEN | \
FSA_CAPABILITY_HFIO_RAW_OPEN | FSA_CAPABILITY_RAMDISK_RAW_OPEN | FSA_CAPABILITY_OTHER_RAW_OPEN)
FSAClientHandle *patchedClientHandles[PATCHED_CLIENT_HANDLES_MAX_COUNT];
int (*const IOS_ResourceReply)(void *, int32_t) = (void *) 0x107f6b4c;
@ -64,7 +44,7 @@ int FSA_IOCTLV_HOOK(ResourceRequest *param_1, uint32_t u2, uint32_t u3) {
int toBeRestored = 0;
for (int i = 0; i < PATCHED_CLIENT_HANDLES_MAX_COUNT; i++) {
if (patchedClientHandles[i] == clientHandle) {
clientHandle->processData->capabilityMask = 0xffffffffffffffffL;
clientHandle->processData->capabilityMask = 0xffffffffffffffffL & ~DISABLED_CAPABILITIES;
// printf("IOCTL: Force mask to 0xFFFFFFFFFFFFFFFF for client %08X\n", (uint32_t) clientHandle);
toBeRestored = 1;
break;
@ -89,7 +69,7 @@ int FSA_IOCTL_HOOK(ResourceRequest *request, uint32_t u2, uint32_t u3, uint32_t
for (int i = 0; i < PATCHED_CLIENT_HANDLES_MAX_COUNT; i++) {
if (patchedClientHandles[i] == clientHandle) {
// printf("IOCTL: Force mask to 0xFFFFFFFFFFFFFFFF for client %08X\n", (uint32_t) clientHandle);
clientHandle->processData->capabilityMask = 0xffffffffffffffffL;
clientHandle->processData->capabilityMask = 0xffffffffffffffffL & ~DISABLED_CAPABILITIES;
toBeRestored = 1;
break;
}

View File

@ -62,11 +62,6 @@ void instant_patches_setup(void) {
*(volatile u32 *) 0x0812CD2C = ARM_B(0x0812CD2C, kernel_syscall_0x81);
// Keep patches for backwards compatibility (libiosuhax)
// patch FSA raw access
*(volatile u32 *) fsa_phys(0x1070FAE8) = 0x05812070;
*(volatile u32 *) fsa_phys(0x1070FAEC) = 0xEAFFFFF9;
// Add IOCTL 0x28 to indicate the calling client should have full fs permissions
*(volatile u32 *) fsa_phys(0x10701248) = _FSA_ioctl0x28_hook;
@ -82,6 +77,57 @@ void instant_patches_setup(void) {
*(volatile u32 *) fsa_phys(0x1073994C) = 0xe3a07020; // mov r7, 0x20
*(volatile u32 *) fsa_phys(0x10739950) = 0xea000013; // b LAB_107399a8
// fat32 driver patches
{
// patch out fopen in stat
*(volatile u32 *) fsa_phys(0x1078c748) = 0xe3a00001; //mov r0, #1
// patch out fclose in stat
*(volatile u32 *) fsa_phys(0x1078c754) = 0xe3a00000; //mov r0, #0
// patch out diropen in stat
*(volatile u32 *) fsa_phys(0x1078c71c) = 0xe3a00001; //mov r0, #1
// patch out dirclose in stat
*(volatile u32 *) fsa_phys(0x1078c728) = 0xe3a00000; //mov r0, #0
// patch out fopen in readdir
*(volatile u32 *) fsa_phys(0x1078b50c) = 0xe3a00001; //mov r0, #1
// patch out fclose in readdir
*(volatile u32 *) fsa_phys(0x1078b518) = 0xe3a00000; //mov r0, #0
// patch out fopendir in readdir
*(volatile u32 *) fsa_phys(0x1078b62c) = 0xe3a00001; //mov r0, #1
// patch out fclosedir in readdir
*(volatile u32 *) fsa_phys(0x1078b638) = 0xe3a00001; //mov r0, #1
// Avoid opening the file for getting the "alloc_size" in stat and readdir
*(volatile u32 *) fsa_phys(0x1078d5b0) = 0xe3a00001; //mov r0, #1 // fopen
*(volatile u32 *) fsa_phys(0x1078d738) = 0xe3a00000; //mov r0, #0 // fclose
*(volatile u32 *) fsa_phys(0x1078d5c4) = 0xe3a00000; //mov r0, #0 // finfo
// patch alloc_size to be filesize
{
// nop some code we don't want
*(volatile u32 *) fsa_phys(0x1078d6e8) = 0xea000000; //mov r0, r0
*(volatile u32 *) fsa_phys(0x1078d6ec) = 0xea000000; //mov r0, r0
// set param_2->allocSize = param_3->size;
*(volatile u32 *) fsa_phys(0x1078d6f0) = 0xe5db3248; //ldrb r3,[r11,#0x248]
*(volatile u32 *) fsa_phys(0x1078d6f4) = 0xe5c93014; //strb r3,[r9,#0x14]
*(volatile u32 *) fsa_phys(0x1078d6f8) = 0xe5db3249; //ldrb r3,[r11,#0x249]
*(volatile u32 *) fsa_phys(0x1078d6fc) = 0xe5c93015; //strb r3,[r9,#0x15]
*(volatile u32 *) fsa_phys(0x1078d700) = 0xe5db324a; //ldrb r3,[r11,#0x24a]
*(volatile u32 *) fsa_phys(0x1078d704) = 0xe5c93016; //strb r3,[r9,#0x16]
*(volatile u32 *) fsa_phys(0x1078d708) = 0xe5db324b; //ldrb r3,[r11,#0x24b]
*(volatile u32 *) fsa_phys(0x1078d70c) = 0xe5c93017; //strb r3,[r9,#0x17]
// nop previous alloc_size assign
*(volatile u32 *) fsa_phys(0x1078d71c) = 0xea000000; //mov r0, r0
*(volatile u32 *) fsa_phys(0x1078d720) = 0xea000000; //mov r0, r0
*(volatile u32 *) fsa_phys(0x1078d724) = 0xea000000; //mov r0, r0
}
}
int (*_iosMapSharedUserExecution)(void *descr) = (void *) 0x08124F88;
// patch kernel dev node registration

View File

@ -49,8 +49,53 @@ void fs_run_patches(uint32_t ios_elf_start) {
section_write_word(ios_elf_start, 0x107044f0, ARM_BL(0x107044f0, FSA_IOCTL_HOOK));
section_write_word(ios_elf_start, 0x10704458, ARM_BL(0x10704458, FSA_IOS_Close_Hook));
// Keep patches for backwards compatibility (libiosuhax)
// patch FSA raw access
section_write_word(ios_elf_start, 0x1070FAE8, 0x05812070);
section_write_word(ios_elf_start, 0x1070FAEC, 0xEAFFFFF9);
// fat32 driver patches
// patch out fopen in stat
section_write_word(ios_elf_start, 0x1078c748, 0xe3a00001); // mov r0, #1
// patch out fclose in stat
section_write_word(ios_elf_start, 0x1078c754, 0xe3a00000); // mov r0, #0
// patch out diropen in stat
section_write_word(ios_elf_start, 0x1078c71c, 0xe3a00001); // mov r0, #1
// patch out dirclose in stat
section_write_word(ios_elf_start, 0x1078c728, 0xe3a00000); // mov r0, #0
// patch out fopen in readdir
section_write_word(ios_elf_start, 0x1078b50c, 0xe3a00001); // mov r0, #1
// patch out fclose in readdir
section_write_word(ios_elf_start, 0x1078b518, 0xe3a00000); // mov r0, #0
// patch out fopendir in readdir
section_write_word(ios_elf_start, 0x1078b62c, 0xe3a00001); // mov r0, #1
// patch out fclosedir in readdir
section_write_word(ios_elf_start, 0x1078b638, 0xe3a00001); // mov r0, #1
// Avoid opening the file for getting the "alloc_size" in stat and readdir
section_write_word(ios_elf_start, 0x1078d5b0, 0xe3a00001); // mov r0, #1 // fopen
section_write_word(ios_elf_start, 0x1078d738, 0xe3a00000); // mov r0, #0 // fclose
section_write_word(ios_elf_start, 0x1078d5c4, 0xe3a00000); // mov r0, #0 // finfo
// patch alloc_size to be filesize
{
// nop some code we don't want
section_write_word(ios_elf_start, 0x1078d6e8, 0xea000000); // mov r0, r0
section_write_word(ios_elf_start, 0x1078d6ec, 0xea000000); // mov r0, r0
// set param_2->allocSize = param_3->size;
section_write_word(ios_elf_start, 0x1078d6f0, 0xe5db3248); // ldrb r3,[r11,#0x248]
section_write_word(ios_elf_start, 0x1078d6f4, 0xe5c93014); // strb r3,[r9,#0x14]
section_write_word(ios_elf_start, 0x1078d6f8, 0xe5db3249); // ldrb r3,[r11,#0x249]
section_write_word(ios_elf_start, 0x1078d6fc, 0xe5c93015); // strb r3,[r9,#0x15]
section_write_word(ios_elf_start, 0x1078d700, 0xe5db324a); // ldrb r3,[r11,#0x24a]
section_write_word(ios_elf_start, 0x1078d704, 0xe5c93016); // strb r3,[r9,#0x16]
section_write_word(ios_elf_start, 0x1078d708, 0xe5db324b); // ldrb r3,[r11,#0x24b]
section_write_word(ios_elf_start, 0x1078d70c, 0xe5c93017); // strb r3,[r9,#0x17]
// nop previous alloc_size assign
section_write_word(ios_elf_start, 0x1078d71c, 0xea000000); // mov r0, r0
section_write_word(ios_elf_start, 0x1078d720, 0xea000000); // mov r0, r0
section_write_word(ios_elf_start, 0x1078d724, 0xea000000); // mov r0, r0
}
}

View File

@ -122,6 +122,13 @@ void kernel_run_patches(u32 ios_elf_start) {
section_write(ios_elf_start, (u32) __KERNEL_CODE_START, __KERNEL_CODE_START, __KERNEL_CODE_END - __KERNEL_CODE_START);
section_write_word(ios_elf_start, 0x0812A120, ARM_BL(0x0812A120, kernel_launch_ios));
// patch kernel dev node registration flag. Should be 1 anyway but this doesn't hurt.
section_write_word(ios_elf_start, 0x081430B4, 1);
// patch IOS_SetResourceManagerRegistrationDisabled to always keep it enabled
section_write_word(ios_elf_start, 0x0812581c, 0xe3a02001);
section_write_word(ios_elf_start, 0x08125820, 0xe1a00000);
section_write(ios_elf_start, 0x08140DE0, KERNEL_MCP_IOMAPPINGS_STRUCT, sizeof(KERNEL_MCP_IOMAPPINGS_STRUCT));
// patch /dev/odm IOCTL 0x06 to return the disc key if in_buf[0] > 2.

View File

@ -74,8 +74,9 @@ int _main() {
/* Save the request handle so we can reply later */
*(volatile u32 *) 0x0012F000 = *(volatile u32 *) 0x1016AD18;
/* Patch kernel_error_handler to BX LR immediately */
*(volatile u32 *) 0x08129A24 = 0xE12FFF1E;
// patch kernel thread stack check
*(volatile uint32_t *) 0x0812c138 = 0xe3a00000; // mov r0, #0
*(volatile uint32_t *) 0x0812c13c = 0xe12fff1e; // bx lr
void *pset_fault_behavior = (void *) 0x081298BC;
kernel_memcpy(pset_fault_behavior, (void *) repairData_set_fault_behavior, sizeof(repairData_set_fault_behavior));

View File

@ -94,7 +94,7 @@ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(shell [ ! -d $(BUILD) ] && mkdir -p $(BUILD))
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------

View File

@ -255,7 +255,7 @@ static int handleServer(int connfd) {
return -1;
}
static int dkThread(void *arg) {
static int dkThread(void *) {
printf("DK: thread started\n");
threadRunning = 1;

View File

@ -23,3 +23,30 @@ char *strncat(char *destination, const char *source, size_t num) {
// destination string is returned by standard strncat()
return destination;
}
char *strchr(const char *p, int ch) {
char c;
c = ch;
for (;; ++p) {
if (*p == c)
return ((char *) p);
if (*p == '\0')
return (NULL);
}
/* NOTREACHED */
}
char *
strrchr(s, c)
register const char *s;
int c;
{
char *rtnval = 0;
do {
if (*s == c)
rtnval = (char *) s;
} while (*s++);
return (rtnval);
}

View File

@ -31,6 +31,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define IOS_ERROR_UNKNOWN_VALUE 0xFFFFFFD6
#define IOS_ERROR_INVALID_ARG 0xFFFFFFE3
@ -140,7 +141,7 @@ static int ipc_ioctl(ipcmessage *message) {
u32 n = message->ioctl.buffer_in[2];
u32 old = *dst;
int i;
u32 i;
for (i = 0; i < n; i++) {
if (*dst != old) {
if (*dst == 0x0) old = *dst;
@ -402,7 +403,7 @@ static int ipc_ioctlv(ipcmessage *message) {
return res;
}
static int ipc_thread(void *arg) {
static int ipc_thread(void *) {
int res;
ipcmessage *message;
u32 messageQueue[0x10];
@ -419,27 +420,22 @@ static int ipc_thread(void *arg) {
switch (message->command) {
case IOS_OPEN: {
log_printf("IOS_OPEN\n");
res = 0;
break;
}
case IOS_CLOSE: {
log_printf("IOS_CLOSE\n");
res = 0;
break;
}
case IOS_IOCTL: {
log_printf("IOS_IOCTL\n");
res = ipc_ioctl(message);
break;
}
case IOS_IOCTLV: {
log_printf("IOS_IOCTLV\n");
res = ipc_ioctlv(message);
break;
}
default: {
log_printf("unexpected command 0x%X\n", message->command);
res = IOS_ERROR_UNKNOWN_VALUE;
break;
}

View File

@ -63,7 +63,7 @@ typedef struct ipcmessage {
u32 *buffer_io;
u32 length_io;
} ioctl;
struct _ioctlv {
struct {
u32 command;
uint32_t num_in;

View File

@ -1,28 +1,14 @@
#ifndef __LOGGER_H_
#define __LOGGER_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#ifdef LOG_IP
int log_init(unsigned int ip);
void log_deinit();
void log_printf(const char *format, ...);
#else
#define log_init(x)
#define log_deinit()
#define log_printf(x, ...)
#endif
#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__)
#define DEBUG_FUNCTION_LINE(FMT, ARGS...) \
do { \
log_printf("[%23s]%30s@L%04d: " FMT "", __FILE__, __FUNCTION__, __LINE__, ##ARGS); \
printf("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
} while (0)
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,6 +1,7 @@
#include "ipc.h"
static int threadsStarted = 0;
extern void wupserver_init();
int _startMainThread(void) {
if (threadsStarted == 0) {

View File

@ -26,116 +26,121 @@
#include <mocha/commands.h>
#include <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <unistd.h>
int (*const real_MCP_LoadFile)(ipcmessage *msg) = (void *) 0x0501CAA8 + 1; //+1 for thumb
int (*const MCP_DoLoadFile)(const char *path, const char *path2, void *outputBuffer, uint32_t outLength, uint32_t pos, int *bytesRead, uint32_t unk) = (void *) 0x05017248 + 1;
static int
MCP_LoadCustomFile(int target, char *path, uint32_t filesize, uint32_t fileoffset, void *buffer_out,
uint32_t buffer_len,
uint32_t pos, bool isRPX);
static int MCP_LoadCustomFile(int target, char *path, int filesize, int fileoffset, void *out_buffer, int buffer_len, int pos);
int (*const real_MCP_LoadFile)(ipcmessage *msg) = (int (*)(ipcmessage *))(0x0501CAA8 + 1); //+1 for thumb
int (*const MCP_DoLoadFile)(const char *path,
const char *path2,
void *outputBuffer,
uint32_t outLength,
uint32_t pos,
int *bytesRead,
uint32_t unk) = (int (*)(const char *,
const char *,
void *,
uint32_t,
uint32_t,
int *,
uint32_t))(0x05017248 + 1);
static bool replace_valid = false;
static bool skipPPCSetup = false;
static bool doWantReplaceRPX = false;
static bool replace_target_device = 0;
static uint32_t rep_filesize = 0;
static uint32_t rep_fileoffset = 0;
static char rpxpath[256];
typedef enum {
REPLACEMENT_TYPE_INVALID = 0,
REPLACEMENT_TYPE_HOMEBREW_RPX = 1,
REPLACEMENT_TYPE_HOMEBREW_RPL = 2,
REPLACEMENT_TYPE_BY_PATH = 3,
REPLACEMENT_TYPE_UNTIL_MEM_HOOK_COMPLETED_BUT_BY_PATH = 4,
} ReplacementType;
#define log(fmt, ...) log_printf("%s: " fmt, __FUNCTION__, __VA_ARGS__)
#define FAIL_ON(cond, val) \
if (cond) { \
log(#cond " (%08X)", val); \
return 29; \
}
typedef enum {
REPLACEMENT_LIFETIME_INVALID = 0,
REPLACEMENT_LIFETIME_UNLIMITED = 1,
REPLACEMENT_LIFETIME_ONE_RPX_LAUNCH = 2,
REPLACEMENT_LIFETIME_DURING_RPX_REPLACEMENT = 3,
} ReplacementLifetime;
int _MCP_LoadFile_patch(ipcmessage *msg) {
typedef enum {
PATH_RELATIVE_INVALID = 0,
PATH_RELATIVE_TO_ENVIRONMENT = 1,
PATH_RELATIVE_TO_SD_ROOT = 2,
} PathRelativeTo;
FAIL_ON(!msg->ioctl.buffer_in, 0);
FAIL_ON(msg->ioctl.length_in != 0x12D8, msg->ioctl.length_in);
FAIL_ON(!msg->ioctl.buffer_io, 0);
FAIL_ON(!msg->ioctl.length_io, 0);
typedef struct RPXFileReplacements {
ReplacementType type;
ReplacementLifetime lifetime;
char replaceName[0x40];
char replacementPath[256];
PathRelativeTo relativeTo;
uint32_t fileSize;
uint32_t fileOffset;
uint32_t ageInApplicationStarts;
} RPXFileReplacements;
MCPLoadFileRequest *request = (MCPLoadFileRequest *) msg->ioctl.buffer_in;
static bool gMemHookCompleted = false;
static bool sReplacedLastRPX = false;
//dumpHex(request, sizeof(MCPLoadFileRequest));
//DEBUG_FUNCTION_LINE("msg->ioctl.buffer_io = %p, msg->ioctl.length_io = 0x%X\n", msg->ioctl.buffer_io, msg->ioctl.length_io);
//DEBUG_FUNCTION_LINE("request->type = %d, request->pos = %d, request->name = \"%s\"\n", request->type, request->pos, request->name);
static const RPXFileReplacements gDefaultReplacements[] = {
// Redirect men.rpx [ENVIRONMENT]/root.rpx until IPC_CUSTOM_MEN_RPX_HOOK_COMPLETED has been called. (e.g. to init PPC homebrew after iosu-reload)
{REPLACEMENT_TYPE_UNTIL_MEM_HOOK_COMPLETED_BUT_BY_PATH, REPLACEMENT_LIFETIME_UNLIMITED, "men.rpx", "root.rpx", PATH_RELATIVE_TO_ENVIRONMENT, 0, 0, 0},
// Redirect men.rpx to [ENVIRONMENT]/men.rpx
{REPLACEMENT_TYPE_BY_PATH, REPLACEMENT_LIFETIME_UNLIMITED, "men.rpx", "men.rpx", PATH_RELATIVE_TO_ENVIRONMENT, 0, 0, 0},
};
LoadRPXTargetEnum replace_target = replace_target_device;
int replace_filesize = rep_filesize;
int replace_fileoffset = rep_fileoffset;
char *replace_path = rpxpath;
static RPXFileReplacements *gDynamicReplacements[5] = {};
if (strlen(request->name) > 1 && request->name[0] == '~' && request->name[1] == '|') {
// OSDynload_Acquire is cutting of the name right after the last '/'. This means "~/wiiu/libs/test.rpl" would simply become "test.rpl".
// To still have directories, Mocha expects '|' instead of '/'. (Modules like the AromaBaseModule might handle this transparent for the user.)
// Example: "~|wiiu|libs|test.rpl" would load "sd://wiiu/libs/test.rpl".
char *curPtr = &request->name[1];
while (*curPtr != '\0') {
if (*curPtr == '|') {
*curPtr = '/';
}
curPtr++;
}
printf("Trying to load %s from sd\n", &request->name[2]);
int result = MCP_LoadCustomFile(LOAD_RPX_TARGET_SD_CARD, &request->name[2], 0, 0, msg->ioctl.buffer_io, msg->ioctl.length_io, request->pos);
// should be at least the size of gDefaultReplacements and gDynamicReplacements
#define TEMP_ARRAY_SIZE 7
if (result >= 0) {
return result;
}
return real_MCP_LoadFile(msg);
}
if (strlen(request->name) > 1 && request->name[strlen(request->name) - 1] == 'x') {
if (strncmp(request->name, "safe.rpx", strlen("safe.rpx")) == 0 || strncmp(request->name, "ply.rpx", strlen("ply.rpx")) == 0) {
if (request->pos == 0 && replace_valid) {
//DEBUG_FUNCTION_LINE("set doWantReplaceRPX to true\n");
doWantReplaceRPX = true;
}
} else {
//DEBUG_FUNCTION_LINE("set replace_valid to false\n");
replace_valid = false;
bool addDynamicReplacement(RPXFileReplacements *pReplacements) {
for (uint32_t i = 0; i < sizeof(gDynamicReplacements) / sizeof(gDynamicReplacements[0]); i++) {
if (gDynamicReplacements[i] == NULL) {
gDynamicReplacements[i] = pReplacements;
return true;
}
}
if (strncmp(request->name, "men.rpx", strlen("men.rpx")) == 0) {
rpxpath[0] = '\0';
if (skipPPCSetup) {
snprintf(rpxpath, sizeof(rpxpath) - 1, "%s/men.rpx", &((char *) 0x0511FF00)[19]); // Copy in environment path
} else {
snprintf(rpxpath, sizeof(rpxpath) - 1, "%s/root.rpx", &((char *) 0x0511FF00)[19]); // Copy in environment path
}
// At startup we want to hook into the Wii U Menu by replacing the men.rpx with a file from the SD Card
// The replacement may restart the application to execute a kernel exploit.
// The men.rpx is hooked until the "IPC_CUSTOM_MEN_RPX_HOOK_COMPLETED" command is passed to IOCTL 0x100.
// If the loading of the replacement file fails, the Wii U Menu is loaded normally.
replace_target = LOAD_RPX_TARGET_SD_CARD;
replace_filesize = 0; // unknown
replace_fileoffset = 0;
} else if (strncmp(request->name, "safe.rpx", strlen("safe.rpx")) == 0 || strncmp(request->name, "ply.rpx", strlen("ply.rpx")) == 0) {
// Needed to support loading files > 4MiB
} else if (!doWantReplaceRPX) {
doWantReplaceRPX = false; // Only replace it once.
replace_path = NULL;
return real_MCP_LoadFile(msg);
}
if (replace_path != NULL && strlen(replace_path) > 0) {
doWantReplaceRPX = false; // Only replace it once.
int result = MCP_LoadCustomFile(replace_target, replace_path, replace_filesize, replace_fileoffset, msg->ioctl.buffer_io, msg->ioctl.length_io, request->pos);
if (result >= 0) {
return result;
}
} else {
DEBUG_FUNCTION_LINE("replace_path was NULL\n");
}
return real_MCP_LoadFile(msg);
DEBUG_FUNCTION_LINE("Failed to find empty slot!\n");
return false;
}
void RemoveByLifetime(ReplacementLifetime lifetime) {
for (uint32_t i = 0; i < sizeof(gDynamicReplacements) / sizeof(gDynamicReplacements[0]); i++) {
if (gDynamicReplacements[i] != NULL && gDynamicReplacements[i]->lifetime == lifetime) {
// We DON'T want to remove new entries which had the chance to be used
// There are intended to be used for the NEXT application start.
if (lifetime == REPLACEMENT_LIFETIME_DURING_RPX_REPLACEMENT &&
gDynamicReplacements[i]->ageInApplicationStarts == 0) {
continue;
}
/*DEBUG_FUNCTION_LINE("Remove item %p: lifetime: %d type: %d replace: %s replaceWith: %s\n",
gDynamicReplacements[i], gDynamicReplacements[i]->lifetime,
gDynamicReplacements[i]->type, gDynamicReplacements[i]->replaceName,
gDynamicReplacements[i]->replacementPath);*/
svcFree(0xCAFF, gDynamicReplacements[i]);
gDynamicReplacements[i] = NULL;
}
}
}
static inline int EndsWith(const char *str, const char *suffix) {
if (!str || !suffix)
return 0;
size_t lenstr = strlen(str);
size_t lensuffix = strlen(suffix);
if (lensuffix > lenstr)
return 0;
return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0;
}
// Set filesize to 0 if unknown.
static int MCP_LoadCustomFile(int target, char *path, int filesize, int fileoffset, void *buffer_out, int buffer_len, int pos) {
static int
MCP_LoadCustomFile(int target, char *path, uint32_t filesize, uint32_t fileoffset, void *buffer_out,
uint32_t buffer_len,
uint32_t pos, bool isRPX) {
if (path == NULL || (filesize > 0 && (pos > filesize))) {
return 0;
}
@ -147,8 +152,8 @@ static int MCP_LoadCustomFile(int target, char *path, int filesize, int fileoffs
char mountpath[] = "/vol/storage_iosu_homebrew";
if (target == LOAD_RPX_TARGET_SD_CARD) {
int fsa_h = svcOpen("/dev/fsa", 0);
int mount_res = FSA_Mount(fsa_h, "/dev/sdcard01", mountpath, 2, NULL, 0);
int fsa_h = svcOpen("/dev/fsa", 0);
FSA_Mount(fsa_h, "/dev/sdcard01", mountpath, 2, NULL, 0);
svcClose(fsa_h);
strncpy(filepath, mountpath, sizeof(filepath) - 1);
@ -160,7 +165,7 @@ static int MCP_LoadCustomFile(int target, char *path, int filesize, int fileoffs
int bytesRead = 0;
int result = MCP_DoLoadFile(filepath, NULL, buffer_out, buffer_len, pos + fileoffset, &bytesRead, 0);
//log("MCP_DoLoadFile returned %d, bytesRead = %d pos %d \n", result, bytesRead, pos + fileoffset);
// DEBUG_FUNCTION_LINE("MCP_DoLoadFile returned %d, bytesRead = %d pos %u\n", result, bytesRead, pos + fileoffset);
if (result >= 0) {
if (bytesRead <= 0) {
@ -174,15 +179,266 @@ static int MCP_LoadCustomFile(int target, char *path, int filesize, int fileoffs
}
}
if (result < 0x400000 && target == LOAD_RPX_TARGET_SD_CARD) {
int fsa_h = svcOpen("/dev/fsa", 0);
int unmount_res = FSA_Unmount(fsa_h, mountpath, 0x80000002);
svcClose(fsa_h);
// Unmount the sd card once the whole file has been read.
if (result >= 0 && result < 0x400000) {
if (target == LOAD_RPX_TARGET_SD_CARD) {
int fsa_h = svcOpen("/dev/fsa", 0);
FSA_Unmount(fsa_h, mountpath, 0x80000002);
svcClose(fsa_h);
}
if (isRPX) {
// if we have read less than 0x400000 bytes we have read the whole file.
// We can now remove the replacements with "one launch" lifetime
RemoveByLifetime(REPLACEMENT_LIFETIME_ONE_RPX_LAUNCH);
}
}
return result;
}
int DoSDRedirectionByPath(ipcmessage *msg, MCPLoadFileRequest *request) {
if (strlen(request->name) > 1 && request->name[0] == '~' && request->name[1] == '|') {
// OSDynload_Acquire is cutting of the name right after the last '/'. This means "~/wiiu/libs/test.rpl" would simply become "test.rpl".
// To still have directories, Mocha expects '|' instead of '/'. (Modules like the AromaBaseModule might handle this transparent for the user.)
// Example: "~|wiiu|libs|test.rpl" would load "sd://wiiu/libs/test.rpl".
char *curPtr = &request->name[1];
while (*curPtr != '\0') {
if (*curPtr == '|') {
*curPtr = '/';
}
curPtr++;
}
// DEBUG_FUNCTION_LINE("Trying to load %s from sd\n", &request->name[2]);
int result = MCP_LoadCustomFile(LOAD_RPX_TARGET_SD_CARD, &request->name[2], 0, 0, msg->ioctl.buffer_io,
msg->ioctl.length_io, request->pos, EndsWith(request->name, ".rpx"));
if (result >= 0) {
return result;
} else {
DEBUG_FUNCTION_LINE("Failed: result was %d\n", result);
}
return real_MCP_LoadFile(msg);
}
return -1;
}
bool isCurrentHomebrewWrapperReplacement(MCPLoadFileRequest *request) {
if ((strncmp("ply.rpx", request->name, strlen("ply.rpx")) == 0) ||
(strncmp("safe.rpx", request->name, strlen("safe.rpx")) == 0)) {
return true;
}
return false;
}
const RPXFileReplacements *GetCurrentRPXReplacementEx(MCPLoadFileRequest *request, const RPXFileReplacements **list, uint32_t list_size) {
for (uint32_t i = 0; i < list_size; i++) {
const RPXFileReplacements *cur = list[i];
if (cur == NULL || cur->lifetime == REPLACEMENT_LIFETIME_INVALID) {
continue;
}
bool valid = false;
switch (cur->type) {
case REPLACEMENT_TYPE_HOMEBREW_RPX: {
// DEBUG_FUNCTION_LINE("Check REPLACEMENT_TYPE_HOMEBREW_RPX for %s... ", cur->replacementPath);
if (isCurrentHomebrewWrapperReplacement(request)) {
// printf("Yay!\n");
valid = true;
} else {
// printf("No :(!\n");
}
break;
}
case REPLACEMENT_TYPE_HOMEBREW_RPL:
// DEBUG_FUNCTION_LINE("Check REPLACEMENT_TYPE_HOMEBREW_RPL for %s (%s) == %s (sReplacedLastRPX %d)... ", cur->replaceName,
// cur->replacementPath, request->name, sReplacedLastRPX);
if (sReplacedLastRPX && strncmp(cur->replaceName, request->name, sizeof(request->name) - 1) == 0) {
// printf("Yay!\n");
valid = true;
} else {
// printf("No :(!\n");
}
break;
case REPLACEMENT_TYPE_BY_PATH:
// DEBUG_FUNCTION_LINE("Check REPLACEMENT_TYPE_BY_PATH for %s (%s) == %s... ", cur->replaceName,
// cur->replacementPath, request->name);
if (strncmp(cur->replaceName, request->name, sizeof(request->name) - 1) == 0) {
// printf("Yay!\n");
valid = true;
} else {
// printf("No :(!\n");
}
break;
case REPLACEMENT_TYPE_UNTIL_MEM_HOOK_COMPLETED_BUT_BY_PATH:
// DEBUG_FUNCTION_LINE("Check REPLACEMENT_TYPE_UNTIL_MEM_HOOK_COMPLETED_BUT_BY_PATH for %s (%s) == %s... ", cur->replaceName,
// cur->replacementPath, request->name);
if (strncmp(cur->replaceName, request->name, sizeof(request->name) - 1) == 0 && !gMemHookCompleted) {
// printf("Yay!\n");
valid = true;
} else {
// printf("No :(!\n");
}
break;
case REPLACEMENT_TYPE_INVALID:
DEBUG_FUNCTION_LINE("REPLACEMENT_TYPE_INVALID\n");
break;
}
if (valid) {
return cur;
}
}
return NULL;
}
static uint32_t getReplacementDataInSingleArray(const RPXFileReplacements **tmpArray, uint32_t tmpArraySize) {
uint32_t offsetInResult = 0;
for (uint32_t i = 0; i < sizeof(gDefaultReplacements) / sizeof(gDefaultReplacements[0]); i++) {
if (offsetInResult >= tmpArraySize) {
DEBUG_FUNCTION_LINE("ELEMENTS DO NOT FIT INTO ARRAY");
return offsetInResult;
}
if (gDefaultReplacements[i].lifetime != REPLACEMENT_LIFETIME_INVALID) {
tmpArray[offsetInResult] = &gDefaultReplacements[i];
offsetInResult++;
}
}
for (uint32_t i = 0; i < sizeof(gDynamicReplacements) / sizeof(gDynamicReplacements[0]); i++) {
if (offsetInResult >= tmpArraySize) {
DEBUG_FUNCTION_LINE("ELEMENTS DO NOT FIT INTO ARRAY");
return offsetInResult;
}
if (gDynamicReplacements[i] != NULL) {
tmpArray[offsetInResult] = gDynamicReplacements[i];
offsetInResult++;
}
}
return offsetInResult;
}
const RPXFileReplacements *GetCurrentRPXReplacement(MCPLoadFileRequest *request) {
const RPXFileReplacements *tmpArray[TEMP_ARRAY_SIZE] = {};
uint32_t elementsInArray = getReplacementDataInSingleArray(tmpArray, TEMP_ARRAY_SIZE);
if (elementsInArray == 0) {
return NULL;
}
return GetCurrentRPXReplacementEx(request, tmpArray, elementsInArray);
}
int DoReplacementByStruct(ipcmessage *msg, MCPLoadFileRequest *request, const RPXFileReplacements *curReplacement) {
char _rpxpath[256] = {};
int target = -1;
if (curReplacement->relativeTo == PATH_RELATIVE_TO_ENVIRONMENT) {
char *environmentPath = &((char *) 0x0511FF00)[19];
snprintf(_rpxpath, sizeof(_rpxpath) - 1, "%s/%s", environmentPath, curReplacement->replacementPath); // Copy in environment path
target = LOAD_RPX_TARGET_SD_CARD;
} else if (curReplacement->relativeTo == PATH_RELATIVE_TO_SD_ROOT) {
strncpy(_rpxpath, curReplacement->replacementPath, sizeof(_rpxpath) - 1);
target = LOAD_RPX_TARGET_SD_CARD;
} else {
DEBUG_FUNCTION_LINE("Unknown relativeTo: %d\n", curReplacement->relativeTo);
return -1;
}
DEBUG_FUNCTION_LINE("Load custom file %s\n", _rpxpath);
return MCP_LoadCustomFile(target,
_rpxpath,
curReplacement->fileSize,
curReplacement->fileOffset,
msg->ioctl.buffer_io,
msg->ioctl.length_io,
request->pos, EndsWith(request->name, ".rpx"));
}
int MCPLoadFileReplacement(ipcmessage *msg, MCPLoadFileRequest *request) {
int res;
if ((res = DoSDRedirectionByPath(msg, request)) >= 0) {
// DEBUG_FUNCTION_LINE("We replaced by path!\n");
return res;
}
const RPXFileReplacements *curReplacement = GetCurrentRPXReplacement(request);
if (curReplacement == NULL) {
// DEBUG_FUNCTION_LINE("Couldn't find replacement for %s!\n", request->name);
return -1;
}
if ((res = DoReplacementByStruct(msg, request, curReplacement)) >= 0) {
return res;
}
return res;
}
void IncreaseApplicationStartCounter() {
for (uint32_t i = 0; i < sizeof(gDynamicReplacements) / sizeof(gDynamicReplacements[0]); i++) {
if (gDynamicReplacements[i] != NULL) {
gDynamicReplacements[i]->ageInApplicationStarts++;
}
}
}
bool hasHomebrewReplacementsEx(const RPXFileReplacements **list, uint32_t list_size) {
for (uint32_t i = 0; i < list_size; i++) {
const RPXFileReplacements *cur = list[i];
if (cur != NULL && cur->type != REPLACEMENT_TYPE_INVALID) {
if (cur->type == REPLACEMENT_TYPE_HOMEBREW_RPX) {
return true;
}
}
}
return false;
}
bool hasHomebrewReplacements() {
const RPXFileReplacements *tmpArray[TEMP_ARRAY_SIZE] = {};
uint32_t elementsInArray = getReplacementDataInSingleArray(tmpArray, TEMP_ARRAY_SIZE);
if (elementsInArray == 0) {
return false;
}
if (!hasHomebrewReplacementsEx(tmpArray, elementsInArray)) {
// DEBUG_FUNCTION_LINE("Has no homebrew replacements\n");
return false;
}
return true;
}
int _MCP_LoadFile_patch(ipcmessage *msg) {
MCPLoadFileRequest *request = (MCPLoadFileRequest *) msg->ioctl.buffer_in;
// we only care about Foreground app/COS-MASTER for now.
if (request->cafe_pid != 7) {
// DEBUG_FUNCTION_LINE("Not for pid 7, lets ignore\n");
return real_MCP_LoadFile(msg);
}
bool requestIsRPX = EndsWith(request->name, ".rpx");
// Check if a fresh RPX is loaded
if (request->pos == 0 && requestIsRPX) {
// DEBUG_FUNCTION_LINE("fresh RPX load\n");
// Remove any old replacements that should only survive one RPX replacement
RemoveByLifetime(REPLACEMENT_LIFETIME_DURING_RPX_REPLACEMENT);
// Increase of the all replacements when we load a new RPX!
IncreaseApplicationStartCounter();
sReplacedLastRPX = false;
}
int res;
if ((res = MCPLoadFileReplacement(msg, request)) >= 0) {
if (requestIsRPX) {
sReplacedLastRPX = true;
}
return res;
}
res = real_MCP_LoadFile(msg);
return res;
}
int _MCP_ReadCOSXml_patch(uint32_t u1, uint32_t u2, MCPPPrepareTitleInfo *xmlData) {
int (*const real_MCP_ReadCOSXml_patch)(uint32_t u1, uint32_t u2, MCPPPrepareTitleInfo * xmlData) = (void *) 0x050024ec + 1; //+1 for thumb
@ -210,7 +466,7 @@ int _MCP_ReadCOSXml_patch(uint32_t u1, uint32_t u2, MCPPPrepareTitleInfo *xmlDat
}
// if we replace the RPX we want to increase the max_codesize and give us full permission!
if (replace_valid) {
if (hasHomebrewReplacements()) {
if (xmlData->titleId == 0x000500101004E000 || // H&S
xmlData->titleId == 0x000500101004E100 ||
xmlData->titleId == 0x000500101004E200 ||
@ -245,7 +501,7 @@ int _MCP_ReadCOSXml_patch(uint32_t u1, uint32_t u2, MCPPPrepareTitleInfo *xmlDat
// When the PPC Kernel reboots we replace the men.rpx to set up our PPC side again
// for this the Wii U Menu temporarily gets replaced by our root.rpx and needs code gen access
if (!skipPPCSetup) {
if (!gMemHookCompleted) {
if (xmlData->titleId == 0x0005001010040000 ||
xmlData->titleId == 0x0005001010040100 ||
xmlData->titleId == 0x0005001010040200) {
@ -264,45 +520,50 @@ extern int _startMainThread(void);
/* RPX replacement! Call this ioctl to replace the next loaded RPX with an arbitrary path.
DO NOT RETURN 0, this affects the codepaths back in the IOSU code */
int _MCP_ioctl100_patch(ipcmessage *msg) {
FAIL_ON(!msg->ioctl.buffer_in, 0);
FAIL_ON(!msg->ioctl.length_in, 0);
if (msg->ioctl.buffer_in && msg->ioctl.length_in >= 4) {
int command = msg->ioctl.buffer_in[0];
switch (command) {
case IPC_CUSTOM_MEN_RPX_HOOK_COMPLETED: {
DEBUG_FUNCTION_LINE("IPC_CUSTOM_MEN_RPX_HOOK_COMPLETED\n");
skipPPCSetup = true;
gMemHookCompleted = true;
break;
}
case IPC_CUSTOM_LOAD_CUSTOM_RPX: {
DEBUG_FUNCTION_LINE("IPC_CUSTOM_LOAD_CUSTOM_RPX\n");
if (msg->ioctl.length_in >= 0x110) {
if (msg->ioctl.length_in >= sizeof(MochaRPXLoadInfo) + 4) {
int target = msg->ioctl.buffer_in[0x04 / 0x04];
if (target == LOAD_RPX_TARGET_EXTRA_REVERT_PREPARE) {
doWantReplaceRPX = false;
replace_valid = false;
return 0;
}
if (target != LOAD_RPX_TARGET_SD_CARD) {
return 29;
}
int filesize = msg->ioctl.buffer_in[0x08 / 0x04];
int fileoffset = msg->ioctl.buffer_in[0x0C / 0x04];
char *str_ptr = (char *) &msg->ioctl.buffer_in[0x10 / 0x04];
memset(rpxpath, 0, sizeof(rpxpath));
uint32_t filesize = msg->ioctl.buffer_in[0x08 / 0x04];
uint32_t fileoffset = msg->ioctl.buffer_in[0x0C / 0x04];
char *str_ptr = (char *) &msg->ioctl.buffer_in[0x10 / 0x04];
strncpy(rpxpath, str_ptr, 256 - 1);
RPXFileReplacements *newReplacement = svcAlloc(0xCAFF, sizeof(RPXFileReplacements));
if (newReplacement == NULL) {
DEBUG_FUNCTION_LINE("Failed to allocate memory on heap\n");
return 22;
}
rep_filesize = filesize;
rep_fileoffset = fileoffset;
doWantReplaceRPX = true;
//doWantReplaceXML = true;
replace_valid = true;
memset(newReplacement, 0, sizeof(*newReplacement));
DEBUG_FUNCTION_LINE("Will load %s for next title from target: %d (offset %d, filesize %d)\n", rpxpath, target, rep_fileoffset, rep_filesize);
strncpy(newReplacement->replacementPath, str_ptr, sizeof(newReplacement->replacementPath) - 1);
newReplacement->fileOffset = fileoffset;
newReplacement->fileSize = filesize;
newReplacement->lifetime = REPLACEMENT_LIFETIME_ONE_RPX_LAUNCH;
newReplacement->relativeTo = PATH_RELATIVE_TO_SD_ROOT;
newReplacement->type = REPLACEMENT_TYPE_HOMEBREW_RPX;
if (!addDynamicReplacement(newReplacement)) {
DEBUG_FUNCTION_LINE("addDynamicReplacement failed, abort redirecting %s\n", newReplacement->replacementPath);
svcFree(0xCAFF, newReplacement);
return 22;
}
DEBUG_FUNCTION_LINE("Will load %s for next title from target: %d (offset %u, filesize %u)\n",
newReplacement->replacementPath, target, fileoffset, filesize);
return 0;
} else {
return 29;

View File

@ -8,6 +8,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/unistd.h>
#include <unistd.h>
static int serverKilled;
static int serverSocket;
@ -87,7 +89,7 @@ static int serverCommandHandler(u32 *command_buffer, u32 length) {
u32 n = command_buffer[3];
u32 old = *dst;
int i;
u32 i;
for (i = 0; i < n; i++) {
if (*dst != old) {
if (*dst == 0x0) old = *dst;
@ -167,7 +169,7 @@ static void serverListenClients() {
serverSocket = -1;
}
static int wupserver_thread(void *arg) {
static int wupserver_thread() {
while (ifmgrnclInit() <= 0) {
//print(0, 0, "opening /dev/net/ifmgr/ncl...");
usleep(1000);
@ -192,12 +194,6 @@ static int wupserver_thread(void *arg) {
usleep(1000);
}
#ifdef LOG_IP
log_init(0xc0a8b2a1);
#else
log_init(0xc0a8b2a1);
#endif
//print(0, 0, "opened /dev/socket !");
usleep(5 * 1000 * 1000);
//print(0, 10, "attempting sockets !");
@ -211,7 +207,6 @@ static int wupserver_thread(void *arg) {
usleep(1000 * 1000);
}
log_deinit();
return 0;
}

View File

@ -3,28 +3,39 @@
#include <coreinit/cache.h>
#include <coreinit/debug.h>
#include <coreinit/ios.h>
#include <coreinit/thread.h>
#include <cstdio>
#include <cstring>
#include <mocha/commands.h>
#include <sysapp/title.h>
#include <whb/log.h>
#include <whb/log_udp.h>
static void StartMCPThreadIfMochaAlreadyRunning() {
// start /dev/iosuhax and wupserver if mocha is already running
int mcpFd = IOS_Open("/dev/mcp", (IOSOpenMode) 0);
if (mcpFd >= 0) {
int in = IPC_CUSTOM_START_MCP_THREAD;
int out = 0;
if (IOS_Ioctl(mcpFd, 100, &in, sizeof(in), &out, sizeof(out)) == IOS_ERROR_OK) {
// give /dev/iosuhax a chance to start.
OSSleepTicks(OSMillisecondsToTicks(100));
}
IOS_Close(mcpFd);
}
}
int main(int argc, char **argv) {
WHBLogUdpInit();
WHBLogPrintf("Hello from mocha");
if (argc >= 1) {
if (strncmp(argv[0], "fs:/", 4) == 0) {
strncpy((char *) 0xF417FEF0, argv[0], 0xFF);
DCStoreRange((void *) 0xF417FEF0, 0x100);
}
}
uint64_t sysmenuIdUll = _SYSGetSystemApplicationTitleId(SYSTEM_APP_ID_WII_U_MENU);
memcpy((void *) 0xF417FFF0, &sysmenuIdUll, 8);
DCStoreRange((void *) 0xF417FFF0, 0x8);
StartMCPThreadIfMochaAlreadyRunning();
ExecuteIOSExploit();
// When the kernel exploit is set up successfully, we signal the ios to move on.
@ -39,7 +50,5 @@ int main(int argc, char **argv) {
IOS_Ioctl(mcpFd, 100, &in, sizeof(in), &out, sizeof(out));
IOS_Close(mcpFd);
}
WHBLogPrintf("Bye from mocha");
return 0;
}