您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關python字節碼手工還原python源碼的示例分析,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
> Python 代碼先被編譯為字節碼后,再由Python虛擬機來執行字節碼, Python的字節碼是一種類似匯編指令的中間語言, 一個Python語句會對應若干字節碼指令,虛擬機一條一條執行字節碼指令, 從而完成程序執行。Python dis 模塊支持對Python代碼進行反匯編, 生成字節碼指令。dis.dis()
將CPython字節碼轉為可讀的偽代碼(類似于匯編代碼)。結構如下:
7 0 LOAD_CONST 1 (0) 3 STORE_FAST 1 (local1) 8 6 LOAD_CONST 2 (101) 9 STORE_GLOBAL 0 (global1) 9 12 LOAD_FAST 1 (local1) 15 PRINT_ITEM 16 LOAD_FAST 0 (arg1) 19 PRINT_ITEM 20 LOAD_GLOBAL 0 (global1) 23 PRINT_ITEM 24 PRINT_NEWLINE 25 LOAD_CONST 0 (None) 28 RETURN_VALUE
其實就是這樣的結構:
源碼行號 | 指令在函數中的偏移 | 指令符號 | 指令參數 | 實際參數值
LOAD_CONST
加載const
變量,比如數值、字符串等等,一般用于傳給函數的參數
55 12 LOAD_GLOBAL 1 (test) 15 LOAD_FAST 0 (2) #讀取2 18 LOAD_CONST 1 ('output') 21 CALL_FUNCTION 2
轉為python代碼就是:
test(2, 'output')
LOAD_FAST
一般加載局部變量的值,也就是讀取值,用于計算或者函數調用傳參等。STORE_FAST
一般用于保存值到局部變量。
61 77 LOAD_FAST 0 (n) 80 LOAD_FAST 3 (p) 83 INPLACE_DIVIDE 84 STORE_FAST 0 (n)
這段bytecode轉為python就是:
n = n / p
函數的形參也是局部變量,如何區分出是函數形參還是其他局部變量呢?形參沒有初始化,也就是從函數開始到LOAD_FAST
該變量的位置,如果沒有看到STORE_FAST
,那么該變量就是函數形參。而其他局部變量在使用之前肯定會使用STORE_FAST
進行初始化。具體看下面的實例:
4 0 LOAD_CONST 1 (0) 3 STORE_FAST 1 (local1) 5 6 LOAD_FAST 1 (local1) 9 PRINT_ITEM 10 LOAD_FAST 0 (arg1) 13 PRINT_ITEM 14 PRINT_NEWLINE 15 LOAD_CONST 0 (None) 18 RETURN_VALUE
對應的python代碼如下,對比一下就一目了然。
def test(arg1): local1 = 0 print local1, arg1
LOAD_GLOBAL
用來加載全局變量,包括指定函數名,類名,模塊名等全局符號。STORE_GLOBAL
用來給全局變量賦值。
8 6 LOAD_CONST 2 (101) 9 STORE_GLOBAL 0 (global1) 20 LOAD_GLOBAL 0 (global1) 23 PRINT_ITEM
對應的python代碼
def test(): global global1 global1 = 101 print global
BUILD_LIST
用于創建一個list結構。
13 0 LOAD_CONST 1 (1) 3 LOAD_CONST 2 (2) 6 BUILD_LIST 2 9 STORE_FAST 0 (k)
對應python代碼是:
k = [1, 2]
另外再看看一種常見的創建list的方式如下:
[x for x in xlist if x!=0 ]
一個實例bytecode如下:
22 235 BUILD_LIST 0 //創建list,為賦值給某變量,這種時候一般都是語法糖結構了 238 LOAD_FAST 3 (sieve) 241 GET_ITER >> 242 FOR_ITER 24 (to 269) 245 STORE_FAST 4 (x) 248 LOAD_FAST 4 (x) 251 LOAD_CONST 2 (0) 254 COMPARE_OP 3 (!=) 257 POP_JUMP_IF_FALSE 242 //不滿足條件contine 260 LOAD_FAST 4 (x)//讀取滿足條件的x 263 LIST_APPEND 2 //把每個滿足條件的x存入list 266 JUMP_ABSOLUTE 242 >> 269 RETURN_VALUE
轉為python代碼是:
[for x in sieve if x != 0]
BUILD_MAP
用于創建一個空的dict。STORE_MAP
用于初始化dict的內容。
13 0 BUILD_MAP 1 3 LOAD_CONST 1 (1) 6 LOAD_CONST 2 ('a') 9 STORE_MAP 10 STORE_FAST 0 (k)
對應的python代碼是:
k = {'a': 1}
再看看修改dict的bytecode:
14 13 LOAD_CONST 3 (2) 16 LOAD_FAST 0 (k) 19 LOAD_CONST 4 ('b') 22 STORE_SUBSCR
對應的python代碼是:
k['b'] = 2
BUILD_SLICE
用于創建slice。對于list、元組、字符串都可以使用slice的方式進行訪問。但是要注意BUILD_SLICE
用于[x:y:z]這種類型的slice,結合BINARY_SUBSCR
讀取slice的值,結合STORE_SUBSCR
用于修改slice的值。另外SLICE+n
用于[a:b]類型的訪問,STORE_SLICE+n
用于[a:b]類型的修改,其中n
表示如下:
SLICE+0() Implements TOS = TOS[:]. SLICE+1() Implements TOS = TOS1[TOS:]. SLICE+2() Implements TOS = TOS1[:TOS]. SLICE+3() Implements TOS = TOS2[TOS1:TOS].
下面看具體實例:
13 0 LOAD_CONST 1 (1) 3 LOAD_CONST 2 (2) 6 LOAD_CONST 3 (3) 9 BUILD_LIST 3 12 STORE_FAST 0 (k1) //k1 = [1, 2, 3] 14 15 LOAD_CONST 4 (10) 18 BUILD_LIST 1 21 LOAD_FAST 0 (k1) 24 LOAD_CONST 5 (0) 27 LOAD_CONST 1 (1) 30 LOAD_CONST 1 (1) 33 BUILD_SLICE 3 36 STORE_SUBSCR //k1[0:1:1] = [10] 15 37 LOAD_CONST 6 (11) 40 BUILD_LIST 1 43 LOAD_FAST 0 (k1) 46 LOAD_CONST 1 (1) 49 LOAD_CONST 2 (2) 52 STORE_SLICE+3 //k1[1:2] = [11] 16 53 LOAD_FAST 0 (k1) 56 LOAD_CONST 1 (1) 59 LOAD_CONST 2 (2) 62 SLICE+3 63 STORE_FAST 1 (a) //a = k1[1:2] 17 66 LOAD_FAST 0 (k1) 69 LOAD_CONST 5 (0) 72 LOAD_CONST 1 (1) 75 LOAD_CONST 1 (1) 78 BUILD_SLICE 3 81 BINARY_SUBSCR 82 STORE_FAST 2 (b) //b = k1[0:1:1]
SETUP_LOOP
用于開始一個循環。SETUP_LOOP 26 (to 35)
中35
表示循環退出點。
23 0 LOAD_CONST 1 (0) 3 STORE_FAST 0 (i) // i=0 24 6 SETUP_LOOP 26 (to 35) >> 9 LOAD_FAST 0 (i) //循環起點 12 LOAD_CONST 2 (10) 15 COMPARE_OP 0 (<) 18 POP_JUMP_IF_FALSE 34 //while i < 10: 25 21 LOAD_FAST 0 (i) 24 LOAD_CONST 3 (1) 27 INPLACE_ADD 28 STORE_FAST 0 (i) // i += 1 31 JUMP_ABSOLUTE 9 // 回到循環起點 >> 34 POP_BLOCK >> 35 LOAD_CONST 0 (None)
對應python代碼是:
i = 0 while i < 10: i += 1
238 LOAD_FAST 3 (sieve)#sieve是個list 241 GET_ITER //開始迭代sieve >> 242 FOR_ITER 24 (to 269) //繼續iter下一個x 245 STORE_FAST 4 (x) ... 266 JUMP_ABSOLUTE 242 //循環
這是典型的for+in結構,轉為python代碼就是:
for x in sieve:
POP_JUMP_IF_FALSE
和JUMP_FORWARD
一般用于分支判斷跳轉。POP_JUMP_IF_FALSE
表示條件結果為FALSE
就跳轉到目標偏移指令。JUMP_FORWARD
直接跳轉到目標偏移指令。
23 0 LOAD_CONST 1 (0) 3 STORE_FAST 0 (i) //i=0 24 6 LOAD_FAST 0 (i) 9 LOAD_CONST 2 (5) 12 COMPARE_OP 0 (<) 15 POP_JUMP_IF_FALSE 26 25 18 LOAD_CONST 3 ('i < 5') 21 PRINT_ITEM 22 PRINT_NEWLINE 23 JUMP_FORWARD 25 (to 51) 26 >> 26 LOAD_FAST 0 (i) 29 LOAD_CONST 2 (5) 32 COMPARE_OP 4 (>) 35 POP_JUMP_IF_FALSE 46 27 38 LOAD_CONST 4 ('i > 5') 41 PRINT_ITEM 42 PRINT_NEWLINE 43 JUMP_FORWARD 5 (to 51) 29 >> 46 LOAD_CONST 5 ('i = 5') 49 PRINT_ITEM 50 PRINT_NEWLINE >> 51 LOAD_CONST 0 (None)
轉為python代碼是:
i = 0 if i < 5: print 'i < 5' elif i > 5: print 'i > 5' else: print 'i = 5'
前面介紹第二列表示指令在函數中的偏移地址,所以看到0就是函數開始,下一個0前一條指令就是函數結束位置,當然也可以通過RETURN_VALUE
來確定函數結尾
54 0 LOAD_FAST 1 (plist) //函數開始 3 LOAD_CONST 0 (None) 6 COMPARE_OP 2 (==) 9 POP_JUMP_IF_FALSE 33 55 ... 67 >> 139 LOAD_FAST 2 (fs) 142 RETURN_VALUE 70 0 LOAD_CONST 1 ('FLAG') //另一個函數開始 3 STORE_FAST 0 (flag)
函數調用類似于push+call
的匯編結構,壓棧參數從左到右依次壓入(當然不是push
,而是讀取指令LOAD_xxxx
來指定參數)。函數名一般通過LOAD_GLOBAL
指令指定,如果是模塊函數或者類成員函數通過LOAD_GLOBAL
+LOAD_ATTR
來指定。先指定要調用的函數,然后壓參數,最后通過CALL_FUNCTION
調用。CALL_FUNCTION
后面的值表示有幾個參數。支持嵌套調用:
6 0 LOAD_GLOBAL 0 (int) //int函數 3 LOAD_GLOBAL 1 (math)//math模塊 6 LOAD_ATTR 2 (sqrt)//sqrt函數 9 LOAD_FAST 0 (n) //參數 12 CALL_FUNCTION 1 15 CALL_FUNCTION 1 18 STORE_FAST 2 (nroot)
這段bytecode
轉換成python
代碼就是
nroot = int(math.sqrt(n)) //其中n是一個局部變量或者函數參數,具體看上下文
其他常見指令,一看就明白,就不具體分析了,更多詳細內容請看官方文檔。
INPLACE_POWER() Implements in-place TOS = TOS1 ** TOS. INPLACE_MULTIPLY() Implements in-place TOS = TOS1 * TOS. INPLACE_DIVIDE() Implements in-place TOS = TOS1 / TOS when from __future__ import division is not in effect. INPLACE_FLOOR_DIVIDE() Implements in-place TOS = TOS1 // TOS. INPLACE_TRUE_DIVIDE() Implements in-place TOS = TOS1 / TOS when from __future__ import division is in effect. INPLACE_MODULO() Implements in-place TOS = TOS1 % TOS. INPLACE_ADD() Implements in-place TOS = TOS1 + TOS. INPLACE_SUBTRACT() Implements in-place TOS = TOS1 - TOS. INPLACE_LSHIFT() Implements in-place TOS = TOS1 << TOS. INPLACE_RSHIFT() Implements in-place TOS = TOS1 >> TOS. INPLACE_AND() Implements in-place TOS = TOS1 & TOS. INPLACE_XOR() Implements in-place TOS = TOS1 ^ TOS. INPLACE_OR() Implements in-place TOS = TOS1 | TOS.
基礎運算還有一套對應的BINARY_xxxx
指令,兩者區別很簡單。
i += 1 //使用INPLACE_xxx i = i + 1 //使用BINARY_xxxx
看完上述內容,你們對python字節碼手工還原python源碼的示例分析有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。