Last active
November 6, 2021 20:19
-
-
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)
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
#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; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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:
https://github.com/astrelsky/Ghidra-Cpp-Class-Analyzer
https://github.com/tacnetsol/ghidra_scripts/blob/master/readmes/rizzo.md
reaper.exe
and importing intojsfx.dll
analysis