From 924ce71bf60f7179edac6ce95659de5c886ff79f Mon Sep 17 00:00:00 2001 From: "giantpune@gmail.com" Date: Wed, 19 Jan 2011 22:32:13 +0000 Subject: [PATCH] *nandchecker: add listing of deleted titles * ohneswanzenegger: fix bug that let original HBC slip though as "installed at the factory" --- WiiQt/tiktmd.cpp | 8 +- nandBinCheck/main.cpp | 212 ++++++++++++++++++++++++++---- ohneschwanzenegger/mainwindow.cpp | 3 +- ohneschwanzenegger/newnandbin.cpp | 3 +- 4 files changed, 191 insertions(+), 35 deletions(-) diff --git a/WiiQt/tiktmd.cpp b/WiiQt/tiktmd.cpp index 2da5c47..0d66256 100644 --- a/WiiQt/tiktmd.cpp +++ b/WiiQt/tiktmd.cpp @@ -493,8 +493,8 @@ static int check_rsa( const QByteArray &h, const quint8 *sig, const quint8 *key, bn_exp( x, sig, key, n, key + n, 4 ); - //qDebug() << "Decrypted signature hash:" << QByteArray( (const char*)&x[ n-20 ], 20 ).toHex(); - //qDebug() << " SHA1 hash:" << h.toHex(); + //qDebug() << "Decrypted signature hash:" << QByteArray( (const char*)&x[ n-20 ], 20 ).toHex(); + //qDebug() << " SHA1 hash:" << h.toHex(); if( memcmp( correct, x, n ) == 0 ) return 0; @@ -502,8 +502,8 @@ static int check_rsa( const QByteArray &h, const quint8 *sig, const quint8 *key, if( strncmp( (char*)h.constData(), (char*) x + n - 20, 20 ) == 0 ) return ERROR_RSA_FAKESIGNED; - qDebug() << "Decrypted signature hash:" << QByteArray( (const char*)&x[ n-20 ], 20 ).toHex(); - qDebug() << " SHA1 hash:" << h.toHex(); + qDebug() << "Decrypted signature hash:" << qPrintable( QString( QByteArray( (const char*)&x[ n-20 ], 20 ).toHex() ) ); + qDebug() << " SHA1 hash:" << qPrintable( QString( h.toHex() ) ); return ERROR_RSA_HASH; } diff --git a/nandBinCheck/main.cpp b/nandBinCheck/main.cpp index 3b49d36..e91bb36 100644 --- a/nandBinCheck/main.cpp +++ b/nandBinCheck/main.cpp @@ -26,26 +26,28 @@ bool CheckTitleIntegrity( quint64 tid ); void Usage() { - qDebug() << "usage" << QCoreApplication::arguments().at( 0 ) << "nand.bin" << ""; + qDebug() << "usage:" << QCoreApplication::arguments().at( 0 ) << "nand.bin" << ""; qDebug() << "\nOther options:"; qDebug() << " -boot shows information about boot 1 and 2"; - qDebug() << "\n"; + qDebug() << ""; qDebug() << " -fs verify the filesystem is in tact"; qDebug() << " verifies presence of uid & content.map & checks the hashes in the content.map"; - qDebug() << " check installed titles for RSA & sha1 validity"; - qDebug() << " check installed titles for required IOS, proper uid & gid"; - qDebug() << "\n"; - qDebug() << " -settingtxt show setting.txt. this must be combined with \"-fs\""; - qDebug() << "\n"; + qDebug() << " check all titles with a ticket for RSA & sha1 validity"; + qDebug() << " check all titles with a ticket titles for required IOS, proper uid & gid"; + qDebug() << ""; + qDebug() << " -settingtxt check setting.txt itself and against system menu resources. this must be combined with \"-fs\""; + qDebug() << ""; + qDebug() << " -uid Look any titles in the uid.sys, check signatures and whatnot. this must be combined with \"-fs\""; + qDebug() << ""; qDebug() << " -clInfo shows free, used, and lost ( marked used, but dont belong to any file ) clusters"; - qDebug() << "\n"; + qDebug() << ""; qDebug() << " -spare calculate & compare ecc for all pages in the nand"; qDebug() << " calculate & compare hmac signatures for all files and superblocks"; - qDebug() << "\n"; + qDebug() << ""; qDebug() << " -all does all of the above"; - qDebug() << "\n"; + qDebug() << ""; qDebug() << " -v increase verbosity"; - qDebug() << "\n"; + qDebug() << ""; qDebug() << " -continue try to keep going as fas as possible on errors that should be fatal"; exit( 1 ); } @@ -62,6 +64,15 @@ QString TidTxt( quint64 tid ) return QString( "%1" ).arg( tid, 16, 16, QChar( '0' ) ); } +QString AsciiTxt( quint32 lower ) +{ + QString ret; + lower = qFromBigEndian( lower ); + for( int i = 0; i < 4; i++ ) + ret += ascii( (char)( lower >> ( 8 * i ) ) & 0xff ); + return ret; +} + void ShowBootInfo( quint8 boot1, QList boot2stuff ) { switch( boot1 ) @@ -183,8 +194,8 @@ QTreeWidgetItem *ItemFromPath( const QString &path ) item = FindItem( lookingFor, item ); if( !item ) { - if( verbose ) - qWarning() << "ItemFromPath ->item not found" << path; +// if( verbose ) +// qWarning() << "ItemFromPath ->item not found" << path; return NULL; } slash = nextSlash + 1; @@ -343,7 +354,10 @@ void PrintName( const QByteArray &app ) QString desc; if( app.size() == 0x40 )//tag for IOS, region select, ... { - desc = QString( app ) + " " + QString( app.right( 0x10 ) ); + desc = QString( app ).simplified(); + QString desc2 = QString( app.right( 0x10 ) ); + if( !desc2.isEmpty() ) + desc += " " + desc2; } else if( U8::GetU8Offset( app ) >= 0 ) //maybe this is an IMET header. try to get a name from it { @@ -371,11 +385,13 @@ void PrintName( const QByteArray &app ) bool CheckTitleIntegrity( quint64 tid ) { if( validIoses.contains( tid ) )//this one has already been checked - return true; + return true; + quint32 upper = ( ( tid >>32 ) & 0xffffffff ); + quint32 lower = ( tid & 0xffffffff ); -// if( verbose ) -// qDebug() << "\n"; - qDebug() << "Checking" << TidTxt( tid ).insert( 8, "-" ) << "..."; + if( verbose ) + qDebug() << ""; + qDebug() << "Checking" << qPrintable( TidTxt( tid ).insert( 8, "-" ) + ( upper != 1 ? " (" + AsciiTxt( lower ) + ")" : "" ) ) << "..."; QString p = TidTxt( tid ); p.insert( 8 ,"/" ); QString tikp = p; @@ -407,11 +423,11 @@ bool CheckTitleIntegrity( quint64 tid ) case ERROR_RSA_TYPE_UNKNOWN: case ERROR_RSA_TYPE_MISMATCH: case ERROR_CERT_NOT_FOUND: - qDebug() << "\t" << it << "RSA signature isn't even close (" << ch << ")"; + qDebug().nospace() << "\t" << qPrintable( it ) << " RSA signature isn't even close ( " << ch << " )"; //return false; //maye in the future this will be true, but for now, this doesnt mean it wont boot break; case ERROR_RSA_FAKESIGNED: - qDebug() << "\t" << it << "fakesigned"; + qDebug().nospace() << "\t" << qPrintable( it ) << " fakesigned"; break; default: break; @@ -436,8 +452,7 @@ bool CheckTitleIntegrity( quint64 tid ) } } - quint32 upper = ((tid >>32 ) & 0xffffffff); - if( upper == 0x10005 || upper == 0x10007 ) //dont try to verify all the contents of DLC, it will just find a bunch of missing contents and bitch about them + if( upper == 0x10005 || upper == 0x10007 ) //dont try to verify all the contents of DLC, it will just find a bunch of missing contents and bitch about them return true; quint16 cnt = t.Count(); @@ -467,8 +482,8 @@ bool CheckTitleIntegrity( quint64 tid ) if( realH != t.Hash( i ) ) { qDebug() << "\tone of the private contents' hash doesnt check out" << i << pA << - "\n\texpected" << t.Hash( i ).toHex() << - "\n\tactual " << realH.toHex(); + "\n\texpected" << qPrintable( QString( t.Hash( i ).toHex() ) ) << + "\n\tactual " << qPrintable( QString( realH.toHex() ) ); //return false; //dont return false, as this this title may still boot } @@ -494,7 +509,9 @@ bool CheckTitleIntegrity( quint64 tid ) if( verbose ) { quint16 vers = t.Version(); - qDebug() << "\tversion:" << QString( "%1.%2" ).arg( ( vers >> 8 ) & 0xff ).arg( vers & 0xff ) << vers << "hex:" << hex << t.Version(); + qDebug() << "\tversion:" << qPrintable( QString( "%1.%2" ).arg( ( vers >> 8 ) & 0xff ).arg( vers & 0xff ).leftJustified( 10 ) ) + << qPrintable( QString( "%1" ).arg( vers ).leftJustified( 10 ) ) + << "hex:" << hex << t.Version(); if( t.AccessFlags() ) qDebug() << "\taccess :" << hex << t.AccessFlags(); } @@ -507,7 +524,7 @@ bool CheckTitleIntegrity( quint64 tid ) } if( verbose > 1 && upper != 1 ) - qDebug() << "\trequires IOS" << ((quint32)( ios & 0xffffffff )) << TidTxt( ios ).insert( 8, "-" ); + qDebug() << "\trequires IOS" << ((quint32)( ios & 0xffffffff ));// << TidTxt( ios ).insert( 8, "-" ); quint32 uid = uidM.GetUid( tid, false ); if( !uid ) { @@ -554,6 +571,139 @@ bool CheckTitleIntegrity( quint64 tid ) return true; } +void ListDeletedTitles() +{ + QByteArray uidSys = uidM.Data(); + if( uidSys.isEmpty() ) + { + qDebug() << "Can\'t check deleted titles without a uid.sys"; + return; + } + qDebug() << "Comparing uid.sys against the filesystem..."; + + QBuffer buf( &uidSys ); + buf.open( QIODevice::ReadWrite ); + + quint64 tid; + quint16 factory = 0; + quint32 cnt = uidSys.size() / 12; + //skip past items installed at the factory + for( quint32 i = 0; i < cnt; i++ ) + { + buf.seek( i * 12 ); + buf.read( (char*)&tid, 8 ); + tid = qFromBigEndian( tid ); + quint32 upper = ( ( tid >> 32 ) & 0xffffffff ); + quint32 lower = ( tid & 0xffffffff ); + if( ( upper == 0x10001 && ( ( lower >> 24 ) & 0xff ) != 0x48 ) || //a channel, not starting with 'H' + lower == 0x48415858 || //original HBC + ( upper == 0x10000 && ( ( lower & 0xffffff00 ) == 0x555000 ) ) ) //a disc update partition + break; + if( ( verbose || upper != 0x10000 ) && !tids.contains( tid ) ) + qDebug().nospace() << "\t" << qPrintable( TidTxt( tid ).insert( 8, "-" ) + ( upper != 1 ? " (" + AsciiTxt( lower ) + ")" : "" ) ) << " was installed at the factory and is now missing"; + + factory++; + } + if( verbose ) + qDebug() << factory << "titles were installed before any user intervention"; + for( quint32 i = factory; i < cnt; i++ ) + { + buf.seek( i * 12 ); + buf.read( (char*)&tid, 8 ); + tid = qFromBigEndian( tid ); + if( tids.contains( tid ) ) //this one is already checked + continue; + quint32 upper = ( ( tid >> 32 ) & 0xffffffff ); + quint32 lower = ( tid & 0xffffffff ); + + bool deleted = false; + //load tmd + QString path = QString( "/title/%1/%2/content/title.tmd" ).arg( upper, 8, 16, QChar( '0' ) ).arg( lower, 8, 16, QChar( '0' ) ); + QTreeWidgetItem *item = ItemFromPath( path ); + if( !item ) + { + if( verbose > 1 ) + qDebug() << "\tCan\'t find TMD for" << qPrintable( TidTxt( tid ).insert( 8, "-" ) + ( upper != 1 ? " (" + AsciiTxt( lower ) + ")" : "" ) ); + deleted = true; + } + else + { + QByteArray ba = nand.GetData( path ); + if( ba.isEmpty() ) + { + if( verbose > 1 ) + qDebug() << "\tError reading TMD for" << qPrintable( TidTxt( tid ).insert( 8, "-" ) + ( upper != 1 ? " (" + AsciiTxt( lower ) + ")" : "" ) ); + deleted = true; + } + else + { + qint32 ch = check_cert_chain( ba ); + switch( ch ) + { + case ERROR_SIG_TYPE: + case ERROR_SUB_TYPE: + case ERROR_RSA_HASH: + case ERROR_RSA_TYPE_UNKNOWN: + case ERROR_RSA_TYPE_MISMATCH: + case ERROR_CERT_NOT_FOUND: + qDebug().nospace() << "\tTMD RSA signature isn't even close for " << qPrintable( TidTxt( tid ).insert( 8, "-" ) ) << " ( " << ch << " )"; + break; + case ERROR_RSA_FAKESIGNED: + qDebug().nospace() << "\tTMD for " << qPrintable( TidTxt( tid ).insert( 8, "-" ) + ( upper != 1 ? " (" + AsciiTxt( lower ) + ")" : "" ) ) << " is fakesigned"; + break; + default: + break; + } + } + } + if( upper != 0x10000 )//dont try to load ticket for disc games + { + //load ticket + path = QString( "/ticket/%1/%2.tik" ).arg( upper, 8, 16, QChar( '0' ) ).arg( lower, 8, 16, QChar( '0' ) ); + item = ItemFromPath( path ); + if( !item ) + { + if( verbose > 1 ) + qDebug() << "\tCan\'t find ticket for" << qPrintable( TidTxt( tid ).insert( 8, "-" ) + ( upper != 1 ? " (" + AsciiTxt( lower ) + ")" : "" ) ); + deleted = true; + } + else + { + QByteArray ba = nand.GetData( path ); + if( ba.isEmpty() ) + { + if( verbose > 1 ) + qDebug() << "\tError reading ticket for" << qPrintable( TidTxt( tid ).insert( 8, "-" ) + ( upper != 1 ? " (" + AsciiTxt( lower ) + ")" : "" ) ); + deleted = true; + } + else + { + qint32 ch = check_cert_chain( ba ); + switch( ch ) + { + case ERROR_SIG_TYPE: + case ERROR_SUB_TYPE: + case ERROR_RSA_HASH: + case ERROR_RSA_TYPE_UNKNOWN: + case ERROR_RSA_TYPE_MISMATCH: + case ERROR_CERT_NOT_FOUND: + qDebug().nospace() << "\tticket RSA signature isn't even close for " << qPrintable( TidTxt( tid ).insert( 8, "-" ) ) << " ( " << ch << " )"; + break; + case ERROR_RSA_FAKESIGNED: + qDebug().nospace() << "\tticket for " << qPrintable( TidTxt( tid ).insert( 8, "-" ) + ( upper != 1 ? " (" + AsciiTxt( lower ) + ")" : "" ) ) << " is fakesigned"; + break; + default: + break; + } + } + } + } + if( deleted ) + qDebug().nospace() << "\t" << qPrintable( TidTxt( tid ).insert( 8, "-" ) + ( upper != 1 ? " (" + AsciiTxt( lower ) + ")" : "" ) ) << " has been deleted"; + } + buf.close(); +} + void CheckLostClusters() { QList u = nand.GetFatsForEntry( 0 );//all clusters actually used for a file @@ -839,7 +989,7 @@ void CheckSettingTxt() { shownSetting = true; hexdump( settingTxt ); - qDebug() << str; + qDebug() << qPrintable( str ); } return; error: @@ -847,7 +997,7 @@ error: if( !shownSetting ) { hexdump( settingTxt ); - qDebug() << str; + qDebug() << qPrintable( str ); } } @@ -902,11 +1052,15 @@ int main( int argc, char *argv[] ) CheckTitleIntegrity( tid ); //if( !CheckTitleIntegrity( tid ) && tid == 0x100000002ull ) //well, this SHOULD be the case. but nintendo doesnt care so much about //Fail( "The System menu isnt valid" ); //checking signatures & hashes as the rest of us. - } + } if( args.contains( "-settingtxt", Qt::CaseInsensitive ) || args.contains( "-all", Qt::CaseInsensitive ) ) { CheckSettingTxt(); } + if( args.contains( "-uid", Qt::CaseInsensitive ) || args.contains( "-all", Qt::CaseInsensitive ) ) + { + ListDeletedTitles(); + } } if( args.contains( "-clInfo", Qt::CaseInsensitive ) || args.contains( "-all", Qt::CaseInsensitive ) ) diff --git a/ohneschwanzenegger/mainwindow.cpp b/ohneschwanzenegger/mainwindow.cpp index 7b68e2d..182d7d9 100644 --- a/ohneschwanzenegger/mainwindow.cpp +++ b/ohneschwanzenegger/mainwindow.cpp @@ -954,7 +954,8 @@ void MainWindow::on_actionFormat_triggered() quint32 upper = ( ( tid >> 32 ) & 0xffffffff ); quint32 lower = ( tid & 0xffffffff ); //qDebug() << hex << i << QString( "%1" ).arg( tid, 16, 16, QChar( '0' ) ) << upper << lower << QChar( ( lower >> 24 ) & 0xff ) << ( lower & 0xffffff00 ); - if( ( upper == 0x10001 && ( ( lower >> 24 ) & 0xff ) != 0x48 ) || //a channel, not starting with 'H' + if( ( upper == 0x10001 && ( ( lower >> 24 ) & 0xff ) != 0x48 ) || //a channel, not starting with 'H' + lower == 0x48415858 || //original HBC ( upper == 0x10000 && ( ( lower & 0xffffff00 ) == 0x555000 ) ) ) //a disc update partition break; titles++; diff --git a/ohneschwanzenegger/newnandbin.cpp b/ohneschwanzenegger/newnandbin.cpp index b56a88f..fcef616 100644 --- a/ohneschwanzenegger/newnandbin.cpp +++ b/ohneschwanzenegger/newnandbin.cpp @@ -352,7 +352,8 @@ QByteArray NewNandBin::GetCleanUid( QByteArray old ) quint32 upper = ( ( tid >> 32 ) & 0xffffffff ); quint32 lower = ( tid & 0xffffffff ); //qDebug() << QString( "%1" ).arg( tid, 16, 16, QChar( '0' ) ) << hex << upper << lower << ( ( lower >> 24 ) & 0xff ) << ( lower & 0xffff00 ); - if( ( upper == 0x10001 && ( ( lower >> 24 ) & 0xff ) != 0x48 ) || //a channel, not starting with 'H' + if( ( upper == 0x10001 && ( ( lower >> 24 ) & 0xff ) != 0x48 ) || //a channel, not starting with 'H' + lower == 0x48415858 || //original HBC ( upper == 0x10000 && ( ( lower & 0xffffff00 ) == 0x555000 ) ) ) //a disc update partition break;