单片机交流
直播中

泡芙奶昔

11年用户 716经验值
私信 关注
[问答]

什么是汇编语言?

什么是汇编语言?

回帖(1)

孙翼飞

2021-11-5 11:12:09

  • 关于ARM架构
    ARM架构,过去称作高级精简指令集机器(英语:Advanced RISC Machine,更早称作Acorn精简指令集机器,Acorn RISC Machine),是一个精简指令集(RISC)处理器架构家族,其广泛地使用在许多嵌入式系统设计。由于节能的特点,其在其他领域上也有很多作为。ARM处理器非常适用于移动通信领域,匹配其主要设计目标为低成本、高性能、低耗电的特性。另一方面,超级计算机消耗大量电能,ARM同样被视作更高效的选择。[3]安谋控股开发此架构并授权其他公司使用,以供他们实现ARM的某一个架构,开发自主的系统单片机和系统模块(system-on-module,SoC)。。
  • 关于汇编语言
    汇编语言(英语:assembly language)是一种用于电子计算机、微处理器、微控制器,或其他可编程器件的低级语言。 在不同的设备中,汇编语言对应着不同的机器语言指令集。 一种汇编语言专用于某种计算机系统结构,而不像许多高级语言,可以在不同系统平台之间移植。


什么是汇编语言?
汇编语言只是机器代码之上的一个简单语法层,它由映射了二进制机器码的助记符组成。二进制机器码是CPU所能理解的指令。那么为什么我们不直接写机器码呢?我只能说,那会很蛋疼(原文为that would be a pain in the ass,终于知道蛋疼怎么说了,新技能get)。因此我们会使用汇编语言,这对于人类来说更易于理解。当然,我们的计算机本身不能运行汇编代码,它需要机器码。我们将使用GNU Binutils工具集中的汇编器as将汇编代码转换为对应的机器码,as会读取后缀为“.s”的汇编源代码文件,然后输出汇编后的二进制目标文件。
编写了后缀为“.s”的汇编文件后,可以使用as将它汇编,最后使用ld链接,如下所示:
$ as program.s –o program.o
$ ld program.o –o program
在接下来的一系列实验中,针对重点和难点部分我都会使用gdb配合调试观察,方便我们理解指令的具体含义,gdb本身功能已经很强大了,不过这里我们给它装上gef插件,gef支持多架构,而且功能强大比如heap的分析功能等。
三条命令安装gef
wget -q -O- https://github.com/hugsy/gef/raw/master/gef.sh | sh # manually # 下载 gef.py, 并将其 source 写入 .gdbinit wget -q -O ~/.gdbinit-gef.py https://github.com/hugsy/gef/raw/master/gef.py echo source ~/.gdbinit-gef.py >> ~/.gdbinit





接着启动gdb如图所示则表示安装成功






接下来我们先学习数据类型和寄存器的知识。
可以供我们载入(load)或者存储(store)的数据类型可以分为有符号和无符号类型的字,半字,或字节。对这些数据类型的扩展是:半字为-h,-sh,字节为-b或者-***,字没有扩展。



涉及到的指令集包括
ldr = Load Word 载入字
ldrh = Load unsigned Half Word 载入无符号半字
ldrsh = Load signed Half Word 载入有符号半字
ldrb = Load unsigned Byte 载入无符号字节
ldr*** = Load signed Bytes 载入有符号字节
str = Store Word 储存字
strh = Store unsigned Half Word 储存无符号半字
strsh = Store signed Half Word 储存有符号半字
strb = Store unsigned Byte 储存无符号字节
str*** = Store signed Byte 储存有符号字节
寄存器的数量取决于ARM的版本。根据ARM参考手册可知,有30个32位的通用寄存器(除了 ARMv6-M和ARMv7-M的处理器)。在本基础系列课程中,我们学习的对象是在任意特权模式下都可以访问的寄存器:R0-R15。这16个寄存器可以被分成两组:通用寄存器和特殊功能寄存器。
R0-r11都是通用寄存器





特殊功能寄存器如下,其中,R12是IP寄存器,内部程序调用寄存器。R13,SP,堆栈指针寄存器。R14,LR,连接寄存器。R15,PC,程序计数器。CPSR,当前程序状态寄存器



R0-R12可以在通常的运算过程中用来存储临时的数据,指针(定位内存)等。以R0为例,当我们执行算数运算或者存储当前函数的返回值时,可以把R0视为累加器。系统调用发生时,R11开始生效,它存储了系统调用数值。R11作为栈指针帮助我们追踪栈的边界(稍后会讲到)。此外,ARM专用的函数调用规则规定了函数的前四个参数应该分别存贮与R0到R3中。
我们写一个名为test1.s的汇编文件,内容如下





然后编译得到test1的二进制文件





接着使用gdb载入调试,在_start设置断点





运行该程序,输入run即可



可以看到,PC此时为0x10054



这里有一个重点:
现在我们输入nexti来执行下一条指令,下一条指令是mov r0,pc;即把pc赋给r0,那么也就是说r0的值应该为0x10054,真的是这样吗?



可以看到现在r0为0x1005c,而不是0x10054,而0x10054+8=0x1005c,也就是说它不是保持之前读取的pc值,而是储存了相对之前读取的0x10054之后的两条指令的地址。从这儿我们可看成,当我们直接读取PC时,它按照定义,PC指向下一条指令,但是当我们调试程序时,PC却指向当前PC值的下面两条指令的地址处(0x10054+8=0x1005C)。这是因为,老款的ARM处理器总是获取当前已经执行的指令的后两条指令的地址。ARM保留着这个定义的原因是为了保证和早期处理器的兼容性
接下来我们看看ARM的指令集
ARM处理器有两种工作状态ARM和Thumb。这两种工作状态和运行模式没有任何关系。比如不论是ARM还是Thumb状态的代码都可以运行在用户模式下。这两种工作状态之间最大的差异是指令集,ARM状态的指令长度是32位的,Thumb状态的指令长度是16位的(也可能为32位)。了解如何使用Thumb工作状态对于编写ARM平台的漏洞利用是至关重要的。当我们编写ARM shellcode时,需要使用16 bit的Thumb指令代替32 bit的ARM指令,从而避免在指令中出现’’截断。
Thumb有不同的版本,下面我们对不同的版本做一下简单的介绍,注意不同的命名只是为了区分不同的版本(换句话说,处理器只知道它运行在Thumb状态,其它一概不知)。
• Thumb-1(16位指令):用于ARMv6和更早的版本。
• Thumb-2(16位和32位指令):对Thumb-1的扩展,添加了更多指令并允许它们为16位或32位宽(ARMv6T2,ARMv7)。
• ThumbEE:在Thumb-2基础上包含了针对动态代码生成(代码在执行前或执行期间编译代码)的一些变更和补充。
我们简单介绍ARM的指令集和它的基本用法
ARM指令通常跟一到两个操作数,我们使用如下模板描述:
MNEMONIC{S}{condition} {Rd}, Operand1, Operand2
其中各个字段的作用如下:
MNEMONIC - 指令的助记符如ADD
{S} - 可选的扩展位
- 如果指令后加了S,将依据计算结果更新CPSR寄存器中相应的FLAG
{condition} - 执行条件,如果没有指定,默认为AL(无条件执行)
{Rd} - 目的寄存器,存储指令计算结果
Operand1 - 第一个操作数,可以是一个寄存器或一个立即数
Operand2 - 第二个(可变)操作数
- 可以是一个立即数或寄存器甚至带移位操作的寄存器
助记符、S扩展位、目的寄存器和第一个操作数的作用很好理解,不多做解释,这里补充解释一下执行条件和第二个操作数。设置了执行条件的指令在执行指令前先校验CPSR寄存器中的标志位,只有标志位的组合匹配所设置的执行条件指令才会被执行。第二个操作数被称为可变操作数,因为它可以被设置为多种形式,包括立即数、寄存器、带移位操作的寄存器,如下所示:
#123 - 立即数
Rx - 寄存器比如R1
Rx, ASR n - 对寄存器中的值进行算术右移n位后的值
Rx, LSL n - 对寄存器中的值进行逻辑左移n位后的值
Rx, LSR n - 对寄存器中的值进行逻辑右移n位后的值
Rx, ROR n - 对寄存器中的值进行循环右移n位后的值
Rx, RRX - 对寄存器中的值进行带扩展的循环右移1位后的值
常见的指令包括:


举报

更多回帖

发帖
×
20
完善资料,
赚取积分