2015-09-14 28 views
6

Ho un file .pyc. Ho bisogno di capire il contenuto di quel file per sapere come funziona il disassemblatore di python, cioè come posso generare un output come dis.dis(function) dal contenuto del file .pyc.Come posso capire un contenuto di file .pyc

per es.

>>> def sqr(x): 
...  return x*x 
... 
>>> import dis 
>>> dis.dis(sqr) 
    2   0 LOAD_FAST    0 (x) 
       3 LOAD_FAST    0 (x) 
       6 BINARY_MULTIPLY  
       7 RETURN_VALUE   

ho bisogno di ottenere un output come questo utilizzando il file .pyc.

risposta

14

.pyc i file contengono alcuni metadati e un oggetto marshaledcode; per caricare l'oggetto code e smontare tale uso:

import dis, marshal, sys 

# Header size changed in 3.3. It might change again, but as of this writing, it hasn't. 
header_size = 12 if sys.version_info >= (3, 3) else 8 

with open(pycfile, "rb") as f: 
    magic_and_timestamp = f.read(header_size) # first 8 or 12 bytes are metadata 
    code = marshal.load(f)      # rest is a marshalled code object 

dis.dis(code) 

demo con il modulo bisect:

>>> import bisect 
>>> import dis, marshal 
>>> import sys 
>>> header_size = 12 if sys.version_info >= (3, 3) else 8 
>>> with open(bisect.__file__, "rb") as f: 
...  magic_and_timestamp = f.read(header_size) # first 8 or 12 bytes are metadata 
...  code = marshal.load(f)      # rest is bytecode 
... 
>>> dis.dis(code) 
    1   0 LOAD_CONST    0 ('Bisection algorithms.') 
       3 STORE_NAME    0 (__doc__) 

    3   6 LOAD_CONST    1 (0) 
       9 LOAD_CONST    8 (None) 
      12 LOAD_CONST    2 (<code object insort_right at 0x106a459b0, file "/Users/mpieters/Development/Library/buildout.python/parts/opt/lib/python2.7/bisect.py", line 3>) 
      15 MAKE_FUNCTION   2 
      18 STORE_NAME    2 (insort_right) 

22   21 LOAD_NAME    2 (insort_right) 
      24 STORE_NAME    3 (insort) 

24   27 LOAD_CONST    1 (0) 
      30 LOAD_CONST    8 (None) 
      33 LOAD_CONST    3 (<code object bisect_right at 0x106a45ab0, file "/Users/mpieters/Development/Library/buildout.python/parts/opt/lib/python2.7/bisect.py", line 24>) 
      36 MAKE_FUNCTION   2 
      39 STORE_NAME    4 (bisect_right) 

45   42 LOAD_NAME    4 (bisect_right) 
      45 STORE_NAME    5 (bisect) 

47   48 LOAD_CONST    1 (0) 
      51 LOAD_CONST    8 (None) 
      54 LOAD_CONST    4 (<code object insort_left at 0x106a45bb0, file "/Users/mpieters/Development/Library/buildout.python/parts/opt/lib/python2.7/bisect.py", line 47>) 
      57 MAKE_FUNCTION   2 
      60 STORE_NAME    6 (insort_left) 

67   63 LOAD_CONST    1 (0) 
      66 LOAD_CONST    8 (None) 
      69 LOAD_CONST    5 (<code object bisect_left at 0x106a45cb0, file "/Users/mpieters/Development/Library/buildout.python/parts/opt/lib/python2.7/bisect.py", line 67>) 
      72 MAKE_FUNCTION   2 
      75 STORE_NAME    7 (bisect_left) 

89   78 SETUP_EXCEPT   14 (to 95) 

90   81 LOAD_CONST    6 (-1) 
      84 LOAD_CONST    7 (('*',)) 
      87 IMPORT_NAME    8 (_bisect) 
      90 IMPORT_STAR   
      91 POP_BLOCK   
      92 JUMP_FORWARD   17 (to 112) 

91  >> 95 DUP_TOP    
      96 LOAD_NAME    9 (ImportError) 
      99 COMPARE_OP    10 (exception match) 
      102 POP_JUMP_IF_FALSE  111 
      105 POP_TOP    
      106 POP_TOP    
      107 POP_TOP    

92   108 JUMP_FORWARD    1 (to 112) 
     >> 111 END_FINALLY   
     >> 112 LOAD_CONST    8 (None) 
      115 RETURN_VALUE   

noti che questo è solo il codice oggetto livello superiore, definendo il modulo. Se si desidera analizzare le funzioni contenute, è necessario caricare gli oggetti nidificati code, dall'array code.co_consts di livello superiore; per esempio, oggetto codice della funzione insort_right è caricato con LOAD_CONST 2, in modo da cercare il codice oggetto in tale indice:

>>> code.co_consts[2] 
<code object insort_right at 0x106a459b0, file "/Users/mpieters/Development/Library/buildout.python/parts/opt/lib/python2.7/bisect.py", line 3> 
>>> dis.dis(code.co_consts[2]) 
12   0 LOAD_FAST    2 (lo) 
       3 LOAD_CONST    1 (0) 
       6 COMPARE_OP    0 (<) 
       9 POP_JUMP_IF_FALSE  27 

13   12 LOAD_GLOBAL    0 (ValueError) 
      15 LOAD_CONST    2 ('lo must be non-negative') 
      18 CALL_FUNCTION   1 
      21 RAISE_VARARGS   1 
      24 JUMP_FORWARD    0 (to 27) 

14  >> 27 LOAD_FAST    3 (hi) 
      30 LOAD_CONST    5 (None) 
      33 COMPARE_OP    8 (is) 
      36 POP_JUMP_IF_FALSE  54 

15   39 LOAD_GLOBAL    2 (len) 
      42 LOAD_FAST    0 (a) 
      45 CALL_FUNCTION   1 
      48 STORE_FAST    3 (hi) 
      51 JUMP_FORWARD    0 (to 54) 

16  >> 54 SETUP_LOOP    65 (to 122) 
     >> 57 LOAD_FAST    2 (lo) 
      60 LOAD_FAST    3 (hi) 
      63 COMPARE_OP    0 (<) 
      66 POP_JUMP_IF_FALSE  121 

17   69 LOAD_FAST    2 (lo) 
      72 LOAD_FAST    3 (hi) 
      75 BINARY_ADD   
      76 LOAD_CONST    3 (2) 
      79 BINARY_FLOOR_DIVIDE 
      80 STORE_FAST    4 (mid) 

18   83 LOAD_FAST    1 (x) 
      86 LOAD_FAST    0 (a) 
      89 LOAD_FAST    4 (mid) 
      92 BINARY_SUBSCR  
      93 COMPARE_OP    0 (<) 
      96 POP_JUMP_IF_FALSE  108 
      99 LOAD_FAST    4 (mid) 
      102 STORE_FAST    3 (hi) 
      105 JUMP_ABSOLUTE   57 

19  >> 108 LOAD_FAST    4 (mid) 
      111 LOAD_CONST    4 (1) 
      114 BINARY_ADD   
      115 STORE_FAST    2 (lo) 
      118 JUMP_ABSOLUTE   57 
     >> 121 POP_BLOCK   

20  >> 122 LOAD_FAST    0 (a) 
      125 LOAD_ATTR    3 (insert) 
      128 LOAD_FAST    2 (lo) 
      131 LOAD_FAST    1 (x) 
      134 CALL_FUNCTION   2 
      137 POP_TOP    
      138 LOAD_CONST    5 (None) 
      141 RETURN_VALUE   

Personalmente mi evitare cercando di analizzare il file .pyc con qualcosa di diverso rispetto alla versione corrispondente di Python e il modulo marshal. Il formato marshal è fondamentalmente un formato di serializzazione interno che cambia con le esigenze di Python stesso. Le nuove funzionalità come le list comprehensions e le dichiarazioni with e async/await richiedono nuove aggiunte al formato, che non viene pubblicato diverso da C source code.

Se si percorre questo percorso e si arriva a read a code object con altri mezzi rispetto all'utilizzo del modulo, è necessario analizzare lo smontaggio dai vari attributi dell'oggetto codice; vedere dis module source per i dettagli su come eseguire questa operazione (sarà necessario utilizzare gli attributi co_firstlineno e co_lnotab per creare una mappa bytecode-offset-to-linenumber, ad esempio).

+0

il mio scopo è scrivere un programma in python che prenda un file .pyc e generi output come un modulo dis.dis() senza usare il modulo 'dis'. Così come posso capire l'inizio di un codice, numero di riga ecc. da un file .pyc? –

+1

C'è un breve post sul blog ['formato file pyc'] (http://nedbatchelder.com/blog/200804/the_structure_of_pyc_files.html). Ma sono sicuro di capirli tutti completamente. Devi tuffarti nella fonte di Python (che ha molta buona documentazione in quest'area). – HelloWorld

+1

@NiyaSimonC: vuoi dire che vuoi analizzare il file '.pyc' da zero? Ciò richiede l'analisi del formato 'marshal', per vedere [come codifica un oggetto' code'] (https://hg.python.org/cpython/file/2.7/Python/marshal.c#l423). Da lì, estrai le informazioni per gli oggetti codice (vedi [modello dati] (https://docs.python.org/2/reference/datamodel.html), scorri verso il basso fino a Tipi interni -> oggetti codice) e associa i byte a istruzioni bytecode (vedi la funzione ['dis.disassemble()'] (https://hg.python.org/cpython/file/2.7/Lib/dis.py#l61). –