diff --git a/SpotifyKeyDumper/Utils.cpp b/SpotifyKeyDumper/Utils.cpp index d33f58c..f78927c 100644 --- a/SpotifyKeyDumper/Utils.cpp +++ b/SpotifyKeyDumper/Utils.cpp @@ -87,31 +87,26 @@ std::string Utils::HexString(BYTE* data, int len) return ss.str(); } -static const std::string illegalChars = "\\/:?\"<>|"; -void Utils::RemoveForbiddenChar(std::string* str) +bool IsEqualIgnoreCase(const std::string& stringOne, const std::string& stringTwo) { - std::string::iterator it; - - for (it = str->begin(); it < str->end(); ++it) - { - bool found = illegalChars.find(*it) != std::string::npos; - - if (found) - *it = '_'; - } + return std::equal(stringOne.begin(), stringOne.end(), stringTwo.begin(), [](char a, char b) + { + return tolower(a) == tolower(b); + }); } -void Utils::RemoveForbiddenCharW(std::wstring* str) +std::wstring Utils::FixPathStr(std::wstring str) { - std::wstring::iterator it; + // No forbidden characters + std::wstring badCharsRegex = std::wstring(L"[<>:\"/\\|?*\x00-\x1F]", 14); // Use this constructor for \x00 + str = std::regex_replace(str, std::wregex(badCharsRegex), L"_"); - for (it = str->begin(); it < str->end(); ++it) - { - bool found = illegalChars.find(*it) != std::string::npos; + // No forbidden words or ending periods or spaces + if (std::regex_match(str, std::wregex(L"^(CON|PRN|AUX|NUL|COM[0-9]|LPT[0-9])$| +$|\\.+$", + std::regex_constants::ECMAScript | std::regex_constants::icase))) + str.append(L"_"); - if (found) - *it = '_'; - } + return str; } std::wstring Utils::Utf8ToUtf16(const std::string& str) @@ -146,16 +141,11 @@ void Utils::DownloadSong(std::string fileId, std::string trackUri, std::string k return; } - /*std::cout << "fileId = " << fileId << std::endl; - std::cout << "trackUri = " << trackUri << std::endl; - std::cout << "key = " << key << std::endl; - std::cout << "authToken = " << authToken << std::endl;*/ - // Get storage resolve from Spotify std::string srStr = DownloadSpotifyUrl("spclient.wg.spotify.com", "/storage-resolve/files/audio/interactive_prefetch/" + fileId + "?product=0", authToken); - if (srStr.substr(0, 5).compare("Error") == 0) + if (srStr.length() > 5 && srStr.substr(0, 5).compare("Error") == 0) { std::cout << srStr << std::endl; return; @@ -164,20 +154,19 @@ void Utils::DownloadSong(std::string fileId, std::string trackUri, std::string k // Parse storage resolve response to get the encrypted song data's URL std::string songHost = (srStr.substr(srStr.find("https://") + 8)) .erase(srStr.substr(srStr.find("https://") + 8).find("/audio/")); - std::string songPath = srStr.substr(srStr.find("/audio/")).erase(srStr.substr(srStr.find("/audio/")).find("=") + 85); + std::string songPath = srStr.substr(srStr.find("/audio/")).erase(srStr.substr(srStr.find("/audio/")).find("=") + + 85); // Download encrypted song data from Spotify std::string songStr = DownloadSpotifyUrl(songHost, songPath, ""); - //std::cout << "URL: " << songHost + songPath << std::endl; - if (songStr.substr(0, 6).compare("") == 0) { std::cout << "Error: " + songStr << std::endl; return; } - // Decrypt encrypted song data + // Decrypt encrypted song data with Tiny AES in C struct AES_ctx ctx; AES_init_ctx_iv(&ctx, reinterpret_cast(&key[0]), IV); AES_CTR_xcrypt_buffer(&ctx, reinterpret_cast(&songStr[0]), songStr.size()); @@ -195,18 +184,7 @@ void Utils::DownloadSong(std::string fileId, std::string trackUri, std::string k songInfo.album = strtok((char*)(metadata.substr(metadata.find(albumSearchPattern) + 404)).c_str(), "\""); songInfo.cover = strtok((char*)(metadata.substr(metadata.find("height") + 30)).c_str(), "\""); - /*std::cout << "<> <> <> <> <> <> <> <> <> <> <> <> <> <>" << std::endl; - std::cout << metadata << std::endl; - std::cout << "<> <> <> <> <> <> <> <> <> <> <> <> <> <>" << std::endl; - std::cout << "=========================================" << std::endl; - std::cout << "title: " << songInfo.title << std::endl; - std::cout << "artist: " << songInfo.artist << std::endl; - std::cout << "album: " << songInfo.album << std::endl; - std::cout << "title: " << songInfo.cover << std::endl; - std::cout << "=========================================" << std::endl;*/ - - std::wstring tempDirArtist = Utf8ToUtf16(songInfo.artist); - RemoveForbiddenCharW(&tempDirArtist); + std::wstring tempDirArtist = FixPathStr(Utf8ToUtf16(songInfo.artist)); songDir = songDirRoot; if (!CreateDirectoryW(songDir.c_str(), NULL) && ERROR_ALREADY_EXISTS != GetLastError()) @@ -215,16 +193,14 @@ void Utils::DownloadSong(std::string fileId, std::string trackUri, std::string k if (CreateDirectoryW(std::wstring(songDir + L"\\" + tempDirArtist).c_str(), NULL) || ERROR_ALREADY_EXISTS == GetLastError()) { - std::wstring tempDirAlbum = Utf8ToUtf16(songInfo.album); - RemoveForbiddenCharW(&tempDirAlbum); + std::wstring tempDirAlbum = FixPathStr(Utf8ToUtf16(songInfo.album)); if (CreateDirectoryW(std::wstring(songDir + L"\\" + tempDirArtist + std::wstring(L"\\") + tempDirAlbum).c_str(), NULL) || ERROR_ALREADY_EXISTS == GetLastError()) { songDir += L"\\" + tempDirArtist + std::wstring(L"\\") + tempDirAlbum; - std::wstring tempDirSong = Utf8ToUtf16(songInfo.title); - RemoveForbiddenCharW(&tempDirSong); + std::wstring tempDirSong = FixPathStr(Utf8ToUtf16(songInfo.title)); std::ofstream songFileOut(songDir + L".\\" + tempDirArtist + L" - " + tempDirSong + L".ogg", std::ios_base::binary); @@ -311,53 +287,6 @@ std::string Utils::DownloadSpotifyUrl(std::string host, std::string path, std::s return response; } -std::string Utils::DownloadUrlOld(std::string host, std::string path, std::string authToken) -{ - std::string authHeader; - std::string userAgent = "Spotify/11" + std::to_string(spotifyVer) + std::string("00") - + std::to_string(spotifyVerEnd) + std::string(" Win32/Windows 10 (10.0.19042; x64)"); - HINTERNET hInternet = InternetOpenA(userAgent.c_str(), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); - HINTERNET hConnect; - HINTERNET hRequest; - BOOL bRequestSent; - - authHeader = "Authorization: Bearer " + authToken; - - if (hInternet == NULL) - return "Error: Could not initialize request!"; - - hConnect = InternetConnectA(hInternet, host.c_str(), 80, NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL); - - if (hConnect == NULL) - return "Error: Could not create connect!"; - - hRequest = HttpOpenRequestA(hConnect, "GET", path.c_str(), NULL, NULL, NULL, INTERNET_FLAG_NO_AUTH, 0); - - if (hRequest == NULL) - return "Error: Could not create open request!"; - - HttpAddRequestHeadersA(hRequest, authHeader.c_str(), -1, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE); - bRequestSent = HttpSendRequestA(hRequest, NULL, 0, NULL, 0); - - if (!bRequestSent) - return "Error: Could not send request!"; - - std::string response; - const int bufferSize = 1024; - char tmpBuffer[bufferSize]; - - BOOL canRead = true; - DWORD bytesRead = -1; - - while (canRead && bytesRead != 0) - { - canRead = InternetReadFile(hRequest, tmpBuffer, bufferSize, &bytesRead); - response.append(tmpBuffer, bytesRead); - } - - return response; -} - bool Utils::BadPtr(void* ptr) { MEMORY_BASIC_INFORMATION mbi = { 0 }; diff --git a/SpotifyKeyDumper/Utils.h b/SpotifyKeyDumper/Utils.h index 4f5ac1c..a09e41f 100644 --- a/SpotifyKeyDumper/Utils.h +++ b/SpotifyKeyDumper/Utils.h @@ -7,12 +7,10 @@ public: static char* TrampHook32(char* src, char* dst, const intptr_t len); static int GetSpotifyVersion(); static std::string HexString(BYTE* data, int len); - static void RemoveForbiddenChar(std::string* str); - static void RemoveForbiddenCharW(std::wstring* str); + static std::wstring FixPathStr(std::wstring str); static std::wstring Utf8ToUtf16(const std::string& str); static void DownloadSong(std::string fileId, std::string fileUri, std::string key, std::string authToken); static std::string DownloadSpotifyUrl(std::string host, std::string path, std::string authToken); - static std::string DownloadUrlOld(std::string host, std::string path, std::string authToken); static bool BadPtr(void* ptr); }; diff --git a/SpotifyKeyDumper/pch.h b/SpotifyKeyDumper/pch.h index 737c58a..af3b77a 100644 --- a/SpotifyKeyDumper/pch.h +++ b/SpotifyKeyDumper/pch.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include