2017-04-03 85 views
1

設想這樣簡單的函數創建的變量default的修改值,modified功能(拔本地變量out)

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變量的值,所以我雖然也許有一種方法如何通過dis模塊刪除功能的特定部分(print(modified)),但我沒有發現任何東西。

有沒有什麼辦法可以刪除可能的所有東西,除了16 CALL_FUNCTION之後的return_value,並且用例如16 CALL_FUNCTION代替它。 return modified?或者還有什麼其他的方式如何在沒有實際執行最後一行的情況下拉出局部變量?

作爲一個可能的解決方案我看到3種方式:

  • 拉拆解代碼和創建我自己的函數(或就地)根據他們刪除代碼,我不想(16 ...後一切)
  • 修改函數的返回,以便它返回modified(不幸調用OS功能)
  • 根據源代碼
  • 手動重新創建功能

我想避免第二種方式,這可能是比第一個更容易,但我必須避免的第三條道路,所以......有什麼辦法如何解決我的問題呢?

+0

即不是變量的修改版本。這是一個全新的對象 –

+0

@MosesKoledoye編輯(我的意思是價值)。 – KeyWeeUsr

+4

有第四種選擇:替換'print()'全局。 '打印= [];打印=拉姆達* ARGS:printed.extend(參數)',然後運行'修改()'和,optionaly,'德爾print'去除遮蔽全球。 –

回答

4

有一個4選項:更換print()全球:

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

其實不然可能產生修改的字節碼,但是這也容易導致該炸掉解釋的錯誤(沒有來自無效零保護字節碼),所以要警告。

您可以創建與更新的字節碼新的編碼對象一個新的函數對象;根據你表明,我手動創建新的字節代碼將在索引0返回局部變量的DIS偏移:

>>> 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在堆棧的頂部返回對象;我所做的只是注入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讓你映射回字節值。

有幾個模塊,在那裏,試圖使它更容易些;看看byteplaybytecode module of the pwnypack project或幾個人,如果你想進一步探討這一點。

我也可以衷心推薦您觀看由Scott Sanderson,Joe Jevnik在PyCon 2016上給出的Playing with Python Bytecode presentation,以及他們的codetransformer module。非常有趣和非常豐富。

+0

呵呵,永遠不會令人失望的Python。總是有一個包!我不知道只返回頂部的項目,這使得事情更容易一些情況下,只有一個單一的電話是不受歡迎的。字節碼錶示非常棒,我記得在YT上看過它。 – KeyWeeUsr

+0

@KeyWeeUsr:當我第一次看到它時,我在觀衆中。 :-D –

+0

還驚訝[即(https://youtu.be/mxjv9KqzwjI?t=15m40s)? :d – KeyWeeUsr