bool z3ResEx::z3Decrypt ( TMemoryStream &src, TMemoryStream &dst, unsigned char *key, unsigned int keylen ) { StringSource keyStr( key, keylen, true ); AutoSeededRandomPool rng; ECIES<ECP>::Decryptor ellipticalEnc( keyStr ); unsigned char *tmpBuffer( new unsigned char[ src.Size() ] ); DecodingResult dr = ellipticalEnc.Decrypt( rng, src.Data(), src.Size(), tmpBuffer ); if( !( dr.isValidCoding ) || ( dr.messageLength == 0 ) ) { delete tmpBuffer; return false; } dst.Write( tmpBuffer, dr.messageLength ); delete tmpBuffer; return true; }
bool z3ResEx::fsRle( TMemoryStream &src, TMemoryStream &dst, bool isMSF ) { unsigned int msfSizeFlag; unsigned int expectedSize, len; unsigned char *pData( src.Data() ), *pDataEnd( pData + src.Size() ); if( isMSF ) { // Read the expected size from data msfSizeFlag = src.ReadUInt(); pData += 4; } if( !( z3Rle::decodeSize( pData, expectedSize, len ) ) ) { dst.Close(); //printf("ERROR: Problems decoding RLE buffer size\n"); return false; } if( isMSF && !( msfSizeFlag == expectedSize ) ) { dst.Close(); //printf("ERROR: Unexpected MSF buffer size\n"); return false; } // Skip the length of the expected size pData += len; unsigned char *tmpBuffer( new unsigned char[ expectedSize ] ); unsigned int tmpOffset( 0 ); while( tmpOffset < expectedSize ) { if( !( z3Rle::decodeInstruction( pData, len, pDataEnd, tmpBuffer, tmpOffset ) ) ) { delete tmpBuffer; //printf("ERROR: Problems decoding RLE buffer\n"); return false; } pData += len; } dst.Write( tmpBuffer, expectedSize ); delete tmpBuffer; return true; }
void z3ResEx::parseMsf( TMemoryStream &msf ) { switch( m_fileindexVer ) { case 0 : { unsigned char method( 0 ); FILEINDEX_ENTRY info; unsigned char *strMRFN( nullptr ); unsigned char *strName( nullptr ); unsigned int items( 0 ), errors( 0 ); while( ( msf.Position() < msf.Size() ) && ( errors < MAX_ERRORS ) ) { method = msf.ReadByte(); msf.Read( &info, sizeof( FILEINDEX_ENTRY ) ); unpackStringEx( msf, strMRFN, info.lenMRFN ); unpackStringEx( msf, strName, info.lenName ); if( m_listContents ) { printf( "%s (%u bytes)\n", strName, info.size ); } else { if( !( extractItem( info, method, (char *)strMRFN, (char *)strName ) ) ) ++errors; } ++items; delete strMRFN; delete strName; } printf( "Processed %u items (%u issues)\n\n", items, errors ); break; } case 1 : { parseMsfMethod2( msf ); break; } } }
bool z3ResEx::z3Decrypt ( TMemoryStream &src, TMemoryStream &dst, unsigned char *key, unsigned int keylen ) { StringSource keyStr( key, keylen, true ); AutoSeededRandomPool rng; ECIES<ECP>::Decryptor ellipticalEnc( keyStr ); vector<unsigned char> tmpBuffer(src.Size()); DecodingResult dr = ellipticalEnc.Decrypt( rng, src.Data(), src.Size(), &tmpBuffer[0] ); if( dr.isValidCoding && dr.messageLength > 0 ) { dst.Write(&tmpBuffer[0], dr.messageLength); return true; } return false; }
void z3ResEx::parseMsf( TMemoryStream &msf ) { switch( m_fileindexVer ) { case 0 : { unsigned char method( 0 ); FILEINDEX_ENTRY info; vector<unsigned char> strMRFN(MAX_STRING_SIZE); vector<unsigned char> strName(MAX_STRING_SIZE); unsigned int items( 0 ), errors( 0 ); while( ( msf.Position() < msf.Size() ) && ( errors < MAX_ERRORS ) ) { method = msf.ReadByte(); msf.Read( &info, sizeof( FILEINDEX_ENTRY ) ); unpackStringEx(msf, strMRFN, info.lenMRFN); unpackStringEx(msf, strName, info.lenName); if( m_listContents ) { printf( "%s (%u bytes)\n", &strName[0], info.size ); } else { if( !( extractItem( info, method, reinterpret_cast<const char*>(&strMRFN[0]), reinterpret_cast<const char*>(&strName[0]) ) ) ) ++errors; } ++items; } printf( "Processed %u items (%u issues)\n\n", items, errors ); break; } case 1 : { parseMsfMethod2( msf ); break; } } }
void z3ResEx::parseMsfMethod2( TMemoryStream &msf ) { unsigned short strLen( 0 ); unsigned short mrfIndexLen( 0 ); // Folders are now in a table at the top of the file msf.Read( &mrfIndexLen, sizeof( unsigned short ) ); if (mrfIndexLen == 0) { // There are no folders in the filesystem return; } // List of filenames vector<string> vecMsf(mrfIndexLen); vector<unsigned char> strBuffer(MAX_STRING_SIZE); // MRF filenames are now packed in a list for( unsigned short i( 0 ); i != mrfIndexLen; ++i ) { strLen = msf.ReadUShort(); unpackStringEx( msf, strBuffer, strLen ); // Required to rename files //vecMsf[i].first.assign( (char *)strBuffer ); // Cached file opening (and a pointer so we can call the constructor) //vecMsf[i].second = new TFileStream( strBuffer ); vecMsf[i] = string(strBuffer.begin(), strBuffer.end()); } // Files are now listed (similar to before) FILEINDEX_ENTRY2 fiItem; unsigned int items( 0 ), errors( 0 ); //msf.SaveToFile("debugFilesys.dat"); bool bMatchesCriteria = true; string tmpFilename; while( ( msf.Position() < msf.Size() ) && ( errors < MAX_ERRORS ) ) { msf.Read( &fiItem, sizeof( FILEINDEX_ENTRY2 ) ); strLen = msf.ReadUShort(); unpackStringEx( msf, strBuffer, strLen ); if( !m_folderCriteria.empty() ) { tmpFilename = string(strBuffer.begin(), strBuffer.end()); std::transform(tmpFilename.begin(), tmpFilename.end(), tmpFilename.begin(), ::toupper); bMatchesCriteria = !( tmpFilename.find( m_folderCriteria ) == string::npos ); } if( bMatchesCriteria ) { if( m_listContents ) { printf( "%s (%u bytes)\n", &strBuffer[0], fiItem.size ); } else { if( !( extractItem2( fiItem, vecMsf[ fiItem.mrfIndex ], reinterpret_cast<const char*>(&strBuffer[0]) ) ) ) ++errors; } } ++items; } vecMsf.clear(); printf( "Processed %u items (%u issues)\n\n", items, errors ); }
void z3ResEx::fsXor( TMemoryStream &src, unsigned int key ) const { z3Xor::rs3Unscramble( src.Data(), src.Size(), key ); }
void z3ResEx::Run( ) { // Check the fileindex exists if( TFileSize( msfName ) == 0 ) { setMessage( "ERROR: Unable to open file (%s)", msfName ); } else { TMemoryStream msf; m_fileindexKey = nullptr; m_fileindexKeyLength = 0; // // Brute-force the key (version 1) // unsigned int keyIndex( 0 ); // For all known keys while( ( keyIndex < keyList1Count ) && ( msf.Size() == 0 ) ) { // Try to read the fileindex if( fsReadMSF( msf, keyList1[ keyIndex ].Data, KeyLength1, 0 ) ) { m_fileindexKey = keyList1[ keyIndex ].Data; m_fileindexKeyLength = KeyLength1; m_fileindexVer = 0; if( m_verboseMessages ) printf("Found key for %s!\n", keyList1[ keyIndex ].Desc ); } ++keyIndex; } // If key has not been found if( m_fileindexKey == nullptr ) { // // Continue to brute-force the key (version 2) // keyIndex = 0; // For all known keys while( ( keyIndex < keyList2Count ) && ( msf.Size() == 0 ) ) { // Try to read the fileindex if( fsReadMSF( msf, keyList2[ keyIndex ].Data, KeyLength2, 1 ) ) { m_fileindexKey = keyList2[ keyIndex ].Data; m_fileindexKeyLength = KeyLength2; m_fileindexVer = 1; if( m_verboseMessages ) printf("Found key for %s!\n", keyList2[ keyIndex ].Desc ); } ++keyIndex; } } // If a valid key has been found and fileindex loaded if( !( ( m_fileindexKey == nullptr ) && ( msf.Size() == 0 ) ) ) { // Attempt to parse it (to extract or list files) msf.Seek( 0, bufo_start ); parseMsf( msf ); } else { // No key found or incompatiable file (not checked) setMessage( "ERROR: This file is using an updated key or unsupported method" ); } msf.Close(); } }
int main( int argc, char **argv ) { printf ( "z3ResEx" \ "\nResearched and coded by x1nixmzeng\n\n" ); // Check arguments if( argc > 1 ) { if( SetCurrentDirectory( argv[1] ) == 0 ) { printf("ERROR: Failed to set the client path (%s)\n", argv[1] ); return 0; } if( argc > 2 ) { // For all other arguments, check against known flags if( argv[2][0] == '-' ) { // -v Verbose // todo // -l List all files if( argv[2][1] == 'l' ) { user_opt_list_files = true; } else // -x No extraction if( argv[2][1] == 'x' ) { user_opt_allow_extraction = false; } // -f Extract only (filter) // todo } } } // Check the fileindex exists if( TFileSize( msfName ) == 0 ) { printf("ERROR: Unable to open file (%s)\n", msfName); } else { unsigned int keyIndex( 0 ); TMemoryStream msf; // Brute-force the key while( ( keyIndex < Z3_KEY_LIST_LENGTH ) && ( msf.Size() == 0 ) ) { if( fsReadMSF( msf, Z3_KEY_LIST[ keyIndex ] ) ) { z3CurrentKey = Z3_KEY_LIST[ keyIndex ]; // todo: verbose? - show key } ++keyIndex; } if( !( z3CurrentKey == nullptr ) ) { // Run main extraction loop if( !( user_opt_allow_extraction ) ) printf("NOTE: Opted NOT to save data\n"); extractionMain( msf ); } else { // No key found or incompatiable file (not checked) printf("ERROR: This file is using an updated key or unsupported method\n"); } msf.Close(); } return 0; }
void extractionMain( TMemoryStream &msf ) { const unsigned int MAX_ERRORS( 50 ); unsigned int items( 0 ); FILEINDEX_ENTRY info; unsigned char method; char *strMRFN( nullptr ), *strName( nullptr ); #define unpackString(buf,len) \ { \ buf = new char[ len +1 ]; \ msf.Read( buf, len ); \ buf[ len ] = 0; \ } #ifdef SAVE_MSF_FILEINDEX msf.SaveToFile("z3debug_fileindex.msf"); #endif // Are we just listing files? if( user_opt_list_files ) { std::string fname; printf("Listing filesystem contents\n\n"); while( msf.Position() < msf.Size() ) { method = msf.ReadByte(); msf.Read( &info, sizeof( FILEINDEX_ENTRY ) ); unpackString( strMRFN, info.lenMRFN ); unpackString( strName, info.lenName ); fname = fsRename( strMRFN, strName ); printf("%s\n", fname.c_str()); ++items; delete strMRFN; delete strName; } fname.clear(); printf("\nLocated %u files\n", items); } else // Run the main extraction loop { unsigned int errors( 0 ); printf("Extracting filesystem contents\n\n"); while( ( msf.Position() < msf.Size() ) && ( errors < MAX_ERRORS ) ) { method = msf.ReadByte(); msf.Read( &info, sizeof( FILEINDEX_ENTRY ) ); unpackString( strMRFN, info.lenMRFN ); unpackString( strName, info.lenName ); if( !( extractItem( info, method, strMRFN, strName ) ) ) ++errors; ++items; delete strMRFN; delete strName; } if( errors >= MAX_ERRORS ) printf("ERROR: Extraction stopped as there were too many errors\n"); else printf("\nExtracted %u files (%u problems)\n", items, errors); } }
void z3ResEx::parseMsfMethod2( TMemoryStream &msf ) { unsigned short strLen( 0 ); unsigned char *strBuffer( nullptr ); unsigned short mrfIndexLen( 0 ); // Folders are now in a table at the top of the file msf.Read( &mrfIndexLen, sizeof( unsigned short ) ); // List of filenames //typedef std::pair<string, TFileStream* > mrfItem; typedef vector<string > mrfItemList; mrfItemList vecMsf; vecMsf.resize( mrfIndexLen ); // MRF filenames are now packed in a list for( unsigned short i( 0 ); i != mrfIndexLen; ++i ) { strLen = msf.ReadUShort(); unpackStringEx( msf, strBuffer, strLen ); // Required to rename files //vecMsf[i].first.assign( (char *)strBuffer ); // Cached file opening (and a pointer so we can call the constructor) //vecMsf[i].second = new TFileStream( strBuffer ); vecMsf[i].assign( (char *)strBuffer ); delete strBuffer; } // Files are now listed (similar to before) FILEINDEX_ENTRY2 fiItem; unsigned int items( 0 ), errors( 0 ); //msf.SaveToFile("debugFilesys.dat"); while( ( msf.Position() < msf.Size() ) && ( errors < MAX_ERRORS ) ) { msf.Read( &fiItem, sizeof( FILEINDEX_ENTRY2 ) ); strLen = msf.ReadUShort(); unpackStringEx( msf, strBuffer, strLen ); if( m_listContents ) { printf( "%s (%u bytes)\n", strBuffer, fiItem.size ); } else { if( !( extractItem2( fiItem, vecMsf[ fiItem.mrfIndex ], (char *)strBuffer ) ) ) ++errors; } delete strBuffer; ++items; } vecMsf.clear(); printf( "Processed %u items (%u issues)\n\n", items, errors ); }