From c1021e327ea2905263410f78e3185f8b7cef9380 Mon Sep 17 00:00:00 2001 From: Steven Smith Date: Sat, 9 Apr 2016 23:56:17 -0700 Subject: [PATCH] Add NAND browsing and dumping, fix system save data ID size, cancel population on list exit. --- buildtools | 2 +- source/main.c | 3 + source/patcher/patcher.c | 111 ++++++++++++ source/patcher/patcher.h | 4 + source/ui/mainmenu.c | 5 +- .../ui/section/action/browsesystemsavedata.c | 2 +- .../ui/section/action/deletesystemsavedata.c | 2 +- source/ui/section/dumpnand.c | 158 ++++++++++++++++++ source/ui/section/extsavedata.c | 9 + source/ui/section/files.c | 19 +++ source/ui/section/pendingtitles.c | 9 + source/ui/section/section.h | 3 + source/ui/section/systemsavedata.c | 9 + source/ui/section/task/listsystemsavedata.c | 8 +- source/ui/section/task/task.h | 2 +- source/ui/section/tickets.c | 9 + source/ui/section/titles.c | 9 + source/ui/ui.c | 2 +- source/util.c | 7 + source/util.h | 1 + 20 files changed, 364 insertions(+), 10 deletions(-) create mode 100644 source/patcher/patcher.c create mode 100644 source/patcher/patcher.h create mode 100644 source/ui/section/dumpnand.c diff --git a/buildtools b/buildtools index 3547b54..b26c7e3 160000 --- a/buildtools +++ b/buildtools @@ -1 +1 @@ -Subproject commit 3547b54340ed59f670dfe6e97abc19f2c66fc999 +Subproject commit b26c7e3ffa4c368de99fe47e9b294ca47264edf7 diff --git a/source/main.c b/source/main.c index 5c7ca19..431e52c 100644 --- a/source/main.c +++ b/source/main.c @@ -6,6 +6,7 @@ #include "screen.h" #include "util.h" #include "libkhax/khax.h" +#include "patcher/patcher.h" #include "ui/mainmenu.h" #include "ui/section/action/clipboard.h" #include "ui/section/task/task.h" @@ -44,6 +45,8 @@ int main(int argc, const char* argv[]) { } } + patch_fs(); + aptOpenSession(); Result setCpuTimeRes = APT_SetAppCpuTimeLimit(30); aptCloseSession(); diff --git a/source/patcher/patcher.c b/source/patcher/patcher.c new file mode 100644 index 0000000..b752966 --- /dev/null +++ b/source/patcher/patcher.c @@ -0,0 +1,111 @@ +#include + +#include <3ds.h> + +#include "patcher.h" + +#pragma pack(1) +typedef struct KBlockInfo { + u32 section_start; + u32 page_count; +} KBlockInfo; + +typedef struct KLinkedListNode { + struct KLinkedListNode* next; + struct KLinkedListNode* prev; + void* data; +} KLinkedListNode; + +typedef struct MemSectionInfo { + u8 padding[0x0C - 0x00]; + KLinkedListNode* first_node; + KLinkedListNode* last_node; +} MemSectionInfo; + +typedef struct KCodeSet { + u8 padding0[0x08 - 0x00]; + MemSectionInfo text_info; + u8 padding1[0x64 - 0x1C]; +} KCodeSet; +#pragma pack(0) + +static u32 kernel_version = 0; +static bool n3ds = false; + +s32 kernel_patch_fs() { + asm volatile("cpsid aif"); + + u32 processSize = 0; + u32 processCodeSetOffset = 0; + u32 processPidOffset = 0; + + if(kernel_version < 0x022C0600) { + processSize = 0x260; + processCodeSetOffset = 0xA8; + processPidOffset = 0xAC; + } else if(n3ds) { + processSize = 0x270; + processCodeSetOffset = 0xB8; + processPidOffset = 0xBC; + } else { + processSize = 0x268; + processCodeSetOffset = 0xB0; + processPidOffset = 0xB4; + } + + u32 currProcessPtr = *(u32*) 0xFFFF9004; + u32 vtablePtr = *(u32*) currProcessPtr; + + for(u32 processPtr = currProcessPtr; *(u32*) processPtr == vtablePtr; processPtr -= processSize) { + if(*(u32*) (processPtr + processPidOffset) == 0) { + KCodeSet* codeSet = *(KCodeSet**) (processPtr + processCodeSetOffset); + if(codeSet != NULL) { + // Patches out an archive access check. + u8 original[] = {0x0C, 0x05, 0x0C, 0x33, 0x46, 0xF7, 0xF7, 0x49, 0xF8, 0x00, 0x28, 0x01, 0xD0, 0x00, 0x20, 0xF8}; + u8 patched[] = {0x0C, 0x05, 0x0C, 0x33, 0x46, 0x01, 0x20, 0x00, 0x00, 0x00, 0x28, 0x01, 0xD0, 0x00, 0x20, 0xF8}; + + for(KLinkedListNode* node = codeSet->text_info.first_node; node != NULL; node = node->next) { + KBlockInfo* blockInfo = (KBlockInfo*) node->data; + u32 blockSize = blockInfo->page_count * 0x1000; + + bool done = false; + for(u32 i = 0; i <= blockSize - sizeof(original); i++) { + u8* dst = (u8*) (blockInfo->section_start + i); + + bool equal = true; + for(u32 b = 0; b < sizeof(original); b++) { + if(dst[b] != original[b]) { + equal = false; + break; + } + } + + if(equal) { + for(u32 b = 0; b < sizeof(patched); b++) { + dst[b] = patched[b]; + } + + done = true; + break; + } + } + + if(done || node == codeSet->text_info.last_node) { + break; + } + } + } + + break; + } + } + + return 0; +} + +void patch_fs() { + kernel_version = *(vu32*) 0x1FF80000; + APT_CheckNew3DS((u8*) &n3ds); + + svcBackdoor(kernel_patch_fs); +} \ No newline at end of file diff --git a/source/patcher/patcher.h b/source/patcher/patcher.h new file mode 100644 index 0000000..fb662e5 --- /dev/null +++ b/source/patcher/patcher.h @@ -0,0 +1,4 @@ +#pragma once + +void patch_fs(); + diff --git a/source/ui/mainmenu.c b/source/ui/mainmenu.c index 4567e82..57b87f1 100644 --- a/source/ui/mainmenu.c +++ b/source/ui/mainmenu.c @@ -8,11 +8,14 @@ #include "section/section.h" #include "../screen.h" -#define MAINMENU_ITEM_COUNT 8 +#define MAINMENU_ITEM_COUNT 11 static u32 mainmenu_item_count = MAINMENU_ITEM_COUNT; static list_item mainmenu_items[MAINMENU_ITEM_COUNT] = { {"SD", 0xFF000000, files_open_sd}, + {"CTR NAND", 0xFF000000, files_open_ctr_nand}, + {"TWL NAND", 0xFF000000, files_open_twl_nand}, + {"Dump NAND", 0xFF000000, dump_nand}, {"Titles", 0xFF000000, titles_open}, {"Pending Titles", 0xFF000000, pendingtitles_open}, {"Tickets", 0xFF000000, tickets_open}, diff --git a/source/ui/section/action/browsesystemsavedata.c b/source/ui/section/action/browsesystemsavedata.c index db6bc2b..908618b 100644 --- a/source/ui/section/action/browsesystemsavedata.c +++ b/source/ui/section/action/browsesystemsavedata.c @@ -4,7 +4,7 @@ #include "../section.h" void action_browse_system_save_data(system_save_data_info* info, bool* populated) { - u32 path[2] = {MEDIATYPE_NAND, (u32) (info->systemSaveDataId & 0xFFFFFFFF)}; + u32 path[2] = {MEDIATYPE_NAND, info->systemSaveDataId}; FS_Archive archive = {ARCHIVE_SYSTEM_SAVEDATA, {PATH_BINARY, 8, path}}; files_open(archive); } \ No newline at end of file diff --git a/source/ui/section/action/deletesystemsavedata.c b/source/ui/section/action/deletesystemsavedata.c index a9ccc6d..d322ee6 100644 --- a/source/ui/section/action/deletesystemsavedata.c +++ b/source/ui/section/action/deletesystemsavedata.c @@ -23,7 +23,7 @@ static void action_delete_system_save_data_success_onresponse(ui_view* view, voi static void action_delete_system_save_data_update(ui_view* view, void* data, float* progress, char* progressText) { delete_system_save_data_data* deleteData = (delete_system_save_data_data*) data; - FS_SystemSaveDataInfo sysInfo = *(FS_SystemSaveDataInfo*) &deleteData->info->systemSaveDataId; + FS_SystemSaveDataInfo sysInfo = {.mediaType = MEDIATYPE_NAND, .saveId = deleteData->info->systemSaveDataId}; Result res = FSUSER_DeleteSystemSaveData(sysInfo); progressbar_destroy(view); diff --git a/source/ui/section/dumpnand.c b/source/ui/section/dumpnand.c new file mode 100644 index 0000000..17a8a11 --- /dev/null +++ b/source/ui/section/dumpnand.c @@ -0,0 +1,158 @@ +#include +#include + +#include <3ds.h> + +#include "../error.h" +#include "../progressbar.h" +#include "../prompt.h" + +typedef struct { + Handle in; + Handle out; + u64 offset; + u64 size; + + u8 buffer[1024 * 1024]; +} dump_nand_data; + +static void dumpnand_done_onresponse(ui_view* view, void* data, bool response) { + prompt_destroy(view); +} + +static void dumpnand_update(ui_view* view, void* data, float* progress, char* progressText) { + dump_nand_data* dumpData = (dump_nand_data*) data; + + if(hidKeysDown() & KEY_B) { + if(dumpData->in != 0) { + FSFILE_Close(dumpData->in); + dumpData->in = 0; + } + + if(dumpData->out != 0) { + FSFILE_Close(dumpData->out); + dumpData->out = 0; + } + + progressbar_destroy(view); + ui_pop(); + + ui_push(prompt_create("Failure", "Dump cancelled.", 0xFF000000, false, data, NULL, NULL, dumpnand_done_onresponse)); + + free(data); + + return; + } + + Result res = 0; + + u32 bytesRead = 0; + u32 bytesWritten = 0; + if(R_SUCCEEDED(res = FSFILE_Read(dumpData->in, &bytesRead, dumpData->offset, dumpData->buffer, sizeof(dumpData->buffer))) && R_SUCCEEDED(res = FSFILE_Write(dumpData->out, &bytesWritten, dumpData->offset, dumpData->buffer, bytesRead, 0)) && bytesRead == bytesWritten) { + dumpData->offset += bytesRead; + } + + if(R_FAILED(res)) { + if(dumpData->in != 0) { + FSFILE_Close(dumpData->in); + dumpData->in = 0; + } + + if(dumpData->out != 0) { + FSFILE_Close(dumpData->out); + dumpData->out = 0; + } + + progressbar_destroy(view); + ui_pop(); + + error_display_res(NULL, NULL, res, "Failed to dump NAND."); + + free(data); + } else if(bytesRead != bytesWritten) { + if(dumpData->in != 0) { + FSFILE_Close(dumpData->in); + dumpData->in = 0; + } + + if(dumpData->out != 0) { + FSFILE_Close(dumpData->out); + dumpData->out = 0; + } + + progressbar_destroy(view); + ui_pop(); + + error_display(NULL, NULL, "Failed to dump NAND: Read/Write size mismatch."); + + free(data); + } else if(dumpData->offset >= dumpData->size) { + if(dumpData->in != 0) { + FSFILE_Close(dumpData->in); + dumpData->in = 0; + } + + if(dumpData->out != 0) { + FSFILE_Close(dumpData->out); + dumpData->out = 0; + } + + progressbar_destroy(view); + ui_pop(); + + ui_push(prompt_create("Success", "NAND dumped.", 0xFF000000, false, NULL, NULL, NULL, dumpnand_done_onresponse)); + + free(data); + } else { + *progress = (float) dumpData->offset / (float) dumpData->size; + snprintf(progressText, PROGRESS_TEXT_MAX, "%.2f MB / %.2f MB", dumpData->offset / 1024.0f / 1024.0f, dumpData->size / 1024.0f / 1024.0f); + } +} + +static void dumpnand_onresponse(ui_view* view, void* data, bool response) { + prompt_destroy(view); + + if(response) { + dump_nand_data* dumpData = (dump_nand_data*) data; + + Result res = 0; + + FS_Archive wnandArchive = {ARCHIVE_NAND_W_FS, fsMakePath(PATH_EMPTY, "")}; + if(R_SUCCEEDED(res = FSUSER_OpenFileDirectly(&dumpData->in, wnandArchive, fsMakePath(PATH_UTF16, u"/"), FS_OPEN_READ, 0)) && R_SUCCEEDED(res = FSFILE_GetSize(dumpData->in, &dumpData->size))) { + FS_Archive sdmcArchive = {ARCHIVE_SDMC, {PATH_BINARY, 0, (u8*) ""}}; + if(R_SUCCEEDED(res = FSUSER_OpenFileDirectly(&dumpData->out, sdmcArchive, fsMakePath(PATH_ASCII, "/NAND.bin"), FS_OPEN_CREATE | FS_OPEN_WRITE, 0))) { + dumpData->offset = 0; + + ui_push(progressbar_create("Dumping NAND", "Press B to cancel.", data, dumpnand_update, NULL)); + } + } + + if(R_FAILED(res)) { + if(dumpData->in != 0) { + FSFILE_Close(dumpData->in); + dumpData->in = 0; + } + + if(dumpData->out != 0) { + FSFILE_Close(dumpData->out); + dumpData->out = 0; + } + + error_display_res(NULL, NULL, res, "Failed to prepare for NAND dump."); + + free(data); + } + } else { + free(data); + } +} + +void dump_nand() { + dump_nand_data* data = (dump_nand_data*) calloc(1, sizeof(dump_nand_data)); + data->in = 0; + data->out = 0; + data->offset = 0; + data->size = 0; + + ui_push(prompt_create("Confirmation", "Dump raw NAND image to the SD card?", 0xFF000000, true, data, NULL, NULL, dumpnand_onresponse)); +} \ No newline at end of file diff --git a/source/ui/section/extsavedata.c b/source/ui/section/extsavedata.c index bf2ff10..56c569d 100644 --- a/source/ui/section/extsavedata.c +++ b/source/ui/section/extsavedata.c @@ -83,6 +83,15 @@ static void extsavedata_update(ui_view* view, void* data, list_item** items, u32 extsavedata_data* listData = (extsavedata_data*) data; if(hidKeysDown() & KEY_B) { + if(listData->cancelEvent != 0) { + svcSignalEvent(listData->cancelEvent); + while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) { + svcSleepThread(1000000); + } + + listData->cancelEvent = 0; + } + ui_pop(); free(listData); list_destroy(view); diff --git a/source/ui/section/files.c b/source/ui/section/files.c index 2181cea..22f3f7e 100644 --- a/source/ui/section/files.c +++ b/source/ui/section/files.c @@ -178,6 +178,15 @@ static void files_update(ui_view* view, void* data, list_item** items, u32** ite listData->archivePath = NULL; } + if(listData->cancelEvent != 0) { + svcSignalEvent(listData->cancelEvent); + while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) { + svcSleepThread(1000000); + } + + listData->cancelEvent = 0; + } + ui_pop(); free(listData); list_destroy(view); @@ -256,4 +265,14 @@ void files_open(FS_Archive archive) { void files_open_sd() { FS_Archive sdmcArchive = {ARCHIVE_SDMC, {PATH_BINARY, 0, (void*) ""}}; files_open(sdmcArchive); +} + +void files_open_ctr_nand() { + FS_Archive sdmcArchive = {ARCHIVE_NAND_CTR_FS, fsMakePath(PATH_EMPTY, "")}; + files_open(sdmcArchive); +} + +void files_open_twl_nand() { + FS_Archive sdmcArchive = {ARCHIVE_NAND_TWL_FS, fsMakePath(PATH_EMPTY, "")}; + files_open(sdmcArchive); } \ No newline at end of file diff --git a/source/ui/section/pendingtitles.c b/source/ui/section/pendingtitles.c index cd4b44f..183a04c 100644 --- a/source/ui/section/pendingtitles.c +++ b/source/ui/section/pendingtitles.c @@ -82,6 +82,15 @@ static void pendingtitles_update(ui_view* view, void* data, list_item** items, u pendingtitles_data* listData = (pendingtitles_data*) data; if(hidKeysDown() & KEY_B) { + if(listData->cancelEvent != 0) { + svcSignalEvent(listData->cancelEvent); + while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) { + svcSleepThread(1000000); + } + + listData->cancelEvent = 0; + } + ui_pop(); free(listData); list_destroy(view); diff --git a/source/ui/section/section.h b/source/ui/section/section.h index b3672b6..b14dac3 100644 --- a/source/ui/section/section.h +++ b/source/ui/section/section.h @@ -2,9 +2,12 @@ #include "../ui.h" +void dump_nand(); void extsavedata_open(); void files_open(FS_Archive archive); void files_open_sd(); +void files_open_ctr_nand(); +void files_open_twl_nand(); void networkinstall_open(FS_MediaType dest); void networkinstall_open_sd(); void networkinstall_open_nand(); diff --git a/source/ui/section/systemsavedata.c b/source/ui/section/systemsavedata.c index d6e6d7d..a8dde93 100644 --- a/source/ui/section/systemsavedata.c +++ b/source/ui/section/systemsavedata.c @@ -82,6 +82,15 @@ static void systemsavedata_update(ui_view* view, void* data, list_item** items, systemsavedata_data* listData = (systemsavedata_data*) data; if(hidKeysDown() & KEY_B) { + if(listData->cancelEvent != 0) { + svcSignalEvent(listData->cancelEvent); + while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) { + svcSleepThread(1000000); + } + + listData->cancelEvent = 0; + } + ui_pop(); free(listData); list_destroy(view); diff --git a/source/ui/section/task/listsystemsavedata.c b/source/ui/section/task/listsystemsavedata.c index a554120..4ab4a07 100644 --- a/source/ui/section/task/listsystemsavedata.c +++ b/source/ui/section/task/listsystemsavedata.c @@ -25,10 +25,10 @@ static void task_populate_system_save_data_thread(void* arg) { Result res = 0; u32 systemSaveDataCount = 0; - u64* systemSaveDataIds = (u64*) calloc(data->max, sizeof(u64)); + u32* systemSaveDataIds = (u32*) calloc(data->max, sizeof(u32)); if(systemSaveDataIds != NULL) { - if(R_SUCCEEDED(res = FSUSER_EnumerateSystemSaveData(&systemSaveDataCount, data->max * sizeof(u64), systemSaveDataIds))) { - qsort(systemSaveDataIds, systemSaveDataCount, sizeof(u64), util_compare_u64); + if(R_SUCCEEDED(res = FSUSER_EnumerateSystemSaveData(&systemSaveDataCount, data->max * sizeof(u32), (u64*) systemSaveDataIds))) { + qsort(systemSaveDataIds, systemSaveDataCount, sizeof(u32), util_compare_u64); for(u32 i = 0; i < systemSaveDataCount && i < data->max; i++) { if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { @@ -40,7 +40,7 @@ static void task_populate_system_save_data_thread(void* arg) { systemSaveDataInfo->systemSaveDataId = systemSaveDataIds[i]; list_item* item = &data->items[*data->count]; - snprintf(item->name, NAME_MAX, "%016llX", systemSaveDataIds[i]); + snprintf(item->name, NAME_MAX, "%08lX", systemSaveDataIds[i]); item->rgba = 0xFF000000; item->data = systemSaveDataInfo; diff --git a/source/ui/section/task/task.h b/source/ui/section/task/task.h index eef4966..1656564 100644 --- a/source/ui/section/task/task.h +++ b/source/ui/section/task/task.h @@ -60,7 +60,7 @@ typedef struct { } ext_save_data_info; typedef struct { - u64 systemSaveDataId; + u32 systemSaveDataId; } system_save_data_info; typedef struct { diff --git a/source/ui/section/tickets.c b/source/ui/section/tickets.c index 8e535fd..066d3ee 100644 --- a/source/ui/section/tickets.c +++ b/source/ui/section/tickets.c @@ -81,6 +81,15 @@ static void tickets_update(ui_view* view, void* data, list_item** items, u32** i tickets_data* listData = (tickets_data*) data; if(hidKeysDown() & KEY_B) { + if(listData->cancelEvent != 0) { + svcSignalEvent(listData->cancelEvent); + while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) { + svcSleepThread(1000000); + } + + listData->cancelEvent = 0; + } + ui_pop(); free(listData); list_destroy(view); diff --git a/source/ui/section/titles.c b/source/ui/section/titles.c index 3789dad..fa3299c 100644 --- a/source/ui/section/titles.c +++ b/source/ui/section/titles.c @@ -85,6 +85,15 @@ static void titles_update(ui_view* view, void* data, list_item** items, u32** it titles_data* listData = (titles_data*) data; if(hidKeysDown() & KEY_B) { + if(listData->cancelEvent != 0) { + svcSignalEvent(listData->cancelEvent); + while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) { + svcSleepThread(1000000); + } + + listData->cancelEvent = 0; + } + ui_pop(); free(listData); list_destroy(view); diff --git a/source/ui/ui.c b/source/ui/ui.c index 99f38ee..2f1b193 100644 --- a/source/ui/ui.c +++ b/source/ui/ui.c @@ -461,7 +461,7 @@ void ui_draw_system_save_data_info(ui_view* view, void* data, float x1, float y1 char buf[64]; - snprintf(buf, 64, "System Save Data ID: %016llX", info->systemSaveDataId); + snprintf(buf, 64, "System Save Data ID: %08lX", info->systemSaveDataId); float saveDataIdWidth; float saveDataIdHeight; diff --git a/source/util.c b/source/util.c index 3aa4d6d..7339e37 100644 --- a/source/util.c +++ b/source/util.c @@ -404,6 +404,13 @@ Result util_ensure_dir(FS_Archive* archive, const char* path) { return res; } +int util_compare_u32(const void* e1, const void* e2) { + u32 id1 = *(u32*) e1; + u32 id2 = *(u32*) e2; + + return id1 > id2 ? 1 : id1 < id2 ? -1 : 0; +} + int util_compare_u64(const void* e1, const void* e2) { u64 id1 = *(u64*) e1; u64 id2 = *(u64*) e2; diff --git a/source/util.h b/source/util.h index dac7d28..24abdde 100644 --- a/source/util.h +++ b/source/util.h @@ -42,5 +42,6 @@ void util_get_path_file(char* out, const char* path, u32 size); void util_get_parent_path(char* out, const char* path, u32 size); Result util_ensure_dir(FS_Archive* archive, const char* path); +int util_compare_u32(const void* e1, const void* e2); int util_compare_u64(const void* e1, const void* e2); int util_compare_directory_entries(const void* e1, const void* e2); \ No newline at end of file