美文网首页
4.状态寄存器&代码还原

4.状态寄存器&代码还原

作者: _顺_1896 | 来源:发表于2018-04-27 14:32 被阅读14次

[TOC]

回顾-函数

指令:bl、ret

指令:pc、lr、sp

栈:函数开辟

​ 存放局部变量、参数,寄存器保护;

函数嵌套:

​ funcA -> funcB -> funcA:每次进入一个函数都会开辟一片栈空间;第二次进入funcA函数依然会开辟新的栈空间,即高级代码只有一份,可以复用,但内存不能复用,需要重新开辟;

​ 死递归 ==》内存溢出

参数:存放位置、大小,是否入栈

状态寄存器(标记寄存器)

cpsr : current program status register,按位起作用,记录特定信息。

cpsr对if的作用:可以改变z为来控制if的比较结果;

cpsr是32位的寄存器,包含4个字节,高4位低8位有特定含义,其余中间位为预留位。

​ 其中低8位(包括I、F、T和M[4:0],称为控制位)由系统控制,除非cpu运行在特权模式下。

​ 高4位(31-28)组成CPSR,条件码标记位,这4位可以由算数或逻辑运算结果所改变,可以决定某条指令是否执行,意义重大!

内嵌(对外联)汇编代码:

// N位
void funcB() {
    asm (
         "mov w0, #0xffffffff\n"
         "adds w0, w0, #0x0\n" // 需要改变cpsr寄存器需要在运算指令后面加上s,如adds
         // 用0x7fffffff替代0x0fffffff,然后add 0x0后N位还为0,因为7f在有符号数据中依然为正数
         //"adds w0, w0, w0\n" // w0=w0+w0, ==> w0=w0*2 ==> w0=w0<<1,即进行左移
    );
}

CPSR

N(Negative)

第31位,符号标志位,记录指令执行后,得到的结果是否为负数,是负数置为1,否则置为0;

Z(Zero)

第30位,0标记位,标记相关指令执行后其结果是否为0 ,为0标记位1,否则标记位0;

C(Carry)

第29位,进位标志位,一般情况下,进行无符号数的运算。

  1. 加法时,有进位时C=1,否则C=0;
  2. 减法时(含CMP,compare),有借位时C=0,否则C=1;

​ <u>Notes:不进不借,C位不变,保存上一次,运算时不区分有无符号,结果再进行区分</u>

​ 加法:重复进行0xaaaaaaaa + 0xaaaaaaaa时, 0xa可转为二进制时1010,所以在相加时(此时可视为左移)时会有进位的现象,C位在1和0之间进行切换。如:

mov w0,#0xaaaaaaaa;0xa 的二进制是 1010
adds w0,w0,w0; 执行后 相当于 1010 << 1 进位1(无符号溢出) 所以C标记 为 1
adds w0,w0,w0; 执行后 相当于 0101 << 1 进位0(无符号没溢出) 所以C标记 为 0
adds w0,w0,w0; 重复上面操作
adds w0,w0,w0

​ 减法:做减法时0x00000000 - 0x000000ff,产生借位,借位后,相当于计算0x100000000 - 0x000000ff=0xffffff01。由于借了一位,所以C位 用来标记借位。如:

mov w0,#0x0
subs w0,w0,#0xff ;
subs w0,w0,#0xff
subs w0,w0,#0xff
负数计算公式

负数 :将无符号数转换成有符号数。计算机表示有符号数的正负时,数的二进制都采用相同表示,如1111 1111,无符号时=255,有符号时-1。运算过程:将1111 1111取反+1。而0x80=-128/128

计算公式:取反+1。包括10进制负数---->二进制,二进制——>10进制负数

2到10,包括最高位(符号)一起取反,然后取反后+1

10到2,先求绝对值部分的2进制,对结果取反,再+1;

负数的补码就是对反码加1,而正数不变,正数的原码反码补码是一样的


如:-42(10进制) = d6(16) 
42 = 0010 1010 -----> 1101 0101 -- +1 --> = 1101 0110 = d6(16)

如:0xa8(16) = -58(16) = -88(10)
a8 = 1010 1000 -----> 0101 0111 -- +1 --> = 0101 1000 = -58(16)

将a8转成-58的含义?为了做加法?,内存不认识-58的负号

V(Overflow)

第28位是V,溢出标志位。在进行有符号数运算的时候,如果超过了机器所能标识的范围,称为溢出。

  • 正数 + 正数 为负数 溢出
  • 负数 + 负数 为正数 溢出
  • 正数 + 负数 不可能溢出

内存分区

分区:

​ 代码区:可读可写可执行

​ 栈区域:放参数和局部变量

​ 堆区域: 动态申请,可读可写

​ 全局: 可读可写(在传递时,传递的是地址)

​ 常量: 只读(有编译器规定)

adrp

adrp = address page

寻找常量和全局变量的指令,如:

adrp   x0, 2 // 2可以为其他数N
add    x0, x0, #0xd10  // 0xd10 可以为其他任意值0x***

解释如下:
adrp   x0, 2
1.将2的值,左移12位 2 0000 0000 0000 == 0x1000
2.将PC寄存器的低12位清零 0x1000ba89c  ==> 0x1000ba000
3.将将1 和 2 的结果相加  给 X0 寄存器!!
4. 1. + 2. => 0x1000bcd10
 
快捷方式:倒数4位加上N,后三位替换为0x***

地址0x1000bcd10即为所要找的常量或全局变量的地址
正向开发时,此时的0x1000bcd10是指向一个int的正整数的全局变量,使用p *(int *)0x1000bcd10 可以将其存放的值打印出来,使用*(int *)将地址转换为指定的类型;如果是char类型,使用char *。参见视频26’’。

左移12位,是寻找偏移地址,12位的寻址能力为212=22^10 = 4KB的范围,4KB(12位)为编译器决定的*

​ 这些的偏移量都是在编译阶段确定的,游戏外挂等有可能采用这种方法。

还原高级代码

IDA操作

  1. IDA在加载Mach-O时Ready按钮变绿色时,表示加载分析完成。
  2. 窗口左侧时所有方法的列表;
  3. 选择某个方法双击进入方法窗口;
  4. 右击进入的方法窗口选择Text View进入全屏显示;
  5. 第4可以用点击后按空格替换;
  6. var_4、var_8为IDA提供的方便变量;
  7. 汇编中可以使用双击进行方法的跳转;

还原代码

​ 如何判断是全局还是常量?暂时将全局视为带有“_”;

​ 函数是否有返回值:如果有返回值,在调用ret指令会对x0寄存器进行操作;否则不会。

​ 传入参数类型:如果有参数传入,函数进入时会对x0、x1…进行数据保护,进行入栈或者,直接对x0,x1赋予立即数。另外在调用前的上一级函数也会对x0,x1进行操作。

​ 在优化高级代码时,从后到前,反向优化;

​ 在验证环节,可以将还原回来的高级代码重新生成mach-o文件然后在反汇编,得到汇编代码进行对比。

相关文章

网友评论

      本文标题:4.状态寄存器&代码还原

      本文链接:https://www.haomeiwen.com/subject/vcwblftx.html