mirror of
https://github.com/martravi/wiiqt.git
synced 2024-11-22 00:59:18 +01:00
* added a class to deal with getting data from the first 8 nand blocks ( ones not belonging to the filesystem )
* really fix the bug that caused the nandBin class not to be able to open keys.bin
This commit is contained in:
parent
1b86844eb4
commit
63f58938fe
5
.gitattributes
vendored
5
.gitattributes
vendored
@ -3,6 +3,8 @@ WiiQt/aes.c -text
|
||||
WiiQt/aes.h -text
|
||||
WiiQt/ash.cpp -text
|
||||
WiiQt/ash.h -text
|
||||
WiiQt/blocks0to7.cpp -text
|
||||
WiiQt/blocks0to7.h -text
|
||||
WiiQt/includes.h -text
|
||||
WiiQt/lz77.cpp -text
|
||||
WiiQt/lz77.h -text
|
||||
@ -35,6 +37,9 @@ WiiQt/wad.cpp -text
|
||||
WiiQt/wad.h -text
|
||||
nandExtract/black.png -text
|
||||
nandExtract/blue.png -text
|
||||
nandExtract/boot2infodialog.cpp -text
|
||||
nandExtract/boot2infodialog.h -text
|
||||
nandExtract/boot2infodialog.ui -text
|
||||
nandExtract/green.png -text
|
||||
nandExtract/grey.png -text
|
||||
nandExtract/main.cpp -text
|
||||
|
562
WiiQt/blocks0to7.cpp
Normal file
562
WiiQt/blocks0to7.cpp
Normal file
@ -0,0 +1,562 @@
|
||||
#include "blocks0to7.h"
|
||||
#include "tools.h"
|
||||
#include "tiktmd.h"
|
||||
|
||||
int check_cert_chain( const QByteArray data );
|
||||
|
||||
enum
|
||||
{
|
||||
ERROR_SUCCESS = 0,
|
||||
ERROR_SIG_TYPE,
|
||||
ERROR_SUB_TYPE,
|
||||
ERROR_RSA_FAKESIGNED,
|
||||
ERROR_RSA_HASH,
|
||||
ERROR_RSA_TYPE_UNKNOWN,
|
||||
ERROR_RSA_TYPE_MISMATCH,
|
||||
ERROR_CERT_NOT_FOUND,
|
||||
ERROR_COUNT
|
||||
};
|
||||
|
||||
Blocks0to7::Blocks0to7( QList<QByteArray>blocks )
|
||||
{
|
||||
_ok = false;
|
||||
if( !blocks.isEmpty() )
|
||||
SetBlocks( blocks );
|
||||
}
|
||||
|
||||
bool Blocks0to7::SetBlocks( QList<QByteArray>blocks )
|
||||
{
|
||||
_ok = false;
|
||||
this->blocks.clear();
|
||||
boot2Infos.clear();
|
||||
quint16 cnt = blocks.size();
|
||||
if( cnt != 8 )
|
||||
return false;
|
||||
for( quint16 i = 0; i < cnt; i++ )
|
||||
{
|
||||
if( blocks.at( i ).size() != 0x20000 )
|
||||
{
|
||||
qWarning() << "Blocks0to7::SetBlocks -> block" << i << "is" << hex << blocks.at( i ).size() << "bytes";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
this->blocks = blocks;
|
||||
_ok = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
quint8 Blocks0to7::Boot1Version()
|
||||
{
|
||||
if( blocks.size() != 8 )
|
||||
return BOOT_1_UNK;
|
||||
QByteArray hash = GetSha1( blocks.at( 0 ) );
|
||||
|
||||
if( hash == QByteArray( "\x4a\x7c\x6f\x30\x38\xde\xea\x7a\x07\xd3\x32\x32\x02\x4b\xe9\x5a\xfb\x56\xbf\x65" ) )
|
||||
return BOOT_1_A;
|
||||
if( hash == QByteArray( "\x2c\xdd\x5a\xff\xd2\xe7\x8c\x53\x76\x16\xa1\x19\xa7\xa2\xe1\xc5\x68\xe9\x1f\x22" ) )
|
||||
return BOOT_1_B;
|
||||
if( hash == QByteArray( "\xf0\x1e\x8a\xca\x02\x9e\xe0\xcb\x52\x87\xf5\x05\x5d\xa1\xa0\xbe\xd2\xa5\x33\xfa" ) )
|
||||
return BOOT_1_C;
|
||||
if( hash == QByteArray( "\x8d\x9e\xcf\x2f\x8f\x98\xa3\xc1\x07\xf1\xe5\xe3\x6f\xf2\x4d\x57\x7e\xac\x36\x08" ) )
|
||||
return BOOT_1_D; //displayed as "boot1?" in ceilingcat
|
||||
|
||||
return BOOT_1_UNK;
|
||||
}
|
||||
|
||||
//really ugly thing to get the different versions of boot2.
|
||||
//this doesnt take into account the possibility that boot2 is bigger and takes up more than 2 blocks
|
||||
//there are 0x40 blocks in the blockmap, but only 8 are used. maybe IOS has the authority to hijack the others if
|
||||
//it runs out of room here. if that ever happns, this code will become quite wrong
|
||||
QList<Boot2Info> Blocks0to7::Boot2Infos()
|
||||
{
|
||||
if( !boot2Infos.isEmpty() )
|
||||
{
|
||||
//qDebug() << "Blocks0to7::Boot2Infos() returning data from last time";
|
||||
return boot2Infos;
|
||||
}
|
||||
|
||||
QList< Boot2Info > ret;
|
||||
if( blocks.size() != 8 )
|
||||
return ret;
|
||||
|
||||
quint16 cnt = blocks.size();
|
||||
if( cnt != 8 )
|
||||
return ret;
|
||||
|
||||
//get all the blockmaps
|
||||
quint16 newest = 0;
|
||||
quint8 lbm[ 8 ];
|
||||
for( quint8 i = 1; i < 8; i++ )
|
||||
{
|
||||
Boot2Info info = GetBlockMap( blocks.at( i ) );
|
||||
if( info.state == BOOT_2_ERROR )//this block doesnt contain a decent blockmap
|
||||
continue;
|
||||
|
||||
info.secondBlock = i; //this one is easy enough
|
||||
//find the first block that belongs to this second one
|
||||
if( i > 4 )//blocks are backwards
|
||||
{
|
||||
bool found = false;
|
||||
for( quint8 j = 7; j > i; j++ )
|
||||
{
|
||||
if( info.blockMap[ j ] )//marked bad
|
||||
continue;
|
||||
|
||||
if( ( ( j > i + 1 ) && !info.blockMap[ i + 1 ] )//probably a much cleaner way to write this
|
||||
|| ( j == 6 && !info.blockMap[ 7 ] ) ) //but basically check for a couple stupid shit in the layout that really shouldnt ever happen
|
||||
break;
|
||||
|
||||
info.firstBlock = j;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if( !found )
|
||||
continue;
|
||||
}
|
||||
else//blocks are forwards
|
||||
{
|
||||
bool found = false;
|
||||
for( quint8 j = 0; j < i; j++ )
|
||||
{
|
||||
if( info.blockMap[ j ] )//marked bad
|
||||
continue;
|
||||
|
||||
info.firstBlock = j;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if( !found )
|
||||
continue;
|
||||
found = false;
|
||||
//probably a much cleaner way to write this
|
||||
//but basically check for a couple stupid shit in the layout that really shouldnt ever happen
|
||||
for( quint8 j = info.firstBlock + 1; j < info.secondBlock && !found; j++ )
|
||||
{
|
||||
if( info.blockMap[ j ] )//marked bad
|
||||
continue;
|
||||
found = true;
|
||||
}
|
||||
if( found )//this means there is some other good block between the first block and this one that has the block map
|
||||
continue;
|
||||
}
|
||||
//we made it this far, it means that so far we have a correct looking blockmap that points to this copy of boot2
|
||||
if( info.generation > newest )
|
||||
{
|
||||
newest = info.generation;
|
||||
for( quint8 j = 0; j < 8; j++ )
|
||||
lbm[ j ] = info.blockMap[ j ];
|
||||
}
|
||||
|
||||
ret << info;
|
||||
}
|
||||
//qDebug() << "newest blockmap" << QByteArray( (const char*)&lbm, 8 ).toHex();
|
||||
|
||||
cnt = ret.size();
|
||||
bool foundBoot = false;
|
||||
bool foundBackup = false;
|
||||
for( quint8 i = 0; i < cnt; i++ )
|
||||
{
|
||||
ret[ i ] = CheckHashes( ret[ i ] );//check all the hashes and stuff
|
||||
if( !foundBoot && !lbm[ ret.at( i ).firstBlock ] && !lbm[ ret.at( i ).secondBlock ] )
|
||||
{
|
||||
//qDebug() << "copy" << i << "is used when booting";
|
||||
ret[ i ].state |= BOOT_2_USED_TO_BOOT;
|
||||
//ret[ i ].usedToBoot = true;
|
||||
foundBoot = true;
|
||||
}
|
||||
else if( lbm[ ret.at( i ).firstBlock ] || lbm[ ret.at( i ).secondBlock ] )
|
||||
{
|
||||
ret[ i ].state |= BOOT_2_MARKED_BAD;
|
||||
}
|
||||
}
|
||||
for( quint8 i = ret.size(); !foundBackup && i > 0; i-- )
|
||||
{
|
||||
if( !lbm[ ret.at( i - 1 ).firstBlock ] && !lbm[ ret.at( i - 1 ).secondBlock ] && ret.at( i - 1 ).firstBlock > ret.at( i - 1 ).secondBlock )
|
||||
{
|
||||
//qDebug() << "copy" << i << "is used when booting from backup";
|
||||
ret[ i - 1 ].state |= BOOT_2_BACKUP_COPY;
|
||||
foundBackup = true;
|
||||
if( !foundBoot )
|
||||
ret[ i - 1 ].state |= BOOT_2_USED_TO_BOOT;
|
||||
}
|
||||
}
|
||||
boot2Infos = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Boot2Info Blocks0to7::GetBlockMap( QByteArray block )
|
||||
{
|
||||
Boot2Info ret;
|
||||
ret.state = BOOT_2_ERROR;
|
||||
if( block.size() != 0x20000 )
|
||||
return ret;
|
||||
|
||||
QByteArray first = block.mid( 0x1f800, 0x4c );
|
||||
QByteArray second = block.mid( 0x1f84c, 0x4c );
|
||||
QByteArray third = block.mid( 0x1f898, 0x4c );
|
||||
QByteArray goodOne = QByteArray( "\x26\xF2\x9A\x40\x1E\xE6\x84\xCF" );
|
||||
if( first.startsWith( goodOne ) && ( first == second || first == third ) )
|
||||
goodOne = first;
|
||||
else if( second.startsWith( goodOne ) && ( second == third ) )
|
||||
goodOne = second;
|
||||
else
|
||||
return ret;
|
||||
|
||||
ret.generation = goodOne.mid( 8, 4 ).toHex().toInt( NULL, 16 );
|
||||
for( quint8 i = 0; i < 8; i++ )
|
||||
ret.blockMap[ i ] = goodOne.at( i + 12 );
|
||||
|
||||
ret.state = BOOT_2_TIK_SIG_OK; //just assign this for now. it will be corrected before this data leaves the class
|
||||
return ret;
|
||||
}
|
||||
|
||||
Boot2Info Blocks0to7::CheckHashes( Boot2Info info )
|
||||
{
|
||||
Boot2Info ret = info;
|
||||
ret.state = BOOT_2_ERROR_PARSING;
|
||||
|
||||
QByteArray stuff = blocks.at( ret.firstBlock );
|
||||
QBuffer b( &stuff );
|
||||
b.open( QIODevice::ReadOnly );
|
||||
quint32 headerSize;
|
||||
quint32 dataOff;
|
||||
quint32 certSize;
|
||||
quint32 ticketSize;
|
||||
quint32 tmdSize;
|
||||
quint32 tmp;
|
||||
|
||||
b.read( (char*)&tmp, 4 );
|
||||
headerSize = qFromBigEndian( tmp );
|
||||
if( headerSize != 0x20 )
|
||||
return ret;
|
||||
|
||||
b.read( (char*)&tmp, 4 );
|
||||
dataOff = qFromBigEndian( tmp );
|
||||
b.read( (char*)&tmp, 4 );
|
||||
certSize = qFromBigEndian( tmp );
|
||||
b.read( (char*)&tmp, 4 );
|
||||
ticketSize = qFromBigEndian( tmp );
|
||||
b.read( (char*)&tmp, 4 );
|
||||
tmdSize = qFromBigEndian( tmp );
|
||||
b.close();
|
||||
|
||||
QByteArray tikD = stuff.mid( headerSize + certSize, ticketSize );
|
||||
QByteArray tmdD = stuff.mid( headerSize + certSize + ticketSize, tmdSize );
|
||||
Tmd t( tmdD );
|
||||
Ticket ticket( tikD );
|
||||
if( t.Tid() != 0x100000001ull || ticket.Tid() != 0x100000001ull )
|
||||
{
|
||||
qWarning() << "Blocks0to7::CheckHashes -> bad TID";
|
||||
return ret;
|
||||
}
|
||||
ret.state = 0;
|
||||
int res = check_cert_chain( tikD );
|
||||
switch( res )
|
||||
{
|
||||
default:
|
||||
case ERROR_SIG_TYPE:
|
||||
case ERROR_SUB_TYPE:
|
||||
case ERROR_RSA_TYPE_UNKNOWN:
|
||||
case ERROR_CERT_NOT_FOUND:
|
||||
ret.state = BOOT_2_ERROR;
|
||||
qWarning() << "check_cert_chain( tikD ):" << res;
|
||||
return ret;
|
||||
break;
|
||||
case ERROR_RSA_TYPE_MISMATCH:
|
||||
case ERROR_RSA_HASH:
|
||||
ret.state = BOOT_2_BAD_SIGNATURE;
|
||||
//qWarning() << "check_cert_chain( tikD ):" << res;
|
||||
return ret;
|
||||
break;
|
||||
case ERROR_RSA_FAKESIGNED:
|
||||
ret.state |= BOOT_2_TIK_FAKESIGNED;
|
||||
break;
|
||||
case ERROR_SUCCESS:
|
||||
ret.state |= BOOT_2_TIK_SIG_OK;
|
||||
break;
|
||||
}
|
||||
res = check_cert_chain( tmdD );
|
||||
switch( res )
|
||||
{
|
||||
default:
|
||||
case ERROR_SIG_TYPE:
|
||||
case ERROR_SUB_TYPE:
|
||||
case ERROR_RSA_TYPE_UNKNOWN:
|
||||
case ERROR_CERT_NOT_FOUND:
|
||||
ret.state = BOOT_2_ERROR;
|
||||
//qWarning() << "check_cert_chain( tikD ):" << res;
|
||||
return ret;
|
||||
break;
|
||||
case ERROR_RSA_TYPE_MISMATCH:
|
||||
case ERROR_RSA_HASH:
|
||||
ret.state = BOOT_2_BAD_SIGNATURE;
|
||||
//qWarning() << "check_cert_chain( tikD ):" << res;
|
||||
return ret;
|
||||
break;
|
||||
case ERROR_RSA_FAKESIGNED:
|
||||
{
|
||||
ret.state |= BOOT_2_TMD_FAKESIGNED;
|
||||
if( tmdD.contains( "BM1.1" ) )
|
||||
ret.version = BOOTMII_11;
|
||||
else if( tmdD.contains( "BM1.3" ) )
|
||||
ret.version = BOOTMII_13;
|
||||
else
|
||||
ret.version = BOOTMII_UNK;
|
||||
}
|
||||
break;
|
||||
case ERROR_SUCCESS:
|
||||
{
|
||||
ret.state |= BOOT_2_TMD_SIG_OK;
|
||||
ret.version = t.Version();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//now decrypt boot2 and check the hash ( only checking 1 content because thats all there is )
|
||||
stuff += blocks.at( ret.secondBlock );
|
||||
|
||||
AesSetKey( ticket.DecryptedKey() );
|
||||
QByteArray decD = AesDecrypt( 0, stuff.mid( dataOff, RU( 0x40, t.Size( 0 ) ) ) );
|
||||
decD.resize( t.Size( 0 ) );
|
||||
QByteArray realHash = GetSha1( decD );
|
||||
if( realHash != t.Hash( 0 ) )
|
||||
ret.state |= BOOT_2_BAD_CONTENT_HASH;
|
||||
return ret;
|
||||
}
|
||||
|
||||
//theres probably a better place to put all this stuff. but until then, just put it here
|
||||
#define BE32(a) (((a)[0] << 24)|((a)[1] << 16)|((a)[2] << 8)|(a)[3])
|
||||
|
||||
#define bn_zero(a,b) memset(a,0,b)
|
||||
#define bn_copy(a,b,c) memcpy(a,b,c)
|
||||
#define bn_compare(a,b,c) memcmp(a,b,c)
|
||||
|
||||
// calc a = a mod N, given n = size of a,N in bytes
|
||||
static void bn_sub_modulus( quint8 *a, const quint8 *N, const quint32 n )
|
||||
{
|
||||
quint32 dig;
|
||||
quint8 c = 0;
|
||||
|
||||
for( quint32 i = n - 1; i < n; i-- )
|
||||
{
|
||||
dig = N[ i ] + c;
|
||||
c = ( a [ i ] < dig );
|
||||
a[ i ] -= dig;
|
||||
}
|
||||
}
|
||||
|
||||
// calc d = (a + b) mod N, given n = size of d,a,b,N in bytes
|
||||
static void bn_add( quint8 *d, const quint8 *a, const quint8 *b, const quint8 *N, const quint32 n )
|
||||
{
|
||||
quint32 i;
|
||||
quint32 dig;
|
||||
quint8 c = 0;
|
||||
|
||||
for( i = n - 1; i < n; i--)
|
||||
{
|
||||
dig = a[ i ] + b[ i ] + c;
|
||||
c = ( dig >= 0x100 );
|
||||
d[ i ] = dig;
|
||||
}
|
||||
if( c )
|
||||
bn_sub_modulus( d, N, n );
|
||||
|
||||
if( bn_compare( d, N, n ) >= 0 )
|
||||
bn_sub_modulus( d, N, n );
|
||||
}
|
||||
|
||||
// calc d = (a * b) mod N, given n = size of d,a,b,N in bytes
|
||||
static void bn_mul( quint8 *d, const quint8 *a, const quint8 *b, const quint8 *N, const quint32 n )
|
||||
{
|
||||
quint8 mask;
|
||||
bn_zero( d, n );
|
||||
|
||||
for( quint32 i = 0; i < n; i++ )
|
||||
for( mask = 0x80; mask != 0; mask >>= 1 )
|
||||
{
|
||||
bn_add( d, d, d, N, n );
|
||||
if( ( a[ i ] & mask ) != 0 )
|
||||
bn_add( d, d, b, N, n );
|
||||
}
|
||||
}
|
||||
|
||||
// calc d = (a ^ e) mod N, given n = size of d,a,N and en = size of e in bytes
|
||||
static void bn_exp( quint8 *d, const quint8 *a, const quint8 *N, const quint32 n, const quint8 *e, const quint32 en )
|
||||
{
|
||||
quint8 t[ 512 ];
|
||||
quint8 mask;
|
||||
|
||||
bn_zero( d, n );
|
||||
d[ n-1 ] = 1;
|
||||
for( quint32 i = 0; i < en; i++ )
|
||||
for( mask = 0x80; mask != 0; mask >>= 1 )
|
||||
{
|
||||
bn_mul( t, d, d, N, n );
|
||||
if( ( e [ i ] & mask ) != 0 )
|
||||
bn_mul( d, t, a, N, n );
|
||||
else
|
||||
bn_copy( d, t, n );
|
||||
}
|
||||
}
|
||||
|
||||
static int get_sig_len( const quint8 *sig )
|
||||
{
|
||||
quint32 type;
|
||||
type = BE32( sig );
|
||||
switch( type - 0x10000 )
|
||||
{
|
||||
case 0:
|
||||
return 0x240;
|
||||
case 1:
|
||||
return 0x140;
|
||||
case 2:
|
||||
return 0x80;
|
||||
}
|
||||
return -ERROR_SIG_TYPE;
|
||||
}
|
||||
|
||||
static int get_sub_len( const quint8 *sub )
|
||||
{
|
||||
quint32 type;
|
||||
type = BE32( sub + 0x40 );
|
||||
switch( type )
|
||||
{
|
||||
case 0: return 0x2c0;
|
||||
case 1: return 0x1c0;
|
||||
case 2: return 0x100;
|
||||
}
|
||||
return -ERROR_SUB_TYPE;
|
||||
}
|
||||
|
||||
static int check_rsa( QByteArray h, const quint8 *sig, const quint8 *key, const quint32 n )
|
||||
{
|
||||
quint8 correct[ 0x200 ], x[ 0x200 ];
|
||||
static const quint8 ber[ 16 ] = { 0x00,0x30,0x21,0x30,0x09,0x06,0x05,0x2b,
|
||||
0x0e,0x03,0x02,0x1a,0x05,0x00,0x04,0x14 };
|
||||
correct[ 0 ] = 0;
|
||||
correct[ 1 ] = 1;
|
||||
memset( correct + 2, 0xff, n - 38 );
|
||||
memcpy( correct + n - 36, ber, 16 );
|
||||
memcpy( correct + n - 20, h.constData(), 20 );
|
||||
|
||||
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();
|
||||
|
||||
if( memcmp( correct, x, n ) == 0 )
|
||||
return 0;
|
||||
|
||||
if( strncmp( (char*)h.constData(), (char*) x + n - 20, 20 ) == 0 )
|
||||
return ERROR_RSA_FAKESIGNED;
|
||||
|
||||
return ERROR_RSA_HASH;
|
||||
}
|
||||
|
||||
static int check_hash( QByteArray h, const quint8 *sig, const quint8 *key )
|
||||
{
|
||||
quint32 type;
|
||||
type = BE32( sig ) - 0x10000;
|
||||
if( (qint32)type != BE32( key + 0x40 ) )
|
||||
return ERROR_RSA_TYPE_MISMATCH;
|
||||
|
||||
if( type == 1 )
|
||||
return check_rsa( h, sig + 4, key + 0x88, 0x100 );
|
||||
return ERROR_RSA_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
static const quint8* find_cert_in_chain( const quint8 *sub, const quint8 *cert, const quint32 cert_len, int *err )
|
||||
{
|
||||
char parent[ 64 ], *child;
|
||||
int sig_len, sub_len;
|
||||
const quint8 *p, *issuer;
|
||||
|
||||
strncpy( parent, (char*)sub, sizeof parent );
|
||||
parent[ sizeof parent - 1 ] = 0;
|
||||
child = strrchr( parent, '-' );
|
||||
if( child )
|
||||
*child++ = 0;
|
||||
else
|
||||
{
|
||||
*parent = 0;
|
||||
child = (char*)sub;
|
||||
}
|
||||
|
||||
*err = -ERROR_CERT_NOT_FOUND;
|
||||
|
||||
for( p = cert; p < cert + cert_len; p += sig_len + sub_len )
|
||||
{
|
||||
sig_len = get_sig_len( p );
|
||||
if( sig_len < 0 )
|
||||
{
|
||||
*err = sig_len;
|
||||
break;
|
||||
}
|
||||
issuer = p + sig_len;
|
||||
sub_len = get_sub_len( issuer );
|
||||
if( sub_len < 0 )
|
||||
{
|
||||
*err = sub_len;
|
||||
break;
|
||||
}
|
||||
if( strcmp( parent, (char*)issuer ) == 0 && strcmp( child, (char*)issuer + 0x44 ) == 0 )
|
||||
return p;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int check_cert_chain( const QByteArray data )
|
||||
{
|
||||
int cert_err;
|
||||
const quint8* key;
|
||||
const quint8 *sig, *sub, *key_cert;
|
||||
int sig_len, sub_len;
|
||||
QByteArray h;
|
||||
int ret;
|
||||
const quint8 *certificates = certs_dat;
|
||||
|
||||
sig = (const quint8*)data.constData();
|
||||
sig_len = get_sig_len( sig );
|
||||
if( sig_len < 0 )
|
||||
return -sig_len;
|
||||
|
||||
sub = (const quint8*)( data.data() + sig_len );
|
||||
sub_len = data.size() - sig_len;
|
||||
if( sub_len <= 0 )
|
||||
return ERROR_SUB_TYPE;
|
||||
|
||||
for( ; ; )
|
||||
{
|
||||
//qDebug() << "Verifying using" << QString( (const char*) sub );
|
||||
if( strcmp((char*)sub, "Root" ) == 0 )
|
||||
{
|
||||
key = root_dat;
|
||||
h = GetSha1( QByteArray( (const char*)sub, sub_len ) );
|
||||
if( BE32( sig ) != 0x10000 )
|
||||
return ERROR_SIG_TYPE;
|
||||
return check_rsa( h, sig + 4, key, 0x200 );
|
||||
}
|
||||
|
||||
key_cert = find_cert_in_chain( sub, certificates, CERTS_DAT_SIZE, &cert_err );
|
||||
if( key_cert )
|
||||
cert_err = get_sig_len( key_cert );
|
||||
|
||||
if( cert_err < 0 )
|
||||
return -cert_err;
|
||||
key = key_cert + cert_err;
|
||||
|
||||
h = GetSha1( QByteArray( (const char*)sub, sub_len ) );
|
||||
ret = check_hash( h, sig, key );
|
||||
// remove this if statement if you don't want to check the whole chain
|
||||
if( ret != ERROR_SUCCESS )
|
||||
return ret;
|
||||
sig = key_cert;
|
||||
sig_len = get_sig_len( sig );
|
||||
if( sig_len < 0 )
|
||||
return -sig_len;
|
||||
sub = sig + sig_len;
|
||||
sub_len = get_sub_len( sub );
|
||||
if( sub_len < 0 )
|
||||
return -sub_len;
|
||||
}
|
||||
}
|
92
WiiQt/blocks0to7.h
Normal file
92
WiiQt/blocks0to7.h
Normal file
@ -0,0 +1,92 @@
|
||||
#ifndef BLOCKS0TO7_H
|
||||
#define BLOCKS0TO7_H
|
||||
|
||||
#include "includes.h"
|
||||
//for now im creating this class to handle getting info about the first 8 blocks of nand
|
||||
//im not sure what all it should be doing, so it may become a clusterfuck
|
||||
//for now, just pass it a list of 8 bytearrays, each 0x20000 bytes. then check IsOk(), and then you can check boot1version and Boot2Infos()
|
||||
|
||||
//! currently it checks the hash of boot1 block against known versions, checks blocks 1-7 for valid blockmaps,
|
||||
//! looks for copies of boot2 in those blocks, then finds the tmd, ticket and encrypted data. next it checks that
|
||||
//! the tid in the tmd&ticket is correct, and verifies all the signatures and decrypts the actual content and checks its hash
|
||||
//! finally it looks at the most recent valid blockmap and determines which copy would be used when booting & when booting from backup
|
||||
//! Because it does all this, the first time requesting this data will cause the current thread to hang
|
||||
//! ( checking the signatures takes the longest ) after the first time, the list returned by Boot2Infos() is remembered
|
||||
//! and it will be returned any time after that when this function is called
|
||||
|
||||
enum //returned by Boot1Version(). theres no official names for these, so ill just stick with lettering them
|
||||
{
|
||||
BOOT_1_UNK = 0,
|
||||
BOOT_1_A,
|
||||
BOOT_1_B,
|
||||
BOOT_1_C,
|
||||
BOOT_1_D
|
||||
};
|
||||
|
||||
#define BOOTMII_UNK 0x2110
|
||||
#define BOOTMII_11 0x2111
|
||||
#define BOOTMII_13 0x2113
|
||||
//TODO: what other versions of bootmii are there?
|
||||
|
||||
//enum used to convey the state of each copy of boot2 on a nand
|
||||
enum
|
||||
{
|
||||
BOOT_2_ERROR_PARSING = -3, //error parsing the thing, cant really tell more info, since it wasnt parsed :P
|
||||
BOOT_2_BAD_SIGNATURE = -2, //RSA signature of boot2 doesnt pass memcmp or strncmp
|
||||
BOOT_2_ERROR = -1, //some other error
|
||||
BOOT_2_BAD_CONTENT_HASH = 1, //sha1 of the content did not match what was in the TMD
|
||||
BOOT_2_TMD_FAKESIGNED = 2, //tmd RSA seems fine, it passed strncmp but not memcmp ( probably bootmii )
|
||||
BOOT_2_TMD_SIG_OK = 4, //tmd RSA seems fine, signature passed memcmp ( probably official shit )
|
||||
BOOT_2_TIK_FAKESIGNED = 8, //tik RSA seems fine, it passed strncmp but not memcmp - not really sure why this would happen
|
||||
BOOT_2_TIK_SIG_OK = 0x10, //tik RSA seems fine, signature passed memcmp
|
||||
BOOT_2_MARKED_BAD = 0x20, //the most recent blockmap says this copy of boot2 falls on bad blocks
|
||||
BOOT_2_USED_TO_BOOT = 0x40, //this is the copy used when trying to boot the wii
|
||||
BOOT_2_BACKUP_COPY = 0x80 //this is the copy used when the first copy failed to boot
|
||||
};
|
||||
|
||||
|
||||
struct Boot2Info //this little guy is just some container to hold the information about the state of boot2 in these blocks
|
||||
{ //the above values are used for the "state". it will either be < 0 or it will be any of the other values |'d together
|
||||
//if the state is above 0, version will either be the version from the TMD or one of the BOOTMII_... values
|
||||
|
||||
quint8 firstBlock; //block that contains the header
|
||||
quint8 secondBlock; //block that contains the blockmap
|
||||
quint32 generation; //generation of the blockmap
|
||||
quint8 blockMap[ 8 ]; //blockmap found on secondBlock ( only the first 8 blocks, as the rest belong to the encrypted FS )
|
||||
qint32 state; //information about this copy of boot2
|
||||
quint16 version; //whatversion of boot2 this is
|
||||
};
|
||||
|
||||
|
||||
class Blocks0to7
|
||||
{
|
||||
public:
|
||||
Blocks0to7( QList<QByteArray>blocks = QList<QByteArray>() );
|
||||
bool SetBlocks( QList<QByteArray>blocks );
|
||||
bool IsOk(){ return _ok; }
|
||||
|
||||
//check which version of boot1 we have
|
||||
quint8 Boot1Version();
|
||||
|
||||
//get a list containing info for each copy of boot2 on the given blocks
|
||||
QList<Boot2Info> Boot2Infos();
|
||||
|
||||
private:
|
||||
bool _ok;
|
||||
//should hold the blocks, without ecc
|
||||
QList<QByteArray>blocks;
|
||||
|
||||
//after teh first time Boot2Infos() is called, store the result here so any subsequent calls can just return this for speed
|
||||
QList< Boot2Info > boot2Infos;
|
||||
|
||||
//this one doesnt really return a complete info, it only gets the block map from a block
|
||||
//and returns it in an incomplete Boot2Info
|
||||
Boot2Info GetBlockMap( QByteArray block );
|
||||
|
||||
//checks the hashes and whatnot in a copy of boot2
|
||||
//returns an incomplete Boot2Info
|
||||
Boot2Info CheckHashes( Boot2Info info );
|
||||
|
||||
};
|
||||
|
||||
#endif // BLOCKS0TO7_H
|
@ -24,7 +24,8 @@ NandBin::~NandBin()
|
||||
bool NandBin::SetPath( const QString &path )
|
||||
{
|
||||
fstInited = false;
|
||||
//nandPath = path;
|
||||
nandPath = path;
|
||||
bootBlocks = Blocks0to7();
|
||||
if( f.isOpen() )
|
||||
f.close();
|
||||
|
||||
@ -235,7 +236,27 @@ bool NandBin::InitNand( QIcon dirs, QIcon files )
|
||||
root = new QTreeWidgetItem( QStringList() << nandPath );
|
||||
AddChildren( root, 0 );
|
||||
|
||||
//ShowInfo();
|
||||
|
||||
//checkout the blocks for boot1&2
|
||||
QList<QByteArray>blocks;
|
||||
for( quint16 i = 0; i < 8; i++ )
|
||||
{
|
||||
QByteArray block;
|
||||
for( quint16 j = 0; j < 8; j++ )
|
||||
{
|
||||
block += GetCluster( ( i * 8 ) + j, false );
|
||||
}
|
||||
if( block.size() != 0x4000 * 8 )
|
||||
{
|
||||
qDebug() << "wrong block size" << i;
|
||||
return false;
|
||||
}
|
||||
blocks << block;
|
||||
}
|
||||
|
||||
if( !bootBlocks.SetBlocks( blocks ) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -253,6 +274,22 @@ int NandBin::GetDumpType( quint64 fileSize )
|
||||
return -1;
|
||||
}
|
||||
|
||||
const QList<Boot2Info> NandBin::Boot2Infos()
|
||||
{
|
||||
if( !bootBlocks.IsOk() )
|
||||
return QList<Boot2Info>();
|
||||
|
||||
return bootBlocks.Boot2Infos();
|
||||
}
|
||||
|
||||
quint8 NandBin::Boot1Version()
|
||||
{
|
||||
if( !bootBlocks.IsOk() )
|
||||
return 0;
|
||||
|
||||
return bootBlocks.Boot1Version();
|
||||
}
|
||||
|
||||
bool NandBin::GetKey( int type )
|
||||
{
|
||||
switch( type )
|
||||
@ -471,7 +508,7 @@ QByteArray NandBin::GetCluster( quint16 cluster_entry, bool decrypt )
|
||||
//1 key set at a time and it may be changed if some other object is decrypting something else
|
||||
AesSetKey( key );
|
||||
|
||||
QByteArray ret = AesDecrypt( 0, cluster );//TODO... is IV really always 0?
|
||||
QByteArray ret = AesDecrypt( 0, cluster );
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -656,3 +693,308 @@ void NandBin::ShowInfo()
|
||||
<< "\nbadBlocks:" << hex << badBlocks << badOnes
|
||||
<< "\nreserved:" << hex << reserved;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
structure of blocks 0 - 7
|
||||
|
||||
block 0 ( boot 1 )
|
||||
|
||||
0 - 0x4320
|
||||
a bunch of encrypted gibberish.
|
||||
the rest of the block as all 0s
|
||||
|
||||
|
||||
sha1 of the whole block is
|
||||
|
||||
2cdd5affd2e78c537616a119a7a2e1c568e91f22 with boot1b
|
||||
f01e8aca029ee0cb5287f5055da1a0bed2a533fa with boot1c
|
||||
|
||||
<sven> {1, {0x4a, 0x7c, 0x6f, 0x30, 0x38, 0xde, 0xea, 0x7a, 0x07, 0xd3, 0x32, 0x32, 0x02, 0x4b, 0xe9, 0x5a, 0xfb, 0x56, 0xbf, 0x65}},
|
||||
<sven> {1, {0x2c, 0xdd, 0x5a, 0xff, 0xd2, 0xe7, 0x8c, 0x53, 0x76, 0x16, 0xa1, 0x19, 0xa7, 0xa2, 0xe1, 0xc5, 0x68, 0xe9, 0x1f, 0x22}},
|
||||
<sven> {0, {0xf0, 0x1e, 0x8a, 0xca, 0x02, 0x9e, 0xe0, 0xcb, 0x52, 0x87, 0xf5, 0x05, 0x5d, 0xa1, 0xa0, 0xbe, 0xd2, 0xa5, 0x33, 0xfa}},
|
||||
<sven> {0, {0x8d, 0x9e, 0xcf, 0x2f, 0x8f, 0x98, 0xa3, 0xc1, 0x07, 0xf1, 0xe5, 0xe3, 0x6f, 0xf2, 0x4d, 0x57, 0x7e, 0xac, 0x36, 0x08}},
|
||||
|
||||
|
||||
block 1 ( boot2 ) //just guessing at these for now
|
||||
|
||||
u32 0x20 //header size
|
||||
u32 0xf00 //data offset
|
||||
u32 0xa00 //cert size
|
||||
u32 0x2a4 //ticket size
|
||||
u32 0x208 //tmd size
|
||||
u32[ 3 ] 0s //padding till 0x20
|
||||
|
||||
0x20 - 0x9f0
|
||||
cert
|
||||
root ca00000001
|
||||
Root-CA00000001 CP00000004
|
||||
Root-CA00000001 XS00000003
|
||||
|
||||
round up to 0xa20
|
||||
ticket for boot2
|
||||
|
||||
0xcc4
|
||||
tmd?
|
||||
|
||||
0xecc - 0xf00
|
||||
gibberish padding
|
||||
|
||||
0xf00
|
||||
start of boot2 contents? ( mine is 0x00023E91 bytes in the TMD for v2 )
|
||||
there is room for 0x1F100 bytes of it left in this block with 0x4D91 leftover
|
||||
|
||||
|
||||
block 2
|
||||
bunch of gibberish till 0x4da0
|
||||
probably the rest of the contents of boot2, padded with gibberish
|
||||
|
||||
|
||||
0x4da0
|
||||
0s till 0x5000
|
||||
|
||||
0x5000 - 0x1f800
|
||||
bunch of 0xffff
|
||||
maybe this is untouched nand. never been written to
|
||||
|
||||
|
||||
|
||||
0x1f800 - 0x1f8e4 blockmap?
|
||||
|
||||
26F29A401EE684CF0000000201000000
|
||||
00000000010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
010101010101010101010101
|
||||
|
||||
26F29A401EE684CF0000000201000000
|
||||
00000000010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
010101010101010101010101
|
||||
|
||||
26F29A401EE684CF0000000201000000
|
||||
00000000010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
010101010101010101010101
|
||||
|
||||
0x1f8e4 - 0x20000 ( end of block )
|
||||
more 0s
|
||||
|
||||
|
||||
block 2 ( looks a lot like block 1 )
|
||||
wad type header again with the same values
|
||||
|
||||
same cert looking doodad again
|
||||
same ticket again
|
||||
|
||||
tmd is different ( zero'd RSA )
|
||||
1 content, 0x00027B5D bytes
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
block 4 ( the rest of bootmii )
|
||||
0 - 0x8a60
|
||||
gibberish - rest of bootmii content still encrypted
|
||||
|
||||
0x8a60 - 0x1f800
|
||||
0s
|
||||
|
||||
0x1f800
|
||||
26F29A401EE684CF0000000501010100
|
||||
00000000010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
010101010101010101010101
|
||||
|
||||
26F29A401EE684CF0000000501010100
|
||||
00000000010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
010101010101010101010101
|
||||
|
||||
26F29A401EE684CF0000000501010100
|
||||
00000000010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
010101010101010101010101
|
||||
|
||||
then 0s till 0x20000 ( end of block )
|
||||
|
||||
|
||||
|
||||
block 5
|
||||
all 0xff
|
||||
|
||||
block 6
|
||||
identical to block 2 except this only difference is the 0x02 is 0x03
|
||||
26F29A401EE684CF0000000301000000
|
||||
00000000010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
010101010101010101010101
|
||||
|
||||
26F29A401EE684CF0000000301000000
|
||||
00000000010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
010101010101010101010101
|
||||
|
||||
26F29A401EE684CF0000000301000000
|
||||
00000000010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
010101010101010101010101
|
||||
|
||||
|
||||
block 7
|
||||
identical to block 1
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////
|
||||
nand #2 ( no bootmii )
|
||||
///////////
|
||||
|
||||
block 0 ( boot 1c )
|
||||
gibberish till 0x43f0. then 0s for the rest of the block
|
||||
|
||||
block 1
|
||||
same wad header as before, with the same values
|
||||
cert doodad and ticket are the same
|
||||
tmd is v4 and the content is 0x00027BE8 bytes
|
||||
|
||||
block 2
|
||||
0 - 0x8af0
|
||||
the rest of the content from boot2 ( padded to 0x40 )
|
||||
|
||||
0s till 0x9000
|
||||
0xff till 0x18f00
|
||||
|
||||
26F29A401EE684CF0000000201000000
|
||||
00000000010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
010101010101010101010101
|
||||
|
||||
26F29A401EE684CF0000000201000000
|
||||
00000000010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
010101010101010101010101
|
||||
|
||||
26F29A401EE684CF0000000201000000
|
||||
00000000010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
010101010101010101010101
|
||||
|
||||
|
||||
|
||||
block 3 - all 0xff
|
||||
block 4 - all 0xff
|
||||
block 5 - all 0xff
|
||||
|
||||
block 6 - identical to block 2 except the blockmap ( generation 3 ? )
|
||||
|
||||
26F29A401EE684CF0000000301000000
|
||||
00000000010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
010101010101010101010101
|
||||
|
||||
26F29A401EE684CF0000000301000000
|
||||
00000000010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
010101010101010101010101
|
||||
|
||||
26F29A401EE684CF0000000301000000
|
||||
00000000010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
010101010101010101010101
|
||||
|
||||
block 7 matches block 1
|
||||
|
||||
|
||||
|
||||
|
||||
/////////////
|
||||
nand #3
|
||||
/////////////
|
||||
|
||||
block 2
|
||||
26F29A401EE684CF
|
||||
00000004
|
||||
01000000000000000101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
|
||||
block 4
|
||||
26F29A401EE684CF
|
||||
0000000F
|
||||
01010100000000000101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
|
||||
block 6
|
||||
26F29A401EE684CF
|
||||
00000005
|
||||
01000000000000000101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
26F29A401EE684CF
|
||||
00000002
|
||||
|
||||
01000000000000000101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
|
||||
|
||||
26F29A401EE684CF
|
||||
00000005
|
||||
01010100000000000101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
01010101010101010101010101010101
|
||||
|
||||
|
||||
*/
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define NANDBIN_H
|
||||
|
||||
#include "includes.h"
|
||||
#include "blocks0to7.h"
|
||||
struct fst_t
|
||||
{
|
||||
quint8 filename[ 0xc ];
|
||||
@ -84,6 +85,10 @@ public:
|
||||
//get the fats for a given file
|
||||
const QList<quint16> GetFatsForFile( quint16 i );
|
||||
|
||||
const Blocks0to7 BootBlocks(){ return bootBlocks; }
|
||||
const QList<Boot2Info> Boot2Infos();
|
||||
quint8 Boot1Version();
|
||||
|
||||
|
||||
private:
|
||||
QByteArray key;
|
||||
@ -129,6 +134,9 @@ private:
|
||||
QTreeWidgetItem *ItemFromPath( const QString &path );
|
||||
QTreeWidgetItem *FindItem( const QString &s, QTreeWidgetItem *parent );
|
||||
|
||||
//holds info about boot1 & 2
|
||||
Blocks0to7 bootBlocks;
|
||||
|
||||
signals:
|
||||
//connect to these to receive messages from this object
|
||||
void SendError( QString );
|
||||
|
123
nandExtract/boot2infodialog.cpp
Normal file
123
nandExtract/boot2infodialog.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
#include "boot2infodialog.h"
|
||||
#include "ui_boot2infodialog.h"
|
||||
|
||||
Boot2InfoDialog::Boot2InfoDialog( QWidget *parent, QList<Boot2Info> boot, quint8 boot1version ) : QDialog(parent), ui(new Ui::Boot2InfoDialog)
|
||||
{
|
||||
ui->setupUi( this );
|
||||
ui->label_boot2Version->clear();
|
||||
|
||||
switch( boot1version )
|
||||
{
|
||||
case BOOT_1_A:
|
||||
ui->label_boot1->setText( "Boot1 A (vulnerable)");
|
||||
break;
|
||||
case BOOT_1_B:
|
||||
ui->label_boot1->setText( "Boot1 B (vulnerable)");
|
||||
break;
|
||||
case BOOT_1_C:
|
||||
ui->label_boot1->setText( "Boot1 C (fixed)");
|
||||
break;
|
||||
case BOOT_1_D:
|
||||
ui->label_boot1->setText( "Boot1 D (fixed)");
|
||||
break;
|
||||
}
|
||||
|
||||
quint8 c = boot.size();
|
||||
if( !c )
|
||||
return;
|
||||
|
||||
ui->label_foundTxt->setText( QString( "Found %1 copies of boot2" ).arg( c ) );
|
||||
boot2s = boot;
|
||||
|
||||
for( quint8 i = 0; i < c; i++ )
|
||||
{
|
||||
QString entry = QString( "%1 - %2" ).arg( boot.at( i ).firstBlock ).arg( boot.at( i ).secondBlock );
|
||||
ui->comboBox_boot2->addItem( entry );
|
||||
if( boot.at( i ).state & BOOT_2_USED_TO_BOOT )
|
||||
{
|
||||
ui->comboBox_boot2->setCurrentIndex( i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Boot2InfoDialog::~Boot2InfoDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void Boot2InfoDialog::on_comboBox_boot2_currentIndexChanged( int index )
|
||||
{
|
||||
if( index > boot2s.size() )
|
||||
return;
|
||||
Boot2Info bi = boot2s.at( index );
|
||||
ui->label_boot2Version->clear();
|
||||
|
||||
ui->lineEdit_contentStatus->clear();
|
||||
ui->lineEdit_bootStatus->clear();
|
||||
ui->lineEdit_tikSig->setText( tr( "Not Bootable" ) );
|
||||
ui->lineEdit_tmdSig->clear();
|
||||
ui->lineEdit_gen->setText( tr( "Generation %1").arg( bi.generation ) );
|
||||
|
||||
|
||||
if( bi.state == BOOT_2_ERROR_PARSING || bi.state == BOOT_2_ERROR )
|
||||
ui->lineEdit_contentStatus->setText( tr( "Error parsing boot2" ) );
|
||||
else if( bi.state == BOOT_2_BAD_SIGNATURE )
|
||||
ui->lineEdit_contentStatus->setText( tr( "Bad RSA Signature" ) );
|
||||
else if( bi.state == BOOT_2_BAD_CONTENT_HASH )
|
||||
ui->lineEdit_contentStatus->setText( tr( "Content hash doesn't match TMD" ) );
|
||||
else if( bi.state == BOOT_2_ERROR_PARSING )
|
||||
ui->lineEdit_contentStatus->setText( tr( "Error parsing boot2" ) );
|
||||
else
|
||||
{
|
||||
|
||||
if( bi.state & BOOT_2_MARKED_BAD )
|
||||
ui->lineEdit_bootStatus->setText( tr( "Marked as bad blocks" ) );
|
||||
else if( bi.state & BOOT_2_USED_TO_BOOT )
|
||||
ui->lineEdit_bootStatus->setText( tr( "Used for booting" ) + " " );//in case the backup copy is used for booting
|
||||
else if( bi.state & BOOT_2_BACKUP_COPY )
|
||||
ui->lineEdit_bootStatus->setText( ui->lineEdit_bootStatus->text() + tr( "Backup copy" ) );
|
||||
|
||||
|
||||
ui->lineEdit_contentStatus->setText( tr( "Content Sha1 matches TMD" ) );
|
||||
|
||||
if( bi.state & BOOT_2_TMD_FAKESIGNED )
|
||||
ui->lineEdit_tmdSig->setText( tr( "TMD is fakesigned" ) );
|
||||
else if( bi.state & BOOT_2_TMD_SIG_OK )
|
||||
ui->lineEdit_tmdSig->setText( tr( "TMD officially signed" ) );
|
||||
else
|
||||
ui->lineEdit_tmdSig->setText( tr( "Error checking TMD" ) );
|
||||
|
||||
if( bi.state & BOOT_2_TIK_FAKESIGNED )
|
||||
ui->lineEdit_tikSig->setText( tr( "Ticket is fakesigned" ) );
|
||||
else if( bi.state & BOOT_2_TIK_SIG_OK )
|
||||
ui->lineEdit_tikSig->setText( tr( "Ticket officially signed" ) );
|
||||
else
|
||||
ui->lineEdit_tikSig->setText( tr( "Error checking ticket" ) );
|
||||
|
||||
QString ver;
|
||||
switch( bi.version )
|
||||
{
|
||||
case BOOTMII_11:
|
||||
ver = "BootMii 1.1";
|
||||
break;
|
||||
case BOOTMII_13:
|
||||
ver = "BootMii 1.3";
|
||||
break;
|
||||
case BOOTMII_UNK:
|
||||
ver = "BootMii (Unk)";
|
||||
break;
|
||||
default:
|
||||
ver = QString( tr( "Version %1" ).arg( bi.version ) );
|
||||
break;
|
||||
}
|
||||
ui->label_boot2Version->setText( ver );
|
||||
}
|
||||
|
||||
|
||||
/*QString bls = QByteArray( (const char*)&bi.blockMap, 8 ).toHex();
|
||||
QString str = QString( "copy:%1\tblocks%2 & %3 gen: %4 state: %5\n%7").arg( index ).arg( bi.firstBlock )
|
||||
.arg( bi.secondBlock ).arg( bi.generation, 8, 16, QChar( '0' ) )
|
||||
.arg( bi.state, 8, 16, QChar( ' ' ) ).arg( bls );
|
||||
|
||||
qDebug() << str;*/
|
||||
}
|
30
nandExtract/boot2infodialog.h
Normal file
30
nandExtract/boot2infodialog.h
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef BOOT2INFODIALOG_H
|
||||
#define BOOT2INFODIALOG_H
|
||||
|
||||
#include "../WiiQt/includes.h"
|
||||
#include "../WiiQt/blocks0to7.h"
|
||||
|
||||
namespace Ui {
|
||||
class Boot2InfoDialog;
|
||||
}
|
||||
|
||||
class Boot2InfoDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Boot2InfoDialog( QWidget *parent = 0, QList<Boot2Info> boot = QList<Boot2Info>(), quint8 boot1version = BOOT_1_UNK );
|
||||
~Boot2InfoDialog();
|
||||
|
||||
private:
|
||||
Ui::Boot2InfoDialog *ui;
|
||||
|
||||
QList<Boot2Info> boot2s;
|
||||
|
||||
//quint8 usedBlockmap[ 8 ];
|
||||
|
||||
private slots:
|
||||
void on_comboBox_boot2_currentIndexChanged( int index );
|
||||
};
|
||||
|
||||
#endif // BOOT2INFODIALOG_H
|
170
nandExtract/boot2infodialog.ui
Normal file
170
nandExtract/boot2infodialog.ui
Normal file
@ -0,0 +1,170 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Boot2InfoDialog</class>
|
||||
<widget class="QDialog" name="Boot2InfoDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>428</width>
|
||||
<height>310</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_boot1">
|
||||
<property name="text">
|
||||
<string>Unknown boot1 version</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_foundTxt">
|
||||
<property name="text">
|
||||
<string>Error getting boot2 info</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBox_boot2">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_boot2Version">
|
||||
<property name="text">
|
||||
<string>version</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEdit_gen"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEdit_tmdSig">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>323</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEdit_tikSig">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEdit_contentStatus">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEdit_bootStatus">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>Boot2InfoDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>Boot2InfoDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
@ -5,19 +5,25 @@ TARGET = nandExtract
|
||||
TEMPLATE = app
|
||||
SOURCES += main.cpp \
|
||||
nandwindow.cpp \
|
||||
../WiiQt/blocks0to7.cpp \
|
||||
../WiiQt/tiktmd.cpp \
|
||||
../WiiQt/nandbin.cpp \
|
||||
../WiiQt/tools.cpp \
|
||||
../WiiQt/savebanner.cpp \
|
||||
../WiiQt/aes.c \
|
||||
../WiiQt/sha1.c \
|
||||
nandthread.cpp
|
||||
nandthread.cpp \
|
||||
boot2infodialog.cpp
|
||||
|
||||
HEADERS += nandwindow.h \
|
||||
../WiiQt/tiktmd.h \
|
||||
../WiiQt/nandbin.h \
|
||||
../WiiQt/tools.h \
|
||||
nandthread.h
|
||||
nandthread.h \
|
||||
boot2infodialog.h
|
||||
|
||||
FORMS += nandwindow.ui
|
||||
FORMS += nandwindow.ui \
|
||||
boot2infodialog.ui
|
||||
|
||||
RESOURCES += \
|
||||
rc.qrc
|
||||
|
@ -43,6 +43,36 @@ bool NandThread::SetPath( const QString &path )
|
||||
return nandBin.SetPath( path );
|
||||
}
|
||||
|
||||
const Blocks0to7 NandThread::BootBlocks()
|
||||
{
|
||||
if( isRunning() )
|
||||
{
|
||||
emit SendError( tr( "Wait till the current job is done" ) );
|
||||
return Blocks0to7();
|
||||
}
|
||||
return nandBin.BootBlocks();
|
||||
}
|
||||
|
||||
const QList<Boot2Info> NandThread::Boot2Infos()
|
||||
{
|
||||
if( isRunning() )
|
||||
{
|
||||
emit SendError( tr( "Wait till the current job is done" ) );
|
||||
return QList<Boot2Info>();
|
||||
}
|
||||
return nandBin.Boot2Infos();
|
||||
}
|
||||
|
||||
quint8 NandThread::Boot1Version()
|
||||
{
|
||||
if( isRunning() )
|
||||
{
|
||||
emit SendError( tr( "Wait till the current job is done" ) );
|
||||
return 0;
|
||||
}
|
||||
return nandBin.Boot1Version();
|
||||
}
|
||||
|
||||
QTreeWidgetItem *NandThread::GetTree()
|
||||
{
|
||||
if( isRunning() )
|
||||
|
@ -30,6 +30,10 @@ class NandThread : public QThread
|
||||
void Extract( QTreeWidgetItem *item, const QString &path );
|
||||
void ForceQuit();
|
||||
|
||||
const Blocks0to7 BootBlocks();
|
||||
const QList<Boot2Info> Boot2Infos();
|
||||
quint8 Boot1Version();
|
||||
|
||||
protected:
|
||||
void run();
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "nandwindow.h"
|
||||
#include "ui_nandwindow.h"
|
||||
#include "boot2infodialog.h"
|
||||
#include "../WiiQt/tools.h"
|
||||
|
||||
NandWindow::NandWindow( QWidget *parent ) : QMainWindow( parent ), ui( new Ui::NandWindow ), nThread( this )
|
||||
@ -405,3 +406,17 @@ void NandWindow::on_treeWidget_currentItemChanged( QTreeWidgetItem* current, QTr
|
||||
|
||||
}
|
||||
|
||||
//get boot2 info and show it as a dialog
|
||||
void NandWindow::on_actionBoot2_triggered()
|
||||
{
|
||||
QList<Boot2Info> b = nThread.Boot2Infos();
|
||||
if( b.isEmpty() )
|
||||
{
|
||||
qDebug() << "!ok";
|
||||
return;
|
||||
}
|
||||
quint8 boot1 = nThread.Boot1Version();
|
||||
|
||||
Boot2InfoDialog d( this, b, boot1 );
|
||||
d.exec();
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ public slots:
|
||||
void ThreadIsDone();
|
||||
|
||||
private slots:
|
||||
void on_actionBoot2_triggered();
|
||||
void on_treeWidget_currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous);
|
||||
void on_actionShow_Usage_triggered();
|
||||
void on_actionOpen_Nand_triggered();
|
||||
|
@ -125,6 +125,7 @@
|
||||
<string>Info</string>
|
||||
</property>
|
||||
<addaction name="actionShow_Usage"/>
|
||||
<addaction name="actionBoot2"/>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
<addaction name="menuInfo"/>
|
||||
@ -160,6 +161,14 @@
|
||||
<string>Ctrl+U</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionBoot2">
|
||||
<property name="text">
|
||||
<string>Boot2</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+B</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources/>
|
||||
|
Loading…
Reference in New Issue
Block a user