机器语言

由于使用机器语言表示时不方便记忆,于是开始使用助记符来代替机器语言,,例如下面使用助记符表示的加减乘除

 

加:INC EAX 通过编译器 0100 0000
减:DEC EAX 通过编译器 0100 1000
乘:MUL EAX 通过编译器 1111 0111 1110 0000
除:DIV EAX 通过编译器 1111 0111 1111 0000

最终的代码在终端设备上显示的过程是这样的


有以下几点说明:

    • 汇编语言主要有以下几个特点:
      • 按理说,汇编这么难,为什么还要学呢,以及学了能做什么?

 

  • 1、是理解整个计算机系统的最佳起点和最有效途径

  • 2、为编写高效代码打下基础

  • 3、理解代码的本质,例如:

 

  • 函数的本质是什么?

  • ++a底层是如何执行的?

  • 编译器在底层到底帮我们做了哪些工作?

  • DEBUG模式和RELEASE模式到底有哪些地方是不同的,以及被我们忽略的?

所以综上所述,汇编是所有程序猿都需要了解的一门非常重要的语言,这也是为什么大学中计算机相关专业学生的必修课,就好比修房子,地基稳了,高楼才能平地起!

  • 1、8086汇编(8086处理器是16bit的CPU)

  • 2、Win32汇编

  • 3、Win64汇编

  • 4、ARM汇编(嵌入式、Mac、iOS)

  • ...

想要学好汇编,需要有以下几个认知

    • 总线是CPU与内存之间的桥梁,如下图所示是iPhone X上的A11(CPU芯片)


从图中可以看出:每一个CPU芯片都有很多管脚,这些管脚和总线相连,CPU通过总线跟外部器件进行交互

 

 

        • 它的宽度决定了CPU的寻址能力,即地址总线决定了CPU所能访问的最大内存空间的大小,例如10根地址线能访问的最大内存是 2^10 = 1024位二进制数据(即1B)

        • 地址总线是地址线数量之和

        • 8086地址总线宽度是20,所以寻址能力是1M(即2^20)

        • 内存地址的单元是 字节byte(简写为B),每个字节里面可以放8位(即bit),以下是内存条的图示

           

          iOS逆向 01:初识汇编_iOS开发

          内存条图示

  • 它的宽度决定了CPU的单次数据传送量(即吞吐量),也就是数据传送速度即CPU和外界的数据传送速度

  • 每条数据线一次只能传输一位二进制数据,例如 8根数据线一次可传送一个8位二进制数据(即1个字节的数据)

  • 数据总线是数据线数量之和

  • 8086的数据总线宽度是16,所以单次最大传递2个字节的数据

  • 它的宽度决定了CPU对其他器件的控制能力,能有多少种控制,即CPU对外部器件的控制能力

  • 控制总线是控制线数量之和

其中内存中的低地址是给用户用的,高地址是给系统用的,如下所示

iOS逆向 01:初识汇编_软件开发_02

内存图示

进制

 

  • 八进制由8个符号组成:0 1 2 3 4 5 6 7 逢八进一

  • 十进制由10个符号组成:0 1 2 3 4 5 6 7 8 9 逢十进一

  • N进制就是由N个符号组成:逢N进一

 

 

<!--练习-->
- 1+1在___情况下等于3

<!--答案-->
十进制由10个符号组成: 0 1 3 2 8 A B E S 7 逢十进一
如果这样定义十进制:1+1等于3

<!--目的-->
传统定义的十进制和自定义的十进制不一样,如果不告诉别人符号表,别人是无法拿到具体的数据的。这样的应用场景主要是用于加密

八进制乘法表

 

0 1 2 3 4 5 6 7 10 11 12 13 14 15 16 17 20 21 22 23 24 25 26 27...
1*1 = 1
1*2 = 2 2*2 = 4
1*3 = 3 2*3 = 6 3*3 = 11
1*4 = 4 2*4 = 10 3*4 = 14 4*4 = 20
1*5 = 5 2*5 = 12 3*5 = 17 4*5 = 24 5*5 = 31
1*6 = 6 2*6 = 14 3*6 = 22 4*6 = 30 5*6 = 36 6*6 = 44
1*7 = 7 2*7 = 16 3*7 = 25 4*7 = 34 5*7 = 43 6*7 = 52 7*7 = 61

实战四则运算

 

   277         236         276         234
+ 333 - 54 * 54 / 4
-------- -------- -------- --------
632 162 1370 47
+ 1666

数据的宽度

 

iOS逆向 01:初识汇编_移动开发_03

断点调试-02

 

  • 位(Bit):1个位就是1个二进制位,即0或1

  • 字节(Byte):1个字节由8个Bit组成,内存中的最小单元Byte

  • 字(Word):1个字由两个字节组成(16位),第2个字节分别称为高字节和低字节

  • 双字(DoubleWord):1个双字由两个字组成(32位)

 

 

  • 无符号数,直接换算

  • 有符号数,符号放在第1位,第1位是0即正数,为1即负数:

 

  • 正数:0 1 2 3 4 5 6 7

  • 负数:F E D B C A 9 8
    表示:-1 -2 -3 -4 -5 -6 -7 -8

刚才通过10进制运算可以转换,然后查表,但是如果是其他进制,就不能转换,要学会直接查表

内部部件之间是由总线连接,如下图所示


针对arm64的CPU来说,

  • 对于程序员来说,CPU中最主要的部件是寄存器,可以通过改变寄存器的内容来实现对CPU的控制

  • 不同CPU,寄存器的个数和结构是不相同的

  • 浮点寄存器

  • 64位:D0 - D31

  • 32位:S0 - S31

通用寄存器

  • 通常,CPU会先将内存中的数据存储到通用寄存器中,然后再对寄存器中的数据进行运算
    假设内存中有块红色内存空间的值是3,现在想把它的值加1,并将结果存储到蓝色内存空间

    iOS逆向 01:初识汇编_iOS_04

    内存案例

    • CPU首先会将红色内存空间的值放到X0寄存器中:mov X0,红色内存空间

    • 然后让X0寄存器与1相加:add X0,1

    • 最后将值赋值给内存空间:mov 蓝色内存空间,X0

寄存器案例分析

pc寄存器调试

  • register write pc 0x104c31ecc

  • register read pc 此时是读不出来的,因为断点断住了,如果step into,此时断点断在哪里?最终通过验证发现,会断在cc的下一行

     

    iOS逆向 01:初识汇编_软件开发_05

    pc寄存器调试-04

     

    iOS逆向 01:初识汇编_移动开发_06

    pc寄存器调试-05

     

    此时pc指向cc,会将cc中的指令拿出来执行,执行完毕后走到cc的下一条指令,而此时d0的指令是还没有执行的

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

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

 

_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

<!--结果-->
流程:
mov x0,#0xa0 -- x0:0xa0
mov x1,#0x00 -- x1:0x00
add x1, x0, #0x14 -- x1:0xa0+0x14=0xb4
mov x0,x1 -- x0:0xb4
add x0, x0, #0x10 -- x0:0xb4+0x10=0xc4
ret -- 回到bl跳转的下一行
mov x0,#0x0 -- x0:0x00

x0的值:0x00

iOS逆向 01:初识汇编_iOS_07

汇编代码验证-汇编代码

然后开始lldb调试,下面是一步步到0x00的过程

  • 1、汇编概述

    • 使用助记符代替机器指令的一种编程语言

    • 汇编和机器指令是一一对应的关系,拿到二进制就可以反汇编

    • 由于汇编和CPU的指令集是对应的,所以汇编不具备移植性

  • 2、总线:是由一堆导线的集合

    • 地址总线:其宽度决定了寻址能力

    • 数据总线:其宽度决定了CPU数据的吞吐量

    • 控制总线:其宽度决定了CPU对其他器件的控制能力

  • 3、进制

    • 计算机中的数据是有宽度的,超过了就会溢出

    • 1024 = 1KB,1024KB = 1MB,1024MB = 1GB

    • 1024 = 1k,1024k = 1M,1024M = 1G

    • 3个二进制 使用一个8进制标识

    • 4个二进制 使用一个16进制标识

    • 两个16进制可以标识一个字节,即8位

    • 任意进制都是由对应个数的符号组成的,符号可以自定义

    • 2/8/16是相对完美的进制,他们之间的关系

    • 数量单位

    • 容量单位

    • 数据的宽度

  • 4、寄存器:CPU为了性能,自内部开辟了一小块临时存储区域

    • PC寄存器里面的值保存的就是CPU接下来需要执行的指令地址

    • 改变PC的值可以改变程序的执行流程

    • mov指令不能更改PC寄存器的值,需要通过bl跳转指令来改变PC寄存器的值

    • ARM64拥有32个64位的通用寄存器X0-X30以及XZR(零寄存器)

    • 为了兼容32位,所以arm64位拥有W0-W28以及WZR 30个32位寄存器

    • 32位寄存器并不是独立存在的,例如 W0是X0的低32位

    • 浮点向量寄存器:用于浮点数/向量的存储及运算

    • 异常状态寄存器

    • 通用寄存器:除了存放数据有时也有特殊的用途

    • PC寄存器:指令指针寄存器