汇编(一)

2021-03-27  本文已影响0人  浅墨入画

前言:

逆向开发中,非常重要的一个环节就是静态分析。我们知道,手机上安装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等

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

我们的代码在终端设备上是这样的过程:

image.png

汇编语言与其他语言的对应关系

汇编语言的特点

汇编语言的用途

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

汇编语言的种类

目前讨论比较多的汇编语言有

image.png

汇编常识

image.png
  1. 可执行文件:程序/App在本地磁盘中即为可执行文件
  2. image(镜像文件):可执行文件装载到内存即为镜像文件,因为与可执行文件是一模一样,相当于copy过来
  3. 内存中除了指令,还有数据,但是都是0和1组合,CPU是通过部件PC寄存器来区分

总线

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

image.png image.png

每一个CPU芯片都有许多管脚,这些管脚和总线相连,CPU通过总线跟外部器件进行交互
总线:一根根导线的集合
总线的分类,如下图所示

image.png image.png

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

image.png

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

内存

CPU是通过总线和硬件设备连接的
内存有RAM主存储器、RAM(主存储器)内存条

image.png

内存按照物理地址划分为主存储器、显存地址、显卡地址、网卡地址、系统地址

截屏2021-03-26 下午11.03.52.png

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

image.png

进制

进制的定义

做个练习
1 + 1 在____情况下等于 3 ?
十进制由10个符号组成: 0 1 3 2 8 A B E S 7 逢十进一
如果这样定义十进制: 1 + 1 = 3
这样的目的何在?
传统我们定义的十进制和自定义的十进制不一样.那么这10个符号如果我们不告诉别人这个符号表,别人是没办法拿到我们的具体数据的!用于加密!

进制的运算
八进制加法表

 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 = 2                     
1+2 = 3   2+2 = 4               
1+3 = 4   2+3 = 5   3+3 = 6
1+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 = 12
1+6 = 7   2+6 = 10  3+6 = 11  4+6 = 12  5+6 = 13  6+6 = 14
1+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 = 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

数据的宽度

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

以下例子都是真机调试

// 创建demo空工程
#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已经溢出

image.png

也可以通过获取地址,在Debug-Debug Workflow-ViewMemory中输入地址查看,下图Address 输入地址,Page 也可以翻页

image.png

计算机中常见的数据宽度

计算机存储数据它会分为有符号数和无符号数,看下图

image.png
无符号数: 直接换算!
有符号数: 符号放在第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

CPU&寄存器

内部部件之间由总线连接

image.png

浮点和向量寄存器

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

通用寄存器

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

image.png

pc寄存器(program counter)

寄存器查看

image.png

通用寄存器中fp对应x29,lr对应x30,fp与lr是对寄存器起了别名,如下图

image.png

Xcode查看汇编 Debug-Debug Workflow-Always Show Disassembly

image.png image.png

pc寄存器调试

image.png image.png 截屏2021-03-27 上午11.16.06.png 截屏2021-03-27 上午11.16.26.png

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

高速缓存

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

bl指令

_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

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

// asm.s文件内容
.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

// ViewController.m 中调用
@implementation ViewController

// 汇编A函数声明
int A();

- (void)viewDidLoad {
    [super viewDidLoad];
    A();
    // Do any additional setup after loading the view.
}
@end

在A()执行处加断点,并执行程序,开启汇编调试
开始调用A函数

image.png

按住control+Step into,继续执行,跳入汇编A函数内

image.png

一步步执行,当执行到0x102925efc <+16>: bl 0x102925f08 ; B,按住control+Step into进入汇编代码B

image.png

继续一步步执行回到汇编代码A

image.png

此时我们发现继续执行的时候,一直在汇编代码A的最后两行执行,发生了死循环,退不回ViewController中

0x102925f00 <+20>: mov    x0, #0x0
->  0x102925f04 <+24>: ret    

分析原因: 汇编代码A中没有保存回去的路,原因我们下一篇讨论

上一篇 下一篇

猜你喜欢

热点阅读