2015-10-26 13 views
6

Tôi đã tự hỏi làm thế nào để sửa đổi mã byte, sau đó biên dịch lại mã đó để tôi có thể sử dụng nó trong python như một hàm? Tôi đã cố gắng:sửa đổi bytecode python

a = """ 
def fact(): 
    a = 8 
    a = 0 
""" 
c = compile(a, '<string>', 'exec') 
w = c.co_consts[0].co_code 
dis(w) 

mà dịch ngược để:

 0 LOAD_CONST   1 (1) 
     3 STORE_FAST   1 (1) 
     6 LOAD_CONST   2 (2) 
     9 STORE_FAST   1 (1) 
    12 LOAD_CONST   0 (0) 
    15 RETURN_VALUE 

giả sử tôi muốn thoát khỏi dòng 0 và 3, tôi gọi:

x = c.co_consts[0].co_code[6:16] 
dis(x) 

mà kết quả trong:

 0 LOAD_CONST   2 (2) 
     3 STORE_FAST   1 (1) 
     6 LOAD_CONST   0 (0) 
     9 RETURN_VALUE 

vấn đề của tôi là gì để làm với x, nếu tôi thử exec x Tôi nhận được 'chuỗi dự kiến ​​không có nullbyte và tôi nhận được như vậy cho exec w, cố gắng biên dịch x kết quả bằng: biên dịch() chuỗi dự kiến ​​không có byte rỗng.

Tôi không chắc chắn cách tốt nhất để tiến hành, ngoại trừ có thể tôi cần tạo một loại đối tượng mã, nhưng tôi không chắc chắn như thế nào, nhưng tôi cho rằng nó phải là có thể được gọi là byteplay, python assemblers et al

Tôi đang sử dụng trăn 2.7.10, nhưng tôi muốn nó tương thích trong tương lai (Ví dụ: python 3) nếu có thể.

+0

Tôi tò mò muốn biết tại sao bạn làm điều này, trường hợp sử dụng của bạn là gì? – shuttle87

+1

Mã byte bytecode được coi là chi tiết triển khai và có thể thay đổi từ phiên bản sang phiên bản và thông dịch viên thành thông dịch viên. Không có tài liệu nào ngoại trừ nguồn thông dịch viên. Bạn có chắc chắn muốn làm điều này? – Antimony

+0

@ shuttle87, tôi đang viết một công cụ trò chơi với các tập lệnh mà tôi hy vọng sẽ viết bằng python, nhưng tôi muốn chạy một số tối ưu trên mã bytecode mà nói lỏng lẻo sẽ không được biết trước (mặc dù cấu trúc cơ bản sẽ được biết đến như tất cả họ đều chia sẻ một lớp cơ sở chung). Tôi đã có tất cả các thành phần khác làm việc, chỉ cần chuyển đổi bytecode này thành một hàm có thể sử dụng là rào cản cuối cùng. –

Trả lời

6

Cập nhật: Vì lý do lặt vặt, tôi đã bắt đầu viết một trình tạo phiên bản Cross-Python. Xem https://github.com/rocky/python-xasm Nó vẫn còn trong bản beta rất sớm.

Theo như tôi biết, không có hiện đang được duy trì Trình kết hợp Python. PEAK's Bytecode Disassembler được phát triển cho Python 2.6 và sau đó được sửa đổi để hỗ trợ Python phiên bản 2.7.

Nó khá tuyệt vời từ documentation. Nhưng nó dựa vào các thư viện PEAK khác có thể có vấn đề.

Tôi sẽ xem xét toàn bộ ví dụ để cho bạn cảm nhận về những gì bạn phải làm. Nó không phải là đẹp, nhưng sau đó bạn nên mong đợi điều đó.

Về cơ bản sau khi sửa đổi mã byte, bạn cần phải tạo đối tượng types.CodeType mới. Bạn cần một cái mới vì nhiều đối tượng trong loại mã, vì lý do chính đáng, bạn không thể thay đổi. Ví dụ, thông dịch viên có thể có một số giá trị đối tượng được lưu trong bộ nhớ cache.

Sau khi tạo mã, bạn có thể sử dụng mã này trong các hàm sử dụng loại mã có thể được sử dụng trong exec hoặc eval.

Hoặc bạn có thể ghi tệp này vào tệp bytecode. Alas định dạng mã đã thay đổi giữa Python 2 và Python 3. Và bằng cách này, có tối ưu hóa và bytecodes. Thực tế, trong Python 3.6, chúng sẽ là từ mã không phải bytecode.

Vì vậy, đây là những gì bạn phải làm ví dụ của bạn:

a = """ 
def fact(): 
    a = 8 
    a = 0 
    return a 
""" 
c = compile(a, '<string>', 'exec') 
fn_code = c.co_consts[0] # Pick up the function code from the main code 
from dis import dis 
dis(fn_code) 
print("=" * 30) 

x = fn_code.co_code[6:16] # modify bytecode 

import types 
opt_fn_code = types.CodeType(fn_code.co_argcount, 
          # c.co_kwonlyargcount, Add this in Python3 
          fn_code.co_nlocals, 
          fn_code.co_stacksize, 
          fn_code.co_flags, 
          x, # fn_code.co_code: this you changed 
          fn_code.co_consts, 
          fn_code.co_names, 
          fn_code.co_varnames, 
          fn_code.co_filename, 
          fn_code.co_name, 
          fn_code.co_firstlineno, 
          fn_code.co_lnotab, # In general, You should adjust this 
          fn_code.co_freevars, 
          fn_code.co_cellvars) 
dis(opt_fn_code) 
print("=" * 30) 
print("Result is", eval(opt_fn_code)) 

# Now let's change the value of what's returned 
co_consts = list(opt_fn_code.co_consts) 
co_consts[-1] = 10 

opt_fn_code = types.CodeType(fn_code.co_argcount, 
          # c.co_kwonlyargcount, Add this in Python3 
          fn_code.co_nlocals, 
          fn_code.co_stacksize, 
          fn_code.co_flags, 
          x, # fn_code.co_code: this you changed 
          tuple(co_consts), # this is now changed too 
          fn_code.co_names, 
          fn_code.co_varnames, 
          fn_code.co_filename, 
          fn_code.co_name, 
          fn_code.co_firstlineno, 
          fn_code.co_lnotab, # In general, You should adjust this 
          fn_code.co_freevars, 
          fn_code.co_cellvars) 

dis(opt_fn_code) 
print("=" * 30) 
print("Result is now", eval(opt_fn_code)) 

Khi tôi chạy đây đây là những gì tôi nhận:

3   0 LOAD_CONST    1 (8) 
       3 STORE_FAST    0 (a) 

    4   6 LOAD_CONST    2 (0) 
       9 STORE_FAST    0 (a) 

    5   12 LOAD_FAST    0 (a) 
      15 RETURN_VALUE 
============================== 
    3   0 LOAD_CONST    2 (0) 
       3 STORE_FAST    0 (a) 

    4   6 LOAD_FAST    0 (a) 
       9 RETURN_VALUE 
============================== 
('Result is', 0) 
    3   0 LOAD_CONST    2 (10) 
       3 STORE_FAST    0 (a) 

    4   6 LOAD_FAST    0 (a) 
       9 RETURN_VALUE 
============================== 
('Result is now', 10) 

Chú ý rằng số dòng có không đã thay đổi mặc dù tôi đã xóa mã bằng một vài dòng. Đó là vì tôi không cập nhật fn_code.co_lnotab.

Nếu bây giờ bạn muốn viết một tệp bytecode Python từ này. Dưới đây là những gì bạn muốn làm:

co_consts = list(c.co_consts) 
co_consts[0] = opt_fn_code 
c1 = types.CodeType(c.co_argcount, 
        # c.co_kwonlyargcount, Add this in Python3 
        c.co_nlocals, 
        c.co_stacksize, 
        c.co_flags, 
        c.co_code, 
        tuple(co_consts), 
        c.co_names, 
        c.co_varnames, 
        c.co_filename, 
        c.co_name, 
        c.co_firstlineno, 
        c.co_lnotab, # In general, You should adjust this 
        c.co_freevars, 
        c.co_cellvars) 

from struct import pack 
with open('/tmp/testing.pyc', 'w') as fp: 
     fp.write(pack('Hcc', 62211, '\r', '\n')) # Python 2.7 magic number 
     import time 
     fp.write(pack('I', int(time.time()))) 
     # In Python 3 you need to write out the size mod 2**32 here 
     import marshal 
     fp.write(marshal.dumps(c1)) 

Để đơn giản hóa bằng văn bản cho bytecode soạn sẵn ở trên, tôi đã thêm một thói quen để xdis gọi write_python_file().

Bây giờ để kiểm tra kết quả:

$ uncompyle6 /tmp/testing.pyc 
# uncompyle6 version 2.9.2 
# Python bytecode 2.7 (62211) 
# Disassembled from: Python 2.7.12 (default, Jul 26 2016, 22:53:31) 
# [GCC 5.4.0 20160609] 
# Embedded file name: <string> 
# Compiled at: 2016-10-18 05:52:13 


def fact(): 
    a = 0 
# okay decompiling /tmp/testing.pyc 
$ pydisasm /tmp/testing.pyc 
# pydisasm version 3.1.0 
# Python bytecode 2.7 (62211) disassembled from Python 2.7 
# Timestamp in code: 2016-10-18 05:52:13 
# Method Name:  <module> 
# Filename:   <string> 
# Argument count: 0 
# Number of locals: 0 
# Stack size:  1 
# Flags:    0x00000040 (NOFREE) 
# Constants: 
# 0: <code object fact at 0x7f815843e4b0, file "<string>", line 2> 
# 1: None 
# Names: 
# 0: fact 
    2   0 LOAD_CONST    0 (<code object fact at 0x7f815843e4b0, file "<string>", line 2>) 
       3 MAKE_FUNCTION   0 
       6 STORE_NAME    0 (fact) 
       9 LOAD_CONST    1 (None) 
      12 RETURN_VALUE 


# Method Name:  fact 
# Filename:   <string> 
# Argument count: 0 
# Number of locals: 1 
# Stack size:  1 
# Flags:    0x00000043 (NOFREE | NEWLOCALS | OPTIMIZED) 
# Constants: 
# 0: None 
# 1: 8 
# 2: 10 
# Local variables: 
# 0: a 
    3   0 LOAD_CONST    2 (10) 
       3 STORE_FAST    0 (a) 

    4   6 LOAD_CONST    0 (None) 
       9 RETURN_VALUE 
$ 

Một cách tiếp cận thay thế cho tối ưu hóa là để tối ưu hóa ở mức Abstract Syntax Tree (AST). Tuy nhiên tôi không chắc chắn bạn có thể thực hiện trực tiếp hoặc. Một số thói quen mô-đun dường như gợi ý rằng bạn có thể. Hoặc cách bạn tạo một tệp bytecode từ một AST. Vì vậy, tôi giả sử bạn viết nó trở lại như là nguồn Python.

Các vấn đề liên quan