Last active
May 1, 2022 13:16
-
-
Save cfillion/6b9633b6375bbcefac30613338d3f520 to your computer and use it in GitHub Desktop.
ReaScript IDE reload prompt bypass https://i.imgur.com/kY34oAN.gif
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// c++ -fPIC -O2 -std=c++14 -DSWELL_PROVIDED_BY_APP -IWDL -IWDL/WDL -dynamiclib reaper_idenoreload.cpp WDL/WDL/swell/swell-modstub.mm -lc++ -framework AppKit -o reaper_ideautoreload.dylib | |
// | |
// cl /nologo /O2 reaper_idenoreload.cpp User32.lib /link /OPT:REF /PDBALTPATH:%_PDB% /DLL /OUT:reaper_ideautoreload.dll | |
#ifndef _WIN32 | |
# include <sys/mman.h> | |
# include <WDL/wdltypes.h> | |
#endif | |
#include <cstdint> | |
#define REAPERAPI_IMPLEMENT | |
#include "reaper_plugin_functions.h" | |
constexpr int RELOAD_TIMER { 3 }; | |
static WNDPROC g_ideProc; | |
static unsigned int g_mode; | |
template<typename T> | |
bool patch(const uintptr_t addr, const T newCode, T *backup = nullptr) | |
{ | |
uint8_t *firstByte { reinterpret_cast<uint8_t *>(addr) }; | |
if(backup) | |
memcpy(backup, firstByte, sizeof(T)); | |
#ifdef _WIN32 | |
DWORD oldProtect; | |
if(!VirtualProtect(firstByte, sizeof(T), PAGE_READWRITE, &oldProtect)) | |
return false; | |
#else | |
const auto pageSize { sysconf(_SC_PAGE_SIZE) }; | |
void *pageStart { reinterpret_cast<void *>(addr - (addr % pageSize)) }; | |
if(mprotect(pageStart, pageSize, PROT_READ | PROT_WRITE)) | |
return false; | |
#endif | |
memcpy(firstByte, &newCode, sizeof(T)); | |
#ifdef _WIN32 | |
VirtualProtect(firstByte, sizeof(T), oldProtect, &oldProtect); | |
#else | |
mprotect(pageStart, pageSize, PROT_READ | PROT_EXEC); | |
#endif | |
return true; | |
} | |
#ifdef _WIN32 | |
# pragma pack(push, 1) | |
# define packed | |
#else | |
# define packed __attribute__((packed)) | |
#endif | |
struct packed Jump { | |
#if defined(__x86_64__) || defined(_WIN64) | |
// uint8_t int3 = 0xcc; | |
uint8_t mov = 0x48, reg = 0xb8; uint64_t dest; // mov rax, dest | |
uint8_t jmp = 0xff, modr_m = 0xe0; // jmp rax | |
#else | |
# error Unimplemented architecture | |
#endif | |
}; | |
#ifdef _WIN32 | |
# pragma pack(pop) | |
#endif | |
class Redirect { | |
public: | |
Redirect(const uintptr_t funcAddr, const uintptr_t redirect) | |
: m_funcAddr { funcAddr } | |
{ | |
Jump jump; | |
jump.dest = redirect; | |
patch(m_funcAddr, jump, &m_code); | |
} | |
~Redirect() | |
{ | |
patch(m_funcAddr, m_code); | |
} | |
private: | |
uintptr_t m_funcAddr; | |
Jump m_code; | |
}; | |
static bool updateMode() | |
{ | |
// FIXME: UTF-8 support on Windows | |
g_mode = GetPrivateProfileInt("reascriptedit", "autoreload", 1, get_ini_file()); | |
return g_mode < 2; | |
} | |
static int WINAPI fakePrompt(HWND, const char *, const char *, const unsigned int) | |
{ | |
return g_mode == 0 ? IDNO : IDYES; | |
} | |
static LRESULT CALLBACK bypassReloadProc(HWND hwnd, UINT msg, | |
WPARAM wParam, LPARAM lParam) | |
{ | |
if(msg != WM_TIMER || wParam != RELOAD_TIMER || !updateMode()) | |
return g_ideProc(hwnd, msg, wParam, lParam); | |
#ifdef _WIN32 | |
Redirect mba { reinterpret_cast<uintptr_t>(&MessageBoxA), | |
reinterpret_cast<uintptr_t>(&fakePrompt) }, | |
mbw { reinterpret_cast<uintptr_t>(&MessageBoxW), | |
reinterpret_cast<uintptr_t>(&fakePrompt) }; | |
#else // function pointer under SWELL | |
Redirect mb { reinterpret_cast<uintptr_t>(MessageBox), | |
reinterpret_cast<uintptr_t>(&fakePrompt) }; | |
#endif | |
return g_ideProc(hwnd, msg, wParam, lParam); | |
} | |
static void installBypassProc(HWND hwnd) | |
{ | |
LONG_PTR newProc { reinterpret_cast<LONG_PTR>(&bypassReloadProc) }, | |
oldProc { SetWindowLongPtr(hwnd, GWLP_WNDPROC, newProc) }; | |
if(oldProc != newProc) | |
g_ideProc = reinterpret_cast<WNDPROC>(oldProc); | |
} | |
static BOOL CALLBACK findIde(HWND hwnd, LPARAM) | |
{ | |
char title[1024]; | |
GetWindowText(hwnd, title, sizeof(title)); | |
// FIXME: localize title string | |
if(strstr(title, "ReaScript Development Environment")) | |
installBypassProc(hwnd); | |
return TRUE; | |
} | |
static void pollIdeWindows() | |
{ | |
#ifdef _WIN32 | |
EnumThreadWindows(GetCurrentThreadId(), &findIde, 0); | |
#else | |
EnumWindows(&findIde, 0); | |
#endif | |
} | |
extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT( | |
REAPER_PLUGIN_HINSTANCE instance, reaper_plugin_info_t *rec) | |
{ | |
if(!rec || rec->caller_version != REAPER_PLUGIN_VERSION) | |
return 0; | |
get_ini_file = reinterpret_cast<decltype(get_ini_file )>(rec->GetFunc("get_ini_file")); | |
plugin_register = reinterpret_cast<decltype(plugin_register)>(rec->GetFunc("plugin_register")); | |
plugin_register("timer", reinterpret_cast<void *>(&pollIdeWindows)); | |
return 1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment