Kodewerx

Our culture has advanced beyond all that you could possibly comprehend with one hundred percent of your brain.
It is currently Sat Jan 18, 2020 1:20 pm

All times are UTC - 8 hours [ DST ]




Post new topic Reply to topic  [ 56 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: DS Game Card Encryption
PostPosted: Wed Aug 08, 2007 6:12 pm 
Offline
Komrade
Komrade
User avatar

Joined: Tue Mar 27, 2007 6:23 pm
Posts: 1354
Location: Mario Raceway, 1509.831, 217.198, -564.429
Title: Mario Kart 64 Hacker
I'm trying to launch DS cards that have been inserted after power-on, but I'm having trouble with the encryption. I've implemented everything as best I can understand according to GBATek. Once I'm in KEY1 mode, all I seem to get back from the cartridge is random data, even when nothing is inserted. (With nothing inserted, before entering KEY1 mode, everything is 0xFF.) I've tried sending commands 1lllliiijjjkkkkk to get the encrypted chip ID, and Flllliiijjjkkkkk which is invalid and should return an endless stream of KEY2-encrypted zeros. In both cases the data I get back (both before and after decryption) is completely random; sending 4llllmmmnnnkkkkk beforehand to enable KEY2 mode doesn't seem to make a difference. (And yes, I am sending KEY1-encrypted commands.)

For i, j, l, and the encryption seeds, which are supposed to all be random, I've simply used zero. k starts out as zero and is incremented after every KEY1 command as specified. I can't test whether KEY1 encryption actually works, since all the returned data is either raw or KEY2. I've written zero to 0x040001B0 through 0x040001BB too.

My best guess (aside from the code simply being wrong) is that once these seeds are set by the BIOS, they can't be changed, and so I have to find out which seeds it used and continue to use them. It must be possible in any case since ARDS and Nitro Hax do it.

These are the crypto routines. Yeah, they need to be cleaned up and commented and so on, I haven't got that far yet.

Code:
#define BYTESWAP32U(x) (u32)((((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24))
#define CARD_FLAG_KEY1_LEN1(x)   ((x) & 0x1FFF)
#define CARD_FLAG_DATA_SCRAMBLE   BIT(13)
#define CARD_FLAG_BIT14         BIT(14) //Same as bit 13?
#define CARD_FLAG_BIT15         BIT(15) //Unknown
#define CARD_FLAG_KEY1_LEN2(x)   (((x) & 0x3F) << 16)
#define CARD_FLAG_CMD_SCRAMBLE   BIT(22)
#define CARD_FLAG_WORD_READY   BIT(23) //Read-only
#define CARD_FLAG_BLOCK_SIZE(x)   (((x) & 7) << 24) //0=none, 1-6 = (0x100 << x) bytes, 7=4 bytes
#define CARD_FLAG_SLOW_CLOCK   BIT(27) //Use 4.2mhz instead of 6.7mhz
#define CARD_FLAG_BIT28         BIT(28) //"Secure area mode (0=normal, 1=other)"
#define CARD_FLAG_BIT29         BIT(29) //Always 1?
#define CARD_FLAG_BIT30         BIT(30) //Always 0?
#define CARD_FLAG_START         BIT(31) //Start transfer
[...]
const u8 Key2Seed[] = {0xE8, 0x4D, 0x5A, 0xB1, 0x17, 0x8F, 0x99, 0xD5};
[...]
u32 KEY1[1042];
u32 KeyCode[3];
bool ReinitKey2 = true;
Code:
/*
Initializes the KeyCode buffer.
Inputs:
   -IDCode: Game code or firmware ID code, depending what is being decrypted.
   -Level: How many times to apply the keycode (1-3).
   -Modulo: Passed to ApplyKeyCode.
*/
void InitKeyCode(u32 IDCode, u32 Level, u32 Modulo)
{
   //Todo: read key from BIOS
   memcpy(KEY1, KEY1_DATA, sizeof(KEY1));

   KeyCode[0] = IDCode;
   KeyCode[1] = IDCode >> 1;
   KeyCode[2] = IDCode << 1;

   /*
   KeyCode[0] = IDCode & 0xFF;
   KeyCode[1] = (IDCode >> 8) & 0xFF);
   KeyCode[2] = (IDCode >> 16) & 0xFF);
   KeyCode[3] = (IDCode >> 24) & 0xFF);

   KeyCode[4] = (IDCode >> 1) & 0xFF;
   KeyCode[5] = ((IDCode >> 1) >> 8) & 0xFF);
   KeyCode[6] = ((IDCode >> 1) >> 16) & 0xFF);
   KeyCode[7] = ((IDCode >> 1) >> 24) & 0xFF);

   KeyCode[8] = (IDCode << 1) & 0xFF;
   KeyCode[9] = ((IDCode << 1) >> 8) & 0xFF);
   KeyCode[10] = ((IDCode << 1) >> 16) & 0xFF);
   KeyCode[11] = ((IDCode << 1) >> 24) & 0xFF);
   */

   if(Level >= 1) ApplyKeyCode(Modulo);
   if(Level >= 2) ApplyKeyCode(Modulo);

   KeyCode[1] <<= 1;
   KeyCode[2] >>= 1;

   /*
   KeyCode[4] <<= 1;
   KeyCode[5] <<= 1;
   KeyCode[6] <<= 1;
   KeyCode[7] <<= 1;

   KeyCode[8] >>= 1;
   KeyCode[9] >>= 1;
   KeyCode[10] >>= 1;
   KeyCode[11] >>= 1;
   */

   if(Level >= 3) ApplyKeyCode(Modulo);

   /*
   copy [arm7bios+0030h..1077h] to [keybuf+0..1047h]
   [keycode+0]=[idcode]
   [keycode+4]=[idcode]/2
   [keycode+8]=[idcode]*2
   IF level>=1 THEN apply_keycode(modulo) ;first apply (always)
   IF level>=2 THEN apply_keycode(modulo) ;second apply (optional)
   [keycode+4]=[keycode+4]*2
   [keycode+8]=[keycode+8]/2
   IF level>=3 THEN apply_keycode(modulo) ;third apply (optional)
   */
}


/*
Used by InitKeyCode().
*/
void ApplyKeyCode(u32 Modulo)
{
   u32 Scratch[2];

   Crypt64bit(true, &KeyCode[1]);
   Crypt64bit(true, &KeyCode[0]);

   Scratch[0] = 0;
   Scratch[1] = 0;

   for(u32 i = 0; i < 11; i++)
      KEY1[i] = KEY1[i] ^ BYTESWAP32(KeyCode[i % Modulo]);

   for(u32 i = 0; i < 0x410; i += 2)
   {
      Crypt64bit(true, Scratch);
      KEY1[i] = Scratch[0];
      KEY1[i + 1] = Scratch[1];
   }

   /*
   crypt_64bit_up(keycode+4)
   crypt_64bit_up(keycode+0)
   [scratch]=0000000000000000h   ;S=0 (64bit)
   FOR I=0 TO 44h STEP 4         ;xor with reversed byte-order (bswap)
      [keybuf+I]=[keybuf+I] XOR bswap_32bit([keycode+(I MOD modulo)])
   NEXT I
   FOR I=0 TO 1040h STEP 8
      crypt_64bit_up(scratch)     ;encrypt S (64bit) by keybuf
      [keybuf+I]=[scratch]        ;write S (64bit) to keybuf
   NEXT I
   */
}


/*
Used by InitKeyCode().
*/
void Crypt64bit(bool Up, u32* Ptr)
{
   u32 X, Y, Z;
   s32 init, check, step;

   Y = Ptr[0];
   X = Ptr[1];

   if(Up)
   {
      init = 0;
      check = 0xF;
      step = 1;
   }
   else {
      init = 0x11;
      check = 2;
      step = -1;
   }

   for(s32 i = init; i != check; i += step)
   {
      Z = KEY1[i << 2] ^ X;
      X = KEY1[0x12  + (((Z >> 24) & 0xFF) << 2)];
      X = KEY1[0x112 + (((Z >> 16) & 0xFF) << 2)] + X;
      X = KEY1[0x212 + (((Z >>  8) & 0xFF) << 2)] ^ X;
      X = KEY1[0x312 + ((Z & 0xFF) << 2)] + X;
      X = Y ^ X;
      Y = Z;
   }

   if(Up) {
      Ptr[0] = X ^ KEY1[0x10];
      Ptr[1] = Y ^ KEY1[0x11];
   }
   else {
      Ptr[0] = X ^ KEY1[1];
      Ptr[1] = Y ^ KEY1[0];
   }

   /*
   Y=[ptr+0]
   X=[ptr+4]
   FOR I=0 TO 0Fh (up), or FOR I=11h TO 02h (down)
      Z=[keybuf+I*4] XOR X
      X=[keybuf+048h+((Z SHR 24) AND FFh)*4]
      X=[keybuf+448h+((Z SHR 16) AND FFh)*4] + X
      X=[keybuf+848h+((Z SHR  8) AND FFh)*4] XOR X
      X=[keybuf+C48h+((Z SHR  0) AND FFh)*4] + X
      X=Y XOR X
      Y=Z
   NEXT I
   [ptr+0]=X XOR [keybuf+40h], or [ptr+0]=X XOR [keybuf+4h] (down)
   [ptr+4]=Y XOR [keybuf+44h], or [ptr+4]=Y XOR [keybuf+0h] (down)
   */
}


/*
Encrypts/decrypts KEY2 data.
*/
void Key2Enc(u8* Data, u8 NumBytes)
{
   static u64 S1 = 0, S2 = 0, Mask = 0x7FFFFFFFFF;
   u64 SeedTemp;

   if(ReinitKey2)
   {
      S1 = 0x6000 + Key2Seed[NDSHeader.deviceType & 3];
      S2 = 0x506CECF09D;

      //Reverse bit order
      SeedTemp = 0;
      for(u32 i = 0; i < 39; i++)
         SeedTemp |= ((S1 >> i) & 1) << (39 - i);
      S1 = SeedTemp;

      //S2 is already reversed
      ReinitKey2 = false;
   }

   for(u32 i=0; i<NumBytes; i++)
   {
      S1 = ((((S1 >> 5) ^ (S1 >> 17) ^ (S1 >> 18) ^ (S1 >> 31)) & 0xFF) + (S1 << 8)) & Mask;
      S2 = ((((S2 >> 5) ^ (S2 >> 23) ^ (S2 >> 18) ^ (S2 >> 31)) & 0xFF) + (S2 << 8)) & Mask;
      Data[i] = (Data[i] ^ S1 ^ S2) & 0xFF;
   }

   /*
   x = S1, y = S2
   x = (((x shr 5)xor(x shr 17)xor(x shr 18)xor(x shr 31)) and 0FFh)+(x shl 8)
   y = (((y shr 5)xor(y shr 23)xor(y shr 18)xor(y shr 31)) and 0FFh)+(y shl 8)
   data = (data xor x xor y) and 0FFh
   */
}


/*
Sends a raw command to the DS card and reads the 32-bit return value.
Inputs:
   -Command: Command data.
   -Flags: Card control flags.
Returns: First word returned from the card.
*/
u32 RawNDSCardCommand(u8* Command, u32 Flags)
{
   CARD_CR1H = CARD_CR1_ENABLE | CARD_CR1_IRQ;

   for(u32 i=0; i<8; i++)
      CARD_COMMAND[i] = Command[i];

   CARD_CR2 = Flags;
   while(!(CARD_CR2 & CARD_DATA_READY));
   return CARD_DATA_RD;
}


/*
Same as RawNDSCardCommand(), but instead of returning the first word received,
the data is copied into a buffer.
Inputs:
   -Command: Command data.
   -Buf: Buffer to copy words to.
   -NumWords: Maximum number of words to copy.
   -SkipWords: Number of words to ignore before starting to copy. Mainly used
    for GetEncryptedChipID() since the command returns a long stream of dummy
    bytes before the ID.
   -Flags: Card control flags.
Returns: Number of words read (including ignored words).
*/
u32 MultiNDSCardCommand(u8* Command, u32* Buf, u32 NumWords, u32 SkipWords, u32 Flags)
{
   u32 Data, Index = 0, WordsRead = 0;

   CARD_CR1H = CARD_CR1_ENABLE | CARD_CR1_IRQ;

   for(u32 i=0; i<8; i++)
      CARD_COMMAND[i] = Command[i];

   CARD_CR2 = Flags;

   do {
      if(CARD_CR2 & CARD_DATA_READY)
      {
         WordsRead++;
         Data = CARD_DATA_RD;
         if(SkipWords) SkipWords--;
         else if(Index < NumWords)
         {
            Buf[Index] = Data;
            Index++;
         }
      }
   } while(CARD_CR2 & CARD_BUSY);
   return WordsRead;
}

/*
Boots the inserted DS card.
Returns: On success, does not return. On failure, returns one of BE_xxx.
*/
BOOTERROR BootDSCard()
{
   u32 ChipID, ChipID2, EncChipID;
   bool SecureAreaTransferMode = true; //true=2x800h, false=1000h
   u32 CmdCount = 0, WordCount;
   u32 GameCode;
   u8 Command[8];
   u32 Flags, EncrFlags;

   ConsoleClear();
   ConsoleMoveTo(0, 0);
   ConsoleSetColour(CONSOLE_COL_WHITE);
   ConsoleEnableDigitSep(false);
   ReinitKey2 = true;

   //Set encryption seeds
   CARD_1B0 = 0;
   CARD_1B4 = 0;
   CARD_1B8 = 0;
   CARD_1BA = 0;


   Flags = CARD_FLAG_KEY1_LEN1(0x1FFF) | CARD_FLAG_KEY1_LEN2(0x3F) | CARD_FLAG_BIT29 | CARD_FLAG_START;
   EncrFlags = Flags | CARD_FLAG_DATA_SCRAMBLE | CARD_FLAG_BIT14 | CARD_FLAG_CMD_SCRAMBLE;


   //Read card header
   ConsolePrint("Read header... ");
   //cardReadHeader((u8*)&NDSHeader.gameTitle[0]);

   for(u32 i=0; i<8; i++) Command[i] = 0;
   WordCount = MultiNDSCardCommand(Command, (u32*)&NDSHeader.gameTitle[0], 128, 0, Flags | CARD_FLAG_BLOCK_SIZE(1));

   GameCode = NDSHeader.gameCode[0] | (NDSHeader.gameCode[1] << 8) | (NDSHeader.gameCode[2] << 16) | (NDSHeader.gameCode[3] << 24);

   ConsolePrintf("%X\nTitle: ", WordCount);
   for(u32 i=0; i<12; i++) ConsolePrintChar(NDSHeader.gameTitle[i]);
   ConsolePrint(" [");
   for(u32 i=0; i<4; i++) ConsolePrintChar(NDSHeader.gameCode[i]);
   ConsolePrint("]\n");
   for(u32 i=0; i<12; i += 2) ConsolePrintf("%02X%02X ", NDSHeader.gameTitle[i], NDSHeader.gameTitle[i + 1]);
   ConsolePrint("\n");
   for(u32 i=0; i<4; i++) ConsolePrintf("%02X ", NDSHeader.gameCode[i]);

   ConsolePrintf("\nExec: %08X, %08X\nChip ID: ", NDSHeader.arm7executeAddress, NDSHeader.arm9executeAddress);


   //Read ROM chip ID, which tells us which secure area transfer mode to use
   Command[0] = 0x90;
   for(u32 i=1; i<8; i++) Command[i] = 0;

   ChipID = RawNDSCardCommand(Command, Flags | CARD_FLAG_BLOCK_SIZE(7));
   ConsolePrintf("%08X\nEnter KEY1 mode... ", ChipID);
   if(ChipID & 0x80000000) SecureAreaTransferMode = false;


   /*
   Enter KEY1 mode: 3C ii ij jj xk kk kk xx
   i, j = anything, must be the same in later commands
   k = anything, must be incremented after each command after this one
   x = ignored
   */
   Command[0] = 0x3C;
   for(u32 i=1; i<8; i++) Command[i] = 0;

   RawNDSCardCommand(Command, Flags | CARD_FLAG_BLOCK_SIZE(7));
   ConsolePrint("OK\nInit key code");

   InitKeyCode(GameCode, 1, 8);
   ConsolePrint(" 1");
   Crypt64bit(false, &NDSHeader.bfPrime1);
   InitKeyCode(GameCode, 2, 8);
   ConsolePrint(" 2 OK\n");


   //Enter KEY2 mode
   //4l ll lm mm nn nk kk kk

   Command[0] = 0x40;
   for(u32 i=1; i<5; i++) Command[i] = 0;
   Command[5] = (CmdCount >> 16) & 0xF;
   Command[6] = (CmdCount >> 8) & 0xFF;
   Command[7] = CmdCount & 0xFF;
   ConsolePrintf("%02X %02X %02X %02X %02X %02X %02X %02X\n",
      Command[0], Command[1], Command[2], Command[3], Command[4], Command[5], Command[6], Command[7]);
   Crypt64bit(true, (u32*)Command);
   ConsolePrintf("%02X %02X %02X %02X %02X %02X %02X %02X\n",
      Command[0], Command[1], Command[2], Command[3], Command[4], Command[5], Command[6], Command[7]);

   ConsolePrint("Enter KEY2 mode... ");
   WordCount = MultiNDSCardCommand(Command, NULL, 0, 0x244, Flags | CARD_FLAG_BLOCK_SIZE(4));
   ConsolePrintf("%X\n", WordCount);
   //ConsolePrint("OK\n");
   CmdCount++;

   ConsolePrintf("Cart seed %u (%02X)\n", NDSHeader.deviceType & 3, Key2Seed[NDSHeader.deviceType & 3]);

   //Read ROM chip ID again
   ConsolePrint("Chip ID: ");

   ChipID2 = 0xAAAAAAAA;

   Command[0] = 0x10;
   Command[1] = 0x00;
   Command[2] = 0x00;
   Command[3] = 0x00;
   Command[4] = 0x00;
   Command[5] = (CmdCount >> 16) & 0xF;
   Command[6] = (CmdCount >> 8) & 0xFF;
   Command[7] = CmdCount & 0xFF;
   Crypt64bit(true, (u32*)Command);

   /*
   Encryption test
   */

   Command[0] = 0xF0;
   Command[1] = 0x00;
   Command[2] = 0x00;
   Command[3] = 0x00;
   Command[4] = 0x00;
   Command[5] = (CmdCount >> 16) & 0xF;
   Command[6] = (CmdCount >> 8) & 0xFF;
   Command[7] = CmdCount & 0xFF;
   Crypt64bit(true, (u32*)Command);

   ChipID2 = 0;
   Key2Enc((u8*)&ChipID2, 4);
   ReinitKey2 = true;

   ConsolePrintf("\n00000000 -> %08X\n", ChipID2);
   WordCount = MultiNDSCardCommand(Command, &ChipID2, 1, 0x250, CARD_FLAG_KEY1_LEN1(0x1FFF) | CARD_FLAG_KEY1_LEN2(0x3F) | CARD_FLAG_BIT29 | CARD_FLAG_START
      | CARD_FLAG_DATA_SCRAMBLE | CARD_FLAG_BIT14 | CARD_FLAG_BLOCK_SIZE(6));
   ConsolePrintf("Returned -> %08X (%X)\n", ChipID2, WordCount);

   Key2Enc((u8*)&ChipID2, 4);
   ConsolePrintf("Decr. 1  -> %08X\n", ChipID2);

   return BE_UNKNOWN_ERROR;

   /*
   End test
   */
}
KEY1_DATA is a copy of the KEY1 table from the BIOS since I haven't got around to making it actually read from the BIOS yet.
I've heard you need to write the command bytes in reverse order, but that doesn't seem to be true. Reading the header and chip ID in raw mode work just fine without that. O_o

_________________
Image 143
HyperNova Software is now live (but may take a few tries to load) currently down; check out my PSP/DS/Game Boy/Windows/Linux homebrew, ROM hacks, and Gameshark codes!


Top
 Profile  
Reply with quote  
PostPosted: Wed Aug 08, 2007 6:47 pm 
Offline
Krew (Admin)
Krew (Admin)
User avatar

Joined: Sun Oct 01, 2006 9:26 pm
Posts: 3765
Title: All in a day's work.
No... Just, no...

Here's some real source, (GPL'd obviously) from Kwurdi, which I started working on in July '05. The code was RE'd from the ARM7 BIOS. I wouldn't trust much of anything from GBAtek because Martin Korth has issues. He also neglected to mention the importance of timing between each command, which my sources handle without the need for interrupts. (it uses Timer 3 in CardInit() Easy to change that to use an input arg to tell the init which timer you want it to use. (Useful if your program already uses timers and you have a nice timer allocation handler or something that you would like to use, etc.)

Using this code is very simple:

Code:
// Define memory locations for CardInit() outputs
#define CARDID            ((u32*)0x027FF800)
#define SECURE_CARDID    ((u32*)0x027FF804)
#define INGAME_CARDID    ((u32*)0x027FFC00)
#define HEADER            ((NDS_HEADER*)0x027FFE00)

CardInit(HEADER, CARDID, SECURE_CARDID, INGAME_CARDID); // Initialize the cardcrypt lib, returns 0 on success, else an error code.
 


Then simply call CardRead() to read anywhere from the card. The only limitations are that the source address must be aligned to 512 bytes, and the length must be a multiple of 512 bytes.

For bonus points, you can also encrypt secure area data using CardEncryptARM9Secure().

Finally, this lib also requires Paul Kocher's C implementation of the Blowfish Algorithm.

cardcrypt.h:
Code:
/* Kwurdi - KodeWerx Universal Remote Debugger Interface
 *
 * Copyright notice for this file:
 *  Copyright (C) 2005-2007 Parasyte
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */


#ifndef __CARDCRYPT_H__
#define __CARDCRYPT_H__

#include <nds.h>


#define CMD_SIZE            128

#define CMD_HEADERREAD        0x00
#define CMD_GETSECUREID        0x10
#define CMD_SECUREREAD        0x20
#define CMD_ENCRYPT            0x3C
#define CMD_SECUREBEGIN        0x40
#define CMD_SECUREEND_A        0x60
#define CMD_SECUREEND        0xA0
#define CMD_BINARYREAD        0xB7
#define CMD_NULL            0xFF

#define CARD_ERR_NONE        0
#define CARD_ERR_MISC        1
#define CARD_ERR_LOGOCRC    2
#define CARD_ERR_HEADERCRC    3
#define CARD_ERR_CMDBUF        4
#define CARD_ERR_SECURE        5

typedef struct {
    char game_title[12];
    char game_code[4];
    char maker_code[2];
    u8 unit_code;
    u8 device_code;
    u8 card_size;

    u8 reserved0[9];

    u8 rom_version;
    u8 boot_info;

    u32 arm9_src;
    u32 arm9_entry;
    u32 arm9_dst;
    u32 arm9_size;

    u32 arm7_src;
    u32 arm7_entry;
    u32 arm7_dst;
    u32 arm7_size;

    u32 fnt_src;
    u32 fnt_size;

    u32 fat_src;
    u32 fat_size;

    u32 arm9_overlay_src;
    u32 arm9_overlay_size;
    u32 arm7_overlay_src;
    u32 arm7_overlay_size;

    u32 rom_control0;
    u32 rom_control1;
    u32 banner_src;
    u16 secure_crc16;
    u16 rom_control2;

    u32 arm9_autoload;
    u32 arm7_autoload;

    u32 reserved1[2];

    u32 rom_size;
    u32 header_size;

    u8 reserved2[56];

    u8 logo[156];

    u16 logo_crc16;
    u16 header_crc16;
}
 NDS_HEADER;

typedef struct {
    int num;
    int order[4]; // 4 should be plenty...
} SECURE_BANK;

typedef struct {
    u8 type;
    u32 cnt;
    u32 size;
    u16 timer;
    u8 data[8];
}
 COMMAND;

typedef struct {
    int read_size;
    u32 crypt_regs[4];
    SECURE_BANK bank;
    COMMAND cmd[CMD_SIZE];
}
 COMMAND_BUFFER;


u8 HardCrypt_Update();
void CardRead(u8 *buffer, u32 addr, u32 size);
int CardInit(NDS_HEADER *nds_header, u32 *cardid, u32 *secure_cardid, u32 *ingame_cardid);
int CardDecryptARM9Secure(u8 *data, NDS_HEADER *nds_header);
int CardEncryptARM9Secure(u8 *data, NDS_HEADER *nds_header);


#endif // __CARDCRYPT_H__
 


cardcrypt.c: [attached]


Attachments:
cardcrypt.c [43.55 KiB]
Downloaded 330 times

_________________
I have to return some video tapes.

Feed me a stray cat.


Last edited by Parasyte on Sun Jul 13, 2008 1:03 am, edited 2 times in total.
Restored formatting and cardcrypt.c
Top
 Profile  
Reply with quote  
PostPosted: Wed Aug 08, 2007 7:01 pm 
Offline
Komrade
Komrade
User avatar

Joined: Tue Mar 27, 2007 6:23 pm
Posts: 1354
Location: Mario Raceway, 1509.831, 217.198, -564.429
Title: Mario Kart 64 Hacker
Ah, that's way better. I couldn't find any source or indeed any info at all on the encryption aside from GBATek, and disassembling it out of the BIOS proved to be far too difficult.

Which version of GPL is that BTW?

_________________
Image 143
HyperNova Software is now live (but may take a few tries to load) currently down; check out my PSP/DS/Game Boy/Windows/Linux homebrew, ROM hacks, and Gameshark codes!


Top
 Profile  
Reply with quote  
PostPosted: Wed Aug 08, 2007 7:03 pm 
Offline
Krew (Admin)
Krew (Admin)
User avatar

Joined: Sun Oct 01, 2006 9:26 pm
Posts: 3765
Title: All in a day's work.
GPL 2.

And I reversed it out of the BIOS two years ago. It can't be that difficult!

_________________
I have to return some video tapes.

Feed me a stray cat.


Top
 Profile  
Reply with quote  
PostPosted: Wed Aug 08, 2007 7:19 pm 
Offline
Komrade
Komrade
User avatar

Joined: Tue Mar 27, 2007 6:23 pm
Posts: 1354
Location: Mario Raceway, 1509.831, 217.198, -564.429
Title: Mario Kart 64 Hacker
And this is why I don't like using other peoples' code.

Code:
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c: In function 'Blowfish_Encrypt_modA':
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c:386: warning: passing argument 2 of 'Blowfish_Encrypt' from incompatible pointer type
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c:386: warning: passing argument 3 of 'Blowfish_Encrypt' from incompatible pointer type
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c: In function 'Blowfish_Encrypt_modB':
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c:393: warning: passing argument 2 of 'Blowfish_Encrypt' from incompatible pointer type
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c:393: warning: passing argument 3 of 'Blowfish_Encrypt' from incompatible pointer type
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c: In function 'Blowfish_Decrypt_mod':
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c:400: warning: passing argument 2 of 'Blowfish_Decrypt' from incompatible pointer type
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c:400: warning: passing argument 3 of 'Blowfish_Decrypt' from incompatible pointer type
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c: In function 'EncryptionContinue':
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c:440: warning: passing argument 2 of 'Blowfish_Encrypt' from incompatible pointer type
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c:440: warning: passing argument 3 of 'Blowfish_Encrypt' from incompatible pointer type
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c: In function 'SoftEncryptCmd':
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c:504: warning: passing argument 2 of 'Blowfish_Encrypt' from incompatible pointer type
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c:504: warning: passing argument 3 of 'Blowfish_Encrypt' from incompatible pointer type
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c: In function 'CardInit':
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c:797: warning: passing argument 2 of 'memcpy' discards qualifiers from pointer target type
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c:813: error: 'CARD_CT' undeclared (first use in this function)
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c:813: error: (Each undeclared identifier is reported only once
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c:813: error: for each function it appears in.)
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c:813: warning: implicit declaration of function 'CARD_L2'
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c:813: warning: implicit declaration of function 'CARD_L1'
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c:816: warning: implicit declaration of function 'CARD_BLOCKS'
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c:862: error: 'CARD_DS' undeclared (first use in this function)
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c:862: error: 'CARD_SE' undeclared (first use in this function)
f:/Programs/Sources/DS/bootmenu2/arm9/source/cardcrypt.c:862: error: 'CARD_CS' undeclared (first use in this function)


CARD_CT on Google turns up a lot of unrelated/foreign websites, adding "libnds" to it produces no results. There's no instances of it in libNDS either. The warnings up to 797 are fixed easily enough with some casting, and I can guess at what some of these should be based on GBATek's notes, but I'm not sure about CARD_CT.

_________________
Image 143
HyperNova Software is now live (but may take a few tries to load) currently down; check out my PSP/DS/Game Boy/Windows/Linux homebrew, ROM hacks, and Gameshark codes!


Top
 Profile  
Reply with quote  
PostPosted: Wed Aug 08, 2007 7:41 pm 
Offline
Krew (Admin)
Krew (Admin)
User avatar

Joined: Sun Oct 01, 2006 9:26 pm
Posts: 3765
Title: All in a day's work.
This might help a bit... I made the following changes to card.h in libnds.

Code:
#define CARD_ACTIVATE    (1 << 31)                // Start/Stop
#define CARD_WR            (1 << 30)                // Write Enable
#define CARD_nRESET        (1 << 29)                // Reset
#define CARD_TRM        (1 << 28)                // Trimmed commands
#define CARD_CT            (1 << 27)                // Unknown
#define CARD_BLOCKS(n)    (((n) & 7) << 24)        // Block size
#define CARD_DATA_READY    (1 << 23)                // Data is on the bus, ready to read
#define CARD_CS            (1 << 22)                // Command Security
#define CARD_L2(n)        (((n) & 0x3F) << 16)    // Latency 2
#define CARD_SCR        (1 << 15)                // Security Register
#define CARD_SE            (1 << 14)                // Master Security Enable
#define CARD_DS            (1 << 13)                // Data Security
#define CARD_L1(n)        ((n) & 0x1FFF)            // Latency 1
#define CARD_ENCRYPTED  (1<<14)  // when writing, this command should be encrypted
#define CARD_BUSY       (1<<31)  // when reading, still expecting incoming data? 


Also, I might have hacked up some of the blowfish lib, so I'll just post what I am using.

blowfish.h:
Code:
/*
blowfish.h:  Header file for blowfish.c

Copyright (C) 1997 by Paul Kocher

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License aint with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


See blowfish.c for more information about this file.
*/


typedef struct {
  
unsigned int P[16 2];
  
unsigned int S[4][256];
BLOWFISH_CTX;

void Blowfish_Init(BLOWFISH_CTX *ctxunsigned char *keyint keyLen);
void Blowfish_Encrypt(BLOWFISH_CTX *ctxunsigned int *xlunsigned int *xr);
void Blowfish_Decrypt(BLOWFISH_CTX *ctxunsigned int *xlunsigned int *xr);
 


blowfish.c:
Code:
/*
blowfish.c:  C implementation of the Blowfish algorithm.

Copyright (C) 1997 by Paul Kocher

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License aint with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/


#include "blowfish.h"

#define N               16

static unsigned int F(BLOWFISH_CTX *ctxunsigned int x) {
   
unsigned short abcd;
   
unsigned int  y;

   
= (unsigned short)(0xFF);
   
>>= 8;
   
= (unsigned short)(0xFF);
   
>>= 8;
   
= (unsigned short)(0xFF);
   
>>= 8;
   
= (unsigned short)(0xFF);
   
ctx->S[0][a] + ctx->S[1][b];
   
ctx->S[2][c];
   
ctx->S[3][d];

   return 
y;
}


void Blowfish_Encrypt(BLOWFISH_CTX *ctxunsigned int *xlunsigned int *xr){
  
unsigned int  Xl;
  
unsigned int  Xr;
  
unsigned int  temp;
  
short       i;

  
Xl = *xl;
  
Xr = *xr;

  for (
0N; ++i) {
    
Xl Xl ctx->P[i];
    
Xr F(ctxXl) ^ Xr;

    
temp Xl;
    
Xl Xr;
    
Xr temp;
  }

  
temp Xl;
  
Xl Xr;
  
Xr temp;

  
Xr Xr ctx->P[N];
  
Xl Xl ctx->P[1];

  *
xl Xl;
  *
xr Xr;
}


void Blowfish_Decrypt(BLOWFISH_CTX *ctxunsigned int *xlunsigned int *xr){
  
unsigned int  Xl;
  
unsigned int  Xr;
  
unsigned int  temp;
  
short       i;

  
Xl = *xl;
  
Xr = *xr;

  for (
11; --i) {
    
Xl Xl ctx->P[i];
    
Xr F(ctxXl) ^ Xr;

    
/* Exchange Xl and Xr */
    
temp Xl;
    
Xl Xr;
    
Xr temp;
  }

  
/* Exchange Xl and Xr */
  
temp Xl;
  
Xl Xr;
  
Xr temp;

  
Xr Xr ctx->P[1];
  
Xl Xl ctx->P[0];

  *
xl Xl;
  *
xr Xr;
}


void Blowfish_Init(BLOWFISH_CTX *ctxunsigned char *keyint keyLen) {
  
int ijk;
  
unsigned int datadataldatar;
/*
  for (i = 0; i < 4; i++) {
    for (j = 0; j < 256; j++)
      ctx->S[i][j] = ORIG_S[i][j];
  }
*/
  
0;
  for (
02; ++i) {
    
data 0x00000000;
    for (
04; ++k) {
      
data = (data << 8) | key[j];
      
1;
      if (
>= keyLen)
        
0;
    }
    
//ctx->P[i] = ORIG_P[i] ^ data;
    
ctx->P[i] ^= data;
  }

  
datal 0x00000000;
  
datar 0x00000000;

  for (
02+= 2) {
    
Blowfish_Encrypt(ctx, &datal, &datar);
    
ctx->P[i] = datal;
    
ctx->P[1] = datar;
  }

  for (
04; ++i) {
    for (
0256+= 2) {
      
Blowfish_Encrypt(ctx, &datal, &datar);
      
ctx->S[i][j] = datal;
      
ctx->S[i][1] = datar;
    }
  }
}
 

_________________
I have to return some video tapes.

Feed me a stray cat.


Top
 Profile  
Reply with quote  
PostPosted: Wed Aug 08, 2007 7:58 pm 
Offline
Komrade
Komrade
User avatar

Joined: Tue Mar 27, 2007 6:23 pm
Posts: 1354
Location: Mario Raceway, 1509.831, 217.198, -564.429
Title: Mario Kart 64 Hacker
Yeah, that got it, thanks. (And I guessed correctly what CARD_CT was! :P)

Is Kwurdi ever going to be finished, anyway? >_>

_________________
Image 143
HyperNova Software is now live (but may take a few tries to load) currently down; check out my PSP/DS/Game Boy/Windows/Linux homebrew, ROM hacks, and Gameshark codes!


Top
 Profile  
Reply with quote  
PostPosted: Wed Aug 08, 2007 8:15 pm 
Offline
Krew (Admin)
Krew (Admin)
User avatar

Joined: Sun Oct 01, 2006 9:26 pm
Posts: 3765
Title: All in a day's work.
No. Datel stole my thunder with Trainer Toolkit. So fuck them and fuck Kwurdi.

_________________
I have to return some video tapes.

Feed me a stray cat.


Top
 Profile  
Reply with quote  
PostPosted: Wed Aug 08, 2007 10:44 pm 
Offline
Komrade
Komrade
User avatar

Joined: Tue Mar 27, 2007 6:23 pm
Posts: 1354
Location: Mario Raceway, 1509.831, 217.198, -564.429
Title: Mario Kart 64 Hacker
Hm, it isn't able to read the ARDS. Just prints "encryObj" and quits. It seems to work if I comment this line in CardInit():
Code:
if (CardDecryptARM9Secure(&arm9_secure[0x4000], header)) return CARD_ERR_SECURE;

I can't tell whether that gets it to actually boot, because I still can't get any of the cards to boot. >_> (Also, I had to remove some exit(1);s in CardDecryptARM9Secure() and CardEncryptARM9Secure() so as not to crash the program when decryption fails.)

It's strange, though, as the ARDS' ARM9 source is 0x4000, so it should have a secure area. Does the BIOS not verify secure areas for cards with the autostart bit or something stupid like that? Or is that another fuckup in GBATek? The only unusual thing I see is the ARM9 destination and source addresses are a bit high (0x02100000), but those are still valid.

_________________
Image 143
HyperNova Software is now live (but may take a few tries to load) currently down; check out my PSP/DS/Game Boy/Windows/Linux homebrew, ROM hacks, and Gameshark codes!


Top
 Profile  
Reply with quote  
PostPosted: Thu Aug 09, 2007 4:46 pm 
Offline
Krew (Admin)
Krew (Admin)
User avatar

Joined: Sun Oct 01, 2006 9:26 pm
Posts: 3765
Title: All in a day's work.
That just means it doesn't have a secure area. You would be better off leaving that line alone and just accepting the CARD_ERR_SECURE return value as successful. But then you'll want to move the last // Get "In Game" card ID ... crap just above the secure area decryption line, which is not a bad idea to do, any way.

I'm not sure how the BIOS handles non-existent secure areas, so my lib just throws an error. There are also a few other cases where I didn't give a shit what the BIOS does, but I still watch for them and shut down when they occur. That's the reason for the exit() calls; there is no defined action to take, so take the safest action: exit the program.

_________________
I have to return some video tapes.

Feed me a stray cat.


Top
 Profile  
Reply with quote  
PostPosted: Thu Aug 09, 2007 11:43 pm 
Offline
Komrade
Komrade
User avatar

Joined: Tue Mar 27, 2007 6:23 pm
Posts: 1354
Location: Mario Raceway, 1509.831, 217.198, -564.429
Title: Mario Kart 64 Hacker
Yeah, I realized today at work what the problem is.

Quote:
Secure Area ID
The first 8 bytes of the secure area are containing the Secure Area ID, the ID is required (verified by BIOS boot code), the ID value changes during boot process:

Value Expl.
"encryObj" raw ID before encryption (raw ROM-image)
(encrypted) encrypted ID after encryption (encrypted ROM-image)
"encryObj" raw ID after decryption (verified by BIOS boot code)
E7FFDEFFh,E7FFDEFFh destroyed ID (overwritten by BIOS after verify)

If the decrypted ID does match, then the BIOS overwrites the first 8 bytes by E7FFDEFFh-values (ie. only the ID is destroyed). If the ID doesn't match, then the first 800h bytes (2K) are overwritten by E7FFDEFFh-values.

[...]

Avoiding Secure Area Encryption
WLAN files are reportedly same format as cartridges, but without Secure Area, so games with Secure Area cannot be booted via WLAN. No$gba can encrypt and decrypt Secure Areas only if the NDS BIOS-images are present. And, Nintendo's devkit doesn't seem to support Secure Area encryption of unreleased games.
So, unencrypted cartridges are more flexible in use. Ways to avoid encryption (which still work on real hardware) are:
1) Set NDS9 ROM offset to 4000h, and leave the first 800h bytes of the Secure Area 00h-filled, which can be (and will be) safely destroyed during loading; due to the missing "encryObj" ID; that method is used by Nintendo's devkit.
That must be what it's doing; ARM9 binary offset is 0x4000. I'll see if I can modify the decryption routine to do the same.

BTW, there's two things GBATek doesn't seem to mention that are giving me problems:
1) The ARM9 binary size in the header; is this the size of the compressed or decompressed binary?
2) Where does the code that loads the binaries run from? Since any part of main RAM could be overwritten during the load, it must be either running from somewhere else or using the size/destination fields to find a place that will be preserved. It'd be nice if I could emulate this behaviour exactly so as not to risk breaking things that rely on it.

[edit] Seems straightforward enough:
Code:
int CardDecryptARM9Secure(u8 *data, NDS_HEADER *nds_header) {
    int i;
    u8 buffer[0x800];
    u32 *buf32 = (u32*)buffer;
    u32 clear = 0xE7FFDEFF;

    header = nds_header;
    EncryptionSetup(&blowfish, (u32*)header->game_code, enc_key, 8);

    arm9len = (0x8000 - (header->arm9_src & 0xFFFFF000));
    if (arm9len > 0x0800) arm9len = 0x0800;

    memcpy(buffer, data, arm9len);
    Blowfish_Decrypt_mod(&blowfish, buf32);
    InitEncryptionF(8);
    Blowfish_Decrypt_mod(&blowfish, buf32);

    if (strncmp((char*)buffer, "encryObj", 8)) {
        //ConsolePrint("encryObj\n");
        //exit(1);
        //return CARD_ERR_SECURE;

        //On failure, wipe the first 0x800 bytes.
        for(i=0; i<0x200; i++)
           buf32[i] = clear;
    }

   //On success, wipe the first 8 bytes.
    buf32[0] = clear;
    buf32[1] = clear;

    i = (arm9len - 8);
    while (i > 0) {
        buf32 = &buf32[2];
        Blowfish_Decrypt_mod(&blowfish, buf32);
        i -= 8;
    }
    memcpy(data, buffer, arm9len);

    return CARD_ERR_NONE;
}
I moved the "get in-game card ID" to above the decryption too, since I see no reason for it not to do that just because the decryption fails. ARDS shows up fine now, though I still haven't got the actual loading part finished so I can't say for sure if it works.

_________________
Image 143
HyperNova Software is now live (but may take a few tries to load) currently down; check out my PSP/DS/Game Boy/Windows/Linux homebrew, ROM hacks, and Gameshark codes!


Top
 Profile  
Reply with quote  
PostPosted: Fri Aug 10, 2007 6:08 am 
Offline
Krew (Admin)
Krew (Admin)
User avatar

Joined: Sun Oct 01, 2006 9:26 pm
Posts: 3765
Title: All in a day's work.
You should just leave the decrypt function alone. Let it return with an error code. That will mean it can't decrypt (exactly what you want) but the whole processes essentially completed successfully any way.

Thus, in your call to CardInit(), you would just check the return value like this:
Code:
int ret CardInit(HEADERCARDIDSECURE_CARDIDINGAME_CARDID);
if ((
ret == CARD_ERR_NONE) || (ret == CARD_ERR_SECURE)) { // handle both return codes as success
   
...
}
else { 
// error handling
   
...


That way you do not destroy the data from the card. The secure area decryption is not completely ancillary, any way. The secure area is stored on the card in its encrypted form, so maybe that's how you want the CardRead() to return data. For booting games, though, it does need to be decrypted.

Booting the games on DS is so bloody simple, just about anyone should be able to do it.
Code:
    // Read ARM9 executable
    
CardRead((u8*)HEADER->arm9_dstHEADER->arm9_srcHEADER->arm9_size);

    
// Read ARM7 executable
    
CardRead((u8*)HEADER->arm7_dstHEADER->arm7_srcHEADER->arm7_size); 


Now that the executables are loaded, reset all of the hardware your program has initialized (screen, VRAM, audio, WiFi, etc.) And simply make both CPUs jump to the HEADER->arm7_entry and HEADER->arm9_entry locations. Tah-dah, the game boots.

So there are a few important aspect that your program is probably not doing properly. #1 is an init function which records all of the HW register values before setting them for your program to use. Along with a corresponding exit function to return all regs to their initial values. That will take care of resetting the hardware. And second is that your program probably loads the ARM9 executable into RAM and runs from 0x02000000, like almost all libnds programs do. You'll want to adjust your make file and linker script to move that up about 3MB so that you can load the ARM9 executable from the card into its proper location without overwriting your own program.

Kwurdi loads its ARM9 executable to 0x02280000.

_________________
I have to return some video tapes.

Feed me a stray cat.


Top
 Profile  
Reply with quote  
PostPosted: Fri Aug 10, 2007 2:10 pm 
Offline
Komrade
Komrade
User avatar

Joined: Tue Mar 27, 2007 6:23 pm
Posts: 1354
Location: Mario Raceway, 1509.831, 217.198, -564.429
Title: Mario Kart 64 Hacker
You don't have to decompress the ARM9 binary? O_o My boot code is running from ITCM (ARM9) and IWRAM (ARM7). It doesn't reset all of the hardware yet, are they that picky about it?

[edit] Fuck yes Mario 64 just booted. :D Thanks so much man. I finally found why, even after relocating to 0x02280000, it wouldn't work. Turns out when you do inline ASM, writing ".arm" generates ARM code - but does not tell the compiler "hey, execute this in ARM mode". Ended up running the ARM instructions in THUMB mode. <_< (Imagine that, I found a use for No$GBA.)

And to answer my own questions, the game didn't seem to care that I had not only not initialized the hardware but even changed the screen brightness. That's just one game though. And I didn't have to decompress the binary. (I was almost hoping I would have to, actually, so I could implement a simple patching system for things like the MKDS track hack. Hmmmmmmmmm... Nah, I'm not gonna try that just yet. Nitro Hax works. >_>)

Now we just need to find a way to put the card back into unencrypted mode without removing it. ;) (If only.)

_________________
Image 143
HyperNova Software is now live (but may take a few tries to load) currently down; check out my PSP/DS/Game Boy/Windows/Linux homebrew, ROM hacks, and Gameshark codes!


Top
 Profile  
Reply with quote  
PostPosted: Fri Aug 10, 2007 4:20 pm 
Offline
Krew (Admin)
Krew (Admin)
User avatar

Joined: Sun Oct 01, 2006 9:26 pm
Posts: 3765
Title: All in a day's work.
There are a lot of games which are picky about how the hardware is set before they boot. Monster House and some lame Shrek game are some good examples. You'll see those fuck up some time. ;)

The ARM9 crt0 does the decompression/autoloading for you, so all you have to do is read the executables into their location and start them.

Patching the executables at runtime is incredibly simple. You see HEADER->arm9_autoload ? It's a pointer to the autoload callback function. Just patch that instruction with a branch to your patching routine. After the you start the executables, they will synchronize and "auto-load" their sections into the proper memory locations. For example, the ARM9 crt0 loads the ITCM and DTCM data from the main executable data that you read from the card, and places them into the TCMs, and also clears the BSS sections, etc. etc. In the case of the ARM9, it also does the decompression. Then it will run this autoload callback function.

So far, I have never seen a game with an actual callback function (the instruction is always a BLR) but it is possible for games to have them, so you might want to do something more complex than a simple overwrite. For example, backup the original instruction, and have your own autoload callback restore the original instruction, and jump back to the original location, to return. Instruction caching should not be a problem, but that's also an easy fix, any way.


I suppose you can tell I've been hacking NDS too long... It's a shame I am so damn sick of it, or I could probably do some sweet hacks and shit. Bah.

_________________
I have to return some video tapes.

Feed me a stray cat.


Top
 Profile  
Reply with quote  
PostPosted: Fri Aug 10, 2007 5:45 pm 
Offline
Komrade
Komrade
User avatar

Joined: Tue Mar 27, 2007 6:23 pm
Posts: 1354
Location: Mario Raceway, 1509.831, 217.198, -564.429
Title: Mario Kart 64 Hacker
Hah, that's awesome. Thanks again, I'll see if I can take advantage of that.

_________________
Image 143
HyperNova Software is now live (but may take a few tries to load) currently down; check out my PSP/DS/Game Boy/Windows/Linux homebrew, ROM hacks, and Gameshark codes!


Top
 Profile  
Reply with quote  
PostPosted: Fri Aug 10, 2007 9:22 pm 
Offline
Komrade
Komrade
User avatar

Joined: Tue Mar 27, 2007 6:23 pm
Posts: 1354
Location: Mario Raceway, 1509.831, 217.198, -564.429
Title: Mario Kart 64 Hacker
Mario Kart sure does react strangely when I change things:
-If I don't wait for a keypress before loading the binary and signalling the ARM7 to launch it, the game won't boot at all. O_o Some sort of timing issue in my code I guess, every other game I've tried works fine.
-CardInit() takes a second or two to complete, while it's instant with all the others.
-The startup sound seems to be random, and at one point I got a new one I've never heard anyone mention before. Just one long "vrooooooooom" and Mario says "here we go" overtop of it. I couldn't reproduce it though.

_________________
Image 143
HyperNova Software is now live (but may take a few tries to load) currently down; check out my PSP/DS/Game Boy/Windows/Linux homebrew, ROM hacks, and Gameshark codes!


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 11, 2007 11:08 am 
Offline
Krew (Admin)
Krew (Admin)
User avatar

Joined: Sun Oct 01, 2006 9:26 pm
Posts: 3765
Title: All in a day's work.
The startup sound is supposed to be random, yes. If it isn't, then something is wrong.

To fix the timing issue (and there are a lot of games sensitive to it!) use IPC to make the ARM7 executable start first, then the ARM9. Also, make sure you are resetting the hardware. It does need to be taken care of, whether or not you believe it.

CardInit() can take up to 2 seconds to initialize some cards. Most notably are the cards with "strange" card IDs. For example, The Rub Rabbits. This is because the command buffer built is unusually large, due to differences in the encryption and initialization stages (the secure area reads are much smaller, so there are more commands to read the same amount of data; each with a specific timeout period). This is another thing gbatek doesn't mention. He's cut corners and generally made his documentation inaccurate.

_________________
I have to return some video tapes.

Feed me a stray cat.


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 11, 2007 5:25 pm 
Offline
Komrade
Komrade
User avatar

Joined: Tue Mar 27, 2007 6:23 pm
Posts: 1354
Location: Mario Raceway, 1509.831, 217.198, -564.429
Title: Mario Kart 64 Hacker
The actual boot procedure is like this on ARM9:
Wait for keypress
Tell ARM7 to enter wait loop
Load ARM9 binary
Load ARM7 binary
Initialize hardware and clear VRAM
Enter wait loop

Until it gets the keypress ARM7 is just waiting for messages as usual, so I'm thinking something like it's lagging behind and the wait loop isn't working correctly.

_________________
Image 143
HyperNova Software is now live (but may take a few tries to load) currently down; check out my PSP/DS/Game Boy/Windows/Linux homebrew, ROM hacks, and Gameshark codes!


Top
 Profile  
Reply with quote  
PostPosted: Mon Sep 10, 2007 12:44 am 
Offline
Kommunist
Kommunist

Joined: Wed Feb 28, 2007 5:52 pm
Posts: 79
Wow, nice code there, Parasyte. It looks much more stable than my version, which relies on the timing of the ARM7.

Just a few tips to add. If you've loaded the header correctly then you can start the loaded binaries by calling SWI 0 on both CPUs. This will put the CPU into supervisor mode before starting the game. To synchronize the CPUs, try having one send the other a signal to start then have both CPUs spin on the VCOUNT register until it reaches a predesignated value. Also, as cReDiAr told me, the (16 bit) value at 0x027ffc40 is very important. A value of 0 indicates that the binary was booted via WMB, while 1 indicates it was started from a DS card. Get this correct or else the games will boot but then crash when they can't find the WMB host.


Top
 Profile  
Reply with quote  
PostPosted: Mon Sep 10, 2007 6:12 am 
Offline
Krew (Admin)
Krew (Admin)
User avatar

Joined: Sun Oct 01, 2006 9:26 pm
Posts: 3765
Title: All in a day's work.
It's only important if your program was started by WiFiME ... in which case, much of the hardware has already been setup and otherwise destroys the boot process. FlashME causes similar problems. For example, it disables the lower screen (at least the 'no-warning' version does), and some games like New Super Mario Bros. will not re-enable it on their own.

_________________
I have to return some video tapes.

Feed me a stray cat.


Top
 Profile  
Reply with quote  
PostPosted: Mon Sep 10, 2007 11:33 am 
Offline
Komrade
Komrade
User avatar

Joined: Mon Oct 02, 2006 5:56 pm
Posts: 1978
Title: Mr. Bitches
What version of FlashMe would that be?
I've played New Super Mario Bros on my flashed DS (version 7) without problem.

_________________
Image
Image
<EggWerx> MetalOverlord: Dsman and lemmayoshi will be used for taco meat, ask mo he knows me.
jleemero wrote:
Being required to learn Java for a Comp Sci MAJOR is like being required to shit on a lawnmower to be an astronaut.


Top
 Profile  
Reply with quote  
PostPosted: Fri Sep 14, 2007 6:09 am 
Offline
Krew (Admin)
Krew (Admin)
User avatar

Joined: Sun Oct 01, 2006 9:26 pm
Posts: 3765
Title: All in a day's work.
v7 and older. If any software between FlashMe and NSMB reset that screen though, it's a moot point. During my dev time on Kwurdi, that is one of many (many, many, many) problems I had with it and other silly hacks.

_________________
I have to return some video tapes.

Feed me a stray cat.


Top
 Profile  
Reply with quote  
PostPosted: Fri Sep 14, 2007 8:51 am 
Offline
Komrade
Komrade
User avatar

Joined: Tue Mar 27, 2007 6:23 pm
Posts: 1354
Location: Mario Raceway, 1509.831, 217.198, -564.429
Title: Mario Kart 64 Hacker
Huh, I could swear I played NSMB with V7. Maybe two versions of the game, or an unannounced V7.1?

_________________
Image 143
HyperNova Software is now live (but may take a few tries to load) currently down; check out my PSP/DS/Game Boy/Windows/Linux homebrew, ROM hacks, and Gameshark codes!


Top
 Profile  
Reply with quote  
PostPosted: Sat Sep 15, 2007 9:29 am 
Offline
Kommunist
Kommunist
User avatar

Joined: Tue Oct 03, 2006 9:39 am
Posts: 312
Location: 4e-2f-41
I have FlashMe v7, and I can play NSMB.

_________________
Quote:
Fix your shitty signature, bitchcakes.

Ok, I did.


Top
 Profile  
Reply with quote  
PostPosted: Sun Sep 23, 2007 3:09 pm 
Offline
Krew (Admin)
Krew (Admin)
User avatar

Joined: Sun Oct 01, 2006 9:26 pm
Posts: 3765
Title: All in a day's work.
Parasyte wrote:
If any software between FlashMe and NSMB reset that screen though, it's a moot point.

PLZ 2 REED KTHX

_________________
I have to return some video tapes.

Feed me a stray cat.


Top
 Profile  
Reply with quote  
PostPosted: Wed Oct 24, 2007 5:10 pm 
Offline
Komrade
Komrade
User avatar

Joined: Tue Mar 27, 2007 6:23 pm
Posts: 1354
Location: Mario Raceway, 1509.831, 217.198, -564.429
Title: Mario Kart 64 Hacker
I had an idea. If you start up with the card already inserted (BIOS has read the secure area which has since been removed from memory), can you still read the rest of the card? I'd try it now but I've got a bunch of other things that need doing first. >_>

_________________
Image 143
HyperNova Software is now live (but may take a few tries to load) currently down; check out my PSP/DS/Game Boy/Windows/Linux homebrew, ROM hacks, and Gameshark codes!


Top
 Profile  
Reply with quote  
PostPosted: Sun Oct 28, 2007 1:20 pm 
Offline
Kommunist
Kommunist

Joined: Wed Oct 24, 2007 5:50 pm
Posts: 3
I have a couple of questions, but let assume (because it's true) that I don't know what this code does or why it needs to be done.

1. Is the idea of this code to boot a slot-1 card inserted after boot up, like in an AR clone?

2. If so, can someone provide me a stand alone .nds that only boots a slot-1 card? I don't like messing with libs when I don't know what I'm doing and potentially stopping other programs from compiling.

Thankyou.


Top
 Profile  
Reply with quote  
PostPosted: Sun Oct 28, 2007 1:25 pm 
Offline
Komrade
Komrade
User avatar

Joined: Tue Mar 27, 2007 6:23 pm
Posts: 1354
Location: Mario Raceway, 1509.831, 217.198, -564.429
Title: Mario Kart 64 Hacker
spinal wrote:
1. Is the idea of this code to boot a slot-1 card inserted after boot up, like in an AR clone?
Not exactly. This code is just to read from the card. Booting it is fairly simple from there.
Quote:
2. If so, can someone provide me a stand alone .nds that only boots a slot-1 card? I don't like messing with libs when I don't know what I'm doing and potentially stopping other programs from compiling.
Yes, but why?

_________________
Image 143
HyperNova Software is now live (but may take a few tries to load) currently down; check out my PSP/DS/Game Boy/Windows/Linux homebrew, ROM hacks, and Gameshark codes!


Top
 Profile  
Reply with quote  
PostPosted: Sun Oct 28, 2007 1:35 pm 
Offline
Kommunist
Kommunist

Joined: Wed Oct 24, 2007 5:50 pm
Posts: 3
Cos I'm too lazy to learn to do it myself.

Wouldn't this be a 100% way to reset back to the inserted flashcards menu/default app, just check if fat1: works, then use this to reload the slot-1 card, else use passme code to boot slot-2?


Top
 Profile  
Reply with quote  
PostPosted: Sun Oct 28, 2007 1:37 pm 
Offline
Komrade
Komrade
User avatar

Joined: Tue Mar 27, 2007 6:23 pm
Posts: 1354
Location: Mario Raceway, 1509.831, 217.198, -564.429
Title: Mario Kart 64 Hacker
No, I mean, why do you want a program that does that?

_________________
Image 143
HyperNova Software is now live (but may take a few tries to load) currently down; check out my PSP/DS/Game Boy/Windows/Linux homebrew, ROM hacks, and Gameshark codes!


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 56 posts ]  Go to page 1, 2  Next

All times are UTC - 8 hours [ DST ]


Who is online

Users browsing this forum: Brandwatch Magpie-Crawler and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group