问题

builtins.getattr(builtins.getattr(builtins.dict,'get'(builtins.golbals(),'builtins'),'eval')(command)是什么意思,详细解释一下

思考

Breaking down the expression

I’m analyzing a Python expression, focusing on dictionary and function calls to eval. The snippet is somewhat unclear but involves obtaining the builtins dictionary and accessing eval from it.

Unraveling obfuscation

This snippet uses Python's education mechanisms to indirectly invoke eval. The goal is to avoid detection, crafting a multi-step approach that cleverly conceals the direct use of eval.

Tracing the evaluation

I’m piecing together a method to trace eval(command) using Python's dictionaries and modules, ultimately retrieving and invoking the eval function from the builtins module.

Thought about Python code evaluation,持续 7 秒

回答

这段代码看起来有点绕,其本质是想通过一系列“间接调用”,最终执行了 eval(command)
下面让我们一步步拆解这行代码(我在此将其稍微排版一下,便于说明):

builtins.getattr( builtins.getattr( builtins.dict, 'get'(builtins.globals(), 'builtins'), 'eval' )(command) )

注意:这段代码里写法有些不规范,尤其在 'get'(builtins.globals(), 'builtins') 这个位置,
实际上我们常见的是 getattr(builtins.dict, 'get')(some_dict, 'some_key') 的形式。
也就是先用 getattr(builtins.dict, 'get') 拿到 dict.get 方法,然后再调用它。
不过为了说明原理,我们先把它当作是“dict.get 被取出并调用”的意思理解即可。


1. builtins.dict

这是 Python 内置模块 builtins 中的 dict 类,代表 Python 的字典类型。


2. 'get'(builtins.globals(), 'builtins')

如果我们把它理解为:

get_method = getattr(builtins.dict, 'get') # 相当于拿到了 dict.get 这个函数 result = get_method(builtins.globals(), 'builtins')
  • builtins.globals():理论上这里是想拿到当前全局作用域(一个字典),即 globals()
    但实际上 Python 并没有 builtins.globals() 这种写法(globals() 直接就是内置函数)。
    可能是某些混淆场景下写法比较怪异,或者依赖特殊的执行环境。
  • dict.get(一个字典, 'builtins'):从这个全局字典里取出键 'builtins' 对应的值,一般情况下就是 builtins 模块本身。

换句话说,这一句的目的是从全局命名空间里找到 builtins 模块对象


3. builtins.getattr( …, 'eval')

拿到 builtins 模块后,再通过 getattr(builtins, 'eval'),就相当于得到了 eval 这个函数的引用。


4. …(command)

最后,对上一步拿到的 eval 函数执行 (command),也就是 eval(command)


整体作用

从上面的分步可以看出,这段“长得很奇怪”的调用链条,本质上就是为了绕过直接写 eval(command),而是通过一大串“取属性”+“取内置字典”+“获取 eval 函数”……最后仍然是执行了 eval(command)

在有些环境下,人们可能会用这种方式绕过一些安全检查(因为直接出现 eval 关键字可能会被检测),或者在某些场景下进行混淆,让阅读者不易一眼看出最终的含义。

总结

builtins.getattr(builtins.getattr(builtins.dict, 'get'(builtins.globals(),'builtins'), 'eval')(command)
就是在比较绕的方式下调用了 eval(command),最终效果是执行 command(当作 Python 表达式/代码)对应的字符串。

问题

builtins.getattr(
builtins.dict,
'get'(builtins.globals(), 'builtins'),
'eval'
)
为什么这里getattr内有三个参数?是怎么一回事?
builtins.dict, 'get'(builtins.globals(), 'builtins'这个直接就能执行得到builtins的操作吗?

思考

Deciphering the code

I’m examining a Python code snippet with getattr that takes three arguments. The syntax 'get'(builtins.globals(), 'builtins') seems unconventional and may involve code obfuscation or a special environment that interprets 'get'(...).

Reframing the context

I'm piecing together that 'get'(builtins.globals(), 'builtins') might imply calling a method, but the standard approach likely involves dict.get on builtins.globals().

Breaking down the syntax

It's worth noting that the confusion arises from calling 'get' as a function. This ambiguity likely stems from an unusual or specialized environment or intended code obfuscation.

Addressing the confusion

The code snippet's motivation likely stems from obfuscation or a specialized environment. 'eval' as the third argument of getattr seems illogical, suggesting a deliberate complexity.

Considering potential errors

It's interesting to see how incorrect formatting could lead to confusion. I’ll avoid assumptions until I understand the intent behind 'get'(builtins.globals(), 'builtins') in context.

Clarifying getattr usage

Breaking down the getattr function, it typically requires two arguments: the object and the attribute name. The third argument is optional, serving as a default value if the attribute is absent.

Thought about getattrdict.get,持续 14 秒

回答

首先要说明的是,在标准的 Python 语法中,getattr 的常见用法是:

getattr(obj, name[, default])
  • 必选参数objname
  • 可选参数default,如果提供该参数并且 obj 没有名为 name 的属性,则返回 default 而不是抛出异常。

因此,通常 我们会看到两种形式:

  1. getattr(obj, "some_attr")
  2. getattr(obj, "some_attr", default_value)

看看题目中这行“怪异”的代码

题目中的代码(带有一些不标准的写法/排版)如下:

builtins.getattr( builtins.dict, 'get'(builtins.globals(), 'builtins'), 'eval' )

直觉上,这好像把“getattr”的三个形参写成了:

  1. obj = builtins.dict
  2. name = 'get'(builtins.globals(), 'builtins')
  3. default = 'eval'

这种写法本身在正常的 Python 里是会出错的——'get'(...) 并不是合法的 Python 调用,因为 'get' 只是个字符串,而字符串不可以直接像函数那样调用(除非经过某些非常规的魔术改造,通常并不存在)。


更合理的解释:排版/拼接时的混淆

更合理的读法应该是先用 getattr(builtins.dict, 'get') 拿到 dict.get 方法,再调用这个方法,然后再把得到的结果作为第一个参数传给下一层 getattr(...)。也就是说,应当是下面这样的结构(我给它加上括号,帮助理解):

builtins.getattr( # ┌────────────────────────┐ # │1. 调用 dict.get(...) │ # │ 并把结果传进来 │ # └────────────────────────┘ builtins.getattr(builtins.dict, 'get')( builtins.globals(), 'builtins' ), # ┌────────────────────────┐ # │2. 要获取的属性名字 'eval' # └────────────────────────┘ 'eval' )

这样就很清晰了:

  1. getattr(builtins.dict, 'get') 就是 dict.get 方法。
  2. 调用 dict.get(builtins.globals(), 'builtins') → 这一步返回的是当前全局变量(globals())里名为 'builtins' 的那个值,大多数情况下就是内置模块 builtins
  3. 接着再调用 builtins.getattr( 这一步返回的对象, 'eval'),也就是 getattr(builtins, 'eval') → 取到内置函数 eval

最后,这段大张旗鼓的代码,其实得到的就是 builtins.eval 这个函数引用


为什么看起来像给 getattr 传了三个参数?

  • 如果你把 'get'(builtins.globals(), 'builtins') 看成整体是字符串 'get'(第二实参)和 '(builtins.globals(), 'builtins')'(好像当成第三实参),那就会产生歧义或语法错误。
  • 但在实际(或说更正确)解析中,'get' 并不是直接传给 getattr,而是先做了 getattr(builtins.dict, 'get'),这会返回一个可调用的函数(即 dict.get),然后再 (builtins.globals(), 'builtins') 调用它,把返回值作为**getattr 的第一个参数**;'eval' 作为**getattr 的第二个参数**。

换言之,真正的函数调用顺序是:

  1. builtins.getattr(builtins.dict, 'get')
  2. 把返回的函数调用:(...)(builtins.globals(), 'builtins') → 获得 builtins 模块
  3. 再对 builtins 模块做 getattr(..., 'eval')

它只是被写成了一种非常混淆的格式,让你一眼看上去好像 getattr 有三个参数分别是 (builtins.dict, 'get'(builtins.globals(), 'builtins'), 'eval'),从而容易混淆。


小结

  • 短答案:表面上像是 getattr 有三个参数,但实际上 'get'(builtins.globals(), 'builtins') 那一坨不是 getattr 的第二实参,而是先取到了 dict.get 并调用返回 builtins,然后将该 builtins 作为 getattr 的第一个参数,'eval' 才是第二实参。
  • 根本原因:这是因为排版/拼接或故意的代码混淆,导致乍一看好像写成了三个参数;可是一旦把调用顺序和括号理顺,真正的语义就很清晰——它只是在间接获取 eval 函数。

分享这个问答