ARM汇编学习二

我们来学习内存指令:加载和存储
ARM使用载入-存储模型来访问内存,意味着只有加载/存储(LDR和STR)指令才可以访问内存。在X86中,大多数指令允许直接操作内存中的数据,而在ARM中,在操作数据之前,必须把数据从内存移动到寄存器中。这意味着在ARM下,若要 增加特定内存地址里的32位的数值,将需要用到三种类型的指令(载入、增加和存储):首先将特定地址里的数值加载到寄存器中,然后在寄存器中增加它,最后将数据从寄存器返存回内存里
为了解释ARM的加载和存储操作的基本原理,我们接下来会使用几个示例,然后通过gdb调试进一步理解。

敲黑板,这一部分非常重要!!!

先来看LDR,STR。
通常,LDR用于将内存数据加载到寄存器中,STR用于从寄存器的值存储到内存地址对应的内存中。如下图所示
在这里插入图片描述
格式如下:
LDR R2, [R0] @ [R0] - 原始地址是R0里的数值
STR R2, [R1] @[R1] - 目标地址是R1里的数值
LDR操作:将在R0中找到的地址的值加载到目标寄存器R2。
STR操作:将R2中找到的值存储在R1中找到的内存地址中。

我们先来看第一种偏移形式:立即数用作偏移。这里我们使用一个立即数(整数)作为偏移量。这个值通过与基址寄存器(下面的例子中的R1)相加或相减来访问数据。
示例代码如下,在test2.s中
在其中已经写好注释了
在这里插入图片描述
中文注释为:
_start:
ldr r0, adr_var1
将变量var1的内存地址通过标签adr_var1载入R0
ldr r1, adr_var2
将变量var2的内存地址通过标签adr_var2载入R1
ldr r2, [r0] @
将r0里的值作为地址取出里面的值(0x03)存入寄存器R2
str r2, [r1, #2]
寻址模式:偏移模式。将存储在R2里的值(0x03)存放在以r1+2为地址指向的内存空间中。基址寄存器(R1)的值不变
str r2, [r1, #4]!
寻址模式:先索引模式。将存储在R2里的值(0x03)存放在以r1+4为地址指向的内存空间中,然后基址寄存器(R1)被修改为R1=R1+4
ldr r3, [r1], #4
寻址模式:后索引模式。将存储在R1里的值作为内存地址取出里面的值存放在以r3中(而非R3+4),然后基址寄存器(R1)被修改为R1=R1+4
bkpt
中断,暂停程序

主要看_start就行了
我们使用 adr_var1和 adr_var2来存储var1变量和var2变量(在顶部的数据段中定义的)的内存地址。第一个LDR指令将var1的地址加载到寄存器R0中。第二个LDR指令对var2做了同样的事并将其加载到R1。然后,将存储在R0中的内存地址加载到R2里,并将R2中找到的值存储在R1中找到的内存地址中
同样编译好test2
在这里插入图片描述
载入gdb调试,在_start下断点
在这里插入图片描述
然后run。
前三条都是ldr指令,用于给r0,r1,r2寄存器赋值,我们不关心这个过程,使用nexti 3直接跳到str r2,[r1,#2]前
在这里插入图片描述
可以看到此时r2已经被复制为0x3了,下一步要执行的指令为str r2,[r1,#2]
按照注释的说法,它会将r2(0x3)存储到值为(r1(0x2009c)+2))所指向的内存空间中。
意思即0x2009e指向的内存空间的内容为0x3,我们使用nexti执行这条指令,然后使用下面命令查看,果然和我们计算的相符合
在这里插入图片描述
下一步指令为
在这里插入图片描述
这是先索引寻址模式,这个模式下基址寄存器将被最终的内存地址更新,这个例子中基址寄存器为r1。执行后,会将r2的值(0x3)存储到(r1(0x2009c)+4)即0x200a0所指向的内存空间中,同时将r1更新为0x200a0
同样使用nexti执行
然后使用下面的命令验证
在这里插入图片描述

接下来要执行的指令是
在这里插入图片描述
这条指令使用的后索引寻址模式。指令执行之后,取出r1(0x200a0)地址的内容,赋给r3,然后r1(0x200a0)+4=0x200a4会更新r1
使用nexti执行这条指令,然后使用下面的命令验证

在这里插入图片描述

接下来我们看看第二种偏移形式:寄存器的值用作偏移。代码如下,写在test3.s中
在这里插入图片描述

关键指令的中文注释如下:
ldr r0, adr_var1
透过adr_var1标签,将var1变量的内存地址加载进R0
ldr r1, adr_var2
透过adr_var2标签,将var2变量的内存地址加载进R1
ldr r2, [r0]
将R0里的值作为内存地址,把地址里的数值(0x03)载入r2
str r2, [r1, r2]
寻址模式:偏移寻址。将R2里的值存入:R1+R2(0x03,偏移)的结果所指向的内存空间中。基址寄存器不更新。
str r2, [r1, r2]!
寻址模式:先索引模式。将R2的值(0x03)存入:R1+R2(0x03,用作偏移)得出的结果所指向的内存空间中。基址寄存器R1更新为R1 = R1+R2。
ldr r3, [r1], r2
寻址模式:后索引模式。将R2的值作为地址取出里面的值,并将其存入寄存器R3。基址寄存器R1更新为R1=R1+R2。
编译
在这里插入图片描述
同样使用gdb调试,在_start下断点,跳过前三条ldr指令,然后看看此时的情况
在这里插入图片描述
此时r1为0x2009c,r2为0x3,下一条要执行的指令为str r2,[r1,r2]
这是偏移寻址模式,执行之后,r2的值(0x3)会被存储在0x2009c+0x3=0x2009f指向的内存空间中
我们使用nexti执行后使用下面的命令查看
在这里插入图片描述
符合我们的计算,同时看到下一条要执行的指令
这是先索引模式。与上一条指令相比,这条指令执行后会用0x2009c+0x3=0x2009f的值更新r1
我们使用nexti执行后使用下面的命令查看
在这里插入图片描述
符合我们的计算,同时看到了下一条要执行的指令
这是后索引寻址模式。执行后会将r1(0x2009f)地址中的值取出来赋给r3,然后更新r1为0x2009f+0x3=0x200a2
我们使用nexti执行后使用下面的命令查看

在这里插入图片描述

接下来我们学习第三种偏移形式:移位寄存器作为偏移。
格式为
LDR Ra, [Rb, Rc, ]
STR Ra, [Rb, Rc, ]
Rb是基寄存器,Rc里面是立即数偏移量(或是寄存器里的一个立即数),用于左/右移位(<移位寄存器>)。这意味着移位寄存器被用来存放移位的偏移量

代码如下,写在test4.s
在这里插入图片描述
关键指令中文注释如下
_start:
ldr r0, adr_var1
透过adr_var1标签,将var1变量的内存地址加载进R0
ldr r1, adr_var2
透过adr_var2标签,将var2变量的内存地址加载进R1
ldr r2, [r0]
将R0里的值作为内存地址,把里面的值(0x03)载入r2
str r2, [r1, r2, LSL#2]
寻址模式:偏移寻址。将R2里的值(0x03)存入:R1的值+偏移量(R2左移2位后的值)的结果所指向的内存空间中。基址寄存器(R1)不更新。
str r2, [r1, r2, LSL#2]!
寻址模式:先索引寻址。将R2里的值(0x03)存入:R1的值+偏移量(R2左移2位后的值)的结果所指向的内存空间中。基址寄存器(R1)更新为R1 = R1 + R2<<2。
ldr r3, [r1], r2, LSL#2
寻址模式:后索引寻址。将R1的值作为地址取出里面的值,并将其存入寄存器R3。基址寄存器R1更新为R1=R1+R2<<2。
bkpt
编译后载入gdb,在_start下断点,run,nexti 3后查看情况
在这里插入图片描述
这条指令使用偏移寻址模式,将在r2中找到的值(0x3)存储在x中,x的计算方式为:
1.r2:x00000003(十六进制)=0000 0011(二进制)
2.0000 0011 左移两位得到 0000 1100
3.0000 1100(二进制) = 0xc(十六进制)
4.然后r1(0x2009c)作为基址,加上0xc(作为偏移)得到0x00200a8
使用nexti执行,使用下面命令验证
在这里插入图片描述
下一条要执行的指令为先索引地址模式。执行后会更新基址寄存器r1。它会首先给r1(0x2009c)向左偏移两位得到0x200a8,把r2(0x3)赋给0x200a8,同时用0x200a8更新r1
使用nexti执行后使用下列命令查看
在这里插入图片描述
符合我们的计算,下一个指令是后索引寻址模式。会将r1(0x200a8)取出作为地址,取出该地址里的值(0x3)赋给r3,然后使用x更新r1
x的值等于r1(x0200a8)加上r2(0x3)左移两位后得到的(0xc),即0x200a8+0x3=0x200b4
nexti后使用下面命令验证
在这里插入图片描述
确实和我们计算的一样。

参考:
azeria-labs 的arm教程
https://azeria-labs.com

### 学习ARM汇编的入门指南 学习ARM汇编语言需要从基础概念入手,并结合实际操作进行深入理解。以下是一些关键点和资源推荐,帮助初学者快速掌握ARM汇编的基础知识。 #### 1. ARM汇编的基本概念 ARM汇编是一种低级编程语言,直接与硬件交互,用于编写高效、紧凑的代码。计算机通过电信号的形式接收指令,并将其解释为一系列0/1序列(bits)。为了便于人类理解和记忆,这些电信号被抽象为助记符,形成了汇编语言[^4]。例如,`MOV R0, #1` 是一条典型的ARM汇编指令,表示将值1加载到寄存器R0中。 #### 2. 学习资源推荐 - **官方文档**:ARM官方提供的参考手册是学习ARM汇编的重要资源。它详细描述了每条指令的功能和使用方法。 - **在线教程**:如《Whirlwind Tour of ARM Assembly》和《ARM assembler in Raspberry Pi》等文章提供了丰富的实例和解释,适合初学者逐步掌握。 - **书籍**:《Practical Reverse Engineering: x86, x64, ARM, Windows Kernel, Reversing Tools, and Obfuscation》是一本优秀的逆向工程书籍,其中包含了ARM汇编的相关内容[^4]。 - **集成开发环境(IDE)**:使用Keil MDK等工具可以方便地编写、调试ARM汇编程序[^5]。例如,在Keil中创建一个STM32项目,可以模拟真实的嵌入式开发环境。 #### 3. 实践案例 通过实际编写代码来加深对ARM汇编的理解是非常重要的。以下是一个简单的“Hello, World”程序示例,展示了如何在ARM架构上运行汇编代码[^3]: ```assembly @ 定义数据段 .data msg: .asciz "Hello, ARM\n" @ 定义代码段 .text .global _start _start: @ 将消息地址加载到寄存器R0 LDR R0, =msg @ 调用系统调用以打印字符串 MOV R7, #4 @ 系统调用号4 (sys_write) MOV R1, #1 @ 文件描述符1 (stdout) LDR R2, =len @ 消息长度 SWI 0 @ 触发软中断 @ 退出程序 MOV R7, #1 @ 系统调用号1 (sys_exit) SWI 0 @ 触发软中断 len = . - msg @ 计算消息长度 ``` 此代码片段展示了如何使用ARM汇编编写一个简单的程序,输出“Hello, ARM”字符串[^3]。 #### 4. 进阶学习建议 随着基础知识的掌握,可以进一步探索ARM架构的高级特性,例如异常处理、中断服务程序以及多核处理器的支持。同时,实践是学习汇编语言的关键,建议多尝试编写和调试小型程序,逐步积累经验[^1]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值