iOS逆向:匯編初探

前言

  • 今天開始學習逆向(掉頭發)啦!

  • 在逆向開發中,非常重要的一個環節就是靜態分析.首先我們是逆向iOS系統上面的APP.那么我們知道,一個APP安裝在手機上面的可執行文件本質上是二進制文件.因為iPhone手機本質上執行的指令是二進制.是由手機上的CPU執行的.所以靜態分析是建立在分析二進制上面.所以今天我們接下來的課程從非常基礎的東西開始講解.

一 、 匯編初識

我們的代碼在終端設備上是這樣的過程:

  • 匯編語言機器語言一一對應,每一條機器指令都有與之對應的匯編指令
  • 匯編語言可以通過編譯得到機器語言機器語言可以通過反匯編得到匯編語言
  • 高級語言可以通過編譯得到匯編語言 \ 機器語言,但匯編語言\機器語言幾乎不可能還原成高級語言

匯編語言的特點

  • 可以直接訪問控制各種硬件設備,比如存儲器、CPU等,能最大限度地發揮硬件的功能

  • 能夠不受編譯器的限制,對生成的二進制代碼進行完全的控制

  • 目標代碼簡短,占用內存少,執行速度快

  • 匯編指令是機器指令的助記符,同機器指令一一對應。每一種CPU都有自己的機器指令集\匯編指令集,所以匯編語言不具備可移植性

  • 知識點過多,開發者需要對CPU等硬件結構有所了解,不易于編寫、調試、維護

  • 不區分大小寫,比如mov和MOV是一樣的

匯編的用途

  • 編寫驅動程序、操作系統(比如Linux內核的某些關鍵部分)
  • 對性能要求極高的程序或者代碼片段,可與高級語言混合使用(內聯匯編)
  • 軟件安全
    • 病毒分析與防治
    • 逆向\加殼\脫殼\破解\外掛\免殺\加密解密\漏洞\黑客
  • 理解整個計算機系統的最佳起點和最有效途徑
  • 為編寫高效代碼打下基礎
  • 弄清代碼的本質
    • 函數的本質究竟是什么?
    • ++a + ++a + ++a 底層如何執行的?
    • 編譯器到底幫我們干了什么?
    • DEBUG模式和RELEASE模式有什么關鍵的地方被我們忽略

越底層越單純!真正的程序員都需要了解的一門非常重要的語言,匯編!

匯編語言的種類

  • 目前討論比較多的匯編語言有

    • 8086匯編(8086處理器是16bit的CPU)
    • Win32匯編
    • Win64匯編
    • ARM匯編(嵌入式、MaciOS
      ......
  • 我們iPhone里面用到的是ARM匯編,但是不同的設備也有差異.因CPU的架構不同.

架構 設備
armv6 iPhone, iPhone2, iPhone3G, 第一代、第二代 iPod Touch
armv7 iPhone3GS, iPhone4, iPhone4S,iPad, iPad2, iPad3(The New iPad), iPad mini, iPod Touch 3G, iPod Touch4
armv7s iPhone5, iPhone5C, iPad4(iPad with Retina Display)
arm64 iPhone5S 以后 iPhoneX , iPad Air, iPad mini2以后

二、幾個必要的常識

  • 要想學好匯編,首先需要了解CPU等硬件結構
  • APP/程序的執行過程
  • 硬件相關最為重要是CPU/內存
  • 在匯編中,大部分指令都是和CPU與內存相關的

2.1 總線

  • 每一個CPU芯片都有許多管腳,這些管腳和總線相連,CPU通過總線跟外部器件進行交互
  • 總線:一根根導線的集合
  • 總線的分類
    • 地址總線
    • 數據總線
    • 控制總線

舉個例子:


  • 地址總線
    • 它的寬度決定了CPU尋址能力
    • 8086的地址總線寬度是20,所以尋址能力是1M( 220 )
  • 數據總線
    • 它的寬度決定了CPU單次數據傳送量,也就是數據傳送速度;
    • 8086的數據總線寬度是16,所以單次最大傳遞2個字節的數據;
  • 控制總線
    • 它的寬度決定了CPU對其他器件的控制能力、能有多少種控制;
小練習:

2.2 內存

  • 內存地址空間的大小受CPU地址總線寬度的限制。8086的地址總線寬度為20,可以定位220個不同的內存單元(內存地址范圍0x00000~0xFFFFF),所以8086的內存空間大小為1MB

  • 0x00000~0x9FFFF:主存儲器。可讀可寫

  • 0xA0000~0xBFFFF:向顯存中寫入數據,這些數據會被顯卡輸出到顯示器。可讀可寫

  • 0xC0000~0xFFFFF:存儲各種硬件\系統信息。只讀

2.3 進制

進制的定義
  • 八進制由8個符號組成:0 1 2 3 4 5 6 7 逢八進一
  • 十進制由10個符號組成:0 1 2 3 4 5 6 7 8 9逢十進一
  • N進制就是由N個符號組成:逢 N 進 一

2.4 數據的寬度

數學上的數字,是沒有大小限制的,可以無限的大。但在計算機中,由于受硬件的制約,數據都是有長度限制的(我們稱為數據寬度),超過最多寬度的數據會被丟棄。

#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int test(){
    int cTemp = 0x1FFFFFFFF;
    return cTemp;
}

int main(int argc, char * argv[]) {
    printf("%x\n",test());
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

計算機中常見的數據寬度

  • 位(Bit): 1個位就是1個二進制位.0或者1
  • 字節(Byte): 1個字節由8個Bit組成(8位).內存中的最小單元Byte.
  • 字(Word): 1個字由2個字節組成(16位),這2個字節分別稱為高字節和低字節.
  • 雙字(Doubleword): 1個雙字由兩個字組成(32位)

那么計算機存儲數據它會分為有符號數無符號數.那么關于這個看圖就理解了!

無符號數,直接換算!
有符號數:
正數:  0    1    2    3    4    5    6    7 
負數:  F    E    D    B    C    A    9    8
      -1   -2   -3   -4   -5   -6   -7   -8

三、CPU&寄存器

內部部件之間由總線連接

CPU除了有控制器、運算器還有寄存器。其中寄存器的作用就是進行數據的臨時存儲。

CPU的運算速度是非常快的,為了性能CPU在內部開辟一小塊臨時存儲區域,并在進行運算時先將數據從內存復制到這一小塊臨時存儲區域中,運算時就在這一小快臨時存儲區域內進行。我們稱這一小塊臨時存儲區域為寄存器。

  • 對于arm64系的CPU來說, 如果寄存器以x開頭則表明的是一個64位的寄存器,如果以w開頭則表明是一個32位的寄存器,在系統中沒有提供16位和8位的寄存器供訪問和使用。其中32位的寄存器是64位寄存器的低32位部分并不是獨立存在的

    • 對程序員來說,CPU中最主要部件是寄存器可以通過改變寄存器的內容來實現對CPU的控制
    • 不同的CPU,寄存器的個數、結構是不相同的

浮點和向量寄存器

因為浮點數的存儲以及其運算的特殊性,CPU中專門提供浮點數寄存器來處理浮點數

  • 浮點寄存器 64位: D0 - D31 32位: S0 - S31

現在的CPU支持向量運算.(向量運算在圖形處理相關的領域用得非常的多)為了支持向量計算系統了也提供了眾多的向量寄存器.

  • 向量寄存器 128位:V0-V31

通用寄存器

  • 通用寄存器也稱數據地址寄存器通常用來做數據計算的臨時存儲、做累加計數地址保存等功能。定義這些寄存器的作用主要是用于在CPU指令中保存操作數,在CPU中當做一些常規變量來使用。
  • ARM64 擁有有 32個64位的通用寄存器 x0 到 x30,以及XZR(零寄存器), 這些通用寄存器有時也有特定用途。
    • 那么w0 到 w28 這些是32位的. 因為64位CPU可以兼容32位. 所以可以只使用64位寄存器的低32位.
    • 比如 w0 就是 x0的低32位!

注意:
了解過8086匯編的同學知道,有一種特殊的寄存器段寄存器:CS,DS,SS,ES四個寄存器來保存這些段的基地址,這個屬于Intel架構CPU中.在ARM中并沒有

  • 通常,CPU會先將內存中的數據存儲到通用寄存器中,然后再對通用寄存器中的數據進行運算
  • 假設內存中有塊紅色內存空間的值是3,現在想把它的值加1,并將結果存儲到藍色內存空間
  • CPU首先會將紅色內存空間的值放到X0寄存器中:mov X0,紅色內存空間
  • 然后讓X0寄存器與1相加:add X0,1
  • 最后將值賦值給內存空間:mov 藍色內存空間,X0

pc寄存器(program counter)

  • 指令指針寄存器,它指示了CPU當前要讀取指令的地址
  • 在內存或者磁盤上,指令和數據沒有任何區別,都是二進制信息
  • CPU在工作的時候把有的信息看做指令,有的信息看做數據,為同樣的信息賦予了不同的意義
    • 比如 1110 0000 0000 0011 0000 1000 1010 1010
    • 可以當做數據 0xE003008AA
    • 也可以當做指令 mov x0, x8
  • CPU根據什么將內存中的信息看做指令?
    • CPU將pc指向的內存單元的內容看做指令
    • 如果內存中的某段內容曾被CPU執行過,那么它所在的內存單元必然被pc指向過

高速緩存

iPhoneX上搭載的ARM處理器A11它的1級緩存的容量是64KB,2級緩存的容量8M.

CPU每執行一條指令前都需要從內存中將指令讀取到CPU內并執行。而寄存器的運行速度相比內存讀寫要快很多,為了性能,CPU還集成了一個高速緩存存儲區域.當程序在運行時,先將要執行的指令代碼以及數據復制到高速緩存中去(由操作系統完成).CPU直接從高速緩存依次讀取指令來執行.

bl指令

  • CPU從何處執行指令是由pc中的內容決定的,我們可以通過改變pc的內容控制CPU執行目標指令

  • ARM64提供了一個mov指令(傳送指令),可以用來修改大部分寄存器的值,比如

    • mov x0,#10、mov x1,#20
  • 但是,mov指令不能用于設置pc的值,ARM64沒有提供這樣的功能

  • ARM64提供了另外的指令來修改PC的值,這些指令統稱為轉移指令,最簡單的是bl指令

狀態寄存器
???CPU內部的寄存器中,有一種特殊的寄存器(對于不同的處理器,個數和結構都可能不同).這種寄存器在ARM中,被稱為狀態寄存器就是CPSR(current program status register)寄存器
CPSR和其他寄存器不一樣,其他寄存器是用來存放數據的,都是整個寄存器具有一個含義.而CPSR寄存器是按位起作用的,也就是說,它的每一位都有專門的含義,記錄特定的信息.

注:CPSR寄存器是32位的

  • CPSR的低8位(包括I、F、T和M[4:0])稱為控制位,程序無法修改,除非CPU運行于特權模式下,程序才能修改控制位!
  • N、Z、C、V均為條件碼標志位。它們的內容可被算術或邏輯運算的結果所改變,并且可以決定某條指令是否被執行!意義重大!

N(Negative)標志

CPSR的第31位是 N,符號標志位。它記錄相關指令執行后,其結果是否為負.如果為負 N = 1,如果是非負數 N = 0.

???注意,在ARM64的指令集中,有的指令的執行時影響狀態寄存器的,比如add\sub\or等,他們大都是運算指令(進行邏輯或算數運算);

Z(Zero)標志

CPSR的第30位是Z,0標志位。它記錄相關指令執行后,其結果是否為0.如果結果為0.那么Z = 1.如果結果不為0,那么Z = 0.

???對于Z的值,我們可以這樣來看,Z標記相關指令的計算結果是否為0,如果為0,則Z要記錄下"是0"這樣的肯定信息.在計算機中1表示邏輯真,表示肯定.所以當結果為0的時候Z = 1,表示"結果是0".如果結果不為0,則Z要記錄下"不是0"這樣的否定信息.在計算機中0表示邏輯假,表示否定,所以當結果不為0的時候Z = 0,表示"結果不為0"。

C(Carry)標志

CPSR的第29位是C,進位標志位。一般情況下,進行無符號數的運算。
加法運算:當運算結果產生了進位時(無符號數溢出),C=1,否則C=0。
減法運算(包括CMP):當運算時產生了借位時(無符號數溢出),C=0,否則C=1。

???對于位數為N的無符號數來說,其對應的二進制信息的最高位,即第N - 1位,就是它的最高有效位,而假想存在的第N位,就是相對于最高有效位的更高位。如下圖所示:

進位

???我們知道,當兩個數據相加的時候,有可能產生從最高有效位想更高位的進位。比如兩個32位數據:0xaaaaaaaa + 0xaaaaaaaa,將產生進位。由于這個進位值在32位中無法保存,我們就只是簡單的說這個進位值丟失了。其實CPU在運算的時候,并不丟棄這個進位制,而是記錄在一個特殊的寄存器的某一位上。ARM下就用C位來記錄這個進位值。比如,下面的指令

mov w0,#0xaaaaaaaa;0xa 的二進制是 1010
adds w0,w0,w0; 執行后 相當于 1010 << 1 進位1(無符號溢出) 所以C標記 為 1
adds w0,w0,w0; 執行后 相當于 0101 << 1 進位0(無符號沒溢出) 所以C標記 為 0
adds w0,w0,w0; 重復上面操作
adds w0,w0,w0

借位

???當兩個數據做減法的時候,有可能向更高位借位。再比如,兩個32位數據:0x00000000 - 0x000000ff,將產生借位,借位后,相當于計算0x100000000 - 0x000000ff。得到0xffffff01 這個值。由于借了一位,所以C位 用來標記借位。C = 0.比如下面指令:

mov w0,#0x0
subs w0,w0,#0xff ;
subs w0,w0,#0xff
subs w0,w0,#0xff

V(Overflow)溢出標志

CPSR的第28位是V,溢出標志位。在進行有符號數運算的時候,如果超過了機器所能標識的范圍,稱為溢出。

  • 正數 + 正數 為負數 溢出
  • 負數 + 負數 為正數 溢出
  • 正數 + 負數 不可能溢出
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372

推薦閱讀更多精彩內容