ARM和THUMB
ARM 处理器有两种主要的运行状态,ARM
和 Thumb
,这两种状态和处理器的权限级别没有关系,也就是说,不管是在用户模式还是系统模式下,代码都可能是ARM或Thumb状态。在ARM状态下,处理器使用的是标准的32位ARM指令集。这意味着每条指令都是32位,可以执行复杂的操作。
Thumb状态是ARM处理器的一种16位指令集,设计用来节省内存空间。虽然Thumb指令通常是16位宽,但也可以是32位,取决于具体的指令和操作。Thumb状态下的指令集是ARM状态的子集。以根据需要在这两种状态之间切换。例如,在编写需要嵌入到其他程序中的小型代码片段(如shellcode)时,使用Thumb状态可以更高效地利用内存。
ARMARM公司在不同时间推出了不同版本的Thumb指令集。每个版本可能支持不同的ARM和Thumb指令集变体。并不是所有版本的ARM都支持相同的Thumb指令集。研究的时候具体了解目标设备使用的ARM版本,以及这个版本特别支持的Thumb指令集就可以了。详细信息可以在ARM信息中心查询。
ARM指令简介
前期先了解汇编语言的最小部分如何运行、它们如何相互连接以及通过组合它们可以实现什么目标就可以了,后续在研究过程中逐渐学习即可。
汇编指令模板如下:
MNEMONIC{S}{condition} {Rd}, Operand1, Operand2
每个字段的含义如下:
- MNEMONIC: 助记符,指令名,例如ADD(加法),MOV(移动)
- {S}: 可选,如果指令中包含
S
,则表示在执行操作后,处理器的状态标志(如零标志、负数标志、进位标志等)将根据操作结果进行更新。 - {condition}:可选,条件表达式,指定了指令执行所需的条件。ARM指令可以在满足特定条件时才执行。
- {Rd}:这是指令执行结果的目标寄存器。指令执行的结果(比如加法操作的和)将被存储在这个寄存器中。
Rd
代表destination Register(目的寄存器) - Operand1:指令的第一个操作数。它可以是一个寄存器的名称或一个立即数(直接给出的数值)
- Operand2:第二个操作数,可以是一个立即数,或者是一个带有可选移位的寄存器。
几个简单的指令如下:
-
ADD R0, R1, R2
:将寄存器R1
和R2
中的内容相加,并将结果存储在寄存器R0
中 -
ADD R0, R1, #2
: 将寄存器R1
中的内容与立即数2
相加,并将结果存储在寄存器R0
中 -
MOV R0, R1, LSL #1
:将寄存器R1
中的内容逻辑左移一位,然后将结果移动到寄存器R0
中 -
CMP R0, R1
:CMP
(比较)指令会将寄存器R0
和R1
的值进行比较,根据比较的结果,改变$CPSR
中的值- 如果
R2
小于R3
,CPSR中的N(负数)标志会被设置为1,表示结果为负 - 如果
R2
等于R3
,则CPSR中的Z(零)标志会被设置为1,表示结果为零
- 如果
-
MOVLE R0, #5
:MOVLE
是一种特殊的移动(move)指令,全称是"Move if Less Than or Equal",意思是“如果小于或等于,就移动”,此时会检查上一条指令比较的结果。是否满足“小于等于
”,如果满足,那么数字5就会被放入寄存器R0
中,如果不满足,R0
中的值就不会改变。
一些基本的指令如下表
指令 | 描述 | 指令 | 描述 |
---|---|---|---|
MOV | 移动数据 | EOR | 按位异或 |
MVN | 将一个数取反并移动 | LDR | 加载,从内存中读取数据到寄存器 |
ADD | 加法 | STR | 存储,将数据从寄存器存储到内存 |
SUB | 减法 | LDM | 加载多个 |
MUL | 乘法 | STM | 存储多个 |
LSL | 逻辑左移 | PUSH | 压栈 |
LSR | 逻辑右移 | POP | 弹出堆栈 |
ASR | 算术右移 | B | 分支,根据条件,跳转到程序的另一个位置 |
ROR | 右旋 | BL | 执行一个子程序调用,并将返回地址存储在寄存器中 |
CMP | 比较 | BX | 跳转到另一个寄存器指向的地址,并可以交换处理器模式 |
AND | 按位与 | BLX | 用于调用子程序,并将链接寄存器更新为子程序的返回地址,同时可以改变处理器模式 |
ORR | 按位或 | SWI/SVC | 系统调用 |