有哪些方法可以访问源代码?

时间:2013-04-14 18:11:51

标签: ruby metaprogramming

有几种方法可以从需要/加载该库的Ruby代码中访问库的源代码。在这些方法中,有些人直接读取库文件并解析它。其他人通过一些内置方法访问源代码,这些方法提供有关源的信息(例如抽象语法树)。在我无法直接读取文件内容的情况下(如前面的方式),访问源的唯一方法是访问提供信息的内置方法。通过重新定义这些方法来做其他事情,我将完全失去对源代码的访问权限。最小的方法集是什么,如果我将它们重新定义为其他方法,我将完全失去对外部文件库的源代码的访问权限?

<小时/> 重新解释问题

假设:

  • 有一个用户可以在文件A中编写任何Ruby代码。
  • 我编写了一个静态Ruby文件B,它加载文件A并调用A中定义的主例程,还定义了用户可以在A中使用的一些类/方法。
  • 用户对B没有+ r(读取)或+ w(写入)权限。

我必须通过在文件B中写入来重新定义(无效)或删除哪些(标准Ruby)方法,以使用户无法访问文件B中写入的源(通过用户可以编写的任何代码)在文件A)当我运行文件B?

有一些像sorcerer,pry这样的库可以提取它有权访问的方法的源代码。在纯Ruby中必须有一些原始命令,这些库依赖于这些命令使它们可以访问源代码。有什么方法可以使这种事情成为可能?

如果您不知道完整的答案,但知道某个特定的图书馆如何提取某些方法的来源,那么这仍然会有所帮助。

2 个答案:

答案 0 :(得分:6)

TL; DR :仅限Ruby的解决方案只能使用source_location,因此只需重新定义此项即可返回['/some/empty/file', 1]之类的内容。对解释器的攻击不使用source_location,但您可以阻止/白名单require和朋友阻止使用C扩展。


首先,为了能够执行Ruby脚本,你必须能够阅读它......

但回到这个问题。我知道Sourcify除了在Proc和Method上使用一个名为source_location的方法之外,不使用任何神秘的方法,它给出了文件名和行号是一个方法/ proc定义。我从经验中知道这种方法非常脆弱,需要编写某种解析器,有时只能在合法的情况下工作。所以,如果你在B中重新定义source_location以返回/dev/null, line 0之类的内容并让Sourcify抛出一个非Ruby源异常,那么Sourcify已经出局了。

从Pry的source开始,似乎Pry使用了相同的source_location方法,所以两只一石二鸟。

现在,所有这些库都有另一种选择,即下拉到C并破解解释器以记录源代码。这几乎完美无瑕。但我们仍然可以通过一种非常简单的方式避免危险。有人可以在A中包含Pry方法源的所有代码。但是,如果不需要C库,则不能包含内联C / C扩展。因此,解决方案显而易见:重新定义requirerequire_relative以及load要么不起作用,要么只允许某些库。这样,你可以阻止C黑客攻击。

在MRI上,除了source_location之外,没有办法(来自Ruby代码)来做这件事。你去吧!

编辑:根据@banister,来自MRI 2.0+的内置binding_of_caller方法可以取代源位置。也是这样。 ;)

警告:Ruby不是一个很好的语言。如果你可以对它们进行元编程,除非你处于不同的过程,否则它们可能会对你进行元编程。

答案 1 :(得分:0)

如果您使用John Mair(Pry的制造商)的精彩'method_source'宝石,这实际上非常容易: 该方法必须在Ruby(而不是C)中实现,并且必须从文件(而不是irb)加载。

以下是使用method_source在Rails控制台中显示方法源代码的示例:

  $ rails console
  > require 'method_source'

  # the following prints out the method code for #lookup in the Rails I18n Backend:

  > I18n::Backend::Simple.instance_method(:lookup).source.display
    def lookup(locale, key, scope = [], options = {})
      init_translations unless initialized?
      keys = I18n.normalize_keys(locale, key, scope, options[:separator])

      keys.inject(translations) do |result, _key|
        _key = _key.to_sym
        return nil unless result.is_a?(Hash) && result.has_key?(_key)
        result = result[_key]
        result = resolve(locale, _key, result, options.merge(:scope => nil)) if result.is_a?(Symbol)
        result
      end
    end
    => nil 

显示的Ruby代码需要来自已经加载的文件, (显然它必须是可读的),它需要是原生的Ruby代码(这对编译/链接库不起作用)。

另见: