Arm 架构是一种 risc 架构,著名的精简指令集。和 x86 架构的汇编思维基本相同,栈帧建立释放,sp 寄存器操作栈,寄存器/内存等等。在区别上主要体现在名称不同。例如对于条件跳转,x86 架构的 jmp 指令,Arm 架构的 b(eq) 指令。x86 的命名明显能视读出 jump,但是 Arm 的汇编则没那么容易看出。所以本文的侧重点可能还会有点奇怪——我更想来看看所有汇编指令的含义,这样记忆起来也会更加深刻。
其次是为什么学习 Arm 架构汇编而不是 x86 架构?其实学习 x86 架构汇编应该是所有学习汇编的开始,无论从历史角度还是技术,然而事实是笔者学习这个技术的目的是为了逆向工程中对汇编的 patch 更加得心应手。而目前笔者逆向工程的对象一般是
Android Software
IOS Software
Mac Software
神奇的是这三都是 Arm 64 架构,所以自然而然接触下来也对 Arm 架构有所了解。
本文仅供初学者阅读,笔者水平很次,如有错误请谅解。本文目标仅仅是在 ida 阅读伪代码之后切换到IDA view的时候能快速定位到汇编,能够知道 patch 什么,怎么 patch。
在介绍一些命令之前,先讲讲一些汇编的基础。首先汇编本身会有一些比较隐含的规则。可能接触过其他任何编程语言,但没接触过 asm 的初学者会对汇编的返回值感觉奇怪,所有其他语言基本上都是
... // some code
return param;
或者对于 rust 可以不写 return
这个关键字,直接写返回值。
... // some code
param // equals `return param;`
然而在汇编中就是
... ; some asm code
ret
事实上在汇编里面,返回值固定就是第一个寄存器,在 x86 中是 rax,arm64 架构是 x0。也就是这和其他语言有一些很大的区别。在别的语言中单句话的语义是全的。也就是说,我做什么任何事情都是对于这个语句能直接看出来的。就以这个返回值为例,我返回一个值在所有语言里面几乎都是 return something
,这个单句实际上表述了返回这个值,而汇编默认返回第一个寄存器;又或者 let a = func(b, c);
一眼就是知道调用了 func ,传入了 b 和 c,返回的值给了 a,而汇编中
mov x0, x3 // 将 b 的值加载到 x0,假如 b 的值原本在 x3 寄存器
mov x1, x4 // 将 c 的值加载到 x1,假如 c 的值原本在 x4 寄存器
bl func // 调用 func 函数
mov w8, w0 // 假设将返回值存储到 w8 寄存器中
调用函数的默认传入参数就是 x0, x1,...
,如果寄存器不够放则调用者负责把超出数量的参数压入栈。从这两个案例中笔者实际上想要表示的意思是,学习汇编不能盯着一行看,因为每一行内容都很短,而其作用会和上下相关。一个值会到处使用,而需要小心这里的东西被那里用到….
另外,例如比较语句,实际上
if (condition) {
do something;
} else {
do something else;
}
这个 if 在汇编中应该也有好几行。我们先简单标注一下位置
if (condition) {
LABEL_1:
do something;
} else {
LABEL_2:
do something else;
}
因此汇编的大体框架是
condition ; 条件判断,例如 cmp r0, r1
beq LABEL_1 ; 如果条件为真,跳转到 LABEL_1
bne LABEL_2 ; 如果条件为假,跳转到 LABEL_2
这里 beq 表示 “branch if equal” 的缩写,假如条件为真则跳转到 LABEL_1
。但是问题是条件是什么?自然是上文紧跟的 cmp,比较的结果直接影响到这里的 beq。如果只盯着这一行看显然是看不懂 beq 到底在做什么了。
对于函数的入栈出栈在本文还不太需要使用到,下篇将会详述。本篇先讲述一些基础的 Arm 架构的 asm 指令使用。我一向认为,先熟悉这些语法,在熟悉语法之后可以简单 patch 汇编之后,我们才需要研究 sp 的寄存器这些栈帧的事情了,到时候无论是举例子还是其他的都比较简单。下篇或是下下篇,应该会对一个真实的案例逐行分析。
第一个是 sub
和 subs
,这俩一看就知道是减法。具体来说
sub
是 “subtract” 的缩写,表示减法。s
表示 “set flags”,即设置条件标志位。
使用的参数格式是
sub/subs <目标寄存器>, <源寄存器1>, <源寄存器2/立即数>
<目标寄存器>
:存储结果的寄存器。<源寄存器1>
:被减数。<源寄存器2/立即数>
:减数。
需要注意的是subs
:与 sub
类似,但会更新条件标志位(如零标志位 Z、负数标志位 N 等)。
一个简单的案例就是
subs w8, w8, #0
- 将寄存器
w8
的值减去0
,并将结果存回w8
,同时更新条件标志位。
第二个是cset
,可以根据条件标志位的值,将目标寄存器设置为 0
或 1
。其中cset
是 “conditional set” 的缩写,表示根据条件设置寄存器。
cset <目标寄存器>, <条件>
<目标寄存器>
:存储结果的寄存器。<条件>
:如ne
(不等于)、eq
(等于)等。
简单的案例:
cset w8, ne
- 如果条件标志位表示“不等于”(
ne
),则将w8
设置为1
,否则设置为0
。
第三个 tbnz
是 “test bit and branch if nonzero” 的缩写。用于测试寄存器中的某一位是否为 1
,如果是,则跳转到指定标签。
tbnz <寄存器>, <位索引>, <标签>
<寄存器>
:要测试的寄存器。<位索引>
:要测试的位(从0
开始计数)。<标签>
:如果测试结果为真,则跳转到的目标地址。
简单案例tbnz w8, #0, LBB0_2
- 测试
w8
的第0
位是否为1
,如果是,则跳转到LBB0_2
。
刚才说过 b
是跳转,其实应该说是无条件跳转。对应 x86 中的 jmp label
.
b LBB0_1
表示无条件跳转到 LBB0_1
。而b
是 “branch” 的缩写,表示分支跳转。
接下来就是神秘命名了。
ldr
:从内存加载数据到寄存器。str
:将寄存器中的数据存储到内存。
其中ldr
是 “load register” 的缩写。str
是 “store register” 的缩写。有点离谱,但是仔细想想又很合理的感觉。
ldr/str <寄存器>, [<基址寄存器>, #偏移量]
<寄存器>
:加载或存储的目标寄存器。<基址寄存器>
:内存地址的基址。#偏移量
:相对于基址的偏移。
ldr w8, [sp, #8]
str w8, [sp, #4]
- 从栈指针
sp
偏移8
的位置加载数据到w8
。 - 将
w8
的值存储到栈指针sp
偏移4
的位置。
与这俩类似的是 stur
和 ldur
,加上 u 实际表示的是 “unscaled”,也就是支持负偏移。用法和上述两者一致,简单案例如下
stur w8, [x29, #-4]
ldur w0, [x29, #-4]
- 将
w8
存储到x29
偏移-4
的位置。 - 从
x29
偏移-4
的位置加载数据到w0
。
7. mov
- 功能:
- 将一个寄存器的值复制到另一个寄存器,或将立即数加载到寄存器。
- 命名原因:
mov
是 “move” 的缩写,表示移动数据。
- 参数格式:
mov <目标寄存器>, <源寄存器/立即数>
<目标寄存器>
:存储结果的寄存器。<源寄存器/立即数>
:要复制的值。
- 例子:
mov w8, #5 mov x10, x8
- 将立即数
5
加载到w8
。 - 将寄存器
x8
的值复制到x10
。
- 将立即数
8. mul
- 功能:
- 执行乘法操作,结果存储在目标寄存器中。
- 命名原因:
mul
是 “multiply” 的缩写,表示乘法。
- 参数格式:
mul <目标寄存器>, <源寄存器1>, <源寄存器2>
<目标寄存器>
:存储结果的寄存器。<源寄存器1>
和<源寄存器2>
:两个乘数。
- 例子:
mul w8, w8, w0
- 将
w8
和w0
相乘,结果存回w8
。
- 将
9. bl
- 功能:
- 调用函数(跳转到指定地址并保存返回地址到
x30
)。
- 调用函数(跳转到指定地址并保存返回地址到
- 命名原因:
bl
是 “branch with link” 的缩写,表示带链接的分支跳转。
- 参数格式:
bl <函数地址>
<函数地址>
:调用的目标函数。
- 例子:
bl _factorial
- 调用
_factorial
函数。
- 调用
10. ret
- 功能:
- 返回到调用者(从
x30
中恢复返回地址)。
- 返回到调用者(从
- 命名原因:
ret
是 “return” 的缩写,表示函数返回。
- 参数格式:
ret
- 无需参数。
总结
以上是代码中所有汇编指令的详细解释。每个指令都有其特定的功能和命名逻辑,理解这些指令有助于深入学习汇编语言。如果有其他疑问,欢迎继续提问!
文章作者: YinMo19
文章链接: https://blog.yinmo19.top/2025/03/07/Arm-%E6%B1%87%E7%BC%96-%E5%AD%A6%E4%B9%A0%E5%B0%8F%E8%AE%B0-1/
版权声明:除另有声明外,本博客文章均采用 CC BY-NC-SA 4.0 许可协议。转载请注明原作者与文章出处。