gpt4 book ai didi

python - 运行时修改函数(拉出局部变量)

转载 作者:行者123 更新时间:2023-11-28 21:42:45 24 4
gpt4 key购买 nike

想象一下这个简单的函数创建变量 default 的修改值,modified:

default = 0
def modify():
modified = default + 1
print(modified) # replace with OS call, I can't see the output

modify() # 1
default # 0

反汇编:

import dis
dis.dis(modify)
2 0 LOAD_GLOBAL 0 (default)
3 LOAD_CONST 1 (1)
6 BINARY_ADD
7 STORE_FAST 0 (modified)
3 10 LOAD_GLOBAL 1 (print)
13 LOAD_FAST 0 (modified)
16 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
19 POP_TOP
20 LOAD_CONST 0 (None)
23 RETURN_VALUE

我无法更改函数 modify(),但我知道其中有什么直接(我可以看到代码)或间接(反汇编)。我需要的是获取 modified 变量的值,所以我可能有一种方法可以删除函数的特定部分 (print(modified))通过 dis 模块,但我没有找到任何东西。

有什么方法可以删除 16 CALL_FUNCTION 之后除了 return_value 之外的所有内容,并将其替换为例如返回修改?或者有没有其他方法可以在不实际执行最后一行的情况下提取局部变量?

作为一种可能的解决方案,我看到了 3 种方式:

  • 拉取反汇编代码并根据它们创建我自己的函数(或就地),删除我不想要的代码(16 ... 之后的所有内容)
  • 修改函数的return 值,使其返回modified(不幸的是调用了操作系统函数)
  • 根据源代码手动重新创建函数

我想避免第二种方式,这可能比第一种方式更容易,但我必须避免第三种方式,所以...有什么办法可以解决我的问题吗?

最佳答案

还有第四个选项:替换 print() 全局:

printed = []
print = lambda *args: printed.extend(args)
modify()
del print
modified = printed[0]

否则可能会生成修改后的字节码,但这很容易导致导致解释器崩溃的错误(对无效字节码的保护为零),因此请注意。

您可以使用更新后的字节码的新代码对象创建一个新的函数对象;根据您显示的 dis 中的偏移量,我手动创建了新的字节码,它将返回索引 0 处的局部变量:

>>> altered_bytecode = modify.__code__.co_code[:8] + bytes(
... [dis.opmap['LOAD_FAST'], 0, # load local variable 0 onto the stack
... dis.opmap['RETURN_VALUE']])) # and return it.
>>> dis.dis(altered_bytecode)
0 LOAD_GLOBAL 0 (0)
2 LOAD_CONST 1 (1)
4 BINARY_ADD
6 STORE_FAST 0 (0)
8 LOAD_FAST 0 (0)
10 RETURN_VALUE

RETURN_VALUE 返回栈顶的对象;我所做的只是注入(inject)一个 LOAD_FAST 操作码,以将 modified 引用加载到堆栈上。

你必须创建一个新的 code 对象,然后是一个新的 function 对象来包装代码对象,以使其可调用:

>>> code = type(modify.__code__)
>>> function = type(modify)
>>> ocode = modify.__code__
>>> new_modify = function(
... code(ocode.co_argcount, ocode.co_kwonlyargcount, ocode.co_nlocals, ocode.co_stacksize,
... ocode.co_flags, altered_bytecode,
... ocode.co_consts, ocode.co_names, ocode.co_varnames, ocode.co_filename,
... 'new_modify', ocode.co_firstlineno, ocode.co_lnotab, ocode.co_freevars,
... ocode.co_cellvars),
... modify.__globals__, 'new_modify', modify.__defaults__, modify.__closure__)
>>> new_modify()
1

显然,这确实需要首先了解 Python 字节码的工作原理; dis 模块包含各种代码的描述,dis.opmap dictionary让您映射回字节值。

有一些模块试图使这更容易;看看byteplay , bytecode module of the pwnypack project或者其他几个,如果你想进一步探索这个。

我也可以衷心推荐您观看 Playing with Python Bytecode presentation由 Scott Sanderson、Joe Jevnik 在 PyCon 2016 上给出,并使用他们的 codetransformer module .极具娱乐性和信息量。

关于python - 运行时修改函数(拉出局部变量),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43185829/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com