chapter7 ARM反汇编基础

215 阅读6分钟

寄存器

共有37个寄存器,其中31个为通用寄存器,6个为状态寄存器。支持的运行模式有:

  • 用户模式(usr):正常的程序执行状态,也是我们需要关心的模式
  • 快速中断模式(fiq):用于高速数据传输或通道处理
  • 外部中断模式(irq):用于通用的中断处理
  • 管理模式(svc):操作系统使用的模式
  • 数据访问终止模式(abt):当数据或指令预取终止时进入该模式,可用于虚拟存储及存储保护
  • 系统模式(sys):运营具有特权的操作系统任务
  • 未定义指令中止模式(und):当未定义的指令执行时进入该模式

在32位的用户模式下,处理器可以访问的寄存器为不分组寄存器R0-R7,分组寄存器R8-R14,程序计数器PC(R15)及当前程序状态寄存器CPSR

  • 未分组寄存器:指的是没有被系统用于特别的用途,这组寄存器不会因为处理器模式的改变而更改指向的寄存器,在所有的处理器模式下未分组寄存器都指向同一个物理寄存器,所以当中断或者异常处理造成寄存器模式转换的时候,由于使用的相同的物理寄存器,就会容易造成数据损坏
  • 分组寄存器:每一次访问的物理寄存器和处理器当前的运行模式有关,因此不同模式下的寄存器是不同的
R13(SP):常用做堆栈指针,一般可以用于异常处理,保证程序的正常执行,比如:程序进入异常模式的时候,可以将需要保护的寄存器放入R13所指向的堆栈,当程序从异常模式返回的时,则从对应的堆栈恢复

R14(LR):常被用做链接寄存器,用于存在调用子程序时返回的地址,如果返回的地址放在堆栈上,则也可以将R14用做通用寄存器,在异常模式下,R14存放异常的返回地址

  • R15(程序计数器PC):指向当前执行的指令的第二条指令,整个CPU中只有一个
  • R16(当前程序状态寄存器CPSR):可以在任何模式下被访问,主要存放的内容:APSR标记(应用程序状态寄存器)、当前处理器模式、中断禁用标记

image.png

调试代码

书中讲了一套完整的ARM原生程序的生成过程,笔者以NDK开发的方式,做了简化,将目光聚焦在ARM的学习上,参考文档:developer.android.google.cn/ndk/guides/…

使用的调试工具是:IDA Pro

  • Android.mk代码
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm

LOCAL_MODULE := demo

LOCAL_SRC_FILES := demo.c

include $(BUILD_EXECUTABLE)
  • Application.mk代码
APP_ABI := armeabi-v7a

APP_BUILD_SCRIPT := Android.mk

APP_PLATFORM := android-17

APP_OPTAPP_OPTIM := debug
  • demo.c 代码
#include <stdio.h>

  
int main()
{
    printf("start....");
    getchar();
    return 0;
}
  • 编译
ndk-build NDK_PROJECT_PATH=. NDK_APPLICATION_MK=Application.mk

image.png

寻址方式

  • 立即寻址:指令的后面是一个立即数(即常数),一般用于给寄存器赋初始值,比如:MOV R0, #1234

image.png

  • 寄存器寻址:操作数的值在寄存器中,指令执行时,直接从寄存器中取值,如:MOV R0, R1,将R1的值修改为4,赋值给R0,发现R0的值也变成了4

image.png

  • 寄存器移位寻址:在操作前,需要对源寄存器操作数进行移位操作,以下是5种移位操作
LSL:逻辑左移,移位后对寄存器空出的低位补0
LSR:逻辑右移,移位后对寄存器空出的高位补0
ASR:算术右移,移位过程中符号位保持不变,如果源操作数为正数,则移位后对空出的高位补0,否则补1
ROR:循环右移,移位后在移出的低位中填写移位空出的高位
RRX:带扩展的循环的右移,操作数右移1位,移位空出的高位用带有C标识的值填充
  • 寄存器间接寻址:由地址码给出的寄存器是操作数的地址指针,所需的操作数保存在由寄存器指定的地址的存储单元中,如:
LDR R0, [R1]:将R1寄存器的值作为地址,取出此地址的值赋予R0寄存器
  • 基址寻址:将地址码给出的基址寄存器与偏移量相加,形成操作数的有效地址,所需的操作数保存在有效地址指向的存储单元中,多用于查表、数组访问等操作
LDR R0, [R1, #-4]:将R1寄存器的值减4作为地址,取出此地址中的值赋予R0寄存器,比如将R1的值修改为地址:0x4009ED60,#-4之后,PC的指向是:0x4009ED5C

image.png

  • 多寄存器寻址:一条指令最多可以完成16个通用寄存器值的传送
LDMIA R0, {R1, R2, R3, R4}:LDM是数据加载指令,IA表示每次执行加载操作后,R0寄存器自增1个字,在ARM指令集中,1个字表示132位的值,因此这条指令执行后,R1=[R0],R2=[R0+#4],R3=[R0+#8],R4=[R0+#12]
  • 堆栈寻址:指令有LDMFA/STMFA,LDMEA/STMEA,LDMFD/STMFD,LDMED/STMED,LDM和STM为指令前缀,表示多寄存器寻址
STMFD SP!, {R1-R7, LR} 将R1-R7, LR寄存器入栈,多用于保存子程序
LDMFD SP!, {R1-R7, LR} 将数据出栈,放入R1-R7, LR寄存器,多用于恢复子程序
  • 块拷贝寻址:用于将连续地址的数据从寄存器的某一个位置复制到另一个位置,指令有LDMIA/STMIA,LDMDA/STMDA,LDMIB/STMIB,LDMDB/STMDB
LDMIA R0!, {R1-R3} 从R0寄存器指向的存储单元中读取3个字,分别放入R1-R3寄存器中
STMIA R0!, {R1-R3} 将R1-R3寄存器的内容存储到R0寄存器指向的存储单元中
  • 相对寻址:以PC当前的值为基地址,以指令中的地址标号为偏移量,将两者相加,得到操作数的有效地址
BL NEXT 表示跳到NEXT标号处执行,BL采用的就是相对寻址,标号NEXT就是偏移量

常见指令

条件指令

image.png

MOV 指令

移动数据到寄存器中,第二个数可以是立即数、寄存器

MOVS总会影响CPRS,包括N,Z,C标志位

基本运算指令

  • ADD/ADC:加法指令系
ADD不影响CPSR条件标记位
ADC在做运算的时候,需要加上CPSR的C位,相当于:R0 = R0 + R1 +cpsr.c
ADDS带进位的加法运算

通过movw和movt来复现,movw和movt主要是用来往寄存器中加载一个32位的值

image.png

当前CPSR的C位为0, R0=1, R1=2, ADD之后,R0=3

image.png

当前CPSR的C位为0, R0=3, R1=2, ADC之后,R0=5

image.png

通过ADDS来影响进位,将R0=#xffff,发现c位变为了1

image.png

当前CPSR的C位为1, R0=1, R1=2, ADD之后,R0=3

image.png

当前CPSR的C位为1, R0=3, R1=2, ADC之后,R0=6

image.png

参考:

introspelliam.github.io/2017/09/08/…

blog.csdn.net/The_dying_m…