void CNANDContentLoader::GetKeyFromTicket(u8* pTicket, u8* pTicketKey) { u8 CommonKey[16] = {0xeb,0xe4,0x2a,0x22,0x5e,0x85,0x93,0xe4,0x48,0xd9,0xc5,0x45,0x73,0x81,0xaa,0xf7}; u8 IV[16]; memset(IV, 0, sizeof IV); memcpy(IV, pTicket + 0x01dc, 8); AESDecode(CommonKey, IV, pTicket + 0x01bf, 16, pTicketKey); }
std::vector<u8> CNANDContentLoader::GetKeyFromTicket(const std::vector<u8>& ticket) { const u8 common_key[16] = {0xeb,0xe4,0x2a,0x22,0x5e,0x85,0x93,0xe4,0x48,0xd9,0xc5,0x45,0x73,0x81,0xaa,0xf7}; u8 iv[16] = {}; std::copy(&ticket[0x01DC], &ticket[0x01DC + 8], iv); return AESDecode(common_key, iv, &ticket[0x01BF], 16); }
void CNANDContentLoader::InitializeContentEntries(const std::vector<u8>& tmd, const std::vector<u8>& decrypted_title_key, const std::vector<u8>& data_app) { m_Content.resize(m_NumEntries); std::array<u8, 16> iv; u32 data_app_offset = 0; for (u32 i = 0; i < m_NumEntries; i++) { const u32 entry_offset = 0x24 * i; SNANDContent& content = m_Content[i]; content.m_ContentID = Common::swap32(&tmd[entry_offset + 0x01E4]); content.m_Index = Common::swap16(&tmd[entry_offset + 0x01E8]); content.m_Type = Common::swap16(&tmd[entry_offset + 0x01EA]); content.m_Size = static_cast<u32>(Common::swap64(&tmd[entry_offset + 0x01EC])); const auto header_begin = std::next(tmd.begin(), entry_offset + 0x01E4); const auto header_end = std::next(header_begin, ArraySize(content.m_Header)); std::copy(header_begin, header_end, content.m_Header); const auto hash_begin = std::next(tmd.begin(), entry_offset + 0x01F4); const auto hash_end = std::next(hash_begin, ArraySize(content.m_SHA1Hash)); std::copy(hash_begin, hash_end, content.m_SHA1Hash); if (m_IsWAD) { u32 rounded_size = Common::AlignUp(content.m_Size, 0x40); iv.fill(0); std::copy(&tmd[entry_offset + 0x01E8], &tmd[entry_offset + 0x01E8 + 2], iv.begin()); content.m_Data = std::make_unique<CNANDContentDataBuffer>(AESDecode( decrypted_title_key.data(), iv.data(), &data_app[data_app_offset], rounded_size)); data_app_offset += rounded_size; continue; } std::string filename; if (content.m_Type & 0x8000) // shared app filename = CSharedContent::AccessInstance().GetFilenameFromSHA1(content.m_SHA1Hash); else filename = StringFromFormat("%s/%08x.app", m_Path.c_str(), content.m_ContentID); content.m_Data = std::make_unique<CNANDContentDataFile>(filename); // Be graceful about incorrect TMDs. if (File::Exists(filename)) content.m_Size = static_cast<u32>(File::GetSize(filename)); } }
void CNANDContentLoader::InitializeContentEntries(const std::vector<u8>& tmd, const std::vector<u8>& decrypted_title_key, const std::vector<u8>& data_app) { m_Content.resize(m_numEntries); std::array<u8, 16> iv; u32 data_app_offset = 0; for (u32 i = 0; i < m_numEntries; i++) { const u32 entry_offset = 0x24 * i; SNANDContent& content = m_Content[i]; content.m_ContentID = Common::swap32(&tmd[entry_offset + 0x01E4]); content.m_Index = Common::swap16(&tmd[entry_offset + 0x01E8]); content.m_Type = Common::swap16(&tmd[entry_offset + 0x01EA]); content.m_Size = static_cast<u32>(Common::swap64(&tmd[entry_offset + 0x01EC])); std::copy(&tmd[entry_offset + 0x01E4], &tmd[entry_offset + 0x01E4 + 36], content.m_Header); std::copy(&tmd[entry_offset + 0x01F4], &tmd[entry_offset + 0x01F4 + 20], content.m_SHA1Hash); if (m_isWAD) { u32 rounded_size = ROUND_UP(content.m_Size, 0x40); iv.fill(0); std::copy(&tmd[entry_offset + 0x01E8], &tmd[entry_offset + 0x01E8 + 2], iv.begin()); content.m_data = AESDecode(decrypted_title_key.data(), iv.data(), &data_app[data_app_offset], rounded_size); data_app_offset += rounded_size; continue; } if (content.m_Type & 0x8000) // shared app content.m_Filename = CSharedContent::AccessInstance().GetFilenameFromSHA1(content.m_SHA1Hash); else content.m_Filename = StringFromFormat("%s/%08x.app", m_Path.c_str(), content.m_ContentID); // Be graceful about incorrect TMDs. if (File::Exists(content.m_Filename)) content.m_Size = static_cast<u32>(File::GetSize(content.m_Filename)); } }
bool CNANDContentLoader::Initialize(const std::string& _rName) { if (_rName.empty()) return false; m_Path = _rName; WiiWAD Wad(_rName); u8* pDataApp = nullptr; u8* pTMD = nullptr; u8 DecryptTitleKey[16]; u8 IV[16]; if (Wad.IsValid()) { m_isWAD = true; m_TIKSize = Wad.GetTicketSize(); m_TIK = new u8[m_TIKSize]; memcpy(m_TIK, Wad.GetTicket(), m_TIKSize); GetKeyFromTicket(m_TIK, DecryptTitleKey); u32 pTMDSize = Wad.GetTMDSize(); pTMD = new u8[pTMDSize]; memcpy(pTMD, Wad.GetTMD(), pTMDSize); pDataApp = Wad.GetDataApp(); } else { std::string TMDFileName(m_Path); if ('/' == *TMDFileName.rbegin()) TMDFileName += "title.tmd"; else m_Path = TMDFileName.substr(0, TMDFileName.find("title.tmd")); File::IOFile pTMDFile(TMDFileName, "rb"); if (!pTMDFile) { WARN_LOG(DISCIO, "CreateFromDirectory: error opening %s", TMDFileName.c_str()); return false; } u32 pTMDSize = (u32)File::GetSize(TMDFileName); pTMD = new u8[pTMDSize]; pTMDFile.ReadBytes(pTMD, (size_t)pTMDSize); pTMDFile.Close(); } memcpy(m_TMDView, pTMD + 0x180, TMD_VIEW_SIZE); memcpy(m_TMDHeader, pTMD, TMD_HEADER_SIZE); m_TitleVersion = Common::swap16(pTMD + 0x01dc); m_numEntries = Common::swap16(pTMD + 0x01de); m_BootIndex = Common::swap16(pTMD + 0x01e0); m_TitleID = Common::swap64(pTMD + 0x018c); m_IosVersion = Common::swap16(pTMD + 0x018a); m_Country = *(u8*)&m_TitleID; if (m_Country == 2) // SYSMENU m_Country = GetSysMenuRegion(m_TitleVersion); m_Content.resize(m_numEntries); for (u32 i=0; i<m_numEntries; i++) { SNANDContent& rContent = m_Content[i]; rContent.m_ContentID = Common::swap32(pTMD + 0x01e4 + 0x24*i); rContent.m_Index = Common::swap16(pTMD + 0x01e8 + 0x24*i); rContent.m_Type = Common::swap16(pTMD + 0x01ea + 0x24*i); rContent.m_Size= (u32)Common::swap64(pTMD + 0x01ec + 0x24*i); memcpy(rContent.m_SHA1Hash, pTMD + 0x01f4 + 0x24*i, 20); memcpy(rContent.m_Header, pTMD + 0x01e4 + 0x24*i, 36); if (m_isWAD) { u32 RoundedSize = ROUND_UP(rContent.m_Size, 0x40); rContent.m_pData = new u8[RoundedSize]; memset(IV, 0, sizeof IV); memcpy(IV, pTMD + 0x01e8 + 0x24*i, 2); AESDecode(DecryptTitleKey, IV, pDataApp, RoundedSize, rContent.m_pData); pDataApp += RoundedSize; continue; } rContent.m_pData = nullptr; if (rContent.m_Type & 0x8000) // shared app { rContent.m_Filename = CSharedContent::AccessInstance().GetFilenameFromSHA1(rContent.m_SHA1Hash); } else { rContent.m_Filename = StringFromFormat("%s/%08x.app", m_Path.c_str(), rContent.m_ContentID); } // Be graceful about incorrect tmds. if (File::Exists(rContent.m_Filename)) { rContent.m_Size = (u32) File::GetSize(rContent.m_Filename); } } delete [] pTMD; return true; }
/* 解析文件,输出文件名 返回:-1---打开错误,-2---文件不合法 >0----文件解析成功 */ JNIEXPORT jint JNICALL Java_com_example_android_bluetoothlegatt_MyNative_update_1fileParse (JNIEnv *env, jobject, jbyteArray file_name) { char *fileName = (char *) env->GetByteArrayElements(file_name,0 ); FILE *fp; char buf[1024], *buf_temp, tempBuf[100]; int versionStr_flag; int fileNum, fileSize, imageOffset; int i, j; char *pTempStr; unsigned long crc; tR11_UPDATEFILE_HEADER imageHeader[FILE_NUM_MAX]; tR11_UPDATEFILE_HEADER *pHeader; U8 headerBuf[1024]; fp = fopen(fileName, "rb"); // for (int i = 0; i < 32; i++) // { // LOGI("string %X", headerBuf[i], 1024);//去字符串s% // } if (fp == NULL) { /* 文件打开错误 */ LOGI("文件打开错误"); return -1; } fread(&headerBuf[0], 1024, 1, fp); // for (int i = 0; i < 32; i++) // { // LOGI("string %X", headerBuf[i], 1024);//去字符串s% // } //fread(&imageHeader[0], FILE_NUM_MAX*sizeof(tR11_UPDATEFILE_HEADER), 1, fp); { /* 信息头AES解密 */ #include "aes.h" extern int AESDecode (BYTE *pUserKey, int keyLen, const char* srcString, int srcLen, char** dstString, int* dstLen); #define USER_KEY_LEN 16 const BYTE userKey[USER_KEY_LEN]= { 0xEA, 0x45, 0x11, 0xEB, 0x02, 0xCE, 0x56, 0xAE, 0xDE, 0x52, 0xEE, 0x42, 0xFC, 0x32, 0x7D, 0xCA }; int ret; char *pcDecodeDst = NULL; int dstLen = 0; ret = AESDecode((BYTE *)userKey, USER_KEY_LEN, (const char *)headerBuf, 1024, &pcDecodeDst, &dstLen); if (ret == 0) { memcpy(&imageHeader[0], pcDecodeDst, FILE_NUM_MAX*sizeof(tR11_UPDATEFILE_HEADER)); } // for (int i = 0; i < 48; i++) // { // LOGI("string %X",(jbyte) pcDecodeDst[i]);//去字符串s% // } free(pcDecodeDst); } fileNum = 0; for (i=0; i<FILE_NUM_MAX; i++) { pHeader = &imageHeader[i]; /* 1. 判断标识 */ // for (int j = 0; j < 16; j++) // { // LOGI("string %X", pHeader->idStr[j]);//去字符串s% // } int result = memcmp(& pHeader->idStr[0], R11_IMAGE_ID_STR, R11_IMAGE_ID_STR_LEN); if ( result != 0) { /* 标识错误,不再继续查找 */ // for (int j = 0; j < 16; j++) // { // LOGI("string %X", pHeader->idStr[j]);//去字符串s% // } // for (int j = 0; j < 16; j++) // { // LOGI("string %X", imageHeader[i].idStr[j]);//去字符串s% // } LOGI("标识错误,不再继续查找 ,返回存在的头文件数量"); break; } /* 2. 判断版本号字符 */ versionStr_flag = 0; pTempStr = &pHeader->versionStr[0]; for (j=0; j<9; j++) { if (pTempStr[j] != '.' && (pTempStr[j] < '0' || pTempStr[j] > '9')) { /* 版本号字符错误 */ LOGI("版本号字符错误 "); versionStr_flag = -1; break; } } if (versionStr_flag == -1) { /* 版本号字符错误,不再继续查找 */ LOGI("版本号字符错误,不再继续查找 "); break; } /* 3. 判断硬件信息 */ if (pHeader->hwInfo == 0) { /* 硬件信息错误 */ LOGI("硬件信息错误 "); break; } /* 4. 判断升级数据大小 */ if (pHeader->imageSize < R11_IMAGE_SIZE_MIN || pHeader->imageSize > R11_IMAGE_SIZE_MAX) { /* 升级数据大小错误,不再继续查找 */ LOGI("升级数据大小错误,不再继续查找 "); break; } memcpy(&r11_updateImage[fileNum].header, pHeader, sizeof(tR11_UPDATEFILE_HEADER)); buf_temp = &r11_updateImage[fileNum].data[0]; fseek(fp, pHeader->imageOffset, SEEK_SET); fread(buf_temp, pHeader->imageSize, 1, fp); fileNum++; } fclose(fp); if (fileNum == 0) { /* 没有找到合法的image信息,文件错误 */ LOGI("没有找到合法的image信息,文件错误 "); return -2; } validImageNum = fileNum; env->ReleaseByteArrayElements(file_name, (jbyte*)fileName, 1); return fileNum; }