iOS開發(fā)同學(xué)的arm64匯編入門

在定位某些crash問題的時候讨永,有時候遇到一些問題很詭異侣姆。有時候掛在了系統(tǒng)庫里面。這個時候定位crash問題往往是比較頭疼的儒将。那么這個時候?qū)W會一些匯編知識师崎,利用匯編調(diào)試技巧進(jìn)行調(diào)試可能會起到意想不到的效果。

學(xué)習(xí)匯編語言不只是幫助定位crash而已椅棺,學(xué)習(xí)匯編可以幫助你真正的理解計算機(jī)犁罩。畢竟CPU上跑的就是對應(yīng)的指令集。

0x1 工具

我們面對的要么是源代碼两疚,要么是二進(jìn)制床估。因此我們需要一些反匯編的工具來輔助我們進(jìn)行匯編代碼查看。推薦工具有: –?Hopper Disassembler?收費(fèi)應(yīng)用诱渤,看匯編代碼非常方便 –?MachOView?開源工具丐巫,看Mach-o文件結(jié)構(gòu)非常方便。

0x2 基本概念

從高級語言過渡到匯編語言,重要的是基本概念的轉(zhuǎn)換递胧。匯編里面要學(xué)習(xí)的三個重要概念碑韵,我認(rèn)為是 寄存器、棧缎脾、指令祝闻。 arm64架構(gòu)又分為2種執(zhí)行狀態(tài):?AArch64 Application Level?和?AArch32 Application Level, 本文只講AArch64.

0x21 寄存器

如果你還不知道什么是寄存器,建議先Google一下遗菠。 這里不再詳細(xì)說明联喘,寄存器是CPU中的高速存儲單元,要比內(nèi)存中存取要快的多辙纬。

這里說明一下arm64有哪些寄存器:

R0 – R30

r0 - r30?是31個通用整形寄存器豁遭。每個寄存器可以存取一個64位大小的數(shù)。 當(dāng)使用?x0 - x30訪問時贺拣,它就是一個64位的數(shù)蓖谢。當(dāng)使用?w0 - w30訪問時,訪問的是這些寄存器的低32位譬涡,如圖:

其實通用寄存器有32個蜈抓,第32個寄存器x31,在指令編碼中昂儒,使用來做?zero register, 即ZR,?XZR/WZR分別代表64/32位,zero register的作用就是0委可,寫進(jìn)去代表丟棄結(jié)果渊跋,拿出來是0.

其中?r29?又被叫做?fp?(frame pointer).??r30?又被叫做?lr?(link register)。其用途會在下一節(jié)《椬徘悖》中講到拾酝。

SP

SP寄存器其實就是 x31,在指令編碼中卡者,使用?SP/WSP來進(jìn)行對SP寄存器的訪問蒿囤。

PC

PC寄存器中存的是當(dāng)前執(zhí)行的指令的地址。在arm64中崇决,軟件是不能改寫PC寄存器的材诽。

V0 – V31

V0 - V31?是向量寄存器,也可以說是浮點(diǎn)型寄存器恒傻。它的特點(diǎn)是每個寄存器的大小是 128 位的脸侥。 分別可以用Bn Hn Sn Dn Qn的方式來訪問不同的位數(shù)。如圖:

Bn Hn Sn Dn Qn可以這樣理解記憶, 基于一個word是32位盈厘,也就是4Byte大姓稣怼:

Bn: 一個Byte的大小?

Hn: half word. 就是16位?

Sn: single word. 32位?

Dn: double word. 64位?

Qn: quad word. 128位?

SPRs

SPRs是狀態(tài)寄存器,用于存放程序運(yùn)行中一些狀態(tài)標(biāo)識。不同于編程語言里面的if else.在匯編中就需要根據(jù)狀態(tài)寄存器中的一些狀態(tài)來控制分支的執(zhí)行外遇。狀態(tài)寄存器又分為?The Current Program Status Register (CPSR)?和?The Saved Program Status Registers (SPSRs)注簿。 一般都是使用CPSR, 當(dāng)發(fā)生異常時跳仿,?CPSR會存入SPSR诡渴。當(dāng)異常恢復(fù)塔嬉,再拷貝回CPSR玩徊。

還有一些系統(tǒng)寄存器,還有?FPSR?FPCR是浮點(diǎn)型運(yùn)算時的狀態(tài)寄存器等谨究《鞲ぃ基本了解上面這些寄存器就可以了。

0x22 棧

棧就是指令執(zhí)行時存放臨時變量的內(nèi)存空間胶哲。在學(xué)習(xí)匯編代碼的執(zhí)行過程中畔塔,了解棧的結(jié)構(gòu)非常重要。

先列出一些棧的特性:

棧是從高地址到低地址的鸯屿, 棧低是高地址澈吨,棧頂是低地址。

fp指向當(dāng)前frame的棧底寄摆,也就是高地址谅辣。

sp指向棧頂,也就是地地址婶恼。

下面的圖簡單的描述了從方法A調(diào)用方法B時 棧是如何劃分的:

其中3行匯編代碼就是方法B的前三行匯編指令桑阶。它們做的事情就是圖中描述的事情 (x29就是fp, x30就是lr):

將fp, lr保存到?sp - 0x10的地方. 也就是圖中?--> fp_B的位置。然后將sp設(shè)置為?sp-0x10

將?fp?設(shè)置為當(dāng)前?sp勾邦。也就是?--> fp_B的位置蚣录。 這一步就設(shè)置了_funcB的 fp了

將?sp?設(shè)置為?sp - 0x30。 也就是將sp指向了圖中?--> sp_B?的位置

注:?lr?是link register中的值眷篇,它存的是方法_funcA的執(zhí)行的最后一行指令的下一行萎河。它的作用也很好理解:當(dāng)_funcB執(zhí)行完了之后要返回_funcA繼續(xù)執(zhí)行,但是計算機(jī)要如何知道返回到哪執(zhí)行呢蕉饼? 就是靠lr記錄了返回的地址虐杯,方法才能得以正常返回。

說道這里昧港,那么當(dāng)?_funcB執(zhí)行完畢后厦幅,是如何把棧恢復(fù)到_funcA的過程的呢慨飘? 我們直接分析?_funcB的最后3條指令:

12345movsp,fp;//? sp 設(shè)置為fp, 就是圖中 -->fp_B 的位置ldpfp,lr,[sp],#0x10;//? 從sp指向的地址中讀取 2個64位确憨,分別存入fp,lr译荞。 然后將sp += 0x10// 這一步執(zhí)行完之后,fp就執(zhí)行了圖中 -->fp_A. lr恢復(fù)成 _funcA的返回地址休弃。 sp指向了 -->sp_A. // 這個時候狀態(tài)已經(jīng)完全恢復(fù)到了 _funcA 的環(huán)境ret;// 返回指令吞歼,這一步直接執(zhí)行l(wèi)r的指令。

上面描述了方法如何調(diào)用的塔猾。我們知道在編程語言里面方法都有入?yún)⒏萋猓蟹祷刂档摹T趨R編里面如何體現(xiàn)呢丈甸?

一般來說 arm64上 x0 – x7 分別會存放方法的前 8 個參數(shù)

如果參數(shù)個數(shù)超過了8個糯俗,多余的參數(shù)會存在棧上,新方法會通過棧來讀取睦擂。

方法的返回值一般都在 x0 上得湘。

如果方法返回值是一個較大的數(shù)據(jù)結(jié)構(gòu)時,結(jié)果會存在 x8 執(zhí)行的地址上顿仇。

0x23 指令

在上一級的內(nèi)容中我們已經(jīng)看到了一些指令淘正。 匯編指令除了數(shù)量較多,其基本原理都是比較簡單的臼闻,單拎出來一條指令就是很simple的操作鸿吆。 比如mov就是一個賦值。ldr就是一個取值述呐。

那匯編指令大概可以分為哪幾種呢惩淳?我認(rèn)為了解以下幾種基本指令就可以正常閱讀匯編代碼了。

0x231?運(yùn)算

算術(shù)運(yùn)算

算術(shù)運(yùn)算就是像?ADD?SUB?MUL?… 等加減乘除運(yùn)算乓搬,也是很好理解的指令?

如:

12345addx0,x1,x2;// 把 x1 + x2 = x0 這樣一個操作思犁。subsp,sp,0x30;// 把 sp - 30 存入sp.cmpx11,#4;// 相當(dāng)于 subs xzr, x11, #4.? // 如果 x11 - 4 == 0, 那么狀態(tài)寄存器NZCV.Z = 1// 如果 x11 - 4 < 0, 那么 NZCV.N = 1

NZCV是狀態(tài)寄存器中存的幾個狀態(tài)值,分別代表運(yùn)算過程中產(chǎn)生的狀態(tài)缤谎,其中:?

* N, negative condition flag,一般代表運(yùn)算結(jié)果是負(fù)數(shù)?

* Z, zero condition flag, 運(yùn)算結(jié)果為0?

* C, carry condition flag, 無符號運(yùn)算有溢出時褐着,C=1坷澡。?

* V, oVerflow condition flag 有符號運(yùn)算有溢出時,V=1含蓉。?

邏輯運(yùn)算指令

有?LSL(邏輯左移)?LSR(邏輯右移)?ASR(算術(shù)右移)?ROR(循環(huán)右移)频敛。?

有?AND(與)??ORR(或)?EOR(異或)

邏輯位移運(yùn)算通常也可以與算術(shù)運(yùn)算一起用,如:

1addx14,x4,x27,lsl#1;// 意思是把? (x27 << 1) + x4 = x14;

拓展位數(shù)運(yùn)算

有?zero extend(高位補(bǔ)0) 和?sign extend(高位填充和符號位一致馅扣,一般有符號數(shù)用這個)斟赚。 一般用來補(bǔ)齊位數(shù)。常和算術(shù)運(yùn)算配合一起.

如:

1addw20,w30,w20,uxth// 取 w20的低16位差油,無符號補(bǔ)齊到32位后再進(jìn)行? w30 + w20的運(yùn)算拗军。

Mov

0x232 尋址

既然是和內(nèi)存相關(guān)的任洞,那就是兩種,一種存发侵,一種取交掏。一般來說?

L打頭的基本都是取值指令,如 LDR LDP;?

S打頭的基本都是存值指令刃鳄,如 STR STP;?

例:

12345ldrx0,[x1];// 從`x1`指向的地址里面取出一個 64 位大小的數(shù)存入 `x0`ldpx1,x2,[x10,#0x10];// 從 x10 + 0x10 指向的地址里面取出 2個 64位的數(shù)盅弛,分別存入x1, x2strx5,[sp,#24];// 把x5的值(64位數(shù)值)存到 sp+24 指向的內(nèi)存地址上stpx29,x30,[sp,#-16]!;// 把 x29, x30的值存到 sp-16的地址上,并且把 sp-=16. ldpx29,x30,[sp],#16;// 從sp地址取出 16 byte數(shù)據(jù)叔锐,分別存入x29, x30. 然后 sp+=16;

其中尋址的格式由分為下面這3種類型:

123[x10,#0x10]// signed offset挪鹏。 意思是從 x10 + 0x10的地址取值[sp,#-16]!// pre-index。? 意思是從 sp-16地址取值愉烙,取值完后在把 sp-16? writeback 回 sp[sp],#16// post-index讨盒。 意思是從 sp 地址取值,取值完后在把 sp+16 writeback 回 sp

0x233 跳轉(zhuǎn)

跳轉(zhuǎn)氛圍有返回跳轉(zhuǎn)BL和無返回跳轉(zhuǎn)B齿梁。 有返回的意思就是會存lr,因此?BL的L也可以理解為LR的意思催植。

1.存了LR也就意味著可以返回到本方法繼續(xù)執(zhí)行。一般用于不同方法直接的調(diào)用

2.B相關(guān)的跳轉(zhuǎn)沒有LR勺择,一般是本方法內(nèi)的跳轉(zhuǎn)创南,如while循環(huán),if else等省核。

跳轉(zhuǎn)相關(guān)的指令還會有種邏輯運(yùn)算稿辙,就是condition code。配合狀態(tài)寄存器中的狀態(tài)標(biāo)示气忠,就是代碼分支if else實現(xiàn)的關(guān)鍵邻储。

condition code有以下這些,表格中還標(biāo)注除了分別是比NZCV的哪個值:?

如:

12345cmpx2,#0;// x2 - 0 = 0旧噪。? 狀態(tài)寄存器標(biāo)識zero: PSTATE.NZCV.Z = 1b.ne0x1000d48f0;// ne就是個condition code, 這句的意思是吨娜,當(dāng)判斷狀態(tài)寄存器 NZCV.Z != 1才跳轉(zhuǎn),因此這句不會跳轉(zhuǎn)0x1000d4ab0bltestFuncA;// 跳轉(zhuǎn)方法淘钟,這個時候 lr 設(shè)置為 0x1000d4ab40x1000d4ab4orrx8,xzr,#0x1f00000000// testFuncA執(zhí)行完之后跳回lr就周到了這一行

0x4 小結(jié)

本文簡單介紹了一些arm64的匯編知識宦赠,arm64匯編的學(xué)習(xí)對于理解iOS代碼的執(zhí)行,計算機(jī)的運(yùn)行都有著不少的好處米母。我們在日常中利用匯編知識可以定位一些疑難雜癥的crash問題勾扭。可以從匯編原理出手開一個個腦洞铁瞒,玩一些黑科技妙色。比如包瘦身,靜態(tài)掃描等慧耍。

匯編指令的執(zhí)行是簡單確定的身辨,不會像我們調(diào)試其他代碼一眼丐谋,有些詭異問題,而匯編每條指令的結(jié)果都是確定的栅表,從這一角度來定位問題往往可以定位到根本原因笋鄙。

在匯編指令執(zhí)行的世界,你可以對代碼執(zhí)行有更深刻的理解怪瓶,原來一行代碼會被分解成這么多的指令萧落!因此,如果你在看完本文后對于學(xué)習(xí)匯編有了興趣洗贰,但是有很多細(xì)節(jié)還不太懂找岖,建議你自己用hopper反編譯一些代碼,自己嘗試一行一行理解每一個指令的意義敛滋,基本看透幾個方法就可以融匯貫通了许布。


https://blog.cnbluebox.com/blog/2017/07/24/arm64-start/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市绎晃,隨后出現(xiàn)的幾起案子蜜唾,更是在濱河造成了極大的恐慌,老刑警劉巖庶艾,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件袁余,死亡現(xiàn)場離奇詭異,居然都是意外死亡咱揍,警方通過查閱死者的電腦和手機(jī)颖榜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來煤裙,“玉大人掩完,你說我怎么就攤上這事∨鹋椋” “怎么了且蓬?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長题翰。 經(jīng)常有香客問我恶阴,道長,這世上最難降的妖魔是什么遍愿? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任存淫,我火速辦了婚禮耘斩,結(jié)果婚禮上沼填,老公的妹妹穿的比我還像新娘。我一直安慰自己括授,他們只是感情好坞笙,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布岩饼。 她就那樣靜靜地躺著,像睡著了一般薛夜。 火紅的嫁衣襯著肌膚如雪籍茧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天梯澜,我揣著相機(jī)與錄音寞冯,去河邊找鬼。 笑死晚伙,一個胖子當(dāng)著我的面吹牛吮龄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播咆疗,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼漓帚,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了午磁?” 一聲冷哼從身側(cè)響起尝抖,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎迅皇,沒想到半個月后昧辽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡喧半,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年奴迅,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挺据。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡取具,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出扁耐,到底是詐尸還是另有隱情暇检,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布婉称,位于F島的核電站块仆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏王暗。R本人自食惡果不足惜悔据,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望俗壹。 院中可真熱鬧科汗,春花似錦、人聲如沸绷雏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽患亿。三九已至五督,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間乘盖,已是汗流浹背早歇。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工倾芝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人箭跳。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓蛀醉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親衅码。 傳聞我的和親對象是個殘疾皇子拯刁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345