原文鏈接 https://azeria-labs.com/writing-arm-assembly-part-1/
寫在前面
歡迎來到ARM匯編基礎教程,這套教程是為了讓你可以在ARM架構下進行漏洞利用打基礎的。在我們能開始寫ARM的shellcode以及構建ROP鏈之前,我們需要先學習相關的ARM匯編基礎知識。
這些基礎知識包括:
- Part 1:ARM匯編介紹
- Part 2:數據類型寄存器
- Part 3: ARM指令集
- Part 4: 內存相關指令:加載以及存儲
- Part 5:重復性加載及存儲
- Part 6: 分支和條件執行
- Part 7:棧以及函數
為了能跟著這個系列教程動手實踐,你可以準備一個ARM的運行環境。如果你沒有ARM設備(比如說樹莓派或者手機),你可以通過QEMU來創建一個,教程在這。如果你對于GDB調試的基礎命令不熟悉的話,可以通過這個學習。在這篇教程中,我們的核心關注點為32位的ARM,相關的例子在ARMv6下編譯。
為什么是ARM?
前面說過,本系列教程的核心目的,是為那些想學習在ARM架構下進行漏洞利用的人而準備。可以看看你身邊,有多少設備是ARM架構的, 手機,路由器,以及IOT設備,很多都是ARM架構的。無疑ARM架構已經成為了全世界主流而廣泛的CPU架構。所以我們面對的越來越多的安全問題,也都會是ARM架構下的,那么在這種架構下的開發以及漏洞利用,也會成為一種主流趨勢。
我們在X86架構上進行了很多研究,而ARM可能是最簡單的廣泛使用的匯編語言。但是人們為什么不關注ARM呢?可能是在intel架構上可供漏洞利用的學習資料比ARM多得多吧。比如Corelan Team寫的很棒的intel X86漏洞利用教程,旨在幫助我們可以更準確更高效的學習到關鍵的漏洞利用基礎知識。如果你對于x86漏洞利用很感興趣,那我覺得Corelan Team的教程是一個不錯的選擇。但是在我們這個系列里,我們要創造一本高效的ARM架構下的漏洞利用新手手冊。
ARM VS. INTEL
ARM處理器Intel處理器有很多不同,但是最主要的不同怕是指令集了。Intel屬于復雜指令集(CISC)處理器,有很多特性豐富的訪問內存的復雜指令集。因此它擁有更多指令代碼以及取址都是,但是寄存器比ARM的要少。復雜指令集處理器主要被應用在PC機,工作站以及服務器上。
ARM屬于簡單指令集(RISC)處理器,所以與復雜指令集先比,只有簡單的差不多100條指令集,但會有更多的寄存器。與Intel不同,ARM的指令集僅僅操作寄存器或者是用于從內存的加載/儲存過程,這也就是說,簡單的加載/存儲指令即可訪問到內存。這意味著在ARM中,要對特定地址中存儲的的32位值加一的話,僅僅需要從內存中加載到寄存器,加一,再從寄存器儲存到內存即可。
簡單的指令集既有好處也有壞處。一個好處就是代碼的執行變得更快了。(RISC指令集允許通過縮短時鐘周期來加速代碼執行)。壞處就是更少的指令集也要求了編寫代碼時要更加注意指令間使用的關系以及約束。還有重要的一點,ARM架構有兩種模式,ARM模式和Thumb模式。Thumb模式的代碼只有2或者4字節。
ARM與X86的不同還體現在:
- ARM中很多指令都可以用來做為條件執行的判斷依據
- X86與X64機器碼使用小端格式
- ARM機器碼在版本3之前是小端。但是之后默認采用大端格式,但可以設置切換到小端。
除了以上這些ARM與Intel間的差異,ARM自身也有很多版本。本系列教程旨在盡力保持通用性的情況下來講講ARM的工作流程。而且當你懂得了這個形式,學習其他版本的也很容易了。在系列教程中使用的樣例都是在32位的ARMv6下運行的,所以相關解釋也是主要依賴這個版本的。
ARM 家族 | ARM 架構 |
---|---|
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命名也是有些復雜:
ARM 家族 | ARM 架構 |
---|---|
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匯編
在開始用ARM匯編做漏洞利用開發之前,還是需要先學習下基礎的匯編語言知識的。為什么我們需要ARM匯編呢,用正常的變成語言寫不夠么?的確不夠,因為如果我們想做逆向工程,或者理解相關二進制程序的執行流程,構建我們自己的ARM架構的shellcode,ROP鏈,以及調試ARM應用,這些都要求先懂得ARM匯編。當然你也不需要學習的太過深入,足夠做逆向工作以及漏洞利用開發剛剛好。如果有些知識要求先了解一些背景知識,別擔心,這些知識也會在本系列文章里面介紹到的。當然如果你想學習更多,也可以去本文末尾提供的相關鏈接學習。
ARM匯編,是一種更容易被人們接受的匯編語言。當然我們的計算機也不能直接運行匯編代碼,還是需要編譯成機器碼的。通過編譯工具鏈中as
程序來將文件后綴為".s"的匯編代碼編譯成機器碼。寫完匯編代碼后,一般保存后綴為".s"的文件,然后你需要用as
編譯以及用ld
鏈接程序:
$ as program.s -o program.o
$ ld program.o -o program
匯編語言本質
讓我們來看看匯編語言的底層本質。在最底層,只有電路的電信號。信號被格式化成可以變化的高低電平0V(off)或者5V(on)。但是通過電壓變化來表述電路狀態是繁瑣的,所以用0和1來代替高低電平,也就有了二進制格式。由二進制序列組成的組合便是最小的計算機處理器工作單元了,比如下面的這句機器碼序列就是例子。
1110 0001 1010 0000 0010 0000 0000 0001
看上去不錯,但是我們還是不能記住這些組合的含義。所以,我們需要用助記符和縮寫來幫助我們記住這些二進制組合。這些助記符一般是連續的三個字母,我們可以用這些助記符作為指令來編寫程序。這種程序就叫做匯編語言程序。用以代表一種計算機的機器碼的助記符集合就叫做這種計算機匯編語言。因此,匯編語言是人們用來編寫程序的最底層語言。同時指令的操作符也有對應的助記符,比如:
MOV R2, R1
現在我們知道了匯編程序是助記符的文本信息集合,我們需要將其轉換成機器碼。就像之前的,在GNU Binutils工程中提供了叫做as
的工具。使用匯編工具去將匯編語言轉換成機器碼的過程叫做匯編(assembling)。
總結一下,在這篇中我們學習了計算機是通過由0101代表高低電平的機器碼序列來進行運算的。我們可以使用機器碼去讓計算機做我們想讓它做的事情。不過因為我們不能記住機器碼,我們使用了縮寫助記符來代表有相關功能的機器碼,這些助記符的集合就是匯編語言。最后我們使用匯編器將匯編語言轉換成機器可以理解的機器碼。當然,在更高級別的語言編譯生成機器碼過程中,核心原理也是這個。
拓展閱讀
- 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.