diff --git a/SpotifyKeyDumper/Hooks.cpp b/SpotifyKeyDumper/Hooks.cpp index 3531a62..9322825 100644 --- a/SpotifyKeyDumper/Hooks.cpp +++ b/SpotifyKeyDumper/Hooks.cpp @@ -40,6 +40,12 @@ isValidUrl_v25 isValidUrl_v25_hook = nullptr; typedef int (__cdecl* getBitrate_v25)(int quality); getBitrate_v25 getBitrate_v25_hook = nullptr; +typedef int (__cdecl* impDecFunc_v47)(DWORD* keyLE, int intOne, DWORD* iv, int* a4); +impDecFunc_v47 impDecFunc_v47_hook = nullptr; + +typedef int (__thiscall* sttngsLkp_v48)(void* This, char* strLookup, size_t len); +sttngsLkp_v48 sttngsLkp_v48_hook = nullptr; + std::string authToken = std::string(); std::string keyStr = std::string(); std::string uriStr = std::string(); @@ -377,6 +383,9 @@ int __cdecl getBitrate_hook_v25(int quality) case 4: qualityStr = "Very High"; break; + case 5: + qualityStr = "700k"; + break; } if (!hasAlertedQuality && quality != 2 && quality != 3) @@ -415,15 +424,85 @@ char* GetKeyFuncAddrV27() return (char*)0x01068F20; } -/* - This will return addrOne if addrOneByte matches the BYTE at addrOne, or return addrTwo if not -*/ -char* GetKeyFunc(char* addrOne, char* addrTwo, BYTE addrOneByte) -{ - if (*(BYTE*)addrOne == addrOneByte) - return addrOne; - return addrTwo; +char* srUrlChars; +void __cdecl printSRUrl() +{ + std::string testStr = std::string(srUrlChars, 95); // 104 + std::cout << "Storage Resolve URL: " << testStr << std::endl; +} + +__declspec(naked) void testSMHook() +{ + __asm + { + pushfd // Push flags and registers to stack to preserve state before + pushad + + mov ebx, [eax] + + mov eax, offset srUrlChars // Get address from char pointer testStr + mov edi, [eax] + + mov cx, 24 // Iterate 24 times (copy 4 bytes each time = 96 bytes total) + COPY_LOOP: + mov eax, [ebx] + mov [edi], eax + add ebx, 4 + add edi, 4 + dec cx + jnz COPY_LOOP + + call printSRUrl // Set keyStr to chars from keyBuffer_v47 (and print key) + + popad // Pop flags and registers back from the stack to preserve state before + popfd + + jmp keyDecoder_v47_hook // Jump to trampoline function to run overwritten instructions and continue + } +} + +constexpr BYTE IV[] = {0x72, 0xE0, 0x67, 0xFB, 0xDD, 0xCB, 0xCF, 0x77, 0xEB, 0xE8, 0xBC, 0x64, 0x3F, 0x63, 0x0D, 0x93}; +int __cdecl impDecFunc_hook_v47(DWORD* keyLE, int intOne, DWORD* iv, int* a4) +{ + if (/*intOne == 0xA &&*/ !Utils::BadPtr(iv) && memcmp(IV, iv, 16) == 0) + { + // Swap endianness of key by 4 bytes each (+1 int; 16 total) + unsigned long keyBE[4] = {}; + keyBE[0] = _byteswap_ulong(*keyLE); + keyBE[1] = _byteswap_ulong(*(keyLE + 1)); + keyBE[2] = _byteswap_ulong(*(keyLE + 2)); + keyBE[3] = _byteswap_ulong(*(keyLE + 3)); + + printf("===============================================================================\n"); + std::cout << "Key: " << Utils::HexString((BYTE*) keyBE, 16) << std::endl; + std::cout << "IV: " << Utils::HexString((BYTE*) iv, 16) << std::endl; + printf("===============================================================================\n"); + } + + return impDecFunc_v47_hook(keyLE, intOne, iv, a4); +} + +int __fastcall sttngsLkp_hook_v48(void* This, void* _EDX, char* strLookup, size_t len) +{ + char* tmpStr; + + if (!Utils::BadPtr(strLookup)) + { + tmpStr = strLookup; + + // Some strLookup's are double pointers + if (!Utils::BadPtr((char*)*(DWORD*)strLookup)) + tmpStr = (char*)*(DWORD*)strLookup; + + if (memcmp(tmpStr, "app-developer", 13) == 0) + return 2; + else if (memcmp(tmpStr, "employee", 8) == 0 || memcmp(tmpStr, "ta-environment", 14) == 0 + || memcmp(tmpStr, "desktop-beta-tester", 19) == 0 || memcmp(tmpStr, "PLACEHOLDER", 11) == 0) + return 1; + } + + return sttngsLkp_v48_hook(This, strLookup, len); } void Hooks::Init() @@ -461,13 +540,13 @@ void Hooks::Init() case 45: keyToLE_v28_hook = (keyToLE_v28)Utils::TrampHook32((char*)0x010CF780, (char*)keyToLE_hook_v28, 6); authToken_v45_hook = (authToken_v45)Utils::TrampHook32((char*)0x00BF75F0, (char*)authToken_hook_v45, 7); - //openTrack_v45_hook = (openTrack_v45)Utils::TrampHook32((char*)0x00CA5740, (char*)openTrack_hook_v45, 5); log_v45_hook = (log_v45)Utils::TrampHook32((char*)0x010F2370, (char*)log_hook_v45, 5); fileIdWriter_v45_hook = (fileIdWriter_v45)Utils::TrampHook32((char*)0x00CBB560, (char*)fileIdWriter_hook_v45, 5); signalEmitter_v45_hook = (signalEmitter_v45)Utils::TrampHook32((char*)0x00B095A0, (char*)signalEmitter_hook_v45, - 5); // 00E48410 + 5); getBitrate_v25_hook = (getBitrate_v25)Utils::TrampHook32((char*)0x00E48410, (char*)getBitrate_hook_v25, 6); + startUpdate_v47_hook = (startUpdate_v47)Utils::TrampHook32((char*)0x00A0B250, (char*)startUpdate_hook_v47, 5); break; case 46: keyToLE_v28_hook = (keyToLE_v28)Utils::TrampHook32((char*)0x010C2FB0, (char*)keyToLE_hook_v28, 6); @@ -478,6 +557,7 @@ void Hooks::Init() signalEmitter_v45_hook = (signalEmitter_v45)Utils::TrampHook32((char*)0x00B02270, (char*)signalEmitter_hook_v45, 5); getBitrate_v25_hook = (getBitrate_v25)Utils::TrampHook32((char*)0x00E3C780, (char*)getBitrate_hook_v25, 6); + startUpdate_v47_hook = (startUpdate_v47)Utils::TrampHook32((char*)0x00A03020, (char*)startUpdate_hook_v47, 5); break; case 47: keyBuffer_v47 = new char[16]; // 128 bits = 16 bytes @@ -490,9 +570,34 @@ void Hooks::Init() signalEmitter_v45_hook = (signalEmitter_v45)Utils::TrampHook32((char*)0x00AFBB50, (char*)signalEmitter_hook_v45, 5); getBitrate_v25_hook = (getBitrate_v25)Utils::TrampHook32((char*)0x00E3E460, (char*)getBitrate_hook_v25, 6); - //startUpdate_v47_hook = (startUpdate_v47)Utils::TrampHook32((char*)0x009FB530, (char*)startUpdate_hook_v47, 5); + startUpdate_v47_hook = (startUpdate_v47)Utils::TrampHook32((char*)0x009FB530, (char*)startUpdate_hook_v47, 5); //uriHandler_v47_hook = (uriHandler_v47)Utils::TrampHook32((char*)0x010A39E0, (char*)uriHandler_hook_v47, 5); //isValidUrl_v25_hook = (isValidUrl_v25)Utils::TrampHook32((char*)0x0105CD80, (char*)isValidUrl_hook_v25, 6); + //impDecFunc_v47_hook = (impDecFunc_v47)Utils::TrampHook32((char*)0x010C52F0, (char*)impDecFunc_hook_v47, 6); + break; + case 48: + printf("Downloading are not currently supported on 1.1.48. Please use 1.1.47.\n"); + //keyBuffer_v47 = new char[16]; // 128 bits = 16 bytes + keyToLE_v28_hook = (keyToLE_v28)Utils::TrampHook32((char*)0x010E0200, (char*)keyToLE_hook_v47, 6); // Not called + //keyDecoder_v47_hook = (keyDecoder_v47)Utils::TrampHook32((char*)0x0153148D, (char*)keyDecoder_hook_v47, 7); + authToken_v45_hook = (authToken_v45)Utils::TrampHook32((char*)0x00BF7B50, (char*)authToken_hook_v45, 7); + log_v45_hook = (log_v45)Utils::TrampHook32((char*)0x011030E0, (char*)log_hook_v45, 5); + fileIdWriter_v45_hook = (fileIdWriter_v45)Utils::TrampHook32((char*)0x00CBCA60, (char*)fileIdWriter_hook_v45, + 5); + signalEmitter_v45_hook = (signalEmitter_v45)Utils::TrampHook32((char*)0x00B02B70, (char*)signalEmitter_hook_v45, + 5); + getBitrate_v25_hook = (getBitrate_v25)Utils::TrampHook32((char*)0x00E54D00, (char*)getBitrate_hook_v25, 6); + startUpdate_v47_hook = (startUpdate_v47)Utils::TrampHook32((char*)0x009FD9E0, (char*)startUpdate_hook_v47, 5); + //isValidUrl_v25_hook = (isValidUrl_v25)Utils::TrampHook32((char*)0x01077370, (char*)isValidUrl_hook_v25, 6); + + // Dump song manifest URL + //testChars = new char[104]; + //keyDecoder_v47_hook = (keyDecoder_v47)Utils::TrampHook32((char*)0x00EE081A, (char*)testSMHook, 5); + + // Not called :( + //impDecFunc_v47_hook = (impDecFunc_v47)Utils::TrampHook32((char*)0x010DF9F0, (char*)impDecFunc_hook_v47, 6); + + sttngsLkp_v48_hook = (sttngsLkp_v48)Utils::TrampHook32((char*)0x0056A7E0, (char*)sttngsLkp_hook_v48, 5); break; } } \ No newline at end of file