Initial Commit
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
cmake-build-*/*
|
||||||
|
library/lib/*.lib
|
||||||
|
library/include/*.h
|
||||||
10
.idea/.gitignore
generated
vendored
Normal file
10
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# GitHub Copilot persisted chat sessions
|
||||||
|
/copilot/chatSessions
|
||||||
4
.idea/misc.xml
generated
Normal file
4
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||||
|
</project>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/rekordunbox.iml" filepath="$PROJECT_DIR$/.idea/rekordunbox.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
2
.idea/rekordunbox.iml
generated
Normal file
2
.idea/rekordunbox.iml
generated
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module classpath="CMake" type="CPP_MODULE" version="4" />
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
11
CMakeLists.txt
Normal file
11
CMakeLists.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.26)
|
||||||
|
project(rekordunbox)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
|
||||||
|
add_library(library SHARED library/library.cpp library/library.h)
|
||||||
|
target_include_directories(library PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/library/include/MinHook.h")
|
||||||
|
target_link_libraries(library PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/library/lib/libMinHook-x64-v141-md.lib")
|
||||||
|
|
||||||
|
add_executable(application application/application.cpp)
|
||||||
8
README.md
Normal file
8
README.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# RekordUnbox
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. MinHook
|
||||||
|
1. Download [MinHook Release](https://github.com/TsudaKageyu/minhook/releases/latest)
|
||||||
|
2. Extract `MinHook.h` to `library/include`
|
||||||
|
3. Extract `libMinHook-x64-v141-md.lib` to `library/lib`
|
||||||
174
application/application.cpp
Normal file
174
application/application.cpp
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
//
|
||||||
|
// Created by Michael on 25/03/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
|
#include "application.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::string libraryPath = "library.dll";
|
||||||
|
|
||||||
|
// find the "library.dll" either in "./" or in "./lib" or in "./cmake-build-release"
|
||||||
|
std::vector<std::string> libraryPaths = {
|
||||||
|
libraryPath,
|
||||||
|
"lib\\" + libraryPath,
|
||||||
|
"cmake-build-release\\" + libraryPath,
|
||||||
|
"../" + libraryPath,
|
||||||
|
"../lib/" + libraryPath,
|
||||||
|
"../cmake-build-release/" + libraryPath
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string libraryFullPath;
|
||||||
|
for(const std::string& path : libraryPaths) {
|
||||||
|
if(GetFileAttributesA(path.c_str()) != INVALID_FILE_ATTRIBUTES) {
|
||||||
|
libraryFullPath = path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(libraryFullPath.empty()) {
|
||||||
|
printf("Library not found\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// In this directory, find all directories that start with "rekordbox" and end with a number.
|
||||||
|
std::string programPath = R"(C:\Program Files\Pioneer\)";
|
||||||
|
|
||||||
|
// Find all matching directories
|
||||||
|
std::vector<std::string> directories;
|
||||||
|
WIN32_FIND_DATAA findData;
|
||||||
|
HANDLE hFind = FindFirstFileA((programPath + "rekordbox*").c_str(), &findData);
|
||||||
|
if(hFind != INVALID_HANDLE_VALUE) {
|
||||||
|
do {
|
||||||
|
if(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||||
|
std::string directoryName = findData.cFileName;
|
||||||
|
if(directoryName.find("rekordbox") == 0 && isdigit(directoryName.back())) {
|
||||||
|
directories.push_back(directoryName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(FindNextFileA(hFind, &findData));
|
||||||
|
FindClose(hFind);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are no directories, exit
|
||||||
|
if(directories.empty()) {
|
||||||
|
MessageBoxA(NULL, "No rekordbox installation found.", "Error", MB_OK | MB_ICONERROR);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print all directories to stdout
|
||||||
|
|
||||||
|
printf("Found rekordbox installations: \n");
|
||||||
|
|
||||||
|
for(const std::string& directory : directories) {
|
||||||
|
printf(" %s\n", directory.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Out of all the directories, find the latest version
|
||||||
|
// Versions are semver
|
||||||
|
int latestVersion[3] = {0, 0, 0};
|
||||||
|
|
||||||
|
for(const std::string& directory : directories) {
|
||||||
|
// Extract the version from the directory name
|
||||||
|
std::string version_str = directory.substr(sizeof("rekordbox ")-1);
|
||||||
|
|
||||||
|
|
||||||
|
// Split into major, minor, patch
|
||||||
|
int version[3];
|
||||||
|
sscanf_s(version_str.c_str(), "%d.%d.%d", &version[0], &version[1], &version[2]);
|
||||||
|
|
||||||
|
// Compare to latest version
|
||||||
|
// If it's a newer major version, replace
|
||||||
|
if(version[0] > latestVersion[0]) {
|
||||||
|
latestVersion[0] = version[0];
|
||||||
|
latestVersion[1] = version[1];
|
||||||
|
latestVersion[2] = version[2];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's the same major, but newer minor, replace
|
||||||
|
if(version[0] == latestVersion[0] && version[1] > latestVersion[1]) {
|
||||||
|
latestVersion[1] = version[1];
|
||||||
|
latestVersion[2] = version[2];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's the same major and minor, but newer patch, replace
|
||||||
|
if(version[0] == latestVersion[0] && version[1] == latestVersion[1] && version[2] > latestVersion[2]) {
|
||||||
|
latestVersion[2] = version[2];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Convert the latest version back to a string like "1.2.3"
|
||||||
|
std::string latestVersion_str = std::to_string(latestVersion[0]) + "." + std::to_string(latestVersion[1]) + "." + std::to_string(latestVersion[2]);
|
||||||
|
|
||||||
|
|
||||||
|
// Get the full path to the latest version
|
||||||
|
std::string latestVersionPath = programPath + "rekordbox " + latestVersion_str + "\\rekordbox.exe";
|
||||||
|
printf("Choosing latest version (v%s) at: %s\n", latestVersion_str.c_str(), latestVersionPath.c_str());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Start the executable with the library injected
|
||||||
|
PROCESS_INFORMATION pi = { 0 };
|
||||||
|
STARTUPINFOW si = { 0 };
|
||||||
|
si.cb = sizeof( si );
|
||||||
|
|
||||||
|
|
||||||
|
std::wstring latestVersionPathw = std::wstring(latestVersionPath.begin(), latestVersionPath.end());
|
||||||
|
LPCWSTR latestVersionPathW = latestVersionPathw.c_str();
|
||||||
|
|
||||||
|
|
||||||
|
DWORD flags = CREATE_SUSPENDED;
|
||||||
|
|
||||||
|
if (!CreateProcessW(latestVersionPathW, nullptr, nullptr, nullptr, FALSE, flags, nullptr, nullptr, &si, &pi )) {
|
||||||
|
printf("Failed to start rekordbox\n");
|
||||||
|
// Get error
|
||||||
|
DWORD error = GetLastError();
|
||||||
|
LPSTR messageBuffer = nullptr;
|
||||||
|
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
|
||||||
|
printf("Error %lu: %s\n", error, messageBuffer);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the stdout of the process the same as the current stdout
|
||||||
|
|
||||||
|
|
||||||
|
LPVOID loc = VirtualAllocEx(pi.hProcess, nullptr, libraryFullPath.size() + 1, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||||
|
BOOL result = WriteProcessMemory(pi.hProcess, loc, libraryFullPath.c_str(), libraryFullPath.size() + 1, nullptr);
|
||||||
|
|
||||||
|
if(!result) {
|
||||||
|
printf("Failed to inject DLL. Couldn't write process memory\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE hThread = CreateRemoteThread(pi.hProcess, nullptr, 0, (LPTHREAD_START_ROUTINE) LoadLibraryA, loc, 0, nullptr);
|
||||||
|
|
||||||
|
if(hThread == nullptr) {
|
||||||
|
printf("Failed to inject DLL. Couldn't create remote thread\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResumeThread(pi.hThread);
|
||||||
|
|
||||||
|
printf("Started rekordbox, goodbye!\n");
|
||||||
|
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
CloseHandle(pi.hProcess);
|
||||||
|
CloseHandle(pi.hThread);
|
||||||
|
|
||||||
|
|
||||||
|
// Sleep for 5 seconds
|
||||||
|
Sleep(5000);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
11
application/application.h
Normal file
11
application/application.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
//
|
||||||
|
// Created by Michael on 25/03/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef REKORDUNBOX_APPLICATION_H
|
||||||
|
#define REKORDUNBOX_APPLICATION_H
|
||||||
|
|
||||||
|
int main();
|
||||||
|
|
||||||
|
|
||||||
|
#endif //REKORDUNBOX_APPLICATION_H
|
||||||
0
library/include/.gitkeep
Normal file
0
library/include/.gitkeep
Normal file
0
library/lib/.gitkeep
Normal file
0
library/lib/.gitkeep
Normal file
142
library/library.cpp
Normal file
142
library/library.cpp
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "library.h"
|
||||||
|
#include "include/MinHook.h"
|
||||||
|
|
||||||
|
// f3 0f 11 8f 98 0b 00 00
|
||||||
|
|
||||||
|
byte orig_place[] = {0xf3, 0x0f, 0x11, 0x8f, 0x98, 0x0b, 0x00, 0x00};
|
||||||
|
|
||||||
|
byte* rekordbox_base;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct ChannelInfo {
|
||||||
|
byte unk[288];
|
||||||
|
double bpm;
|
||||||
|
UINT64 unk2[2];
|
||||||
|
double secondsElapsed;
|
||||||
|
UINT64 unk3;
|
||||||
|
double beat; // 328 + 8 = 336
|
||||||
|
double beatRounded; // 336 + 8 = 344
|
||||||
|
byte unk4[76];
|
||||||
|
byte isPlaying;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef INT64(*updateChannelInfo)(ChannelInfo* channelInfo);
|
||||||
|
updateChannelInfo pUpdateChannelInfo = nullptr;
|
||||||
|
|
||||||
|
UINT64 count = 0;
|
||||||
|
BOOL shouldPrint = false;
|
||||||
|
INT64 detourUpdateChannelInfo(ChannelInfo* channelInfo) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if(shouldPrint && count % 101 == 0) {
|
||||||
|
printf("Channel: 0x%llx\n", (UINT64)channelInfo);
|
||||||
|
printf("BPM: %f\n", channelInfo->bpm );
|
||||||
|
printf("Time: %f\n", channelInfo->secondsElapsed );
|
||||||
|
printf("Beat: %f\n", channelInfo->beat );
|
||||||
|
printf("~Beat: %f\n", channelInfo->beatRounded );
|
||||||
|
printf("RelBeat: %i/4\n", ((int)channelInfo->beatRounded) % 4);
|
||||||
|
printf("RelBeatF: %f\n", (channelInfo->beat - channelInfo->beatRounded) + ((int)channelInfo->beatRounded) % 4 );
|
||||||
|
printf("isPlaying: %i\n", channelInfo->isPlaying );
|
||||||
|
printf("--------------------\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
INT64 result = pUpdateChannelInfo(channelInfo);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Hook SyncManager? function
|
||||||
|
typedef UINT64(*syncManager)(UINT64* param1);
|
||||||
|
syncManager pSyncManager = nullptr;
|
||||||
|
|
||||||
|
ChannelInfo* master = nullptr;
|
||||||
|
|
||||||
|
UINT64 detourSyncManager(UINT64* param1) {
|
||||||
|
auto new_master = (ChannelInfo*)param1[0x1e];
|
||||||
|
|
||||||
|
if(master != new_master && new_master != nullptr) {
|
||||||
|
master = new_master;
|
||||||
|
printf("--------------------\n");
|
||||||
|
printf("New master: %llx\n", (UINT64)master);
|
||||||
|
printf("--------------------\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT64 result = pSyncManager(param1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) {
|
||||||
|
if (dwReason == DLL_PROCESS_ATTACH) {
|
||||||
|
DisableThreadLibraryCalls(hModule); //disables attach and detach notifications
|
||||||
|
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE) entry, NULL, NULL, NULL); //creates a new thread and starts at function entry()
|
||||||
|
}
|
||||||
|
else if (dwReason == DLL_PROCESS_DETACH) {
|
||||||
|
printf("Unloading RekordUnBox\n");
|
||||||
|
MH_Uninitialize();
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void entry() {
|
||||||
|
AllocConsole();
|
||||||
|
FILE* fp;
|
||||||
|
freopen_s(&fp, "CONOUT$", "w", stdout);
|
||||||
|
|
||||||
|
rekordbox_base = (byte*) GetModuleHandleA("rekordbox.exe");
|
||||||
|
|
||||||
|
printf("Injected RekordUnBox\n");\
|
||||||
|
printf("Rekordbox Base: 0x%ux\n", (unsigned long)rekordbox_base);
|
||||||
|
|
||||||
|
// Print 8 bytes at rekordbox.exe+0x193c8b0
|
||||||
|
byte* updateChannelInfo_location = (byte*)(rekordbox_base + 0x1acd290);
|
||||||
|
|
||||||
|
if (MH_Initialize() != MH_OK) {
|
||||||
|
printf("Failed to initialize MinHook\n");
|
||||||
|
fclose(fp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Successfully initialized MinHook\n");
|
||||||
|
|
||||||
|
|
||||||
|
MH_STATUS status;
|
||||||
|
|
||||||
|
// Update loop
|
||||||
|
status = MH_CreateHook(updateChannelInfo_location, &detourUpdateChannelInfo, reinterpret_cast<void**>(&pUpdateChannelInfo));
|
||||||
|
if(status != MH_OK) {
|
||||||
|
printf("Error: Couldn't hook updateChannelInfo!\nError: %s", MH_StatusToString(status));
|
||||||
|
fclose(fp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyncManager
|
||||||
|
status = MH_CreateHook((byte*)(rekordbox_base + 0x18f7630), &detourSyncManager, reinterpret_cast<void**>(&pSyncManager));
|
||||||
|
if(status != MH_OK) {
|
||||||
|
printf("Error: Couldn't hook syncManager!\nError: %s", MH_StatusToString(status));
|
||||||
|
fclose(fp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(MH_EnableHook(MH_ALL_HOOKS) != MH_OK) {
|
||||||
|
printf("Error: Couldn't enable hooks!\n");
|
||||||
|
fclose(fp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (master != nullptr) {
|
||||||
|
printf("Master: %f\n", master->beat);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sleep(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
6
library/library.h
Normal file
6
library/library.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef REKORDUNBOX_LIBRARY_H
|
||||||
|
#define REKORDUNBOX_LIBRARY_H
|
||||||
|
|
||||||
|
void entry();
|
||||||
|
|
||||||
|
#endif //REKORDUNBOX_LIBRARY_H
|
||||||
Reference in New Issue
Block a user