mirror of
https://github.com/martravi/wiiqt.git
synced 2024-12-24 16:31:49 +01:00
*nandchecker: add listing of deleted titles
* ohneswanzenegger: fix bug that let original HBC slip though as "installed at the factory"
This commit is contained in:
parent
96860e5aeb
commit
924ce71bf6
@ -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;
|
||||
}
|
||||
|
||||
|
@ -26,26 +26,28 @@ bool CheckTitleIntegrity( quint64 tid );
|
||||
|
||||
void Usage()
|
||||
{
|
||||
qDebug() << "usage" << QCoreApplication::arguments().at( 0 ) << "nand.bin" << "<other options>";
|
||||
qDebug() << "usage:" << QCoreApplication::arguments().at( 0 ) << "nand.bin" << "<other options>";
|
||||
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<Boot2Info> 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<quint16> 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 ) )
|
||||
|
@ -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++;
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user