溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

python字節(jié)碼手工還原python源碼的示例分析

發(fā)布時間:2021-12-04 16:40:40 來源:億速云 閱讀:175 作者:柒染 欄目:安全技術(shù)

今天就跟大家聊聊有關(guān)python字節(jié)碼手工還原python源碼的示例分析,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

0x1.前言

> Python 代碼先被編譯為字節(jié)碼后,再由Python虛擬機來執(zhí)行字節(jié)碼, Python的字節(jié)碼是一種類似匯編指令的中間語言, 一個Python語句會對應(yīng)若干字節(jié)碼指令,虛擬機一條一條執(zhí)行字節(jié)碼指令, 從而完成程序執(zhí)行。Python dis 模塊支持對Python代碼進行反匯編, 生成字節(jié)碼指令。dis.dis()將CPython字節(jié)碼轉(zhuǎn)為可讀的偽代碼(類似于匯編代碼)。結(jié)構(gòu)如下:

  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

其實就是這樣的結(jié)構(gòu):

源碼行號 | 指令在函數(shù)中的偏移 | 指令符號 | 指令參數(shù) | 實際參數(shù)值

0x2.變量

1.const

LOAD_CONST加載const變量,比如數(shù)值、字符串等等,一般用于傳給函數(shù)的參數(shù)

55       12 LOAD_GLOBAL              1 (test)
         15 LOAD_FAST                0 (2) #讀取2
         18 LOAD_CONST               1 ('output') 
         21 CALL_FUNCTION            2

轉(zhuǎn)為python代碼就是:

test(2, 'output')

2.局部變量

LOAD_FAST一般加載局部變量的值,也就是讀取值,用于計算或者函數(shù)調(diào)用傳參等。STORE_FAST一般用于保存值到局部變量。

61          77 LOAD_FAST                0 (n)
             80 LOAD_FAST                3 (p)
             83 INPLACE_DIVIDE
             84 STORE_FAST               0 (n)

這段bytecode轉(zhuǎn)為python就是:

n = n / p

函數(shù)的形參也是局部變量,如何區(qū)分出是函數(shù)形參還是其他局部變量呢?形參沒有初始化,也就是從函數(shù)開始到LOAD_FAST該變量的位置,如果沒有看到STORE_FAST,那么該變量就是函數(shù)形參。而其他局部變量在使用之前肯定會使用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

對應(yīng)的python代碼如下,對比一下就一目了然。

def test(arg1):
    local1 = 0
    print local1, arg1

3.全局變量

LOAD_GLOBAL用來加載全局變量,包括指定函數(shù)名,類名,模塊名等全局符號。STORE_GLOBAL用來給全局變量賦值。

  8           6 LOAD_CONST               2 (101)
              9 STORE_GLOBAL             0 (global1)
              20 LOAD_GLOBAL              0 (global1)
              23 PRINT_ITEM

對應(yīng)的python代碼

def test():
    global global1
    global1 = 101
    print global

0x3.常用數(shù)據(jù)類型

1.list

BUILD_LIST用于創(chuàng)建一個list結(jié)構(gòu)。

 13           0 LOAD_CONST               1 (1)
              3 LOAD_CONST               2 (2)
              6 BUILD_LIST               2
              9 STORE_FAST               0 (k)

對應(yīng)python代碼是:

k = [1, 2]

另外再看看一種常見的創(chuàng)建list的方式如下:

[x for x in xlist if x!=0 ]

一個實例bytecode如下:

 22         235 BUILD_LIST               0 //創(chuàng)建list,為賦值給某變量,這種時候一般都是語法糖結(jié)構(gòu)了
            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

轉(zhuǎn)為python代碼是:

[for x in sieve if x != 0]

2.dict

BUILD_MAP用于創(chuàng)建一個空的dict。STORE_MAP用于初始化dict的內(nèi)容。

 13           0 BUILD_MAP                1
              3 LOAD_CONST               1 (1)
              6 LOAD_CONST               2 ('a')
              9 STORE_MAP
             10 STORE_FAST               0 (k)

對應(yīng)的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

對應(yīng)的python代碼是:

k['b'] = 2

3.slice

BUILD_SLICE用于創(chuàng)建slice。對于list、元組、字符串都可以使用slice的方式進行訪問。但是要注意BUILD_SLICE用于[x:y:z]這種類型的slice,結(jié)合BINARY_SUBSCR讀取slice的值,結(jié)合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]

0x4.循環(huán)

SETUP_LOOP用于開始一個循環(huán)。SETUP_LOOP              26 (to 35)35表示循環(huán)退出點。

while循環(huán)

 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) //循環(huán)起點
             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    // 回到循環(huán)起點
        >>   34 POP_BLOCK
        >>   35 LOAD_CONST               0 (None)

對應(yīng)python代碼是:

i = 0
    while i < 10:
        i += 1

for in結(jié)構(gòu)

            238 LOAD_FAST                3 (sieve)#sieve是個list
            241 GET_ITER                    //開始迭代sieve
        >>  242 FOR_ITER                24 (to 269) //繼續(xù)iter下一個x
            245 STORE_FAST               4 (x)
            ...
            266 JUMP_ABSOLUTE          242 //循環(huán)

這是典型的for+in結(jié)構(gòu),轉(zhuǎn)為python代碼就是:

for x in sieve:

0x5.if

POP_JUMP_IF_FALSEJUMP_FORWARD一般用于分支判斷跳轉(zhuǎn)。POP_JUMP_IF_FALSE表示條件結(jié)果為FALSE就跳轉(zhuǎn)到目標偏移指令。JUMP_FORWARD直接跳轉(zhuǎn)到目標偏移指令。

 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)

轉(zhuǎn)為python代碼是:

    i = 0
    if i < 5:
        print 'i < 5'
    elif i > 5:
        print 'i > 5'
    else:
        print 'i = 5'

0x6.分辨函數(shù)

1.函數(shù)范圍

前面介紹第二列表示指令在函數(shù)中的偏移地址,所以看到0就是函數(shù)開始,下一個0前一條指令就是函數(shù)結(jié)束位置,當(dāng)然也可以通過RETURN_VALUE來確定函數(shù)結(jié)尾

54         0 LOAD_FAST                1 (plist) //函數(shù)開始
           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') //另一個函數(shù)開始
           3 STORE_FAST               0 (flag)

2.函數(shù)調(diào)用

函數(shù)調(diào)用類似于push+call的匯編結(jié)構(gòu),壓棧參數(shù)從左到右依次壓入(當(dāng)然不是push,而是讀取指令LOAD_xxxx來指定參數(shù))。函數(shù)名一般通過LOAD_GLOBAL指令指定,如果是模塊函數(shù)或者類成員函數(shù)通過LOAD_GLOBAL+LOAD_ATTR來指定。先指定要調(diào)用的函數(shù),然后壓參數(shù),最后通過CALL_FUNCTION調(diào)用。CALL_FUNCTION后面的值表示有幾個參數(shù)。支持嵌套調(diào)用:

6           0 LOAD_GLOBAL              0 (int) //int函數(shù)
              3 LOAD_GLOBAL              1 (math)//math模塊
              6 LOAD_ATTR                2 (sqrt)//sqrt函數(shù)
              9 LOAD_FAST                0 (n) //參數(shù)
             12 CALL_FUNCTION            1
             15 CALL_FUNCTION            1
             18 STORE_FAST               2 (nroot)

這段bytecode轉(zhuǎn)換成python代碼就是

nroot = int(math.sqrt(n)) //其中n是一個局部變量或者函數(shù)參數(shù),具體看上下文

0x7.其他指令

其他常見指令,一看就明白,就不具體分析了,更多詳細內(nèi)容請看官方文檔。

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.

基礎(chǔ)運算還有一套對應(yīng)的BINARY_xxxx指令,兩者區(qū)別很簡單。

i += 1 //使用INPLACE_xxx
i = i + 1 //使用BINARY_xxxx

看完上述內(nèi)容,你們對python字節(jié)碼手工還原python源碼的示例分析有進一步的了解嗎?如果還想了解更多知識或者相關(guān)內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI