ARM汇编
C位 carrcyr condition标志位,用于比较无符号数的大小
Arm汇编条件跳转表,指令是否执行判断取决于指令的第一字节前四位,分为下面的16中情况
这里对比x86的jcc,分析arm的条件跳转,关于ZF位的使用,和x86一样,都是结果为零则置zf为1,但是CF位的处理和x86有所不同
先看加法指令adds(注意,add指令不影响标志位,只有在指令后加s,才代表指令影响标志位,对应的指令的第9位要置1,比如sub不影响标志位,subs则影响)
adds r0,r0,r1 将r0和r1的值加到r0中,这里会产生溢出,并且结果为0,所以zf和cf都会被置1,这和x86没有什么不同
这是因为在arm汇编中,当执行到sub指令时,会先将cf置1,如果发生了溢出,就会将cf置0,这里和x86里相反
当读取PC寄存器时,如果当前CPU模式为ARM模式,会写入PC+8的值
这里有一种情况,就是如果我的下一条指令是跳转,那么我存储的还是下下条指令的地址吗?
这里读取的就是下两条指令的前一条指令,就是下一条指令的硬编码
但是这里实际读取时,读取出来的是一个固定的值0xE7F001F0
那么当我们执行到下一条语句时,这条语句应该被修改了为一个能够触发断点异常的值,
在x86中,这个值是0xCC,在arm汇编中,这个值是0xE7F001F0
3.mov register,register_SHFITED
把16位的立即数分为两部分,中间四位为寄存器比俺还,其余位数固定
可以看到,立即数的位数最多为16位,也就是最多表示2^16次方的数字,这可能远远不够
即0010循环右移3 * 2,即6位,就可以得到0x8000000
但是这种只适用于部分数字,因为有些数字只靠8位和循环移位是表示不出来的
所以如果要表示类似于0x12345678这样的数,可以使用两个mov指令完成
这里可能看不出来,因为这里ida把两条指令默认合成了一条指令
注意,要先写低字节,再写高字节,因为写低字节会自动清零高字节
先来看最简单的版本,12~15位存储目标寄存器,低四位存放源寄存器
7~11位为移位的位数,stype有四种,逻辑右移 逻辑左移 算术右移和循环移位
3.mov register,register_SHFITED
1.ADD register,register,immedidate
这里的立即数和之前的A1格式是一样的,也是采用循环右移的格式,这种格式叫做A32ExpandImm
2.ADD register,register,register
3.ADD register,register,register-shifted
偏移为立即数,U为代表偏移的正负,1为正,0为负,P位为1代表外偏移,0代表内偏移,W代表有无附加行为
ldr r0,[r1],读取寄存器R1地址处的值到R0中,这是偏移为0的情况,且没有附加条件
ldr r0,[r1,4],读取寄存器R1地址偏移+4处的值到R0中,无附加条件
ldr r0,[r1,#-4],读取寄存器R1地址偏移-4处的值到R0中,无附加条件
ldr r0,[r1,4]! 读取寄存器R1地址偏移+4处的值到R0中,R1寄存器加4
ldr r0,[r1],4,读取寄存器R1地址偏移处的值到R0中,R1寄存器加4
注意,上面两条指令都对r1寄存器+4,但是区别是内偏移读取的是加完后的地址处的值,外偏移是加之前的值
由于ldr和str指令可以对任意寄存器进行读写,所以可以用ldr和str实现push和pop
IA是从当前的指针开始读取,读取次,每次读一个数据,指针向高地址移动,读取的次数取决于参数2的寄存器的个数,如果加上!,代表读取完之后指针也跟着移动
上面讲了用LDR和STR实现push和pop,同样的,作为访存指令,LDM和STM也可以实现
push指令,先将栈指针-4,然后写入数据,要写入SP寄存器指向的值
输入一个地址,会计算出距离当前PC的偏移,如果是arm模式,会将偏移除4,然后写入硬编码
(0x001360DC - 0x001360C4) / 4 = 4
这里把下一条指令的地址写入了LR寄存器,这里写入的值和当前的模式有关,如果当前模式为arm模式,就直接写入,如果是thumb模式,则会把这个地址 or 1,这里到后面还会提到
BX是可以修改模式的,这里跳转的地址为0x10465,实际是0x10464 | 1,表示跳转的同时切换到thumb模式,相当于把1写入了T标志位
和BL指令不同的是BL会根据地址的值判断是否进行模式切换,BLX则是直接切换,如果当前为arm,则切换为thumb,如果为thumb,就切换为arm,并且写入LR寄存器
BLX reg和BX指令类似,根据寄存器内的值(最后一位)判断是否进行模式切换,但是BLX会写入LR寄存器,BX不会
当调用函数返回时,如何判断返回处是thumb模式还是arm模式呢?
通过LR寄存器,LR寄存器存储的是先前的指令下一条指令地址,根据存放的地址最后一位,判断是否进行模式切换
还有两种修改PC的方法,一种是直接写PC,另一种是使用访存指令
和BX指令,类似,可以通过寄存器的值判断是否切换模式,通常使用这条指令来调用函数
thumb模式和arm模式最大的区别就是变长指令,指令分为双字节和四字节
//运算指令优先对于第一,第二操作数相同情况会生成短指令编码
在thumb模式下,双字节的指令比四字节指令多,四字节指令大部分都会在指令后加上W表示四字节,但是B这种无条件跳转指令就不会
如果第一操作数和第二操作数相同,则会合并前两个操作数,并且编为短指令
第二条指令把R2和R0的合放到R0里,由于前两个操作数相同,所以合并为一个,并且编为短指令
第三条指令和第一条指令的不同点是影响标志位,影响标志位会被编为短指令
在thumb模式中,一般使用R0~R7寄存器,不使用R8~R12寄存器
可以看到,上面的指令中,及时ADDS指令影响标志位,但是因为使用了R8寄存器,还是编为了长指令
可以看到,短指令编码时,留给寄存器的位数是3位,最多有8种可能,所以只能是R0~R7,如果是R8~R12,只能使用长指令编码
这条指令是thumb模式中特有的指令,执行到IT语句时,根据IT后面的条件判断下面的指令是否执行,比如EQ判断Z标志位是否为1,如果为1,则执行MOV指令,如果为0,则不执行
现在Z标志位为0,R0为0,执行IT语句,MOV指令应该不会执行
这里Z位为1,EQ条件成立,所以条件为T的指令都会执行,所以除了R1,其他寄存器都会被写入值(这里有一个问题,其实IT只控制了三条指令,MOVS r4,#4这条指令并不受IT控制,因为I后面跟的是TET,这里是我写的有问题)
注意,无论后面的条件怎样设置,IT指令的下一条指令判断条件一定是T,和IT指令后的判断条件相同
还有,如果IT下面的指令为B指令,那么IT块影响的指令可能就会发生变化,可能就会变为跳转后地址的后续指令
指令的长度根据最后一个1来判断,如果最后一个1位于最低位,就代表长度为4,位于第一位,就代表长度为1