x86 sbb与第一个和第二个操作数

时间:2016-12-08 04:41:13

标签: assembly x86 intel flags cpu-registers

我正在分析一系列x86指令,并与以下代码混淆:

135328495: sbb edx, edx
135328497: neg edx
135328499: test edx, edx
135328503: jz 0x810f31c

我理解sbb等于des = des - (src + CF),换句话说,第一条指令以某种方式将-CF放入edx。然后negtive -CF进入CFtest CF是否等于零?

但请注意,jz会检查标记ZF,而不是CF!那么基本上上面的代码序列是什么尝试呢?这是一个合法的x86指令序列,由g++版本4.6.3生成。

C++代码实际上来自botan项目。您可以在here找到整体汇编代码(Botan RSA解密示例)。在反汇编代码中有很多这样的指令序列。

2 个答案:

答案 0 :(得分:5)

sbb edx, edx

您对此说明的分析是正确的。 SBB表示“借用减法”。它以一种将进位标志(CF)考虑在内的方式从目的地中减去来源。

因此,它相当于dst = dst - (src + CF),因此这是edx = edx - (edx + CF),或只是edx = -CF

不要让你欺骗源头和目标操作数都在这里edxSBB same, same是编译器生成的代码中非常常见的习惯用法,用于隔离进位标志(CF),尤其是当它们试图生成无分支代码时。还有其他方法可以做到这一点,即SETC指令,在大多数x86架构中可能更快(请参阅注释以获得更彻底的解剖),但不是很多。来自不同供应商(甚至可能是不同版本)的编译器往往倾向于选择其中一个,并且在您不进行特定于体系结构的调优时随处使用。

neg edx

同样,您对此说明的分析是正确的。这是一个非常简单的。 NEG对其操作数执行二进制补码。因此,这只是edx = -edx

在这种情况下,我们知道edx最初包含-CF,这意味着其初始值为0-1(因为CF是始终为0或1,开或关)。否定它意味着edx现在包含01

也就是说,如果CF 最初设置,edx现在将包含1;否则,它将包含0。这实际上是上面讨论的习语的完成;您需要NEG来完全隔离CF的值。

test edx, edx

TEST指令与AND指令相同,只是它不影响目标操作数 - 它只设置标志。

但这是另一个特例。 TEST same, samea standard idiom in optimized code来有效地确定寄存器中的值是否为0.您可以编写CMP edx, 0,这是人类程序员天真的做法,但test更快。 (为什么这样做?由于按位AND的真值表。value & value == 0value为0时的唯一情况。)

所以这具有设置标志的效果。具体来说,如果ZF为0,则设置零标记(edx),如果edx非零,则将其清除。

因此,如果CF 最初设置,ZF现在将会清除;否则,它将被设置。也许更简单的方法是将这三条指令设置为ZFCF的原始值相反。

以下是两种可能的数据流:

  • CF == 0→edx = 0→edx = 0ZF = 1
  • CF == 1→edx = -1→edx = 1ZF = 0
jz 0x810f31c

最后,这是一个基于ZF值的条件跳转。如果设置了ZF,则跳转到0x810f31c;否则,它会进入下一条指令。

将所有内容放在一起,然后,此代码通过涉及零标志(CF)的间接路由测试进位标志(ZF)的补码。如果进位标志最初是清除的,它会分支,如果最初设置了进位标志,它就会失效。

这就是它的工作原理。也就是说,我无法解释为什么编译器选择以这种方式生成代码。它似乎在许多级别上都是次优的。最明显的是,编译器可以简单地发出JNC指令(如果不携带则跳转)。虽然Peter Cordes和我在评论中做了各种其他观察和推测,但我认为将所有这些都纳入答案是没有意义的,除非可以提供有关此代码来源的更多信息。

答案 1 :(得分:2)

  

我知道sbb等于des = des - (src + CF),换句话说,第一条指令以某种方式将-CF放入edx。

是的,edx = edx - (edx + CF) = -CF。因此,sbb edx,edx会在CF = 0时将edx设置为0,并在CF = 1时设置为-10xFFFFFFFF)。此外,减法本身会产生新的CF值,如果我没有太多困惑,它等于旧值。

  

然后它将-CF否定为CF,并测试CF是否等于零??

差不多但是没有。它否定edx,而不是CF.为了否定CF,有单独的指令CMC(来自stc/clc/cmc进位标志修改指令系列)。

因此,从0 / -1开始,edx将被修改为0/1,CF将再次设置为0/1(哇,我不知道neg将CF设置为~ZF)。此外neg已经设置了ZF,因此以下test edx,edx是多余的。

test edx,edx不测试CF,而是测试edx(此时为01),它将产生CF = 0和ZF = 1/0的0 / 1值。

所以你坚持认为edx中的数值来自CF,你一直在想CF,但实际上从第一个sbb开始,你就忘记了旧CF ,每个下一条指令(包括sbb)都是算术运算,因此它会以自己的方式修改CF.但是那些neg/test指令edx集中在注册号码上,CF只是他们计算的副产品。

  

但请注意,jz会检查标志ZF,而不是CF!

确实,因为CF在0之后确实包含test,与sbb之前的初始CF值完全无关。另一方面,ZF与原始CF值直接相关,如果代码以CF = 1开始,那么最后jz将不被采用(ZF = 0),如果代码在CF = 0下开始,最后jz将被采用(ZF = 1)。