欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

iOS匯編入門教程之ARM64匯編基礎(chǔ)教程

 更新時(shí)間:2020年02月10日 10:58:02   作者:Soulghost  
這篇文章主要介紹了iOS匯編入門教程之ARM64匯編基礎(chǔ)教程,本文通過例子給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

前言

對于應(yīng)用層開發(fā)人員而言,僅僅掌握Objective-C和系統(tǒng)框架即可較好的完成開發(fā),但在涉及到應(yīng)用加固、逆向分析等內(nèi)容時(shí)僅有應(yīng)用層開發(fā)技能就會(huì)顯得非常的無力,因此掌握匯編對于突破iOS開發(fā)水平的瓶頸十分有效。

一個(gè)例子

以反調(diào)試為例,我們知道,通過調(diào)用ptrace函數(shù)可以阻止調(diào)試器依附。

ptrace(31, 0, 0, 0)

這種方式能夠被函數(shù)hook輕易破解,例如使用facebook的fishhook。為了防止函數(shù)被hook,我們可以將函數(shù)調(diào)用轉(zhuǎn)為通過匯編發(fā)起系統(tǒng)調(diào)用,即使用下面的代碼。

mov x0, #31
mov x1, #0
mov x2, #0
mov x3, #0
mov x16, #26
svc #0x80

其中x0-x3存儲(chǔ)的為函數(shù)入?yún)?,x16存儲(chǔ)的為函數(shù)編號(hào),通過Apple提供的System Call Table 可以查出ptrace的編號(hào)為26,最后一句指令發(fā)起了系統(tǒng)調(diào)用。通過使用_asm_指令能夠?qū)R編代碼嵌入我們的函數(shù)中,構(gòu)成反調(diào)試方法。

// 使用inline方式將函數(shù)在調(diào)用處強(qiáng)制展開,防止被hook和追蹤符號(hào)
static __attribute__((always_inline)) void anti_debug() {
// 判斷是否是ARM64處理器指令集
#ifdef __arm64__
 // volatile修飾符能夠防止匯編指令被編譯器忽略
 __asm__ __volatile__(
  "mov x0, #31\n"
  "mov x1, #0\n"
  "mov x2, #0\n"
  "mov x3, #0\n"
  "mov x16, #26\n"
  "svc #0x80\n"
  );
#endif
}

雖然上面的反調(diào)試機(jī)制并不完善,但是比直接調(diào)用ptrace要好上很多倍,從這一點(diǎn)來看,掌握匯編技能對于iOS應(yīng)用安全和底層研究非常有利。

入門攻略

iOS設(shè)備主要使用的為ARM64匯編,因此本文主要介紹ARM64匯編的入門技巧。匯編入門最難的地方在于對棧的理解,匯編的所有指令操作都是圍繞棧實(shí)現(xiàn)的,在匯編中,沒有變量的概念,只有寄存器和內(nèi)存。

匯編中的棧是由高地址向低地址生長的數(shù)據(jù)結(jié)構(gòu),sp指針永遠(yuǎn)指向棧頂,需要記住的是,在某位置進(jìn)行存儲(chǔ)時(shí),是向高地址進(jìn)行的,下面以一個(gè)簡單的例子講解匯編的棧操作。我們以一段簡單的C代碼為例。

// hello.c
#include <stdio.h>
 
int test(int a, int b) {
 int res = a + b;
 return res;
}
 
int main() {
 int res = test(1, 2);
 return 0;
}

使用clang可以將其編譯為特定指令集的匯編代碼,這里我們將其編譯為ARM64指令集的匯編代碼。

clang -S -arch arm64 -isysroot `xcrun --sdk iphoneos --show-sdk-path` hello.c

完整的匯編代碼如下。

.section	__TEXT,__text,regular,pure_instructions
	.ios_version_min 11, 2
	.globl	_test
	.p2align	2
_test:   ; @test
; BB#0:
	sub	sp, sp, #16 ; =16
	str	w0, [sp, #12]
	str	w1, [sp, #8]
	ldr	w0, [sp, #12]
	ldr	w1, [sp, #8]
	add		w0, w0, w1
	str	w0, [sp, #4]
	ldr	w0, [sp, #4]
	add	sp, sp, #16 ; =16
	ret
 
	.globl	_main
	.p2align	2
_main:   ; @main
; BB#0:
	sub	sp, sp, #32 ; =32
	stp	x29, x30, [sp, #16] ; 8-byte Folded Spill
	add	x29, sp, #16 ; =16
	orr	w0, wzr, #0x1
	orr	w1, wzr, #0x2
	stur	wzr, [x29, #-4]
	bl	_test
	mov	w1, #0
	str	w0, [sp, #8]
	mov	 x0, x1
	ldp	x29, x30, [sp, #16] ; 8-byte Folded Reload
	add	sp, sp, #32 ; =32
	ret
.subsections_via_symbols

本節(jié)我們只討論棧操作,因此忽略main函數(shù)和printf調(diào)用部分,我們只看對test函數(shù)的調(diào)用,節(jié)選這一段匯編代碼如下。

sub	sp, sp, #16 ; =16
	str	w0, [sp, #12]
	str	w1, [sp, #8]
	ldr	w0, [sp, #12]
	ldr	w1, [sp, #8]
	add w0, w0, w1
	str	w0, [sp, #4]
	ldr	w0, [sp, #4]
	add	sp, sp, #16 ; =16
	ret

首先介紹一下基本指令和指令的學(xué)習(xí)方式,要查詢某個(gè)指令如何使用,最好的方式是去查詢ARM公司提供的官方文檔,在官方文檔頁面可以直接搜索指令并查看用法和例程,本文會(huì)簡單講解上面的匯編代碼中出現(xiàn)的指令。

sub用于對寄存器實(shí)施減法, suba,b,c等價(jià)于 a=b-c,在ARM匯編中,目的操作數(shù)一般出現(xiàn)最前方,例如 mov ra,rb 代表將rb寄存器的值復(fù)制到ra寄存器。add和sub同理,只是將減法變成了加法。

str和ldr是一對指令,str的全稱是store register,即將寄存器的值存儲(chǔ)到內(nèi)存中,ldr的全稱是load register,即將內(nèi)存中的值讀到寄存器,因此他們的第一個(gè)參數(shù)都是寄存器,第二個(gè)參數(shù)都是內(nèi)存地址。[sp,#12] 代表 sp+12 這個(gè)地址,同理 [sp,#-12] 代表 sp-12 這個(gè)地址。注意這里的數(shù)字都是以字節(jié)為單位的偏移量,以 str w0,[sp,#12] 為例,w是4字節(jié)的寄存器,這個(gè)指令代表將w0寄存器的值存儲(chǔ)在sp+12這個(gè)地址上,由于w0有4個(gè)字節(jié),所以存儲(chǔ)后會(huì)占據(jù) sp+12~sp+16這個(gè)內(nèi)存區(qū)域。

下面將分段講解這段匯編代碼,在編譯器生成匯編時(shí),首先會(huì)計(jì)算需要的??臻g大小,并利用sp指針向低地址開辟相應(yīng)的空間,我們再來看一下test函數(shù)。

int test(int a, int b) {
 int res = a + b;
 return res;
}

這里涉及了3個(gè)int變量,分別是a、b、res,int變量占據(jù)4個(gè)字節(jié),因此一共需要12個(gè)字節(jié),但ARM64匯編為了提高訪問效率要求按照16字節(jié)進(jìn)行對齊,因此需要16byte的空間,也就是需要在棧上開辟16字節(jié)的空間,我們來看匯編的第一句,正是將sp指針下移16字節(jié)。

sub	sp, sp, #16

sp下移16后,留下了4個(gè)4字節(jié)的內(nèi)存空格,共計(jì)16字節(jié),我們繼續(xù)看下面的句子。

這兩句的含義是將w0存儲(chǔ)在sp+12的格子中,w1存儲(chǔ)在sp+8的格子中,上面的例子中提到 x0, x1等寄存器將順序存放函數(shù)的入?yún)?,x0和w0是同一個(gè)寄存器的不同大小體現(xiàn),x0為8字節(jié),w0為x0的前4個(gè)字節(jié),因此w0是函數(shù)的第一個(gè)入?yún),w1是函數(shù)的第二個(gè)入?yún),由于存儲(chǔ)是從低地址到高地址的,所以a將占據(jù) sp+12~sp+16,同理b將占據(jù) sp+8~sp+12,則棧的結(jié)構(gòu)變?yōu)橄聢D。

str	w0, [sp, #12]
str	w1, [sp, #8]

按照“上帝視角”,接下來test函數(shù)應(yīng)該將a和b相加,需要注意的是,只有寄存器才能參與運(yùn)算,因此接下來的匯編代碼又將變量的值從內(nèi)存中讀出,進(jìn)行相加運(yùn)算。

ldr	w0, [sp, #12]
	ldr	w1, [sp, #8]
	add w0, w0, w1

由此可見先存儲(chǔ)再讀取后運(yùn)算其實(shí)是多余的,這是沒有進(jìn)行編譯優(yōu)化的結(jié)果,學(xué)習(xí)不進(jìn)行編譯優(yōu)化的匯編更能讓我們理解其工作機(jī)制。

接下來的代碼將w0存入了sp+4,也就是res變量的內(nèi)存區(qū)域。

str	w0, [sp, #4]

接下來就要進(jìn)行返回了,在例子中我們提到,函數(shù)的返回值一般存儲(chǔ)在x0寄存器中返回,因此我們需要將res的值載入x0寄存器。

ldr	w0, [sp, #4]

這里之所以使用w寄存器,是因?yàn)閕nt為4字節(jié),這也就是類型轉(zhuǎn)換時(shí)帶來信息丟失的原因,例如從long到int的轉(zhuǎn)換就類似于將x寄存器的值以w的形式進(jìn)行存儲(chǔ)。最后的代碼為將棧還原,并返回到函數(shù)調(diào)用處繼續(xù)向下執(zhí)行。

add	sp, sp, #16
ret

顯然,經(jīng)過這樣的操作,棧被完全還原到了函數(shù)調(diào)用以前的樣子,需要注意的細(xì)節(jié)是,??臻g中的內(nèi)存單元并未被清空,這也就導(dǎo)致下一次使用低地址的棧時(shí),未初始化單元的值是不確定的,這也就是局部變量不初始化值隨機(jī)的根本原因。

通過上面的例子,我們對棧有了基本的認(rèn)識(shí),匯編的操作基本都是對棧進(jìn)行的,只要理解了棧機(jī)制,只需要學(xué)習(xí)各種指令,即可掌握足夠使用的匯編技能。

深入

在了解了棧以后,就可以看一些較為復(fù)雜的匯編片段來進(jìn)行學(xué)習(xí)了,初級(jí)階段可以嘗試看著函數(shù)寫匯編代碼,高級(jí)階段要求能夠看著匯編還原成函數(shù)邏輯,本文僅僅介紹入門基礎(chǔ),下面推薦一些大牛的博客供大家深入學(xué)習(xí)匯編技能。

1.知兵的知乎專欄

2.劉坤的匯編入門文章

總結(jié)

掌握ARM匯編能夠幫助開發(fā)者更好地了解編譯器和CPU的工作原理,除了能夠指導(dǎo)編碼外,還能夠擴(kuò)寬視野,通過反編譯分析一些閉源代碼的邏輯或是進(jìn)行一些安全加固,因此在匯編上付出時(shí)間是十分值得的。

參考資料

1.知兵. iOS調(diào)試進(jìn)階 https://zhuanlan.zhihu.com/c_142064221

2.ARM官方文檔 http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0802a/STUR_fpsimd.html

3.反調(diào)試和繞過 http://jmpews.github.io/2017/08/09/darwin/反調(diào)試及繞過

以上所述是小編給大家介紹的iOS匯編入門教程之ARM64匯編基礎(chǔ),希望對大家有所幫助!

相關(guān)文章

  • 詳解匯編語言中中括號(hào)[]作用及l(fā)ea和mov指令的區(qū)別

    詳解匯編語言中中括號(hào)[]作用及l(fā)ea和mov指令的區(qū)別

    這篇文章主要介紹了匯編語言中中括號(hào)[]作用及l(fā)ea和mov指令的區(qū)別,本文分步驟給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-01-01
  • Windows10下利用DOSBOX和MASM32搭建匯編語言開發(fā)環(huán)境

    Windows10下利用DOSBOX和MASM32搭建匯編語言開發(fā)環(huán)境

    這篇文章主要介紹了Windows10下利用DOSBOX和MASM32搭建匯編語言開發(fā)環(huán)境,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-01-01
  • 一文秒懂匯編中的循環(huán)問題

    一文秒懂匯編中的循環(huán)問題

    這篇文章主要介紹了一文秒懂匯編中的循環(huán)問題,通過每一行指令詳細(xì)分析,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-04-04
  • UEFI開發(fā)基礎(chǔ)HII代碼示例

    UEFI開發(fā)基礎(chǔ)HII代碼示例

    這篇文章主要為大家介紹了UEFI開發(fā)基礎(chǔ)HII代碼示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • GNU ARM匯編語法原理及操作解析

    GNU ARM匯編語法原理及操作解析

    這篇文章主要介紹了GNU ARM匯編語法原理及操作解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • 匯編語言實(shí)現(xiàn)電子鬧鐘思路詳解

    匯編語言實(shí)現(xiàn)電子鬧鐘思路詳解

    這篇文章主要介紹了匯編語言實(shí)現(xiàn)電子鬧鐘思路詳解,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-02-02
  • 常用的匯編指令與技巧(收藏)

    常用的匯編指令與技巧(收藏)

    這篇文章主要介紹了常用的匯編指令與技巧,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-01-01
  • 匯編語言MIPS指令分類及尋址模式原理概念

    匯編語言MIPS指令分類及尋址模式原理概念

    這篇文章主要為大家介紹了匯編語言MIPS指令分類及尋址模式的原理及概念,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2021-11-11
  • 從Go匯編角度解讀for循環(huán)的問題

    從Go匯編角度解讀for循環(huán)的問題

    Go常用的遍歷方式有兩種:for和for-range。這篇文章主要介紹了從Go匯編角度解讀for循環(huán)的兩個(gè)疑點(diǎn),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-02-02
  • 匯編指令-狀態(tài)寄存器、cmp、test、jz等指令詳細(xì)說明

    匯編指令-狀態(tài)寄存器、cmp、test、jz等指令詳細(xì)說明

    這篇文章主要介紹了匯編指令-狀態(tài)寄存器、cmp、test、jz等指令詳細(xì)說明,需要的朋友可以參考下
    2020-01-01

最新評論