原文鏈接 https://azeria-labs.com/writing-arm-assembly-part-1/
寫(xiě)在前面
歡迎來(lái)到ARM匯編基礎(chǔ)教程,這套教程是為了讓你可以在ARM架構(gòu)下進(jìn)行漏洞利用打基礎(chǔ)的。在我們能開(kāi)始寫(xiě)ARM的shellcode以及構(gòu)建ROP鏈之前,我們需要先學(xué)習(xí)相關(guān)的ARM匯編基礎(chǔ)知識(shí)。
這些基礎(chǔ)知識(shí)包括:
- Part 1:ARM匯編介紹
- Part 2:數(shù)據(jù)類(lèi)型寄存器
- Part 3: ARM指令集
- Part 4: 內(nèi)存相關(guān)指令:加載以及存儲(chǔ)
- Part 5:重復(fù)性加載及存儲(chǔ)
- Part 6: 分支和條件執(zhí)行
- Part 7:棧以及函數(shù)
為了能跟著這個(gè)系列教程動(dòng)手實(shí)踐,你可以準(zhǔn)備一個(gè)ARM的運(yùn)行環(huán)境。如果你沒(méi)有ARM設(shè)備(比如說(shuō)樹(shù)莓派或者手機(jī)),你可以通過(guò)QEMU來(lái)創(chuàng)建一個(gè),教程在這。如果你對(duì)于GDB調(diào)試的基礎(chǔ)命令不熟悉的話,可以通過(guò)這個(gè)學(xué)習(xí)。在這篇教程中,我們的核心關(guān)注點(diǎn)為32位的ARM,相關(guān)的例子在ARMv6下編譯。
為什么是ARM?
前面說(shuō)過(guò),本系列教程的核心目的,是為那些想學(xué)習(xí)在ARM架構(gòu)下進(jìn)行漏洞利用的人而準(zhǔn)備。可以看看你身邊,有多少設(shè)備是ARM架構(gòu)的, 手機(jī),路由器,以及IOT設(shè)備,很多都是ARM架構(gòu)的。無(wú)疑ARM架構(gòu)已經(jīng)成為了全世界主流而廣泛的CPU架構(gòu)。所以我們面對(duì)的越來(lái)越多的安全問(wèn)題,也都會(huì)是ARM架構(gòu)下的,那么在這種架構(gòu)下的開(kāi)發(fā)以及漏洞利用,也會(huì)成為一種主流趨勢(shì)。
我們?cè)赬86架構(gòu)上進(jìn)行了很多研究,而ARM可能是最簡(jiǎn)單的廣泛使用的匯編語(yǔ)言。但是人們?yōu)槭裁床魂P(guān)注ARM呢?可能是在intel架構(gòu)上可供漏洞利用的學(xué)習(xí)資料比ARM多得多吧。比如Corelan Team寫(xiě)的很棒的intel X86漏洞利用教程,旨在幫助我們可以更準(zhǔn)確更高效的學(xué)習(xí)到關(guān)鍵的漏洞利用基礎(chǔ)知識(shí)。如果你對(duì)于x86漏洞利用很感興趣,那我覺(jué)得Corelan Team的教程是一個(gè)不錯(cuò)的選擇。但是在我們這個(gè)系列里,我們要?jiǎng)?chuàng)造一本高效的ARM架構(gòu)下的漏洞利用新手手冊(cè)。
ARM VS. INTEL
ARM處理器Intel處理器有很多不同,但是最主要的不同怕是指令集了。Intel屬于復(fù)雜指令集(CISC)處理器,有很多特性豐富的訪問(wèn)內(nèi)存的復(fù)雜指令集。因此它擁有更多指令代碼以及取址都是,但是寄存器比ARM的要少。復(fù)雜指令集處理器主要被應(yīng)用在PC機(jī),工作站以及服務(wù)器上。
ARM屬于簡(jiǎn)單指令集(RISC)處理器,所以與復(fù)雜指令集先比,只有簡(jiǎn)單的差不多100條指令集,但會(huì)有更多的寄存器。與Intel不同,ARM的指令集僅僅操作寄存器或者是用于從內(nèi)存的加載/儲(chǔ)存過(guò)程,這也就是說(shuō),簡(jiǎn)單的加載/存儲(chǔ)指令即可訪問(wèn)到內(nèi)存。這意味著在ARM中,要對(duì)特定地址中存儲(chǔ)的的32位值加一的話,僅僅需要從內(nèi)存中加載到寄存器,加一,再?gòu)募拇嫫鲀?chǔ)存到內(nèi)存即可。
簡(jiǎn)單的指令集既有好處也有壞處。一個(gè)好處就是代碼的執(zhí)行變得更快了。(RISC指令集允許通過(guò)縮短時(shí)鐘周期來(lái)加速代碼執(zhí)行)。壞處就是更少的指令集也要求了編寫(xiě)代碼時(shí)要更加注意指令間使用的關(guān)系以及約束。還有重要的一點(diǎn),ARM架構(gòu)有兩種模式,ARM模式和Thumb模式。Thumb模式的代碼只有2或者4字節(jié)。
ARM與X86的不同還體現(xiàn)在:
- ARM中很多指令都可以用來(lái)做為條件執(zhí)行的判斷依據(jù)
- X86與X64機(jī)器碼使用小端格式
- ARM機(jī)器碼在版本3之前是小端。但是之后默認(rèn)采用大端格式,但可以設(shè)置切換到小端。
除了以上這些ARM與Intel間的差異,ARM自身也有很多版本。本系列教程旨在盡力保持通用性的情況下來(lái)講講ARM的工作流程。而且當(dāng)你懂得了這個(gè)形式,學(xué)習(xí)其他版本的也很容易了。在系列教程中使用的樣例都是在32位的ARMv6下運(yùn)行的,所以相關(guān)解釋也是主要依賴這個(gè)版本的。
ARM 家族 | ARM 架構(gòu) |
---|---|
ARM7 | ARM v4 |
ARM9 | ARM v5 |
ARM11 | ARM v6 |
Cortex-A | ARM v7-A |
Cortex-R | ARM v7-R |
Cortex-M | ARM v7-M |
不同版本的ARM命名也是有些復(fù)雜:
ARM 家族 | ARM 架構(gòu) |
---|---|
ARM7 | ARM v4 |
ARM9 | ARM v5 |
ARM11 | ARM v6 |
Cortex-A | ARM v7-A |
Cortex-R | ARM v7-R |
Cortex-M | ARM v7-M |
寫(xiě)ARM匯編
在開(kāi)始用ARM匯編做漏洞利用開(kāi)發(fā)之前,還是需要先學(xué)習(xí)下基礎(chǔ)的匯編語(yǔ)言知識(shí)的。為什么我們需要ARM匯編呢,用正常的變成語(yǔ)言寫(xiě)不夠么?的確不夠,因?yàn)槿绻覀兿胱瞿嫦蚬こ蹋蛘呃斫庀嚓P(guān)二進(jìn)制程序的執(zhí)行流程,構(gòu)建我們自己的ARM架構(gòu)的shellcode,ROP鏈,以及調(diào)試ARM應(yīng)用,這些都要求先懂得ARM匯編。當(dāng)然你也不需要學(xué)習(xí)的太過(guò)深入,足夠做逆向工作以及漏洞利用開(kāi)發(fā)剛剛好。如果有些知識(shí)要求先了解一些背景知識(shí),別擔(dān)心,這些知識(shí)也會(huì)在本系列文章里面介紹到的。當(dāng)然如果你想學(xué)習(xí)更多,也可以去本文末尾提供的相關(guān)鏈接學(xué)習(xí)。
ARM匯編,是一種更容易被人們接受的匯編語(yǔ)言。當(dāng)然我們的計(jì)算機(jī)也不能直接運(yùn)行匯編代碼,還是需要編譯成機(jī)器碼的。通過(guò)編譯工具鏈中as
程序來(lái)將文件后綴為".s"的匯編代碼編譯成機(jī)器碼。寫(xiě)完匯編代碼后,一般保存后綴為".s"的文件,然后你需要用as
編譯以及用ld
鏈接程序:
$ as program.s -o program.o
$ ld program.o -o program
匯編語(yǔ)言本質(zhì)
讓我們來(lái)看看匯編語(yǔ)言的底層本質(zhì)。在最底層,只有電路的電信號(hào)。信號(hào)被格式化成可以變化的高低電平0V(off)或者5V(on)。但是通過(guò)電壓變化來(lái)表述電路狀態(tài)是繁瑣的,所以用0和1來(lái)代替高低電平,也就有了二進(jìn)制格式。由二進(jìn)制序列組成的組合便是最小的計(jì)算機(jī)處理器工作單元了,比如下面的這句機(jī)器碼序列就是例子。
1110 0001 1010 0000 0010 0000 0000 0001
看上去不錯(cuò),但是我們還是不能記住這些組合的含義。所以,我們需要用助記符和縮寫(xiě)來(lái)幫助我們記住這些二進(jìn)制組合。這些助記符一般是連續(xù)的三個(gè)字母,我們可以用這些助記符作為指令來(lái)編寫(xiě)程序。這種程序就叫做匯編語(yǔ)言程序。用以代表一種計(jì)算機(jī)的機(jī)器碼的助記符集合就叫做這種計(jì)算機(jī)匯編語(yǔ)言。因此,匯編語(yǔ)言是人們用來(lái)編寫(xiě)程序的最底層語(yǔ)言。同時(shí)指令的操作符也有對(duì)應(yīng)的助記符,比如:
MOV R2, R1
現(xiàn)在我們知道了匯編程序是助記符的文本信息集合,我們需要將其轉(zhuǎn)換成機(jī)器碼。就像之前的,在GNU Binutils工程中提供了叫做as
的工具。使用匯編工具去將匯編語(yǔ)言轉(zhuǎn)換成機(jī)器碼的過(guò)程叫做匯編(assembling)。
總結(jié)一下,在這篇中我們學(xué)習(xí)了計(jì)算機(jī)是通過(guò)由0101代表高低電平的機(jī)器碼序列來(lái)進(jìn)行運(yùn)算的。我們可以使用機(jī)器碼去讓計(jì)算機(jī)做我們想讓它做的事情。不過(guò)因?yàn)槲覀儾荒苡涀C(jī)器碼,我們使用了縮寫(xiě)助記符來(lái)代表有相關(guān)功能的機(jī)器碼,這些助記符的集合就是匯編語(yǔ)言。最后我們使用匯編器將匯編語(yǔ)言轉(zhuǎn)換成機(jī)器可以理解的機(jī)器碼。當(dāng)然,在更高級(jí)別的語(yǔ)言編譯生成機(jī)器碼過(guò)程中,核心原理也是這個(gè)。
拓展閱讀
- Whirlwind Tour of ARM Assembly.
- ARM assembler in Raspberry Pi.
- Practical Reverse Engineering: x86, x64, ARM, Windows Kernel, Reversing Tools, and Obfuscation by Bruce Dang, Alexandre Gazet, Elias Bachaalany and Sebastien Josse.
- ARM Reference Manual.
- Assembler User Guide.