最近在学习逆向这一块儿,学习后按照自己的理解整理一下,以便加深理解。有不对的地方 后面慢慢改正。
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: 需要真机才能编译
-
断点查看执行汇编
Xcode -> Debug -> Debug workflow -> Always show
LLDB :
输入:ni 单步执行
输入:s 跳入方法
- 查看地址中的数据 command + shift +m
- 在xcode中查看寄存器 需要真机