Add full support for 1.1.47 with new key extraction mechanism; stop duplicate downloads
This commit is contained in:
parent
8545596781
commit
5fa5698d9e
@ -19,6 +19,7 @@ Crossed out items support key dumping but not automatic downloading
|
|||||||
* ~~1.1.44~~
|
* ~~1.1.44~~
|
||||||
* 1.1.45
|
* 1.1.45
|
||||||
* 1.1.46
|
* 1.1.46
|
||||||
|
* 1.1.47
|
||||||
|
|
||||||
## Using
|
## Using
|
||||||
1. Make sure `SpotifyKeyDumperInjector.exe` and `SpotifyKeyDumper.dll` are located in the same folder as Spotify (`Spotify.exe`).
|
1. Make sure `SpotifyKeyDumperInjector.exe` and `SpotifyKeyDumper.dll` are located in the same folder as Spotify (`Spotify.exe`).
|
||||||
|
@ -24,12 +24,17 @@ fileIdWriter_v45 fileIdWriter_v45_hook = nullptr;
|
|||||||
typedef void (__thiscall* signalEmitter_v45)(void* This, int a1, int a2);
|
typedef void (__thiscall* signalEmitter_v45)(void* This, int a1, int a2);
|
||||||
signalEmitter_v45 signalEmitter_v45_hook = nullptr;
|
signalEmitter_v45 signalEmitter_v45_hook = nullptr;
|
||||||
|
|
||||||
|
typedef void (__cdecl* keyDecoder_v47)();
|
||||||
|
keyDecoder_v47 keyDecoder_v47_hook = nullptr;
|
||||||
|
|
||||||
std::string authToken = std::string();
|
std::string authToken = std::string();
|
||||||
std::string keyStr = std::string();
|
std::string keyStr = std::string();
|
||||||
|
|
||||||
std::string trackUriStr = std::string();
|
std::string trackUriStr = std::string();
|
||||||
__int64 newPosition = 0;
|
__int64 newPosition = 0;
|
||||||
bool signalled = false;
|
bool signalled = false;
|
||||||
|
int destKeyPtr = 0;
|
||||||
|
char* keyBuffer_v47;
|
||||||
|
|
||||||
int __cdecl keyToLE_hook_v25(unsigned int* dest, int* key, int bits)
|
int __cdecl keyToLE_hook_v25(unsigned int* dest, int* key, int bits)
|
||||||
{
|
{
|
||||||
@ -160,6 +165,7 @@ int* __fastcall log_hook_v45(void* This, void* _EDX, int a2, int a3, void* a4, c
|
|||||||
return log_v45_hook(This, a2, a3, a4, classStr, a6, logThing);
|
return log_v45_hook(This, a2, a3, a4, classStr, a6, logThing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string lastKey = std::string();
|
||||||
void __fastcall fileIdWriter_hook_v45(void* This, void* _EDX, int* a2)
|
void __fastcall fileIdWriter_hook_v45(void* This, void* _EDX, int* a2)
|
||||||
{
|
{
|
||||||
// [[ebp+8]+28]
|
// [[ebp+8]+28]
|
||||||
@ -167,10 +173,11 @@ void __fastcall fileIdWriter_hook_v45(void* This, void* _EDX, int* a2)
|
|||||||
|
|
||||||
//std::cout << "fileId: " << fileId << std::endl << std::endl;
|
//std::cout << "fileId: " << fileId << std::endl << std::endl;
|
||||||
|
|
||||||
if (signalled)
|
if (signalled && lastKey.compare(keyStr) != 0)
|
||||||
{
|
{
|
||||||
//std::cout << "signalled = false" << std::endl;
|
//std::cout << "signalled = false" << std::endl;
|
||||||
signalled = false;
|
signalled = false;
|
||||||
|
lastKey = keyStr;
|
||||||
std::thread t2(Utils::DownloadSong, std::string(fileId), trackUriStr, keyStr, authToken);
|
std::thread t2(Utils::DownloadSong, std::string(fileId), trackUriStr, keyStr, authToken);
|
||||||
t2.detach();
|
t2.detach();
|
||||||
}
|
}
|
||||||
@ -182,8 +189,6 @@ int signalEmitterInitCount = 0;
|
|||||||
const int signalEmitterRequired = 3;
|
const int signalEmitterRequired = 3;
|
||||||
void __fastcall signalEmitter_hook_v45(void* This, void* _EDX, int a1, int a2)
|
void __fastcall signalEmitter_hook_v45(void* This, void* _EDX, int a1, int a2)
|
||||||
{
|
{
|
||||||
//std::cout << "signalEmitter!!!" << std::endl << std::endl;
|
|
||||||
|
|
||||||
// Required in order to guarentee accurate data needed
|
// Required in order to guarentee accurate data needed
|
||||||
if (signalEmitterInitCount < signalEmitterRequired)
|
if (signalEmitterInitCount < signalEmitterRequired)
|
||||||
signalEmitterInitCount++;
|
signalEmitterInitCount++;
|
||||||
@ -196,6 +201,65 @@ void __fastcall signalEmitter_hook_v45(void* This, void* _EDX, int a1, int a2)
|
|||||||
return signalEmitter_v45_hook(This, a1, a2);
|
return signalEmitter_v45_hook(This, a1, a2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int __cdecl keyToLE_hook_v47(unsigned int* dest, int* key, int bits, bool isEncoded)
|
||||||
|
{
|
||||||
|
// Same hook as previously, except key is not decoded in this function anymore
|
||||||
|
|
||||||
|
if (bits == 128 && isEncoded)
|
||||||
|
destKeyPtr = (int)dest;
|
||||||
|
|
||||||
|
return keyToLE_v28_hook(dest, key, bits, isEncoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __cdecl KeyBufferToKeyStr()
|
||||||
|
{
|
||||||
|
std::string newKey = std::string(keyBuffer_v47, 16);
|
||||||
|
|
||||||
|
if (keyStr.compare(newKey) != 0)
|
||||||
|
{
|
||||||
|
keyStr = newKey;
|
||||||
|
std::cout << "Key: " << Utils::HexString(reinterpret_cast<BYTE*>(&newKey[0]), 16) << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(naked) void keyDecoder_hook_v47()
|
||||||
|
{
|
||||||
|
// The spot where this hooks and runs is very weird. Spotify now handles decoding of the key in an instruction set
|
||||||
|
// held in the .data section (where disassemblers won't typically explore). Looks intentional. :)
|
||||||
|
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
pushfd // Push flags and registers to stack to preserve state before
|
||||||
|
pushad
|
||||||
|
|
||||||
|
mov ebx, esi
|
||||||
|
sub ebx, 12 // Subtract 12 from esi to compare; esi will match on the last 4 bytes of key
|
||||||
|
cmp destKeyPtr, ebx // Check if destKeyPtr matches ebx (esi - 12)
|
||||||
|
jne END // If destKeyPtr and esi - 12 don't match, jump to end
|
||||||
|
|
||||||
|
mov eax, offset keyBuffer_v47 // Get address from char pointer keyBuffer_v47
|
||||||
|
mov edi, [eax]
|
||||||
|
|
||||||
|
mov cx, 4 // Iterate 4 times (copy 4 bytes each time)
|
||||||
|
BSWAP_LOOP: // Loop to swap endianness of each dword
|
||||||
|
mov eax, [ebx]
|
||||||
|
bswap eax
|
||||||
|
mov [edi], eax
|
||||||
|
add ebx, 4
|
||||||
|
add edi, 4
|
||||||
|
dec cx
|
||||||
|
jnz BSWAP_LOOP
|
||||||
|
|
||||||
|
call KeyBufferToKeyStr // Set keyStr to chars from keyBuffer_v47 (and print key)
|
||||||
|
|
||||||
|
END:
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
char* GetKeyFuncAddrV26()
|
char* GetKeyFuncAddrV26()
|
||||||
{
|
{
|
||||||
BYTE ref_v19 = 0x55;
|
BYTE ref_v19 = 0x55;
|
||||||
@ -282,5 +346,16 @@ void Hooks::Init()
|
|||||||
signalEmitter_v45_hook = (signalEmitter_v45)Utils::TrampHook32((char*)0x00B02270, (char*)signalEmitter_hook_v45,
|
signalEmitter_v45_hook = (signalEmitter_v45)Utils::TrampHook32((char*)0x00B02270, (char*)signalEmitter_hook_v45,
|
||||||
5);
|
5);
|
||||||
break;
|
break;
|
||||||
|
case 47:
|
||||||
|
keyBuffer_v47 = new char[16]; // 128 bits = 16 bytes
|
||||||
|
keyToLE_v28_hook = (keyToLE_v28)Utils::TrampHook32((char*)0x010C5B00, (char*)keyToLE_hook_v47, 6);
|
||||||
|
keyDecoder_v47_hook = (keyDecoder_v47)Utils::TrampHook32((char*)0x0153148D /*0x015337CD*/, (char*)keyDecoder_hook_v47, 7 /*6*/);
|
||||||
|
authToken_v45_hook = (authToken_v45)Utils::TrampHook32((char*)0x00BED0F0, (char*)authToken_hook_v45, 7);
|
||||||
|
log_v45_hook = (log_v45)Utils::TrampHook32((char*)0x010E8750, (char*)&log_hook_v45, 5);
|
||||||
|
fileIdWriter_v45_hook = (fileIdWriter_v45)Utils::TrampHook32((char*)0x00CB0630, (char*)&fileIdWriter_hook_v45,
|
||||||
|
5);
|
||||||
|
signalEmitter_v45_hook = (signalEmitter_v45)Utils::TrampHook32((char*)0x00AFBB50, (char*)signalEmitter_hook_v45,
|
||||||
|
5);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user