Python3 字节码详解
文章目錄
- 前言
- 什么是 py 字節碼?
- 變量
- 常用數據類型
- list
- dict
- slice
- 循環
- while
- for
- if
- 其他指令
- 后記
前言
在逆向的時候遇到過反編譯 py 字節碼,之前也就沒咋在意,啥不會查就完事兒了,好家伙,省賽讓我給遇到了,直接嚶嚶嚶😭,但還好解出來了;
今天趁這個機會,系統的學習一下,以防下次陰溝里翻船,本博文的 Python 版本是3.8.5,版本不同形成的字節碼會略有不同,但是大同小異;
【記】2021年第四屆浙江省大學生網絡安全技能挑戰賽:
- CSDN
- 個人博客
?
什么是 py 字節碼?
Python 代碼先被編譯為字節碼后,再由 Python 虛擬機來執行字節碼,Python 的字節碼是一種類似匯編指令的中間語言,一個 Python 語句會對應若干字節碼指令,虛擬機一條一條執行字節碼指令,從而完成程序執行。
Python 的 dis 模塊支持對 Python 代碼進行反匯編, 生成字節碼指令。
結構:
源碼行號 | 指令在函數中的偏移 | 指令符號 | 指令參數 | 實際參數值源碼:
str = [88, 117, 124, 124, 127, 48, 71, 127, 98, 124, 116, 48, 61, 61, 121, 116, 33, 32, 100, 62]def test():for st in str:print(chr(st^16),end='')test()字節碼:
1 0 LOAD_CONST 0 (0)2 LOAD_CONST 1 (None)4 IMPORT_NAME 0 (dis)6 STORE_NAME 0 (dis)3 8 LOAD_CONST 2 (88)10 LOAD_CONST 3 (117)12 LOAD_CONST 4 (124)14 LOAD_CONST 4 (124)16 LOAD_CONST 5 (127)18 LOAD_CONST 6 (48)20 LOAD_CONST 7 (71)22 LOAD_CONST 5 (127)24 LOAD_CONST 8 (98)26 LOAD_CONST 4 (124)28 LOAD_CONST 9 (116)30 LOAD_CONST 6 (48)32 LOAD_CONST 10 (61)34 LOAD_CONST 10 (61)36 LOAD_CONST 11 (121)38 LOAD_CONST 9 (116)40 LOAD_CONST 12 (33)42 LOAD_CONST 13 (32)44 LOAD_CONST 14 (100)46 LOAD_CONST 15 (62)48 BUILD_LIST 2050 STORE_NAME 1 (str)5 52 LOAD_CONST 16 (<code object test at 0x0170E2F8, file "1.py", line 5>)54 LOAD_CONST 17 ('test')56 MAKE_FUNCTION 058 STORE_NAME 2 (test)9 60 LOAD_NAME 2 (test)62 CALL_FUNCTION 064 POP_TOP66 LOAD_CONST 1 (None)68 RETURN_VALUEDisassembly of <code object test at 0x0170E2F8, file "1.py", line 5>:6 0 LOAD_GLOBAL 0 (str)2 GET_ITER>> 4 FOR_ITER 24 (to 30)6 STORE_FAST 0 (st)7 8 LOAD_GLOBAL 1 (print)10 LOAD_GLOBAL 2 (chr)12 LOAD_FAST 0 (st)14 LOAD_CONST 1 (16)16 BINARY_XOR18 CALL_FUNCTION 120 LOAD_CONST 2 ('')22 LOAD_CONST 3 (('end',))24 CALL_FUNCTION_KW 226 POP_TOP28 JUMP_ABSOLUTE 4>> 30 LOAD_CONST 0 (None)32 RETURN_VALUE稍后會詳細介紹;
?
變量
1、CONST
LOAD_CONST 加載 const 變量,比如數值、字符串等等,一般用于傳給函數的參數;
11 52 LOAD_NAME 2 (test)54 LOAD_CONST 16 ('nice')56 CALL_FUNCTION 158 POP_TOP test('nice')?
2、局部變量
- LOAD_FAST 一般加載局部變量的值,也就是讀取值,用于計算或者函數調用傳參等;
- STORE_FAST 一般用于保存值到局部變量;
那問題來了,函數的形參也是局部變量,如何區分出是函數形參還是其他局部變量呢?
我們可以自己寫一段代碼推敲一下:
import disstr = ''def test(arg):str = 'idi10t'print(arg,str)dis.dis(test) 6 0 LOAD_CONST 1 ('idi10t')2 STORE_FAST 1 (str) 7 4 LOAD_GLOBAL 0 (print)6 LOAD_FAST 0 (arg) 8 LOAD_FAST 1 (str) 10 CALL_FUNCTION 2 12 POP_TOP14 LOAD_CONST 0 (None) 16 RETURN_VALUE可以得出結論:形參沒有初始化,也就是從函數開始到 LOAD_FAST 該變量的位置,如果沒有看到 STORE_FAST,那么該變量就是函數形參;而其他局部變量在使用之前肯定會使用 STORE_FAST 進行初始化。
?
3、全局變量
- LOAD_GLOBAL 用來加載全局變量,包括指定函數名,類名,模塊名等全局符號;
- STORE_GLOBAL 用來給全局變量賦值;
?
常用數據類型
list
BUILD_LIST 用于創建一個 list 結構:
str = [88, 117, 124, 124, 127, 48, 71, 127, 98, 124, 116, 48, 61, 61, 121, 116, 33, 32, 100, 62] 3 8 LOAD_CONST 2 (88)10 LOAD_CONST 3 (117)12 LOAD_CONST 4 (124)14 LOAD_CONST 4 (124)16 LOAD_CONST 5 (127)18 LOAD_CONST 6 (48)20 LOAD_CONST 7 (71)22 LOAD_CONST 5 (127)24 LOAD_CONST 8 (98)26 LOAD_CONST 4 (124)28 LOAD_CONST 9 (116)30 LOAD_CONST 6 (48)32 LOAD_CONST 10 (61)34 LOAD_CONST 10 (61)36 LOAD_CONST 11 (121)38 LOAD_CONST 9 (116)40 LOAD_CONST 12 (33)42 LOAD_CONST 13 (32)44 LOAD_CONST 14 (100)46 LOAD_CONST 15 (62)48 BUILD_LIST 2050 STORE_NAME 1 (str)再看看另一種的 list 創建方式:
str = [88, 117, 124, 124, 127, 48, 71, 127, 98, 124, 116, 48, 61, 61, 121, 116, 33, 32, 100, 62][x for x in str if x != 48] 1 0 LOAD_CONST 0 (88)2 LOAD_CONST 1 (117)4 LOAD_CONST 2 (124)6 LOAD_CONST 2 (124)8 LOAD_CONST 3 (127)10 LOAD_CONST 4 (48)12 LOAD_CONST 5 (71)14 LOAD_CONST 3 (127)16 LOAD_CONST 6 (98)18 LOAD_CONST 2 (124)20 LOAD_CONST 7 (116)22 LOAD_CONST 4 (48)24 LOAD_CONST 8 (61)26 LOAD_CONST 8 (61)28 LOAD_CONST 9 (121)30 LOAD_CONST 7 (116)32 LOAD_CONST 10 (33)34 LOAD_CONST 11 (32)36 LOAD_CONST 12 (100)38 LOAD_CONST 13 (62)40 BUILD_LIST 2042 STORE_NAME 0 (str)3 44 LOAD_CONST 14 (<code object <listcomp> at 0x016FE2F8, file "1.py", line 3>)46 LOAD_CONST 15 ('<listcomp>')48 MAKE_FUNCTION 050 LOAD_NAME 0 (str)52 GET_ITER54 CALL_FUNCTION 156 POP_TOP58 LOAD_CONST 16 (None)60 RETURN_VALUEDisassembly of <code object <listcomp> at 0x016FE2F8, file "1.py", line 3>:3 0 BUILD_LIST 0 # 創建 list,為賦值給某變量2 LOAD_FAST 0 (.0)>> 4 FOR_ITER 16 (to 22)6 STORE_FAST 1 (x)8 LOAD_FAST 1 (x)10 LOAD_CONST 0 (48)12 COMPARE_OP 3 (!=)14 POP_JUMP_IF_FALSE 4 # 不滿足條件則 break16 LOAD_FAST 1 (x) # 讀取滿足條件的 x18 LIST_APPEND 2 # 把每個滿足條件的 x 存入 list20 JUMP_ABSOLUTE 4>> 22 RETURN_VALUE?
dict
- BUILD_MAP 用于創建一個空的 dict;
- STORE_NAME 用于初始化 dict 的內容;
?
slice
這里直接借用了大佬博文的數據;
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 312 STORE_FAST 0 (k1) //k1 = [1, 2, 3]14 15 LOAD_CONST 4 (10)18 BUILD_LIST 121 LOAD_FAST 0 (k1)24 LOAD_CONST 5 (0)27 LOAD_CONST 1 (1)30 LOAD_CONST 1 (1)33 BUILD_SLICE 336 STORE_SUBSCR //k1[0:1:1] = [10]15 37 LOAD_CONST 6 (11)40 BUILD_LIST 143 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+363 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 381 BINARY_SUBSCR82 STORE_FAST 2 (b) //b = k1[0:1:1]?
循環
while
Python3.8 及以上就沒有 SETUP_LOOP 了,
大致意思就是將循環塊送入到堆棧當中去,
i = 0 while i < 10:i += 1 1 0 LOAD_CONST 0 (0)2 STORE_NAME 0 (i)2 >> 4 LOAD_NAME 0 (i)6 LOAD_CONST 1 (10)8 COMPARE_OP 0 (<)10 POP_JUMP_IF_FALSE 223 12 LOAD_NAME 0 (i)14 LOAD_CONST 2 (1)16 INPLACE_ADD18 STORE_NAME 0 (i)20 JUMP_ABSOLUTE 4>> 22 LOAD_CONST 3 (None)24 RETURN_VALUE?
for
Python 中典型的 for in 結構:
for i in range(8): 2 4 LOAD_NAME 1 (range)6 LOAD_CONST 1 (8)8 CALL_FUNCTION 110 GET_ITER>> 12 FOR_ITER 38 (to 52)14 STORE_NAME 2 (i)...50 JUMP_ABSOLUTE 12>> 52 LOAD_CONST 6 (None)?
if
POP_JUMP_IF_FALSE 和 JUMP_FORWARD 一般用于分支判斷跳轉:
- POP_JUMP_IF_FALSE 表示條件結果為 FALSE 就跳轉到目標偏移指令;
- JUMP_FORWARD 直接跳轉到目標偏移指令;
?
其他指令
上述就是比較常用的一些指令了,當然還有更多的指令,這里就不一一介紹了,詳情見官方文檔,這里的是 Python3.8 版本的官方文檔;
?
后記
開卷有益,多多益善;
參考:
官方文檔
Python內置模塊dis.py源碼詳解
死磕python字節碼-手工還原python源碼
?
總結
以上是生活随笔為你收集整理的Python3 字节码详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小型移动 webApp Demo 知识点
- 下一篇: python3 爬虫 requests安