307 lines
9.0 KiB
C++
307 lines
9.0 KiB
C++
#include "pch.h"
|
|
#include "Utils.h"
|
|
#include "Hooks.h"
|
|
|
|
typedef int (__cdecl* keyToLE_v25)(unsigned int* dest, int* key, int bits);
|
|
keyToLE_v25 keyToLE_v25_hook = nullptr;
|
|
|
|
typedef int (__cdecl* keyToLE_v28)(unsigned int* dest, int* key, int bits, bool isEncoded);
|
|
keyToLE_v28 keyToLE_v28_hook = nullptr;
|
|
|
|
typedef int (__thiscall* authTokenOld_v45)(void* This, char* authToken, int a3, int a4, int a5, size_t tokenLength,
|
|
int a7, int a8, int a9, __int64 bearerStr, int a11, int a12, int a13, int a14, char a15, int a16, int int_40,
|
|
int a18, char a19, int a20);
|
|
authTokenOld_v45 authTokenOld_v45_hook = nullptr;
|
|
|
|
typedef int* (__thiscall* authToken_v45)(void* This, int* a2);
|
|
authToken_v45 authToken_v45_hook = nullptr;
|
|
|
|
typedef char* (__cdecl* hexToStr_v45)(char* dest, BYTE* src, size_t srcLength);
|
|
hexToStr_v45 hexToStr_v45_hook = nullptr;
|
|
|
|
typedef int* (__thiscall* openTrack_v45)(void* This, int a2, void* a3, int a4, __int64 position, char a6, void* a7);
|
|
openTrack_v45 openTrack_v45_hook = nullptr;
|
|
|
|
typedef int* (__thiscall* log_v45)(void* This, int a2, int a3, void* a4, const char* classStr, int a6,
|
|
DWORD* logThing);
|
|
log_v45 log_v45_hook;
|
|
|
|
typedef void (__thiscall* fileIdWriter_v45)(void* This, int* a2);
|
|
fileIdWriter_v45 fileIdWriter_v45_hook = nullptr;
|
|
|
|
//typedef int(__stdcall* signalEmitter_v45)(int a1, int a2);
|
|
typedef void (__thiscall* signalEmitter_v45)(void* This, int a1, int a2);
|
|
signalEmitter_v45 signalEmitter_v45_hook = nullptr;
|
|
|
|
std::string authToken;
|
|
std::string keyStr = std::string();
|
|
|
|
std::string trackUriStr = std::string();
|
|
__int64 newPosition = 0;
|
|
bool signalled = false;
|
|
|
|
int __cdecl keyToLE_hook_v25(unsigned int* dest, int* key, int bits)
|
|
{
|
|
if (bits == 128)
|
|
{
|
|
BYTE keyBuffer[16];
|
|
BYTE* keyBufPtr = keyBuffer;
|
|
memcpy(keyBufPtr, key, 16);
|
|
|
|
// Only print out key if it is different
|
|
std::string newKeyStr = Utils::HexString(keyBufPtr, 16);
|
|
if (newKeyStr.compare(keyStr) != 0)
|
|
std::cout << "Key: " << newKeyStr << std::endl << std::endl;
|
|
|
|
keyStr = newKeyStr;
|
|
}
|
|
|
|
return keyToLE_v25_hook(dest, key, bits);
|
|
}
|
|
|
|
int __cdecl keyToLE_hook_v28(unsigned int* dest, int* key, int bits, bool isEncoded)
|
|
{
|
|
if (bits == 128)
|
|
{
|
|
void* decodedKeyPtr = key;
|
|
|
|
if (isEncoded)
|
|
{
|
|
// key is encoded with some sort of algorithm; decode it here
|
|
|
|
unsigned int keyDecoded[4];
|
|
unsigned int uVar1;
|
|
unsigned int keyPtr;
|
|
unsigned int uVar3;
|
|
int index;
|
|
|
|
keyPtr = *key;
|
|
index = 15;
|
|
uVar3 = key[1];
|
|
keyDecoded[0] = key[2];
|
|
keyDecoded[1] = key[3];
|
|
keyDecoded[2] = key[4];
|
|
keyDecoded[3] = key[5];
|
|
|
|
do
|
|
{
|
|
uVar1 = keyDecoded[index - 1 & 3];
|
|
keyDecoded[index & 3] =
|
|
keyDecoded[index & 3] +
|
|
(((((uVar1 + index + keyPtr & uVar1 * 16 + uVar3) * 2 + uVar1 * -17) -
|
|
index) - uVar3) - keyPtr);
|
|
|
|
index--;
|
|
}
|
|
while (index >= 0);
|
|
|
|
decodedKeyPtr = &keyDecoded;
|
|
}
|
|
|
|
// Copy key bytes to new buffer
|
|
char keyBuffer[16];
|
|
char* keyBufPtr = keyBuffer;
|
|
memcpy(keyBufPtr, decodedKeyPtr, 16);
|
|
|
|
// Only print out key if it is different
|
|
std::string newKey = std::string(keyBufPtr, 16);
|
|
if (newKey.compare(keyStr) != 0)
|
|
{
|
|
std::cout << "Key: " << Utils::HexString(reinterpret_cast<BYTE*>(&newKey[0]), 16) << std::endl << std::endl;
|
|
keyStr = newKey;
|
|
}
|
|
}
|
|
|
|
return keyToLE_v28_hook(dest, key, bits, isEncoded);
|
|
}
|
|
|
|
int __fastcall authTokenHookOld_hook_v45(void* This, void* _EDX, char* authToken, int a3, int a4, int a5,
|
|
size_t tokenLength, int a7, int a8, int a9, __int64 bearerStr, int a11, int a12, int a13, int a14, char a15,
|
|
int a16, int int_40, int a18, char a19, int a20)
|
|
{
|
|
if (tokenLength == 311)
|
|
{
|
|
//std::cout << "Auth token: " << authToken << std::endl << std::endl;
|
|
|
|
::authToken = std::string(authToken);
|
|
}
|
|
|
|
return authTokenOld_v45_hook(This, authToken, a3, a4, a5, tokenLength, a7, a8, a9, bearerStr, a11, a12, a13, a14,
|
|
a15, a16, int_40, a18, a19, a20);
|
|
}
|
|
|
|
int* __fastcall authToken_hook_v45(void* This, void* _EDX, int* a2)
|
|
{
|
|
char* authToken = (char*) *(DWORD*)((a2) + 2); // 8/4 = 2
|
|
|
|
//std::cout << "authToken: " << authToken << std::endl << std::endl;
|
|
::authToken = std::string(authToken);
|
|
|
|
return authToken_v45_hook(This, a2);
|
|
}
|
|
|
|
char* __cdecl hexToStr_hook_v45(char* dest, BYTE* src, size_t srcLength)
|
|
{
|
|
//std::cout << "hexToStr hook" << std::endl;
|
|
|
|
if (srcLength == 20)
|
|
{
|
|
// Assume it is file id
|
|
//std::cout << "File id: " << Utils::HexString(src, srcLength) << std::endl << std::endl;
|
|
//std::cout << "file id!?" << std::endl;
|
|
}
|
|
|
|
return hexToStr_v45_hook(dest, src, srcLength);
|
|
}
|
|
|
|
int* __fastcall openTrack_hook_v45(void* This, void* _EDX, int a2, void* a3, int a4, __int64 position, char a6,
|
|
void* a7)
|
|
{
|
|
std::cout << "openTrack!!!" << std::endl << std::endl;
|
|
|
|
return openTrack_v45_hook(This, a2, a3, a4, newPosition, a6, a7);
|
|
}
|
|
|
|
int* __fastcall log_hook_v45(void* This, void* _EDX, int a2, int a3, void* a4, const char* classStr, int a6,
|
|
DWORD* logThing)
|
|
{
|
|
if (!Utils::BadPtr(logThing))
|
|
{
|
|
char* logChars = (char*)(*logThing);
|
|
|
|
if (!Utils::BadPtr(logChars))
|
|
{
|
|
//std::string logStr = std::string(logChars).substr(8, 5);
|
|
std::string logStr = std::string(logChars);
|
|
|
|
//std::cout << "logStr: " << logStr << std::endl;
|
|
|
|
if (logStr.compare(8, 9, "track_uri") == 0)
|
|
{
|
|
if (logStr.compare(19, 13, "spotify:track") == 0)
|
|
{
|
|
//std::cout << "Track URI: " << logStr.substr(19, std::string::npos) << std::endl;
|
|
trackUriStr = logStr.substr(19, std::string::npos);
|
|
newPosition = 0;
|
|
}
|
|
//else if (logStr.compare(19, 11, "spotify:ad") == 0) // Possibly this works?
|
|
else
|
|
{
|
|
std::cout << "Skipping ad: " << logStr.substr(19, std::string::npos) << std::endl;
|
|
newPosition = 29000; // 29 seconds: duration of ad
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return log_v45_hook(This, a2, a3, a4, classStr, a6, logThing);
|
|
}
|
|
|
|
void __fastcall fileIdWriter_hook_v45(void* This, void* _EDX, int* a2)
|
|
{
|
|
// [[ebp+8]+28]
|
|
char* fileId = (char*) *(DWORD*)(a2 + 16); // 0x40 / 4 = 16
|
|
|
|
//std::cout << "fileId: " << fileId << std::endl << std::endl;
|
|
|
|
if (signalled)
|
|
{
|
|
//std::cout << "signalled = false" << std::endl;
|
|
signalled = false;
|
|
std::thread t2(Utils::DownloadSong, std::string(fileId), trackUriStr, keyStr, authToken);
|
|
t2.detach();
|
|
}
|
|
|
|
return fileIdWriter_v45_hook(This, a2);
|
|
}
|
|
|
|
int signalEmitterInitCount = 0;
|
|
const int signalEmitterRequired = 5;
|
|
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
|
|
if (signalEmitterInitCount < signalEmitterRequired)
|
|
signalEmitterInitCount++;
|
|
else
|
|
{
|
|
//std::cout << "signalled = true" << std::endl;
|
|
signalled = true;
|
|
}
|
|
|
|
return signalEmitter_v45_hook(This, a1, a2);
|
|
}
|
|
|
|
char* GetKeyFuncAddrV26()
|
|
{
|
|
BYTE ref_v19 = 0x55;
|
|
BYTE* byteAtAddrStr = (BYTE*)0x010800C0;
|
|
|
|
// Byte at byteAtAddr in 1.1.26-19 is 0x55
|
|
if (*byteAtAddrStr == ref_v19)
|
|
return (char*)0x010800C0;
|
|
else
|
|
return (char*)0x0107FEC0;
|
|
}
|
|
|
|
char* GetKeyFuncAddrV27()
|
|
{
|
|
BYTE ref_v7 = 0x55;
|
|
BYTE* byteAtAddrStr = (BYTE*)0x01068F90;
|
|
|
|
// Byte at byteAtAddr in 1.1.27-7 is 0x55
|
|
if (*byteAtAddrStr == ref_v7)
|
|
return (char*)0x01068F90;
|
|
else
|
|
return (char*)0x01068F20;
|
|
}
|
|
|
|
void Hooks::Init()
|
|
{
|
|
int spotifyVer = Utils::GetSpotifyVersion();
|
|
|
|
// Method is stripped from Release build if this isn't here :/
|
|
std::cout << "Spotify version: 1.1." << Utils::GetSpotifyVersion() << std::endl << std::endl;
|
|
|
|
switch (spotifyVer)
|
|
{
|
|
case 25:
|
|
keyToLE_v25_hook = (keyToLE_v25)Utils::TrampHook32((char*)0x0106B920, (char*)keyToLE_hook_v25, 6);
|
|
break;
|
|
case 26:
|
|
// Two 1.1.26 versions
|
|
keyToLE_v25_hook = (keyToLE_v25)Utils::TrampHook32(GetKeyFuncAddrV26(), (char*)keyToLE_hook_v25, 6);
|
|
break;
|
|
case 27:
|
|
// Two 1.1.27 versions
|
|
keyToLE_v25_hook = (keyToLE_v25)Utils::TrampHook32(GetKeyFuncAddrV27(), (char*)keyToLE_hook_v25, 6);
|
|
break;
|
|
case 28:
|
|
keyToLE_v28_hook = (keyToLE_v28)Utils::TrampHook32((char*)0x01074650, (char*)keyToLE_hook_v28, 6);
|
|
break;
|
|
case 29:
|
|
keyToLE_v28_hook = (keyToLE_v28)Utils::TrampHook32((char*)0x010861B0, (char*)keyToLE_hook_v28, 6);
|
|
break;
|
|
case 30:
|
|
keyToLE_v28_hook = (keyToLE_v28)Utils::TrampHook32((char*)0x0108E840, (char*)keyToLE_hook_v28, 6);
|
|
break;
|
|
case 44:
|
|
keyToLE_v28_hook = (keyToLE_v28)Utils::TrampHook32((char*)0x010CABC0, (char*)keyToLE_hook_v28, 6);
|
|
break;
|
|
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*)0x10F2370, (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);
|
|
//hexToStr_v45_hook = (hexToStr_v45)Utils::TrampHook32((char*)0x010F81A0, (char*)hexToStr_hook_v45, 7);
|
|
break;
|
|
case 46:
|
|
keyToLE_v28_hook = (keyToLE_v28)Utils::TrampHook32((char*)0x010C2FB0, (char*)keyToLE_hook_v28, 6);
|
|
break;
|
|
}
|
|
} |