Add NAND browsing and dumping, fix system save data ID size, cancel population on list exit.

This commit is contained in:
Steven Smith 2016-04-09 23:56:17 -07:00
parent 1f7f33390d
commit c1021e327e
20 changed files with 364 additions and 10 deletions

@ -1 +1 @@
Subproject commit 3547b54340ed59f670dfe6e97abc19f2c66fc999
Subproject commit b26c7e3ffa4c368de99fe47e9b294ca47264edf7

View File

@ -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();

111
source/patcher/patcher.c Normal file
View File

@ -0,0 +1,111 @@
#include <string.h>
#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);
}

4
source/patcher/patcher.h Normal file
View File

@ -0,0 +1,4 @@
#pragma once
void patch_fs();

View File

@ -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},

View File

@ -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);
}

View File

@ -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);

View File

@ -0,0 +1,158 @@
#include <malloc.h>
#include <stdio.h>
#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));
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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();

View File

@ -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);

View File

@ -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;

View File

@ -60,7 +60,7 @@ typedef struct {
} ext_save_data_info;
typedef struct {
u64 systemSaveDataId;
u32 systemSaveDataId;
} system_save_data_info;
typedef struct {

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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);