Last active
August 29, 2015 13:56
-
-
Save Sleepingwell/8928074 to your computer and use it in GitHub Desktop.
An example how to make R code hard to find using C.
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
#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); | |
} |
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
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