趙利軍 董莎莎
(陸軍工程大學(xué)軍事理論創(chuàng)新與作戰(zhàn)實(shí)驗(yàn)中心 江蘇 徐州 221000)
ARM架構(gòu)處理器在嵌入式領(lǐng)域占有絕對(duì)的領(lǐng)先優(yōu)勢(shì),其市場(chǎng)占有率逐年保持持續(xù)增長(zhǎng)狀態(tài)。2011年ARM公司發(fā)布新一代處理器架構(gòu)-ARMv8[1]。ARMv8處理器64位處理能力進(jìn)一步擴(kuò)大了其在高性能低功耗領(lǐng)域的地位。ARMv8新型體系架構(gòu)即將在網(wǎng)絡(luò)設(shè)備、智能移動(dòng)設(shè)備和高端服務(wù)器等領(lǐng)域得以大規(guī)模部署。隨著ARMv8架構(gòu)的持續(xù)發(fā)展,其上的安全問(wèn)題也必將越來(lái)越受到研究者關(guān)注。2010年Davi等[2]在前人研究基礎(chǔ)上提出了ARM架構(gòu)上的JOP(jump-oriented programming)攻擊。ARM架構(gòu)上的JOP攻擊使用以BLX Rm指令結(jié)尾的gadget。
2014年,南京大學(xué)的邢驍?shù)萚5]在前人的研究基礎(chǔ)之上提出了一種新的攻擊技術(shù)——自動(dòng)構(gòu)造增強(qiáng)型ROP攻擊(BIOP)。該技術(shù)使用JMP指令或CALL指令結(jié)尾的短指令序列構(gòu)造攻擊。相比于以前的工作,BIOP不引入新的特征,能有效避免針對(duì)ROP攻擊和JOP攻擊的防御技術(shù)。同年,南京大學(xué)的Chao Yang等[6]提出了一種基于長(zhǎng)指令序列的ROP攻擊技術(shù)(A-R攻擊),成功地避開(kāi)了Ping Chen的檢測(cè)方法。2015年楊超[7]在此基礎(chǔ)上提出了一種自動(dòng)化ROP攻擊工具,該工具可以自動(dòng)填充與連接方案,保證圖靈完備的情況下降低其集合之間的依賴(lài)性,保證修正后的gadget可以滿足自動(dòng)化構(gòu)建的需要。2015年林志添[9]提出了一種不完全依賴(lài)棧的ROP攻擊技術(shù),該方法將gadgets地址、運(yùn)算數(shù)據(jù)存放在堆中,通過(guò)一些類(lèi)似于KeyFrame整形溢出的漏洞方式劫持程序流來(lái)運(yùn)行g(shù)adgets從而完成惡意計(jì)算的攻擊技術(shù)。2018年,彭建山等[12]針對(duì)可用gadget數(shù)量少、實(shí)現(xiàn)難度大等問(wèn)題,提出了一種基于多路徑分發(fā)的ROP框架構(gòu)造方法。基于3種類(lèi)型的gadget模塊構(gòu)造了一個(gè)gadget循環(huán)執(zhí)行的框架,框架內(nèi)可以使用豐富的常規(guī)gadget,從而形成一條完整、高效的ROP攻擊鏈,并通過(guò)實(shí)驗(yàn)證明不僅能夠完成復(fù)雜的ROP功能,而且特征足夠小,能夠繞過(guò)主流ROP檢測(cè)工具的檢測(cè)。
ROP構(gòu)造功能代碼的難點(diǎn)是實(shí)現(xiàn)條件轉(zhuǎn)移邏輯。通過(guò)深入分析條件轉(zhuǎn)移機(jī)器指令的執(zhí)行上下文發(fā)現(xiàn),對(duì)這些指令的傳統(tǒng)認(rèn)知存在一定的局限性。事實(shí)上,在已有代碼中存在少量的條件轉(zhuǎn)移指令,它們的兩個(gè)分支的開(kāi)始部分都是可復(fù)用的代碼片段(稱(chēng)為gadgets),而且這兩個(gè)gadgets會(huì)從不同的內(nèi)存單元中取得下一個(gè)gadget的地址。因此,以這些條件轉(zhuǎn)移指令開(kāi)始的代碼片段可以幫助ROP實(shí)現(xiàn)條件轉(zhuǎn)移邏輯。把這種代碼片段稱(chēng)為if-gadget。袁平海等[10]通過(guò)實(shí)驗(yàn)證明在Linux和Windows系統(tǒng)if-gadget普遍存在,即使在代碼量很小的日常可執(zhí)行程序中也存在,并且引入if-gadget后,構(gòu)造復(fù)雜的ROP shellcode代碼要比用傳統(tǒng)方法容易得多。
ARMv7指令集中的每條指令都具有條件執(zhí)行功能的特殊性,這使得ARMv7架構(gòu)的條件跳轉(zhuǎn)gadget的構(gòu)造非常簡(jiǎn)單。圖1顯示了相等條件執(zhí)行{if(v1==v2)·:jump T1,else jump T2}的gadget實(shí)例,其中g(shù)adget_4就是一個(gè)典型的if-gadget。gadget_3的CMP指令可以通過(guò)比較R3和R4寄存器中值進(jìn)行標(biāo)志位的改變。然后從棧中將gadget_4的首地址加載到寄存器LR,程序跳轉(zhuǎn)到gadget_4執(zhí)行。gadget_4的第一條指令BX.eq r5為條件執(zhí)行指令,POP{r2,lr}指令將棧中的gadget_5的首地址加載到LR寄存器,然后程序跳轉(zhuǎn)到gadget_5繼續(xù)執(zhí)行,成功地實(shí)現(xiàn)了跳轉(zhuǎn)。

圖1 ARMv7架構(gòu)條件跳轉(zhuǎn)gadget構(gòu)造實(shí)例
從圖1中可以看出,ARMv7架構(gòu)下通過(guò)條件跳轉(zhuǎn)gadget(gadget_4)很好地實(shí)現(xiàn)了條件跳轉(zhuǎn)。然而ARMv8是ARM公司最新發(fā)布的首款支持64位指令集的處理器架構(gòu),其引入了一個(gè)全新的64位指令集——A64。與ARMv7架構(gòu)相比,其在指令集、地址長(zhǎng)度、通用寄存器和工作模式等方面存在較大區(qū)別,簡(jiǎn)要對(duì)比如表1所示。

表1 ARMv8架構(gòu)與ARMv7架構(gòu)對(duì)比表
通過(guò)對(duì)ARMv8架構(gòu)指令系統(tǒng)的學(xué)習(xí)總結(jié)[1-5],發(fā)現(xiàn)總共有5類(lèi)跳轉(zhuǎn)指令可以影響程序的控制流。其可以分成三大類(lèi):條件跳轉(zhuǎn)分支指令、非條件直接跳轉(zhuǎn)分支指令和非條件間接跳轉(zhuǎn)分支指令。AArch64中條件跳轉(zhuǎn)指令的匯編格式如下:B.cond label,其編碼格式如圖2所示,其中imm19為跳轉(zhuǎn)目標(biāo)地址的偏移量。

圖2 AArch64條件跳轉(zhuǎn)指令編碼格式
從圖2中可以看出,ARMv8架構(gòu)條件接跳轉(zhuǎn)指令的目標(biāo)地址已經(jīng)被編碼到指令格式中,其目的地址無(wú)法再進(jìn)行修改。因此,ARMv7架構(gòu)基于間接條件跳轉(zhuǎn)指令構(gòu)造條件跳轉(zhuǎn)gadget的方法在ARMv8架構(gòu)中已經(jīng)不再適用,盡管通過(guò)多個(gè)gadget的重復(fù)使用也可達(dá)成上述功能,但將浪費(fèi)大量的內(nèi)存空間。基于上述問(wèn)題,本文提出了ARMv8架構(gòu)基于CMP指令和CSEL指令構(gòu)造if-gadget方法,解決了ARMv8架構(gòu)中缺失if-gadget的問(wèn)題。最后通過(guò)實(shí)驗(yàn)證明了本文方法的可行性,與傳統(tǒng)的方法相比,本文的方法節(jié)省更大的內(nèi)存開(kāi)支,提高了執(zhí)行效率。
ARMv7架構(gòu)基于間接條件跳轉(zhuǎn)指令構(gòu)造條件跳轉(zhuǎn)gadget的方法在ARMv8架構(gòu)中已經(jīng)不再適用。通過(guò)對(duì)ARMv8指令集的分析,本文提出了一種利用CSEL指令gadget和CMP指令gadget構(gòu)造ARMv8 ROP shellcode復(fù)雜控制流的方法。實(shí)現(xiàn)過(guò)程如下:
Step1通過(guò)算術(shù)或邏輯運(yùn)算來(lái)設(shè)置相應(yīng)的標(biāo)志位。例如,使用CMP命令來(lái)判斷兩個(gè)數(shù)是否相等,如果兩個(gè)數(shù)相等ZF為1,否則為0。
Step2CSEL指令可根據(jù)標(biāo)志位來(lái)選擇相應(yīng)的源寄存器對(duì)目的寄存器進(jìn)行賦值。例如csel x0,x1,x2,ne中,若ZF為0,x1賦給x0;若ZF為1,x2賦給x0。若x2和x1中預(yù)置為應(yīng)轉(zhuǎn)向的相應(yīng)目標(biāo)地址,通過(guò)下一步可實(shí)現(xiàn)相應(yīng)的條件跳轉(zhuǎn)。
Step3Br X0指令實(shí)現(xiàn)跳轉(zhuǎn)。
下面的例子要實(shí)現(xiàn):比較兩個(gè)64位數(shù)V1和V2,相等跳轉(zhuǎn)到LABEL1;不等跳轉(zhuǎn)到LABEL2。其對(duì)等的代碼邏輯如下:
if (V1==V2)
jump LABEL1;
else
jump LABEL2
該條件跳轉(zhuǎn)控制流gadget總共包含四個(gè)gadget,gadget(9)將V1和V2兩個(gè)操作數(shù)讀取到寄存器X3和X4中;gadget(10)將LABEL1和LABEL2的地址分別賦給寄存器X0和X1;gadget(11)完成兩個(gè)操作數(shù)X3和X4的比較,如果不相等X0中為L(zhǎng)ABEL2的地址,否則為L(zhǎng)ABEL1的地址;gadget(12)實(shí)現(xiàn)相應(yīng)的條件跳轉(zhuǎn),若V1等于V2,轉(zhuǎn)去執(zhí)行LABEL1;不等就執(zhí)行LABEL2。
工作原理如圖3所示。首先第一個(gè)gadget從當(dāng)前的棧頂讀取兩個(gè)常數(shù)v1和v2,存儲(chǔ)到寄存器X3和X4中,然后SP加上0x30,這時(shí)棧頂存儲(chǔ)的是第2個(gè)gadget的首地址。當(dāng)?shù)?個(gè)gadget執(zhí)行完畢后,會(huì)將第2個(gè)gadget的首地址讀取到X30寄存器中,這時(shí)程序?qū)?huì)跳轉(zhuǎn)到第2個(gè)gadget處執(zhí)行,對(duì)應(yīng)圖中的1、2、3步。第2個(gè)gadget完成和第1個(gè)gadget相似的功能,從當(dāng)前棧頂讀取目標(biāo)地址存儲(chǔ)到X0和X1寄存器中,然后程序跳轉(zhuǎn)到第3個(gè)gadget執(zhí)行,對(duì)應(yīng)圖中的4、5、6步。第3個(gè)gadget對(duì)常數(shù)v1和v2比較,如果不相等,則目標(biāo)地址為X1寄存器中的地址,否則目標(biāo)地址等于X0寄存器中的地址,程序跳轉(zhuǎn)到第4個(gè)gadget執(zhí)行,對(duì)應(yīng)圖中的7、8、9步。現(xiàn)在已經(jīng)將目標(biāo)地址存儲(chǔ)到了寄存器X0中,第4個(gè)gadget用br x0指令實(shí)現(xiàn)跳轉(zhuǎn)。

圖3 條件控制流gadget構(gòu)造方法
上面的4個(gè)gadget完成了相等條件下的跳轉(zhuǎn),其他類(lèi)型的條件跳轉(zhuǎn),如小于、大于、小于等于、大于等于均可使用上述規(guī)則進(jìn)行構(gòu)造,只需將第3個(gè)gadget中的csel指令中的條件域改為相應(yīng)的條件即可。有了條件跳轉(zhuǎn),我們就可以實(shí)現(xiàn)有限的循環(huán)操作。
大部分的shellcode都會(huì)涉及到循環(huán)結(jié)構(gòu),循環(huán)結(jié)構(gòu)保證了原始shellcode到生成的ARMv8 ROP shellcode間的控制流語(yǔ)義的一致性。雖然通過(guò)研究ROP的工作原理可以看出gadget的執(zhí)行流程是通過(guò)控制結(jié)構(gòu)來(lái)進(jìn)行控制的。即使沒(méi)有if-gadget,可以通過(guò)合理地安排控制結(jié)構(gòu)來(lái)控制gadget的執(zhí)行流程,但是這種方法存在一個(gè)重大的缺點(diǎn),它需要多次重復(fù)使用相同的數(shù)據(jù),這將造成內(nèi)存空間的極度浪費(fèi),而基于if-gadget的構(gòu)造方法卻可以避免這個(gè)問(wèn)題。
if-gadget是循環(huán)結(jié)構(gòu)的基礎(chǔ),本文將會(huì)基于ARMv8 if-gadget給出ARMv8 ROP shellcode中循環(huán)結(jié)構(gòu)的構(gòu)造方法。由于循環(huán)體需要多次使用相同的數(shù)據(jù),所以必須尋找一個(gè)gadget對(duì)SP指針進(jìn)行調(diào)整以致gadget鏈可以多次使用同一塊數(shù)據(jù)。本文使用下面的3個(gè)gadget來(lái)構(gòu)建ARMv8 ROP shellcode中的循環(huán)結(jié)構(gòu):
0x7fb7f86240EB0A009Fcmp x4,x10
0x7fb7f86244 9A811042 csel x2, x2, x1, ne
0x7fb7f86248 A8C17BFD ldp x29,x30,[sp],#0x10
0x7fb7f8624c D65F03C0 ret
gadget(13)
0x7fb7f2caf4 D10043BF sub sp,x29,#0x10
0x7fb7f2caf8 A9417BFD ldp x29,x30,[sp,#0x10]
0x7fb7f2cafc 910083FF add sp,sp,#0x20
0x7fb7f2cb00 D65F03C0 ret
gadget(14)
0x7fb7f53458 D61F0040 br x2
gadget(15)
其實(shí)現(xiàn)原理如圖4所示。當(dāng)循環(huán)體執(zhí)行完核心功能后跳轉(zhuǎn)到gadget(13)執(zhí)行。gadget(13)、gadget(14)和gadget(15)為ARMv8架構(gòu)基于跳轉(zhuǎn)跳轉(zhuǎn)gadget的ROP shellcode中的循環(huán)結(jié)構(gòu)的構(gòu)造方法。gadget(13)中的第一條指令”cmp x4,x10”通過(guò)比較X1寄存器和X10寄存器的大小影響相應(yīng)的標(biāo)志位,第二條指令”csel x2,x2,x1,ne”根據(jù)標(biāo)志位設(shè)置寄存器X2的值,如果X4中的值不等于X10寄存器中的值,寄存器X2中保存循環(huán)體中第一個(gè)gadget的首地址。gadget(14)用于實(shí)現(xiàn)棧指針的調(diào)整,第一條指令將X29寄存器的值賦值給SP,而X29寄存器中的值是通過(guò)gadget(13)中的倒數(shù)第二條指令進(jìn)行設(shè)置的,為了可以準(zhǔn)確地使SP指向循環(huán)體指向的數(shù)據(jù)結(jié)構(gòu),必須準(zhǔn)確安排X29寄存器中的值。本文中g(shù)adget(14)執(zhí)行完畢后,棧指針位于SP1位置,而循環(huán)體的數(shù)據(jù)的開(kāi)始地址位于SP3位置,通過(guò)對(duì)gadget(14)的分析,gadget(13)應(yīng)將X29寄存器中的值設(shè)置為SP2+0x10所得的值,這樣當(dāng)gadget(14)執(zhí)行完畢后,棧指針將位于SP3位置。如果條件成立,gadget(15)將會(huì)使程序跳轉(zhuǎn)到循環(huán)體繼續(xù)執(zhí)行,至此實(shí)現(xiàn)了ARMv8 ROP shellcode中循環(huán)結(jié)構(gòu)。

圖4 ARMv8 ROP shellcode中循環(huán)結(jié)構(gòu)的實(shí)現(xiàn)原理
本節(jié)將通過(guò)完成∑i的運(yùn)算實(shí)例證明ARMv8架構(gòu)基于CMP指令和CSEL指令構(gòu)造的if-gadget方法完成復(fù)雜ARMv8 ROP shellcode的可行性。并通過(guò)實(shí)驗(yàn)證明與傳統(tǒng)方法相比,本文的方法節(jié)省了大量的內(nèi)存空間。
本例gadget鏈所用的7個(gè)gadget來(lái)自libc.so.6或ld-linux-aarch64.so.1庫(kù)文件。gadget(16)用于從內(nèi)存加載數(shù)據(jù)到寄存器X10;gadget(2)從內(nèi)存中連續(xù)讀取4個(gè)數(shù)據(jù)依次放入寄存器X1、X2、X3和X4;gadget(17)將X3和X4相加,結(jié)果保存到寄存器X3中;gadget(18)完成X4寄存器的加1操作;gadget(13)和gadget(14)用于實(shí)現(xiàn)循環(huán),gadget(13)中的第一條指令”cmp x4,x10”通過(guò)比較X1寄存器和X10寄存器的大小影響相應(yīng)的標(biāo)志位;第二條指令”csel x2,x2,x1,ne”根據(jù)標(biāo)志位設(shè)置寄存器X2的值,如果X4中的值不等于X10寄存器中的值,寄存器X2中保存gadget(17)的首地址,如果X4中的值等于X10寄存器中的值,寄存器X2中保存gadget(14)的首地址;第三條指令“l(fā)dp x29,x30,[sp],#0x10”將從gadget的CS中加載gadget(14)函數(shù)的入口地址到X30中,最后的RET指令將轉(zhuǎn)去執(zhí)行g(shù)adget(17);gadget(14)用于實(shí)現(xiàn)棧指針的調(diào)整,第一條指令將X29寄存器的值賦值給SP,第二條指令從內(nèi)存SP+0x10的位置讀取兩個(gè)數(shù)據(jù)放入寄存器X29和X30,之后使用RET指令跳轉(zhuǎn)待gadget(15)執(zhí)行;gadget(15)實(shí)現(xiàn)跳轉(zhuǎn)。
0x7fb7f4a5c4F84087EAldr x10,[sp],#8
0x7fb7f4a5c8 F84087FE ldr x30,[sp],#8
0x7fb7f4a5cc D65F03C0 ret
gadget(16)
0x7fb7fe835c A8C10BE1 ldp x1,x2,[sp],#0x10
0x7fb7fe8360 A1C113E3 ldp x3,x4,[sp],#0x10
0x7fb7fe8364 A8C27BFD ldp x29,x30,[sp],#0x20
0x7fb7fe8368 D65F03C0 ret
gadget(2)
0x7fb7f6ba7c 8B040063 add x3,x3,x4
0x7fb7f6ba80 A8C17BFD ldp x29,x30,[sp],#0x10
0x7fb7f6ba84 D65F03C0 ret
gadget(17)
0x7fb7f8a4c8 91000484 add x4,x4,#0x1
0x7fb7f8a4cc A8C17BFD ldp x29,x30,[sp],#0x10
0x7fb7f8a4d0 D65F03C0 ret
gadget(18)
0x7fb7f86240 EB0A009F cmp x4,x10
0x7fb7f86244 9A811042 csel x2,x2,x1,ne
0x7fb7f86248 A8C17BFD ldp x29,x30,[sp],#0x10
0x7fb7f8624c D65F03C0 ret
gadget(13)
0x7fb7f2caf4 D10043BF sub sp,x29,#0x10
0x7fb7f2caf8 A9417BFD ldp x29,x30,[sp,#0x10]
0x7fb7f2cafc 910083FF add sp,sp,#0x20
0x7fb7f2cb00 D65F03C0 ret
gadget(14)
0x7fb7f53458 D61F0040 br x2
gadget(15)
本例中取n=3,基于if-gadget和無(wú)條件跳轉(zhuǎn)gadget的原理如圖5所示。圖的左側(cè)給出ARMv8 ROP的內(nèi)存安排和內(nèi)存地址的后12位,圖的右側(cè)的虛線代表指令序列的執(zhí)行順序。

(a) 基于條件跳轉(zhuǎn)gadget

(b) 無(wú)條件跳轉(zhuǎn)gadget圖5 不同情況跳轉(zhuǎn)gadget的ROP工作原理圖
可以看出,盡管通過(guò)無(wú)條件跳轉(zhuǎn)gadget也可完成相同的功能,但是與本文提出的基于條件跳轉(zhuǎn)gadget的方法相比,其需要不斷地重復(fù)使用gadget(13)、gadget(17)和gadget(18),造成了內(nèi)存空間的冗余。當(dāng)n=3時(shí),基于if-gadget和無(wú)條件跳轉(zhuǎn)gadget對(duì)應(yīng)的控制結(jié)構(gòu)如圖6所示。基于if-gadget的方法生成了128字節(jié)大小的控制結(jié)構(gòu),無(wú)條件跳轉(zhuǎn)gadget的方法生成了240字節(jié)大小的控制結(jié)構(gòu),造成了112字節(jié)的空間浪費(fèi)。而且隨著n的增大,無(wú)條件跳轉(zhuǎn)gadget的方法的控制結(jié)構(gòu)也將越來(lái)越大,浪費(fèi)的內(nèi)存空間也將越來(lái)越多。表2給出了n=4,5,6,7時(shí)的情形,可以看出,當(dāng)n接近無(wú)窮大時(shí),本文的方法可以節(jié)省大量的內(nèi)存空間,從而提高執(zhí)行效率。

圖6 ARMv8 ROP shellcode

方法基于if-gadget無(wú)條件跳轉(zhuǎn)gadget浪費(fèi)的內(nèi)存空間大小/Byten=4128304176n=5128368240n=6128432304n=7128496368
ARMv7架構(gòu)基于條件跳轉(zhuǎn)指令構(gòu)造條件跳轉(zhuǎn)gadget的方法在ARMv8架構(gòu)中已經(jīng)不再適用,而大多數(shù)的shellcode存在循環(huán)結(jié)構(gòu),條件跳轉(zhuǎn)gadget是ROP shellcode中循環(huán)結(jié)構(gòu)構(gòu)造的基礎(chǔ)。基于上述問(wèn)題,本文首先提出一種在ARMv8平臺(tái)利用CSEL指令和CMP指令gadget構(gòu)造條件跳轉(zhuǎn)gadget的方法。然后基于條件跳轉(zhuǎn)gadget后給出了ARMv8 ROP shellcode中循環(huán)結(jié)構(gòu)的構(gòu)造方法,并通過(guò)一個(gè)實(shí)例證明了本文的方法在ARMv8架構(gòu)上的可行性。給出了ARMv8 ROP shellcode中循環(huán)結(jié)構(gòu)的使用方法和包含循環(huán)結(jié)構(gòu)的ARMv8 ROP控制結(jié)構(gòu)的生成過(guò)程,并通過(guò)與無(wú)條件跳轉(zhuǎn)gadget方法相比,本文的方法減小了ARMv8 ROP控制結(jié)構(gòu)的大小,節(jié)省了內(nèi)存空間。