- crash fixes
- reformat everything with clang-format
This commit is contained in:
Arsenii es3n1n 2022-08-10 17:11:45 +02:00
parent d51804ee7b
commit be25b03a74
36 changed files with 789 additions and 925 deletions

31
.clang-format Normal file

@ -0,0 +1,31 @@
# es3n1n's clang-format, last upd 19 jun 22 17:50:03
BasedOnStyle: WebKit
Language: Cpp
AlignTrailingComments: false
AllowAllArgumentsOnNextLine: false
AllowShortEnumsOnASingleLine: false
AllowShortLambdasOnASingleLine: Inline
BreakBeforeBraces: Custom
BraceWrapping:
AfterEnum: false
AfterStruct: false
SplitEmptyFunction: false
AfterFunction: false
AfterNamespace: false
BreakBeforeTernaryOperators: false
BreakConstructorInitializers: BeforeColon
ColumnLimit: 155
FixNamespaceComments: true
IndentPPDirectives: BeforeHash
ReflowComments: false
NamespaceIndentation: All
BreakStringLiterals: true
CommentPragmas: true
IndentCaseLabels: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes

@ -2,65 +2,64 @@
#include <TlHelp32.h> #include <TlHelp32.h>
#include <stdio.h> #include <stdio.h>
#define INJECTOR_FAIL(s) \
#define INJECTOR_FAIL(s) { util::logger::error(s " Error code: %d\n", GetLastError( ) ); return false; } { \
util::logger::error(s " Error code: %d\n", GetLastError()); \
return false; \
}
namespace injector { namespace injector {
namespace detail { namespace detail {
void* get_process_by_name( const wchar_t* name ) { void* get_process_by_name(const wchar_t* name) {
void* thSnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ); void* thSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if ( thSnapShot == INVALID_HANDLE_VALUE ) if (thSnapShot == INVALID_HANDLE_VALUE)
return nullptr; return nullptr;
PROCESSENTRY32W pe; PROCESSENTRY32W pe;
pe.dwSize = sizeof( PROCESSENTRY32W ); pe.dwSize = sizeof(PROCESSENTRY32W);
unsigned long ret = 0; unsigned long ret = 0;
for ( bool proc = Process32FirstW( thSnapShot, &pe ); proc; proc = Process32NextW( thSnapShot, &pe ) ) { for (bool proc = Process32FirstW(thSnapShot, &pe); proc; proc = Process32NextW(thSnapShot, &pe)) {
if ( wcscmp( pe.szExeFile, name ) ) if (wcscmp(pe.szExeFile, name))
continue; continue;
ret = pe.th32ProcessID; ret = pe.th32ProcessID;
break; break;
} }
CloseHandle( thSnapShot ); CloseHandle(thSnapShot);
return ret ? OpenProcess( PROCESS_ALL_ACCESS, false, ret ) : nullptr; return ret ? OpenProcess(PROCESS_ALL_ACCESS, false, ret) : nullptr;
} }
} } // namespace detail
bool inject( const wchar_t* proc, const char* path ) { bool inject(const wchar_t* proc, const char* path) { return ::injector::inject(detail::get_process_by_name(proc), path); }
return ::injector::inject( detail::get_process_by_name( proc ), path );
}
bool inject( HANDLE proc, const char* path ) { bool inject(HANDLE proc, const char* path) {
if ( !proc ) if (!proc)
INJECTOR_FAIL( "Failed to open a process. Make sure injector is running as an admin." ); INJECTOR_FAIL("Failed to open a process. Make sure injector is running as an admin.");
char full_path[ 260 ]; char full_path[260];
if ( !GetFullPathNameA( path, sizeof( full_path ), full_path, nullptr ) ) if (!GetFullPathNameA(path, sizeof(full_path), full_path, nullptr))
INJECTOR_FAIL( "Failed to find a dll." ); INJECTOR_FAIL("Failed to find a dll.");
HMODULE kernel32 = GetModuleHandleW( L"kernel32.dll" ); HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
if ( !kernel32 ) if (!kernel32)
INJECTOR_FAIL( "Failed to get kernel32.dll handle." ); INJECTOR_FAIL("Failed to get kernel32.dll handle.");
void* lla = reinterpret_cast< void* >( GetProcAddress( kernel32, "LoadLibraryA" ) ); void* lla = reinterpret_cast<void*>(GetProcAddress(kernel32, "LoadLibraryA"));
if ( !lla ) if (!lla)
INJECTOR_FAIL( "Failed to find LoadLibraryA function." ); INJECTOR_FAIL("Failed to find LoadLibraryA function.");
void* str = VirtualAllocEx( proc, nullptr, strlen( full_path ), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE ); void* str = VirtualAllocEx(proc, nullptr, strlen(full_path), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if ( !str ) if (!str)
INJECTOR_FAIL( "Failed to allocate memory region for str." ); INJECTOR_FAIL("Failed to allocate memory region for str.");
WriteProcessMemory( proc, str, full_path, strlen( full_path ), nullptr ); WriteProcessMemory(proc, str, full_path, strlen(full_path), nullptr);
CreateRemoteThread( proc, nullptr, 0, reinterpret_cast< LPTHREAD_START_ROUTINE >( lla ), str, 0, nullptr ); CreateRemoteThread(proc, nullptr, 0, reinterpret_cast<LPTHREAD_START_ROUTINE>(lla), str, 0, nullptr);
CloseHandle( proc );
return true;
}
}
CloseHandle(proc);
return true;
}
} // namespace injector
#undef INJECTOR_FAIL #undef INJECTOR_FAIL

@ -1,13 +1,12 @@
#pragma once #pragma once
#include <Windows.h>
#include "shared/logo.h" #include "shared/logo.h"
#include <Windows.h>
namespace injector { namespace injector {
namespace detail { namespace detail {
void* get_process_by_name( const wchar_t* name ); void* get_process_by_name(const wchar_t* name);
} }
bool inject( const wchar_t* proc, const char* path ); bool inject(const wchar_t* proc, const char* path);
bool inject( HANDLE proc, const char* path ); bool inject(HANDLE proc, const char* path);
} } // namespace injector

@ -4,36 +4,35 @@
#include "injector/injector.h" #include "injector/injector.h"
int main() {
util::logo::create_console_and_draw_logo();
int main( ) { STARTUPINFOA si = { .cb = sizeof(si) };
util::logo::create_console_and_draw_logo( ); PROCESS_INFORMATION pi = {};
STARTUPINFOA si = { .cb = sizeof( si ) }; constexpr const char* exe_path = "oSpotify.exe";
PROCESS_INFORMATION pi = {};
constexpr const char* exe_path = "oSpotify.exe"; // Creating process
//
util::logger::debug("Spawning %s", exe_path);
if (!CreateProcessA(exe_path, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) {
util::logger::error("Unable to create process, error code: %d", GetLastError());
system("pause");
goto END;
}
// Creating process // Injecting Unspotify
// //
util::logger::debug( "Spawning %s", exe_path ); util::logger::debug("Injecting spotify-reverse.dll");
if ( !CreateProcessA( exe_path, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi ) ) { if (!injector::inject(pi.hProcess, "spotify-reverse.dll")) {
util::logger::error( "Unable to create process, error code: %d", GetLastError( ) ); util::logger::error("Unable to inject module, error code: %d", GetLastError());
system( "pause" ); TerminateProcess(pi.hProcess, 0x0);
goto END; system("pause");
} goto END;
}
// Injecting Unspotify ResumeThread(pi.hThread);
//
util::logger::debug( "Injecting spotify-reverse.dll" );
if ( !injector::inject( pi.hProcess, "spotify-reverse.dll" ) ) {
util::logger::error( "Unable to inject module, error code: %d", GetLastError( ) );
TerminateProcess( pi.hProcess, 0x0 );
system( "pause" );
goto END;
}
ResumeThread(pi.hThread);
END: END:
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

@ -9,14 +9,6 @@
<Configuration>Release</Configuration> <Configuration>Release</Configuration>
<Platform>Win32</Platform> <Platform>Win32</Platform>
</ProjectConfiguration> </ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Globals"> <PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion> <VCProjectVersion>16.0</VCProjectVersion>
@ -39,22 +31,7 @@
<PlatformToolset>ClangCL</PlatformToolset> <PlatformToolset>ClangCL</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization> <WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet> <CharacterSet>Unicode</CharacterSet>
<LLVMToolsVersion>12.0.0</LLVMToolsVersion> <LLVMToolsVersion>13.0.1</LLVMToolsVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<LLVMToolsVersion>12.0.0</LLVMToolsVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>ClangCL</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<LLVMToolsVersion>12.0.0</LLVMToolsVersion>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings"> <ImportGroup Label="ExtensionSettings">
@ -68,12 +45,6 @@
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup> </ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" /> <PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental> <LinkIncremental>true</LinkIncremental>
@ -87,18 +58,6 @@
<IntDir>$(SolutionDir)intermediates\$(Configuration)\$(MSBuildProjectName)\</IntDir> <IntDir>$(SolutionDir)intermediates\$(Configuration)\$(MSBuildProjectName)\</IntDir>
<TargetName>Spotify</TargetName> <TargetName>Spotify</TargetName>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)output\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)intermediates\$(Configuration)\$(MSBuildProjectName)\</IntDir>
<TargetName>Spotify</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)output\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)intermediates\$(Configuration)\$(MSBuildProjectName)\</IntDir>
<TargetName>Spotify</TargetName>
</PropertyGroup>
<PropertyGroup Label="Vcpkg"> <PropertyGroup Label="Vcpkg">
<VcpkgEnabled>false</VcpkgEnabled> <VcpkgEnabled>false</VcpkgEnabled>
</PropertyGroup> </PropertyGroup>

@ -1,87 +1,92 @@
#pragma once #pragma once
#include <cstdint>
#include <stdio.h>
#include <Windows.h> #include <Windows.h>
#include <cstdint>
#include <functional> #include <functional>
#include <stdio.h>
#define L_ERROR(...) util::logger::error(__FUNCTION__ "(): " __VA_ARGS__); #define L_ERROR(...) util::logger::error(__FUNCTION__ "(): " __VA_ARGS__);
#define TRACE_FN util::logger::debug( "%s()", __FUNCTION__ ); #define TRACE_FN util::logger::debug("%s()", __FUNCTION__);
#define LOGGER_PARSE_FMT char buf[2048]; va_list va; va_start( va, fmt ); _vsnprintf_s( buf, 1024, fmt, va ); va_end( va ); #define LOGGER_PARSE_FMT \
#define CREATE_LOGGER_METHOD(n) inline void n(const char* fmt, ...) { LOGGER_PARSE_FMT; log( #n, e_level_color::level_color_ ##n, buf ); } char buf[2048]; \
va_list va; \
va_start(va, fmt); \
_vsnprintf_s(buf, 1024, fmt, va); \
va_end(va);
#define CREATE_LOGGER_METHOD(n) \
inline void n(const char* fmt, ...) { \
LOGGER_PARSE_FMT; \
log(#n, e_level_color::level_color_##n, buf); \
}
namespace util { namespace util {
namespace logger { namespace logger {
enum class e_level_color : uint32_t { enum class e_level_color : uint32_t {
level_color_none = 15, // black bg and white fg level_color_none = 15, // black bg and white fg
level_color_debug = 8, level_color_debug = 8,
level_color_info = 10, level_color_info = 10,
level_color_warn = 14, level_color_warn = 14,
level_color_error = 12 level_color_error = 12
}; };
namespace _colors { namespace _colors {
inline void* m_console_handle = nullptr; inline void* m_console_handle = nullptr;
inline bool ensure_handle( ) { inline bool ensure_handle() {
if ( !m_console_handle ) if (!m_console_handle)
m_console_handle = GetStdHandle( STD_OUTPUT_HANDLE ); m_console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
return static_cast< bool >( m_console_handle ); return static_cast<bool>(m_console_handle);
} }
inline void apply( uint32_t clr ) { inline void apply(uint32_t clr) {
if ( !ensure_handle( ) ) return; if (!ensure_handle())
SetConsoleTextAttribute( m_console_handle, clr ); return;
} SetConsoleTextAttribute(m_console_handle, clr);
}
inline void reset( ) { inline void reset() { apply(static_cast<uint32_t>(e_level_color::level_color_none)); }
apply( static_cast< uint32_t >( e_level_color::level_color_none ) );
}
inline void colorify( uint32_t clr, std::function<void( )> cb ) { inline void colorify(uint32_t clr, std::function<void()> cb) {
apply( clr ); apply(clr);
cb( ); cb();
reset( ); reset();
} }
} } // namespace _colors
inline void log( const char* prefix, e_level_color level, const char* message ) { inline void log(const char* prefix, e_level_color level, const char* message) {
_colors::colorify( static_cast< uint32_t >( level ), [ prefix ] ( ) -> void { _colors::colorify(static_cast<uint32_t>(level), [prefix]() -> void { printf("%s >> ", prefix); });
printf( "%s >> ", prefix );
} );
printf( "%s\n", message ); printf("%s\n", message);
} }
#ifdef _DEBUG #ifdef _DEBUG
CREATE_LOGGER_METHOD( debug ); CREATE_LOGGER_METHOD(debug);
#else #else
__forceinline void debug( const char* fmt, ... ) { } __forceinline void debug(const char* fmt, ...) { }
#endif #endif
CREATE_LOGGER_METHOD( info ); CREATE_LOGGER_METHOD(info);
CREATE_LOGGER_METHOD( warn ); CREATE_LOGGER_METHOD(warn);
CREATE_LOGGER_METHOD( error ); CREATE_LOGGER_METHOD(error);
inline void fatal( const char* fmt, ... ) { inline void fatal(const char* fmt, ...) {
LOGGER_PARSE_FMT; LOGGER_PARSE_FMT;
error( "Fatal error: %s", buf ); error("Fatal error: %s", buf);
MessageBoxA( NULL, buf, "Unspotify fatal error", MB_OK ); MessageBoxA(NULL, buf, "Unspotify fatal error", MB_OK);
ExitProcess( -1 ); ExitProcess(-1);
} }
__forceinline void startup( ) { __forceinline void startup() {
::AllocConsole( ); ::AllocConsole();
freopen_s( reinterpret_cast< FILE** >( stdin ), "CONIN$", "r", stdin ); freopen_s(reinterpret_cast<FILE**>(stdin), "CONIN$", "r", stdin);
freopen_s( reinterpret_cast< FILE** >( stdout ), "CONOUT$", "w", stdout ); freopen_s(reinterpret_cast<FILE**>(stdout), "CONOUT$", "w", stdout);
::SetConsoleTitleA( "Unspotify" ); ::SetConsoleTitleA("Unspotify");
} }
__forceinline void detach( ) { __forceinline void detach() {
::ShowWindow( ::GetConsoleWindow( ), SW_HIDE ); ::ShowWindow(::GetConsoleWindow(), SW_HIDE);
::FreeConsole( ); ::FreeConsole();
} }
} } // namespace logger
} } // namespace util
#undef CREATE_LOGGER_METHOD #undef CREATE_LOGGER_METHOD

@ -1,30 +1,28 @@
#pragma once #pragma once
#include "logger.h" #include "logger.h"
#define UNSPOTIFY_GIT_URL "https://git.tcp.direct/dg/Unspotify" #define UNSPOTIFY_GIT_URL "https://git.tcp.direct/dg/Unspotify"
#define UNSPOTIFY_DISCORD_URL "https://discord.gg/U7X9kKcJzF" #define UNSPOTIFY_DISCORD_URL "https://discord.gg/U7X9kKcJzF"
namespace util { namespace util {
namespace logo { namespace logo {
inline void create_console_and_draw_logo( ) { inline void create_console_and_draw_logo() {
util::logger::startup( ); util::logger::startup();
util::logger::info( " d8, ,d8888b " ); util::logger::info(" d8, ,d8888b ");
util::logger::info( " d8P `8P 88P' " ); util::logger::info(" d8P `8P 88P' ");
util::logger::info( " d888888P d888888P " ); util::logger::info(" d888888P d888888P ");
util::logger::info( "?88 d8P 88bd88b .d888b,?88,.d88b, d8888b ?88' 88b ?88' ?88 d8P " ); util::logger::info("?88 d8P 88bd88b .d888b,?88,.d88b, d8888b ?88' 88b ?88' ?88 d8P ");
util::logger::info( "d88 88 88P' ?8b ?8b, `?88' ?88d8P' ?88 88P 88P 88P d88 88 " ); util::logger::info("d88 88 88P' ?8b ?8b, `?88' ?88d8P' ?88 88P 88P 88P d88 88 ");
util::logger::info( "?8( d88 d88 88P `?8b 88b d8P88b d88 88b d88 d88 ?8( d88 " ); util::logger::info("?8( d88 d88 88P `?8b 88b d8P88b d88 88b d88 d88 ?8( d88 ");
util::logger::info( "`?88P'?8bd88' 88b`?888P' 888888P'`?8888P' `?8b d88' d88' `?88P'?8b " ); util::logger::info("`?88P'?8bd88' 88b`?888P' 888888P'`?8888P' `?8b d88' d88' `?88P'?8b ");
util::logger::info( " 88P' )88" ); util::logger::info(" 88P' )88");
util::logger::info( " d88 ,d8P" ); util::logger::info(" d88 ,d8P");
util::logger::info( " ?8P `?888P " ); util::logger::info(" ?8P `?888P ");
util::logger::info( " compiled at %s %s", __DATE__, __TIME__ ); util::logger::info(" compiled at %s %s", __DATE__, __TIME__);
util::logger::info( " git: %s", UNSPOTIFY_GIT_URL ); util::logger::info(" git: %s", UNSPOTIFY_GIT_URL);
util::logger::info( " join our discord: %s", UNSPOTIFY_DISCORD_URL ); util::logger::info(" join our discord: %s", UNSPOTIFY_DISCORD_URL);
util::logger::info( "" ); util::logger::info("");
util::logger::info( "" ); util::logger::info("");
} }
} } // namespace logo
} } // namespace util

@ -1,7 +1,7 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16 # Visual Studio Version 17
VisualStudioVersion = 16.0.31515.178 VisualStudioVersion = 17.1.32210.238
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spotify-reverse", "spotify-reverse\spotify-reverse.vcxproj", "{930F56C2-377E-4C4D-815D-B0BB2C7DDFC9}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spotify-reverse", "spotify-reverse\spotify-reverse.vcxproj", "{930F56C2-377E-4C4D-815D-B0BB2C7DDFC9}"
EndProject EndProject
@ -9,12 +9,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spotify-reverse-launcher",
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spotify-reverse-shared", "spotify-reverse-shared\spotify-reverse-shared.vcxitems", "{80C10288-CFBD-49D7-84DE-BF83B13E3B24}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spotify-reverse-shared", "spotify-reverse-shared\spotify-reverse-shared.vcxitems", "{80C10288-CFBD-49D7-84DE-BF83B13E3B24}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solution", "solution", "{C6266EFA-749F-4905-80BB-F71B4ECE4595}"
ProjectSection(SolutionItems) = preProject
.clang-format = .clang-format
EndProjectSection
EndProject
Global Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
spotify-reverse-shared\spotify-reverse-shared.vcxitems*{0b43a5fb-d64c-45cb-b87d-af45d7d67932}*SharedItemsImports = 4
spotify-reverse-shared\spotify-reverse-shared.vcxitems*{80c10288-cfbd-49d7-84de-bf83b13e3b24}*SharedItemsImports = 9
spotify-reverse-shared\spotify-reverse-shared.vcxitems*{930f56c2-377e-4c4d-815d-b0bb2c7ddfc9}*SharedItemsImports = 4
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86 Debug|x86 = Debug|x86
Release|x86 = Release|x86 Release|x86 = Release|x86
@ -35,4 +35,9 @@ Global
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4464B17F-3892-49EA-B9CF-52D3AC9C0DD8} SolutionGuid = {4464B17F-3892-49EA-B9CF-52D3AC9C0DD8}
EndGlobalSection EndGlobalSection
GlobalSection(SharedMSBuildProjectFiles) = preSolution
spotify-reverse-shared\spotify-reverse-shared.vcxitems*{0b43a5fb-d64c-45cb-b87d-af45d7d67932}*SharedItemsImports = 4
spotify-reverse-shared\spotify-reverse-shared.vcxitems*{80c10288-cfbd-49d7-84de-bf83b13e3b24}*SharedItemsImports = 9
spotify-reverse-shared\spotify-reverse-shared.vcxitems*{930f56c2-377e-4c4d-815d-b0bb2c7ddfc9}*SharedItemsImports = 4
EndGlobalSection
EndGlobal EndGlobal

@ -1,52 +1,51 @@
#include "bootstrap.h" #include "bootstrap.h"
#include "../util/util.h"
#include "../spotify/spotify.h"
#include "../hooks/hooks.h"
#include "../updates/updates.h"
#include "../exceptions/exceptions.h" #include "../exceptions/exceptions.h"
#include "../hooks/hooks.h"
#include "../spotify/spotify.h"
#include "../updates/updates.h"
#include "../util/util.h"
#include "shared/logo.h" #include "shared/logo.h"
#include <thread> #include <thread>
namespace bootstrap { namespace bootstrap {
DWORD __stdcall _initial_routine( HANDLE ) { DWORD __stdcall _initial_routine(HANDLE) {
util::logo::create_console_and_draw_logo( ); util::logo::create_console_and_draw_logo();
exceptions::subscribe( ); exceptions::subscribe();
#ifdef CHECK_FOR_UPDATES #ifdef CHECK_FOR_UPDATES
std::thread( updates::do_job ).detach( ); std::thread(updates::do_job).detach();
#endif #endif
spotify::init( ); spotify::init();
hooks::init( ); hooks::init();
#ifdef _DEBUG #ifdef _DEBUG
while ( !GetAsyncKeyState( VK_DELETE ) ) { while (!GetAsyncKeyState(VK_DELETE)) {
#else #else
util::logger::warn( "press any key to close this console" ); util::logger::warn("press any key to close this console");
_getwch( ); _getwch();
util::logger::detach( ); util::logger::detach();
while ( true ) { while (true) {
#endif #endif
std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) ); std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
_shutdown( ); _shutdown();
return 1; // unreachable but whatever return 1; // unreachable but whatever
} }
bool startup( HINSTANCE dll_handle ) { bool startup(HINSTANCE dll_handle) {
detail::dll_handle = dll_handle; detail::dll_handle = dll_handle;
detail::region_size = util::mem::module_t( uintptr_t( dll_handle ) ).get_nt_headers( )->OptionalHeader.SizeOfImage; detail::region_size = util::mem::module_t(uintptr_t(dll_handle)).get_nt_headers()->OptionalHeader.SizeOfImage;
CreateThread( nullptr, 0, _initial_routine, 0, 0, nullptr ); CreateThread(nullptr, 0, _initial_routine, 0, 0, nullptr);
return true; return true;
} }
void _shutdown( ) { void _shutdown() {
hooks::shutdown( ); hooks::shutdown();
FreeLibraryAndExitThread( reinterpret_cast< HMODULE >( detail::dll_handle ), 0x1 ); FreeLibraryAndExitThread(reinterpret_cast<HMODULE>(detail::dll_handle), 0x1);
} }
} } // namespace bootstrap

@ -1,16 +1,14 @@
#pragma once #pragma once
#include <Windows.h> #include <Windows.h>
namespace bootstrap { namespace bootstrap {
namespace detail { namespace detail {
inline HINSTANCE dll_handle; inline HINSTANCE dll_handle;
inline DWORD region_size; inline DWORD region_size;
} } // namespace detail
DWORD __stdcall _initial_routine( HANDLE ); DWORD __stdcall _initial_routine(HANDLE);
bool startup( HINSTANCE handle );
void _shutdown( );
}
bool startup(HINSTANCE handle);
void _shutdown();
} // namespace bootstrap

@ -2,12 +2,12 @@
// autoupdates // autoupdates
// @note: es3n1n: v1.0.7 = 107 // @note: es3n1n: v1.0.8 = 108
#define UNSPOTIFY_VERSION 107 #define UNSPOTIFY_VERSION 108
#define AUTOUPDATER_DOMAIN "es3n.in" #define AUTOUPDATER_DOMAIN "es3n.in"
#define AUTOUPDATER_URL "unspotify.json" #define AUTOUPDATER_URL "unspotify.json"
#ifndef _DEBUG #ifndef _DEBUG
#define CHECK_FOR_UPDATES #define CHECK_FOR_UPDATES
#endif #endif

@ -1,9 +1,9 @@
#include "bootstrap/bootstrap.h" #include "bootstrap/bootstrap.h"
#include <cstdint> #include <cstdint>
BOOL __stdcall DllMain(std::uintptr_t hinstDll, std::uint32_t fdwReason, std::uintptr_t) {
if (fdwReason != DLL_PROCESS_ATTACH)
return 1;
BOOL __stdcall DllMain( std::uintptr_t hinstDll, std::uint32_t fdwReason, std::uintptr_t ) { return bootstrap::startup(reinterpret_cast<HINSTANCE>(hinstDll));
if ( fdwReason != DLL_PROCESS_ATTACH ) return 1;
return bootstrap::startup( reinterpret_cast< HINSTANCE >( hinstDll ) );
} }

@ -1,42 +1,39 @@
#include "exceptions.h" #include "exceptions.h"
#include "../bootstrap/bootstrap.h" #include "../bootstrap/bootstrap.h"
#include "shared/logger.h" #include "shared/logger.h"
#include <string>
#include <sstream>
#include <iostream> #include <iostream>
#include <sstream>
#include <string>
namespace exceptions { namespace exceptions {
LONG __stdcall handler( EXCEPTION_POINTERS* info ) { LONG __stdcall handler(EXCEPTION_POINTERS* info) {
static bool logged { false }; static bool logged { false };
if ( logged ) if (logged)
return EXCEPTION_CONTINUE_SEARCH; // @note: es3n1n: log exceptions only once return EXCEPTION_CONTINUE_SEARCH; // @note: es3n1n: log exceptions only once
auto exc_addr = reinterpret_cast< uintptr_t >( info->ExceptionRecord->ExceptionAddress ); auto exc_addr = reinterpret_cast<uintptr_t>(info->ExceptionRecord->ExceptionAddress);
if ( ( exc_addr < reinterpret_cast< uintptr_t >( bootstrap::detail::dll_handle ) || if ((exc_addr < reinterpret_cast<uintptr_t>(bootstrap::detail::dll_handle)
exc_addr >( reinterpret_cast< uintptr_t >( bootstrap::detail::dll_handle ) + bootstrap::detail::region_size ) ) ) { || exc_addr > (reinterpret_cast<uintptr_t>(bootstrap::detail::dll_handle) + bootstrap::detail::region_size))) {
return EXCEPTION_CONTINUE_SEARCH; // @note: es3n1n: log exceptions only from our mod return EXCEPTION_CONTINUE_SEARCH; // @note: es3n1n: log exceptions only from our mod
} }
std::stringstream log_msg; std::stringstream log_msg;
log_msg << "Module base: " << std::hex << std::showbase << bootstrap::detail::dll_handle << std::endl; log_msg << "Module base: " << std::hex << std::showbase << bootstrap::detail::dll_handle << std::endl;
log_msg << "Module size: " << std::hex << std::showbase << bootstrap::detail::region_size << std::endl; log_msg << "Module size: " << std::hex << std::showbase << bootstrap::detail::region_size << std::endl;
log_msg << "Exception address: " << std::hex << std::showbase << exc_addr << std::endl; log_msg << "Exception address: " << std::hex << std::showbase << exc_addr << std::endl;
log_msg << "Exception code: " << std::hex << std::showbase << info->ExceptionRecord->ExceptionCode << std::endl; log_msg << "Exception code: " << std::hex << std::showbase << info->ExceptionRecord->ExceptionCode << std::endl;
log_msg << "EAX: " << std::hex << std::showbase << info->ContextRecord->Eax << " | EBX: " << info->ContextRecord->Ebx << std::endl; log_msg << "EAX: " << std::hex << std::showbase << info->ContextRecord->Eax << " | EBX: " << info->ContextRecord->Ebx << std::endl;
log_msg << "ECX: " << std::hex << std::showbase << info->ContextRecord->Ecx << " | EDX: " << info->ContextRecord->Edx << std::endl; log_msg << "ECX: " << std::hex << std::showbase << info->ContextRecord->Ecx << " | EDX: " << info->ContextRecord->Edx << std::endl;
log_msg << "EBP: " << std::hex << std::showbase << info->ContextRecord->Ebp << " | ESP: " << info->ContextRecord->Esp << std::endl; log_msg << "EBP: " << std::hex << std::showbase << info->ContextRecord->Ebp << " | ESP: " << info->ContextRecord->Esp << std::endl;
log_msg << "ESI: " << std::hex << std::showbase << info->ContextRecord->Esi << " | EDI: " << info->ContextRecord->Edi << std::endl; log_msg << "ESI: " << std::hex << std::showbase << info->ContextRecord->Esi << " | EDI: " << info->ContextRecord->Edi << std::endl;
util::logger::fatal( log_msg.str( ).c_str( ) ); util::logger::fatal(log_msg.str().c_str());
logged = true; logged = true;
return EXCEPTION_EXECUTE_HANDLER; return EXCEPTION_EXECUTE_HANDLER;
} }
void subscribe( ) { void subscribe() { AddVectoredExceptionHandler(1, handler); }
AddVectoredExceptionHandler( 1, handler ); } // namespace exceptions
}
}

@ -1,9 +1,8 @@
#pragma once #pragma once
#include <cstdint>
#include <Windows.h> #include <Windows.h>
#include <cstdint>
namespace exceptions { namespace exceptions {
LONG __stdcall handler( EXCEPTION_POINTERS* info ); LONG __stdcall handler(EXCEPTION_POINTERS* info);
void subscribe( ); void subscribe();
} } // namespace exceptions

@ -1,22 +1,18 @@
#include "../hooks.h" #include "../hooks.h"
namespace hooks { namespace hooks {
namespace hooked { namespace hooked {
/* /*
@xref: " Creating track player for track (playback_id %s)" @xref: " Creating track player for track (playback_id %s)"
*/ */
void __fastcall create_track( void __fastcall create_track(void* pthis, void* pedx, spotify::structs::player_meta_t* player_meta,
void* pthis, void* pedx, spotify::structs::player_track_meta_t* track_meta, double speed, int normalization, int urgency, int track_select_flag, int flag,
spotify::structs::player_meta_t* player_meta, int stream_type) {
spotify::structs::player_track_meta_t* track_meta, player_meta->m_should_skip = static_cast<std::uint32_t>(static_cast<bool>(strstr(track_meta->m_track_uri, "spotify:ad:")));
double speed, int normalization, int urgency, int track_select_flag, int flag, int stream_type util::logger::info("Playing %s | should_skip: %s", track_meta->m_track_uri, player_meta->m_should_skip ? "true" : "false");
) { if (player_meta->m_should_skip)
player_meta->m_should_skip = static_cast< std::uint32_t >( static_cast< bool >( strstr( track_meta->m_track_uri, "spotify:ad:" ) ) ); speed = 29.0;
util::logger::info( "Playing %s | should_skip: %s", track_meta->m_track_uri, player_meta->m_should_skip ? "true" : "false" ); original::create_track(pthis, pedx, player_meta, track_meta, speed, normalization, urgency, track_select_flag, flag, stream_type);
if ( player_meta->m_should_skip ) }
speed = 29.0; } // namespace hooked
original::create_track( pthis, pedx, player_meta, track_meta, speed, normalization, urgency, track_select_flag, flag, stream_type ); } // namespace hooks
}
}
}

@ -1,19 +1,19 @@
#include "../hooks.h" #include "../hooks.h"
namespace hooks { namespace hooks {
namespace hooked { namespace hooked {
#ifdef _DEBUG #ifdef _DEBUG
std::uintptr_t __cdecl debug_msg( std::uint32_t, std::uint32_t, const char* win, const char* flag, std::uint32_t size, std::uint32_t, const char* fmt, ... ) { // @xref: "Path provided in --%s '%s' (resolved to '%s') does not exist." std::uintptr_t __cdecl debug_msg(std::uint32_t, std::uint32_t, const char* win, const char* flag, std::uint32_t size, std::uint32_t,
LOGGER_PARSE_FMT; const char* fmt, ...) { // @xref: "Path provided in --%s '%s' (resolved to '%s') does not exist."
LOGGER_PARSE_FMT;
for ( int i = 0; i < strlen( buf ); i++ ) for (std::size_t i = 0; i < strlen(buf); i++)
buf[ i ] = buf[ i ] == '\n' ? ' ' : buf[ i ]; buf[i] = buf[i] == '\n' ? ' ' : buf[i];
printf( "[spotify::debug_msg] %s\n", buf ); printf("[spotify::debug_msg] %s\n", buf);
return 0; return 0;
} }
#endif #endif
} } // namespace hooked
} } // namespace hooks

@ -1,10 +1,9 @@
#include "../hooks.h" #include "../hooks.h"
namespace hooks { namespace hooks {
namespace hooked { namespace hooked {
std::uintptr_t __cdecl get_ad( int a1, int a2 ) { // @xref: "advertiser" std::uintptr_t __cdecl get_ad(int a1, int a2) { // @xref: "advertiser"
return 0; return 0;
} }
} } // namespace hooked
} } // namespace hooks

@ -1,24 +1,23 @@
#include "hooks.h" #include "hooks.h"
#define CREATE_HOOK(n) \
#define CREATE_HOOK(n) if ( !util::hooking::detour::create(spotify::addr:: ##n, hooked:: ##n, reinterpret_cast< void** > ( &original:: ##n ) ) ) util::logger::fatal("Unable to hook %s", #n); if (!util::hooking::detour::create(spotify::addr::##n, hooked::##n, reinterpret_cast<void**>(&original::##n))) \
util::logger::fatal("Unable to hook %s", #n);
namespace hooks { namespace hooks {
void init( ) { void init() {
if ( !util::hooking::detour::init( ) ) if (!util::hooking::detour::init())
util::logger::fatal( "Unable to init minhook" ); util::logger::fatal("Unable to init minhook");
#ifdef _DEBUG #pragma warning(disable : 5103)
CREATE_HOOK( debug_msg ); #ifdef _DEBUG
#endif CREATE_HOOK(debug_msg);
CREATE_HOOK( get_ad ); #endif
CREATE_HOOK( create_track ); CREATE_HOOK(get_ad);
} CREATE_HOOK(create_track);
#pragma warning(default : 5103)
void shutdown( ) { }
util::hooking::detour::remove( );
}
}
void shutdown() { util::hooking::detour::remove(); }
} // namespace hooks
#undef CREATE_HOOK #undef CREATE_HOOK

@ -1,24 +1,26 @@
#pragma once #pragma once
#include "../util/util.h"
#include "../spotify/spotify.h" #include "../spotify/spotify.h"
#include "../util/util.h"
namespace hooks { namespace hooks {
namespace hooked { namespace hooked {
#ifdef _DEBUG #ifdef _DEBUG
std::uintptr_t __cdecl debug_msg( std::uint32_t, std::uint32_t, const char* win, const char* flag, std::uint32_t size, std::uint32_t, const char* fmt, ... ); std::uintptr_t __cdecl debug_msg(std::uint32_t, std::uint32_t, const char* win, const char* flag, std::uint32_t size, std::uint32_t,
#endif const char* fmt, ...);
std::uintptr_t __cdecl get_ad( int a1, int a2 ); #endif
void __fastcall create_track( void* pthis, void* pedx, spotify::structs::player_meta_t* player_meta, spotify::structs::player_track_meta_t* track_meta, double speed, int normalization, int urgency, int track_select_flag, int flag, int stream_type ); std::uintptr_t __cdecl get_ad(int a1, int a2);
} void __fastcall create_track(void* pthis, void* pedx, spotify::structs::player_meta_t* player_meta,
namespace original { spotify::structs::player_track_meta_t* track_meta, double speed, int normalization, int urgency, int track_select_flag, int flag,
#ifdef _DEBUG int stream_type);
inline decltype( &hooked::debug_msg ) debug_msg; } // namespace hooked
#endif namespace original {
inline decltype( &hooked::get_ad ) get_ad; #ifdef _DEBUG
inline decltype( &hooked::create_track ) create_track; inline decltype(&hooked::debug_msg) debug_msg;
} #endif
inline decltype(&hooked::get_ad) get_ad;
inline decltype(&hooked::create_track) create_track;
} // namespace original
void init( ); void init();
void shutdown( ); void shutdown();
} } // namespace hooks

@ -9,14 +9,6 @@
<Configuration>Release</Configuration> <Configuration>Release</Configuration>
<Platform>Win32</Platform> <Platform>Win32</Platform>
</ProjectConfiguration> </ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Globals"> <PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion> <VCProjectVersion>16.0</VCProjectVersion>
@ -31,7 +23,7 @@
<UseDebugLibraries>true</UseDebugLibraries> <UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>ClangCL</PlatformToolset> <PlatformToolset>ClangCL</PlatformToolset>
<CharacterSet>Unicode</CharacterSet> <CharacterSet>Unicode</CharacterSet>
<LLVMToolsVersion>12.0.0</LLVMToolsVersion> <LLVMToolsVersion>13.0.1</LLVMToolsVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType> <ConfigurationType>DynamicLibrary</ConfigurationType>
@ -39,22 +31,7 @@
<PlatformToolset>ClangCL</PlatformToolset> <PlatformToolset>ClangCL</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization> <WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet> <CharacterSet>Unicode</CharacterSet>
<LLVMToolsVersion>12.0.0</LLVMToolsVersion> <LLVMToolsVersion>13.0.1</LLVMToolsVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>ClangCL</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<LLVMToolsVersion>12.0.0</LLVMToolsVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>ClangCL</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<LLVMToolsVersion>12.0.0</LLVMToolsVersion>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings"> <ImportGroup Label="ExtensionSettings">
@ -68,32 +45,18 @@
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup> </ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" /> <PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental> <LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)output\$(Configuration)\</OutDir> <OutDir>$(SolutionDir)output\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)intermediates\$(Configuration)\$(MSBuildProjectName)\</IntDir> <IntDir>$(SolutionDir)intermediates\$(Configuration)\$(MSBuildProjectName)\</IntDir>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental> <LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)output\$(Configuration)\</OutDir> <OutDir>$(SolutionDir)output\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)intermediates\$(Configuration)\$(MSBuildProjectName)\</IntDir> <IntDir>$(SolutionDir)intermediates\$(Configuration)\$(MSBuildProjectName)\</IntDir>
</PropertyGroup> <GenerateManifest>false</GenerateManifest>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)output\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)intermediates\$(Configuration)\$(MSBuildProjectName)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)output\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)intermediates\$(Configuration)\$(MSBuildProjectName)\</IntDir>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Label="Vcpkg"> <PropertyGroup Label="Vcpkg">
<VcpkgEnabled>false</VcpkgEnabled> <VcpkgEnabled>false</VcpkgEnabled>
@ -107,7 +70,7 @@
<LanguageStandard>stdcpplatest</LanguageStandard> <LanguageStandard>stdcpplatest</LanguageStandard>
<LanguageStandard_C>stdc17</LanguageStandard_C> <LanguageStandard_C>stdc17</LanguageStandard_C>
<MultiProcessorCompilation>true</MultiProcessorCompilation> <MultiProcessorCompilation>true</MultiProcessorCompilation>
<AdditionalOptions>-Xclang -Ofast -Xclang -fno-threadsafe-statics -Xclang -fdelayed-template-parsing -fcf-protection=none -mllvm -pgso -Wno-missing-braces -Wno-deprecated-volatile -Wno-missing-field-initializers -Wno-ignored-pragma-optimize /clang:-fno-unwind-tables /clang:-ffast-math /clang:-fno-builtin -Wno-gnu-string-literal-operator-template -Wno-unused-private-field -Wno-invalid-token-paste -Wno-microsoft-cast -Wno-unused-command-line-argument -Wno-pragma-once-outside-header %(AdditionalOptions)</AdditionalOptions> <AdditionalOptions>-Xclang -Ofast -Xclang -fno-threadsafe-statics -Xclang -fdelayed-template-parsing -fcf-protection=none -mllvm -pgso -Wno-int-to-void-pointer-cast -Wno-missing-braces -Wno-deprecated-volatile -Wno-missing-field-initializers -Wno-ignored-pragma-optimize /clang:-fno-unwind-tables /clang:-ffast-math /clang:-fno-builtin -Wno-gnu-string-literal-operator-template -Wno-unused-private-field -Wno-invalid-token-paste -Wno-microsoft-cast -Wno-unused-command-line-argument -Wno-sign-compare -Wno-multichar -Wno-unused-parameter -Wno-microsoft-enum-forward-reference </AdditionalOptions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<InlineFunctionExpansion>Disabled</InlineFunctionExpansion> <InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
</ClCompile> </ClCompile>
@ -123,53 +86,12 @@
<FunctionLevelLinking>true</FunctionLevelLinking> <FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>_HAS_STATIC_RTTI=0;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard> <LanguageStandard>stdcpplatest</LanguageStandard>
<LanguageStandard_C>stdc17</LanguageStandard_C> <LanguageStandard_C>stdc17</LanguageStandard_C>
<MultiProcessorCompilation>true</MultiProcessorCompilation> <MultiProcessorCompilation>true</MultiProcessorCompilation>
<AdditionalOptions>-Xclang -Ofast -Xclang -fno-threadsafe-statics -Xclang -fdelayed-template-parsing -fcf-protection=none -mllvm -pgso -Wno-missing-braces -Wno-deprecated-volatile -Wno-missing-field-initializers -Wno-ignored-pragma-optimize /clang:-fno-unwind-tables /clang:-ffast-math /clang:-fno-builtin -Wno-gnu-string-literal-operator-template -Wno-unused-private-field -Wno-invalid-token-paste -Wno-microsoft-cast -Wno-unused-command-line-argument -Wno-pragma-once-outside-header %(AdditionalOptions)</AdditionalOptions> <AdditionalOptions>-Xclang -fno-rtti -Xclang -Ofast -Xclang -fno-threadsafe-statics -Xclang -fdelayed-template-parsing -fcf-protection=none -mllvm -pgso -Wno-int-to-void-pointer-cast -Wno-missing-braces -Wno-deprecated-volatile -Wno-missing-field-initializers -Wno-ignored-pragma-optimize /clang:-fno-unwind-tables /clang:-ffast-math /clang:-fno-builtin -Wno-gnu-string-literal-operator-template -Wno-unused-private-field -Wno-invalid-token-paste -Wno-microsoft-cast -Wno-unused-command-line-argument -Wno-sign-compare -Wno-multichar -Wno-unused-parameter -Wno-microsoft-enum-forward-reference </AdditionalOptions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalOptions>/MAP /MERGE:.voltbl=.data /MERGE:.retplne=.data /MERGE:.gehcont=.data /MERGE:.00cfg=.data /MERGE:_RDATA=.rdata /FORCE:UNRESOLVED %(AdditionalOptions)</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<LanguageStandard_C>stdc17</LanguageStandard_C>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<AdditionalOptions>-Xclang -Ofast -Xclang -fno-threadsafe-statics -Xclang -fdelayed-template-parsing -fcf-protection=none -mllvm -pgso -Wno-missing-braces -Wno-deprecated-volatile -Wno-missing-field-initializers -Wno-ignored-pragma-optimize /clang:-fno-unwind-tables /clang:-ffast-math /clang:-fno-builtin -Wno-gnu-string-literal-operator-template -Wno-unused-private-field -Wno-invalid-token-paste -Wno-microsoft-cast -Wno-unused-command-line-argument -Wno-pragma-once-outside-header %(AdditionalOptions)</AdditionalOptions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalOptions>/MAP /MERGE:.voltbl=.data /MERGE:.retplne=.data /MERGE:.gehcont=.data /MERGE:.00cfg=.data /MERGE:_RDATA=.rdata /FORCE:UNRESOLVED %(AdditionalOptions)</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<LanguageStandard_C>stdc17</LanguageStandard_C>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<AdditionalOptions>-Xclang -Ofast -Xclang -fno-threadsafe-statics -Xclang -fdelayed-template-parsing -fcf-protection=none -mllvm -pgso -Wno-missing-braces -Wno-deprecated-volatile -Wno-missing-field-initializers -Wno-ignored-pragma-optimize /clang:-fno-unwind-tables /clang:-ffast-math /clang:-fno-builtin -Wno-gnu-string-literal-operator-template -Wno-unused-private-field -Wno-invalid-token-paste -Wno-microsoft-cast -Wno-unused-command-line-argument -Wno-pragma-once-outside-header %(AdditionalOptions)</AdditionalOptions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary> <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile> </ClCompile>
<Link> <Link>
@ -197,7 +119,6 @@
<ClCompile Include="util\hooking\detours\mh\hook.c" /> <ClCompile Include="util\hooking\detours\mh\hook.c" />
<ClCompile Include="util\hooking\detours\mh\trampoline.c" /> <ClCompile Include="util\hooking\detours\mh\trampoline.c" />
<ClCompile Include="util\hooking\vmt\vmt.cpp" /> <ClCompile Include="util\hooking\vmt\vmt.cpp" />
<ClCompile Include="util\mem\module.h" />
<ClCompile Include="util\networking\networking.cpp" /> <ClCompile Include="util\networking\networking.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -219,6 +140,7 @@
<ClInclude Include="util\hooking\vmt\vmt.h" /> <ClInclude Include="util\hooking\vmt\vmt.h" />
<ClInclude Include="util\mem\addr.h" /> <ClInclude Include="util\mem\addr.h" />
<ClInclude Include="defines.h" /> <ClInclude Include="defines.h" />
<ClInclude Include="util\mem\module.h" />
<ClInclude Include="util\networking\ext\json.hpp" /> <ClInclude Include="util\networking\ext\json.hpp" />
<ClInclude Include="util\networking\networking.h" /> <ClInclude Include="util\networking\networking.h" />
<ClInclude Include="util\util.h" /> <ClInclude Include="util\util.h" />

@ -18,9 +18,6 @@
<ClCompile Include="dllmain.cpp"> <ClCompile Include="dllmain.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="util\mem\module.h">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bootstrap\bootstrap.cpp"> <ClCompile Include="bootstrap\bootstrap.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@ -134,5 +131,8 @@
<ClInclude Include="exceptions\exceptions.h"> <ClInclude Include="exceptions\exceptions.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="util\mem\module.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>

@ -13,14 +13,4 @@
<LocalDebuggerAttach>true</LocalDebuggerAttach> <LocalDebuggerAttach>true</LocalDebuggerAttach>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor> <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LocalDebuggerCommand>oSpotify.exe</LocalDebuggerCommand>
<LocalDebuggerAttach>true</LocalDebuggerAttach>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LocalDebuggerCommand>Spotify.exe</LocalDebuggerCommand>
<LocalDebuggerAttach>true</LocalDebuggerAttach>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
</Project> </Project>

@ -1,76 +1,75 @@
#include "spotify.h" #include "spotify.h"
#define ASSERT_PATTERN(s) \
#define ASSERT_PATTERN(s) if ( !addr:: ##s .valid( ) ) util::logger::fatal( "Pattern %s not found. Please update your Unspotify version", #s); if (!addr::##s.valid()) \
#define ASSERT_PATTERN_STEP(s) if ( !s ) util::logger::fatal( "Unable to find %s at %d", #s, __LINE__ ) util::logger::fatal("Pattern %s not found. Please update your Unspotify version", #s);
#define ASSERT_PATTERN_STEP(s) \
if (!s) \
util::logger::fatal("Unable to find %s at %d", #s, __LINE__)
namespace spotify { namespace spotify {
void init( ) { void init() {
TRACE_FN; #pragma warning(disable : 5103)
TRACE_FN;
modules::spotify = util::mem::module_t( nullptr ); modules::spotify = util::mem::module_t(nullptr);
util::logger::debug( "module::spotify = 0x%p", modules::spotify ); util::logger::debug("module::spotify = 0x%p", modules::spotify);
uint32_t str; uint32_t str;
util::mem::addr_t sig; util::mem::addr_t sig;
std::vector<int> pattern = {}; std::vector<int> pattern = {};
#ifdef _DEBUG
// "Could not create stream reader for file: %s"
str = modules::spotify.sig(
"43 6F 75 6C 64 20 6E 6F 74 20 63 72 65 61 74 65 20 73 74 72 65 61 6D 20 72 65 61 64 65 72 20 66 6F 72 20 66 69 6C 65 3A 20 25 73");
ASSERT_PATTERN_STEP(str);
pattern.clear();
pattern.emplace_back(0x68 /* push offset */);
for (int i = 0; i < 4; i++)
pattern.emplace_back((int)(((uint8_t*)(&str))[i]));
sig = modules::spotify.sig(pattern);
ASSERT_PATTERN_STEP(sig);
addr::debug_msg = sig.walk_until(0x6A /* push 02 */).walk_until(0xE8 /* call */).rel(1);
util::logger::debug("addr::debug_msg = 0x%p", addr::debug_msg);
ASSERT_PATTERN(debug_msg);
#endif
#ifdef _DEBUG // "ad_type"
// "Could not create stream reader for file: %s" str = modules::spotify.sig("61 64 5F 74 79 70 65 00");
str = modules::spotify.sig( "43 6F 75 6C 64 20 6E 6F 74 20 63 72 65 61 74 65 20 73 74 72 65 61 6D 20 72 65 61 64 65 72 20 66 6F 72 20 66 69 6C 65 3A 20 25 73" ); ASSERT_PATTERN_STEP(str);
ASSERT_PATTERN_STEP( str ); pattern.clear();
pattern.clear( ); for (int i = 0; i < 4; i++)
pattern.emplace_back( 0x68 /* push offset */ ); pattern.emplace_back((int)(((uint8_t*)(&str))[i]));
for ( int i = 0; i < 4; i++ ) sig = modules::spotify.sig(pattern);
pattern.emplace_back( ( int )( ( ( uint8_t* )( &str ) )[ i ] ) ); ASSERT_PATTERN_STEP(sig);
sig = modules::spotify.sig( pattern ); do {
ASSERT_PATTERN_STEP( sig ); sig = sig.walk_back_until(0xC2 /* retn */);
addr::debug_msg = } while (sig.offset(-5).read<uint8_t>() != 0xE8 && sig.offset(-8).read<uint8_t>() != 0xE8);
sig.walk_until( 0x6A /* push 02 */ ).walk_until( 0xE8 /* call */ ).rel( 1 ); addr::get_ad = sig.add(1).read<uint8_t>() == 0x68 /* push */ ? sig.add(1) : sig.walk_until(0x68 /* push 0D4h */);
util::logger::debug( "addr::debug_msg = 0x%p", addr::debug_msg ); util::logger::debug("addr::get_ad = 0x%p", addr::get_ad);
ASSERT_PATTERN( debug_msg ); ASSERT_PATTERN(get_ad);
#endif
// "ad_type"
str = modules::spotify.sig( "61 64 5F 74 79 70 65 00" );
ASSERT_PATTERN_STEP( str );
pattern.clear( );
for ( int i = 0; i < 4; i++ )
pattern.emplace_back( ( int )( ( ( uint8_t* )( &str ) )[ i ] ) );
sig = modules::spotify.sig( pattern );
ASSERT_PATTERN_STEP( sig );
do {
sig = sig.walk_back_until( 0xC3 /* retn */ );
} while ( sig.offset( -5 ).read<uint8_t>( ) != 0xE8 );
addr::get_ad = sig.add( 1 ).read<uint8_t>( ) == 0x68 /* push */ ? sig.add( 1 ) : sig.walk_until( 0x55 /* push ebp */ );
util::logger::debug( "addr::get_ad = 0x%p", addr::get_ad );
ASSERT_PATTERN( get_ad );
// " Creating track player for track (playback_id %s)"
str = modules::spotify.sig( "20 20 20 20 43 72 65 61 74 69 6E 67 20 74 72 61 63 6B 20 70 6C 61 79 65 72 20 66 6F 72 20 74 72 61 63 6B 20 28 70 6C 61 79 62 61 63 6B 5F 69 64 20 25 73 29" );
ASSERT_PATTERN_STEP( sig );
pattern.clear( );
pattern.emplace_back( 0x68 /* push offset */ );
for ( int i = 0; i < 4; i++ )
pattern.emplace_back( ( int )( ( ( uint8_t* )( &str ) )[ i ] ) );
sig = modules::spotify.sig( pattern );
ASSERT_PATTERN_STEP( sig );
addr::create_track = sig.walk_back_until( 0xC2 /* retn */ ).offset( 3 /* C2 04 00 */ );
while ( addr::create_track.read<uint8_t>( ) == 0xE8 /* call */ )
addr::create_track = addr::create_track.add( 5 ); // E8 + 4 bytes addr
//if ( addr::create_track.read<uint8_t>( ) == 0xCC /* align */ )
// addr::create_track = addr::create_track.walk_until( 0x55 /* push ebp */ );
while ( addr::create_track.read<uint8_t>( ) == 0xCC )
addr::create_track = addr::create_track.add( 1 ); // skip aligns
util::logger::debug( "addr::create_track = 0x%p", addr::create_track );
ASSERT_PATTERN( create_track );
}
}
// " Creating track player for track (playback_id %s)"
str = modules::spotify.sig("20 20 20 20 43 72 65 61 74 69 6E 67 20 74 72 61 63 6B 20 70 6C 61 79 65 72 20 66 6F 72 20 74 72 61 63 6B 20 28 70 6C "
"61 79 62 61 63 6B 5F 69 64 20 25 73 29");
ASSERT_PATTERN_STEP(sig);
pattern.clear();
pattern.emplace_back(0x68 /* push offset */);
for (int i = 0; i < 4; i++)
pattern.emplace_back((int)(((uint8_t*)(&str))[i]));
sig = modules::spotify.sig(pattern);
ASSERT_PATTERN_STEP(sig);
addr::create_track = sig.walk_back_until(0xC2 /* retn */).offset(3 /* C2 04 00 */);
while (addr::create_track.read<uint8_t>() == 0xE8 /* call */)
addr::create_track = addr::create_track.add(5); // E8 + 4 bytes addr
while (addr::create_track.read<uint8_t>() == 0xCC)
addr::create_track = addr::create_track.add(1); // skip aligns
util::logger::debug("addr::create_track = 0x%p", addr::create_track);
ASSERT_PATTERN(create_track);
#pragma warning(default : 5103)
}
} // namespace spotify
#undef ASSERT_PATTERN #undef ASSERT_PATTERN
#undef ASSERT_PATTERN_STEP #undef ASSERT_PATTERN_STEP

@ -1,31 +1,37 @@
#pragma once #pragma once
#include "../util/util.h" #include "../util/util.h"
namespace spotify { namespace spotify {
namespace structs { namespace structs {
struct player_meta_t { struct player_meta_t {
private: char __pad[ 0x74 ]; private:
public: std::uint32_t m_should_skip; char __pad[0x74];
};
struct player_track_meta_t { public:
private: char __pad[ 0x48 ]; std::uint32_t m_should_skip;
public: const char* m_track_uri; };
};
}
namespace modules { struct player_track_meta_t {
inline util::mem::module_t spotify; private:
} char __pad[0x48];
namespace addr { public:
#ifdef _DEBUG const char* m_track_uri;
inline util::mem::addr_t debug_msg; };
#endif } // namespace structs
inline util::mem::addr_t get_ad;
inline util::mem::addr_t create_track;
}
void init( ); namespace modules {
} inline util::mem::module_t spotify;
}
namespace addr {
#ifdef _DEBUG
inline util::mem::addr_t debug_msg;
#endif
inline util::mem::addr_t get_ad;
inline util::mem::addr_t create_track;
inline util::mem::addr_t should_show_ad;
} // namespace addr
void init();
} // namespace spotify

@ -1,46 +1,41 @@
#include "updates.h" #include "updates.h"
namespace updates { namespace updates {
update_info_t poll_info( ) { update_info_t poll_info() {
auto [data, error] = util::networking::get( AUTOUPDATER_DOMAIN, AUTOUPDATER_URL ); auto [data, error] = util::networking::get(AUTOUPDATER_DOMAIN, AUTOUPDATER_URL);
if ( error ) if (error)
return update_info_t { return update_info_t { .m_error = true, .m_error_desc = "Internal server error :shrug:" };
.m_error = true,
.m_error_desc = "Internal server error :shrug:"
};
return update_info_t { return update_info_t { .m_error = false,
.m_error = false, .m_version = data["version"].get<uint32_t>(),
.m_version = data[ "version" ].get<uint32_t>( ), .m_changelog = data["changelog"].get<std::string>(),
.m_changelog = data[ "changelog" ].get<std::string>( ), .m_is_required = data["required"].get<bool>(),
.m_is_required = data[ "required" ].get<bool>( ), .m_download_url = data["download_url"].get<std::string>() };
.m_download_url = data[ "download_url" ].get<std::string>( ) }
};
}
void do_job( ) { void do_job() {
auto update_info = poll_info( ); auto update_info = poll_info();
if ( update_info.m_error ) { if (update_info.m_error) {
util::logger::error( "Failed to poll newest version info: %s", update_info.m_error_desc.c_str( ) ); util::logger::error("Failed to poll newest version info: %s", update_info.m_error_desc.c_str());
return; return;
} }
if ( update_info.m_version <= UNSPOTIFY_VERSION ) // @note: es3n1n: if we are up2date if (update_info.m_version <= UNSPOTIFY_VERSION) // @note: es3n1n: if we are up2date
return; return;
if ( update_info.m_is_required ) { if (update_info.m_is_required) {
util::logger::fatal( "New version is available!\n\nChangelog:\n%s\nDownload url: %s", update_info.m_changelog.c_str( ), update_info.m_download_url.c_str( ) ); util::logger::fatal("New version is available!\n\nChangelog:\n%s\nDownload url: %s", update_info.m_changelog.c_str(),
return; update_info.m_download_url.c_str());
} return;
}
util::logger::info( "New version is available!" ); util::logger::info("New version is available!");
util::logger::info( "" ); util::logger::info("");
util::logger::info( "" ); util::logger::info("");
util::logger::info( "Changelog:" ); util::logger::info("Changelog:");
printf( "%s\n", update_info.m_changelog.c_str( ) ); printf("%s\n", update_info.m_changelog.c_str());
util::logger::info( "" ); util::logger::info("");
util::logger::info( "" ); util::logger::info("");
util::logger::info( "Download url: %s", update_info.m_download_url.c_str( ) ); util::logger::info("Download url: %s", update_info.m_download_url.c_str());
} }
} } // namespace updates

@ -3,18 +3,17 @@
#include "../util/networking/networking.h" #include "../util/networking/networking.h"
#include "shared/logger.h" #include "shared/logger.h"
namespace updates { namespace updates {
struct update_info_t { struct update_info_t {
bool m_error = false; bool m_error = false;
std::string m_error_desc = "Unable to connect to server"; std::string m_error_desc = "Unable to connect to server";
uint32_t m_version = UNSPOTIFY_VERSION; uint32_t m_version = UNSPOTIFY_VERSION;
std::string m_changelog = "Failed to request update info"; std::string m_changelog = "Failed to request update info";
bool m_is_required = false; bool m_is_required = false;
std::string m_download_url = "https://git.tcp.direct/dg/unspotify"; std::string m_download_url = "https://git.tcp.direct/dg/unspotify";
}; };
update_info_t poll_info( ); update_info_t poll_info();
void do_job( ); void do_job();
} } // namespace updates

@ -1,17 +1,11 @@
#include "detours.h" #include "detours.h"
namespace util::hooking::detour { namespace util::hooking::detour {
bool init( ) { bool init() { return !MH_Initialize(); }
return MH_Initialize( ) == MH_OK;
}
bool create( void* target, void* detour, void** orig ) { bool create(void* target, void* detour, void** orig) {
return MH_CreateHook( target, detour, orig ) == MH_OK && return MH_CreateHook(target, detour, orig) == MH_STATUS::MH_OK && MH_EnableHook(target) == MH_STATUS::MH_OK;
MH_EnableHook( target ) == MH_OK; }
}
bool remove( void* target ) { bool remove(void* target) { return MH_DisableHook(target) == MH_STATUS::MH_OK; }
return MH_DisableHook( target ) == MH_OK; } // namespace util::hooking::detour
}
}

@ -1,11 +1,10 @@
#pragma once #pragma once
#include "mh/minhook.h"
#include "../../mem/addr.h" #include "../../mem/addr.h"
#include "mh/minhook.h"
namespace util::hooking::detour { namespace util::hooking::detour {
bool init( ); bool init();
bool create( void* target, void* detour, void** orig ); bool create(void* target, void* detour, void** orig);
bool remove( void* target = nullptr /* nullptr = MH_ALL_HOOKS */ ); bool remove(void* target = nullptr /* nullptr = MH_ALL_HOOKS */);
} } // namespace util::hooking::detour

@ -1,3 +1,3 @@
#pragma once #pragma once
#include "vmt/vmt.h"
#include "detours/detours.h" #include "detours/detours.h"
#include "vmt/vmt.h"

@ -1,57 +1,55 @@
#include "vmt.h" #include "vmt.h"
namespace util::hooking { namespace util::hooking {
namespace detail { namespace detail {
region_protector::region_protector( void* base, size_t len, std::uint32_t flags ) { region_protector::region_protector(void* base, size_t len, std::uint32_t flags) {
_base = base; _base = base;
_length = len; _length = len;
VirtualProtect( base, len, flags, ( PDWORD )&_old ); VirtualProtect(base, len, flags, (PDWORD)&_old);
} }
region_protector::~region_protector( ) { region_protector::~region_protector() { VirtualProtect(_base, _length, _old, (PDWORD)&_old); }
VirtualProtect( _base, _length, _old, ( PDWORD )&_old ); } // namespace detail
}
}
vmt::vmt( ) : class_base( nullptr ), vftbl_len( 0 ), new_vftbl( nullptr ), old_vftbl( nullptr ) {} vmt::vmt() : class_base(nullptr), vftbl_len(0), new_vftbl(nullptr), old_vftbl(nullptr) { }
vmt::vmt( void* base ) : class_base( base ), vftbl_len( 0 ), new_vftbl( nullptr ), old_vftbl( nullptr ) {} vmt::vmt(void* base) : class_base(base), vftbl_len(0), new_vftbl(nullptr), old_vftbl(nullptr) { }
vmt::~vmt( ) { vmt::~vmt() {
unhook( ); unhook();
delete[ ] new_vftbl; delete[] new_vftbl;
} }
void vmt::setup( void* base ) { void vmt::setup(void* base) {
if ( base != nullptr ) if (base != nullptr)
class_base = base; class_base = base;
old_vftbl = *reinterpret_cast< std::uintptr_t** >( class_base ); old_vftbl = *reinterpret_cast<std::uintptr_t**>(class_base);
vftbl_len = estimate_vftbl_length( old_vftbl ) * sizeof( std::uintptr_t ); vftbl_len = estimate_vftbl_length(old_vftbl) * sizeof(std::uintptr_t);
new_vftbl = new std::uintptr_t[ vftbl_len + 1 ]( ); new_vftbl = new std::uintptr_t[vftbl_len + 1]();
std::memcpy( &new_vftbl[ 1 ], old_vftbl, vftbl_len * sizeof( std::uintptr_t ) ); std::memcpy(&new_vftbl[1], old_vftbl, vftbl_len * sizeof(std::uintptr_t));
auto guard = detail::region_protector { class_base, sizeof( std::uintptr_t ), PAGE_READWRITE }; auto guard = detail::region_protector { class_base, sizeof(std::uintptr_t), PAGE_READWRITE };
new_vftbl[ 0 ] = old_vftbl[ -1 ]; new_vftbl[0] = old_vftbl[-1];
*reinterpret_cast< std::uintptr_t** >( class_base ) = &new_vftbl[ 1 ]; *reinterpret_cast<std::uintptr_t**>(class_base) = &new_vftbl[1];
} }
std::size_t vmt::estimate_vftbl_length( std::uintptr_t* vftbl_start ) { std::size_t vmt::estimate_vftbl_length(std::uintptr_t* vftbl_start) {
MEMORY_BASIC_INFORMATION memInfo = { NULL }; MEMORY_BASIC_INFORMATION memInfo = { NULL };
int m_nSize = -1; int m_nSize = -1;
do { do {
m_nSize++; m_nSize++;
VirtualQuery( reinterpret_cast< LPCVOID >( vftbl_start[ m_nSize ] ), &memInfo, sizeof( memInfo ) ); VirtualQuery(reinterpret_cast<LPCVOID>(vftbl_start[m_nSize]), &memInfo, sizeof(memInfo));
} while ( memInfo.Protect == PAGE_EXECUTE_READ || memInfo.Protect == PAGE_EXECUTE_READWRITE ); } while (memInfo.Protect == PAGE_EXECUTE_READ || memInfo.Protect == PAGE_EXECUTE_READWRITE);
return m_nSize; return m_nSize;
} }
void vmt::unhook( ) { void vmt::unhook() {
if ( old_vftbl == nullptr ) return; if (old_vftbl == nullptr)
return;
auto guard = detail::region_protector { class_base, sizeof( std::uintptr_t ), PAGE_READWRITE }; auto guard = detail::region_protector { class_base, sizeof(std::uintptr_t), PAGE_READWRITE };
*reinterpret_cast< std::uintptr_t** >( class_base ) = old_vftbl; *reinterpret_cast<std::uintptr_t**>(class_base) = old_vftbl;
old_vftbl = nullptr; old_vftbl = nullptr;
} }
} } // namespace util::hooking

@ -4,47 +4,46 @@
#include <cstdint> #include <cstdint>
#include <stdexcept> #include <stdexcept>
namespace util::hooking { namespace util::hooking {
namespace detail { namespace detail {
class region_protector { class region_protector {
public: public:
region_protector( void* base, size_t len, std::uint32_t flags ); region_protector(void* base, size_t len, std::uint32_t flags);
~region_protector( ); ~region_protector();
private: private:
void* _base; void* _base;
size_t _length; size_t _length;
std::uint32_t _old; std::uint32_t _old;
}; };
} } // namespace detail
class vmt { class vmt {
public: public:
vmt( ); vmt();
vmt( void* base ); vmt(void* base);
~vmt( ); ~vmt();
void setup( void* base = nullptr ); void setup(void* base = nullptr);
template<typename T> template <typename T>
void hook( int index, T fun ) { void hook(int index, T fun) {
new_vftbl[ index + 1 ] = reinterpret_cast< std::uintptr_t >( fun ); new_vftbl[index + 1] = reinterpret_cast<std::uintptr_t>(fun);
} }
void unhook( ); void unhook();
template<typename T> template <typename T>
T original( int index ) { T original(int index) {
return reinterpret_cast< T >( old_vftbl[ index ] ); return reinterpret_cast<T>(old_vftbl[index]);
} }
private: private:
static inline std::size_t estimate_vftbl_length( std::uintptr_t* vftbl_start ); static inline std::size_t estimate_vftbl_length(std::uintptr_t* vftbl_start);
void* class_base; void* class_base;
std::size_t vftbl_len; std::size_t vftbl_len;
std::uintptr_t* new_vftbl; std::uintptr_t* new_vftbl;
std::uintptr_t* old_vftbl; std::uintptr_t* old_vftbl;
}; };
} } // namespace util::hooking

@ -1,123 +1,114 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
namespace util::mem { namespace util::mem {
template< typename ptr_type = std::uintptr_t > template <typename ptr_type = std::uintptr_t>
struct memory_address_t { struct memory_address_t {
public: public:
// //
// constructors etc... // constructors etc...
memory_address_t( ) : m_ptr( ptr_type( 0 ) ) { }; memory_address_t() : m_ptr(ptr_type(0)) {};
memory_address_t( ptr_type v ) : m_ptr( v ) { }; memory_address_t(ptr_type v) : m_ptr(v) {};
memory_address_t( void* v ) : m_ptr( ptr_type( v ) ) { }; memory_address_t(void* v) : m_ptr(ptr_type(v)) {};
memory_address_t( const void* v ) : m_ptr( ptr_type( v ) ) { }; memory_address_t(const void* v) : m_ptr(ptr_type(v)) {};
~memory_address_t( ) = default; ~memory_address_t() = default;
// //
// operators // operators
inline operator ptr_type( ) { inline operator ptr_type() { return m_ptr; }
return m_ptr;
}
inline operator void* ( ) { inline operator void*() { return reinterpret_cast<void*>(m_ptr); }
return reinterpret_cast< void* >( m_ptr );
}
inline memory_address_t& operator+=( ptr_type offset ) { inline memory_address_t& operator+=(ptr_type offset) {
m_ptr += offset; m_ptr += offset;
return *this; return *this;
} }
inline memory_address_t& operator-=( ptr_type offset ) { inline memory_address_t& operator-=(ptr_type offset) {
m_ptr -= offset; m_ptr -= offset;
return *this; return *this;
} }
inline memory_address_t operator-( ptr_type offset ) { inline memory_address_t operator-(ptr_type offset) { return memory_address_t<ptr_type>(m_ptr - offset); }
return memory_address_t<ptr_type>( m_ptr - offset );
}
inline memory_address_t operator+( ptr_type offset ) { inline memory_address_t operator+(ptr_type offset) { return add(offset); }
return add( offset );
}
inline bool operator>( ptr_type v2 ) { inline bool operator>(ptr_type v2) { return m_ptr > v2; }
return m_ptr > v2;
}
inline bool operator>=( ptr_type v2 ) { inline bool operator>=(ptr_type v2) { return m_ptr >= v2; }
return m_ptr >= v2;
}
inline bool operator<( ptr_type v2 ) { inline bool operator<(ptr_type v2) { return m_ptr < v2; }
return m_ptr < v2;
}
inline bool operator<=( ptr_type v2 ) { inline bool operator<=(ptr_type v2) { return m_ptr <= v2; }
return m_ptr <= v2;
}
inline memory_address_t add( ptr_type offset ) { inline memory_address_t add(ptr_type offset) { return memory_address_t<ptr_type>(m_ptr + offset); }
return memory_address_t<ptr_type>( m_ptr + offset );
}
template <typename t = uint32_t> template <typename t = uint32_t>
inline memory_address_t rel( ptr_type offset ) { inline memory_address_t rel(ptr_type offset) {
return this->add( this->add( offset ).template read<t>( ) ).add( offset + sizeof( t ) ); return this->add(this->add(offset).template read<t>()).add(offset + sizeof(t));
} }
// //
// utils // utils
memory_address_t<ptr_type> offset( ptr_type off ) { return memory_address_t<ptr_type>( m_ptr + off ); } memory_address_t<ptr_type> offset(ptr_type off) { return memory_address_t<ptr_type>(m_ptr + off); }
template <typename T> template <typename T>
T read( ) { return *ptr<T>( ); } T read() {
return *ptr<T>();
}
template <typename T> template <typename T>
T* read_ptr( ) { return read<T*>( ); } T* read_ptr() {
return read<T*>();
}
template <typename T> template <typename T>
void write( T value ) { *ptr<T>( ) = value; } void write(T value) {
*ptr<T>() = value;
}
template<typename T> template <typename T>
T* ptr( ) { return reinterpret_cast< T* >( m_ptr ); } T* ptr() {
return reinterpret_cast<T*>(m_ptr);
}
template <typename T> template <typename T>
T cast( ) { return reinterpret_cast< T >( m_ptr ); } T cast() {
return reinterpret_cast<T>(m_ptr);
}
memory_address_t<ptr_type>& self_get( ptr_type count = 1 ) { memory_address_t<ptr_type>& self_get(ptr_type count = 1) {
for ( ptr_type i = 0; i < count; i++ ) for (ptr_type i = 0; i < count; i++)
m_ptr = *reinterpret_cast< ptr_type* >( m_ptr ); m_ptr = *reinterpret_cast<ptr_type*>(m_ptr);
return *this; return *this;
} }
memory_address_t<ptr_type> walk_until( uint8_t byte ) { memory_address_t<ptr_type> walk_until(uint8_t byte) {
constexpr int max_iterations = 1000; constexpr int max_iterations = 1000;
for ( int i = 0; i < max_iterations; i++ ) { for (int i = 0; i < max_iterations; i++) {
if ( offset( i ).template read<uint8_t>( ) != byte ) if (offset(i).template read<uint8_t>() != byte)
continue; continue;
return offset( i ); return offset(i);
} }
return memory_address_t<ptr_type>( ); return memory_address_t<ptr_type>();
} }
memory_address_t<ptr_type> walk_back_until( uint8_t byte ) { memory_address_t<ptr_type> walk_back_until(uint8_t byte) {
constexpr int max_iterations = 1000; constexpr int max_iterations = 1000;
for ( int i = 1; i < max_iterations; i++ ) { for (int i = 1; i < max_iterations; i++) {
if ( offset( -i ).template read<uint8_t>( ) != byte ) if (offset(-i).template read<uint8_t>() != byte)
continue; continue;
return offset( -i ); return offset(-i);
} }
return memory_address_t<ptr_type>( ); return memory_address_t<ptr_type>();
} }
inline bool valid( ) { return static_cast< bool >( m_ptr ) && m_ptr > 15; } inline bool valid() { return static_cast<bool>(m_ptr) && m_ptr > 15; }
ptr_type raw( ) { return m_ptr; } ptr_type raw() { return m_ptr; }
private: private:
ptr_type m_ptr; ptr_type m_ptr;
}; };
using addr_t = mem::memory_address_t< std::uintptr_t >; using addr_t = mem::memory_address_t<std::uintptr_t>;
} } // namespace util::mem

@ -1,94 +1,86 @@
#pragma once #pragma once
#include <string>
#include <Windows.h>
#include <vector>
#include "../mem/addr.h" #include "../mem/addr.h"
#include <Windows.h>
#include <string>
#include <vector>
namespace util::mem { namespace util::mem {
struct module_t { struct module_t {
public: public:
// //
// constructors, etc... // constructors, etc...
module_t( ) : m_addr( ) { }; module_t() : m_addr() {};
module_t( uintptr_t s ) : m_addr( s ) { }; module_t(uintptr_t s) : m_addr(s) {};
module_t( const char* module_name ) : m_addr( GetModuleHandleA( module_name ) ) { }; module_t(const char* module_name) : m_addr(GetModuleHandleA(module_name)) {};
~module_t( ) = default; ~module_t() = default;
public: public:
// //
// exports related // exports related
mem::addr_t get_export( const char* name ) { mem::addr_t get_export(const char* name) { return mem::addr_t(reinterpret_cast<void*>(GetProcAddress(m_addr.cast<HMODULE>(), name))); }
return mem::addr_t( reinterpret_cast< void* >( GetProcAddress( m_addr.cast<HMODULE>( ), name ) ) );
}
// //
// pattern scan related // pattern scan related
public: public:
mem::addr_t sig( std::string_view pattern ) { mem::addr_t sig(std::string_view pattern) { return sig(pattern_to_byte(pattern)); }
return sig( pattern_to_byte( pattern ) );
}
mem::addr_t sig( std::vector<int> pattern_bytes ) { mem::addr_t sig(std::vector<int> pattern_bytes) {
unsigned long image_size = get_nt_headers( )->OptionalHeader.SizeOfImage; unsigned long image_size = get_nt_headers()->OptionalHeader.SizeOfImage;
int* pattern_data = pattern_bytes.data( ); int* pattern_data = pattern_bytes.data();
size_t pattern_size = pattern_bytes.size( ); size_t pattern_size = pattern_bytes.size();
for ( unsigned long i = 0ul; i < image_size - pattern_size; i++ ) { for (unsigned long i = 0ul; i < image_size - pattern_size; i++) {
bool found = true; bool found = true;
for ( unsigned long j = 0ul; j < pattern_size; j++ ) { for (unsigned long j = 0ul; j < pattern_size; j++) {
if ( pattern_data[ j ] == -1 ) if (pattern_data[j] == -1)
continue; continue;
if ( m_addr.offset( i + j ).read<std::uint8_t>( ) == pattern_data[ j ] ) if (m_addr.offset(i + j).read<std::uint8_t>() == pattern_data[j])
continue; continue;
found = false; found = false;
break; break;
} }
if ( !found ) if (!found)
continue; continue;
return m_addr.offset( i ); return m_addr.offset(i);
} }
return mem::addr_t( ); return mem::addr_t();
} }
public:
bool safe( std::uintptr_t ptr ) {
return ptr >= m_addr && ptr <= m_addr.add( get_nt_headers( )->OptionalHeader.SizeOfImage );
}
public:
IMAGE_DOS_HEADER* get_dos_headers( ) {
return m_addr.ptr<IMAGE_DOS_HEADER>( );
}
IMAGE_NT_HEADERS* get_nt_headers( ) { public:
return m_addr.offset( get_dos_headers( )->e_lfanew ).ptr< IMAGE_NT_HEADERS >( ); bool safe(std::uintptr_t ptr) { return ptr >= m_addr && ptr <= m_addr.add(get_nt_headers()->OptionalHeader.SizeOfImage); }
}
protected:
std::vector< int > pattern_to_byte( std::string_view pattern ) {
auto bytes = std::vector<int> {};
auto start = const_cast< char* >( pattern.data( ) );
auto end = const_cast< char* >( start ) + pattern.length( );
for ( auto current = start; current < end; ++current ) { public:
if ( *current == '?' ) { IMAGE_DOS_HEADER* get_dos_headers() { return m_addr.ptr<IMAGE_DOS_HEADER>(); }
++current;
if ( *current == '?' ) IMAGE_NT_HEADERS* get_nt_headers() { return m_addr.offset(get_dos_headers()->e_lfanew).ptr<IMAGE_NT_HEADERS>(); }
++current;
bytes.emplace_back( -1 ); protected:
} else std::vector<int> pattern_to_byte(std::string_view pattern) {
bytes.emplace_back( strtoul( current, &current, 16 ) ); auto bytes = std::vector<int> {};
} auto start = const_cast<char*>(pattern.data());
return bytes; auto end = const_cast<char*>(start) + pattern.length();
}
private: for (auto current = start; current < end; ++current) {
mem::addr_t m_addr; if (*current == '?') {
}; ++current;
}
if (*current == '?')
++current;
bytes.emplace_back(-1);
} else
bytes.emplace_back(strtoul(current, &current, 16));
}
return bytes;
}
private:
mem::addr_t m_addr;
};
} // namespace util::mem

@ -1,48 +1,45 @@
#include "networking.h" #include "networking.h"
namespace util { namespace util {
namespace networking { namespace networking {
errorable_json_result get( const char* domain, const char* url ) { errorable_json_result get(const char* domain, const char* url) {
std::string response_data = err_json_data; std::string response_data = err_json_data;
auto internet_session = InternetOpenA( "Unspotify/1.0", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0 ); auto internet_session = InternetOpenA("Unspotify/1.0", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if ( !internet_session ) if (!internet_session)
return { nlohmann::json::parse( response_data ), true }; return { nlohmann::json::parse(response_data), true };
auto http_session = InternetConnectA( internet_session, domain, 80, 0, 0, INTERNET_SERVICE_HTTP, 0, NULL ); auto http_session = InternetConnectA(internet_session, domain, 80, 0, 0, INTERNET_SERVICE_HTTP, 0, NULL);
if ( !http_session ) if (!http_session)
return { nlohmann::json::parse( response_data ), true }; return { nlohmann::json::parse(response_data), true };
HINTERNET http_req = HttpOpenRequestA( http_session, "GET", url, 0, 0, 0, INTERNET_FLAG_RELOAD, 0 ); HINTERNET http_req = HttpOpenRequestA(http_session, "GET", url, 0, 0, 0, INTERNET_FLAG_RELOAD, 0);
if ( !http_session ) if (!http_session)
return nlohmann::json::parse( response_data ); return nlohmann::json::parse(response_data);
const char* szHeaders = "Content-Type: application/json\r\nUser-Agent: Unspotify/1.0"; const char* szHeaders = "Content-Type: application/json\r\nUser-Agent: Unspotify/1.0";
if ( !HttpSendRequestA( http_req, szHeaders, strlen( szHeaders ), NULL, NULL ) ) if (!HttpSendRequestA(http_req, szHeaders, strlen(szHeaders), NULL, NULL))
return { response_data, true }; return { response_data, true };
response_data.clear( ); response_data.clear();
CHAR temp_buffer[ 1024 ] = { 0 }; CHAR temp_buffer[1024] = { 0 };
DWORD read_ret = 0; DWORD read_ret = 0;
while ( InternetReadFile( http_req, temp_buffer, sizeof( temp_buffer ) - 1, &read_ret ) && read_ret ) while (InternetReadFile(http_req, temp_buffer, sizeof(temp_buffer) - 1, &read_ret) && read_ret)
response_data.append( temp_buffer, read_ret ); response_data.append(temp_buffer, read_ret);
InternetCloseHandle( http_req ); InternetCloseHandle(http_req);
InternetCloseHandle( http_session ); InternetCloseHandle(http_session);
InternetCloseHandle( internet_session ); InternetCloseHandle(internet_session);
try { try {
return { nlohmann::json::parse( response_data ), false }; return { nlohmann::json::parse(response_data), false };
} catch ( const nlohmann::json::parse_error& er ) { } catch (const nlohmann::json::parse_error& er) { return { nlohmann::json::parse(err_json_data), true }; }
return { nlohmann::json::parse( err_json_data ), true }; }
} } // namespace networking
} } // namespace util
}
}

@ -1,7 +1,9 @@
#pragma once #pragma once
// clang-format off
#include <Windows.h> #include <Windows.h>
#include <urlmon.h>
#include <WinInet.h> #include <WinInet.h>
#include <urlmon.h>
// clang-format on
#include <map> #include <map>
#include "ext/json.hpp" #include "ext/json.hpp"
@ -9,12 +11,11 @@
#pragma comment(lib, "urlmon.lib") #pragma comment(lib, "urlmon.lib")
#pragma comment(lib, "wininet.lib") #pragma comment(lib, "wininet.lib")
namespace util { namespace util {
namespace networking { namespace networking {
using errorable_json_result = std::pair<nlohmann::json, bool>; using errorable_json_result = std::pair<nlohmann::json, bool>;
constexpr const char* err_json_data = "{\"error\": \"Unable to connect to server\"}"; constexpr const char* err_json_data = "{\"error\": \"Unable to connect to server\"}";
errorable_json_result get( const char* domain, const char* url ); errorable_json_result get(const char* domain, const char* url);
} } // namespace networking
} } // namespace util

@ -1,9 +1,7 @@
#pragma once #pragma once
#include "hooking/hooking.h" #include "hooking/hooking.h"
#include "shared/logger.h"
#include "mem/addr.h" #include "mem/addr.h"
#include "mem/module.h" #include "mem/module.h"
#include "shared/logger.h"
namespace util { }
namespace util {}