说我在python解释器中,并定义了如下函数:
def h(a):
return a
如果我想查看字节码(不是使用dis进行反汇编),通常可以使用
h.func_code.co_code
。还有其他方法可以查看字节码吗?这个特定的应用程序与一个自定义的python解释器打包在一起(可能使用py2exe),该解释器删除了对co_code的访问。我不能仅仅查看pyc文件,因为它们已加密。例如,在解释器中,如果我直接输入
h
而不使它成为函数调用,那么我将获得函数的地址。我可以使用该地址获取字节码吗?还有其他方法吗?P.S.当时我这样做的最初目标是使用pyREtic(称为co_code)进行反编译。由于它调用了co_code,因此它将无法正常工作。我想出了一种方法来做,最终将作为答案发布。想看看别人做了什么或想出什么。
#1 楼
首先,仅提醒一下“什么是co_code
”。在Python中,语言的每个元素(函数,方法,类等)都已定义并存储在一个对象中。
co_code
是附加到用于表示函数或方法的类的字段之一。让我们用Python 2.7进行一些练习。$> python2.7
Python 2.7.3 (default, Mar 4 2013, 14:57:34)
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> def foo():
... print('Hello World!')
...
>>> dir(foo.__code__)
['__class__', '__cmp__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__',
'__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__',
'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts',
'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_lnotab', 'co_name',
'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']
>>> foo.__code__.co_code
'd\x01\x00GHd\x00\x00S'
因此,您可以看到
co_code
字段包含我们之前定义的函数的已编译字节码。实际上,似乎co_code
只是一个缓冲区,用于以惰性方式存储已编译的字节码。仅在首次访问时才编译它。假设,
co_code
只是访问字节码的统一助手,该字节码可能以多种形式存储。一种形式是*.pyc
文件,这些文件存储整个文件的已编译Python字节码。另一种形式是函数/方法的即时编译。但是,有一种方法可以直接访问函数/方法的定义,从而直接访问字节码。关键是要用
gdb
拦截Python进程并进行分析。网络上有一些有关此的教程(请参见此处,此处,此处或此处)。但是,这是一个简单的示例(您需要首先安装python-gdb
软件包):$> python2.7-dbg
Python 2.7.3 (default, Mar 4 2013, 14:27:19)
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> def foo():
... print('Hello World!')
...
[40809 refs]
>>> foo
<function foo at 0x1a5e1b0>
[40811 refs]
>>> foo.__code__.co_code
'd\x01\x00GHd\x00\x00S'
[40811 refs]
>>>
[1]+ Stopped python2.7-dbg
然后,您需要获取Python进程的PID并附加
gdb
$ gdb -p 5164
GNU gdb (GDB) 7.4.1-debian
...
Attaching to process 5164
Program received signal SIGTSTP, Stopped (user).
Reading symbols from /usr/bin/python2.7-dbg...done.
Reading symbols from /lib/x86_64-linux-gnu/libpthread.so.0...
Reading symbols from /usr/lib/debug/lib/x86_64-linux-gnu/libpthread-2.13.so...done.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".done.
...
(gdb) print *(PyFunctionObject*)0x1a5e1b0
= {_ob_next = 0x187aca0, _ob_prev = 0x189dd08, ob_refcnt = 2,
ob_type = 0x87ce00, func_code = <code at remote 0x187aca0>,
func_globals = {'__builtins__': <module at remote 0x7f5ebcb5e470>,
'__name__': '__main__', 'foo': <function at remote 0x1a5e1b0>, '__doc__': None,
'__package__': None}, func_defaults = 0x0, func_closure = 0x0, func_doc = None,
func_name = 'foo', func_dict = 0x0, func_weakreflist = 0x0,
func_module = '__main__'}
(gdb) print (*(PyFunctionObject*)0x1a5e1b0)->func_name
= 'foo'
(gdb) print (*(PyCodeObject*)0x187aca0)
= {_ob_next = 0x18983a8, _ob_prev = 0x1a5e1b0, ob_refcnt = 1, ob_type = 0x872680,
co_argcount = 0, co_nlocals = 0, co_stacksize = 1, co_flags = 67,
co_code = 'd\x01\x00GHd\x00\x00S', co_consts = (None, 'Hello World!'),
co_names = (), co_varnames = (), co_freevars = (), co_cellvars = (),
co_filename = '<stdin>', co_name = 'foo', co_firstlineno = 1,
co_lnotab = '\x00\x01', co_zombieframe = 0x0, co_weakreflist = 0x0}
(gdb) print (*(PyCodeObject*)0x187aca0)->co_code
= 'd\x01\x00GHd\x00\x00S'
因此,在给定函数地址的情况下,这是直接访问字节码的方法。
尝试一下为了完整起见,我在Python字节码(以及如何访问它)上找到的最好的文档是Python代码本身,尤其是
inspect
模块(2.7、3.2)。尝试看一下,它很有启发性。您可以使用的另一种帮助是
dis
模块,该模块为Python字节码提供了反汇编程序。这是此反汇编程序的示例。$> python2.7
Python 2.7.3 (default, Mar 4 2013, 14:57:34)
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> def foo():
... print("Hello World!")
...
>>> import dis
>>> dis.dis(foo)
2 0 LOAD_CONST 1 ('Hello World!')
3 PRINT_ITEM
4 PRINT_NEWLINE
5 LOAD_CONST 0 (None)
8 RETURN_VALUE
评论
太酷了!我不是100%肯定这将在我正在处理的特定项目中工作,因为它具有自定义python解释器,我可能无法为其安装gdb绑定。仍然是一项伟大的技术。感谢分享!
–mikeazo
13年4月27日在19:14
#2 楼
我认为恐怖的答案是正确的方法。我想发布我最终为他人着想的方式,以防万一我在对perror的回答的评论中提到的问题是正确的。我现在没有所有注释,如果需要的话会进行更新。我在函数的末尾附近设置了断点,以便创建对象。从那里,我能够查看内存中的对象以提取字节码。要将此信息返回给pyREtic,我将函数名和字节码从GDB中转储到文件中,并修改了pyREtic,就像我说的那样,它已经有一段时间了(stackoverflow问题来自2012年9月),而不是调用co_code来获取字节码,而是从文件中提取字节码。我将回顾一下笔记并填写详细信息。
评论
现在无法访问计算机,但是您是否尝试过研究python源代码以查看co_code的作用?是的,co_code只是一个存储给定函数字节码的缓冲区。