逆向工程简介
iOS 逆向工程,是指从目标应用的界面以及功能表现入手,使用不同工具和理论知识去分析其实现原理,得出应用的代码结构、整体设计、功能实现、执行流程等,然后利用iOS的系统知识和语言特性,借鉴或修改原有实现流程的技术。
前置基础知识
学习逆向工程我们需要先了解设备知识,下面我们先介绍一下设备相关的基础知识。
ARM
学习ARM首先要了解下CPU是什么
分不清ARM和X86架构,别跟我说你懂CPU
Central Processing Unit(CPU)的组成部分
- 运算器(ALU- arithmetic logic unit 算术逻辑单元,马达)
- 控制器(Control 负责分配指定运算)
- 寄存器(Register ARM汇编就是操作寄存器和内存的指令集)
- cpu内部总线(物理)地址线路,数据线路 控制线路(内存读写命令) 地址总线 限制CPU内存地址空间的大小,地址总线宽度为32,2的32次方=4GB.个内存单元,一个内存单元可以存放8位数据也就是一个字的数据。cpu访问内存单元时,要给出内存单元的地址,所有的内存单元构成的存储空间是一个一维的线性空间。每一个内存丹云在这个空间中都有唯一的地址,我们将这个唯一的地址称为物理地址。 N位结构。比如64位(位机,字长与结构含义相同),描述了一个cpu具有下面几方面的结构特性
- 运算器一次最多可以处理64位的数据 8个byte
- 寄存器的最大宽度为64位
- 寄存器和运算器之间的通路为64位。
- 对于65位cpu,能一次性处理,传输,暂时存储64位的地址
总结:从字面意思看运算器就是起着运算的作用
控制器就是负责发出CPU每条指令所需要的信息
寄存器就是保存运算或者指令的一些临时文件,这样可以保证更高的速度。
CPU有着处理指令、执行操作、控制时间、处理数据四大作用,打个比喻来说,CPU就像我们的大脑,帮我们完成各种各样的生理活动。因此如果没有CPU,那么电脑就是一堆废物,无法工作。移动设备其实很复杂,这些CPU需要执行数以百万计的指示,才能使它向我们期待的方向运行,而CPU的速度和功率效率是至关重要的。速度影响用户体验,而效率影响电池寿命。最完美的移动设备是高性能和低功耗相结合。
cup的模式切换,模式和模式间相对独立。说白了就是切换状态 cpu要读取一个内存单元的时候,必须先给出这个内存单元的地址。
段的概念
错误认识:
内存被划分成了一个一个的段,每一个段有一个段地址。
正确认识:
内存并没有分段,段的划分来自于CPU,由于CUP用 段地址*16 + 偏移地址 = 物理地址 的方式给出内存单元的物理地址,使得我们可以用分段的方式来管理内存。
以后,在编程时可以根据需要,将若干地址连续的内存单元看作一个段。
偏移地址为16位,16位地址的寻址能力为64k,所以一个段的长度最大64KB。
为什么需要寄存器
cup切换状态时,比如funA -> funB,实际上操作的是内存地址,把这些操作内存地址的指令或者内存地址直接放在cpu里,可以更好的控制和提高效率,cpu就需要寄存器来存放这些指令。
寄存器常用术语
- SP(stack point)栈指针,存储栈地址,指向栈顶
- LR(link register)链接寄存器
- PC(program count)程序技术器
- A\C PSR(program status register)程序状态寄存器
- SPSR(saved program status register)已保存程序状态寄存器
- user模式没有SPSR
指令集
要了解X86和ARM,就得先了解复杂指令集(CISC)和精简指令集(RISC)
- Complex Instruction Set Computing - CISC
- reduced instruction set computing
从CPU发明到现在,有非常多种架构,从我们熟悉的X86,ARM,到不太熟悉的MIPS,IA64,它们之间的差距都非常大。但是如果从最基本的逻辑角度来分类的话,它们可以被分为两大类,即所谓的“复杂指令集”与“精简指令集”系统,也就是经常看到的“CISC”与“RISC”。 Intel和ARM处理器的第一个区别是,前者使用复杂指令集(CISC),而后者使用精简指令集(RISC)。属于这两种类中的各种架构之间最大的区别,在于它们的设计者考虑问题方式的不同。
我们可以继续举个例子,比如说我们要命令一个人吃饭,那么我们应该怎么命令呢?我们可以直接对他下达“吃饭”的命令,也可以命令他“先拿勺子,然后舀起一勺饭,然后张嘴,然后送到嘴里,最后咽下去”。
从这里可以看到,对于命令别人做事这样一件事情,不同的人有不同的理解,有人认为,如果我首先给接受命令的人以足够的训练,让他掌握各种复杂技能(即在硬件中实现对应的复杂功能),那么以后就可以用非常简单的命令让他去做很复杂的事情——比如只要说一句“吃饭”,他就会吃饭。
但是也有人认为这样会让事情变的太复杂,毕竟接受命令的人要做的事情很复杂,如果你这时候想让他吃菜怎么办?难道继续训练他吃菜的方法?我们为什么不可以把事情分为许多非常基本的步骤,这样只需要接受命令的人懂得很少的基本技能,就可以完成同样的工作,无非是下达命令的人稍微累一点——比如现在我要他吃菜,只需要把刚刚吃饭命令里的“舀起一勺饭”改成“舀起一勺菜”,问题就解决了,多么简单。
这就是“复杂指令集”和“精简指令集”的逻辑区别。
x86多用于电脑,ARM多用于手机。
参考文章
ARM汇编以及汇编语言基础介绍
汇编基础
模拟器32位处理器是i386架构,
模拟器64位处理器是x86_64架构,
真机32位处理器是armv7,或者armv7s架构,(armv7s 是iPhone5C、armv7是iphone4之前的手机版本)
真机64位处理器是arm64架构。(iPhone5s之后的手机)【接下来主要学习的】
ARM64下的寄存器
功能:
- 进行数据的临时存储
- 数据执行算术及逻辑运算
- 操作内存(寻址)
寄存器分类:
通用寄存器(用来存放一般性的数据)
- x0~x30(64位)
- x29 又名fp(用于保存栈底的地址)
- x30 又名 lr(bl 跳转后就会把下一条指令地址写到lr中)
- w0w30(32位)这些就是x0x30的低32位
浮点寄存器(CPU中专门提供浮点数寄存器来处理浮点数)
- D0~D31(64位)
- S0
S31 (32位)这些就是D0D31的低32位
向量寄存器
(现在的CPU支持向量运算.(向量运算在图形处理相关的领域用得非常的多)为了支持向量计算系统了也提供了众多的向量寄存器.)- V0-V31(128位)
状态寄存器(又称 CPSR【current program status register】寄存器)
- CPSR寄存器是32位的,每一位的功能如下
31 30 29 28 27~8 7 6 5 4 3 2 1 0
N Z C V 保留 I F T M4 M3 M2 M1 M0 - CPSR的低8位(包括I、F、T和M[4:0])称为控制位,程序无法修改,除非CPU运行于特权模式下,程序才能修改控制位!
- N【负数标志】、Z【0标志】、C【进位标志】、V【溢出标志】均为条件码标志位。
- CPSR寄存器是32位的,每一位的功能如下
栈寄存器
- SP (任意时刻会保存我们栈顶的地址)
- FP (用于保存栈底的地址)
ARM64下常用的汇编指令(重要)
基础指令
- MOV - MOV X1,X0 ; 将寄存器X0的值传送到寄存器X1
- ADD - ADD X0,X1,X2 ; 寄存器X1和X2的值相加后传送到X0
- SUB - SUB X0,X1,X2 ; 寄存器X1和X2的值相减后传送到X0
- AND - AND X0,X0,#0xF ; X0的值与0xF相位与后的值传送到X0
- ORR - ORR X0,X0,#9 ; X0的值与9相或后的值传送到X0
- EOR - EOR X0,X0,#0xF ; X0的值与0xF相异或后的值传送到X0
堆栈操作
- STR - 将数据从寄存器中读出来,存到内存中.
STR - STR X0, [SP, #0x8] ;X0寄存器的数据传送到SP+0x8地址值指向的存储空间 - STP - STR 的变种指令,可以同时操作两个寄存器
STP x29, x30, [sp, #0x10] ; 将x29,x30存入栈中 - LDR - 将数据从内存中读出来,存到寄存器中
LDR X5,[X6,#0x08] ;X6寄存器加0x08的和的地址值内的数据传送到X5 - LDP - LDR 的变种指令,可以同时操作两个寄存器
LDP x29, x30, [sp, #0x10] ; 将栈中的值取出存放到x29, x30
跳转操作
- BL 将下一条指令的地址放入lr(x30)寄存器
- RET 默认使用lr(x30)寄存器的值,通过底层指令提示CPU此处作为下条指令地址!
- CMP 比较指令,相当于SUBS,影响程序状态寄存器
- B.GT 比较结果是大于,执行标号,否则不跳转
- B.GE 比较结果是大于等于,执行标号,否则不跳转
- B.EQ 比较结果是等于,执行标号,否则不跳转
- B.HI 比较结果是无符号大于,执行标号,否则不跳转
- CBZ - CBZ ; 比较(Compare),如果结果为零(Zero)就转移(只能跳到后面的指令)
- CBNZ - CBNZ ; 比较,如果结果非零(Non Zero)就转移(只能跳到后面的指令)
逆向工具
对任何平台进行逆向分析时都会借助很多工具,iOS逆向也不例外。以逆向的流程为例,会使用解密工具、class-dump、Cycript、Reveal、Charles、Hopper、IDA、Xcode、Theos等,这些工具都是必须了解的。
除了使用工具,还需要了解每个工具的实现原理,有哪些值得借鉴的地方。
效率工具
- iTerm2
- oh-my-zsh
- Go2Shell
- autojump
- Alfred
实用工具
效率提升后,需要一些实用工具来帮助完成某些复杂的操作
- Homebrew
- Cakebrew
- libimobiledevice
- tree
- 010 Editor
逆向工具
逆向工具很重要,需要介绍一下
- jtool: 查看文件结构,代码签名
- capstone: 多平台、多架构支持的反汇编框架
- keystone: 将汇编指令转换为 Hex 机器码
- radare2: 开放源代码的逆向工程平台
- mobiledevice: 安装 app 或 ipa 包
最后的总结
这些只是我们接触逆向时,必要的准备工作,还有进阶的知识,后面会慢慢讲解。