iOS逆向 :初识汇编

2021-04-30  本文已影响0人  iOS鑫

在逆向开发中,非常重要的一个环节就是静态分析,众所周知,手机上安装App其本质是一个二进制文件,而静态分析是建立在分析二进制上面的。所以在学习逆向之前,需要提前了解相关的汇编知识


汇编语言的发展

机器语言

是由0和1组成的机器指令,表示特定的功能,如下所示

加:0100 0000减:0100 1000乘:1111 0111 1110 0000除:1111 0111 1111 0000

汇编语言(assembly language)

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

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

高级语言(high-level programming language)

在后期,为了更加高效的编程,在汇编语言的基础之上有了更高级的语言,例如C/C++/Java/OC/Swift等,这些语言更加接近人类的自然语言。例如c语言表示的加减乘除

加:A+B 通过编译器 0100 0000减:A-B 通过编译器 0100 1000乘:A*B 通过编译器 1111 0111 1110 0000除:A/B 通过编译器 1111 0111 1111 0000

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

作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发交流群:130 595 548,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!)

代码在终端的编译过程

有以下几点说明:

汇编语言的特点

汇编语言主要有以下几个特点:

汇编语言的用途

按理说,汇编这么难,为什么还要学呢,以及学了能做什么?

汇编学习的目的

任何高级语言最终都会被编译成汇编,学习了解汇编的相关知识,可以更好的日常开发、学习探索中帮助我们更好的排查问题、理解底层运行的机制。大致分为有以下几点:

汇编使用的场景

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

汇编语言的种类

目前讨论较多的婚变语言有以下几种

在iPhone中使用的是ARM汇编,但是不同的设备之间也会因为CPU架构的不同而有所差异,下面是iPhone中不同架构所对应的设备

架构 设备
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以后

必备常识

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

App的执行过程

总线

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

A11

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

总线:是一根根导线的集合

总线的分类
总线主要分为三类,如下图所示:

总线分类

举例:CPU从内存的3号单元读取数据

总线举例

地址总线

注意区分数量单位容量单位

  • 数量单位: 1M=1024k,1k=1024
  • 容量单位: 字节Byte(B) 1024B=1KB,1024KB=1MB,IBM银行的独立系统是以2字节为一个单位,常用的电脑是以1字节为一个单位
  • 网络带宽100M=100Mbpt(比特位,每秒传输12.5MB/s)

数据总线

我们常说的32位(4字节)、64位(8字节)CPU,这里的32、64指的就是数据吞吐量

控制总线

内存

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

内存图示

进制

每一种进制都是完美的,而我们通常总是以十进制为依托去考虑其他进制,总是先转换为十进制,在转换为其他进制,其实这种方式是错误的。所以想要学好进制,首先需要忘掉十进制,其次是忘掉进制间的转换

进制的定义

<!--练习-->- 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 1720 21 22 23 24 25 26 27...1+1 = 2                     1+2 = 3   2+2 = 4               1+3 = 4   2+3 = 5   3+3 = 61+4 = 5   2+4 = 6   3+4 = 7   4+4 = 10  1+5 = 6   2+5 = 7   3+5 = 10  4+5 = 11  5+5 = 121+6 = 7   2+6 = 10  3+6 = 11  4+6 = 12  5+6 = 13  6+6 =141+7 = 10  2+7 = 11  3+7 = 12  4+7 = 13  5+7 = 14  6+7 =15  7+7 = 16

八进制乘法表

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 = 201*5 = 5   2*5 = 12  3*5 = 17  4*5 = 24  5*5 
= 311*6 = 6   2*6 = 14  3*6 = 22  4*6 = 30  5*6 = 36  6*6 = 441*7 = 7   2*7 = 16  3*7 = 25  4*7 = 34  5*7 = 43  6*7 = 52  7*7 = 61

做个练习

277+333计算过程

实战四则运算

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

二进制的简写形式

       二进制: 1 0 1 1 1 0 1 1 1 1 0 0三个二进制一组: 101 110 111 100       八进制:   5   6   7   4四个二进制一组: 1011 1011 1100     十六进制:    b    b    c

使用二进制从0写到1111:0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
发现这样使用二进制太麻烦,所以将其改为更简单一点的符号
0 1 2 3 4 5 6 7 8 9 A B C D E F 这就是十六进制了

数据的宽度

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

#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]));    }}

断点调试结果如下,可以发现1溢出了

断点调试-01

也可以通过获取的地址,然后在Debug-Debug Workflow-ViewMemory中输入地址查看

断点调试-02

计算机中常见的数据宽度

那么计算机存储数据它会分为有符号数无符号数,如下图所示

负数+正数图示

自定义进制符号

练习1
现在有10进制数 10个符号分别是:2,9,1,7,6,5,4, 8,3 , A 逢10进1 那么:123 + 234 = ____(AA6)

十进制:    0  1  2  3  4  5  6  7  8  9自定义:    2  9  1  7  6  5  4  8  3  A(十进制10)92 99 91 97 96 95 94 98 93 9A         12 19 11 17 16 15 14 18 13 1A         72 79 71 77 76 75 74 78 73 7A         62 69 61 67 66 65 64 68 63 6A         52 59 51 57 56 55 54 58 53 5A         42 49 41 47 46 45 44 48 43 4A         82 89 81 87 86 85 84 88 83 8A         32 39 31 37 36 35 34 38 33 3A         922         (00)2  (01)9  (02)1  (03)7  (04)6  (05)5  (06)4  (07)8  (08)3  (09)A(10)92 (11)99 (12)91 (13)97 (14)96 (15)95 (16)94 (17)98 (18)93 (19)9A(20)12 (21)19 (22)11 (23)17 (24)16 (25)15 (26)14 (27)18 (28)13 (29)1A(30)72 (31)79 (32)71 (33)77 (34)76 (35)75 (36)74 (37)78 (38)73 (39)7A(40)62 (41)69 (42)61 (43)67 (44)66 (45)65 (46)64 (47)68 (48)63 (49)6A(50)52 59 51 57 56 55 54 58 53 5A(60)42 49 41 47 46 45 44 48 43 4A(70)82 89 81 87 86 85 84 88 83 8A(80)32 39 31 37 36 35 34 38 33 3A(90)922

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

练习2
现在有9进制数 9个符号分别是:2,9,1,7,6,5,4, 8,3 逢9进1 那么:123 + 234 = ____(9926)

十进制:    0  1  2  3  4  5  6  7  8  自定义:    2  9  1  7  6  5  4  8  3           92 99 91 97 96 95 94 98 93          12 19 11 17 16 15 14 18 13          72 79 71 77 76 75 74 78 73          62 69 61 67 66 65 64 68 63          52 59 51 57 56 55 54 58 53          42 49 41 47 46 45 44 48 43          82 89 81 87 86 85 84 88 83          32 39 31 37 36 35 34 38 33          922         (00)2  (01)9  (02)1  (03)7  (04)6  (05)5  (06)4  (07)8  (08)3  (10)92 (11)99 (12)91 (13)97 (14)96 (15)95 (16)94 (17)98 (18)93 (20)12 19 11 17 16 15 14 18 13 (30)72 79 71 77 76 75 74 78 73 (40)62 69 61 67 66 65 64 68 63 (50)52 59 51 57 56 55 54 58 53 (60)42 49 41 47 46 45 44 48 43 (70)82 89 81 87 86 85 84 88 83 (80)32 39 31 37 36 35 34 38 33 (90)922

CPU & 寄存器

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

cpu图示

针对arm64的CPU来说,

在系统中没有提供16位和32位的寄存器供访问和使用,其中32位的寄存器是64位寄存器的低32位部分,并不是独立存在的

浮点和向量寄存器

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

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

通用寄存器

注意:了解过8086汇编的一定知道,还有一种特殊的寄存器段寄存器:CS、DS、SS、ES。这四个寄存器主要用来保存这些段的基地址,是属于Intel架构的CPU的,在arm中并没有

内存案例

PC寄存器(program counter)

寄存器案例分析

下面通过以下代码的例子来分析

#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]));    }}

pc寄存器调试

除了读还可以写

高速缓存

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

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

bl指令

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:0x00x0的值:0x00
汇编代码验证

下面来写一段汇编代码验证(com+n -- empty -- asm.s(汇编代码文件,会编译成源码))

汇编代码验证-汇编代码

如何执行呢?

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

总结

上一篇 下一篇

猜你喜欢

热点阅读