一文让你完全弄懂Stegosaurus
國內關于 Stegosaurus 的介紹少之又少,一般只是單純的工具使用的講解之類的,并且本人在學習過程中也是遇到了很多的問題,基于此種情況下寫下此文,也是為我逝去的青春時光留個念想吧~
Stegosaurus是什么?
在了解 Stegosaurus 是什么之前,我們首先需要弄清楚的一個問題是:什么是隱寫術?
隱寫術,從字面上來理解,隱是隱藏,所以我們從字面上可以知道,隱寫術是一類可以隱藏自己寫的一些東西的方法,可能我們所寫的這些東西是一些比較重要的信息,不想讓別人看到,我們會考慮采取一些辦法去隱藏它,比如對所寫的文件加解密,用一些特殊的紙張(比如紙張遇到水后,上面的字才會顯示出來)之類的。隱寫術這種手段在日常生活中用的十分廣泛,我相信部分小伙伴們小時候曾經有過寫日記的習慣,寫完的日記可能不想讓爸爸媽媽知道(青春期萌動的內心,咱們都是過來人,都懂這個2333),所以以前常常會買那種上了把鎖的那種日記本,這樣就不怕自己的小秘密被爸爸媽媽知道啦。
事實上,隱寫術是一門關于信息隱藏的技巧與科學,專業一點的講,就是指的是采取一些不讓除預期的接收者之外的任何人知曉信息的傳遞事件或者信息的內容的方法。隱寫術的英文叫做 Steganography ,根據維基百科的解釋,這個英文來源于特里特米烏斯的一本講述密碼學與隱寫術的著作 Steganographia ,該書書名源于希臘語,意為“隱秘書寫”。(這個不是重點)
所以今天呢,我們要給大家介紹的是隱寫術的其中一個分支(也就是其中一種隱寫的方法),也就是 Stegosaurus , Stegosaurus 是一款隱寫工具,它允許我們在 Python 字節碼文件( pyc 或 pyo )中嵌入任意 Payload 。由于編碼密度較低,因此我們嵌入 Payload 的過程既不會改變源代碼的運行行為,也不會改變源文件的文件大小。 Payload 代碼會被分散嵌入到字節碼之中,所以類似 strings 這樣的代碼工具無法查找到實際的 Payload 。 Python 的 dis 模塊會返回源文件的字節碼,然后我們就可以使用 Stegosaurus 來嵌入 Payload 了。
為了方便維護,我將此項目移至 Github 上:https://github.com/AngelKitty/stegosaurus
首先講到一個工具,不可避免的,我們需要講解它的用法,我并不會像文檔一樣工整的把用法羅列在一起,如果需要了解更加細節的部分請參考 Github上的詳細文檔,我會拿一些實際的案例去給大家講解一些常見命令的用法,在后續的文章中,我會大家深入理解 python 反編譯的一些東西。
Stegosaurus 僅支持 Python3.6 及其以下版本
拿到一個工具,我們一般會看看它的基本用法:
python3 stegosaurus.py -h $ python3 -m stegosaurus -h usage: stegosaurus.py [-h] [-p PAYLOAD] [-r] [-s] [-v] [-x] carrierpositional arguments:carrier Carrier py, pyc or pyo fileoptional arguments:-h, --help show this help message and exit-p PAYLOAD, --payload PAYLOADEmbed payload in carrier file-r, --report Report max available payload size carrier supports-s, --side-by-side Do not overwrite carrier file, install side by sideinstead.-v, --verbose Increase verbosity once per use-x, --extract Extract payload from carrier file我們可以看到有很多參數選項,我們就以一道賽題來講解部分參數命令吧~
我們此次要講解的這道題是來自 Bugku 的 QAQ
賽題鏈接如下:
http://ctf.bugku.com/files/447e4b626f2d2481809b8690613c1613/QAQ http://ctf.bugku.com/files/5c02892cd05a9dcd1c5a34ef22dd9c5e/cipher.txt首先拿到這道題,用 010Editor 乍一眼看過去,我們可以看到一些特征信息:
可以判斷這是個跟 python 有關的東西,通過查閱相關資料可以判斷這是個 python 經編譯過后的 pyc 文件。這里可能很多小伙伴們可能不理解了,什么是 pyc 文件呢?為什么會生成 pyc 文件? pyc 文件又是何時生成的呢?下面我將一一解答這些問題。
簡單來說, pyc 文件就是 Python 的字節碼文件,是個二進制文件。我們都知道 Python 是一種全平臺的解釋性語言,全平臺其實就是 Python 文件在經過解釋器解釋之后(或者稱為編譯)生成的 pyc 文件可以在多個平臺下運行,這樣同樣也可以隱藏源代碼。其實, Python 是完全面向對象的語言, Python 文件在經過解釋器解釋后生成字節碼對象 PyCodeObject , pyc 文件可以理解為是 PyCodeObject 對象的持久化保存方式。而 pyc 文件只有在文件被當成模塊導入時才會生成。也就是說, Python 解釋器認為,只有 import 進行的模塊才需要被重用。 生成 pyc 文件的好處顯而易見,當我們多次運行程序時,不需要重新對該模塊進行重新的解釋。主文件一般只需要加載一次,不會被其他模塊導入,所以一般主文件不會生成 pyc 文件。
我們舉個例子來說明這個問題:
為了方便起見,我們事先創建一個test文件夾作為此次實驗的測試:
mkdir test && cd test/假設我們現在有個 test.py 文件,文件內容如下:
def print_test():print('Hello,Kitty!')print_test()我們執行以下命令:
python3 test.py不用說,想必大家都知道打印出的結果是下面這個:
Hello,Kitty!我們通過下面命令查看下當前文件夾下有哪些文件:
ls -alh我們可以發現,并沒有 pyc 文件生成。
‘我們再去創建一個文件為 import_test.py 文件,文件內容如下:
注: test.py 和 import_test.py 應當放在同一文件夾下
import testtest.print_test()我們執行以下命令:
python3 import_test.py結果如下:
Hello,Kitty! Hello,Kitty!誒,為啥會打印出兩句相同的話呢?我們再往下看,我們通過下面命令查看下當前文件夾下有哪些文件:
ls -alh結果如下:
總用量 20K drwxr-xr-x 3 python python 4.0K 11月 5 20:38 . drwxrwxr-x 4 python python 4.0K 11月 5 20:25 .. -rw-r--r-- 1 python python 31 11月 5 20:38 import_test.py drwxr-xr-x 2 python python 4.0K 11月 5 20:38 __pycache__ -rw-r--r-- 1 python python 58 11月 5 20:28 test.py誒,多了個 __pycache__ 文件夾,我們進入文件夾下看看有什么?
cd __pycache__ && ls我們可以看到生成了一個 test.cpython-36.pyc 。為什么是這樣子呢?
我們可以看到,我們在執行 python3 import_test.py 命令的時候,首先開始執行的是 import test ,即導入 test 模塊,而一個模塊被導入時, PVM(Python Virtual Machine) 會在后臺從一系列路徑中搜索該模塊,其搜索過程如下:
- 在當前目錄下搜索該模塊
- 在環境變量 PYTHONPATH 中指定的路徑列表中依次搜索
- 在 python 安裝路徑中搜索
事實上, PVM 通過變量 sys.path 中包含的路徑來搜索,這個變量里面包含的路徑列表就是上面提到的這些路徑信息。
模塊的搜索路徑都放在了 sys.path 列表中,如果缺省的 sys.path 中沒有含有自己的模塊或包的路徑,可以動態的加入 (sys.path.apend) 即可。
事實上, Python 中所有加載到內存的模塊都放在 sys.modules 。當 import 一個模塊時首先會在這個列表中查找是否已經加載了此模塊,如果加載了則只是將模塊的名字加入到正在調用 import 的模塊的 Local 名字空間中。如果沒有加載則從 sys.path 目錄中按照模塊名稱查找模塊文件,模塊文件可以是 py 、 pyc 、 pyd ,找到后將模塊載入內存,并加入到 sys.modules 中,并將名稱導入到當前的 Local 名字空間。
可以看出來,一個模塊不會重復載入。多個不同的模塊都可以用 import 引入同一個模塊到自己的 Local 名字空間,其實背后的 PyModuleObject 對象只有一個。
在這里,我還要說明一個問題,import 只能導入模塊,不能導入模塊中的對象(類、函數、變量等)。例如像上面這個例子,我在 test.py 里面定義了一個函數 print_test() ,我在另外一個模塊文件 import_test.py不能直接通過 import test.print_test 將 print_test 導入到本模塊文件中,只能用 import test 進行導入。如果我想只導入特定的類、函數、變量,用 from test import print_test 即可。
既然說到了 import 導入機制,再提一提嵌套導入和 Package 導入。
import 嵌套導入
嵌套,不難理解,就是一個套著一個。小時候我們都玩過俄羅斯套娃吧,俄羅斯套娃就是一個大娃娃里面套著一個小娃娃,小娃娃里面還有更小的娃娃,而這個嵌套導入也是同一個意思。假如我們現在有一個模塊,我們想要導入模塊 A ,而模塊 A 中有含有其他模塊需要導入,比如模塊 B ,模塊 B 中又含有模塊 C ,一直這樣延續下去,這種方式我們稱之為 import 嵌套導入。
對這種嵌套比較容易理解,我們需要注意的一點就是各個模塊的 Local 名字空間是獨立的,所以上面的例子,本模塊 import A 完了后,本模塊只能訪問模塊 A ,不能訪問 B 及其它模塊。雖然模塊 B 已經加載到內存了,如果要訪問,還必須明確在本模塊中導入 import B 。
那如果我們有以下嵌套這種情況,我們該怎么處理呢?
比如我們現在有個模塊 A :
# A.py from B import D class C:pass還有個模塊 B :
# B.py from A import C class D:pass我們簡單分析一下程序,如果程序運行,應該會去從模塊B中調用對象D。
我們嘗試執行一下 python A.py :
報 ImportError 的錯誤,似乎是沒有加載到對象 D ,而我們將 from B import D 改成 import B ,我們似乎就能執行成功了。
這是怎么回事呢?這其實是跟 Python 內部 import 的機制是有關的,具體到 from B import D , Python 內部會分成以下幾個步驟:
- 在 sys.modules 中查找符號 B
- 如果符號 B 存在,則獲得符號 B 對應的 module 對象 <module B> 。從 <module B> 的 __dict__ 中獲得符號 D 對應的對象,如果 D 不存在,則拋出異常
- 如果符號 B 不存在,則創建一個新的 module 對象 <module B> ,注意,此時 module 對象的 __dict__ 為空。執行 B.py 中的表達式,填充 <module B> 的 __dict__ 。從 <module B> 的 __dict__ 中獲得 D 對應的對象。如果 D 不存在,則拋出異常。
所以,這個例子的執行順序如下:
1、執行 A.py 中的 from B import D
注:由于是執行的 python A.py ,所以在 sys.modules 中并沒有 <module B> 存在,首先為 B.py 創建一個 module 對象( <module B> ),注意,這時創建的這個 module 對象是空的,里邊啥也沒有,在 Python 內部創建了這個 module 對象之后,就會解析執行 B.py ,其目的是填充 <module B> 這個 dict 。
2、執行 B.py 中的 from A import C
注:在執行 B.py 的過程中,會碰到這一句,首先檢查 sys.modules 這個 module 緩存中是否已經存在 <module A> 了,由于這時緩存還沒有緩存 <module A> ,所以類似的, Python 內部會為 A.py 創建一個 module 對象( <module A> ),然后,同樣地,執行 A.py 中的語句。
3、再次執行 A.py 中的 from B import D
注:這時,由于在第 1 步時,創建的 <module B> 對象已經緩存在了 sys.modules 中,所以直接就得到了 <module B> ,但是,注意,從整個過程來看,我們知道,這時 <module B> 還是一個空的對象,里面啥也沒有,所以從這個 module 中獲得符號 D 的操作就會拋出異常。如果這里只是 import B ,由于 B 這個符號在 sys.modules 中已經存在,所以是不會拋出異常的。
我們可以從下圖很清楚的看到 import 嵌套導入的過程:
Package 導入
包 (Package) 可以看成模塊的集合,只要一個文件夾下面有個 __init__.py 文件,那么這個文件夾就可以看做是一個包。包下面的文件夾還可以成為包(子包)。更進一步的講,多個較小的包可以聚合成一個較大的包。通過包這種結構,我們可以很方便的進行類的管理和維護,也方便了用戶的使用。比如 SQLAlchemy 等都是以包的形式發布給用戶的。
包和模塊其實是很類似的東西,如果查看包的類型: import SQLAlchemy type(SQLAlchemy) ,可以看到其實也是 <type 'module'> 。 import 包的時候查找的路徑也是 sys.path。
包導入的過程和模塊的基本一致,只是導入包的時候會執行此包目錄下的 __init__.py ,而不是模塊里面的語句了。另外,如果只是單純的導入包,而包的 __init__.py 中又沒有明確的其他初始化操作,那么此包下面的模塊是不會自動導入的。
假設我們有如下文件結構:
. └── PA├── __init__.py├── PB1│?? ├── __init__.py│?? └── pb1_m.py├── PB2│?? ├── __init__.py│?? └── pb2_m.py└── wave.pywave.py , pb1_m.py , pb2_m.py 文件中我們均定義了如下函數:
def getName():pass__init__.py 文件內容均為空。
我們新建一個 test.py ,內容如下:
import sys import PA.wave #1 import PA.PB1 #2 import PA.PB1.pb1_m as m1 #3 import PA.PB2.pb2_m #4 PA.wave.getName() #5 m1.getName() #6 PA.PB2.pb2_m.getName() #7我們運行以后,可以看出是成功執行成功了,我們再看看目錄結構:
. ├── PA │?? ├── __init__.py │?? ├── __init__.pyc │?? ├── PB1 │?? │?? ├── __init__.py │?? │?? ├── __init__.pyc │?? │?? ├── pb1_m.py │?? │?? └── pb1_m.pyc │?? ├── PB2 │?? │?? ├── __init__.py │?? │?? ├── __init__.pyc │?? │?? ├── pb2_m.py │?? │?? └── pb2_m.pyc │?? ├── wave.py │?? └── wave.pyc └── test.py我們來分析一下這個過程:
- 當執行#1 后, sys.modules 會同時存在 PA 、 PA.wave 兩個模塊,此時可以調用 PA.wave 的任何類或函數了。但不能調用 PA.PB1(2) 下的任何模塊。當前 Local 中有了 PA 名字。
- 當執行 #2 后,只是將 PA.PB1 載入內存, sys.modules 中會有 PA 、 PA.wave 、 PA.PB1 三個模塊,但是 PA.PB1 下的任何模塊都沒有自動載入內存,此時如果直接執行 PA.PB1.pb1_m.getName() 則會出錯,因為 PA.PB1 中并沒有 pb1_m 。當前 Local 中還是只有 PA 名字,并沒有 PA.PB1 名字。
- 當執行 #3 后,會將 PA.PB1 下的 pb1_m 載入內存, sys.modules 中會有 PA 、 PA.wave 、 PA.PB1 、 PA.PB1.pb1_m 四個模塊,此時可以執行 PA.PB1.pb1_m.getName() 了。由于使用了 as ,當前 Local 中除了 PA 名字,另外添加了 m1 作為 PA.PB1.pb1_m 的別名。
- 當執行 #4 后,會將 PA.PB2 、 PA.PB2.pb2_m 載入內存, sys.modules 中會有 PA 、 PA.wave 、 PA.PB1 、 PA.PB1.pb1_m 、 PA.PB2 、 PA.PB2.pb2_m 六個模塊。當前 Local 中還是只有 PA 、 m1 。
- 下面的 #5 ,#6 , #7 都是可以正確運行的。
注:需要注意的問題是如果 PA.PB2.pb2_m 想導入 PA.PB1.pb1_m 、 PA.wave 是可以直接成功的。最好是采用明確的導入路徑,對于 ../.. 相對導入路徑還是不推薦使用。
既然我們已經知道 pyc 文件的產生,再回到那道賽題,我們嘗試將 pyc 文件反編譯回 python 源碼。我們使用在線的開源工具進行嘗試:
部分代碼沒有反編譯成功???我們可以嘗試分析一下,大概意思就是讀取 cipher.txt 那個文件,將那個文件內容是通過 base64 編碼的,我們的目的是將文件內容解碼,然后又已知 key ,通過 encryt 函數進行加密的,我們可以嘗試將代碼補全:
def encryt(key, plain):cipher = ''for i in range(len(plain)):cipher += chr(ord(key[i % len(key)]) ^ ord(plain[i]))return cipherdef getPlainText():plain = ''with open('cipher.txt') as (f):while True:line = f.readline()if line:plain += lineelse:breakreturn plain.decode('base_64')def main():key = 'LordCasser'plain = getPlainText()cipher = encryt(key, plain)with open('xxx.txt', 'w') as (f):f.write(cipher)if __name__ == '__main__':main()結果如下:
YOU ARE FOOLED THIS IS NOT THAT YOU WANT GO ON DUDE CATCH THAT STEGOSAURUS提示告訴我們用 STEGOSAURUS 工具進行隱寫的,我們直接將隱藏的payload分離出來即可。
python3 stegosaurus.py -x QAQ.pyc我們得到了最終的 flag 為:flag{fin4lly_z3r0_d34d}
既然都說到這個份子上了,我們就來分析一下我們是如何通過 Stegosaurus 來嵌入 Payload 。
我們仍然以上面這個代碼為例子,我們設置腳本名稱為 encode.py 。
第一步,我們使用 Stegosaurus 來查看在不改變源文件 (Carrier) 大小的情況下,我們的 Payload 能攜帶多少字節的數據:
python3 -m stegosaurus encode.py -r現在,我們可以安全地嵌入最多24個字節的 Payload 了。如果不想覆蓋源文件的話,我們可以使用 -s 參數來單獨生成一個嵌入了 Payload 的 py 文件:
python3 -m stegosaurus encode.py -s --payload "flag{fin4lly_z3r0_d34d}"現在我們可以用 ls 命令查看磁盤目錄,嵌入了 Payload 的文件( carrier 文件)和原始的字節碼文件兩者大小是完全相同的:
注:如果沒有使用 -s 參數,那么原始的字節碼文件將會被覆蓋。
我們可以通過向 Stegosaurus 傳遞 -x 參數來提取出 Payload :
python3 -m stegosaurus __pycache__/encode.cpython-36-stegosaurus.pyc -x我們構造的 Payload 不一定要是一個 ASCII 字符串, shellcode 也是可以的:
我們重新編寫一個 example.py 模塊,代碼如下:
import sys import os import math def add(a,b):return int(a)+int(b) def sum1(result):return int(result)*3def sum2(result):return int(result)/3def sum3(result):return int(result)-3def main():a = 1b = 2result = add(a,b)print(sum1(result))print(sum2(result))print(sum3(result))if __name__ == "__main__":main()我們讓它攜帶 Payload 為 flag_is_here。
我們可以查看嵌入 Payload 之前和之后的 Python 代碼運行情況:
通過 strings 查看 Stegosaurus 嵌入了 Payload 之后的文件輸出情況( payload 并沒有顯示出來):
接下來使用 Python 的 dis 模塊來查看 Stegosaurus 嵌入 Payload 之前和之后的文件字節碼變化情況:
嵌入payload之前:
#( 11/29/18@ 5:14下午 )( python@Sakura ):~/桌面python3 -m dis example.py 1 0 LOAD_CONST 0 (0)2 LOAD_CONST 1 (None)4 IMPORT_NAME 0 (sys)6 STORE_NAME 0 (sys)2 8 LOAD_CONST 0 (0)10 LOAD_CONST 1 (None)12 IMPORT_NAME 1 (os)14 STORE_NAME 1 (os)3 16 LOAD_CONST 0 (0)18 LOAD_CONST 1 (None)20 IMPORT_NAME 2 (math)22 STORE_NAME 2 (math)4 24 LOAD_CONST 2 (<code object add at 0x7f90479778a0, file "example.py", line 4>)26 LOAD_CONST 3 ('add')28 MAKE_FUNCTION 030 STORE_NAME 3 (add)6 32 LOAD_CONST 4 (<code object sum1 at 0x7f9047977810, file "example.py", line 6>)34 LOAD_CONST 5 ('sum1')36 MAKE_FUNCTION 038 STORE_NAME 4 (sum1)9 40 LOAD_CONST 6 (<code object sum2 at 0x7f9047977ae0, file "example.py", line 9>)42 LOAD_CONST 7 ('sum2')44 MAKE_FUNCTION 046 STORE_NAME 5 (sum2)12 48 LOAD_CONST 8 (<code object sum3 at 0x7f9047977f60, file "example.py", line 12>)50 LOAD_CONST 9 ('sum3')52 MAKE_FUNCTION 054 STORE_NAME 6 (sum3)15 56 LOAD_CONST 10 (<code object main at 0x7f904798c300, file "example.py", line 15>)58 LOAD_CONST 11 ('main')60 MAKE_FUNCTION 062 STORE_NAME 7 (main)23 64 LOAD_NAME 8 (__name__)66 LOAD_CONST 12 ('__main__')68 COMPARE_OP 2 (==)70 POP_JUMP_IF_FALSE 7824 72 LOAD_NAME 7 (main)74 CALL_FUNCTION 076 POP_TOP>> 78 LOAD_CONST 1 (None)80 RETURN_VALUE嵌入 payload 之后:
#( 11/29/18@ 5:31下午 )( python@Sakura ):~/桌面python3 -m dis example.py 1 0 LOAD_CONST 0 (0)2 LOAD_CONST 1 (None)4 IMPORT_NAME 0 (sys)6 STORE_NAME 0 (sys)2 8 LOAD_CONST 0 (0)10 LOAD_CONST 1 (None)12 IMPORT_NAME 1 (os)14 STORE_NAME 1 (os)3 16 LOAD_CONST 0 (0)18 LOAD_CONST 1 (None)20 IMPORT_NAME 2 (math)22 STORE_NAME 2 (math)4 24 LOAD_CONST 2 (<code object add at 0x7f146e7038a0, file "example.py", line 4>)26 LOAD_CONST 3 ('add')28 MAKE_FUNCTION 030 STORE_NAME 3 (add)6 32 LOAD_CONST 4 (<code object sum1 at 0x7f146e703810, file "example.py", line 6>)34 LOAD_CONST 5 ('sum1')36 MAKE_FUNCTION 038 STORE_NAME 4 (sum1)9 40 LOAD_CONST 6 (<code object sum2 at 0x7f146e703ae0, file "example.py", line 9>)42 LOAD_CONST 7 ('sum2')44 MAKE_FUNCTION 046 STORE_NAME 5 (sum2)12 48 LOAD_CONST 8 (<code object sum3 at 0x7f146e703f60, file "example.py", line 12>)50 LOAD_CONST 9 ('sum3')52 MAKE_FUNCTION 054 STORE_NAME 6 (sum3)15 56 LOAD_CONST 10 (<code object main at 0x7f146e718300, file "example.py", line 15>)58 LOAD_CONST 11 ('main')60 MAKE_FUNCTION 062 STORE_NAME 7 (main)23 64 LOAD_NAME 8 (__name__)66 LOAD_CONST 12 ('__main__')68 COMPARE_OP 2 (==)70 POP_JUMP_IF_FALSE 7824 72 LOAD_NAME 7 (main)74 CALL_FUNCTION 076 POP_TOP>> 78 LOAD_CONST 1 (None)80 RETURN_VALUE注: Payload 的發送和接受方法完全取決于用戶個人喜好, Stegosaurus 只提供了一種向 Python 字節碼文件嵌入或提取 Payload 的方法。但是為了保證嵌入之后的代碼文件大小不會發生變化,因此 Stegosaurus 所支持嵌入的 Payload 字節長度十分有限。因此 ,如果你需要嵌入一個很大的 Payload ,那么你可能要將其分散存儲于多個字節碼文件中了。
為了在不改變源文件大小的情況下向其嵌入 Payload ,我們需要識別出字節碼中的無效空間( Dead Zone )。這里所謂的無效空間指的是那些即使被修改也不會改變原 Python 腳本正常行為的那些字節數據。
需要注意的是,我們可以輕而易舉地找出 Python3.6 代碼中的無效空間。 Python 的引用解釋器 CPython 有兩種類型的操作碼:即無參數的和有參數的。在版本號低于 3.5 的 Python 版本中,根據操作碼是否帶參,字節碼中的操作指令將需要占用 1 個字節或 3 個字節。在 Python3.6 中就不一樣了, Python3.6 中所有的指令都占用 2 個字節,并會將無參數指令的第二個字節設置為 0 ,這個字節在其運行過程中將會被解釋器忽略。這也就意味著,對于字節碼中每一個不帶參數的操作指令, Stegosaurus 都可以安全地嵌入長度為 1 個字節的 Payload 代碼。
我們可以通過 Stegosaurus 的 -vv 選項來查看 Payload 是如何嵌入到這些無效空間之中的:
#( 11/29/18@10:35下午 )( python@Sakura ):~/桌面python3 -m stegosaurus example.py -s -p "ABCDE" -vv 2018-11-29 22:36:26,795 - stegosaurus - DEBUG - Validated args 2018-11-29 22:36:26,797 - stegosaurus - INFO - Compiled example.py as __pycache__/example.cpython-36.pyc for use as carrier 2018-11-29 22:36:26,797 - stegosaurus - DEBUG - Read header and bytecode from carrier 2018-11-29 22:36:26,798 - stegosaurus - DEBUG - POP_TOP (0) 2018-11-29 22:36:26,798 - stegosaurus - DEBUG - POP_TOP (0) 2018-11-29 22:36:26,798 - stegosaurus - DEBUG - POP_TOP (0) 2018-11-29 22:36:26,798 - stegosaurus - DEBUG - RETURN_VALUE (0) 2018-11-29 22:36:26,798 - stegosaurus - DEBUG - BINARY_SUBTRACT (0) 2018-11-29 22:36:26,798 - stegosaurus - DEBUG - RETURN_VALUE (0) 2018-11-29 22:36:26,798 - stegosaurus - DEBUG - BINARY_TRUE_DIVIDE (0) 2018-11-29 22:36:26,798 - stegosaurus - DEBUG - RETURN_VALUE (0) 2018-11-29 22:36:26,798 - stegosaurus - DEBUG - BINARY_MULTIPLY (0) 2018-11-29 22:36:26,798 - stegosaurus - DEBUG - RETURN_VALUE (0) 2018-11-29 22:36:26,798 - stegosaurus - DEBUG - BINARY_ADD (0) 2018-11-29 22:36:26,798 - stegosaurus - DEBUG - RETURN_VALUE (0) 2018-11-29 22:36:26,798 - stegosaurus - DEBUG - POP_TOP (0) 2018-11-29 22:36:26,798 - stegosaurus - DEBUG - RETURN_VALUE (0) 2018-11-29 22:36:26,798 - stegosaurus - INFO - Found 14 bytes available for payload Payload embedded in carrier 2018-11-29 22:36:26,799 - stegosaurus - DEBUG - POP_TOP (65) ----A 2018-11-29 22:36:26,799 - stegosaurus - DEBUG - POP_TOP (66) ----B 2018-11-29 22:36:26,799 - stegosaurus - DEBUG - POP_TOP (67) ----C 2018-11-29 22:36:26,799 - stegosaurus - DEBUG - RETURN_VALUE (68) ----D 2018-11-29 22:36:26,799 - stegosaurus - DEBUG - BINARY_SUBTRACT (69) ----E 2018-11-29 22:36:26,799 - stegosaurus - DEBUG - RETURN_VALUE (0) 2018-11-29 22:36:26,799 - stegosaurus - DEBUG - BINARY_TRUE_DIVIDE (0) 2018-11-29 22:36:26,799 - stegosaurus - DEBUG - RETURN_VALUE (0) 2018-11-29 22:36:26,799 - stegosaurus - DEBUG - BINARY_MULTIPLY (0) 2018-11-29 22:36:26,799 - stegosaurus - DEBUG - RETURN_VALUE (0) 2018-11-29 22:36:26,799 - stegosaurus - DEBUG - BINARY_ADD (0) 2018-11-29 22:36:26,799 - stegosaurus - DEBUG - RETURN_VALUE (0) 2018-11-29 22:36:26,799 - stegosaurus - DEBUG - POP_TOP (0) 2018-11-29 22:36:26,799 - stegosaurus - DEBUG - RETURN_VALUE (0) 2018-11-29 22:36:26,799 - stegosaurus - DEBUG - Creating new carrier file name for side-by-side install 2018-11-29 22:36:26,799 - stegosaurus - INFO - Wrote carrier file as __pycache__/example.cpython-36-stegosaurus.pyc參考文獻
- https://bitbucket.org/jherron/stegosaurus/src
- https://github.com/AngelKitty/stegosaurus
- https://www.freebuf.com/sectool/129357.html
轉載于:https://www.cnblogs.com/ECJTUACM-873284962/p/10041534.html
總結
以上是生活随笔為你收集整理的一文让你完全弄懂Stegosaurus的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASPxPopupControl出现前一
- 下一篇: 呼叫中心crm系统是什么(呼叫中心crm