汇编语言笔记-ARM架构基本寄存器

Posted 凯之~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了汇编语言笔记-ARM架构基本寄存器相关的知识,希望对你有一定的参考价值。

文章目录

寄存器组

       Cortex-M3和Cortex-M4处理器(ARM架构)用于数据处理与控制的寄存器组中有16个寄存器,其中13个(R0 ~ R12)为通用目的寄存器,另外三个具有特殊用途:

1.R0 ~ R12

       寄存器R0 ~ R12为通用目的寄存器,其中前8个(R0 ~ R7)也被称作低寄存器,由于指令中可用的空间有限,许多16位指令只能访问低寄存器。而其它寄存器(R8 ~ R12)被称为高寄存器,可用于32位指令和部分16位指令,如MOV。

R0 ~ R12的初始值是未定义的。

2.R13

       R13为栈指针(SP),设置该指针的值后可通过PUSH和POP指令访问栈。

       物理上存在两个栈指针:
       1.主栈指针(MSP,有些ARM文献也称其为SP_main)为默认的栈指针,在复位后或处理器处于处理模式时该指针被处理器选择使用。
       2.进程栈指针(PSP,有些ARM文献也称其为SP_ process),只用于线程模式。

       栈指针的选择由特殊寄存器CONTROL决定。对于一般的程序,这两个寄存器(即MSP和PSP两个寄存器)只有一个可见。
       MSP和PSP都是32位寄存器,但最低两位固定为0。对于 ARM Cortex-M处理器,PUSH和POP指令总是32位的,栈操作的地址也必须对齐到32位的字边界上。

大多情况下,若不需要使用嵌入式OS则没必要使用PSP。许多简单的应用可以完全依赖MSP。一般在用到嵌入式OS时才会使用PSP,此时OS内核同应用任务的栈是相互独立的。PSP的初始值未定义,而MSP的初始值则需要在复位流程中从存储器的第一个字中取出。

3.R14

       R14为链接寄存器(LR),用于保存函数或子程序调用时返回地址。
       在函数或子程序结束时,程序将LR的数值加载至程序计数器(PC)中返回调用程序处并继续执行(也就是跳转回上层函数)。
       当执行了函数或子程序调用后,LR的数值会自动更新。若某函数调用另外一个函数或子程序,则需要将LR的数值保存在栈中,否则,函数执行后,LR的当前值会丢失(函数嵌套必须将对应的上层函数地址保存在栈中)。
       在异常处理期间,LR自动更新为特殊的EXC_RETURN(异常返回)数值,以在异常处理结束时触发异常返回。

4.R15

       R15为程序计数器(PC),可读写寄存器,读操作返回当前指令地址加4(由于设计的流水线特性及同ARM7TDMI处理器兼容的需要)。写操作(例如,使用数据传输/处理指令)产生程序跳转。
       由于指令必须对齐到长度(半字或字),PC的最低位(LSB)为0。但使用部分跳转/读存储器指令更新PC时,需要将PC值的LSB置1表示 Thumb状态,否则就会由于试图使用不支持的ARM指令(如ARM7TDMI中的32位ARM指令)而触发错误异常。高级编程语言(包括C和C+)编译器会自动将跳转目标的LSB置位。
       多数情况下,跳转和调用由专门的指令实现。访问位于程序存储器中的字符数据时,PC的数值非常有用,因此存储器读操作经常将PC作为基地址寄存器,并通过指令中的立即数生成偏移地址。

多数汇编工访问寄存器组中的寄存器时可以使用多种名称。一些汇编工具如ARM汇编(被DS5 Professional和 Keil MDK-ARM支持)可以使用大写、小写或者大小写混合,如下表所示:

特殊寄存器

       特殊寄存器未经过存储器映射,可以使用MSR和MRS等特殊寄存器访问指令来进行访问:

MRS <reg>,<special_reg>    ;将特殊寄存器读入寄存器
MSR <special_reg>,<reg>    ;写入特殊寄存器

       CMSIS-Core(为Cortex-M处理器核和外设定义的应用程序接口API)提供了几个用于访问特殊寄存器的函数。

不要混淆特殊寄存器和其他微控制器架构中的“特殊功能寄存器(SFR)”,SFR一般指的是用于I/O控制的寄存器

程序状态寄存器(xPSR)

       程序状态寄存器包括以下三个状态寄存器:

  • 应用PSR(APSR)
  • 执行PSR(EPSR)
  • 中断PSR(IPSR)

       三个寄存器各个功能位为:

       各个位的作用如下表所示:

注意,APSR和EPSR的一些位域在ARMv6-M架构(如 Cortex-M0)中不可用,且与ARM7TDMI等经典ARM处理器之间存在很大的差异。

       这三个寄存器可以通过一个组合寄存器访问,该寄存器在有些文献中被称作xPSR,ARM汇编器访问xPSR时通过“PSR”访问:

MRS R0, PSR   ;读组合程序状态字
MSR PSR, R0   ;写组合程序状态字

注意,此时组合寄存器为三个寄存器的有效位叠合,如下图所示:

       同时,也可以单独访问某个PSR:

MRS R0, APSR   ;将标志状态读入R0
MRS R0, IPSR   ;读取异常/中断状态
MSR APSR, R0   ;写标志状态

无法使用MRS(读出为0)或MSR直接访问EPSR。
IPSR为只读寄存器。

中断/异常屏蔽寄存器

名 字功能描述
PRIMASK1位宽寄存器。置位后关闭NMI(不可屏蔽中断和HardFault外的所有可屏蔽异常/中断。
FAULTMASK1位宽寄存器。置位后只有NMI可以响应(相比于PRIMASK,可屏蔽的中断/异常多了HardFault)。
BASEPRI根据优先级屏蔽中断/异常,该寄存器最多有9位(由设计实现的优先级位数决定)。该寄存器定义了被屏蔽优先级的阈值。当它被设置为某个值后,所有优先级号大于等于此值的中断都被关(注意:优先级号越大,优先级越低);若设置成0,则不关断任何中断。

注意:FAULTMASK和BASEPRI寄存器在ARMv6-M中不存在(如Cortex-M0)。

只有特权状态才可以操作三个寄存器(非特权状态下的写操作会被忽略,读操作返回0)。三个寄存器默认值为0,即屏蔽(禁止异常/中断)不起作用。

       使用MRS/MSR指令访问这三个寄存器,比如:

  MRS    R0, BASEPRI            ;将BASEPRI寄存器的值写入R0
  MSR    BASEPRI, R0            ;将R0的值写入BASEPRI寄存器

       可通过CSP(修改处理器状态)指令快速设置中断/异常的开关:

  CPSID    I    ;PRIMASK=1,关中断
  CPSIE    I    ;PRIMASK=0,开中断
  CPSID    F    ;FAULTMASK=1,关异常    
  CPSIE    F    ;FAULTMASK=0,开异常

CONTROL寄存器

       Cortex-M3、M4及的CONTROL寄存器如下,其中:

  • nPRIV在Cortex-M0中不存在,在 Cortex-M0+中可选
  • 具有浮点单元的Cortex-M4处理器的CONTROL寄存器中有1位表示当前是否使用浮点单元。

       各个位的解释如下:

描述
nPRIV用于定义线程模式中的特权等级:
0(默认):处于线程模式模式中的特权等级
1:处于线程模式模式中的非特权等级
SPSEL用于定义栈指针的选择:
0(默认):线程模式使用主栈指针(MSP)
1:线程模式使用进程栈指针
处理模式下该位始终为0,且无法写入
FPCA只存在于具有浮点单元的Cortex-M4中。异常处理机制通过该位确定异常产生时浮点单元相应寄存器是否需要保存。
0:当前的上下文不使用浮点指令
1:当前的上下文使用浮点指令(此时产生异常自动保存相应浮点寄存器)。

该寄存器复位后默认为0

只有特权状态才能修改(写操作)CONTROI寄存器。(读操作则不需要特权状态)

       同样的,使用MRS/MSR指令访问该寄存器:

  MRS    R0, CONTROL            ;将CONTROL寄存器的值写入R0
  MSR    CONTROL, R0            ;将R0的值写入CONTROL寄存器

浮点寄存器

1.S0 ~ S31和D0 ~ D15

       S0 ~ S31(S)都为32位寄存器,可通过浮点指令或利用符号D0 ~ D15(D代表双字/双精度)成对访问。
       例如,S0和S1成对组成D0,而S2和S3则成对组成D1。

浮点状态和控制寄存器(FPSCR)

       FPSCR寄存器用于定义浮点运算动作并提供浮点运算结果的状态信息,其位域及各个位的描述如下所示:

描述
N负标志(由浮点比较运算更新)
Z零标志(由浮点比较运算更新)
C进位/借位标志(由浮点比较运算更新)
V溢出标志(由浮点比较运算更新)
AHP交替半精度控制位:
0:IEEE半精度格式(默认)
1:交替半精度格式
DN默认NaN(非数值)模式控制位:
0:NaN操作数会变为浮点运算的输出(默认)
1:任何涉及一个或多个NaN的运算会返回默认的NaN
FZ清零模式控制位:
0:清零模式禁止(默认)(符合IEE754标准)
1:清零模式使能
RMode舍入模式控制位,表示的舍入模式基本上适用于所有的浮点指令:
00:最近舍入(RN)模式(默认)
01:正无穷舍入(RP)模式
10:负无穷舍入(RM)模式
11:向零舍入(RZ)模式
IDC输入非正常累积异常位(结果未在正常数值范围内)
IXC不精确的累积异常位
UFC下溢累积异常位
OFC溢出累积异常位
DZC被零除累积异常位
IOC非法操作累积异常位

上述的6个异常位在浮点异常产生时置位,写0则将其清除

浮点单元控制寄存器(经存储器映射)

       暂略…

注意

       ARM架构具有两种状态:ARM状态和Thumb状态,其中Cortex-M3和M4只能使用Thumb状态,而在该状态下部分寄存器的访问是有限制的:

IOS逆向-arm64汇编

真机和模拟器上的 汇编指令是不一样的

1. 寄存器

1.1 寄存器介绍

  • 通用寄存器:
    • 64bit的:x0 ~ x28
    • 32bit : w0 ~ w28(属于x0 ~ x28的低32bit)
    • x0-x7通常用来拿来存放函数的参数,更多的是参数使用堆栈来传递
    • x0通常拿来存放函数的返回值
  • 程序寄存器: pc(program Counter)
  • 堆栈指针寄存器:sp(Stack Pointer)、fp(Frame Pointer)
  • 链接寄存器:lr(Link Register), 也就是x30
  • 程序状态寄存器:
    • cpsr:(Cuurent Progame Status Register)
    • spsr: (Saved Program Status Register)

1.2 一些调试LLDB的相关指令

  • LLDB指令:

    • memory read : 读取所有寄存器的值
    • memory write 寄存器 值:给某个寄存器写入值
    • po $x0:打印方法调用者
    • x/s $s1: 打印方法名
    • po $x2:打印参数(以此类推, x3,x4也可能是参数)
    • 如果是非arm64,寄存器就是r0,r1,r2
  • 首先我们创建一个Xcode项目,打个断点,然后通过LLDB调试环境来查看汇编语言的寄存器信息:在这里插入图片描述

我们可以读取并修改寄存器的值:在这里插入图片描述

2. 汇编常用指令

2.1 在Xcode中创建汇编文件

  • 汇编文件的后缀为.asm,这里简写成.s文件
  • 我们以函数的形式来学习汇编的指令

在这里插入图片描述
在这里插入图片描述

  • 但是当我们编译项目后,报如下错误:

在这里插入图片描述

  • 分析错误发现是找不到_test函数,其实我们表面是调用test()函数,但是底层实质是寻找_test()函数 ,所以我们编写汇编代码的时候,需要修改函数名称为_test

在这里插入图片描述
当修改问函数名之后,在次编译发现还是报上述那个错误,找不到_test方法,其实如果是直接在方法前面加上_表示私有函数,我们还需要使用.global关键字来声明下函数

在这里插入图片描述

2.2 mov、ret

  • mov : 赋值
    • 指令格式:mov {条件} {S} 目的寄存器,源操作数
    • mov指令可完成从另外一个寄存器、被移位的寄存器或将一个立即数加载到目的寄存器。其中S选项指令的操作是否影响CPSR中条件标志位的值,当没有S指令不更新CPSR中条件标志位的值
    • 指令示例:
      • mov R1, R0 :将寄存器R0的值传递到寄存器R1
      • mov PC, R14:将寄存器R14的值传送到PC,常用语子程序返回
      • mov R1, R0, LSL#3:将寄存器R0的值左移3位传送到R1
  • ret:函数返回,将lr(x30)寄存器的值赋值给pc寄存器
  • mov的示例代码如下
// 作用 告诉写的函数放在哪个段,.text:表示函数放在代码段
.text
//表示函数是公开的,可以被被人访问,而函数的实现在下面
.global _test
//函数名称 test . 相当于打个标记  
_test:
mov x0, #0x8 // 把立即数8储存到寄存器x0中
mov x1, x0  //把寄存器x0的值传到寄存器x1中
ret

在这里插入图片描述

2.3 add

  • add: 加法
    • 指令格式:add {条件} {S}目的寄存器,操作数1,操作数2
    • add指令用于把两个数相加,并将结果存放到目的寄存器中,操作数据1,应该是一个寄存器,操作数2也可以是一个寄存器,被移位的寄存器,或一个立即数
    • 指令示例:
      • add R0, R1, R2: R0 = R1 + R2
      • add R0, R1, #256: R0 = R1 + 256
      • add R0, R2, R3, LSL#1: R0 = R2 + (R3 << 1)
  • add的示例代码
.text
.global _add
;add指令
_add:
mov x0, #0x1
mov x1, #0x2
add x0, x0, x1
ret

在这里插入图片描述

2.3 sub

  • sub:减法
    • 指令格式:sub {条件} {S}目的寄存器,操作数1,操作数2
    • sub指令用于把操作数1减去操作数2,并将结果存放到目的寄存器中。操作数1应是一个寄存区,操作数2可以是一个寄存器,被移位的寄存器,或是一个立即数。该指令可用于有符号或无符号数的减法运算
    • 指令示例:
      • add R0, R1, R2: R0 = R1 - R2
      • add R0, R1, #256: R0 = R1 - 256
      • add R0, R2, R3, LSL#1: R0 = R2 - (R3 << 1
  • sub代码示例
.text
.global _sub
;sub指令
_sub:
sub x0, x0, x1 //把x0 - x1相减的值放到x0中
ret
  • 调用sub(6, 2);
    在这里插入图片描述
    由上述断点调试图可知, 这次我们并没直接使用汇编指令给x0、x1寄存器赋值,当我们传递两个参数进去之后,会自动赋值到x0、x1寄存器上,所以我直接使用sub指令把x0寄存器上的值减去x1寄存器上面的值,然后保存到x0寄存器上面 , 通过LLDB调试指令可以看出相见后的结果储存在x0寄存器中

2.4 cmp(compare:比较)

2.4.1 cmp基本使用

  • cmp: 将2个寄存器相减,将相减的结果会影响cpsr寄存器的标志位

    • 指令格式:cmp {条件}操作数1,操作数2
    • cmp指令用于把一个寄存器的内容和另外一个寄存器的内容或立即数进行比较,同时更新CPSR中的标志位的值。该指令进行一次减法运算,但是不储存结果,只更改条件标志位。标志位表示是操作数1和操作数2的关系(大、小、相等),例如,当操作数1大于操作数2,则此后的有GT的后缀的指令将可以执行。
    • 指令示例:
      • cmp R1, R0 : 将寄存器R1的值与寄存器R0的值相减,并根据结果设置CPSR的标志位
      • cmp R1, #100:将寄存器R1的值与立即数100相减,并根据结果设置CPSR的标志位
  • 代码示例:

_test:
;cmp 指令
mov x0, #0x3
mov x1, #0x1
// 和sub指令相似,都表示x0 - x1,但是cmp是把相减的值,会影响cpsr寄存器中的标志位
cmp x0, x1  
ret

在这里插入图片描述

随着汇编代码的不断执行,我们可以看到cpsr寄存器的值在不断改变, 我们可以通过计算器来查看cpsr的值,看看cmp指令的对cpsr寄存器的影响

2.4.2 cpsr寄存器

在这里插入图片描述

  • cpsr寄存器标志位解释
    在这里插入图片描述

2.5 b

2.5.1 B指令的基本使用

  • b:跳转指令,可以带条件跳转,一般跟cmp配合使用
    • 指令格式:b{条件} 目标地址
    • b指令是最简单的跳转指令。一旦遇到一个b指令,ARM处理器将立即跳转到给定的目标地址,从哪里继续执行。注意储存在跳转指令中的实际值是相对当前PC值的一个偏移量,而不是一个绝对的地址值,它的值由汇编器来计算(参考寻址方式中的相对寻址)。它是24位有符号数,左移两位后有符号扩展为32位,表示的有效偏移为26位(前后32MB的地址空间)。
    • 指令示例:
      • B Lable:程序无条件跳转到标号Lable处执行
      • cmp R1 , #0: 当CPSR寄存器中的z条件码置位时,程序跳转到标号Lable处执行
      • BEQ Lable: 当相等时,跳转到标号Lable处执行
  • 示例代码:
_test:

;b指令
mov x1, #0x7
b mycode
mov x0, #0x5
mycode:
mov x0, #0x6
ret 

在这里插入图片描述

当我执行si指令之后,我们可以看到,程序直接跳转到mycode标记之后继续执行代码,并没有执行mov x0, #0x5这句汇编代码
在这里插入图片描述

2.5.2指令的条件域

当处理器工作在ARM状态时,几乎所有的指令均根据CPSR中条件码和指令的域有条件的执行。当指令的执行条件满足时,指令被执行,否则指令被忽略。

每一条ARM指令包含4位的条件码,位于指令的最搞4位【31:28】。条件码共有16种,每条条形码可用两个字符表示,这两个字符可以添加在指令助计符的后面和指令同时使用 。例如跳转指令B可以上后缀EQ变为BEQ表示"相等则跳转"

在这里插入图片描述

  • ARM指令及功能描述:
    在这里插入图片描述
    在这里插入图片描述

  • 代码示例:

_test:
;b指令带条件的跳转
mov x0, #0x1
mov x1, #0x1
cmp x0,x1
beq mycode  ;如果上述条件不相等,就不会跳转会按照顺序指向下列代码
mov x2, #0x2
; 如果想达到上面语句和mycode:后面代码只执行其中一行时,可以选择在改行加上 ret 语句
mycode:
mov x3, #0x3 ;条件不想等时,也回执行到这句代码
ret

在这里插入图片描述
在这里插入图片描述

2.6 bl

  • bl:带返回的跳转指令,执行的操作:**将下一条指令的地址存储到lr(x30)寄存器中,跳转到标记处开始执行代码,当跳转函数执行完时(执行到ret语句时),会将lr(x30)寄存器中存储的地址值赋值给pc寄存器,达到了返回的目的 ** 有点类似函数调用
    • 指令格式:BL {条件} 目标地址
    • BL是另一个跳转指令,单是跳转之前,会在寄存器R14中保存PC当前的内容,因此可以通过将R14的内容重新加载到PC中,来返回到跳转指令之后的那个指令执行处。该指令是实现子程序调用的一个基本但常用的手段。
    • BL Lable:当程序无条件跳转到标号Lable处执行时,同时将当前的PC值保存到R14中

在这里插入图片描述
执行si直接跳转到mycode
在这里插入图片描述

mycode执行完之后,在返回继续之心后面代码
在这里插入图片描述
在这里插入图片描述

编写函数调用的本质,底层汇编使用bl指令来实现

3. 内存操作

3.1 寻址方式

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.2 load,从内存中读取数据

3.2.1 ldr

  • ldr(从内存中读取数据)
    • 格式:LDR{条件} 目的寄存器, <储存器地址>
    • LDR指令用于从储存器中将一个32位的字数据传送到目的寄存器中。该指令通常用于从储存器中读取32位的字数据到通用寄存器,然后对数据进行处理。
    • 指令示例:

在这里插入图片描述

  • 代码示例:
.text
.global _test

_test:
; ldr指令: 从内存冲读取数据
ldr x0, [x1] ;找到x1寄存器中存储的那个地址值对应的那块内存
ret
  • ldr x0, [x1]:从x1寄存器的地址开始读取8个字节数据到 x0
  • ldr w0 , [x1]:从x1寄存器的地址读取4个字节数据到w0

在这里插入图片描述
在这里插入图片描述

  • 从上述断点结果来看, 我们可以发现x0的值和x1的值不相等, 造成这个结果的原因是,因为ldr x0, [x1]是从x1的地址往后读取8个字节的内容到x0,但是变量a只占4个字节,所以还多读了4个字节的内容

3.2.2 ldur

  • 相同点:和ldr指令作用类似,都是取内存中的数据放到某个寄存器中
  • 区别: ldur:一般用于负数,ldr一般用于正数
  • 下图的用法可以参考上面的 寻址方式

在这里插入图片描述

3.2.3 ldp

  • ldp : 从内存中读取数据放到一对寄存器中
  • 示例代码:
ldp w0, w1, [x2, #0x10]

首先从[x2, #0x10]得到的新地址中,往高字节(高地址方向)读取4个字节赋值给w0,然后再接着读取4个字节赋值给w1
在这里插入图片描述

3.2 store往内存中写入数据(str、stur、stp)

  • str:往内存中写入数据str w0, [x1] : 把w0寄存器的值写入x1中
  • stur: 往内存中写入负数,一般用于便宜量是负数
  • stp:成对写入数据stp w0, w1 [x1]: 先把w0数据写入x1中,然后在将w1中内容写入x1中

在这里插入图片描述

  • 零寄存区器: 里面存储的值是0
    • wzr(32bit)
    • xzr(64bit)

想要把某个内存地址的值清零,可以直接使用str指令把wzrxzr寄存器值写入内存地址,因为wzrxzr的寄存器的储存的值就是0
在这里插入图片描述
断点调试如下:
在这里插入图片描述

3.3 pc、lr

  • pc: 程序计数器,用来记录CPU当前指令是那一条指令,存储当前cpu正在执行指令的地址,类似于8086汇编的ip寄存器

在这里插入图片描述

  • lr:链接寄存器,其实也是x30寄存器,存储着函数的返回地址
    在这里插入图片描述

在这里插入图片描述

我们可以看到当test函数执行完成之后,执行下一句代码的时候,我们可以看到lr寄存器存储的地址就是这句代码的地址,所以lr寄存器存储的是函数的返回的地址

4. 函数的堆栈

  • 把C语言文件转换成汇编文件:xcrun -sdk iphones clang -arch arm64 -S C语言文件 -o 输出文件,如果不写-o也会自动输出
    在这里插入图片描述

4.1 叶子函数(内部不会调用其他函数的函数)

  • 叶子函数分析
void haha()
{
    int a = 2;
    int b = 3;
}

//对应的核心汇编源码
	
	
	//核心代码
	//sp指针-16 
	sub	sp, sp, #16             ; =16
	
	.cfi_def_cfa_offset 16
	mov	w8, #2 // 把立即数2赋值给w8寄存器
	str	w8, [sp, #12] // 把w8寄存器的值, 写入到 【sp + 12】的内存空间中
	mov	w8, #3  // 把立即数3赋值给w8寄存器
	str	w8, [sp, #8] // 把w8寄存器的值, 写入到 【sp + 8】的内存空间中

	add	sp, sp, #16             ; =16 // 当函数之心完成时,需要把sp指针(寄存器)恢复到初始化位置, 这样就完成函数执行过程中的内存分配, 其次我们需要知道, 我们只是把sp指针的位置回复到初始位置,并没有清楚那段内存空间的数据, 这些数据被称为垃圾数据, 等下次其它函数的数据覆盖掉这些内存空间
	
	ret

在这里插入图片描述

4.2 非叶子函数

void haha()
{
    int a = 2;
    int b = 3;
}

void hehe()
{
    int a = 4;
    int b = 5;
    haha();
}


对应的转换核心汇编代码

	//hehe函数的汇编代码 
	sub	sp, sp, #32             ; =32
	stp	x29, x30, [sp, #16]     ; 16-byte Folded Spill. //存储fp(x29)、lr(x30)的值
	add	x29, sp, #16            ; =16

	mov	w8, #4
	stur	w8, [x29, #-4] //写入数据, 移动fp指针
	mov	w8, #5
	str	w8, [sp, #8]  // 写入数据,移动 sp指针
	bl	_haha //跳转到haha 函数
	
	ldp	x29, x30, [sp, #16]     ; 16-byte Folded Reload // 读取 fp、lr的值
	add	sp, sp, #32             ; =32 // 回复sp指针的位置 
	ret //返回

在这里插入图片描述

非叶子函数不仅需要开辟空间储存数据, 还需要开辟空间储存 fp(x29)、lr(x30)

4.3 sp、fp 堆栈指针

  • sp(Stack Pointer): 通过sp指针(寄存器)指定一块存储空间给函数使用
  • fp (Frame Pointer):也就是x29寄存器

5 实战

5.1 破解(Mac程序)命令行工具

  • 首先创建一个mac命令行项目,编写一段简单的命令行测试代码:
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int password = 0;
        while (password != 123456) {
            printf("请输入正确的密码:");
            scanf("%d",&password);
        }
        printf("密码输入正确,欢迎使用XX管理系统!\\n");
    }
    return 0;
}

  • 然后编译,在找到Products下的可执行文件,拷贝到一个路径下,并使用终端执行该文件

在这里插入图片描述

  • 接下来我们使用hopper工具来分析该可执行文件

在这里插入图片描述

我们分析汇编代码, 我们可以通过hopper去修改汇编代码, 这里是去掉while循环的代码
选中汇编代码

在这里插入图片描述

修改之后的汇编代码:在这里插入图片描述

然后再使用hopper工具导出新的可执行文件工具, 在重新执行可执行文件就可以看到破解效果
在这里插入图片描述

5.2 破解iphone程序

破解iPhone流程和破解Mac上程序过程类似, 首先需要分析代码, 然后直接修改可执行文件,或则通过tweak代码来达到修改的目的, 最终把修改好的可执行文件直接替换到手机中该可执行文件即可

6. 问题

  • 如果返回值超过x0寄存器的大小,怎么处理?
    • 如果返回值很大,可能会将返回值放到堆栈, 然后把内存地址赋值到x0上返回
  • x0寄存器又当参数,又是返回值,会不会乱套?
    • 不会,因为操作的时间是分开的,进入函数的时候是参数,返回的时候是返回值
  • 所有的调用方法在汇编中都已下划线_开头的,如果不写_,则表示不是公开的函数,如果是公开的函数一定需要_

以上是关于汇编语言笔记-ARM架构基本寄存器的主要内容,如果未能解决你的问题,请参考以下文章

ARM处理器

优化系列汇编优化技术:ARM架构64位(AARCH64)汇编优化及demo

优化系列汇编优化技术:ARM架构内联汇编优化及demo

ARM 架构

Arm汇编学习总结

ARM架构获取精确时间的方法