Wii Save Export can now successfully create data.bin files that the wii will accept as long as the user has their wii's private keys.

requires NG-id, NG-key-id, NG-priv, NG-sig
sorry for not fixing this sooner, I forgot that I cleared my wiis private keys before the initial commit

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@7642 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
LPFaint99 2011-06-27 06:19:23 +00:00
parent 46b95db6c7
commit f95bcfc1c2
2 changed files with 95 additions and 74 deletions

View file

@ -33,18 +33,16 @@ const u8 SDKey[16] = {0xAB, 0x01, 0xB9, 0xD8, 0xE1, 0x62, 0x2B, 0x08,
const u8 MD5_BLANKER[0x10] = {0x0E, 0x65, 0x37, 0x81, 0x99, 0xBE, 0x45, 0x17,
0xAB, 0x06, 0xEC, 0x22, 0x45, 0x1A, 0x57, 0x93};
CWiiSaveCrypted::CWiiSaveCrypted(const char* FileName, u64 title)
: _saveGameTitle(title)
CWiiSaveCrypted::CWiiSaveCrypted(const char* FileName, u64 TitleID)
: m_TitleID(TitleID)
{
Common::ReadReplacements(replacements);
strcpy(pathData_bin, FileName);
memcpy(SD_IV, "\x21\x67\x12\xE6\xAA\x1F\x68\x9F\x95\xC5\xA2\x23\x24\xDC\x6A\x98", 0x10);
if (!title)
if (!TitleID) // Import
{
AES_set_decrypt_key(SDKey, 128, &m_AES_KEY);
AES_set_decrypt_key(SDKey, 128, &m_AES_KEY);
do
{
b_valid = true;
@ -66,12 +64,39 @@ CWiiSaveCrypted::CWiiSaveCrypted(const char* FileName, u64 title)
if (getPaths(true))
{
memset(&keys, 0, sizeof(_keys));
std::string keysdir = File::GetSysDirectory() + "/Wii/";
std::string NG_id = keysdir + "NG-id";
std::string NG_key_id = keysdir + "NG-key-id";
std::string NG_priv = keysdir + "NG-priv";
std::string NG_sig = keysdir + "NG-sig";
if (!File::Exists(NG_id) || !File::Exists(NG_key_id) ||
!File::Exists(NG_priv) ||!File::Exists(NG_sig))
{
if (!AskYesNoT("Wii Private Keys not found, the exported save will only work with dolphin\n Continue?"))
{
PanicAlertT("Key files not found, save will not copy to wii\nPlace NG-id, NG-key-id, NG-priv, and NG-sig in the folder\n%s", keysdir.c_str());
return;
}
}
else
{
File::IOFile fp_key(NG_id, "rb");
fp_key.ReadBytes(keys.NG_id, 4);
fp_key.Open(NG_key_id, "rb");
fp_key.ReadBytes(keys.NG_key_id, 4);
fp_key.Open(NG_priv, "rb");
fp_key.ReadBytes(keys.NG_priv, 30);
fp_key.Open(NG_sig, "rb");
fp_key.ReadBytes(keys.NG_sig, 60);
}
do
{
b_valid = true;
WriteHDR();
WriteBKHDR();
ExportWiiSaveFiles();
ExportWiiSaveFiles();
do_sig();
if (b_valid)
{
@ -110,7 +135,7 @@ void CWiiSaveCrypted::ReadHDR()
b_valid = false;
return;
}
_saveGameTitle = Common::swap64(_header.hdr.SaveGameTitle);
m_TitleID = Common::swap64(_header.hdr.SaveGameTitle);
memcpy(md5_file, _header.hdr.Md5, 0x10);
memcpy(_header.hdr.Md5, MD5_BLANKER, 0x10);
@ -126,10 +151,10 @@ void CWiiSaveCrypted::ReadHDR()
b_valid = false;
return;
}
if (!File::Exists(pathBanner_bin) || AskYesNoT("%s already exists, overwrite?", pathBanner_bin))
if (!File::Exists(BannerFilePath) || AskYesNoT("%s already exists, overwrite?", BannerFilePath.c_str()))
{
INFO_LOG(CONSOLE, "creating file %s", pathBanner_bin);
File::IOFile fpBanner_bin(pathBanner_bin, "wb");
INFO_LOG(CONSOLE, "creating file %s", BannerFilePath.c_str());
File::IOFile fpBanner_bin(BannerFilePath, "wb");
fpBanner_bin.WriteBytes(_header.BNR, _bannerSize);
}
}
@ -139,14 +164,15 @@ void CWiiSaveCrypted::WriteHDR()
if (!b_valid) return;
memset(&_header, 0, HEADER_SZ);
_header.hdr.BannerSize = Common::swap32(File::GetSize(pathBanner_bin));
u32 bannerSize = File::GetSize(BannerFilePath);
_header.hdr.BannerSize = Common::swap32(bannerSize);
_header.hdr.SaveGameTitle = Common::swap64(_saveGameTitle);
_header.hdr.SaveGameTitle = Common::swap64(m_TitleID);
memcpy(_header.hdr.Md5, MD5_BLANKER, 0x10);
_header.hdr.Permissions = 0x3C;//0x35;
_header.hdr.Permissions = 0x35;
File::IOFile fpBanner_bin(pathBanner_bin, "rb");
if (!fpBanner_bin.ReadBytes(_header.BNR, Common::swap32(_header.hdr.BannerSize)))
File::IOFile fpBanner_bin(BannerFilePath, "rb");
if (!fpBanner_bin.ReadBytes(_header.BNR, bannerSize))
{
PanicAlertT("Failed to read banner.bin");
b_valid = false;
@ -202,8 +228,8 @@ void CWiiSaveCrypted::ReadBKHDR()
if (_sizeOfFiles + FULL_CERT_SZ != _totalSize)
WARN_LOG(CONSOLE, "Size(%x) + cert(%x) does not equal totalsize(%x)", _sizeOfFiles, FULL_CERT_SZ, _totalSize);
if (_saveGameTitle != Common::swap64(bkhdr.SaveGameTitle))
WARN_LOG(CONSOLE, "encrypted title (%llx) does not match unencrypted title (%llx)", _saveGameTitle, Common::swap64(bkhdr.SaveGameTitle));
if (m_TitleID != Common::swap64(bkhdr.SaveGameTitle))
WARN_LOG(CONSOLE, "encrypted title (%llx) does not match unencrypted title (%llx)", m_TitleID, Common::swap64(bkhdr.SaveGameTitle));
}
void CWiiSaveCrypted::WriteBKHDR()
@ -212,21 +238,15 @@ void CWiiSaveCrypted::WriteBKHDR()
_numberOfFiles = 0;
_sizeOfFiles = 0;
ScanForFiles(pathSavedir, FilesList, &_numberOfFiles, &_sizeOfFiles);
ScanForFiles(WiiTitlePath, FilesList, &_numberOfFiles, &_sizeOfFiles);
memset(&bkhdr, 0, BK_SZ);
bkhdr.size = Common::swap32(BK_LISTED_SZ);
bkhdr.magic = Common::swap32(BK_HDR_MAGIC);
//customize this
bkhdr.NGid = Common::swap32(1);
//
bkhdr.NGid = *(u32*)(keys.NG_id);
bkhdr.numberOfFiles = Common::swap32(_numberOfFiles);
bkhdr.sizeOfFiles = Common::swap32(_sizeOfFiles);
bkhdr.totalSize = Common::swap32(_sizeOfFiles + FULL_CERT_SZ);
bkhdr.SaveGameTitle = Common::swap64(_saveGameTitle);
// customize this
const u8 MAC[6] = {0};
//
memcpy(bkhdr.MACaddress, MAC, 6);
bkhdr.SaveGameTitle = Common::swap64(m_TitleID);
File::IOFile fpData_bin(pathData_bin, "ab");
if (!fpData_bin.WriteBytes(&bkhdr, BK_SZ))
@ -279,8 +299,8 @@ void CWiiSaveCrypted::ImportWiiSaveFiles()
fileName.replace(j, 1, iter->second);
}
sprintf(pathRawSave, "%s%s", pathSavedir, fileName.c_str());
File::CreateFullPath(pathRawSave);
std::string fullFilePath = WiiTitlePath + fileName;
File::CreateFullPath(fullFilePath);
if (_tmpFileHDR.type == 1)
{
_fileSize = Common::swap32(_tmpFileHDR.size);
@ -299,11 +319,11 @@ void CWiiSaveCrypted::ImportWiiSaveFiles()
AES_cbc_encrypt((const unsigned char *)_encryptedData, _data, RoundedFileSize, &m_AES_KEY, IV, AES_DECRYPT);
delete []_encryptedData;
if (!File::Exists(pathRawSave) || AskYesNoT("%s already exists, overwrite?", pathRawSave))
if (!File::Exists(fullFilePath) || AskYesNoT("%s already exists, overwrite?", fullFilePath.c_str()))
{
INFO_LOG(CONSOLE, "creating file %s", pathRawSave);
INFO_LOG(CONSOLE, "creating file %s", fullFilePath.c_str());
File::IOFile fpRawSaveFile(pathRawSave, "wb");
File::IOFile fpRawSaveFile(fullFilePath, "wb");
fpRawSaveFile.WriteBytes(_data, _fileSize);
}
delete []_data;
@ -330,7 +350,7 @@ void CWiiSaveCrypted::ExportWiiSaveFiles()
tmpFileHDR.magic = Common::swap32(FILE_HDR_MAGIC);
tmpFileHDR.size = Common::swap32(_fileSize);
tmpFileHDR.Permissions = 0x3C;
tmpFileHDR.Permissions = 0x35;
tmpFileHDR.type = File::IsDirectory(FilesList[i]) ? 2 : 1;
SplitPath(FilesList[i], NULL, &__name, &__ext);
@ -342,8 +362,6 @@ void CWiiSaveCrypted::ExportWiiSaveFiles()
for (size_t j = 0; (j = __name.find(iter->second, j)) != __name.npos; ++j)
{
/*std::string tmp = __name.substr(0, j) + iter->first +__name.substr(j+iter->second.length(), __name.length());
__name = tmp;*/
__name.replace(j, iter->second.length(), 1, iter->first);
}
}
@ -409,16 +427,10 @@ void CWiiSaveCrypted::do_sig()
char name[64];
u8 *data;
u32 data_size;
//allow customization
u32 ng_id = Common::swap32(1);
u32 ng_key_id = Common::swap32(2);
u8 ng_sig[0x3C] = {0};
u8 ng_priv[0x1E] = {0};
//allow customization
sprintf(signer, "Root-CA00000001-MS00000002");
sprintf(name, "NG%08x", ng_id);
make_ec_cert(ng_cert, ng_sig, signer, name, ng_priv, ng_key_id);
sprintf(name, "NG%08x", Common::swap32(keys.NG_id));
make_ec_cert(ng_cert, keys.NG_sig, signer, name, keys.NG_priv, Common::swap32(keys.NG_key_id));
memset(ap_priv, 0, sizeof ap_priv);
@ -426,12 +438,12 @@ void CWiiSaveCrypted::do_sig()
memset(ap_sig, 81, sizeof ap_sig); // temp
sprintf(signer, "Root-CA00000001-MS00000002-NG%08x", ng_id);
sprintf(signer, "Root-CA00000001-MS00000002-NG%08x", Common::swap32(keys.NG_id));
sprintf(name, "AP%08x%08x", 1, 2);
make_ec_cert(ap_cert, ap_sig, signer, name, ap_priv, 0);
sha1(ap_cert + 0x80, 0x100, hash);
generate_ecdsa(ap_sig, ap_sig + 30, ng_priv, hash);
generate_ecdsa(ap_sig, ap_sig + 30, keys.NG_priv, hash);
make_ec_cert(ap_cert, ap_sig, signer, name, ap_priv, 0);
data_size = Common::swap32(bkhdr.sizeOfFiles) + 0x80;
@ -446,7 +458,10 @@ void CWiiSaveCrypted::do_sig()
fpData_bin.Seek(0xf0c0, SEEK_SET);
if (!fpData_bin.ReadBytes(data, data_size))
PanicAlert("read data for sig check");
{
b_valid = false;
return;
}
sha1(data, data_size, hash);
sha1(hash, 20, hash);
@ -460,13 +475,12 @@ void CWiiSaveCrypted::do_sig()
}
generate_ecdsa(sig, sig + 30, ap_priv, hash);
*(u32*)(sig + 60) = Common::swap32(0x2f536969);
fpData_bin.WriteArray(sig, sizeof(sig));
fpData_bin.WriteArray(ng_cert, sizeof(ng_cert));
fpData_bin.WriteArray(ap_cert, sizeof(ap_cert));
if (!fpData_bin.WriteArray(sig, sizeof(sig)))
PanicAlert("write sig");
if (!fpData_bin.WriteArray(ng_cert, sizeof(ng_cert)))
PanicAlert("write NG cert");
if (!fpData_bin.WriteArray(ap_cert, sizeof(ap_cert)))
PanicAlert("write AP cert");
b_valid = fpData_bin.IsGood();
}
@ -485,40 +499,40 @@ void CWiiSaveCrypted::make_ec_cert(u8 *cert, u8 *sig, char *signer, char *name,
bool CWiiSaveCrypted::getPaths(bool forExport)
{
if (_saveGameTitle)
if (m_TitleID)
{
sprintf(pathSavedir, "%stitle/%08x/%08x/data/",
File::GetUserPath(D_WIIUSER_IDX).c_str(),
(u32)(_saveGameTitle>>32), (u32)_saveGameTitle);
sprintf(pathBanner_bin, "%sbanner.bin", pathSavedir);
sprintf(_saveGameString, "%c%c%c%c",
(u8)(_saveGameTitle >> 24) & 0xFF, (u8)(_saveGameTitle >> 16) & 0xFF,
(u8)(_saveGameTitle >> 8) & 0xFF, (u8)_saveGameTitle & 0xFF);
WiiTitlePath = Common::GetTitleDataPath(m_TitleID);
BannerFilePath = WiiTitlePath + "banner.bin";
}
if (forExport)
{
if(!File::IsDirectory(pathSavedir))
char GameID[5];
sprintf(GameID, "%c%c%c%c",
(u8)(m_TitleID >> 24) & 0xFF, (u8)(m_TitleID >> 16) & 0xFF,
(u8)(m_TitleID >> 8) & 0xFF, (u8)m_TitleID & 0xFF);
if(!File::IsDirectory(WiiTitlePath))
{
b_valid = false;
PanicAlertT("No save folder found for title %s", _saveGameString);
PanicAlertT("No save folder found for title %s", GameID);
return false;
}
if(!File::Exists(pathBanner_bin))
if(!File::Exists(BannerFilePath))
{
b_valid = false;
PanicAlertT("No banner file found for title %s", _saveGameString);
PanicAlertT("No banner file found for title %s", GameID);
return false;
}
if (strlen(pathData_bin) == 0)
strcpy(pathData_bin, "."); // If no path was passed, use current dir
sprintf(pathData_bin, "%s/private/wii/title/%s/data.bin", pathData_bin, _saveGameString);
sprintf(pathData_bin, "%s/private/wii/title/%s/data.bin", pathData_bin, GameID);
File::CreateFullPath(pathData_bin);
}
else
{
File::CreateFullPath(pathSavedir);
if (!AskYesNoT("Warning! it is advised to backup all files in the folder:\n%s\nDo you wish to continue?", pathSavedir))
File::CreateFullPath(WiiTitlePath);
if (!AskYesNoT("Warning! it is advised to backup all files in the folder:\n%s\nDo you wish to continue?", WiiTitlePath.c_str()))
return false;
}
return true;

View file

@ -30,7 +30,7 @@
class CWiiSaveCrypted
{
public:
CWiiSaveCrypted(const char* FileName, u64 title = 0);
CWiiSaveCrypted(const char* FileName, u64 TitleID = 0);
~CWiiSaveCrypted();
void ReadHDR();
void ReadBKHDR();
@ -50,11 +50,10 @@ private:
u8 SD_IV[0x10];
std::vector<std::string> FilesList;
char pathData_bin[2048],
pathSavedir[2048],
pathBanner_bin[2048], //should always be FULL_WII_USER_DIR "title/%08x/%08x/data/"
pathRawSave[2048],
_saveGameString[5];
char pathData_bin[2048];
std::string BannerFilePath,
WiiTitlePath;
u8 IV[0x10],
*_encryptedData,
@ -62,6 +61,14 @@ private:
md5_file[16],
md5_calc[16];
struct _keys
{
u8 NG_priv[0x1E],
NG_sig[0x3C],
NG_id[4],
NG_key_id[4];
}keys;
u32 _bannerSize,
_numberOfFiles,
_sizeOfFiles,
@ -69,7 +76,7 @@ private:
_fileSize,
_roundedfileSize;
u64 _saveGameTitle;
u64 m_TitleID;
bool b_valid,
b_tryAgain;