diff --git a/apps/photo_upgrader/meta.xml b/apps/photo_upgrader/meta.xml new file mode 100644 index 0000000..c894b7d --- /dev/null +++ b/apps/photo_upgrader/meta.xml @@ -0,0 +1,11 @@ + + + photo_upgrader + 0.9.0 + + thepikachugamer + + + + + diff --git a/include/tools.h b/include/tools.h index 902f054..7a8f064 100644 --- a/include/tools.h +++ b/include/tools.h @@ -24,6 +24,9 @@ void init_video(int row, int col); uint16_t input_scan(uint16_t value); int quit(int ret); +int net_init_retry(unsigned int retries); +int get_title_rev(const uint64_t tid); +unsigned int check_title(const uint64_t tid); bool check_dolphin(void); bool check_vwii(void); diff --git a/photo_upgrader.zip b/photo_upgrader.zip index feda510..f15c305 100644 Binary files a/photo_upgrader.zip and b/photo_upgrader.zip differ diff --git a/source/main.c b/source/main.c index 914b917..8e90087 100644 --- a/source/main.c +++ b/source/main.c @@ -24,6 +24,12 @@ const char header[] = "Photo Channel 1.1 installer v" VERSION ", by thepikachuga bool offline_mode = true; static fstats file_stats ATTRIBUTE_ALIGN(32); + +const uint64_t + tid = 0x0001000248415941LL, + tid_new = 0x0001000248414141LL, + tid_stub = 0x0001000048415A41LL; + const aeskey wii_ckey = {0xEB, 0xE4, 0x2A, 0x22, 0x5E, 0x85, 0x93, 0xE4, 0x48, 0xD9, 0xC5, 0x45, 0x73, 0x81, 0xAA, 0xF7}; void* NUS_Download(const uint64_t tid, const char* obj, unsigned int* size, int* ec) { @@ -53,16 +59,15 @@ void* NUS_Download(const uint64_t tid, const char* obj, unsigned int* size, int* } void* FS_Read(const char* filepath, unsigned int* filesize, int* ret) { - *filesize = 0; *ret = ISFS_Open(filepath, ISFS_OPEN_READ); if(*ret < 0) return NULL; int fd = *ret; - - *ret = ISFS_GetFileStats(fd, &file_stats); - if(*ret < 0) return NULL; - *filesize = file_stats.file_length; - + if(! *filesize) { + *ret = ISFS_GetFileStats(fd, &file_stats); + if(*ret < 0) return NULL; + *filesize = file_stats.file_length; + } unsigned char *buffer = memalign(0x20, *filesize); if(!buffer) { *ret = -ENOMEM; @@ -85,296 +90,213 @@ signed_blob* fetch_tmd(const uint64_t tid, unsigned int* size, int* ret) { signed_blob* s_tmd = NULL; if(offline_mode) { - printf("> Reading %016llx title metadata... ", tid); *ret = ES_GetStoredTMDSize(tid, size); - if(*ret < 0) { - printf("failed! (%d)\n", *ret); - return NULL; - } + if(*ret < 0) return NULL; + s_tmd = memalign(0x20, *size); if(!s_tmd) { - printf("failed! (No memory? [%u bytes])\n", *size); *ret = -ENOMEM; return NULL; } + *ret = ES_GetStoredTMD(tid, s_tmd, *size); - if(! *ret) { - printf("OK! (size = %u)\n", *size); - return s_tmd; - } - else { + if(*ret < 0) { free(s_tmd); - printf("failed! (%d)\n", *ret); return NULL; } + + return s_tmd; } else { - printf("> Downloading %016llx title metadata... ", tid); unsigned char* _tmd = NUS_Download(tid, "tmd", size, ret); - if(*ret < 0) { - printf("failed! (%d)\n", (*ret < -117000) ? abs(*ret + 117000) : *ret); - return NULL; - } + if(*ret < 0) return NULL; s_tmd = memalign(0x20, *size); - if(!s_tmd) { - printf("failed! (No memory? [%u bytes])\n", *size); - *ret = -ENOMEM; - } - else { + if(s_tmd) memcpy(s_tmd, _tmd, *size); - printf("OK! (size = %u)\n", *size); - } + else + *ret = -ENOMEM; + free(_tmd); + return s_tmd; } - - return s_tmd; } signed_blob* fetch_tik(const uint64_t tid, unsigned int* size, int* ret) { signed_blob* s_tik; if(offline_mode) { - printf("> Reading %016llx ticket... ", tid); char filepath[30]; sprintf(filepath, "/ticket/%08x/%08x.tik", tid_hi(tid), tid_lo(tid)); - s_tik = FS_Read(filepath, size, ret); - if(*ret < 0) - printf("failed! (%d)\n", *ret); - else - printf("OK! (size = %u)\n", *size); - - return s_tik; + return FS_Read(filepath, size, ret); } else { - printf("> Downloading %016llx ticket... ", tid); unsigned char* _tik = NUS_Download(tid, "cetk", size, ret); - if(*ret < 0) { - printf("failed! (%d)\n", (*ret + 117000) > -1000 ? abs(*ret + 117000) : *ret); - return NULL; - } + if(*ret < 0) return NULL; s_tik = memalign(0x20, *size); - if(!s_tik) { - printf("failed! (No memory? [%u bytes])\n", *size); - *ret = -ENOMEM; - } - else { + if (s_tik) memcpy(s_tik, _tik, *size); - printf("OK! (size = %u)\n", *size); - } + else + *ret = -ENOMEM; free(_tik); return s_tik; } } +void get_titlekey(tik* ticket, aeskey out) { + struct AES_ctx aes = {}; + aeskey iv = {}; + memcpy(out, ticket->cipher_title_key, sizeof(aeskey)); + *(uint64_t*) iv = ticket->titleid; -int main() { - const uint64_t - tid = 0x0001000248415941LL, - tid_new = 0x0001000248414141LL, - tid_stub = 0x0001000048415A41LL; + AES_init_ctx_iv(&aes, wii_ckey, iv); + AES_CBC_decrypt_buffer(&aes, out, sizeof(aeskey)); +} - int ret = 0; +void change_tid(tmd* tmd, tik* ticket, const uint64_t tid_new, aeskey title_key) { + struct AES_ctx aes = {}; + aeskey iv = {}; - unsigned int - certs_size = 0, - tmd_size = 0, - tik_size = 0, - cnt = 0, - _cnt = 0; + ticket->titleid = tid_new; + *(uint64_t*) iv = ticket->titleid; - signed_blob - *s_certs = NULL, - *s_tmd = NULL, - *s_tik = NULL; + AES_init_ctx_iv(&aes, wii_ckey, iv); + AES_CBC_encrypt_buffer(&aes, title_key, sizeof(aeskey)); + memcpy(ticket->cipher_title_key, title_key, sizeof(aeskey)); - struct AES_ctx AESctx_titlekey = {}, AESctx_title = {}; + tmd->title_id = tid_new; +} - init_video(2, 0); - printf(header); +int purge_title(const uint64_t tid) { + unsigned int viewcnt = 0; + int ret; - printf("Applying IOS patches... "); - ret = IosPatch_RUNTIME(true, false, true, false); - if (ret < 0) { - printf("Failed! (%d)" "\n" - "Is your Homebrew Channel updated?" "\n" - "Is in meta.xml?" "\n\n" + ret = ES_GetNumTicketViews(tid, &viewcnt); + if (!viewcnt) return ret ? ret : ENOENT; - "Exiting in 5s...", ret); - sleep(5); - return 0x0D800064; + tikview + view ATTRIBUTE_ALIGN(0x20) = {}, + views[viewcnt] ATTRIBUTE_ALIGN(0x20) = {}; + + ret = ES_GetTicketViews(tid, views, viewcnt); + if (ret < 0) return ret; + for(unsigned int i = 0; i < viewcnt; i++) { + memcpy(&view, views + i, sizeof(tikview)); + ret = ES_DeleteTicket(&view); + if (ret < 0) return ret; } - printf("OK! (patch count = %d)\n\n", ret); - WPAD_Init(); - PAD_Init(); - ISFS_Initialize(); + ES_DeleteTitleContent(tid); + return ES_DeleteTitle(tid); // not fatal enough to matter tbh +} + +int install() { + int ret = 0; + unsigned int certs_size = 0, tmd_size = 0, tik_size = 0; - ES_GetNumTicketViews(tid_stub, &_cnt); - if(_cnt) { - printf("This console owns the Photo Channel 1.1 stub.\n" "Retrieve it at the Wii Shop Channel.\n"); - return quit(0); + signed_blob *s_certs = NULL, *s_tmd = NULL, *s_tik = NULL; + + struct AES_ctx AESctx_title = {}; + + ret = get_title_rev(tid_new); + if(ret > 2) { + printf( + "You already did this?" "\n" + "(Photo channel 1.0 title version %u is > 2)" "\n", ret); + return 0; + } + + ret = check_title(tid_stub); + if (ret) { + printf( ret >= 2 ? + "Photo channel 1.1 stub is already installed, do you need something..?\n" : + "You own Photo Channel 1.1 in the Wii Shop Channel, maybe you want to get it?\n"); + return 0; } - ES_GetTitleContentsCount(tid, &cnt); - if (!cnt) { + if (check_title(tid) >= 2) + printf("HAYA is present, using offline mode.\n\n"); + + else { printf("HAYA is not present, using online mode.\n"); printf("> Initializing network... "); - - for (int s = 0; s < 5; s++) { - ret = net_init(); - if(!ret || ret != -EAGAIN) break; - } + ret = net_init_retry(5); if (ret < 0) { printf( - "failed! (%d)\n\n" - "Try grab a Photo Channel 1.1 WAD from NUS Downloader and install it.\n" - "This will work around the internet requirement.\n", ret); - return quit(ret); + "failed! (%d)" "\n\n" + + "Try grab a Photo Channel 1.1 WAD from NUS Downloader and install it." "\n" + "This will work around the internet requirement." "\n", ret); + return ret; } + uint32_t hostip = net_gethostip(); printf("OK! Wii IP address: %hhu.%hhu.%hhu.%hhu\n\n", hostip >>24, hostip >>16, hostip >>8, hostip >>0); - offline_mode = false; } - else - printf("HAYA is present, using offline mode.\n\n"); if(check_dolphin()) printf("\x1b[41m This is Dolphin Emulator, expect -2011. \x1b[40m\n"); printf( - "This application will install the hidden Photo Channel 1.1" "\n" + "This will install the hidden Photo Channel 1.1" "\n" "title directly over Photo Channel 1.0." "\n\n" - "Is this OK?" "\n" - ); + "Is this OK?" "\n"); sleep(3); - if(!confirmation()) return quit(2976579765); + if(!confirmation()) return 2976579765; printf("\n> Reading certs... "); s_certs = FS_Read("/sys/cert.sys", &certs_size, &ret); if (ret < 0) { printf("failed! (%d)\n", ret); - return quit(ret); + return ret; } - printf("OK! (size = %u)\n", certs_size); + printf("> Reading TMD... "); s_tmd = fetch_tmd(tid, &tmd_size, &ret); - if(ret < 0) return quit(ret); - + if (ret < 0) { + printf("failed! (%d)\n", ret); + return ret; + } + printf("OK! (size = %u)\n", tmd_size); tmd* p_tmd = SIGNATURE_PAYLOAD(s_tmd); + printf("> Reading ticket... "); s_tik = fetch_tik(tid, &tik_size, &ret); - if(ret < 0) return quit(ret); - + if (ret < 0) { + printf("failed! (%d)\n", ret); + return ret; + } + printf("OK! (size = %u)\n", tik_size); tik* p_tik = SIGNATURE_PAYLOAD(s_tik); printf("> Changing Title ID... "); - - aeskey cipher_tkey = {}, title_key = {}, iv = {}; - memcpy(title_key, p_tik->cipher_title_key, sizeof(aeskey)); - *(uint64_t*) iv = p_tik->titleid; - - AES_init_ctx_iv(&AESctx_titlekey, wii_ckey, iv); - AES_CBC_decrypt_buffer(&AESctx_titlekey, title_key, sizeof(aeskey)); - + aeskey title_key = {}; + get_titlekey(p_tik, title_key); AES_init_ctx(&AESctx_title, title_key); - memcpy(cipher_tkey, title_key, sizeof(aeskey)); - - p_tik->titleid = tid_new; - *(uint64_t*) iv = p_tik->titleid; - AES_ctx_set_iv(&AESctx_titlekey, iv); - AES_CBC_encrypt_buffer(&AESctx_titlekey, cipher_tkey, sizeof(aeskey)); - - memcpy(p_tik->cipher_title_key, cipher_tkey, sizeof(aeskey)); - - p_tmd->title_id = tid_new; + change_tid(p_tmd, p_tik, tid_new, title_key); printf("OK!\n"); - if(check_vwii()) { - printf("* This seems to be a vWii, setting IOS to 58.\n"); - p_tmd->sys_version = 0x000000010000003ALL; - } - -/* printf("> Faking signatures... "); + if(check_vwii()) p_tmd->sys_version = 1LL << 32 | 58; - memset(SIGNATURE_SIG(s_tik), 0, SIGNATURE_SIZE(s_tik) - 4); - memset(SIGNATURE_SIG(s_tmd), 0, SIGNATURE_SIZE(s_tmd) - 4); - - sha1 tmdhash, tikhash; - short int filler = 0; - for(; filler < ((1 << (sizeof(filler) * 8) ) - 1); filler++) { - if(tmdhash[0]) { - p_tmd->fill3 = filler; - SHA1((unsigned char*)p_tmd, TMD_SIZE(p_tmd), tmdhash); - } - if(tikhash[0]) { - p_tik->padding = filler; - SHA1((unsigned char*)p_tik, sizeof(tik), tikhash); - } - if(!tmdhash[0] && !tikhash[0]) break; - } - if(tmdhash[0] || tikhash[0]) { - printf("failed! What?\n"); - return quit(~1); - } - printf("OK!\n"); -*/ printf("> Removing Photo Channel 1.0... "); - tikview - *views = NULL, - view ATTRIBUTE_ALIGN(0x20) = {}; - unsigned int viewcnt = 0; - - ret = ES_GetNumTicketViews(tid_new, &viewcnt); - if (ret < 0) { - printf("failed! (get view count, %d)\n", ret); - return quit(ret); - } - - if(viewcnt) { - views = memalign(0x20, sizeof(tikview) * viewcnt); - if (!views) { - printf("No memory?\n"); - return -ENOMEM; - } - - ret = ES_GetTicketViews(tid_new, views, viewcnt); - if (ret < 0) { - printf("failed! (get views, %d)\n", ret); - return quit(ret); - } - - for(unsigned int i = 0; i < viewcnt; i++) { - memcpy(&view, views + i, sizeof(tikview)); - ret = ES_DeleteTicket(&view); - if (ret < 0) { - printf("failed! (delete view #%d -> %d)", i, ret); - return quit(ret); - } - } - free(views); - - ES_DeleteTitleContent(tid_new); - ES_DeleteTitle(tid_new); // not fatal enough to matter tbh - } else - printf("not present.. "); - - printf("OK!\n"); + ret = purge_title(tid_new); + if (ret < 0) printf("failed..? (%d)\n", ret); + else printf("OK!\n"); printf("> Installing ticket... "); ret = ES_AddTicket(s_tik, STD_SIGNED_TIK_SIZE, s_certs, certs_size, NULL, 0); if (ret < 0) { printf("failed! (%d)\n", ret); - return quit(ret); + return ret; } printf("OK!\n"); @@ -383,7 +305,7 @@ int main() { if (ret < 0) { ES_AddTitleCancel(); printf("failed! (%d)\n", ret); - return quit(ret); + return ret; } printf("OK!\n"); @@ -402,7 +324,7 @@ int main() { if(ret < 0) { printf("failed! (start, %d)\n", ret); ES_AddTitleCancel(); - return quit(ret); + return ret; } cfd = ret; @@ -412,7 +334,7 @@ int main() { if(ret < 0) { printf("failed! (read, %d)\n", ret); ES_AddTitleCancel(); - return quit(ret); + return ret; } aeskey encrypt_iv = {}; @@ -437,7 +359,7 @@ int main() { printf("error! (%d)\n", ret); ES_AddContentFinish(cfd); ES_AddTitleCancel(); - return quit(ret); + return ret; } } } else { @@ -449,7 +371,7 @@ int main() { if(ret < 0) { printf("failed! (%d)\n", ret); ES_AddTitleFinish(); - return quit(ret); + return ret; } printf("OK! (size = %u)\n", _csize); @@ -458,7 +380,7 @@ int main() { if(ret < 0) { printf("failed! (start, %d)\n", ret); ES_AddTitleCancel(); - return quit(ret); + return ret; } cfd = ret; @@ -467,7 +389,7 @@ int main() { printf("failed! (%d)\n", ret); ES_AddContentFinish(cfd); ES_AddTitleCancel(); - return quit(ret); + return ret; } } @@ -476,7 +398,7 @@ int main() { if (ret < 0) { printf("failed! (%d)\n", ret); ES_AddTitleCancel(); - return quit(ret); + return ret; } printf("OK!\n\n"); @@ -486,10 +408,187 @@ int main() { if (ret < 0) { printf("failed! (%d)\n", ret); ES_AddTitleCancel(); - return quit(ret); + return ret; } printf("OK!\n"); printf("\x1b[42m All done! \x1b[40m\n"); - return quit(0); + return 0; +} + +int restore() { + int ret = 0; + unsigned int certs_size = 0, tik_size = 0, tmd_size = 0; + signed_blob *s_certs, *s_tmd, *s_tik; + + int HAAA_rev = get_title_rev(tid_new), HAZA = check_title(tid_stub); + OSReport("HAAA rev is %u, HAZA existence level is %d\n", HAAA_rev, HAZA); + if(HAAA_rev <= 2 && HAZA >= 2) { + printf("Check Data management.\n"); + return 0; + } + + printf("> Initializing network... "); + ret = net_init_retry(5); + if (ret < 0) { + printf( + "failed! (%d)" "\n\n" + + "May as well just grab the original Photo Channel 1.0 WAD" "\n\n", ret + ); + return ret; + } + uint32_t hostip = net_gethostip(); + printf("OK! Wii IP address: %hhu.%hhu.%hhu.%hhu\n\n", + hostip >>24, hostip >>16, hostip >>8, hostip >>0); + offline_mode = false; + + printf("> Removing Photo Channel 1.0*... "); + ret = purge_title(tid_new); + if (ret < 0) printf("failed? (%d)...\n", ret); + else printf("OK!\n"); + + printf("> Reading certs... "); + s_certs = FS_Read("/sys/cert.sys", &certs_size, &ret); + if (ret < 0) { + printf("failed! (%d)\n", ret); + return ret; + } + printf("OK! (size = %u)\n", certs_size); + + printf("> Downloading TMD... "); + s_tmd = fetch_tmd(tid_new, &tmd_size, &ret); + if (ret < 0) { + printf("failed! (%d)\n", ret); + return ret; + } + printf("OK! (size = %u)\n", tmd_size); + tmd* p_tmd = SIGNATURE_PAYLOAD(s_tmd); + + printf("> Downloading ticket... "); + s_tik = fetch_tik(tid_new, &tik_size, &ret); + if (ret < 0) { + printf("failed! (%d)\n", ret); + return ret; + } + printf("OK! (size = %u)\n", tik_size); + + printf("> Installing ticket... "); + ret = ES_AddTicket(s_tik, STD_SIGNED_TIK_SIZE, s_certs, certs_size, NULL, 0); + if (ret < 0) { + printf("failed! (%d)\n", ret); + return ret; + } + printf("OK!\n"); + + printf("> Starting title installation... "); + ret = ES_AddTitleStart(s_tmd, SIGNED_TMD_SIZE(s_tmd), s_certs, certs_size, NULL, 0); + if (ret < 0) { + ES_AddTitleCancel(); + printf("failed! (%d)\n", ret); + return ret; + } + printf("OK!\n"); + + for (unsigned int i = 0; i < p_tmd->num_contents; ++i) { + tmd_content* content = p_tmd->contents + i; + unsigned int cid = content->cid, csize = content->size, align_csize = ALIGN(csize, 0x20), _csize = 0; + unsigned char* buffer = NULL; + int cfd = 0; + + printf(">> Downloading content %08x... ", cid); + + ret = ES_AddContentStart(p_tmd->title_id, cid); + if (ret < 0) { + ES_AddTitleCancel(); + printf("failed! (start, %d)\n", ret); + return ret; + } + cfd = ret; + + char cidstr[9]; + sprintf(cidstr, "%08x", cid); + + buffer = NUS_Download(tid_new, cidstr, &_csize, &ret); + if (ret < 0) { + ES_AddContentFinish(cfd); + ES_AddTitleCancel(); + printf("failed! (%d)\n", ret); + return ret; + } + printf("OK! (size = %u)\n", _csize); + + printf(">> Installing... "); + ret = ES_AddContentData(cfd, buffer, align_csize); + if (ret < 0) { + ES_AddContentFinish(cfd); + ES_AddTitleCancel(); + printf("failed! (%d)\n", ret); + return ret; + } + + ret = ES_AddContentFinish(cfd); + if (ret < 0) { + ES_AddTitleCancel(); + printf("failed! (%d)\n", ret); + return ret; + } + printf("OK!\n"); + } + ret = ES_AddTitleFinish(); + if (ret < 0) { + printf("failed! (%d)\n", ret); + return ret; + } + + printf("OK!!\n"); + return 0; +} + +int main() { + int ret = 0; + + init_video(2, 0); + printf(header); + + if(!check_dolphin()) { + printf("Applying IOS patches... "); + ret = IosPatch_RUNTIME(true, false, true, false); + if (ret < 0) { + printf("Failed! (%d)" "\n" + "Is your Homebrew Channel updated?" "\n" + "Is in meta.xml?" "\n\n" + + "Exiting in 5s...", ret); + sleep(5); + return 0x0D800064; + } + printf("OK! (patch count = %d)\n\n", ret); + } + WPAD_Init(); + PAD_Init(); + ISFS_Initialize(); + + printf( + "Super basic menu" "\n\n" + + "Press A to install Photo Channel 1.1." "\n" + "Press B to restore Photo Channel 1.0." "\n" + "Press HOME/START to return to loader." "\n" + ); + + while(true) { + unsigned int input = input_scan(0); + if(input & input_a) { + ret = install(); + break; + } + else if(input & input_b) { + ret = restore(); + break; + } + else if(input & input_home) return 0; + } + return quit(ret); + } diff --git a/source/tools.c b/source/tools.c index 2e743e2..20fcd3e 100644 --- a/source/tools.c +++ b/source/tools.c @@ -1,6 +1,7 @@ #include "tools.h" #include +#include #include #include #include @@ -25,7 +26,23 @@ void init_video(int row, int col) { VIDEO_Flush(); VIDEO_WaitVSync(); if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync(); - printf("\x1b[%d;%dH", row, col); + //printf("\x1b[%d;%dH", row, col); +} + +unsigned int check_title(const uint64_t tid) { + OSReport("check_title(%016llx)\n", tid); + unsigned int lv = 0, viewcnt = 0, cnt = 0; + + ES_GetNumTicketViews(tid, &viewcnt); + OSReport("* view count: %u\n", viewcnt); + if (viewcnt) { + lv++; + ES_GetTitleContentsCount(tid, &cnt); + OSReport("* contents count: %u\n", cnt); + if (cnt) lv++; + } + OSReport("total existence level is %u\n", lv); + return lv; } bool check_dolphin(void) { @@ -38,14 +55,33 @@ bool check_dolphin(void) { } bool check_vwii(void) { - if(!~vwii) { - unsigned int cnt = 0; - ES_GetTitleContentsCount(0x0000000100000200LL, &cnt); - vwii = cnt > 0; - } - return vwii; + if(!~vwii) vwii = check_title( 1LL << 32 | 0x200 ) >= 2; + return vwii; +} + +int get_title_rev(const uint64_t tid) { + unsigned int viewsize = 0; + + ES_GetTMDViewSize(tid, &viewsize); + if(!viewsize) return -ENOENT; + + unsigned char _view[viewsize] ATTRIBUTE_ALIGN(0x20); + tmd_view* view = (void*)_view; + + int ret = ES_GetTMDView(tid, _view, viewsize); + if(ret < 0) return ret; + else return view->title_version; } +int net_init_retry(unsigned int retries) { + int ret = 0; + for (int s = 0; s < 5; s++) { + ret = net_init(); + if(!ret || ret != -EAGAIN) break; + } + + return ret; +} uint16_t input_scan(uint16_t value) { uint16_t input = 0x00;