2015-10-26 27 views
6

Bayt kodunun nasıl değiştirileceğini merak ettim, sonra bu kodu yeniden derleyip python'da bir işlev olarak kullanabilirim? Ben çalışıyorum: için decompilespython bayt kodunu değiştiriyor

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

: Ben hatları 0 ve 3 kurtulmak istiyorum varsayarak

 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 

, arayacağım: sonuçlanır

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

:

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

Sorunum nedir? x ile yapmak için exec x deneyin, nullbayt olmadan beklenen bir dize alıyorum ve exec w, için x sonuçları derlemeye çalışıyorsunuz: compile() null bayt olmadan beklenen dize.

Devam etmek için en iyi yolun ne olduğundan emin değilim, belki de bir çeşit kod nesnesi oluşturmam gerekiyor, ama emin değilim, ama sanırım bunun olası bir bayt olması gerekir. python assemblers et al

Python 2.7.10 kullanıyorum, ancak gelecekteki uyumlu olması (örneğin python 3) olmasını istiyorum.

+0

Bunu neden yaptığınızı merak ediyorum, kullanım durumunuz nedir? – shuttle87

+1

Python bytecode bir uygulama detayı olarak kabul edilir ve versiyondan sürüme ve tercümana tercüman olarak değişebilir. Tercüman kaynağı dışında dokümantasyon yoktur. Bunu yapmak istediğine emin misin? – Antimony

+0

@ shuttle87, Ben python yazmayı umuyoruz komut dosyaları ile bir oyun motoru yazıyorum, ama ben gevşek konuşma önceden bilinmeyecek betik bytecode bazı optimizasyonlar çalıştırmak istiyorum (her ne kadar temel yapısı olacak rağmen hepsinin ortak bir temel sınıfı paylaştığı bilinmelidir). Diğer tüm bileşenleri çalışıyorum, sadece bu bytecode'u kullanılabilir bir işleve dönüştürmek son engel. –

cevap

6

Güncelleme: Çeşitli nedenlerden ötürü bir Cross-Python sürümü derleyicisini yazmaya başladım. https://github.com/rocky/python-xasm'a bakın. Hala çok erken beta.

Bildiğim kadarıyla şu anda bakımsız Python assembler var. PEAK's Bytecode Disassembler, Python 2.6 için geliştirilmiş ve daha sonra Python 2.7'in erken dönemini desteklemek üzere değiştirilmiştir.

documentation'dan oldukça havalıdır. Ancak sorun yaratabilecek diğer PEAK kütüphanelerine dayanır.

Yapacağınız şey için size bir fikir vermek için tüm örneği gözden geçireceğim. Güzel değil, ama sonra bunu beklemelisin.

Temel olarak bayt kodunu değiştirdikten sonra, yeni bir types.CodeType nesnesi oluşturmanız gerekir. Yeni bir tanesine ihtiyacınız var çünkü kod türündeki birçok nesne, iyi bir sebepten dolayı değişemezsiniz. Örneğin yorumcunun önbelleğe alınmış bu nesne değerlerinden bazıları olabilir.

Kod oluşturduktan sonra, exec veya eval'da kullanılabilen bir kod türü kullanan işlevlerde kullanabilirsiniz.

Veya bunu bir bytecode dosyasına yazabilirsiniz. Ne yazık ki Python 2 ve Python 3 arasında kod formatı değişmiştir. Bu arada optimizasyon ve bayt kodları da vardır. Aslında, Python 3.6'da kelime kodları bayt kodları olmayacaktır.Satır numaralarını yapmadıysanız

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) 

Bildirim o: Bu buraya sığındılar

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)) 

ne var: Yani burada

Eğer örneğin yapmak zorunda kalacak ne kodda birkaç satır kaldırılmış olsa bile değişti. Bunun nedeni, fn_code.co_lnotab'u güncellemedim.

Bundan böyle bir Python bayt kodu dosyası yazmak istiyorsanız. Burada ne yaparım geçerli:

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)) 

yukarıdaki Demirbaş baytkodu, ben xdis denilen write_python_file() için bir rutin ekledik yazma basitleştirmek için. Şimdi

sonuçları kontrol etmek:

$ 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 
$ 

optimizasyonu için alternatif bir yaklaşım Abstract Syntax Tree seviyesinde (AST) de optimize etmektir. Ancak, bunu doğrudan uygulayacağınızdan da emin değilim. Bazı modül rutinleri yapabildiğinizi öneriyor gibi görünüyor. Ya da bir AST'den nasıl bir bytecode dosyası oluşturursunuz. Bu yüzden bunu Python kaynağı olarak yazdığınızı varsayalım.