Eval代码解析
تم الإنشاء في: ١٨ يناير ٢٠٢٥
تم الإنشاء في: ١٨ يناير ٢٠٢٥
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
被取出并调用”的意思理解即可。
builtins.dict
这是 Python 内置模块 builtins
中的 dict
类,代表 Python 的字典类型。
'get'(builtins.globals(), 'builtins')
如果我们把它理解为:
get_method = getattr(builtins.dict, 'get') # 相当于拿到了 dict.get 这个函数 result = get_method(builtins.globals(), 'builtins')
builtins.globals()
:理论上这里是想拿到当前全局作用域(一个字典),即 globals()
。builtins.globals()
这种写法(globals()
直接就是内置函数)。dict.get(一个字典, 'builtins')
:从这个全局字典里取出键 'builtins'
对应的值,一般情况下就是 builtins
模块本身。换句话说,这一句的目的是从全局命名空间里找到 builtins
模块对象。
builtins.getattr( …, 'eval')
拿到 builtins
模块后,再通过 getattr(builtins, 'eval')
,就相当于得到了 eval
这个函数的引用。
…(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 表达式/代码)对应的字符串。
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 getattr
和dict.get
,持续 14 秒
首先要说明的是,在标准的 Python 语法中,getattr
的常见用法是:
getattr(obj, name[, default])
obj
和 name
default
,如果提供该参数并且 obj
没有名为 name
的属性,则返回 default
而不是抛出异常。因此,通常 我们会看到两种形式:
getattr(obj, "some_attr")
getattr(obj, "some_attr", default_value)
题目中的代码(带有一些不标准的写法/排版)如下:
builtins.getattr( builtins.dict, 'get'(builtins.globals(), 'builtins'), 'eval' )
直觉上,这好像把“getattr
”的三个形参写成了:
obj = builtins.dict
name = 'get'(builtins.globals(), 'builtins')
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' )
这样就很清晰了:
getattr(builtins.dict, 'get')
就是 dict.get
方法。dict.get(builtins.globals(), 'builtins')
→ 这一步返回的是当前全局变量(globals()
)里名为 'builtins'
的那个值,大多数情况下就是内置模块 builtins
。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
的第二个参数**。换言之,真正的函数调用顺序是:
builtins.getattr(builtins.dict, 'get')
(...)(builtins.globals(), 'builtins')
→ 获得 builtins
模块builtins
模块做 getattr(..., 'eval')
它只是被写成了一种非常混淆的格式,让你一眼看上去好像 getattr
有三个参数分别是 (builtins.dict, 'get'(builtins.globals(), 'builtins'), 'eval')
,从而容易混淆。
getattr
有三个参数,但实际上 'get'(builtins.globals(), 'builtins')
那一坨不是 getattr
的第二实参,而是先取到了 dict.get
并调用返回 builtins
,然后将该 builtins
作为 getattr
的第一个参数,'eval'
才是第二实参。eval
函数。