mirror of
https://github.com/martravi/wiiqt.git
synced 2024-11-25 18:46:56 +01:00
added functions for modifying permissions of a wad. applied titleid setting for tickets.
This commit is contained in:
parent
374699c434
commit
0f506a205d
@ -52,6 +52,34 @@ bool Tmd::SetIOS( quint64 ios )
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Tmd::SetAhb( bool remove )
|
||||||
|
{
|
||||||
|
if( !p_tmd )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
quint32 access = qFromBigEndian( p_tmd->access_rights );
|
||||||
|
if( remove )
|
||||||
|
access |= 1;
|
||||||
|
else
|
||||||
|
access &= 0xfffffffe;
|
||||||
|
p_tmd->access_rights = qToBigEndian( access );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Tmd::SetDiskAccess( bool allow )
|
||||||
|
{
|
||||||
|
if( !p_tmd )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
quint32 access = qFromBigEndian( p_tmd->access_rights );
|
||||||
|
if( allow )
|
||||||
|
access |= 2;
|
||||||
|
else
|
||||||
|
access &= 0xfffffffd;
|
||||||
|
p_tmd->access_rights = qToBigEndian( access );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
quint16 Tmd::Gid()
|
quint16 Tmd::Gid()
|
||||||
{
|
{
|
||||||
if( !p_tmd )
|
if( !p_tmd )
|
||||||
@ -122,7 +150,7 @@ bool Tmd::SetSize( quint16 cid, quint32 size )
|
|||||||
if( !p_tmd || cid >= qFromBigEndian( p_tmd->num_contents ) )
|
if( !p_tmd || cid >= qFromBigEndian( p_tmd->num_contents ) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
p_tmd->contents[ cid ].size = qFromBigEndian( size );
|
p_tmd->contents[ cid ].size = qFromBigEndian( (quint64)size );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,11 +277,23 @@ bool Ticket::SetTid( quint64 tid )
|
|||||||
{
|
{
|
||||||
if( !p_tik )
|
if( !p_tik )
|
||||||
return false;
|
return false;
|
||||||
//hexdump( data, payLoadOffset, data.size() - payLoadOffset );
|
|
||||||
|
|
||||||
quint64 t = qFromBigEndian( tid );
|
p_tik->titleid = qFromBigEndian( tid );
|
||||||
p_tik->titleid = t;
|
|
||||||
//hexdump( data, payLoadOffset, data.size() - payLoadOffset );
|
//create new title key
|
||||||
|
quint8 iv[ 16 ];
|
||||||
|
quint8 keyin[ 16 ];
|
||||||
|
quint8 commonkey[ 16 ] = COMMON_KEY;
|
||||||
|
|
||||||
|
memcpy( &keyin, (quint8 *)decKey.data(), 16 );
|
||||||
|
memset( &p_tik->cipher_title_key, 0, 16 );
|
||||||
|
memset( &iv, 0, 16 );
|
||||||
|
memcpy( &iv, &p_tik->titleid, sizeof(quint64) );
|
||||||
|
|
||||||
|
aes_set_key( (quint8 *)&commonkey );
|
||||||
|
aes_encrypt( (quint8 *)&iv, (quint8 *)&keyin,
|
||||||
|
(quint8 *)&p_tik->cipher_title_key, 16 );
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,6 +207,8 @@ public:
|
|||||||
bool SetSize( quint16 i, quint32 size );
|
bool SetSize( quint16 i, quint32 size );
|
||||||
bool SetHash( quint16 i, const QByteArray hash );
|
bool SetHash( quint16 i, const QByteArray hash );
|
||||||
bool SetIOS( quint64 ios );
|
bool SetIOS( quint64 ios );
|
||||||
|
bool SetAhb( bool remove = true );
|
||||||
|
bool SetDiskAccess( bool allow = true );
|
||||||
|
|
||||||
bool FakeSign();
|
bool FakeSign();
|
||||||
|
|
||||||
|
482
WiiQt/wad.cpp
482
WiiQt/wad.cpp
@ -34,8 +34,8 @@ Wad::Wad( const QByteArray stuff )
|
|||||||
b.read( (char*)&tmp, 4 );
|
b.read( (char*)&tmp, 4 );
|
||||||
tmp = qFromBigEndian( tmp );
|
tmp = qFromBigEndian( tmp );
|
||||||
if( tmp != 0x49730000 &&
|
if( tmp != 0x49730000 &&
|
||||||
tmp != 0x69620000 &&
|
tmp != 0x69620000 &&
|
||||||
tmp != 0x426b0000 )
|
tmp != 0x426b0000 )
|
||||||
{
|
{
|
||||||
b.close();
|
b.close();
|
||||||
hexdump( stuff, 0, 0x40 );
|
hexdump( stuff, 0, 0x40 );
|
||||||
@ -43,91 +43,93 @@ Wad::Wad( const QByteArray stuff )
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
quint32 certSize;
|
quint32 certSize;
|
||||||
quint32 tikSize;
|
quint32 tikSize;
|
||||||
quint32 tmdSize;
|
quint32 tmdSize;
|
||||||
quint32 appSize;
|
quint32 appSize;
|
||||||
quint32 footerSize;
|
quint32 footerSize;
|
||||||
|
|
||||||
b.read( (char*)&certSize, 4 );
|
b.read( (char*)&certSize, 4 );
|
||||||
certSize = qFromBigEndian( certSize );
|
certSize = qFromBigEndian( certSize );
|
||||||
|
|
||||||
b.seek( 0x10 );
|
b.seek( 0x10 );
|
||||||
b.read( (char*)&tikSize, 4 );
|
b.read( (char*)&tikSize, 4 );
|
||||||
tikSize = qFromBigEndian( tikSize );
|
tikSize = qFromBigEndian( tikSize );
|
||||||
b.read( (char*)&tmdSize, 4 );
|
b.read( (char*)&tmdSize, 4 );
|
||||||
tmdSize = qFromBigEndian( tmdSize );
|
tmdSize = qFromBigEndian( tmdSize );
|
||||||
b.read( (char*)&appSize, 4 );
|
b.read( (char*)&appSize, 4 );
|
||||||
appSize = qFromBigEndian( appSize );
|
appSize = qFromBigEndian( appSize );
|
||||||
b.read( (char*)&footerSize, 4 );
|
b.read( (char*)&footerSize, 4 );
|
||||||
footerSize = qFromBigEndian( footerSize );
|
footerSize = qFromBigEndian( footerSize );
|
||||||
|
|
||||||
b.close();//close the buffer, the rest of the data can be checked without it
|
b.close();//close the buffer, the rest of the data can be checked without it
|
||||||
|
|
||||||
//sanity check this thing
|
//sanity check this thing
|
||||||
quint32 s = stuff.size();
|
quint32 s = stuff.size();
|
||||||
if( s < ( RU( 0x40, certSize ) + RU( 0x40, tikSize ) + RU( 0x40, tmdSize ) + RU( 0x40, appSize ) + RU( 0x40, footerSize ) ) )
|
if( s < ( RU( 0x40, certSize ) + RU( 0x40, tikSize ) + RU( 0x40, tmdSize ) + RU( 0x40, appSize ) + RU( 0x40, footerSize ) ) )
|
||||||
{
|
|
||||||
Err( "Total size is less than the combined sizes of all the parts that it is supposed to contain" );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
quint32 pos = 0x40;
|
|
||||||
certData = stuff.mid( pos, certSize );
|
|
||||||
pos += RU( 0x40, certSize );
|
|
||||||
tikData = stuff.mid( pos, tikSize );
|
|
||||||
pos += RU( 0x40, tikSize );
|
|
||||||
tmdData = stuff.mid( pos, tmdSize );
|
|
||||||
pos += RU( 0x40, tmdSize );
|
|
||||||
|
|
||||||
Ticket ticket( tikData );
|
|
||||||
Tmd t( tmdData );
|
|
||||||
|
|
||||||
//hexdump( tikData );
|
|
||||||
//hexdump( tmdData );
|
|
||||||
|
|
||||||
if( ticket.Tid() != t.Tid() )
|
|
||||||
qWarning() << "wad contains 2 different TIDs";
|
|
||||||
|
|
||||||
quint32 cnt = t.Count();
|
|
||||||
//qDebug() << "Wad contains" << hex << cnt << "contents";
|
|
||||||
|
|
||||||
//another quick sanity check
|
|
||||||
quint32 totalSize = 0;
|
|
||||||
for( quint32 i = 0; i < cnt; i++ )
|
|
||||||
totalSize += t.Size( i );
|
|
||||||
|
|
||||||
if( totalSize > appSize )
|
|
||||||
{
|
|
||||||
Err( "Size of all the apps in the tmd is greater than the size in the wad header" );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//read all the contents, check the hash, and remember the data ( still encrypted )
|
|
||||||
for( quint32 i = 0; i < cnt; i++ )
|
|
||||||
{
|
|
||||||
|
|
||||||
quint32 s = RU( 0x40, t.Size( i ) );
|
|
||||||
//qDebug() << "content" << i << "is at" << hex << pos << "with size" << s;
|
|
||||||
QByteArray encData = stuff.mid( pos, s );
|
|
||||||
pos += s;
|
|
||||||
|
|
||||||
//doing this here in case there is some other object that is using the AES that would change the key on us
|
|
||||||
AesSetKey( ticket.DecryptedKey() );
|
|
||||||
|
|
||||||
QByteArray decData = AesDecrypt( i, encData );
|
|
||||||
decData.resize( t.Size( i ) );
|
|
||||||
QByteArray realHash = GetSha1( decData );
|
|
||||||
if( realHash != t.Hash( i ) )
|
|
||||||
{
|
{
|
||||||
Err( QString( "hash doesnt match for content %1" ).arg( i ) );
|
Err( "Total size is less than the combined sizes of all the parts that it is supposed to contain" );
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
partsEnc << encData;
|
|
||||||
}
|
quint32 pos = 0x40;
|
||||||
//wtf? some VC titles have a full banner as the footer? maybe somebody's wad packer is busted
|
certData = stuff.mid( pos, certSize );
|
||||||
//QByteArray footer = stuff.mid( pos, stuff.size() - pos );
|
pos += RU( 0x40, certSize );
|
||||||
//qDebug() << "footer";
|
tikData = stuff.mid( pos, tikSize );
|
||||||
//hexdump( footer );
|
pos += RU( 0x40, tikSize );
|
||||||
ok = true;
|
tmdData = stuff.mid( pos, tmdSize );
|
||||||
|
pos += RU( 0x40, tmdSize );
|
||||||
|
|
||||||
|
Ticket ticket( tikData );
|
||||||
|
Tmd t( tmdData );
|
||||||
|
|
||||||
|
//hexdump( tikData );
|
||||||
|
//hexdump( tmdData );
|
||||||
|
|
||||||
|
if( ticket.Tid() != t.Tid() )
|
||||||
|
qWarning() << "wad contains 2 different TIDs";
|
||||||
|
|
||||||
|
quint32 cnt = t.Count();
|
||||||
|
//qDebug() << "Wad contains" << hex << cnt << "contents";
|
||||||
|
|
||||||
|
//another quick sanity check
|
||||||
|
quint32 totalSize = 0;
|
||||||
|
for( quint32 i = 0; i < cnt; i++ )
|
||||||
|
totalSize += t.Size( i );
|
||||||
|
|
||||||
|
if( totalSize > appSize )
|
||||||
|
{
|
||||||
|
Err( "Size of all the apps in the tmd is greater than the size in the wad header" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//read all the contents, check the hash, and remember the data ( still encrypted )
|
||||||
|
for( quint32 i = 0; i < cnt; i++ )
|
||||||
|
{
|
||||||
|
|
||||||
|
quint32 s = RU( 0x40, t.Size( i ) );
|
||||||
|
qDebug() << "content" << i << "is at" << hex << pos
|
||||||
|
<< "with size" << s;
|
||||||
|
QByteArray encData = stuff.mid( pos, s );
|
||||||
|
pos += s;
|
||||||
|
|
||||||
|
//doing this here in case there is some other object that
|
||||||
|
//is using the AES that would change the key on us
|
||||||
|
AesSetKey( ticket.DecryptedKey() );
|
||||||
|
|
||||||
|
QByteArray decData = AesDecrypt( i, encData );
|
||||||
|
decData.resize( t.Size( i ) );
|
||||||
|
QByteArray realHash = GetSha1( decData );
|
||||||
|
if( realHash != t.Hash( i ) )
|
||||||
|
{
|
||||||
|
Err( QString( "hash doesnt match for content %1" ).arg( i ) );
|
||||||
|
}
|
||||||
|
partsEnc << encData;
|
||||||
|
}
|
||||||
|
//wtf? some VC titles have a full banner as the footer? maybe somebody's wad packer is busted
|
||||||
|
//QByteArray footer = stuff.mid( pos, stuff.size() - pos );
|
||||||
|
//qDebug() << "footer";
|
||||||
|
//hexdump( footer );
|
||||||
|
ok = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Wad::Wad( QList< QByteArray > stuff, bool encrypted )
|
Wad::Wad( QList< QByteArray > stuff, bool encrypted )
|
||||||
@ -245,93 +247,94 @@ void Wad::Err( QString str )
|
|||||||
|
|
||||||
const QByteArray Wad::Data( quint32 magicWord, const QByteArray footer )
|
const QByteArray Wad::Data( quint32 magicWord, const QByteArray footer )
|
||||||
{
|
{
|
||||||
//qDebug() << "Wad::Data" << hex << magicWord << footer.size();
|
//qDebug() << "Wad::Data" << hex << magicWord << footer.size();
|
||||||
if( !partsEnc.size() || tmdData.isEmpty() || tikData.isEmpty() || ( certData.isEmpty() && globalCert.isEmpty() ) )
|
if( !partsEnc.size() || tmdData.isEmpty() || tikData.isEmpty() || ( certData.isEmpty() && globalCert.isEmpty() ) )
|
||||||
{
|
|
||||||
Err( "Dont have all the parts to make a wad" );
|
|
||||||
return QByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
//do some brief checks to make sure that wad is good
|
|
||||||
Ticket ticket( tikData );
|
|
||||||
Tmd t( tmdData );
|
|
||||||
if( t.Tid() != ticket.Tid() )
|
|
||||||
{
|
|
||||||
Err( "Ticket and TMD have different TID" );
|
|
||||||
return QByteArray();
|
|
||||||
}
|
|
||||||
if( partsEnc.size() != t.Count() )
|
|
||||||
{
|
|
||||||
Err( "Dont have enough contents according to the TMD" );
|
|
||||||
return QByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
//everything seems in order, try to make the thing
|
|
||||||
QByteArray cert = certData.isEmpty() ? globalCert : certData;
|
|
||||||
quint32 certSize = cert.size();
|
|
||||||
quint32 tikSize = ticket.SignedSize();
|
|
||||||
quint32 tmdSize = t.SignedSize();
|
|
||||||
quint32 appSize = 0;
|
|
||||||
quint32 footerSize = footer.size();
|
|
||||||
|
|
||||||
//add all the app sizes together and check that they match the TMD
|
|
||||||
quint16 cnt = t.Count();
|
|
||||||
for( quint16 i = 0; i < cnt; i++ )
|
|
||||||
{
|
|
||||||
quint32 s = RU( 0x40, partsEnc.at( i ).size() );
|
|
||||||
if( RU( 0x40, t.Size( i ) ) != s )
|
|
||||||
{
|
{
|
||||||
Err( QString( "Size of content %1 is bad ( %2, %3, %4 )" ).arg( i )
|
Err( "Dont have all the parts to make a wad" );
|
||||||
.arg( t.Size( i ), 0, 16 )
|
return QByteArray();
|
||||||
.arg( RU( 0x40, t.Size( i ) ), 0, 16 )
|
|
||||||
.arg( s, 0, 16 ) );
|
|
||||||
return QByteArray();
|
|
||||||
}
|
}
|
||||||
appSize += s;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray header( 0x20, '\0' );
|
//do some brief checks to make sure that wad is good
|
||||||
QBuffer buf( &header );
|
Ticket ticket( tikData );
|
||||||
buf.open( QIODevice::WriteOnly );
|
Tmd t( tmdData );
|
||||||
|
if( t.Tid() != ticket.Tid() )
|
||||||
|
{
|
||||||
|
Err( "Ticket and TMD have different TID" );
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
if( partsEnc.size() != t.Count() )
|
||||||
|
{
|
||||||
|
Err( "Dont have enough contents according to the TMD" );
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
quint32 tmp = qFromBigEndian( 0x20 );//header size
|
//everything seems in order, try to make the thing
|
||||||
buf.write( (const char*)&tmp, 4 );
|
QByteArray cert = certData.isEmpty() ? globalCert : certData;
|
||||||
tmp = qFromBigEndian( magicWord );//magic word
|
quint32 certSize = cert.size();
|
||||||
buf.write( (const char*)&tmp, 4 );
|
quint32 tikSize = ticket.SignedSize();
|
||||||
tmp = qFromBigEndian( certSize );
|
quint32 tmdSize = t.SignedSize();
|
||||||
buf.write( (const char*)&tmp, 4 );
|
quint32 appSize = 0;
|
||||||
buf.seek( 0x10 );
|
quint32 footerSize = footer.size();
|
||||||
tmp = qFromBigEndian( tikSize );
|
|
||||||
buf.write( (const char*)&tmp, 4 );
|
|
||||||
tmp = qFromBigEndian( tmdSize );
|
|
||||||
buf.write( (const char*)&tmp, 4 );
|
|
||||||
tmp = qFromBigEndian( appSize );
|
|
||||||
buf.write( (const char*)&tmp, 4 );
|
|
||||||
tmp = qFromBigEndian( footerSize );
|
|
||||||
buf.write( (const char*)&tmp, 4 );
|
|
||||||
buf.close();
|
|
||||||
//hexdump( header, 0, 0x20 );
|
|
||||||
|
|
||||||
QByteArray ret = PaddedByteArray( header, 0x40 ) + PaddedByteArray( cert, 0x40 );
|
//add all the app sizes together and check that they match the TMD
|
||||||
//make sure we dont have the huge ticket and TMD that come when downloading from NUS
|
quint16 cnt = t.Count();
|
||||||
QByteArray tik = tikData;
|
for( quint16 i = 0; i < cnt; i++ )
|
||||||
tik.resize( tikSize );
|
{
|
||||||
tik = PaddedByteArray( tik, 0x40 );
|
quint32 s = RU( 0x40, partsEnc.at( i ).size() );
|
||||||
|
if( RU( 0x40, t.Size( i ) ) != s )
|
||||||
|
{
|
||||||
|
Err( QString( "Size of content %1 is bad ( %2, %3, %4 )" )
|
||||||
|
.arg( i )
|
||||||
|
.arg( t.Size( i ), 0, 16 )
|
||||||
|
.arg( RU( 0x40, t.Size( i ) ), 0, 16 )
|
||||||
|
.arg( s, 0, 16 ) );
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
appSize += s;
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray tm = tmdData;
|
QByteArray header( 0x20, '\0' );
|
||||||
tm.resize( tmdSize );
|
QBuffer buf( &header );
|
||||||
tm = PaddedByteArray( tm, 0x40 );
|
buf.open( QIODevice::WriteOnly );
|
||||||
|
|
||||||
//hexdump( tik );
|
quint32 tmp = qFromBigEndian( 0x20 );//header size
|
||||||
//hexdump( tm );
|
buf.write( (const char*)&tmp, 4 );
|
||||||
|
tmp = qFromBigEndian( magicWord );//magic word
|
||||||
|
buf.write( (const char*)&tmp, 4 );
|
||||||
|
tmp = qFromBigEndian( certSize );
|
||||||
|
buf.write( (const char*)&tmp, 4 );
|
||||||
|
buf.seek( 0x10 );
|
||||||
|
tmp = qFromBigEndian( tikSize );
|
||||||
|
buf.write( (const char*)&tmp, 4 );
|
||||||
|
tmp = qFromBigEndian( tmdSize );
|
||||||
|
buf.write( (const char*)&tmp, 4 );
|
||||||
|
tmp = qFromBigEndian( appSize );
|
||||||
|
buf.write( (const char*)&tmp, 4 );
|
||||||
|
tmp = qFromBigEndian( footerSize );
|
||||||
|
buf.write( (const char*)&tmp, 4 );
|
||||||
|
buf.close();
|
||||||
|
//hexdump( header, 0, 0x20 );
|
||||||
|
|
||||||
ret += tik + tm;
|
QByteArray ret = PaddedByteArray( header, 0x40 ) + PaddedByteArray( cert, 0x40 );
|
||||||
for( quint16 i = 0; i < cnt; i++ )
|
//make sure we dont have the huge ticket and TMD that come when downloading from NUS
|
||||||
{
|
QByteArray tik = tikData;
|
||||||
ret += PaddedByteArray( partsEnc.at( i ), 0x40 );
|
tik.resize( tikSize );
|
||||||
}
|
tik = PaddedByteArray( tik, 0x40 );
|
||||||
ret += footer;
|
|
||||||
return ret;
|
QByteArray tm = tmdData;
|
||||||
|
tm.resize( tmdSize );
|
||||||
|
tm = PaddedByteArray( tm, 0x40 );
|
||||||
|
|
||||||
|
//hexdump( tik );
|
||||||
|
//hexdump( tm );
|
||||||
|
|
||||||
|
ret += tik + tm;
|
||||||
|
for( quint16 i = 0; i < cnt; i++ )
|
||||||
|
{
|
||||||
|
ret += PaddedByteArray( partsEnc.at( i ), 0x40 );
|
||||||
|
}
|
||||||
|
ret += footer;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Wad::WadName( QString path )
|
QString Wad::WadName( QString path )
|
||||||
@ -546,58 +549,129 @@ QByteArray Wad::FromDirectory( QDir dir )
|
|||||||
|
|
||||||
bool Wad::SetTid( quint64 tid )
|
bool Wad::SetTid( quint64 tid )
|
||||||
{
|
{
|
||||||
if( !tmdData.size() || !tikData.size() )
|
if( !tmdData.size() || !tikData.size() )
|
||||||
{
|
{
|
||||||
Err( "Mising parts of the wad" );
|
Err( "Mising parts of the wad" );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Tmd t( tmdData );
|
Tmd t( tmdData );
|
||||||
Ticket ti( tikData );
|
Ticket ti( tikData );
|
||||||
|
|
||||||
t.SetTid( tid );
|
t.SetTid( tid );
|
||||||
ti.SetTid( tid );
|
ti.SetTid( tid );
|
||||||
|
|
||||||
if( !t.FakeSign() )
|
if( !t.FakeSign() )
|
||||||
{
|
{
|
||||||
Err( "Error signing TMD" );
|
Err( "Error signing TMD" );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if( !ti.FakeSign() )
|
if( !ti.FakeSign() )
|
||||||
{
|
{
|
||||||
Err( "Error signing ticket" );
|
Err( "Error signing ticket" );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
tmdData = t.Data();
|
tmdData = t.Data();
|
||||||
tikData = ti.Data();
|
tikData = ti.Data();
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Wad::SetIOS( quint32 ios )
|
||||||
|
{
|
||||||
|
if( !tmdData.size() || !tikData.size() )
|
||||||
|
{
|
||||||
|
Err( "Mising parts of the wad" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Tmd t( tmdData );
|
||||||
|
|
||||||
|
t.SetIOS( ios );
|
||||||
|
|
||||||
|
if( !t.FakeSign() )
|
||||||
|
{
|
||||||
|
Err( "Error signing TMD" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
tmdData = t.Data();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Wad::SetAhb( bool remove )
|
||||||
|
{
|
||||||
|
if( !tmdData.size() || !tikData.size() )
|
||||||
|
{
|
||||||
|
Err( "Mising parts of the wad" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Tmd t( tmdData );
|
||||||
|
|
||||||
|
t.SetAhb( remove );
|
||||||
|
|
||||||
|
if( !t.FakeSign() )
|
||||||
|
{
|
||||||
|
Err( "Error signing TMD" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
tmdData = t.Data();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Wad::SetDiskAccess( bool allow )
|
||||||
|
{
|
||||||
|
if( !tmdData.size() || !tikData.size() )
|
||||||
|
{
|
||||||
|
Err( "Mising parts of the wad" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Tmd t( tmdData );
|
||||||
|
|
||||||
|
t.SetDiskAccess( allow );
|
||||||
|
|
||||||
|
if( !t.FakeSign() )
|
||||||
|
{
|
||||||
|
Err( "Error signing TMD" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
tmdData = t.Data();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Wad::ReplaceContent( quint16 idx, const QByteArray ba )
|
bool Wad::ReplaceContent( quint16 idx, const QByteArray ba )
|
||||||
{
|
{
|
||||||
if( idx >= partsEnc.size() || !tmdData.size() || !tikData.size() )
|
if( idx >= partsEnc.size() || !tmdData.size() || !tikData.size() )
|
||||||
{
|
{
|
||||||
Err( "Mising parts of the wad" );
|
Err( "Mising parts of the wad" );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
QByteArray hash = GetSha1( ba );
|
QByteArray hash = GetSha1( ba );
|
||||||
quint32 size = ba.size();
|
quint32 size = ba.size();
|
||||||
|
|
||||||
Tmd t( tmdData );
|
Tmd t( tmdData );
|
||||||
t.SetHash( idx, hash );
|
if( !t.SetHash( idx, hash ) )
|
||||||
t.SetSize( idx, size );
|
{
|
||||||
if( !t.FakeSign() )
|
Err( "Error setting content hash" );
|
||||||
{
|
return false;
|
||||||
Err( "Error signing the tmd" );
|
}else{
|
||||||
return false;
|
qDebug() << "Hash :" << hash.toHex();
|
||||||
}
|
}
|
||||||
tmdData = t.Data();
|
if( !t.SetSize( idx, size ) )
|
||||||
|
{
|
||||||
|
Err( "Error setting content size" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if( !t.FakeSign() )
|
||||||
|
{
|
||||||
|
Err( "Error signing the tmd" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
tmdData = t.Data();
|
||||||
|
|
||||||
Ticket ti( tikData );
|
Ticket ti( tikData );
|
||||||
AesSetKey( ti.DecryptedKey() );
|
AesSetKey( ti.DecryptedKey() );
|
||||||
QByteArray decDataPadded = PaddedByteArray( ba, 0x40 );
|
QByteArray decDataPadded = PaddedByteArray( ba, 0x40 );
|
||||||
|
|
||||||
QByteArray encData = AesEncrypt( idx, decDataPadded );
|
QByteArray encData = AesEncrypt( idx, decDataPadded );
|
||||||
partsEnc.replace( idx, encData );
|
partsEnc.replace( idx, encData );
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,15 @@ public:
|
|||||||
//set the tid in the ticket&tmd and fakesign the wad
|
//set the tid in the ticket&tmd and fakesign the wad
|
||||||
bool SetTid( quint64 tid );
|
bool SetTid( quint64 tid );
|
||||||
|
|
||||||
|
//set the ios in the ticket&tmd and fakesign the wad
|
||||||
|
bool SetIOS( quint32 ios );
|
||||||
|
|
||||||
|
//set the tmd to allow AHBPROT removal
|
||||||
|
bool SetAhb( bool remove = true );
|
||||||
|
|
||||||
|
//set the tmd to allow direct disc access
|
||||||
|
bool SetDiskAccess( bool allow = true );
|
||||||
|
|
||||||
//replace a content of this wad, update the size & hash in the tmd and sign it
|
//replace a content of this wad, update the size & hash in the tmd and sign it
|
||||||
//ba should be decrypted
|
//ba should be decrypted
|
||||||
bool ReplaceContent( quint16 idx, const QByteArray ba );
|
bool ReplaceContent( quint16 idx, const QByteArray ba );
|
||||||
|
Loading…
Reference in New Issue
Block a user