汇编学习(一)

最近在学习逆向这一块儿,学习后按照自己的理解整理一下,以便加深理解。有不对的地方 后面慢慢改正。

1 汇编语言介绍

  • 直接对硬件进行操作,最大限度发挥其功能
  • 目标代码简短,占用内存小,执行速度快
  • 不同CPU机器的汇编指令不同
  • 汇编和机器语言一一对应,每一条机器指令都有与之对应的汇编指令;
  • 汇编语音可以通过编译得到机器语言,机器语言可以通过反编译得到汇编语言。
  • 高级语言可以通过编译得到汇编语言,但是汇编、机器语言几乎不可能还原成高级语言
汇编语言种类有很多:
    8086汇编 16bit
    Win32汇编
    Win64汇编
    ARM汇编
    苹果5S以及以上的手机都是ARM64位
    5和5C是armv7s 32位的

2 CUP和内存

操作系统将我们的可执行文件全部装载到内存中,接着CPU从入口函数开始,在内存中读取一条指令,执行一条指令,然后依次下去...

学好汇编必须了解CPU和内存,因为大部分指令都是与CPU和内存相关的。

总线:一根根导线的集合

CUP要想从内存中某一个地址中读取或者写入一个数据。需要地址总线提供需要操作的地址,数据总线负责提供数据、控制总线控制着是读取还是写入。

  • 地址总线
    它的宽度决定CPU的寻址能力,假如有N根地址总线,那么CPU的寻址能力是2^N
例如:有20根选址能力就是1M = 2^10 kb = 2^20 byte  
  • 数据总线

它的宽度觉定了CPU单次传输数据量,也就是传输速度。一根数据总线传输一个bit ,因为一根线可以代表0或者1

二进制的一位数据 叫一个bit。8个bit是一个byte(字节),
16个bit是一个word(字),32个bit是一个 double word(双字)。。

1Byte  = 8bit
1KB = 1024Byte = 2^10 Byte
1MB = 1024KB = 2^10Kb = 2^20Byte
1GB = 1024MB = 2^10Mb = 2^20Kb = 2^30Byte
1TB = 1024GB = 2^10GB = 2^20Mb = 2^30Kb = 2^40Byte
...

  • 控制总线

它的宽度觉定了CUP对其他控件有多少种控制能力

内存:能使用的内存空间大小受CPU地址总线个数影响

32位的 最多能时候用4GB的内存大小;

寄存器:CPU最主要的部件

通过改变寄存器的内存对CPU进行控制,不同CPU寄存器个数、结构不同;

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

1、 通用寄存器

ARM64有31个64位通用寄存器x0-x30,存放一般数据,也有特定用途的。

w0 - w28 是32位的寄存器,因此64位CPU兼容32位

64位:x0-x30 ,XZR(零寄存器)
32位:w0-w30 ,WZR(零寄存器)

通常,CPU会先将内存中的数据存储到通用寄存器中,然后再对通用寄存器中的数据进行运算

2、PC寄存器
  • 指令寄存器,它指示了CPU当前要读取指令的地址
  • 在内存或磁盘中 指令和数据没有区分都是二进制数据,CPU就是通过pc指向的内存单元的内容看成是指令,如果内存中的某段内容曾被CPU执行过,那么它所在的内存单元必然被pc指向过。
3、浮点寄存器和向量寄存器

浮点寄存器64位:D0 - D31 32位:S0 - S31
向量寄存器 128位: V0 - V31

3 进制

进制的定义:

  • 八进制是由八个符号组成:0 1 2 3 4 5 6 7 逢8进1
0  1  2  3  4  5  6  7 
10 11 12 13 14 15 16 17
20 21 22 23 24 25 26 27
30 31 32 33 34 35 36 37
40 41 42 43 44 45 46 47
50 51 52 53 54 55 56 57
60 61 62 63 64 65 66 67
...
八进制相加的话就是 在一个数的基础上向后查询多少位

例如 4+6 = 12 可以理解成:4向后查询6位或者是6向后查询4位 得到结果是12

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

同理减法 是在当前数向前查询

M*N 
乘法可以理解成 在一个数M的基础上向后查(N-1)次M位获取到当前数据:
比如 5*6 = 36 可以理解成 5 向后再查5次5位数据 或者从6开始向后查询4次6位数据。

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

除法就可以成从当前数据M向前查询长度N能查询到N 所需要的个数+1 就是要得到的结果;
25/7 就是每次向前查7位 查到7 需要查两次 然后 2+1 = 3 就是结果
例如:

  • 十进制是由十个符号组成:0 1 2 3 4 5 6 7 8 9 逢10进1
  • 16进制是由16个符号组成:0 1 2 3 4 5 6 7 8 9 A B C D E F 逢 F进一
  • 二进制是有两个符号组成:0 1 逢2进1
二进制数有事太长嫌麻烦就简写下:
       二进制: 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
  • 同理N进制就是N个符号组成:逢N进1
    自定义七进制:
    那你呢
    十进制:0 1 2 3 4 5 6  
    
    七进制:6 5 2 8 9 1 4 
    
    6  5  2  8  9  1  4
    56 55 52 58 59 51 54
    26 25 22 28 28 21 24
    86 85 82 88 89 81 84
    96 95 92 98 99 91 94
    16 15 12 18 19 11 14
    46 45 42 48 49 41 44
    
    266 265 262 268 269 261 264
    256 255 252 258 259 251 254
    ... 
    
    
    2 + 4 = 55  【我的理解】: 4对应的十进制是6 2对应的十进制是2 2+4 就是从4向后查询2位 就是55
    
    89 - 21 = ? 6     
       

数据宽度

计算机受硬件的制约,数据的长度(数据宽度)是有限制的,超过最多的宽度数据会被丢弃;

int 4个字节 32个bit位 可以写成8个16进制 0xFFFFFFFF
但是要是写成0x1FFFFFFFF 1读不出来 就被丢弃了

4 常用的汇编指令

算术指令

add:加指令
sub:减指令
mul:乘法指令
sdiv:有符号指令

逻辑运算指令
and:按位与
xor:异或运算
or:按位或
not:非运算
tst:按位与(不保存结果,只保存结果是否为0)
shl:<< 左移       【各二进制位全部向左移若干位,高位丢弃,低位补0】
shr:>> 无符号右移【各二进制位全部向右移若干位,低位丢弃,高位补0】
sar:>> 有符号右移【各二进制位全部向右移若干位,低位丢弃,高位补1】
数据传输指令
str:把源寄存器的值存入内存或者目标寄存器
ldr:把内存或者源寄存器的内存存入目标寄存器
stp:和str一样的功能,只是可以操作两个寄存器
ldp:和ldr一样的功能,只是可以操作两个寄存器
mov:把立即数(比如:#10)或者源寄存器的值赋值给目标寄存器
例如:mov x0 #0xa1  把16进制数0xa1赋值给寄存器x0
跳转指令

bl:执行函数调用,并把是lr指向调用者的下一条指令

5 汇编寻址方式

1、立即数寻址

操作数在指令中,

如:add x0,x0,#10  => x0 = x0+10 x0中的内容+10 在赋值给x0

2、寄存器寻址

利用寄存器中的值作为操作数,

如:add x0,x1,x2 => x0 = x1 + x2

3、寄存器间接寻址

寄存器中的值作为操作数的地址,操作数本身放在存储器中,

如:mov x0,[x1] ==> x0 = [x1],取出x1中存的地址中的值,赋值给x0

4、基址变址寻址

基址寄存器的的内容与指令中的偏移量相加,得到有效的操作数的地址,然后访问该地址空间

  • 前索引
如:mov x0,[x1,#4]  ==> 取x1+4地址里面的值赋值给x0
  • 自动索引
如:mov x0,[x1,#4]!  ==> 取x1+4地址里面的值赋值给x0,并把x1+4的地址回写进x1中
注:!表示回写地址
  • 后索引
如:mov x0,[x1],#4  ==> x1地址里面的值赋值给x0,x1+4地址存进x1

6 在xcode中查看汇编代码

  • xcode 可以在lldb 写输入 dis -n "方法名" 来输出查看对应方法的汇编

  • 通过xcode的Product->Perform Action -> Assemble ”xxxx“ 来生成汇编代码。里面很多开头的语句是注释

PS1: 需要真机才能编译

汇编学习(一)_第1张图片
812EA7D3301111FF40668588F3C74601
  • 断点查看执行汇编
    Xcode -> Debug -> Debug workflow -> Always show


    汇编学习(一)_第2张图片
    image
LLDB : 
    输入:ni 单步执行
    输入:s 跳入方法
  • 查看地址中的数据 command + shift +m
汇编学习(一)_第3张图片
  • 在xcode中查看寄存器 需要真机
汇编学习(一)_第4张图片

你可能感兴趣的:(汇编学习(一))