ARM 匯編簡介
via:https://azeria-labs.com/writing-arm-assembly-part-1/
介紹
歡迎來到 ARM 匯編基礎系列教程。這是為后續的 ARM 利用開發系列教程做的准備。在開始創建 ARM shellcode 和構建 ROP 鏈之前,我們需要先介紹一些 ARM 匯編語言的基礎知識。
為了后續的練習,您需要一個基於 ARM 的實驗室環境。如果您沒有 ARM 設備(如 Raspberry Pi ),您可以按照 這個教程 使用 QEMU 和 Raspberry Pi 發行版鏡像在虛擬機中設置自己的實驗室環境。如果您不熟悉 GDB 的基本調試,那么您可以從 這個教程 中獲得基礎知識。在本教程中,重點將放在 32 位 ARM 上,示例都是在 ARMv6 上編譯的。
本教程一般是為那些想要學習 ARM 匯編基礎知識的人准備的。特別是對於那些對在 ARM 平台上編寫漏洞利用代碼感興趣的人。您可能已經注意到,您周圍到處都是 ARM 處理器。當我環顧四周時,我可以數出我家里使用 ARM 處理器的設備比使用英特爾處理器的設備多得多。這包括電話、路由器,更不要忘了最近銷量激增的物聯網設備。也就是說,ARM 處理器已經成為世界上使用最廣泛的 CPU 之一。這讓我們認識到,與 pc 一樣,物聯網設備也容易受到不當輸入的影響,比如緩沖區溢出。基於 ARM 的設備的廣泛使用和潛在的誤用,使得對這些設備的攻擊變得更加普遍。
然而,在 x86 安全研究方面的專家比在 ARM 方面的專家多得多,盡管 ARM 匯編語言可能是廣泛使用的最簡單的匯編語言。那么,為什么沒有更多的人關注 ARM 呢?也許是因為與 ARM 相比,存在更多 Intel 漏洞利用的學習資源。想想由 Fuzzy Security 或 Corelan 團隊編寫的關於 Intel x86 的優秀教程——像這樣的指南可以幫助對特定領域感興趣的人獲得實踐知識和獲得這些教程所涵蓋的內容以外的知識的靈感。如果您對編寫 x86 利用程序感興趣,Corelan 和 Fuzzysec 教程是您完美的起點。在本系列教程中,我們將重點介紹 ARM 匯編基礎知識和編寫 ARM 利用程序。
對比 ARM 處理器和 intel 處理器
Intel 和 ARM 有很多不同,但主要的不同是指令集。Intel 是一個 CISC ( Complex Instruction Set Computing) 處理器,它有更大更豐富的指令集,擁有許多復雜的訪存指令。因此,它有更多的操作,尋址模式,但寄存器比 ARM 少。CISC 處理器主要用於普通的 P C機、工作站和服務器。
ARM 是一個 RISC (Reduced instruction set Computing) 處理器,有一個簡化指令集 (100 條或更少)和比 CISC 更通用的寄存器。與 intel 不同的是,ARM 使用的指令只能在寄存器上操作,並使用 Load/Store 內存模型 來訪問內存,這意味着只有 加載/存儲指令 才能訪問內存。意味着遞增一個 32位 的值在一個特定的內存地址的 ARM 需要三種類型的指令 (裝載、增加和存儲(load, increment and store) ) 首先通過變量的地址把變量的值加載到寄存器中,對寄存器中的值進行加運算,然后將寄存器中的值放回內存。
簡化后的指令集有其優點也有其缺點。其中一個優點是可以更快地執行指令,潛在地允許更大的速度( RISC 系統通過減少每條指令的時鍾周期來縮短執行時間)。缺點是較少的指令意味着更強調用有限的可用指令高效地編寫軟件。同樣重要的是,ARM 有兩種模式,ARM 模式和 Thumb 模式。Thumb 指令可以是 2 個字節也可以是 4 個字節(詳見第3部分:ARM 指令集)。
ARM和x86之間更多的區別是:
- 在ARM中,大多數指令都可以用於條件執行。
- Intel x86 和 x86-64 系列處理器使用小端序(little-endian) 格式
- 在 version 3 之前,ARM 的架構是小端序 (little-endian) 。在 version 3 后,ARM 處理器變成了大端序 (BI-endian),並提供了切換字節序的設置。
不同 ARM 版本的命名也可能令人感到困惑:
ARM family | ARM architecture |
---|---|
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 exploit 之前,在您開始欣賞它之前,我們首先需要了解匯編語言編程的基礎知識,這需要一點背景知識。但為什么我們需要學 ARM 匯編,難道用 “正常” 編程/腳本語言來編寫我們的漏洞還不夠嗎?如果我們想要能夠做逆向工程,理解 ARM 二進制文件的程序執行流程,構建我們自己的 ARM shellcode ,構建 ARM ROP 鏈,調試 ARM 應用程序,那么不會 ARM 匯編將寸步難行。
進行逆向工程和漏銅利用開發不需要您了解匯編語言的每一個小細節,但是其中一些是理解大局所必需的。本系列教程將介紹基礎知識。如果你想了解更多,你可以訪問本章末尾列出的鏈接。
那么,匯編語言到底是什么呢?匯編語言只是機器代碼之上的一層薄薄的語法,機器代碼是由指令組成的,這些指令用二進制表示(機器碼)編碼,這是我們的計算機所能理解的。我們為什么不直接寫機器代碼呢?嗯,那會是一個令人頭疼的問題。因為這個原因,我們寫匯編語言而不是機器碼。ARM 匯編,對人類來說更容易理解(相對而言)。我們的計算機本身不能運行匯編代碼,因為它需要機器碼。我們將使用的將匯編代碼組裝成機器代碼的工具是 GNU Binutils 項目中的 GNU Assembler,用於處理擴展名為 *.s 的源文件。
一旦您編寫了擴展名為 *.s 的源文件,您需要使用 as 將其匯編,並用 ld 進行鏈接:
$ as program.s -o program.o
$ ld program.o -o program
匯編之下
讓我們從最底層開始,再到匯編語言。在最底層,我們的電路中有電子信號。信號是通過切換電壓到兩個電平中的一個形成的,比如 0 伏特('關')或 5 伏特('開')。因為通過我們不能輕易告訴電路使用什么電壓,我們選擇寫的開/關電壓模式使用視覺表示,數字 0 和 1 ,不僅代表缺席的想法或信號,還因為 0 和 1 是二進制的數字系統。然后我們將 0 和 1 的序列組合成一個機器代碼指令,它是計算機處理器的最小工作單元。下面是一個機器語言指令的例子:
1110 0001 1010 0000 0010 0000 0000 0001
到目前為止,一切都很好,但我們不記得這些 (0和1) 的組合是什么意思。出於這個原因,我們使用所謂的助記符、縮寫來幫助我們記住這些二進制,其中每個機器代碼指令都有一個名稱。這些助記法通常包括三個字母,但這不是必須的。我們可以用這些助記符作為指令來編寫程序。這個程序被稱為匯編語言程序,用來表示計算機機器代碼的助記符集被稱為該計算機的匯編語言。因此,匯編語言是人類用來編寫計算機程序的最低層次的語言。指令的操作數在助記符之后。下面是一個例子:
MOV R2, R1
既然我們知道匯編程序是由稱為助記符的文本信息組成的,我們就需要把它轉換成機器碼。如上所述,在 ARM 環境下,GNU Binutils 項目為我們提供了一個稱為 as 的工具。使用像 as 這樣的匯編程序將 (ARM) 匯編語言轉換為 (ARM) 機器碼的過程稱為匯編。
總之,我們了解到計算機可以理解 (響應) 電壓 (信號) 的存在或不存在,並且我們可以用一個包含 0 和 1 的序列來表示多個信號。我們可以使用機器碼 (信號序列) 使計算機以某種明確定義的方式作出響應。因為我們不記得所有這些序列的意思,所以我們給它們寫了縮寫——助記符,然后用它們來表示指令。這些助記符就是是計算機的匯編語言,我們使用一個叫做匯編器的程序來將代碼從助記符表示轉換為計算機可讀的機器碼,就像編譯器對高級語言所做的那樣。
延伸閱讀
Whirlwind Tour of ARM Assembly: https://www.coranac.com/tonc/text/asm.htm
ARM assembler in Raspberry Pi: http://thinkingeek.com/arm-assembler-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: http://infocenter.arm.com/help/topic/com.arm.doc.dui0068b/index.html
Assembler User Guide: http://www.keil.com/support/man/docs/armasm/default.htm