Skip to content

Instantly share code, notes, and snippets.

@ess7
Last active November 6, 2021 20:19
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ess7/6c75c111194a9159d05472aadc956c01 to your computer and use it in GitHub Desktop.
Save ess7/6c75c111194a9159d05472aadc956c01 to your computer and use it in GitHub Desktop.
Call C function from Reaper JSFX (x86/x64, no hardcoded offsets, 6.x tested)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <reaper_plugin.h>
#include <ns-eel.h>
/*
* If you actually try this, the JS effect that uses the new function should not be the first one loaded,
* otherwise you will get an "undefined 'myadd'" error.
* Workarounds: recompile/reset or add any JS effect before this
*/
/*
* Copyright (C) 2008 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* This uses the "Not So Naive" algorithm, a very simple but
* usually effective algorithm, see:
* http://www-igm.univ-mlv.fr/~lecroq/string/
*/
#include <string.h>
void *memmem(const void *haystack, size_t n, const void *needle, size_t m)
{
if (m > n || !m || !n)
return NULL;
if (__builtin_expect((m > 1), 1)) {
const unsigned char* y = (const unsigned char*) haystack;
const unsigned char* x = (const unsigned char*) needle;
size_t j = 0;
size_t k = 1, l = 2;
if (x[0] == x[1]) {
k = 2;
l = 1;
}
while (j <= n-m) {
if (x[1] != y[j+1]) {
j += k;
} else {
if (!memcmp(x+2, y+j+2, m-2) && x[0] == y[j])
return (void*) &y[j];
j += l;
}
}
} else {
/* degenerate case */
return (void *)memchr(haystack, ((unsigned char*)needle)[0], n);
}
return NULL;
}
// https://github.com/ess7/reaper-resampler-plugin/blob/fbf641222551cff56459e3ca0aa99fdbef0b86e0/plugin.cpp#L92
static bool getTextVAAndSize(HMODULE hmod, PVOID *textVA, SIZE_T *textSize) {
const ULONG_PTR imageBase = (ULONG_PTR)hmod;
const PIMAGE_DOS_HEADER imageDosHeader = (PIMAGE_DOS_HEADER)imageBase;
if (imageDosHeader->e_magic != 0x5a4d) {
return false;
}
const PIMAGE_NT_HEADERS imageNTHeader = (PIMAGE_NT_HEADERS)(imageBase + imageDosHeader->e_lfanew);
if (imageNTHeader->Signature != 0x4550) {
return false;
}
const PIMAGE_FILE_HEADER imageFileHeader = &imageNTHeader->FileHeader;
PIMAGE_SECTION_HEADER imageSectionHeader = (PIMAGE_SECTION_HEADER)
((ULONG_PTR)imageFileHeader + sizeof(IMAGE_FILE_HEADER) + imageFileHeader->SizeOfOptionalHeader);
for (int i = 0; i < imageFileHeader->NumberOfSections; i++, imageSectionHeader++) {
if (strcmp((char *)imageSectionHeader->Name, ".text") == 0) {
*textVA = (PVOID)(imageBase + imageSectionHeader->VirtualAddress);
*textSize = imageSectionHeader->Misc.VirtualSize;
return true;
}
}
return false;
}
typedef void (*pNSEEL_addfunc_ret_type_t)(const char *, int, int, NSEEL_PPPROC, void *, eel_function_table *);
static pNSEEL_addfunc_ret_type_t pNSEEL_addfunc_ret_type;
static void NSEEL_addfunc_ret_type(const char *name, int np, int ret_type, NSEEL_PPPROC pproc, void *fptr, eel_function_table *destination) {
pNSEEL_addfunc_ret_type(name, np, ret_type, pproc, fptr, destination);
}
static NSEEL_PPPROC NSEEL_PProc_THIS;
// the function to be called from jsfx
static EEL_F NSEEL_CGEN_CALL add(void *opaque, EEL_F *a, EEL_F *b) {
return *a + *b;
}
static void *(*JesusonicAPI_createInstance_orig)(uintptr_t, uintptr_t, uintptr_t);
static void *JesusonicAPI_createInstance(uintptr_t arg1, uintptr_t arg2, uintptr_t arg3) {
void *ret = JesusonicAPI_createInstance_orig(arg1, arg2, arg3);
// register our function as "myadd"
NSEEL_addfunc_retval("myadd", 2, NSEEL_PProc_THIS, add);
return ret;
}
extern "C" {
REAPER_PLUGIN_DLL_EXPORT
int REAPER_PLUGIN_ENTRYPOINT(REAPER_PLUGIN_HINSTANCE hInstance, reaper_plugin_info_t *rec) {
if (rec == NULL) { return 0; }
// get the NSEEL function addresses from reaper.exe
pNSEEL_addfunc_ret_type = (pNSEEL_addfunc_ret_type_t)rec->GetFunc("NSEEL_addfunc_ret_type");
NSEEL_PProc_THIS = (NSEEL_PPPROC) rec->GetFunc("NSEEL_PProc_THIS");
if (pNSEEL_addfunc_ret_type == NULL) { return 0; }
if (NSEEL_PProc_THIS == NULL) { return 0; }
// search for same code in jsfx.dll
HMODULE jsfx_base = GetModuleHandle("jsfx.dll");
if (jsfx_base == NULL) { return 0; }
PVOID jsfx_text;
SIZE_T jsfx_text_size;
if (!getTextVAAndSize(jsfx_base, &jsfx_text, &jsfx_text_size)) { return 0; }
const bool x64 =
#ifdef _WIN64
true;
#else
false;
#endif
pNSEEL_addfunc_ret_type = (pNSEEL_addfunc_ret_type_t)memmem(jsfx_text, jsfx_text_size, (void *)pNSEEL_addfunc_ret_type, x64 ? 14 : 18);
NSEEL_PProc_THIS = (NSEEL_PPPROC) memmem(jsfx_text, jsfx_text_size, (void *)NSEEL_PProc_THIS, x64 ? 15 : 24);
if (pNSEEL_addfunc_ret_type == NULL) { return 0; }
if (NSEEL_PProc_THIS == NULL) { return 0; }
// hook JesusonicAPI createInstance
void **JesusonicAPI = (void **)GetProcAddress(jsfx_base, "JesusonicAPI");
if (JesusonicAPI == NULL) { return 0; }
JesusonicAPI_createInstance_orig = (void *(*)(uintptr_t, uintptr_t, uintptr_t))JesusonicAPI[1];
JesusonicAPI[1] = (void *)JesusonicAPI_createInstance;
return 1;
}
}
@GavinRay97
Copy link

GavinRay97 commented Nov 6, 2021

Heya, it's "gxray" from Cockos forums =D
You're an absolute legend.

I will see if I can use the Ghidra API to do write a script for calculation of the memmem(..., ..., size_t l) arg, seems like a great way to learn more about this. It's fascinating.

Do you have recommendations for helpful plugins?

Previously I have used these:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment