Skip to content

Instantly share code, notes, and snippets.

@Sleepingwell
Last active August 29, 2015 13:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Sleepingwell/8928074 to your computer and use it in GitHub Desktop.
Save Sleepingwell/8928074 to your computer and use it in GitHub Desktop.
An example how to make R code hard to find using C.
#include <R.h>
#include <Rinternals.h>
#include <R_ext/Parse.h>
/**
* A very simple example of hiding R code via C.
*
* To compile this, you can type:
*
* R CMD SHLIB -o deep_thought.dll deep_thought.c
*
* at a console in the same directory as you have saved this file,
* then from R you can load it with:
*
* >> dyn.load("deep_thought")
*
* You would then call the functions as described in the comments
* below.
*
* To use this method in practice, you would include the first
* function ('run_r_code') in your source file, then implement
* methods similar to "the_meaning_of_life" and
* "the_meaning_of_multiple_lives" that contain the R code you
* want hidden.
*
* Note that this is not very efficient, because the R code is
* parsed every time the function is called. One could avoid this
* by making the function once then saving that in one of the
* fields of an external pointer (see writing R extensions
* section 5.13), perhaps wrapped in a C struct. This would make
* it opaque to the interepreter (hence the hiding). A clever
* programmer would then be able to 'chase down' the R code,
* which I have not tried, but it would (probably) take some
* effort... well, at least more effort than opening the shared
* object in a text editor and searching for the stirng
* "function(", which will give you the code anyway.
*
* To avoid that problem, you would need to get hookey with how
* you build the string containing the functions code in order
* to obfuscate it sufficiently , or encrypt the strings and
* have a clever way of hiding the keys... good luck with that.
*/
/**
* This is the function that parses and evaluates the R code. It is
* meant to be used by functions like "the_meaning_of_life" and
* "the_meaning_of_multiple_lives" and cannot (well, at least,
* should not) be called from R directly.
*
* Plese see the functions "the_meaning_of_life" and
* "the_meaning_of_multiple_lives" below and their comments for
* examples of how to use it.
*/
static SEXP run_r_code(char const* rawCode, SEXP args) {
SEXP code, codeExpr, func, args2, args2_tmp, rho, res;
ParseStatus status;
// the R character vector to contain our code
PROTECT(code = allocVector(STRSXP, 1));
// put the raw code in the r character vector
SET_STRING_ELT(code, 0, mkChar(rawCode));
// create the R expression containing our function
codeExpr = PROTECT(R_ParseVector(code, -1, &status, R_NilValue));
if (status != PARSE_OK) {
// then our R code is broken
UNPROTECT(2);
error("invalid R code");
}
if(length(codeExpr) != 1) {
// then we evaluted more than just a function
UNPROTECT(2);
error("to much R code (more than just one expression)");
}
// make the function by evaluating the code in the GobalEnv
func = eval(VECTOR_ELT(codeExpr, 0), R_GlobalEnv);
// check that it is a function we just parsed
if(!isFunction(func)) error("expression was not a function");
// skip the name
args = CDR(args);
// build the list of args to make the call
PROTECT(args2 = allocList(length(args)));
SET_TYPEOF(args2, LANGSXP);
SETCAR(args2, func);
if(length(args) > 0) {
args2_tmp = CDR(args2);
for(int i=0; i<length(args)-1; ++i) {
SETCAR(args2_tmp, CAR(args));
args2_tmp = CDR(args2_tmp);
args = CDR(args);
}
}
// extract the environment to execute in and check that it is valid.
rho = CAR(args);
if(!isEnvironment(rho)) error("’rho’ should be an environment");
PROTECT(res = eval(args2, rho));
UNPROTECT(4);
return res;
}
/**
* This is en example a function that hides interpreted R code.
* It must be called using .External from R, and the last argument
* must be an environment.
*
* For example, one would call this from R like:
*
* >> .External('the_meaning_of_life', environment())
*
* to evaluate a function in the environment from which the above
* call as made.
*
* Of course, it is probably easier to make an R wrapper for this,
* which might look like:
*
* >> the_meaning_of_life <- function() .External('the_meaning_of_life', environment())
*
* and would be called from R like:
*
* >> theAnswer <- the_meaning_of_life()
*
*/
SEXP the_meaning_of_life(SEXP args) {
static char const* rawCode = "function() 42.0";
return run_r_code(rawCode, args);
}
/**
* This is en example how a function that hides interpreted R code.
* It must be called using .External from R, and the last argument
* must be an environment.
*
* For example, one would call this from R like:
*
* >> .External('the_meaning_of_multiple_lives', 2, environment())
*
* to evaluate a function in the environment from which the above
* call as made.
*
* Of course, it is probably easier to make an R wrapper for this,
* which might look like:
*
* >> the_meaning_of_multiple_lives <- function(nLives) .External('the_meaning_of_multiple_lives', nLives, environment())
*
* and would be called from R like:
*
* >> theAnswer <- the_meaning_of_multiple_lives(10)
*/
SEXP the_meaning_of_multiple_lives(SEXP args) {
static char const* rawCode = "function(multiple) multiple*42.0";
return run_r_code(rawCode, args);
}
setwd("c:/dir/containing/compiled/binary")
dyn.load("deep_thought")
.External('the_meaning_of_life', environment())
.External('the_meaning_of_multiple_lives', 42, environment())
dyn.unload("deep_thought")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment