IDA PRO 实践05 - 标志寄存器

113 阅读9分钟

处理器执行一个操作数为 2 个寄存器的指令时,无法判别这是有符号还是无符号数的运算。读者会知道是因为可以从后面那个条件跳转指令来判断。所以处理器同时考虑这 2 种情况,并且触发对应的标志。

使用 IDA 调试 crackme.exe 程序,运行到程序入口暂停。

如果使用 keypatch 修改指令后,发现汇编指令变成  db 形式了,记得先把修改的那段 Undefine,然后再转成 Code。

CF(CARRY FLAG)

测试一

随便可以触发到的地方打个断点,让程序运行到断点处。

使用 Keypatch 插件将第一句指令改为 ADD EAX, 1

在 EAX 寄存器右键单击,选择 MODIFY VALUE,输入 0xffffffff。

按 F8 键单步执行,观察 CF 标志位变化。可以看到计算结果超过了数值上限,因而触发了 CF 标志。

测试二

再将下一条指令改为 SUB EAX, EDX

如果两个无符号数相减结果为负这种错误,同样也会触发 CF。修改寄存器使得 EAX=0x25,EDX=0x40,同样可以看到,CF 还是为 1。

测试三

将下一条指令修改为 SUB EAX, EDX 这条指令,并设置 EAX=0x100,EDX=0x40。

按 F8 键单步执行。结果没有触发 CF 标志。

那么结论是:

在 ARM64 架构中,CF(Carry Flag,进位标志)是一个用于表示算术运算结果的状态标志。CF 的主要用途包括:

  1. 加法运算:如果在加法运算中发生了进位(即结果超出了寄存器的位数),CF 会被设置为 1;如果没有进位,CF 会被清零(0)。
  2. 减法运算:在减法运算中,CF 表示是否发生了借位。如果发生借位,CF 会被设置为 1;否则为 0。
  3. 无符号比较:在进行无符号整数比较时,CF 用于指示一个值是否小于另一个值。例如,使用 CMP 指令进行比较时,如果第一个操作数小于第二个操作数,CF 会被设置为 1。

CF 在许多指令中都会受到影响,包括加法、减法、位操作等,通常用于控制条件分支和处理复杂的算术逻辑。

OF(OVERFLOW FLAG)

OF 标志类似于 CF,但它是针对有符号数的。

测试一

将下一条指令改为 ADD EAX,1 指令,并使 EAX=0x7fffffff。

按 F8 键单步执行。

因为最大正数 0x7fffffff 加上 1 之后变成 0x80000000 是一个负数,产生了错误,所以触发了 OF。

测试二

同样再将 EAX(0x80000000)减去 EDX(0x40)的值。

按 F8 键执行,再次触发 OF,因为最小负数 0x80000000 减去 0x40 之后结果是一个很大正数,所以产生了错误。

OF 标志位的设置规则:

  1. 加法运算

    • 当两个有符号数相加的结果超出了其表示范围时,OF 会被设置为 1。
    • 例如,对于 8 位有符号数,最大值是 127(0x7F),如果计算 100 + 50,结果是 150,但 150 超出了 8 位有符号数的范围,因此 OF 被设置为 1。
  2. 减法运算

    • 当从一个有符号数中减去另一个有符号数时,如果结果超出了其表示范围,OF 也会被设置为 1。
    • 例如,对于 8 位有符号数,计算 100 - 50 时,结果为 -150,超出了范围,因此 OF 被设置为 1。

OF 的常见用途:

  • 条件跳转:在执行算术运算后,可以根据 OF 的值来决定是否进行条件跳转。例如,如果在加法操作后 OF 被设置,可以跳转到处理溢出的代码块。
  • 错误检测:通过检查 OF 标志位,可以检测是否发生了溢出,这在处理有符号数时非常重要。

SF(SIGNED FLAG)

SF 标志比较简单。任何操作的结果如果是一个负数就会触发 SF。SF 只表示结果的符号,并不表示结果是对还是错。

例子一

0x8000000+0x1 结果是一个负数,结果是 0x8000001,并且触发 SF。

CF 和 OF 都没有触发,因为无论是有符号或者无符号,计算的结果都是正确的。

ZF(ZERO FLAG)

ZF 在以下条件下将会触发:在内部实际是减法的比较指令中,两个操作数相同;增或减导致结果为 0;相减结果为 0。

例子一

下一条指令改为 ADD EAX, 1 ,将 EAX 的值改为 0xffffffff,再加上 1 会发生什么。

触发了 ZF,因为结果是 0。不考虑符号运算超过数值上限触发了 CF。考虑符号-1 + 1 = 0 没有错误,所以没有触发 OF。结果 0 是个非负数,所以 SF 也没有触发。

ZF(Zero Flag,零标志位)是计算机体系结构中的一个状态标志,用于指示算术或逻辑运算的结果是否为零。ZF 的设置规则和用途如下:

ZF 标志位的设置规则:

  1. 算术运算

    • 当执行加法、减法等算术运算后,如果结果为零,则 ZF 会被设置为 1;如果结果不为零,ZF 会被清零(0)。
    • 例如,执行 ADD EAX, EBX 后,如果 EAXEBX 的和为 0,则 ZF 被设置为 1。
  2. 比较运算

    • 如果两个操作数相等,ZF 设置为 1。
    • 如果不相等,ZF 设置为 0。
    • 在执行比较指令(如 CMP)时,ZF 会反映比较的结果:
  3. 逻辑运算

    • 对于逻辑运算(如 ANDOR),如果结果为零,ZF 也会被设置为 1。

ZF 的常见用途:

  • 条件跳转:ZF 常用于控制流,程序可以根据 ZF 的值来决定是否进行条件跳转。例如,JE(Jump if Equal)指令在 ZF 设置为 1 时跳转。
  • 错误检测:在执行某些运算后,可以使用 ZF 来检测是否得到了预期的结果,尤其是在处理状态或结果时。

条件跳转与标志位

再介绍一下条件跳转。

例子一

使用 keypatch 写两条指令:

SUB EAX, EDX

JB 0x401018

结果如下:

CODE:0040110C sub     eax, edxCODE:0040110E nopCODE:0040110F nopCODE:00401110 nopCODE:00401111 jb      loc_401018

因为指令是变长的,所以中间补了一些 nop 指令。

设置 EAX=0x40,EDX=0x2,然后按 F8 键执行 SUB 指令。

因为 EAX 的值比 EDX 的值大,所以红色的箭头会闪烁,没有执行跳转。

上面是 SUB 执行后的标志寄存器状态。JB 是一个无符号条件跳转指令,如果触发 CF 标志,跳转将会执行。由于指令对两个正数进行操作,结果也是正数,这表示第一个数大于第二个数,所以没有触发 CF 标志,也没有引发跳转。

例子二

将 EAX 设置为 0x40,EDX 设置为 0x80,然后再执行这条减法指令。

由于 EAX 小于 EDX,程序会沿着绿色箭头执行跳转。

由于 JB 是否执行取决于 CF 标志,因为这两个无符号数相减的结果是个负数产生了错误,导致 CF 触发,所以跳转被执行。SF 也被触发,因为结果是个负数。而 OF 没有触发,因为作为有符号数 0x40 – 0x80 的结果就是负数,结果是正确的。

例子三

现在将 JB 改成 JL。

执行 SUB 之后,程序跟着绿色箭头执行跳转,因为第一个数比第二个数小(less),但是决定 JL 的是哪一个标志呢。

当 SF 不等于 OF 的时候执行 JL 跳转。

本例中 SF=1,OF=0 所以执行了跳转。

因为 SUB 与 CMP 指令类似,除了它会保存计算的结果,只要第一个数比第二个数小(less)就会触发 SF 执行跳转。

这部分内容的目的是作者不希望读者完全通过标志来判断条件跳转。还是要理解它本身的含义。也就是当 2 个操作数相同,JZ 指令会执行跳转。如果第一个无符号操作数比第二个小,JB 指令会执行跳转。如果第一个有符号操作数比第二个小,JL 指令会执行跳转。一般只需要看无符号条件跳转和有符号条件跳转表格就行。当然深入研究也没有问题。

条件跳转

在 x86 汇编语言中,条件跳转指令用于根据某个条件的真值来决定程序的控制流。以下是常见的条件跳转指令及其对应的条件:

指令条件描述
JE (Jump if Equal)ZF = 1如果两个值相等,跳转
JNE (Jump if Not Equal)ZF = 0如果两个值不相等,跳转
JG (Jump if Greater)ZF = 0 and SF = 0如果第一个值大于第二个值
JGE (Jump if Greater or Equal)SF = 0如果第一个值大于或等于第二个值
JL (Jump if Less)SF ≠ OF如果第一个值小于第二个值
JLE (Jump if Less or Equal)ZF = 1 or SF ≠ OF如果第一个值小于或等于第二个值
JA (Jump if Above)ZF = 0 and CF = 0对于无符号数,如果第一个值大于第二个值
JAE (Jump if Above or Equal)CF = 0对于无符号数,如果第一个值大于或等于第二个值
JB (Jump if Below)CF = 1对于无符号数,如果第一个值小于第二个值
JBE (Jump if Below or Equal)ZF = 1 or CF = 1对于无符号数,如果第一个值小于或等于第二个值
JO (Jump if Overflow)OF = 1如果发生了溢出,跳转
JNO (Jump if Not Overflow)OF = 0如果没有发生溢出,跳转
JS (Jump if Sign)SF = 1如果结果为负,跳转
JNS (Jump if Not Sign)SF = 0如果结果为非负,跳转

关注我的微信公众号:二手的程序员。

20200925-101923-883e.gif