Python 字節(jié)碼與字節(jié)碼混淆

Python 字節(jié)碼

  • 雖然 Python 作為解釋型語(yǔ)言哀澈,但是其也不是直接對(duì)源代碼進(jìn)行解釋
    • Python 解釋器會(huì)將源代碼處理成字節(jié)碼后伴箩,借助 Python 解釋器運(yùn)行程序
  • 通過(guò) Python 自帶的模塊 dis 可以將目標(biāo)函數(shù)轉(zhuǎn)換成字節(jié)碼
import dis

def fun(x, y, z):
    a = 1
    a += 1
    print("aaa")
    fun(1, 2, 3)
    return

dis.dis(fun)
  • 控制臺(tái)輸出內(nèi)容如下
    • 第一列對(duì)應(yīng)的是源代碼中的行號(hào)
    • 第二列對(duì)應(yīng)的是源代碼轉(zhuǎn)化成的字節(jié)碼
    • 第三列為此次操作對(duì)應(yīng)的值(括號(hào)內(nèi)為具體值)
  • 例如第六行
    • 解釋器先讀取了全局對(duì)象 print 函數(shù)排监,推入程序棧
    • 程序又將字符串 'aaa' 推入程序棧
    • 調(diào)用函數(shù)可岂,并解釋只有 1 個(gè)變量陡蝇,解釋器便會(huì)將棧頂?shù)?1 個(gè)變量傳遞給函數(shù),然后調(diào)用函數(shù)
      • 需要注意牺勾,如果有多個(gè)參數(shù)的話正罢,參數(shù)入棧順序是從左到右,也就是最右邊的參數(shù)在最頂端
      • CALL_FUNCTION 會(huì)在結(jié)束后彈出棧頂對(duì)應(yīng)參數(shù)數(shù)量的元素驻民,但是函數(shù)不會(huì)被彈出棧翻具,因此最后有一個(gè) POP_TOP
> python3 -u "/Users/biox/NutStore/Codes/VSCode/python/py1.py"
  4           0 LOAD_CONST               1 (1)
              2 STORE_FAST               3 (a)

  5           4 LOAD_FAST                3 (a)
              6 LOAD_CONST               1 (1)
              8 INPLACE_ADD
             10 STORE_FAST               3 (a)

  6          12 LOAD_GLOBAL              0 (print)
             14 LOAD_CONST               2 ('aaa')
             16 CALL_FUNCTION            1
             18 POP_TOP

  7          20 LOAD_GLOBAL              1 (fun)
             22 LOAD_CONST               1 (1)
             24 LOAD_CONST               3 (2)
             26 LOAD_CONST               4 (3)
             28 CALL_FUNCTION            3
             30 POP_TOP

  8          32 LOAD_CONST               0 (None)
             34 RETURN_VALUE

常見(jiàn)指令

  • 詳細(xì)內(nèi)容見(jiàn)官方文檔

  • 一般指令與一元操作指令

指令 作用
NOP 無(wú)作用,用于占位
POP_TOP 彈出棧頂元素
LOAD_CONST 將讀取的值推入棧
LOAD_GLOBAL 將全局變量對(duì)象壓入棧頂
STORE_FAST 將棧頂指令存入對(duì)應(yīng)局部變量
COMPARE_OP 比較操作符
CALL_FUNCTION 調(diào)用函數(shù)
BUILD_SLICE 調(diào)用切片回还,跟的參數(shù)為切片的值的個(gè)數(shù)裆泳,一般從上到下為 [Val1:Val2:Val3]
JUMP_ABSOLUTE 向下跳轉(zhuǎn)幾句操作符,變量為跳轉(zhuǎn)偏移量
UNARY_POSITIVE 實(shí)現(xiàn) Val1 = +Val1
UNARY_NEGATIVE 實(shí)現(xiàn) Val1 = -Val1
UNARY_NOT 實(shí)現(xiàn) Val1 = not Val1
UNARY_INVERT 實(shí)現(xiàn) Val1 = ~Val
FOR_ITER for 循環(huán)
GET_ITER 獲取迭代器(一般后面跟循環(huán))
GET_YIELD_FROM_ITER 獲取 yield 生成器
  • 二元操作指令
指令 作用
BINARY_POWER 乘方柠硕,棧頂數(shù)為指數(shù)
BINARY_MULTIPLY 乘法
BINARY_MATRIX_MULTIPLY 矩陣乘法工禾,3.5 引入的新功能
BINARY_FLOOR_DIVIDE 除法,結(jié)果向下取整
BINARY_TRUE_DIVIDE 除法
BINARY_MODULO 取余
BINARY_ADD 加法
BINARY_SUBTRACT 減法
BINARY_SUBSCR 數(shù)組取下標(biāo)蝗柔,棧頂為下標(biāo)
BINARY_LSHIFT 左移操作符(乘2)
BINARY_RSHIFT 右移操作符(除2向下取整)
BINARY_AND 按位與
BINARY_XOR 異或
BINARY_OR 按位或
STORE_SUBSCR 列表下標(biāo)存儲(chǔ)闻葵,例如 Val1[Val2] = Val3
DELETE_SUBSCR 按下標(biāo)刪除元素,例如 del Val1[Val2]
  • 自身操作指令癣丧,類似 b += 1 槽畔,就是上方有 BINARY 的指令的 BINARY 改成 INPLACE

  • 其他指令見(jiàn)官方文檔

Pyc 文件解析

  • Pyc 文件是 PythonCodeObject 對(duì)象的持久化保存方式
  • 有時(shí)候會(huì)見(jiàn)到 Pyo 文件,這個(gè)是經(jīng)過(guò) Python 解釋器優(yōu)化后生成的字節(jié)碼
    • 這個(gè)優(yōu)化只是縮小了文件的體積胁编,在代碼運(yùn)行速度上和 Pyc 差不多
  • 尤其對(duì)于被 import 的文件厢钧,Python 解釋器為了加快下一次被引用文件的讀取速度,都會(huì)生成一個(gè)對(duì)應(yīng)的 Pyc 文件
    • 當(dāng)后續(xù)被 import 的時(shí)候嬉橙,解釋器會(huì)優(yōu)先尋找持久化存儲(chǔ)的對(duì)象
  • 在 Python 源代碼運(yùn)行的時(shí)候早直,Python 解釋器會(huì)先將代碼處理成 PythonCodeObject 對(duì)象,保存在內(nèi)存中處理
  • 除去預(yù)處理 PythonCodeObject 對(duì)象的過(guò)程市框,在執(zhí)行速度上 Pyc 文件霞扬、Pyo 文件和源代碼文件的速度相差無(wú)幾
  • 需要注意的是,Pyc 文件只能運(yùn)行在生成出此文件的解釋器版本上
    • Python 在生成 Pyc 文件的時(shí)候也引入了 MagicNumber枫振,來(lái)標(biāo)示此 Pyc 文件對(duì)應(yīng)的版本號(hào)
  • 在 Python 解釋器目錄下 ./lib/python3.7/importlib/_bootstrap-external.py 中有明確的版本號(hào)記錄
    • 這里的版本號(hào)是解釋器字節(jié)碼更新的版本號(hào)
# Magic word to reject .pyc files generated by other Python versions.
# It should change for each incompatible change to the bytecode.
#
# The value of CR and LF is incorporated so if you ever read or write
# a .pyc file in text mode the magic number will be wrong; also, the
# Apple MPW compiler swaps their values, botching string constants.
#
# There were a variety of old schemes for setting the magic number.
# The current working scheme is to increment the previous value by
# 10.
#
# Starting with the adoption of PEP 3147 in Python 3.2, every bump in magic
# number also includes a new "magic tag", i.e. a human readable string used
# to represent the magic number in __pycache__ directories.  When you change
# the magic number, you must also set a new unique magic tag.  Generally this
# can be named after the Python major version of the magic number bump, but
# it can really be anything, as long as it's different than anything else
# that's come before.  The tags are included in the following table, starting
# with Python 3.2a0.
#
# Known values:
#  Python 1.5:   20121
#  Python 1.5.1: 20121
#     Python 1.5.2: 20121
#     Python 1.6:   50428
#     Python 2.0:   50823
#     Python 2.0.1: 50823
#     Python 2.1:   60202
#     Python 2.1.1: 60202
#     Python 2.1.2: 60202
#     Python 2.2:   60717
#     Python 2.3a0: 62011
#     Python 2.3a0: 62021
#     Python 2.3a0: 62011 (!)
#     Python 2.4a0: 62041
#     Python 2.4a3: 62051
#     Python 2.4b1: 62061
#     Python 2.5a0: 62071
#     Python 2.5a0: 62081 (ast-branch)
#     Python 2.5a0: 62091 (with)
#     Python 2.5a0: 62092 (changed WITH_CLEANUP opcode)
#     Python 2.5b3: 62101 (fix wrong code: for x, in ...)
#     Python 2.5b3: 62111 (fix wrong code: x += yield)
#     Python 2.5c1: 62121 (fix wrong lnotab with for loops and
#                          storing constants that should have been removed)
#     Python 2.5c2: 62131 (fix wrong code: for x, in ... in listcomp/genexp)
#     Python 2.6a0: 62151 (peephole optimizations and STORE_MAP opcode)
#     Python 2.6a1: 62161 (WITH_CLEANUP optimization)
#     Python 2.7a0: 62171 (optimize list comprehensions/change LIST_APPEND)
#     Python 2.7a0: 62181 (optimize conditional branches:
#                          introduce POP_JUMP_IF_FALSE and POP_JUMP_IF_TRUE)
#     Python 2.7a0  62191 (introduce SETUP_WITH)
#     Python 2.7a0  62201 (introduce BUILD_SET)
#     Python 2.7a0  62211 (introduce MAP_ADD and SET_ADD)
#     Python 3000:   3000
#                    3010 (removed UNARY_CONVERT)
#                    3020 (added BUILD_SET)
#                    3030 (added keyword-only parameters)
#                    3040 (added signature annotations)
#                    3050 (print becomes a function)
#                    3060 (PEP 3115 metaclass syntax)
#                    3061 (string literals become unicode)
#                    3071 (PEP 3109 raise changes)
#                    3081 (PEP 3137 make __file__ and __name__ unicode)
#                    3091 (kill str8 interning)
#                    3101 (merge from 2.6a0, see 62151)
#                    3103 (__file__ points to source file)
#     Python 3.0a4: 3111 (WITH_CLEANUP optimization).
#     Python 3.0b1: 3131 (lexical exception stacking, including POP_EXCEPT
                          #3021)
#     Python 3.1a1: 3141 (optimize list, set and dict comprehensions:
#                         change LIST_APPEND and SET_ADD, add MAP_ADD #2183)
#     Python 3.1a1: 3151 (optimize conditional branches:
#                         introduce POP_JUMP_IF_FALSE and POP_JUMP_IF_TRUE
                          #4715)
#     Python 3.2a1: 3160 (add SETUP_WITH #6101)
#                   tag: cpython-32
#     Python 3.2a2: 3170 (add DUP_TOP_TWO, remove DUP_TOPX and ROT_FOUR #9225)
#                   tag: cpython-32
#     Python 3.2a3  3180 (add DELETE_DEREF #4617)
#     Python 3.3a1  3190 (__class__ super closure changed)
#     Python 3.3a1  3200 (PEP 3155 __qualname__ added #13448)
#     Python 3.3a1  3210 (added size modulo 2**32 to the pyc header #13645)
#     Python 3.3a2  3220 (changed PEP 380 implementation #14230)
#     Python 3.3a4  3230 (revert changes to implicit __class__ closure #14857)
#     Python 3.4a1  3250 (evaluate positional default arguments before
#                        keyword-only defaults #16967)
#     Python 3.4a1  3260 (add LOAD_CLASSDEREF; allow locals of class to override
#                        free vars #17853)
#     Python 3.4a1  3270 (various tweaks to the __class__ closure #12370)
#     Python 3.4a1  3280 (remove implicit class argument)
#     Python 3.4a4  3290 (changes to __qualname__ computation #19301)
#     Python 3.4a4  3300 (more changes to __qualname__ computation #19301)
#     Python 3.4rc2 3310 (alter __qualname__ computation #20625)
#     Python 3.5a1  3320 (PEP 465: Matrix multiplication operator #21176)
#     Python 3.5b1  3330 (PEP 448: Additional Unpacking Generalizations #2292)
#     Python 3.5b2  3340 (fix dictionary display evaluation order #11205)
#     Python 3.5b3  3350 (add GET_YIELD_FROM_ITER opcode #24400)
#     Python 3.5.2  3351 (fix BUILD_MAP_UNPACK_WITH_CALL opcode #27286)
#     Python 3.6a0  3360 (add FORMAT_VALUE opcode #25483)
#     Python 3.6a1  3361 (lineno delta of code.co_lnotab becomes signed #26107)
#     Python 3.6a2  3370 (16 bit wordcode #26647)
#     Python 3.6a2  3371 (add BUILD_CONST_KEY_MAP opcode #27140)
#     Python 3.6a2  3372 (MAKE_FUNCTION simplification, remove MAKE_CLOSURE
#                         #27095)
#     Python 3.6b1  3373 (add BUILD_STRING opcode #27078)
#     Python 3.6b1  3375 (add SETUP_ANNOTATIONS and STORE_ANNOTATION opcodes
#                         #27985)
#     Python 3.6b1  3376 (simplify CALL_FUNCTIONs & BUILD_MAP_UNPACK_WITH_CALL
                          #27213)
#     Python 3.6b1  3377 (set __class__ cell from type.__new__ #23722)
#     Python 3.6b2  3378 (add BUILD_TUPLE_UNPACK_WITH_CALL #28257)
#     Python 3.6rc1 3379 (more thorough __class__ validation #23722)
#     Python 3.7a1  3390 (add LOAD_METHOD and CALL_METHOD opcodes #26110)
#     Python 3.7a2  3391 (update GET_AITER #31709)
#     Python 3.7a4  3392 (PEP 552: Deterministic pycs #31650)
#     Python 3.7b1  3393 (remove STORE_ANNOTATION opcode #32550)
#     Python 3.7b5  3394 (restored docstring as the first stmt in the body;
#                         this might affected the first line number #32911)
#
# MAGIC must change whenever the bytecode emitted by the compiler may no
# longer be understood by older implementations of the eval loop (usually
# due to the addition of new opcodes).
#
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.
  • 對(duì)于 pyc 文件整體的 C 結(jié)構(gòu)體喻圃,可以在 ./include/python2.7/code.h 或不同版本類似的文件中找到
    • 具體代碼如下
/* Bytecode object */
typedef struct {
    PyObject_HEAD
    int co_argcount;        /* #arguments, except *args */
    int co_nlocals;     /* #local variables */
    int co_stacksize;       /* #entries needed for evaluation stack */
    int co_flags;       /* CO_..., see below */
    PyObject *co_code;      /* instruction opcodes */
    PyObject *co_consts;    /* list (constants used) */
    PyObject *co_names;     /* list of strings (names used) */
    PyObject *co_varnames;  /* tuple of strings (local variable names) */
    PyObject *co_freevars;  /* tuple of strings (free variable names) */
    PyObject *co_cellvars;      /* tuple of strings (cell variable names) */
    /* The rest doesn't count for hash/cmp */
    PyObject *co_filename;  /* string (where it was loaded from) */
    PyObject *co_name;      /* string (name, for reference) */
    int co_firstlineno;     /* first source line number */
    PyObject *co_lnotab;    /* string (encoding addr<->lineno mapping) See
                   Objects/lnotab_notes.txt for details. */
    void *co_zombieframe;     /* for optimization only (see frameobject.c) */
    PyObject *co_weakreflist;   /* to support weakrefs to code objects */
} PyCodeObject;
  • 將 Python 源代碼生成為 Pyc 文件,這里使用的版本是 Python 2.7.6
    • 這里我們將一個(gè)名為 py1.py 的文件
import py_compile
py_compile.compile('./py1.py')
  • 會(huì)在源代碼文件目錄下找到編譯后的文件
  • 使用 010 editor 打開(kāi)蒋得,會(huì)提示是否需要安裝 pyc 字節(jié)碼輔助插件级及,雖然說(shuō)只支持 2.4 - 2.7
    • Python 3 以后的都不能用...
  • 此處以中南大學(xué)2020校賽的一道逆向題目 py&flower.pyc 作為介紹,使用 010 editor 打開(kāi)
  • 最前面的 4 個(gè)字節(jié)為 Magic Number 额衙,其中前兩個(gè)直接為解釋器的版本號(hào)

    • 此處前兩個(gè)字節(jié)為 62211饮焦,也就是 Python 2.7.0a0 版本的字節(jié)碼解釋器
    • 注意這里是小端序,就是高位在后面窍侧,所以是 0xF303
  • Magic Number 之后的四個(gè)字節(jié)為時(shí)間戳县踢,這里是 0x5EC652B0,之后就是 Python 代碼對(duì)象

  • 代碼對(duì)象首先一個(gè)字節(jié)表示此處的對(duì)象類型伟件,這里值為 TYPE_CODE硼啤,值為 0x63,

  • 此后四個(gè)字節(jié)表示參數(shù)的個(gè)數(shù)斧账,也就是 co_argcount 的值

  • 往后四個(gè)字節(jié)是局部變量的個(gè)數(shù) co_nlocals

  • 往后四個(gè)字節(jié)是椙捶担空間大小 co_stacksize

  • 往后四個(gè)字節(jié)是 co_flags

  • 之后就是 co_code 了煞肾,也就是編譯好的字節(jié)碼的部分

    • co_code 部分首先的一個(gè)字節(jié)也是表示此處的對(duì)象類型,這里是 TYPE_STRING嗓袱,為 0x73
    • 接下來(lái)四個(gè)字節(jié)表示此 co_code 對(duì)象的長(zhǎng)度籍救,此后就是代碼對(duì)象,這里的代碼長(zhǎng)度為 0xA7
    • 也就是后方 163 個(gè)字節(jié)的長(zhǎng)度都是代碼對(duì)象
  • 此 co_code 對(duì)象的字節(jié)碼內(nèi)容結(jié)束后渠抹,接著是 co_consts 內(nèi)容蝙昙,也就是用到的常量的內(nèi)容
    • 最開(kāi)始是 TYPE_TUPLE,表示這是個(gè)元組類型
    • 此后四個(gè)字節(jié)是元素個(gè)數(shù)梧却,這里是 0x23奇颠,之后每一個(gè)字節(jié)與對(duì)應(yīng)的值一組,一共 0x23 組
      • 每組中第一個(gè)字節(jié)表示元素類型放航,比如 0x69 指 TYPE_INT烈拒,此后為對(duì)應(yīng)的值
  • 后方也對(duì)應(yīng)結(jié)構(gòu)體中的相應(yīng)內(nèi)容

字節(jié)碼混淆

Anti-uncompyle6

  • 對(duì)于正常的 pyc 文件,使用 uncompyle6 插件可以正常的進(jìn)行字節(jié)碼逆向三椿,得到原來(lái)的代碼
> uncompyle6 ./py2.pyc
  • 如果需要使 uncompyle6 失效的話缺菌,只要在 co_code 頭部加上 0x71 0x03 0x00 ,然后把記錄 co_code 長(zhǎng)度的數(shù)據(jù)加 3
    • 這段字節(jié)碼指 JUMP_ABSOLUTE 3 搜锰,也就是向后跳 3 個(gè)字節(jié)后繼續(xù)執(zhí)行伴郁,實(shí)際上沒(méi)有改變代碼邏輯
    • 但是 uncompyle6 插件的還原邏輯就沒(méi)辦法識(shí)別此字節(jié)碼原先的意思,導(dǎo)致解析異常

Anti-dis

  • 上文的改法會(huì)導(dǎo)致 uncompyle6 插件異常蛋叼,但是這個(gè)方法的實(shí)質(zhì)只是增加了一句字節(jié)碼
  • Python 可以借助自帶的 dis 庫(kù)和 marshal 庫(kù)解析 pyc 二進(jìn)制文件中的信息焊傅,此處以一個(gè)簡(jiǎn)單的代碼作為例子
def fun1():
    enc = "Ua`|{f.4V}$l4h4Vx{s.4|``dg.;;vx{s:v}$l:wz;4h4Dxqugq4}zp}wu`q4`|q4g{afwq4ur`qf4m{af4pqf}bu`}{z"
    flag = ""
    for i in enc:
        flag += chr(ord(i) ^ 0x14)
    print flag
fun1()
  • 編譯成 pyc 文件后,嘗試加入 JUMP_ABSOLUTE 3 到代碼頭部
    • 橙色的字節(jié)為編輯過(guò)的
  • 使用 uncompyle6 發(fā)生 Parse error 異常狈涮,但是還是可以正常運(yùn)行
  • 嘗試使用 marshal 模塊搭配 dis 模塊進(jìn)行字節(jié)碼解析
import marshal, dis
fp = open("./py1.pyc")
fp.read(8) # Read out magic number and timestamp
co_code = marshal.load(fp)
dis.dis(co_code)
  • 程序輸出了完整的字節(jié)碼狐胎,根據(jù)字節(jié)碼還是可以順利的還原出源代碼信息
    • 可以發(fā)現(xiàn),頭部已經(jīng)加上了我們自己編輯的 JUMP_ABSOLUTE 3
> python2 -u "/Users/biox/NutStore/Codes/VSCode/python/py2.py"
  1           0 JUMP_ABSOLUTE            3
        >>    3 LOAD_CONST               0 (<code object fun1 at 0x1010486b0, file "./py1.py", line 1>)
              6 MAKE_FUNCTION            0

  7           9 STORE_NAME               0 (fun1)
             12 LOAD_NAME                0 (fun1)
             15 CALL_FUNCTION            0
             18 POP_TOP             
             19 LOAD_CONST               1 (None)
             22 RETURN_VALUE  
  • 如果我們不想讓 dis 順利的導(dǎo)出字節(jié)碼歌馍,也可以用一些指令來(lái)使得 dis 模塊產(chǎn)生異常
    • 比如來(lái)個(gè)指令重疊握巢,中間插一個(gè)讀取數(shù)據(jù)的字節(jié)碼0x71 0x04 0x00 0x64 0x71 0x08 0x00 0x00
    • 這里的 0x64 為解釋器的 LOAD_CONST 指令,如果正常的話這里應(yīng)該是 LOAD_CONST 0x0871
    • 那么 dis 模塊就看不懂了松却,實(shí)際上通過(guò)前面的 0x71 0x04 0x00 會(huì)跳過(guò)此字節(jié)碼暴浦,實(shí)際上是不執(zhí)行的
    • 后方的0x71 0x08 0x00 是根據(jù)前面第一個(gè) 0x71 開(kāi)始跳轉(zhuǎn)的,所以跟的是 0x08
  • 嘗試 dis 字節(jié)碼晓锻,直接拋出 IndexError 了歌焦,同時(shí) uncompyle6 也 IndexError 了
> python2 -u "/Users/biox/NutStore/Codes/VSCode/python/py2.py"                                                                        python
  1           0 JUMP_ABSOLUTE            4
              3 LOAD_CONST            2161
Traceback (most recent call last):
  File "/Users/biox/NutStore/Codes/VSCode/python/py2.py", line 5, in <module>
    dis.dis(co_code)
  File "/usr/local/Cellar/python@2/2.7.16_1/Frameworks/Python.framework/Versions/2.7/lib/python2.7/dis.py", line 43, in dis
    disassemble(x)
  File "/usr/local/Cellar/python@2/2.7.16_1/Frameworks/Python.framework/Versions/2.7/lib/python2.7/dis.py", line 98, in disassemble
    print '(' + repr(co.co_consts[oparg]) + ')',
IndexError: tuple index out of range
  • 如果需要還原成能正常 dis 的 pyc 文件,只能手動(dòng)修補(bǔ)了

動(dòng)態(tài)創(chuàng)建內(nèi)置類型

  • Python 還有一個(gè)自帶的庫(kù)砚哆,叫做 types独撇,借助這個(gè)庫(kù)可以生成 Python 內(nèi)置的類型
  • 比如生成 code 對(duì)象,這里以 2020 年 XCTF-CyBRICS 逆向 Ployglot 為例,其最后一層逆向的 Python 代碼就是用了此方法
import types

def define_func(argcount, nlocals, code, consts, names):
    #PYTHON3.8!!!
    def inner():
        return 0

    fn_code = inner.__code__
    cd_new = types.CodeType(argcount,
                             0,
                             fn_code.co_kwonlyargcount,
                             nlocals,
                             1024,
                             fn_code.co_flags,
                             code,
                             consts,
                             names,
                             tuple(["v%d" for i in range(nlocals)]),
                             fn_code.co_filename,
                             fn_code.co_name,
                             fn_code.co_firstlineno,
                             fn_code.co_lnotab,
                             fn_code.co_freevars,
                             fn_code.co_cellvars)
    inner.__code__ = cd_new
    return inner

f1 = define_func(2,2,b'|\x00|\x01k\x02S\x00', (None,), ())
f2 = define_func(1,1,b't\x00|\x00\x83\x01S\x00', (None,), ('ord',))
f3 = define_func(0,0,b't\x00d\x01\x83\x01S\x00', (None,  'Give me flag: '), ('input',))
f4 = define_func(1, 3, b'd\x01d\x02d\x03d\x04d\x05d\x01d\x06d\x07d\x08d\td\x03d\nd\x0bd\x0cd\rd\x08d\x0cd\x0ed\x0cd\x0fd\x0ed\x10d\x11d\td\x12d\x03d\x10d\x03d\x0ed\x13d\x0bd\nd\x14d\x08d\x13d\x01d\x01d\nd\td\x01d\x12d\x0bd\x10d\x0fd\x14d\x03d\x0bd\x15d\x16g1}\x01t\x00|\x00\x83\x01t\x00|\x01\x83\x01k\x03r\x82t\x01d\x17\x83\x01\x01\x00d\x18S\x00t\x02|\x00|\x01\x83\x02D\x00]$}\x02t\x03|\x02d\x19\x19\x00t\x04|\x02d\x1a\x19\x00\x83\x01\x83\x02d\x18k\x02r\x8c\x01\x00d\x18S\x00q\x8cd\x1bS\x00',
                 (None, 99, 121, 98, 114, 105, 115, 123, 52, 97, 100, 51, 101, 55, 57, 53, 54, 48, 49, 50, 56, 102, 125, 'Length mismatch!', False, 1, 0, True),
                 ('len', 'print', 'zip', 'f1', 'f2'))
f5 = define_func(0, 1,b't\x00\x83\x00}\x00t\x01|\x00\x83\x01d\x01k\x08r\x1ct\x02d\x02\x83\x01\x01\x00n\x08t\x02d\x03\x83\x01\x01\x00d\x00S\x00',(None, False, 'Nope!', 'Yep!'), ('f3', 'f4', 'print'))
f5()
  • 使用給定的字節(jié)碼纷铣,構(gòu)造 CodeType 對(duì)象卵史,直接轉(zhuǎn)換成函數(shù)來(lái)調(diào)用
  • 只要把對(duì)應(yīng)的 PyCodeObject 中應(yīng)該有的值構(gòu)造正確,就能順利執(zhí)行
  • 這樣的函數(shù)如果沒(méi)加花指令关炼,是可以直接 dis 出來(lái)的

如有錯(cuò)誤程腹,歡迎師傅們指正

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載匣吊,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者儒拂。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市色鸳,隨后出現(xiàn)的幾起案子社痛,更是在濱河造成了極大的恐慌,老刑警劉巖命雀,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蒜哀,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡吏砂,警方通過(guò)查閱死者的電腦和手機(jī)撵儿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)狐血,“玉大人淀歇,你說(shuō)我怎么就攤上這事⌒僦” “怎么了浪默?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)缀匕。 經(jīng)常有香客問(wèn)我纳决,道長(zhǎng),這世上最難降的妖魔是什么乡小? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任阔加,我火速辦了婚禮,結(jié)果婚禮上满钟,老公的妹妹穿的比我還像新娘胜榔。我一直安慰自己,他們只是感情好零远,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布苗分。 她就那樣靜靜地躺著,像睡著了一般牵辣。 火紅的嫁衣襯著肌膚如雪摔癣。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,328評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音择浊,去河邊找鬼戴卜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛琢岩,可吹牛的內(nèi)容都是我干的投剥。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼担孔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼江锨!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起糕篇,我...
    開(kāi)封第一講書(shū)人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤啄育,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后拌消,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體挑豌,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年墩崩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了氓英。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鹦筹,死狀恐怖铝阐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情盛龄,我是刑警寧澤饰迹,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站余舶,受9級(jí)特大地震影響啊鸭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜匿值,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一采幌、第九天 我趴在偏房一處隱蔽的房頂上張望髓窜。 院中可真熱鬧猴娩,春花似錦墩朦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至达传,卻和暖如春篙耗,著一層夾襖步出監(jiān)牢的瞬間迫筑,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工宗弯, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脯燃,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓蒙保,卻偏偏與公主長(zhǎng)得像辕棚,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子邓厕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359