nfp: Fix corruption, correct structs and use write counters (#310)

This commit is contained in:
Narr the Reg 2022-09-29 06:00:46 -05:00 committed by GitHub
parent 3fb4b5e26c
commit 3767257220
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 82 additions and 38 deletions

View File

@ -261,7 +261,7 @@ void amiiboEncrypt(AmiiboRawNFCData* nfcOutput)
amiiboCrypto_internalToNfcFormat(&internalCopy, nfcOutput);
// restore NFC values that aren't part of the internal representation
memcpy(nfcOutput->lockBytes, nfp_data.amiiboNFCData.lockBytes, 4);
memcpy(nfcOutput->dynamicLock, nfp_data.amiiboNFCData.dynamicLock, 4);
memcpy(nfcOutput->cfg0, nfp_data.amiiboNFCData.cfg0, 4);
memcpy(nfcOutput->cfg1, nfp_data.amiiboNFCData.cfg1, 4);
}

View File

@ -25,16 +25,20 @@ void nnNfpUnlock()
struct AmiiboInternal
{
/* +0x000 */ uint32 ukn000;
/* +0x004 */ uint32 ukn004;
/* +0x000 */ uint16 lockBytes;
/* +0x002 */ uint16 staticLock;
/* +0x004 */ uint32 cc;
/* +0x008 */ uint8 dataHMAC[32];
/* +0x028 */ uint32 ukn028;
/* +0x028 */ uint8 ukn_A5; // always 0xA5
/* +0x029 */ uint8 writeCounterHigh;
/* +0x029 */ uint8 writeCounterLow;
/* +0x02B */ uint8 unk02B;
/* encrypted region starts here */
struct
{
/* +0x02C */ uint8 flags;
/* +0x02D */ uint8 countryCode;
/* +0x02E */ uint16be writeCounter;
/* +0x02E */ uint16be crcWriteCounter;
/* +0x030 */ uint16be date1;
/* +0x032 */ uint16be date2;
/* +0x034 */ uint32be crc;
@ -42,7 +46,7 @@ struct AmiiboInternal
/* +0x04C */ uint8 mii[0x60];
/* +0x0AC */ uint32be appDataTitleIdHigh;
/* +0x0B0 */ uint32be appDataTitleIdLow;
/* +0x0B4 */ uint16be writeCounter2;
/* +0x0B4 */ uint16be appWriteCounter;
/* +0x0B6 */ uint16be appDataIdHigh;
/* +0x0B8 */ uint16be appDataIdLow;
/* +0x0BA */ uint16be ukn0BA;
@ -70,11 +74,19 @@ struct AmiiboInternal
/* +0x0DC */ uint8 applicationData[0xD8];
/* encrypted region ends here */
/* +0x1B4 */ uint8 tagHMAC[32];
/* +0x1D4 */ uint32 ukn1D4;
/* +0x1D8 */ uint32 ukn1D8;
/* +0x1DC */ uint32 ukn1DC;
/* +0x1E0 */ uint8 ukn1E0[0x20];
/* +0x200 */ uint8 ukn200[0x8];
/* +0x1D4 */ uint8 ntagSerial[7];
/* +0x1DB */ uint8 nintendoId;
struct
{
/* +0x1DC */ uint8 gameAndCharacterId[2];
/* +0x1DE */ uint8 characterVariation;
/* +0x1DF */ uint8 amiiboFigureType;
/* +0x1E0 */ uint8 amiiboModelNumber[2];
/* +0x1E2 */ uint8 amiiboSeries;
/* +0x1E3 */ uint8 ukn_02; // always 0x02 ?
/* +0x1E4 */ uint8 ukn5C[4];
}amiiboIdentificationBlock;
/* +0x1E8 */ uint8 keygenSalt[32];
};
static_assert(sizeof(AmiiboInternal) == 0x208);
@ -109,18 +121,21 @@ union AmiiboRawNFCData
};
struct
{
uint8 rawByte[16*4];
uint8 rawByte[16 * 4];
};
struct
{
/*+0x000 */ uint8 ntagSerial[9];
/*+0x009 */ uint8 ukn009;
/*+0x00A */ uint8 lockBytes[2];
/*+0x00C */ uint8 cc[4];
/*+0x010 */ uint8 ukn010[4];
/*+0x014 */ uint8 ukn014[32]; // crypto related?
/*+0x034 */ uint8 tagHMAC[32]; // data hmac
/*+0x054 */
/* +0x000 */ uint8 ntagSerial[7];
/* +0x007 */ uint8 nintendoId;
/* +0x008 */ uint8 lockBytes[2];
/* +0x00A */ uint8 staticLock[2];
/* +0x00C */ uint8 cc[4]; // compatibility container
/* +0x010 */ uint8 ukn_A5; // always 0xA5
/* +0x011 */ uint8 writeCounter[2];
/* +0x013 */ uint8 unk013;
/* +0x014 */ uint8 encryptedSettings[32];
/* +0x034 */ uint8 tagHMAC[32]; // data hmac
/* +0x054 */
struct
{
/* +0x54 */ uint8 gameAndCharacterId[2];
@ -129,15 +144,20 @@ union AmiiboRawNFCData
/* +0x58 */ uint8 amiiboModelNumber[2];
/* +0x5A */ uint8 amiiboSeries;
/* +0x5B */ uint8 ukn_02; // always 0x02 ?
/* +0x5C */ uint8 ukn00[0x80-0x5C]; // not part of identification block?
/* +0x5C */ uint8 ukn5C[4];
}amiiboIdentificationBlock;
/*+0x080 */ uint8 dataHMAC[32];
/*+0x0A0 */ uint8 ukn0A0[0x114];
/*+0x1B4 */ uint8 ukn1B4[0x54];
/*+0x208 */ uint8 lockBytes208[4];
/*+0x20C */ uint8 cfg0[4];
/*+0x210 */ uint8 cfg1[4];
/* +0x060 */ uint8 keygenSalt[32];
/* +0x080 */ uint8 dataHMAC[32];
/* +0x0A0 */ uint8 encryptedMii[0x60];
/* +0x100 */ uint8 encryptedTitleId[8];
/* +0x108 */ uint8 encryptedApplicationWriteCounter[2];
/* +0x10A */ uint8 encryptedApplicationAreaId[4];
/* +0x10E */ uint8 ukn10E[2];
/* +0x110 */ uint8 unk110[32];
/* +0x130 */ uint8 encryptedApplicationArea[0xD8];
/* +0x208 */ uint8 dynamicLock[4];
/* +0x20C */ uint8 cfg0[4];
/* +0x210 */ uint8 cfg1[4];
};
};
@ -400,7 +420,7 @@ typedef struct
typedef struct
{
/* +0x00 */ nfpDate_t date;
/* +0x04 */ uint16be writeCount;
/* +0x04 */ uint8 writeCount[2];
/* +0x06 */ uint8 characterId[3];
/* +0x09 */ uint8 amiiboSeries;
/* +0x0A */ uint16be number;
@ -435,6 +455,9 @@ void nnNfpExport_GetNfpCommonInfo(PPCInterpreter_t* hCPU)
forceLogDebug_printf("GetNfpCommonInfo(0x%08x)");
commonInfo->writeCount[0] = nfp_data.amiiboNFCData.writeCounter[0];
commonInfo->writeCount[1] = nfp_data.amiiboNFCData.writeCounter[1];
commonInfo->characterId[0] = nfp_data.amiiboNFCData.amiiboIdentificationBlock.gameAndCharacterId[0];
commonInfo->characterId[1] = nfp_data.amiiboNFCData.amiiboIdentificationBlock.gameAndCharacterId[1];
commonInfo->characterId[2] = nfp_data.amiiboNFCData.amiiboIdentificationBlock.characterVariation;
@ -590,6 +613,8 @@ void nnNfpExport_WriteApplicationArea(PPCInterpreter_t* hCPU)
for (uint32 i = len; i < sizeof(nfp_data.amiiboInternal.applicationData); i++)
nfp_data.amiiboInternal.applicationData[i] = rand() & 0xFF;
nfp_data.amiiboInternal.amiiboSettings.appWriteCounter = nfp_data.amiiboInternal.amiiboSettings.appWriteCounter + 1;
osLib_returnFromFunction(hCPU, BUILD_NN_RESULT(NN_RESULT_LEVEL_SUCCESS, NN_RESULT_MODULE_NN_NFP, 0));
}
@ -629,6 +654,7 @@ void nnNfpExport_CreateApplicationArea(PPCInterpreter_t* hCPU)
nfp_data.amiiboInternal.amiiboSettings.setAppDataAppId(createInfo->appAreaId);
nfp_data.amiiboInternal.amiiboSettings.flags |= 0x20; // set application data exists bit
nfp_data.amiiboInternal.amiiboSettings.appWriteCounter = nfp_data.amiiboInternal.amiiboSettings.appWriteCounter + 1;
nfp_data.hasOpenApplicationArea = false;
@ -666,6 +692,7 @@ void nnNfpExport_DeleteApplicationArea(PPCInterpreter_t* hCPU)
nfp_data.amiiboInternal.amiiboSettings.setAppDataAppId(0);
nfp_data.amiiboInternal.amiiboSettings.flags &= ~0x20;
nfp_data.amiiboInternal.amiiboSettings.appWriteCounter = nfp_data.amiiboInternal.amiiboSettings.appWriteCounter + 1;
// this API forces a flush
if (!nnNfp_writeCurrentAmiibo())
@ -760,6 +787,17 @@ bool nnNfp_touchNfcTagFromFile(const wchar_t* filePath, uint32* nfcError)
memcpy(&rawData, nfcData->data(), sizeof(AmiiboRawNFCData));
// verify if the file is a valid ntag215/amiibo file
if (rawData.dynamicLock[0] != 0x01 || rawData.dynamicLock[1] != 0x00 || rawData.dynamicLock[2] != 0x0F || rawData.dynamicLock[3] != 0xBD)
{
// Temporary workaround to fix corrupted files by old cemu versions
rawData.dynamicLock[0] = 0x01;
rawData.dynamicLock[1] = 0x00;
rawData.dynamicLock[2] = 0x0F;
rawData.dynamicLock[3] = 0xBD;
// *nfcError = NFC_ERROR_INVALID_FILE_FORMAT;
// return false;
}
if (rawData.cfg0[0] != 0x00 || rawData.cfg0[1] != 0x00 || rawData.cfg0[2] != 0x00 || rawData.cfg0[3] != 0x04)
{
*nfcError = NFC_ERROR_INVALID_FILE_FORMAT;
@ -770,7 +808,7 @@ bool nnNfp_touchNfcTagFromFile(const wchar_t* filePath, uint32* nfcError)
*nfcError = NFC_ERROR_INVALID_FILE_FORMAT;
return false;
}
if (rawData.lockBytes[0] != 0x0F || rawData.lockBytes[1] != 0xE0 )
if (rawData.staticLock[0] != 0x0F || rawData.staticLock[1] != 0xE0)
{
*nfcError = NFC_ERROR_INVALID_FILE_FORMAT;
return false;
@ -789,10 +827,10 @@ bool nnNfp_touchNfcTagFromFile(const wchar_t* filePath, uint32* nfcError)
serialNumber[3] = rawData.ntagSerial[4];
serialNumber[4] = rawData.ntagSerial[5];
serialNumber[5] = rawData.ntagSerial[6];
serialNumber[6] = rawData.ntagSerial[7];
serialNumber[6] = rawData.nintendoId;
uint8 serialCheckByte0 = rawData.ntagSerial[3];
uint8 serialCheckByte1 = rawData.ntagSerial[8];
uint8 serialCheckByte1 = rawData.lockBytes[0];
uint8 bcc0 = serialNumber[0] ^ serialNumber[1] ^ serialNumber[2] ^ 0x88;
uint8 bcc1 = serialNumber[3] ^ serialNumber[4] ^ serialNumber[5] ^ serialNumber[6];
@ -830,6 +868,12 @@ bool nnNfp_writeCurrentAmiibo()
nnNfpUnlock();
return false;
}
uint16 writeCounter = nfp_data.amiiboInternal.writeCounterLow + (nfp_data.amiiboInternal.writeCounterHigh << 8);
writeCounter++;
nfp_data.amiiboInternal.writeCounterLow = writeCounter & 0xFF;
nfp_data.amiiboInternal.writeCounterHigh = (writeCounter >> 8) & 0xFF;
// open file for writing
FileStream* fs = FileStream::openFile2(nfp_data.amiiboPath, true);
if (!fs)