Gamecube memory card unlock reverse engineering. This reversing is not pretending to be compilable, and can be used for education purposes only. I dont hold any responsibility for damage of your Gamecube clock settings or memory card saves or else, produced from using code/information represented in this document. Version 0.1. (12 May 2005) -------------------------------------------------------------------------- Overview. Memory card unlock is really weirdo operation, because its using GC DSP. It proves, that N still have crazy developers and its good. This reversing representing card unlock operation from both CPU and DSP sides. -------------------------------------------------------------------------- PART I: CPU Side. Unlock call. s32 __CARDUnlock <-- CARD_RESULT -------------------------------------------------------------------------- PART II: CPU Side. Helper calls. These routines are replacement for stdc's srand and rand. static u32 next = 1; // rand seed u32 CARDRand(void) { next = next * 0x41C64E6D + 12345; return (next >> 16) & 0x7fff; // RAND_MAX = 0x7fff } void CARDSrand(u32 seed) { next = seed; } --- u32 exnor_1st(u32 data, int rshift) { u32 work = data; for(int i=0; i> 23) ^ (work >> 15) ^ (work >> 7) ^ work); work = (work >> 1) | ((wk & 2) << 30); } return work; } u32 exnor(u32 data, int lshift) { u32 work = data; for(int i=0; i> 30) & 2) } return work; } --- Reverse bit order, for example 0xBC2F1895 will be 0xA918F43D : 10111100001011110001100010010101 -> 10101001000110001111010000111101. u32 bitrev(u32 data) { u32 wk = 0; int j = 0, k = 1; for(int i=0; i<32; i++) { if(i > 15) { if(i == 31) { wk |= (data >> 31); } else { wk |= (data & (1 << i)) >> k; k += 2; } } else { wk |= (data & (1 << i)) << (31 - i - j); j++; } } return wk; } --- s32 ReadArrayUnlock(s32 chan, u32 data, void *rbuf, s32 rlen, s32 mode) { CARDControl *card; s32 err; u8 cmd[5]; ASSERT(0 <= chan && chan < 2); card = &__CARDBlock[chan]; if( EXISelect(chan, 0, __CARDFreq) == 0 ) return CARD_RESULT_NOCARD; // Round down data to 4096 bytes (sector number) data &= ~(4096 - 1); // Commands for MX25L4004 // ReadArray 0x52 SA2 SA1 PN BA 0x00 0x00 0x00 0x00 memset(&cmd, 0, sizeof(cmd)); cmd[0] = 0x52; if(mode == 0) { cmd[1] = (data >> 29) & 3; cmd[2] = (data >> 21) & 0xff; cmd[3] = (data >> 19) & 1; cmd[4] = (data >> 12) & 0x7f; } else { cmd[1] = (u8)(data >> 24); cmd[2] = (u8)(data >> 16); } // EXI error accumulate macro is used widely in libraries, but I think // it doesnt have appropriate C-version (since cntlzw) and implemented as // macro in SDK private exi.h header. // First parameter is error flag and second is returned value of EXI call. // In case you interested how its accumulating, here is asm version: // cntlzw r0, result // rlwinm r0,r0,27,5,31 // or error,error,r0 err = 0; EXIAccumError(err, EXIImmEx(chan, cmd, sizeof(cmd), EXI_WRITE)); EXIAccumError(err, EXIImmEx(chan, card->workArea, rlen, EXI_WRITE)); EXIAccumError(err, EXIImmEx(chan, rbuf, rlen, EXI_READ)); EXIAccumError(err, EXIDeselect(chan)); return (err) ? CARD_RESULT_NOCARD : CARD_RESULT_READY; } --- u32 GetInitVal(void) { OSTick tick = OSGetTick(); CARDSrand(tick); return (CARDRand() | 0x7FEC0000) & ~(4096 - 1); } --- 00000330 : 330: 7c 08 02 a6 mflr r0 334: 90 01 00 04 stw r0,4(r1) 338: 94 21 ff e8 stwu r1,-24(r1) 33c: bf 81 00 08 stmw r28,8(r1) 340: 3b c0 00 01 li r30,1 344: 3b 80 00 00 li r28,0 348: 48 00 00 01 bl 348 348: R_PPC_REL24 OSGetTick 34c: 7c 7d 1b 78 mr r29,r3 350: 7f a3 eb 78 mr r3,r29 354: 48 00 00 01 bl 354 354: R_PPC_REL24 CARDSrand 358: 48 00 00 01 bl 358 358: R_PPC_REL24 CARDRand 35c: 7c 7f 1b 78 mr r31,r3 360: 57 ff 06 fe clrlwi r31,r31,27 364: 3b ff 00 01 addi r31,r31,1 368: 48 00 00 3c b 3a4 36c: 48 00 00 01 bl 36c 36c: R_PPC_REL24 OSGetTick 370: 7c 7d 1b 78 mr r29,r3 374: 7f bf f0 30 slw r31,r29,r30 378: 3b de 00 01 addi r30,r30,1 37c: 28 1e 00 10 cmplwi r30,16 380: 40 81 00 08 ble 388 384: 3b c0 00 01 li r30,1 388: 7f e3 fb 78 mr r3,r31 38c: 48 00 00 01 bl 38c 38c: R_PPC_REL24 CARDSrand 390: 48 00 00 01 bl 390 390: R_PPC_REL24 CARDRand 394: 7c 7f 1b 78 mr r31,r3 398: 57 ff 06 fe clrlwi r31,r31,27 39c: 3b ff 00 01 addi r31,r31,1 3a0: 3b 9c 00 01 addi r28,r28,1 3a4: 2c 1f 00 04 cmpwi r31,4 3a8: 40 80 00 0c bge 3b4 3ac: 28 1c 00 0a cmplwi r28,10 3b0: 41 80 ff bc blt 36c 3b4: 2c 1f 00 04 cmpwi r31,4 3b8: 40 80 00 08 bge 3c0 3bc: 3b e0 00 04 li r31,4 3c0: 7f e3 fb 78 mr r3,r31 3c4: 80 01 00 1c lwz r0,28(r1) 3c8: bb 81 00 08 lmw r28,8(r1) 3cc: 38 21 00 18 addi r1,r1,24 3d0: 7c 08 03 a6 mtlr r0 3d4: 4e 80 00 20 blr -------------------------------------------------------------------------- PART III: DSP Side. Disassembly obtained from duddie's GDTOOL. 0000 0000 NOP 0001 0000 NOP 0002 0000 NOP 0003 0000 NOP 0004 0000 NOP 0005 0000 NOP 0006 0000 NOP 0007 0000 NOP 0008 0000 NOP 0009 0000 NOP 000a 0000 NOP 000b 0000 NOP 000c 0000 NOP 000d 0021 HALT 000e 02ff RTI 000f 0021 HALT 0010 1306 SBSET #0x06 0011 1203 SBCLR #0x03 0012 1204 SBCLR #0x04 0013 1305 SBSET #0x05 0014 0092 00ff LRI $18, #0x00ff 0016 0088 ffff LRI $8, #0xffff 0018 0089 ffff LRI $9, #0xffff 001a 008a ffff LRI $10, #0xffff 001c 008b ffff LRI $11, #0xffff 001e 8f00 S16 001f 02bf 0088 CALL 0x0088 0021 16fc dcd1 SI @0xfffc, #0xdcd1 0023 16fd 0000 SI @0xfffd, #0x0000 0025 16fb 0001 SI @0xfffb, #0x0001 0027 02bf 008e CALL 0x008e 0029 25ff LRS $29, @0xffff 002a 0380 ff00 CMPI $31, #0xff00 002c 0294 0027 JNE 0x0027 002e 02bf 008e CALL 0x008e 0030 1fdf MRR $30, $31 0031 24ff LRS $28, @0xffff 0032 0240 0fff ANDI $30, #0x0fff 0034 0098 0400 LRI $24, #0x0400 0036 009a 0010 LRI $26, #0x0010 0038 0099 0000 LRI $25, #0x0000 003a 8e00 S40 003b 02bf 0094 CALL 0x0094 003d 02bf 8644 CALL 0x8644 003f 02bf 0088 CALL 0x0088 0041 16fc dcd1 SI @0xfffc, #0xdcd1 0043 16fd 0003 SI @0xfffd, #0x0003 0045 16fb 0001 SI @0xfffb, #0x0001 0047 8f00 S16 0048 02bf 008e CALL 0x008e 004a 0380 cdd1 CMPI $31, #0xcdd1 004c 0294 0048 JNE 0x0048 004e 27ff LRS $31, @0xffff 004f 0380 0001 CMPI $31, #0x0001 0051 0295 005a JEQ 0x005a 0053 0380 0002 CMPI $31, #0x0002 0055 0295 8000 JEQ 0x8000 0057 029f 0048 JMP 0x0048 0059 0021 HALT 005a 8e00 S40 005b 02bf 008e CALL 0x008e 005d 25ff LRS $29, @0xffff 005e 02bf 008e CALL 0x008e 0060 25ff LRS $29, @0xffff 0061 02bf 008e CALL 0x008e 0063 25ff LRS $29, @0xffff 0064 02bf 008e CALL 0x008e 0066 00c5 ffff LR $5, @0xffff 0068 0340 0fff ANDI $31, #0x0fff 006a 1c9f MRR $4, $31 006b 02bf 008e CALL 0x008e 006d 00c7 ffff LR $7, @0xffff 006f 02bf 008e CALL 0x008e 0071 00c6 ffff LR $6, @0xffff 0073 02bf 008e CALL 0x008e 0075 00c0 ffff LR $0, @0xffff 0077 02bf 008e CALL 0x008e 0079 20ff LRS $24, @0xffff 007a 0340 0fff ANDI $31, #0x0fff 007c 1f5f MRR $26, $31 007d 02bf 008e CALL 0x008e 007f 21ff LRS $25, @0xffff 0080 02bf 008e CALL 0x008e 0082 23ff LRS $27, @0xffff 0083 1205 SBCLR #0x05 0084 1206 SBCLR #0x06 0085 029f 80b5 JMP 0x80b5 0087 0021 HALT 0088 27fc LRS $31, @0xfffc 0089 03c0 8000 ANDF $31, #0x8000 008b 029d 0088 JNZ 0x0088 008d 02df RET 008e 27fe LRS $31, @0xfffe 008f 03c0 8000 ANDF $31, #0x8000 0091 029c 008e JZR 0x008e 0093 02df RET 0094 2ece SRS @0xffce, $30 0095 2ccf SRS @0xffcf, $28 0096 00f8 ffcd SR @0xffcd, $24 0098 00f9 ffc9 SR @0xffc9, $25 009a 00fa ffcb SR @0xffcb, $26 009c 26c9 LRS $30, @0xffc9 009d 02c0 0004 ANDF $30, #0x0004 009f 029d 009c JNZ 0x009c 00a1 02df RET 00a2 0000 NOP 00a3 0000 NOP 00a4 0000 NOP 00a5 0000 NOP 00a6 0000 NOP 00a7 0000 NOP 00a8 0000 NOP 00a9 0000 NOP 00aa 0000 NOP 00ab 0000 NOP 00ac 0000 NOP 00ad 0000 NOP 00ae 0000 NOP 00af 0000 NOP