如何使R中无法访问全局环境中用户定义函数的源代码? (用于教育目的)

Posted

技术标签:

【中文标题】如何使R中无法访问全局环境中用户定义函数的源代码? (用于教育目的)【英文标题】:How to make source code of a user-defined function in global environment inaccessible in R? (for education purposes) 【发布时间】:2020-11-05 20:54:57 【问题描述】:

我是一名分子生物学讲师,由于学校已经上线,我不得不想办法继续进行“实验”。我决定在 R 中进行我的“实验”,因为孩子们已经可以使用它了。

我编写了一个函数,它通过使用某些数字来模拟数据,学生将被要求使用输出来计算这些数字。

我计划为它们提供作为全局环境对象的功能。但是,我不希望他们使用getAnywhere() 函数跳转到源代码,并查看那里的数字并简单地复制+粘贴内容。有没有办法让我的用户定义函数的源代码无法访问?我知道隐藏代码并不酷,但为了在线教学,我会这样做。如果没有办法,那我会尝试寻找其他解决方案。

【问题讨论】:

没有办法做到这一点。您可以使获取代码变得更加困难,但始终可以获取在您的 R 会话中运行的任何函数的 R 代码。如果 R 需要运行代码,它需要能够看到代码。一种替代方法是将“私有”逻辑移动到服务器并让程序包对服务器进行 API 调用 我同意 100%。几年前我研究过这个问题,我找到的答案都说“做不到”。可以尝试使它变得晦涩难懂(例如,R6,尽管将其用于此目的严重歪曲了它的意图),但总会有办法查看代码。我最接近这样做的是“将 C/C++ 代码编译成共享对象并链接到它”;这可以使用Rcpp 等,并且只将二进制(非源)包分发给学生。我怀疑这种模糊程度(尽管它并不完美)可能足以满足您的需求。 非常感谢您的回复。我懂了。我会相应地重新设计我的问题。 @r2evans 我认为您可以直接在 R 中获得合理的遮蔽。我认为大多数学生对于如下遮蔽的功能不会走得太远,尽管我从未教过 R 课程 - 也许您想知道这在现实世界中的效果如何? 从“偏执狂”的角度来看,总会有一个学生表现出一点主动性,甚至可能从某人那里得到关于 SO 的建议(作为对问题的回应),然后将其传播给学生。它不会每学期都发生,但是一旦挖出那里,就没有隐藏函数的内容了。 “Gouge”持续存在。话虽如此,我真的对你的回答很感兴趣,它有很多优点!向你致敬! 【参考方案1】:

我想如果这是出于教育目的,那么合理的模糊尝试应该就足够了。这是一个将任何其他函数作为参数并返回具有相同行为但其代码不可读的函数的函数:

obscure <- function(func) 
  f <- as.character(body(func))
  r <- charToRaw(paste(paste(f, collapse = "\n"), "", sep = "\n"))
  bod <- as.call(list(quote(eval), as.call(list(quote(parse), 
                      text = as.call(list(quote(eval), 
                             as.call(list(quote(rawToChar), r))))))))
  as.function(c(formals(func), bod))

所以,假设您想隐藏一个封闭形式的斐波那契生成器:

fib <- function(n) 
  round(((5 + sqrt(5)) / 10) * (( 1 + sqrt(5)) / 2) ** (1:n - 1))


fib(10)
#> [1]  1  1  2  3  5  8 13 21 34 55

您需要做的就是:

fib <- obscure(fib)
fib(10)
#> [1]  1  1  2  3  5  8 13 21 34 55

但是如果你看一下函数本身,它现在看起来像这样:

fib
#> function (n) 
#> eval(parse(text = eval(rawToChar(as.raw(c(0x7b, 0x0a, 0x72, 0x6f, 
#> 0x75, 0x6e, 0x64, 0x28, 0x28, 0x28, 0x35, 0x20, 0x2b, 0x20, 0x73, 
#> 0x71, 0x72, 0x74, 0x28, 0x35, 0x29, 0x29, 0x2f, 0x31, 0x30, 0x29, 
#> 0x20, 0x2a, 0x20, 0x28, 0x28, 0x31, 0x20, 0x2b, 0x20, 0x73, 0x71, 
#> 0x72, 0x74, 0x28, 0x35, 0x29, 0x29, 0x2f, 0x32, 0x29, 0x5e, 0x28, 
#> 0x31, 0x3a, 0x6e, 0x20, 0x2d, 0x20, 0x31, 0x29, 0x29, 0x0a, 0x7d
#> ))))))
#> <environment: 0x0000025b9f7dc068>

当然,高级用户可能会在几分钟内对其进行逆向工程,但如果您正在教授 R 课程,我猜大多数学生会看到这一点并放弃。

【讨论】:

我对此很感兴趣......我认为你是对的,大多数人不知道如何修改这个。虽然你和我都知道如何立即(并且很简单地)扭转它(它需要一个简单的命令),但我认为这将一直有效,直到一个有进取心的学生弄清楚并传出消息。 我用过这个,效果很好!非常感谢你,艾伦。我怎样才能扭转这个顺便说一句?我怎样才能再次看到源代码?我的一个功能不起作用,我现在需要访问源代码,而且在运行此代码之前我没有将其保存在某个地方=)它甚至让我得到了。 啊哈!这是衡量成功的好方法!您可以使用 body(fib) &lt;- eval(body(fib)[[2]]) 反转该示例。显然,只需将 fib 更改为您的函数名称即可。当您将此行保存在某处时告诉我 - 我可能应该删除此评论。 谢谢你,Allan =) 是的,我想这是衡量成功的标准 =) 我们可以保留这些消息。孩子们已经提交了他们的作业,他们很喜欢。再次非常感谢您提供帮助。【参考方案2】:

这是一种很难逆转的方法。它使用walkast package 来混淆函数调用。

本质上,它将f(x) 替换为randomstring &lt;- f; randomNumber randomOperator randomNumber; randomstring(x)

修改runif(1, min = 0, max = 1)中的minmax,使其在你的密码范围内。

obfuscate <- function(f) 
  result <- f
  
  body(result) <- walkast::walk_ast(
    body(result),
    walkast::make_visitor(
      hd = function(f) 
        new_name <- as.name(paste0("x", runif(1, 0, 100000000)))
        decoy_f <- as.name(sample(c("+", "-", "*", "/", "%%", "%*%"), 1))
        bquote(
          .(new_name) <- .(f)
          .(decoy_f)(.(runif(1, min = 0, max = 1)), .(runif(1, min = 0, max = 1)))
          .(new_name)
        )
      
    )
  )
  result



fib <- function(n) 
  round(((5 + sqrt(5)) / 10) * (( 1 + sqrt(5)) / 2) ** (1:n - 1))

obfuscate(fib) 产生以下函数(不难反转)。

function (n) 

    x67911473.8479257 <- ``
    0.130855958675966%%0.881266586482525
    x67911473.8479257
(
    x76250116.8530434 <- round
    0.302100569708273 - 0.405373327899724
    x76250116.8530434
(
    x82210419.5132852 <- `*`
    0.49425036739558 + 0.228636586572975
    x82210419.5132852
(
    x15765817.3236996 <- `(`
    0.797338604461402 %*% 0.514307061443105
    x15765817.3236996
(
    x71525579.597801 <- `/`
    0.909275721525773 %*% 0.446680159773678
    x71525579.597801
(
    x30879483.6513698 <- `(`
    0.383802859345451%%0.83112145261839
    x30879483.6513698
(
    x27604046.7899293 <- `+`
    0.876430705189705/0.856748269638047
    x27604046.7899293
(5, 
    x73314726.5156731 <- sqrt
    0.708666266873479/0.336862851632759
    x73314726.5156731
(5))), 10)), 
    x70590986.6606817 <- `^`
    0.566703328397125%%0.671325634233654
    x70590986.6606817
(
    x24871017.2018036 <- `(`
    0.0474121794104576 + 0.763756504980847
    x24871017.2018036
(
    x88098038.5933071 <- `/`
    0.328246646560729 + 0.514381068525836
    x88098038.5933071
(
    x36233226.7919555 <- `(`
    0.000577503815293312/0.30069664795883
    x36233226.7919555
(
    x96489506.5175369 <- `+`
    0.836315712891519%%0.218373921466991
    x96489506.5175369
(1, 
    x65204835.5899751 <- sqrt
    0.546689067035913/0.838512626476586
    x65204835.5899751
(5))), 2)), 
    x41097439.0804768 <- `(`
    0.318402596283704 + 0.181835192954168
    x41097439.0804768
(
    x14225565.2230233 <- `-`
    0.0201902554836124 - 0.763278516940773
    x14225565.2230233
(
    x67021324.7649372 <- `:`
    0.603728511603549/0.995236924383789
    x67021324.7649372
(1, n), 1))))))

但是,obfuscate 可以被多次调用。 obfuscate(obfuscate(fib)) 结果如下

function (n) 

    x13820952.9686719 <- 
        x14231921.8954071 <- ``
        0.964784309733659 - 0.318950723856688
        x14231921.8954071
    (
        x98483844.473958 <- `<-`
        0.499919829424471 %*% 0.25088473292999
        x98483844.473958
    (x77140931.5057099, ``), 
        x71807632.4323192 <- `+`
        0.704186083516106 + 0.909325393149629
        x71807632.4323192
    (0.508911525830626, 0.712898454861715), x77140931.5057099)
    0.915209622122347 + 0.290264912880957
    x13820952.9686719
(
    x34399270.1265961 <- 
        x98775665.2990356 <- ``
        0.581808406859636 %*% 0.577146121067926
        x98775665.2990356
    (
        x72328119.2127615 <- `<-`
        0.989937041886151 %*% 0.646907166577876
        x72328119.2127615
    (x52481220.1270834, round), 
        x6828759.70378518 <- `%*%`
        0.27078406792134/0.295143382623792
        x6828759.70378518
    (0.801469809608534, 0.987752696499228), x52481220.1270834)
    0.922306241467595 - 0.56200037105009
    x34399270.1265961
(
    x94231348.6244529 <- 
        x57375249.103643 <- ``
        0.72766156680882 * 0.781972301891074
        x57375249.103643
    (
        x3551404.13157642 <- `<-`
        0.0673318353947252%%0.513684156117961
        x3551404.13157642
    (x3026251.47160143, `*`), 
        x12980086.7522135 <- `*`
        0.215703511377797 + 0.520233752438799
        x12980086.7522135
    (0.569129609037191, 0.133742173202336), x3026251.47160143)
    0.510110176866874 - 0.0307286188472062
    x94231348.6244529
(
    x89750883.4721521 <- 
        x600093.929097056 <- ``
        0.321572621352971 %*% 0.0427047829143703
        x600093.929097056
    (
        x71001436.9338751 <- `<-`
        0.771520792506635 %*% 0.772518398938701
        x71001436.9338751
    (x25210925.8202836, `(`), 
        x68944892.520085 <- `*`
        0.268720921361819 %*% 0.425112737575546
        x68944892.520085
    (0.52520202845335, 0.656426891451702), x25210925.8202836)
    0.47237404435873 * 0.495256265625358
    x89750883.4721521
(
    x71292330.3479329 <- 
        x51233950.9557933 <- ``
        0.357416934100911/0.0655053614173084
        x51233950.9557933
    (
        x30669082.3053941 <- `<-`
        0.534916127100587 + 0.67862187908031
        x30669082.3053941
    (x47235390.8233345, `/`), 
        x15582043.2817563 <- `/`
        0.873394214781001 * 0.931123967515305
        x15582043.2817563
    (0.986741927452385, 0.742083500837907), x47235390.8233345)
    0.15898777008988%%0.68169358978048
    x71292330.3479329
(
    x32579387.0491907 <- 
        x73828555.5504262 <- ``
        0.978108135983348 - 0.102359032956883
        x73828555.5504262
    (
        x49107574.8577714 <- `<-`
        0.7187738600187/0.428680357057601
        x49107574.8577714
    (x57762583.6245716, `(`), 
        x57358532.4920714 <- `*`
        0.178639843361452 * 0.673680510604754
        x57358532.4920714
    (0.204503980930895, 0.0359067062381655), x57762583.6245716)
    0.0257267474662513/0.612508951220661
    x32579387.0491907
(
    x96714596.9159901 <- 
        x11374640.2319521 <- ``
        0.0720340947154909 + 0.944227180676535
        x11374640.2319521
    (
        x62696768.6694115 <- `<-`
        0.639268048806116%%0.525764014804736
        x62696768.6694115
    (x76306633.4184259, `+`), 
        x42795905.0051868 <- `+`
        0.94626947841607 + 0.515851546544582
        x42795905.0051868
    (0.904065714450553, 0.583518052240834), x76306633.4184259)
    0.604620382655412 * 0.631076122168452
    x96714596.9159901
(5, 
    x83315562.5732616 <- 
        x83753042.9475009 <- ``
        0.349399645114318 + 0.651053918525577
        x83753042.9475009
    (
        x69223746.0520118 <- `<-`
        0.29312734818086 * 0.881964908912778
        x69223746.0520118
    (x73975120.1821491, sqrt), 
        x67292128.5731718 <- `+`
        0.480338253779337 - 0.482176560908556
        x67292128.5731718
    (0.601392406970263, 0.380848217988387), x73975120.1821491)
    0.0832952118944377 + 0.0914795149583369
    x83315562.5732616
(5))), 10)), 
    x98729057.8894317 <- 
        x77170495.1068386 <- ``
        0.227124285651371/0.278982728952542
        x77170495.1068386
    (
        x48747039.726004 <- `<-`
        0.782126144738868 * 0.675149171613157
        x48747039.726004
    (x89894129.1496158, `^`), 
        x96451648.7671062 <- `%%`
        0.715406887698919 %*% 0.282268565380946
        x96451648.7671062
    (0.27587090106681, 0.145314523251727), x89894129.1496158)
    0.381198771763593%%0.307774103712291
    x98729057.8894317
(
    x97305397.3633796 <- 
        x70868420.926854 <- ``
        0.277484080987051 + 0.973759955028072
        x70868420.926854
    (
        x72545721.4051858 <- `<-`
        0.50800796574913 * 0.856975607108325
        x72545721.4051858
    (x33412416.2793159, `(`), 
        x20543128.5547093 <- `*`
        0.219559877878055%%0.570555842481554
        x20543128.5547093
    (0.106497411616147, 0.345936729339883), x33412416.2793159)
    0.706679665949196 * 0.461514561669901
    x97305397.3633796
(
    x92932011.8622854 <- 
        x25150360.353291 <- ``
        0.945939902681857 + 0.0913955171126872
        x25150360.353291
    (
        x31840611.0163778 <- `<-`
        0.00124536827206612 * 0.692353655351326
        x31840611.0163778
    (x61161323.3806565, `/`), 
        x63229239.1266674 <- `%*%`
        0.56810829625465%%0.350754451937973
        x63229239.1266674
    (0.266102685593069, 0.217837403062731), x61161323.3806565)
    0.544797678478062 * 0.0696846419014037
    x92932011.8622854
(
    x72556719.5564508 <- 
        x2918882.92413205 <- ``
        0.382788195740432%%0.233721876982599
        x2918882.92413205
    (
        x1332410.1222679 <- `<-`
        0.121992226224393 + 0.0637996080331504
        x1332410.1222679
    (x88590273.0282396, `(`), 
        x84656940.9128278 <- `*`
        0.363471970660612 * 0.967681086389348
        x84656940.9128278
    (0.47135023586452, 0.495704435743392), x88590273.0282396)
    0.802222049562261 %*% 0.314430670579895
    x72556719.5564508
(
    x33861630.6427866 <- 
        x7418637.51318306 <- ``
        0.00906742154620588%%0.331271679606289
        x7418637.51318306
    (
        x69295283.2672745 <- `<-`
        0.00934437615796924 %*% 0.161317143123597
        x69295283.2672745
    (x49074813.9331117, `+`), 
        x42538731.6150591 <- `*`
        0.713973646285012/0.187995387706906
        x42538731.6150591
    (0.276253602933139, 0.325099671958014), x49074813.9331117)
    0.527837971923873 * 0.880572498776019
    x33861630.6427866
(1, 
    x77607083.4835991 <- 
        x68655005.4699183 <- ``
        0.858293377095833%%0.16589346411638
        x68655005.4699183
    (
        x20779904.4670537 <- `<-`
        0.0172271258197725 - 0.85583262424916
        x20779904.4670537
    (x63671239.4887581, sqrt), 
        x92613451.6252205 <- `%*%`
        0.921735821058974 + 0.557587723247707
        x92613451.6252205
    (0.53566943667829, 0.865151737350971), x63671239.4887581)
    0.357367471093312 * 0.977310829563066
    x77607083.4835991
(5))), 2)), 
    x48921614.093706 <- 
        x57634989.1722202 <- ``
        0.408984839916229 + 0.219921594019979
        x57634989.1722202
    (
        x20686308.6903468 <- `<-`
        0.87269201874733 * 0.290828781668097
        x20686308.6903468
    (x31356643.6292604, `(`), 
        x54914335.5572596 <- `/`
        0.42553190421313 * 0.873098325682804
        x54914335.5572596
    (0.809229557868093, 0.487828205805272), x31356643.6292604)
    0.903046790510416%%0.475023675011471
    x48921614.093706
(
    x46909810.5793819 <- 
        x71135967.9931775 <- ``
        0.534440700896084 - 0.723820263287053
        x71135967.9931775
    (
        x40692157.0654958 <- `<-`
        0.809898991836235 * 0.154304394498467
        x40692157.0654958
    (x25574955.6235969, `-`), 
        x87320719.3100825 <- `-`
        0.171987064182758 * 0.565342281479388
        x87320719.3100825
    (0.353071014164016, 0.349617956206203), x25574955.6235969)
    0.145128468051553 + 0.605287740007043
    x46909810.5793819
(
    x76092396.4902759 <- 
        x33732652.5477692 <- ``
        0.427568282932043/0.671309909550473
        x33732652.5477692
    (
        x17347284.3598574 <- `<-`
        0.135752162430435%%0.060178006067872
        x17347284.3598574
    (x64530064.5381212, `:`), 
        x49644516.8508217 <- `%*%`
        0.75615629600361 * 0.157231835182756
        x49644516.8508217
    (0.353608228033409, 0.409501505084336), x64530064.5381212)
    0.827440596884117 - 0.0416028220206499
    x76092396.4902759
(1, n), 1))))))

您可以任意多次拨打obfuscate。调用它 4 次会导致超过 4k 行代码

f <- fib
for (i in 1:4) 
  f <- obfuscate(f)

length(readLines(textConnection(as.character(body(f)))))
#> 4354

【讨论】:

不错!我花了一段时间才明白这一点。对一个已经完成了几次的函数进行逆向工程需要一段时间,而且肯定比你给学生的任何实际问题都要难。我认为这可以解决问题 +1 试图破译这将花费他们很多时间,而他们没有(希望如此)。非常感谢你们!现在我可以专注于改进我的研究问题。

以上是关于如何使R中无法访问全局环境中用户定义函数的源代码? (用于教育目的)的主要内容,如果未能解决你的问题,请参考以下文章

要求 r 函数在全局环境中使用对象而不是对象的副本

R编辑在父环境中定义的全局对象

执行环境,作用域,作用域链详解

如何全局访问 AJAX 定义的变量? [复制]

如何将自定义函数加载到 R 中的 foreach 循环中?

R中包中的全局变量