ARM汇编(1)-初识汇编

  目录

1. 初识汇编

汇编语言(Assembly Language)是任何一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。在汇编语言中,用助记符代替机器指令的操作码,用地址符号或标号代替指令或操作数的地址。在不同的设备中,汇编语言对应着不同的机器语言指令集,通过汇编过程转换成机器指令。特定的汇编语言和特定的机器语言指令集是一一对应的,不同平台之间不可直接移植。

一个APP安装在手机上面的可执行文件本质上是二进制文件。因为iPhone手机本质上执行的指令是二进制,是由手机上的CPU执行的。所以静态分析是建立在分析二进制上面。

2. 发展历程

2.1 编程语言

从1946年第一台电子计算机问世,人类和机器的交流方式和语言就成为了软件工程师和计算机从业者的主要研究方向。在过去的几十年,编程语言有了长足的发展。

2.2 机器语言

计算机的硬件作为一种电路元件,它的输出和输入只能是有电或者没电,也就是所说的高电平和低电平,所以计算机传递的数据是由“0” 和“1”组成的二进制数,所以说二进制的语言是计算机语言的本质。

2.3 汇编语言

不难看出机器语言作为一种编程语言,灵活性较差可阅读性也很差,为了减轻机器语言带给软件工程师的不适应,人们对机器语言进行了升级和改进:用一些容易理解和记忆的字母,单词来代替一个特定的指令。这就是助记符。

2.4 高级语言

人们需要设计一个能够不依赖于计算机硬件,能够在不同机器上运行的程序。这样可以免去很多编程的重复过程,提高效率,同时这种语言又要接近于数学语言或人的自然语言。这就诞生了高级编程语言。比如:C、C++、Java、OC、Swift。

我们的代码在终端设备上的执行过程,如下图:

  • 汇编语言与机器语言一一对应,每一条机器指令都有与之对应的汇编指令
  • 汇编语言可以通过编译得到机器语言,机器语言可以通过反汇编得到汇编语言
  • 高级语言可以通过编译得到汇编语言\机器语言,但汇编语言\机器语言几乎不可能还原成高级语言

2.5 汇编语言的特点

  • 可以直接访问、控制各种硬件设备,比如存储器、CPU等,能最大限度地发挥硬件的功能
  • 能够不受编译器的限制,对生成的二进制代码进行完全的控制
  • 目标代码简短,占用内存少,执行速度快
  • 汇编指令是机器指令的助记符,同机器指令一一对应。每一种CPU都有自己的机器指令集\汇编指令集,所以汇编语言不具备可移植性
  • 不区分大小写,比如mov和MOV是一样的
  • 知识点过多,开发者需要对CPU等硬件结构有所了解,不易于编写、调试、维护

2.6 用途

  • 编写驱动程序、操作系统(比如Linux内核的某些关键部分)
  • 对性能要求极高的程序或者代码片段,可与高级语言混合使用(内联汇编)
  • 软件安全
    • 病毒分析与防治
    • 逆向\加壳\脱壳\破解\外挂\免杀\加密解密\漏洞\黑客
  • 理解整个计算机系统的最佳起点和最有效途径
  • 为编写高效代码打下基础
  • 弄清代码的本质

越底层越单纯,但是使用起来越困难,同时也是程序员都需要了解的非常重要的语言。

2.7 汇编语言的分类

  • 8086汇编 (8086处理器时16bit的CPU)
  • Win32汇编
  • Win64汇编
  • ARM汇编(Mac,iOS)

在iPhone里面,用的的ARM汇编,又根据CPU的架构不同而有差异。

架构 设备
armv6 古老的iPhone3G之前,iPod Touch(第一代,第二代)
armv7 iPhone3GS, iPhone4, iPhone4S, iPad, iPad2, iPad3, iPad Mini, iPod Touch 3G, iPod Touch4
armv7s iPhone5, iPhone5C, iPad4
armv64 iPhone5S及以后的设备,iPad Air,iPad Mini2以后

3. 几个必要的知识点

3.1 bit

bit:表示『位』或者『比特』,是计算机运算的基础单位,是二进制数的最小单元。1 bit就是1位二进制数,只能存放0或者1。

3.2 Byte

Byte:表示『字节』,是计算机文件大小的基本单位。1Byte = 8bit,表示一个字节可以存放8位无符号数。一个汉子是2个字节,16位。一个英文字母是1个字节。

1个字节可以表示为:0000 0000,1111 1111。

3.3 总线

总线(Bus)是计算机各种功能部件之间传送信息的公共通信干线,它是由导线组成的传输线束, 按照计算机所传输的信息种类,计算机的总线可以划分为数据总线、地址总线和控制总线,分别用来传输数据、数据地址和控制信号。

总线是一种内部结构,它是cpu、内存、输入、输出设备传递信息的公用通道,主机的各个部件通过总线相连接,外部设备通过相应的接口电路再与总线相连接,从而形成了计算机硬件系统。

在计算机系统中,各个部件之间传送信息的公共通路叫总线,微型计算机是以总线结构来连接各个功能部件的。

简单来说

  • 每一个CPU芯片都有许多管脚,这些管脚和总线相连,CPU通过总线跟外部器件交互。
  • 总线就是一根根导线的集合

3.3.1 总线分类

一般情况下分为5大类:

  1. 数据总线:在CPU与RAM之间来回传送需要处理或需要存储的数据。它的宽度决定了CPU单次数据传送量,也就是数据传送的速度。例如:8086微处理器子长16位,其数据总线宽度也是16位,所以单次最大传递2个字节的数据。
  2. 地址总线:用来指定在RAM之中存储的数据的地址。它的宽度决定了CPU的寻址能力。例如8086的地址总线宽度是20位,那么它的寻址能力是1M(2^20)。
  3. 控制总线:将微处理器控制单元的信号,传送到周边设备。它的宽度决定了CPU对其他器件的控制能力。
  4. 扩展总线:外部设备和计算机主机进行数据通信的总线,例如ISA,PCI总线。
  5. 局部总线:取代更高速数据传输的扩展总线。

其中数据总线、地址总线、控制总线也统称为系统总线。

3.4 进制

常用的进制有2进制,8进制,10进制,16进制。
2进制:00 11 11 100 101,逢2进1
8进制 0 1 2 3 4 5 6 7 10, 逢8进1
16进制 0 1 2 3 4 5 6 7 8 9 A B C D E F 10, 逢16进1

还有一些可以自己定义的进制,约定几个符合来表示对应的数据:
比如3进制 我们用a 3 1表示,通常约定好一些符合来表示对应的数据,以此来达到加密的效果。

运算等一些列就不说了~~~

3.5 数据的宽度

数学上的数字,是没有大小限制的,可以无限的大。但在计算机中,由于受硬件的制约,数据都是有长度限制的(我们称为数据宽度),超过最多宽度的数据会被丢弃。

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

在上面的代码中,我们定义了一个int类型的变量,但是赋值的是9位16进制数。但是这里打印cTemp的值发现是-1

(lldb) p cTemp
(int) $0 = -1
(lldb) p &cTemp
(int *) $1 = 0x000000016f8e926c
(lldb) x 0x000000016f8e926c
0x16f8e926c: ff ff ff ff b0 92 8e 6f 01 00 00 00 88 a5 51 00  .......o......Q.
0x16f8e927c: 01 00 00 00 10 00 00 00 00 00 00 00 02 00 00 00  ................
(lldb) p (uint)cTemp
$0 = 4294967295

通过上面的打印,发现cTemp所在的地址钟存放的是ff ff ff ff,而前面的1没有了,这就是发生了溢出。但是通过转换无符号数,发现是可以正常打印的。这就说明了,地址钟存放的数据是没有发生变化的,只是由于位数的限制导致的有符号和无符号的区别,导致的数据不一致。

在通常的二进制中,第一位表示正负,0表示正 1表示负。

// int 4个字节,表示32位。第一位为符号为。
 F    F    F    F    F    F    F    F
1111 1111 1111 1111 1111 1111 1111 1111

这里提一嘴我们经常用的宽带,都是100M的,200M的,但是这个的意思是100Mbps,说的也是宽度,除以8才是对应的数据传输速度。

4. CPU & 寄存器

CPU除了有控制器、运算器还有寄存器。其中寄存器的作用就是进行数据的临时存储。

CPU的运算速度是非常快的,为了性能CPU在内部开辟一小块临时存储区域,并在进行运算时先将数据从内存复制到这一小块临时存储区域中,运算时就在这一小快临时存储区域内进行。我们称这一小块临时存储区域为寄存器。

对于arm64系的CPU来说, 如果寄存器以x开头则表明的是一个64位的寄存器,如果以w开头则表明是一个32位的寄存器,在系统中没有提供16位和8位的寄存器供访问和使用。其中32位的寄存器是64位寄存器的低32位部分并不是独立存在的。

  • 对程序员来说,CPU中最主要部件是寄存器,可以通过改变寄存器的内容来实现对CPU的控制。
  • 不同的CPU,寄存器的个数、结构是不相同的。

4.1 浮点和向量寄存器

因为浮点数的存储以及其运算的特殊性,CPU中专门提供浮点数寄存器来处理浮点数

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

现在的CPU支持向量运算.(向量运算在图形处理相关的领域用得非常的多)为了支持向量计算系统了也提供了众多的向量寄存器.

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

4.2 通用寄存器

通用寄存器也称数据地址寄存器通常用来做数据计算的临时存储、做累加、计数、地址保存等功能。定义这些寄存器的作用主要是用于在CPU指令中保存操作数,在CPU中当做一些常规变量来使用。

ARM64拥有有32个64位的通用寄存器 x0 到 x30,以及XZR(零寄存器),这些通用寄存器有时也有特定用途。

  • 那么w0 到 w28 这些是32位的. 因为64位CPU可以兼容32位.所以可以只使用64位寄存器的低32位.
  • 比如 w0 就是 x0的低32位!

图片上标注了1、2、3、4分别有所对应。
1 - 点击进入汇编
2 - 进入汇编之后,选择『All Variables, register』,会显示如图所示
3 - 浮点和向量寄存器,存放对应的v0-v31,d0-d31,s0-s31
4 - 通用寄存器,存放x0-x28,fp,lr,sp,pc,w0-w28

mov x0, #0xa0           ; x0 = 0xa0
mov x1,#0x00            ; x1 = 0x00
add x1, x0, #0x14       ; x1 = x0 + 0x14
mov x0,x1               ; x0 = x1

4.3 pc寄存器(program counter)

  • 为指令指针寄存器,它指示了CPU当前要读取指令的地址
  • 在内存或者磁盘上,指令和数据没有任何区别,都是二进制信息
  • CPU在工作的时候把有的信息看做指令,有的信息看做数据,为同样的信息赋予了不同的意义
    • 比如 1110 0000 0000 0011 0000 1000 1010 1010
    • 可以当做数据 0xE003008AA
    • 也可以当做指令 mov x0, x8
  • CPU根据什么将内存中的信息看做指令?
    • CPU将pc指向的内存单元的内容看做指令
    • 如果内存中的某段内容曾被CPU执行过,那么它所在的内存单元必然被pc指向过

4.4 高速缓存

iPhoneX上搭载的ARM处理器A11它的1级缓存的容量是64KB,2级缓存的容量8M.

CPU每执行一条指令前都需要从内存中将指令读取到CPU内并执行。而寄存器的运行速度相比内存读写要快很多,为了性能,CPU还集成了一个高速缓存存储区域.当程序在运行时,先将要执行的指令代码以及数据复制到高速缓存中去(由操作系统完成).CPU直接从高速缓存依次读取指令来执行.

4.5 bl指令

CPU从何处执行指令是由pc中的内容决定的,我们可以通过改变pc的内容来控制CPU执行目标指令。

ARM64提供了一个mov指令(传送指令),可以用来修改大部分寄存器的值,比如
mov x0,#10、mov x1,#20。但是,mov指令不能用于设置pc的值,ARM64没有提供这样的功能

ARM64提供了另外的指令来修改PC的值,这些指令统称为转移指令,最简单的是bl指令

4.5.1 bl指令 – 练习

现在有两段代码!假设程序先执行A,请写出指令执行顺序.最终寄存器x0的值是多少?

.text
.global _A,_B

_A:
    mov x0,#0xa0
    mov x1,#0x00
    add x1, x0, #0x14
    mov x0,x1
    bl _B
    mov x0,#0x0
    ret
    
_B:
    add x0, x0, #0x10
    ret
  1. 打开Xcode,新建一个工程,在新建文件是选择 『Assembly File』,生成的是『.s』文件。然后将上面的代码复制粘贴。
  2. 在ViewController中,进行函数声明。这里需要注意的是,方法明需要一致。
//函数的声明~~
int A();
  1. viewDidLoad中直接调用A(),打断点,然后跳转到汇编。
_A:
    mov x0, #0xa0        ; x0 = a0
    mov x1,#0x00         ; x1 = 0
    add x1, x0, #0x14    ; x1 = x0 + 0x14
    mov x0,x1            ; x0 = x1
    bl _B                ; 跳转到方法 _B --> 执行B
    mov x0, #0x0         ; 从B回来之后执行 x0 = 0
    ret                  ; return, 继续点击会发现啥?

_B:
  add x0, x0, #0x10      ; x0 += 10
  ret                    ; return,方法结束回到A继续执行

这里会有一点问的哈~会导致循环了。为啥会导致循环了呢?翻下一章。

5. 总结

  1. 汇编基础知识,发展、用途、特点
  2. 几个知识点:bit,Byte,总线,数据宽度
  3. CPU 寄存器。浮点寄存器(64位: D0 - D31 32位: S0 - S31);向量寄存器(128位:V0-V31);通用寄存器(32位w0-w28,64位x0-x28),PC寄存器。
  4. bl指令

文章作者: 改变世界
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 改变世界 !
评论