完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
扫一扫,分享给好友
|
|
相关推荐
1个回答
|
|
汇编语言只是机器代码之上的一个简单语法层,它由映射了二进制机器码的助记符组成。二进制机器码是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位后的值 常见的指令包括: |
|
|
|
只有小组成员才能发言,加入小组>>
2513 浏览 0 评论
1083浏览 2评论
700浏览 1评论
452浏览 0评论
193浏览 0评论
329浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-22 06:20 , Processed in 1.137047 second(s), Total 79, Slave 60 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号