王小強(qiáng),顧乃杰
(1.中國(guó)科學(xué)技術(shù)大學(xué) a.計(jì)算機(jī)科學(xué)與技術(shù)學(xué)院; b.先進(jìn)技術(shù)研究院,合肥 230027;2.安徽省計(jì)算與通信軟件重點(diǎn)實(shí)驗(yàn)室,合肥 230027)
Python是一種面向?qū)ο蟮慕忉屝陀?jì)算機(jī)程序設(shè)計(jì)語(yǔ)言,由于語(yǔ)法簡(jiǎn)潔清晰、簡(jiǎn)單易學(xué)、免費(fèi)、開源、擴(kuò)展性強(qiáng)等優(yōu)點(diǎn),自從20世紀(jì)90年代誕生以來(lái),就廣受歡迎和使用,如網(wǎng)絡(luò)文件同步工具Dropbox、社交新聞?wù)军c(diǎn)Reddit、網(wǎng)絡(luò)問答社區(qū)知乎,都是由Python編程語(yǔ)言所開發(fā)。Python字節(jié)碼文件(.pyc)由Python編程語(yǔ)言編寫的程序編譯而成,與傳統(tǒng)的針對(duì)特定處理器和操作系統(tǒng)的二進(jìn)制文件相比,Python字節(jié)碼文件保留了Python源碼文件中的全部信息,是針對(duì)Python虛擬機(jī)的具有特定結(jié)構(gòu)和特征的文件,具有可跨平臺(tái)使用的特性。正是因?yàn)樽止?jié)碼文件的這種結(jié)構(gòu)和特征,導(dǎo)致其極易被攻擊者反編譯出其中的源碼,如使用uncompyle2[1]、Decompyle++[2]、Easy Python Decompiler[3]等工具就可以將Python字節(jié)碼文件反編譯為源碼文件,這不僅會(huì)造成重要數(shù)據(jù)結(jié)構(gòu)、算法、業(yè)務(wù)邏輯的暴露,而且會(huì)給開發(fā)者帶來(lái)巨大的經(jīng)濟(jì)損失,有時(shí)甚至具有嚴(yán)重的安全隱患。
針對(duì)上述問題,本文提出一種基于操作碼合并的Python程序防逆轉(zhuǎn)算法。在不影響Python程序正確執(zhí)行的前提下,引入新操作碼對(duì)原Python操作碼集進(jìn)行擴(kuò)充,并在編譯生成字節(jié)碼文件時(shí)使用新操作碼來(lái)代替操作碼序列中的2個(gè)或多個(gè)操作碼,從而縮短Python字節(jié)碼文件中操作碼序列的長(zhǎng)度,改變操作碼序列的結(jié)構(gòu)和語(yǔ)義。
代碼混淆旨在通過布局混淆、數(shù)據(jù)混淆、控制流混淆[4-5]等措施,將一個(gè)程序轉(zhuǎn)換為能夠妨礙攻擊者理解其中算法和數(shù)據(jù)結(jié)構(gòu)或能夠阻止攻擊者從程序文本中提取有價(jià)值信息的另一種形式[6-7]。
雖然Java、Python等腳本語(yǔ)言經(jīng)代碼混淆后編譯生成的字節(jié)碼文件可以跨平臺(tái)使用,但是其仍然遵守原來(lái)的文件格式和指令集,因此,代碼混淆技術(shù)對(duì)Python字節(jié)碼文件的保護(hù)能力不足。此外,代碼混淆在處理多文件項(xiàng)目中的導(dǎo)入模塊和對(duì)象名稱時(shí)有局限性,且該方法會(huì)給代碼執(zhí)行效率帶來(lái)一定的影響。
本地編譯是一種將Python腳本程序和解釋器一起編譯為平臺(tái)相關(guān)程序的技術(shù),其工作步驟為[8]:首先編寫Python源碼程序,然后借助特定的工具(如py2exe[9])將目標(biāo)腳本程序進(jìn)行轉(zhuǎn)換并且將Python解釋器編譯為平臺(tái)相關(guān)的動(dòng)態(tài)鏈接庫(kù),最后運(yùn)用一個(gè)額外的可執(zhí)行程序來(lái)運(yùn)行該動(dòng)態(tài)鏈接程序和轉(zhuǎn)換后的文件。
雖然本地編譯技術(shù)對(duì)Python程序起到了一定的保護(hù)作用,但是采用這種方式,Python程序在發(fā)布時(shí)需要同時(shí)發(fā)布解釋器對(duì)應(yīng)的動(dòng)態(tài)鏈接程序和腳本文件所依賴的所有庫(kù)文件,導(dǎo)致目標(biāo)程序所占用的空間大大增加。
數(shù)字水印通常是永久鑲嵌在宿主數(shù)據(jù)中的具有可鑒別性的數(shù)字信號(hào)或模式,其可以用來(lái)標(biāo)識(shí)作者、所有者、發(fā)行者、使用者等信息[10-11],且不影響宿主數(shù)據(jù)的可用性。使用數(shù)字水印技術(shù)并不能阻止字節(jié)碼文件被反編譯[12],但是能夠阻止數(shù)字產(chǎn)品被偷竊,或者當(dāng)偷竊發(fā)生時(shí)為使用者提供數(shù)字產(chǎn)品的擁有權(quán)證明[13]。
雖然理論上水印能夠作為軟件所有權(quán)的有力憑證,但實(shí)際中無(wú)論是靜態(tài)水印還是動(dòng)態(tài)水印,都很容易通過混淆變換和優(yōu)化等措施從軟件中被移除[14-15],因此,水印技術(shù)并不能為Java、Python等字節(jié)碼文件提供有效保護(hù)。
除上述防逆轉(zhuǎn)方法外,研究人員在操作碼替換領(lǐng)域也進(jìn)行了研究。操作碼替換技術(shù)是將字節(jié)碼文件中的每個(gè)操作碼替換為其他操作碼,使得編譯生成的字節(jié)碼文件對(duì)于標(biāo)準(zhǔn)的解釋器而言包含非法的操作碼序列,以此達(dá)到防逆轉(zhuǎn)的目的。
操作碼替換技術(shù)已分別被基于虛擬機(jī)Dropbox[16]和基于PC的應(yīng)用,在操作碼隨機(jī)化的安全[17]中實(shí)現(xiàn),且網(wǎng)絡(luò)上已有工具python-obfuscation[18]對(duì)Python操作碼進(jìn)行隨機(jī)替換。但是,該技術(shù)的實(shí)質(zhì)是利用密碼學(xué)中的單表代替密碼[19],如果攻擊者了解采取的保護(hù)方式,就可以利用操作碼的一些統(tǒng)計(jì)學(xué)規(guī)律進(jìn)行分析,如采用頻率分析進(jìn)行攻擊,因此,該技術(shù)的安全性不足。
Python字節(jié)碼文件易被反編譯的主要原因是字節(jié)碼文件保留了Python源碼文件的所有信息,且字節(jié)碼文件中操作碼序列的結(jié)構(gòu)和每個(gè)操作碼的含義極易被攻擊者分析和理解。因此,改變操作碼序列的結(jié)構(gòu)或隱藏操作碼的含義,是保護(hù)Python字節(jié)碼文件的重要方法,但是,這類方法又會(huì)對(duì)虛擬機(jī)執(zhí)行字節(jié)碼文件的結(jié)果造成影響。鑒于此,本文將探索如何利用窺孔優(yōu)化策略,在不影響字節(jié)碼文件正確執(zhí)行的前提下,通過引入新操作碼來(lái)對(duì)Python操作碼集進(jìn)行擴(kuò)充,進(jìn)而改變Python字節(jié)碼文件中操作碼序列的結(jié)構(gòu),最終達(dá)到防逆轉(zhuǎn)的目的。
定義1操作碼
操作碼即Python虛擬機(jī)中的指令碼,占1 Byte的長(zhǎng)度,其規(guī)定了Python虛擬機(jī)需要執(zhí)行哪一條指令。目前,Python 2.7.9版本的虛擬機(jī)規(guī)范中定義了110個(gè)操作碼,其中不帶參數(shù)的操作碼共61個(gè),帶一個(gè)參數(shù)的操作碼共49個(gè),每個(gè)參數(shù)占2 Byte的長(zhǎng)度。
定義2操作碼序列
一個(gè)字節(jié)碼文件的操作碼序列S由操作碼和其所帶的參數(shù)構(gòu)成,操作碼序列結(jié)構(gòu)如圖1所示,其中,OPi為操作碼,ARGij為OPi的參數(shù)。OPi、ARGij均為正整數(shù),1≤i≤n,1≤j≤2,n為一個(gè)字節(jié)碼文件所包含的操作碼總數(shù)。
圖1操作碼序列示意圖
Python虛擬機(jī)執(zhí)行字節(jié)碼文件的本質(zhì)是利用循環(huán)依序讀取操作碼序列中的一個(gè)操作碼OPi和2個(gè)字節(jié)參數(shù)ARGi1與ARGi2,然后查找并執(zhí)行OPi對(duì)應(yīng)的解釋程序,修改虛擬機(jī)內(nèi)部眾多的狀態(tài)值,接著再讀取下一個(gè)操作碼OP(i+1)和字節(jié)參數(shù)ARG(i+1)1與ARG(i+1)2,重復(fù)上述步驟直至操作碼序列讀取結(jié)束。
定義3基本塊
基本塊是由S中順序執(zhí)行的若干個(gè)操作碼構(gòu)成的序列。通常情況下,若S中沒有出現(xiàn)條件跳轉(zhuǎn)、絕對(duì)跳轉(zhuǎn)操作,則這些操作碼處于同一個(gè)基本塊中。
定義4基本塊信息
字節(jié)碼文件的基本塊信息B是一個(gè)由正整數(shù)構(gòu)成的單調(diào)不減序列,其長(zhǎng)度與操作碼序列S相同,且其中的每個(gè)元素與S中的每個(gè)操作碼一一對(duì)應(yīng),元素值為對(duì)應(yīng)的操作碼在S中所在的基本塊號(hào)。圖2所示為一個(gè)基本塊信息,從圖中可以看出,S中第1個(gè)操作碼處于第1個(gè)基本塊,第2個(gè)~第5個(gè)操作碼處于第2個(gè)基本塊,依此類推。
圖2基本塊信息示意圖
本文提出的操作碼合并算法流程如圖3所示。其中,操作碼序列提取與頻率統(tǒng)計(jì)的目的是尋找待合并的操作碼序列;基本塊信息提取是為了從大量的待合并操作碼序列中選出可以合并的序列,減少對(duì)操作碼集擴(kuò)充的干擾;操作碼集擴(kuò)充是添加新操作碼的定義與解釋執(zhí)行。
圖3操作碼合并算法流程
2.2.1 操作碼序列提取
本文提取Python/Lib下的1 950個(gè)庫(kù)文件源程序?qū)?yīng)的1 950個(gè)操作碼序列。這些程序通常提供接口給其他模塊進(jìn)行導(dǎo)入,其功能多樣且不會(huì)隨意發(fā)生改變,選它們進(jìn)行操作碼序列提取較為合理。1 950個(gè)操作碼序列中,最長(zhǎng)的操作碼序列包含10 000多個(gè)操作碼,最短的也包含60個(gè)操作碼,共有操作碼240 263個(gè)。
2.2.2 頻率統(tǒng)計(jì)
頻率統(tǒng)計(jì)的目的是獲取候選合并對(duì)象。在2.2.1節(jié)獲取多個(gè)操作碼序列Sx的基礎(chǔ)上,統(tǒng)計(jì)出Sx中所有長(zhǎng)度為len的操作碼子序列出現(xiàn)的次數(shù),并按出現(xiàn)次數(shù)從高到底進(jìn)行排序,其中,2≤len,1≤x≤k。
2.2.3 基本塊信息提取
基本塊信息的提取是在眾多的候選子序列中篩選出可以合并的子序列。在2.2.2節(jié)的排序結(jié)果中,靠前的對(duì)象并不一定都是可以進(jìn)行合并的操作碼子序列,原因是有些子序列中的操作碼處于不同的基本塊中,如果將該類操作碼進(jìn)行合并,將會(huì)破壞程序的執(zhí)行邏輯。因此,需要進(jìn)一步根據(jù)程序執(zhí)行邏輯來(lái)提取上述操作碼序列Sx的基本塊信息Bx,計(jì)算表達(dá)式如下:
其中,n為Sx中操作碼的個(gè)數(shù),1≤i 表1 部分長(zhǎng)度為2 Byte的操作碼子序列以及其出現(xiàn)次數(shù) 2.2.4 操作碼集擴(kuò)充 操作碼集擴(kuò)充是利用窺孔優(yōu)化策略將出現(xiàn)頻率較高且處于同一個(gè)基本塊中的操作碼子序列(OP1,OP2,…,OPlen)合并為一個(gè)新的操作碼OPnew,并在Python虛擬機(jī)中添加對(duì)OPnew的解釋執(zhí)行過程,使得對(duì)OPnew的解釋執(zhí)行效果與依次執(zhí)行(OP1,OP2,…,OPlen)的效果等價(jià)。 圖4 操作碼合并示意圖 因?yàn)樵赑ython字節(jié)碼序列中任意一個(gè)操作碼僅占1 Byte的大小,所以最多可以定義28個(gè)操作碼。而在Python 2.7.9版本的虛擬機(jī)規(guī)范中僅使用了其中的110個(gè)操作碼,因此,可以利用剩余的146個(gè)操作碼來(lái)定義新操作碼,從而在利用操作碼合并縮短序列的同時(shí)隱藏操作碼序列的含義。本文對(duì)表1中的5個(gè)子序列進(jìn)行合并,并在Python 2.7.9中利用剩余的146個(gè)操作碼中的5個(gè)來(lái)表示合并后的OPnew。 Python 3在Python 2的基礎(chǔ)上對(duì)某些語(yǔ)法和模塊進(jìn)行調(diào)整和改動(dòng),因?yàn)橥瑯幼裱僮鞔a僅占1 Byte長(zhǎng)度的特性和操作碼解釋執(zhí)行的機(jī)制,所以操作碼合并算法可以在Python 3上實(shí)現(xiàn),如Python 3.6.1使用了256個(gè)操作碼中的119個(gè),只能利用剩余的137個(gè)操作碼來(lái)定義新操作碼。 在操作碼僅占1 Byte長(zhǎng)度的基礎(chǔ)上進(jìn)行操作碼合并,會(huì)大大限制操作碼合并的擴(kuò)展空間。若要生成和使用更多的操作碼,則必須增加操作碼所占存儲(chǔ)空間的大小(如將操作碼大小全部設(shè)為2 Byte或?qū)⒉糠植僮鞔a設(shè)為2 Byte),但該操作會(huì)因多次讀取操作碼而導(dǎo)致程序執(zhí)行效率下降。 2.3.1 安全性 Ox=min(256nx+2mx,f) (1) 2.3.2 執(zhí)行效率 當(dāng)Python虛擬機(jī)執(zhí)行字節(jié)碼文件時(shí),每次只能讀取一個(gè)操作碼和一個(gè)參數(shù),并調(diào)用相應(yīng)的解釋執(zhí)行過程。操作碼合并前后Python虛擬機(jī)執(zhí)行字節(jié)碼文件運(yùn)行過程如圖5所示。 圖5 操作碼合并前后執(zhí)行過程對(duì)比 如圖5(a)所示,由于操作碼序列中包含ADD、SUB列操作,因此需要讀取2次操作碼和參數(shù)以對(duì)棧頂?shù)?個(gè)元素a和b出棧、求和,將結(jié)果r1壓棧,然后對(duì)棧頂元素r1和c出棧、求差,將結(jié)果r再壓棧。式(2)為操作碼合并前Python虛擬機(jī)執(zhí)行源碼文件filex.py的時(shí)間。 (2) 如圖5(b)所示,操作碼合并后用新操作碼ADD_SUB代替原先的2個(gè)操作碼,用新操作碼一次完成棧頂3個(gè)元素a、b、c出棧,并將a+b-c的結(jié)果r運(yùn)算完之后再壓棧。該操作雖然增加了編譯源碼文件所需的時(shí)間,但減少了冗余的讀取操作及修改Python虛擬機(jī)內(nèi)部狀態(tài)的次數(shù),且編譯后的字節(jié)碼文件在以后的運(yùn)行過程中無(wú)需再次編譯,從而提升了字節(jié)碼文件的執(zhí)行效率。 式(3)為操作碼合并后Python虛擬機(jī)執(zhí)行源碼文件filex.py的時(shí)間。 (3) 操作碼合并算法的整體性能與合并前后的字節(jié)碼文件執(zhí)行時(shí)間有關(guān)。式(4)為操作碼合并前后字節(jié)碼文件執(zhí)行時(shí)間的變化率。 (4) 2.3.3 存儲(chǔ)空間 操作碼合并將操作碼序列Sx中連續(xù)出現(xiàn)的多個(gè)操作碼合并為一個(gè)新操作碼,在不減少Sx中參數(shù)個(gè)數(shù)的同時(shí)減少了操作碼的個(gè)數(shù),從而縮短了操作碼序列的長(zhǎng)度,如原本需占用(nx+2mx) Byte的操作碼序列,現(xiàn)只需占用(nx+2mx-gx(num)) Byte即可,字節(jié)碼文件的大小縮小了(gx(num)) Byte。在需要存儲(chǔ)和導(dǎo)入大量模塊時(shí),該方法有利于節(jié)約計(jì)算機(jī)磁盤和內(nèi)存。同時(shí),基于操作碼合并生成的字節(jié)碼文件需要新的解釋器來(lái)解釋執(zhí)行,這就需要客戶安裝指定的Python解釋器。雖然在首次使用時(shí)會(huì)給客戶帶來(lái)一些不便(以后無(wú)需再次安裝),但該過程也是為了確保程序的安全性。 本文基于Python 2.7.9版本,實(shí)驗(yàn)測(cè)試平臺(tái)為Intel Xeon E5-2690,主頻2.6 GHz,252 GB內(nèi)存,操作系統(tǒng)為CentOS 6.7 64位,實(shí)驗(yàn)對(duì)象為表1中的5對(duì)操作碼。采用microbenchmark[20]來(lái)對(duì)新生成運(yùn)行環(huán)境的防逆轉(zhuǎn)效果、執(zhí)行效率、文件大小進(jìn)行檢驗(yàn)。microbenchmark包含數(shù)字運(yùn)算、字符處理等多個(gè)方面的Python源碼文件,是一個(gè)較好的測(cè)試集。 3.2.1 安全性 本文通過操作碼合并改變了原操作碼序列的結(jié)構(gòu),對(duì)原操作序列信息進(jìn)行了較好的隱藏。傳統(tǒng)的反編譯工具uncompile2、Decompyle++等無(wú)法識(shí)別合并后的操作碼序列結(jié)構(gòu),且無(wú)法理解新操作碼所包含的語(yǔ)義信息,因此,無(wú)法對(duì)新的字節(jié)碼文件進(jìn)行反編譯。 3.2.2 執(zhí)行效率 表2 操作碼合并前后文件執(zhí)行時(shí)間比較 從表2中的實(shí)驗(yàn)數(shù)據(jù)可以看出,使用操作碼合并算法在參數(shù)個(gè)數(shù)不變的前提下,減少了字節(jié)碼文件中操作碼序列的操作碼個(gè)數(shù),即減少了CPU讀取操作碼的次數(shù)和多個(gè)操作碼執(zhí)行之間的跳轉(zhuǎn)次數(shù),從而縮短了程序的執(zhí)行時(shí)間。 從表2實(shí)驗(yàn)數(shù)據(jù)中還可以看出,操作碼合并后test.pyc和pydigits.pyc文件執(zhí)行時(shí)間較之前有明顯的減少,通過分析發(fā)現(xiàn)其原因?yàn)?test.pyc操作碼序列中包含的操作碼個(gè)數(shù)較少,只需要合并若干個(gè)操作碼就可以使其個(gè)數(shù)有明顯的減少;合并前的pydigits.pyc操作碼包含在循環(huán)中,因?yàn)檠h(huán)中的操作碼被多次執(zhí)行,每次執(zhí)行的時(shí)間較合并前都有所減少,所以使得整個(gè)操作碼序列的執(zhí)行時(shí)間有明顯減少。 3.2.3 存儲(chǔ)空間 表3 操作碼合并前后文件大小比較 從表3可以看出,操作碼合并后所有字節(jié)碼文件的大小都有所減小。 本文提出基于操作碼合并的Python字節(jié)碼文件防逆轉(zhuǎn)算法,與文獻(xiàn)[21]基于隱藏參數(shù)的方法相同,都是通過擴(kuò)充操作碼集來(lái)改變操作碼序列的內(nèi)容和結(jié)構(gòu),最終達(dá)到防逆轉(zhuǎn)的目的。雖然目前兩者都受限于操作碼僅占1 Byte長(zhǎng)度的特性,存在擴(kuò)展空間不足的問題,但本文算法建立在對(duì)操作碼序列特征統(tǒng)計(jì)分析的基礎(chǔ)上,其不會(huì)因程序運(yùn)行時(shí)參數(shù)的變化而產(chǎn)生影響,通用性較強(qiáng)。實(shí)驗(yàn)結(jié)果表明,在字節(jié)碼文件執(zhí)行效率和文件大小兩方面,本文算法在操作碼合并前后都有明顯提升。為進(jìn)一步提升字節(jié)碼文件的安全性和算法的適用性,下一步將考慮通過抽象語(yǔ)法樹的變換和優(yōu)化,對(duì)多基本塊之間的操作碼進(jìn)行合并操作。 [1] Mysterie.A Python 2.7 byte-code decompiler[EB/OL].[2017-03-10].https://github.com/wibiti/uncompyle2. [2] Niwit.Decompyle-Python decompiler[EB/OL].[2017-03-10].https://sourceforge.net/projects/decompyle/?source=typ_redirect. [3] Extremecoders.Python 1.0-3.4 bytecode decompiler[EB/OL].[2017-03-10].https://sourceforge.net/projects/easypytho ndecompiler/. [4] 蔣 華,劉 勇,王 鑫.基于控制流的代碼混淆技術(shù)研究[J].計(jì)算機(jī)應(yīng)用研究,2013,30(3):897-899. [5] 楊 樂,周強(qiáng)強(qiáng),薛錦云.基于垃圾代碼的控制流混淆算法[J].計(jì)算機(jī)工程,2011,37(12):23-25. [6] KUZURIN N,SHOKUROV A,VARNOVSKY N,et al.On the concept of software obfuscation in computer security[C]//Proceedings of International Conference on Information Security.Washington D.C.,USA:IEEE Press,2007:281-298. [7] 徐海銀,雷植洲,李 丹.代碼混淆技術(shù)研究[J].計(jì)算機(jī)與數(shù)字工程,2007,35(10):4-7. [8] 鮑福良,彭俊艷,方志剛.Java類文件保護(hù)方法綜述[J].計(jì)算機(jī)系統(tǒng)應(yīng)用,2007,16(6):124-126. [9] Py2exe:A Python distutils extension which converts Python scripts into executable windows programs[EB/OL].[2017-03-10].http://www.py2exe.org/. [10] 陳明奇,鈕心忻.數(shù)字水印的研究進(jìn)展和應(yīng)用[J].通信學(xué)報(bào),2001,22(5):71-79. [11] 孫圣和,陸哲明.數(shù)字水印處理技術(shù)[J].電子學(xué)報(bào),2000,28(8):85-90. [12] 陳 晗,趙軼群,繆亞波.Java字節(jié)碼的水印嵌入[J].計(jì)算機(jī)應(yīng)用,2003,23(9):96-98. [13] COLLBERG C S,THOMBORSON C.Watermarking tamper-proofing and obfuscation[J].IEEE Transactions on Software Engineering,2000,28(8):735-746. [14] HAMILTON J,DANICIC S.An evaluation of static Java bytecode watermarking[EB/OL].[2017-02-25].https://jameshamilton.eu/sites/default/files/JavaBytecodeWatermar kingSurvey.pdf. [15] KUMAR K,KEHAR V,KAUR P.An evaluation of dynamic Java bytecode software watermarking algori-thms[J].International Journal of Security and Its Applications,2016,10(7):147-156. [16] KHOLIA D,WEGRZYN P.Looking inside the (Drop) box[EB/OL].[2017-03-01].https://www.usenix.org/system/files/conference/woot13/woot13-kholia.pdf. [17] J.C.斯普拉德林.通過操作碼隨機(jī)化的安全:CN 102592082 A[P].2012-07-18. [18] Omab.Python-obfuscation[EB/OL].[2017-03-10].https://github.com/citrusbyte/python-obfuscation. [19] STALLINGS M.Cryptography and network security[M].王章宜,楊 敏,杜瑞穎,等,譯.北京:電子工業(yè)出版社,2012. [20] MODZELEWSKI K.Microbenchmarks[EB/OL].[2017-03-10].https://github.com/dropbox/pyston/tree/master/micro benchmarks. [21] GUELTON S.Building an obfuscated Python interpreter:we need more opcodes[EB/OL].[2017-03-10].https://blog.quarkslab.com/building-an-obfuscated-python-interpreter-we-need-more-opcodes.html.2.3 算法性能分析
3 實(shí)驗(yàn)結(jié)果與分析
3.1 實(shí)驗(yàn)環(huán)境
3.2 結(jié)果分析
4 結(jié)束語(yǔ)