5

我正在分析一系列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++version生成4.6.3

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

4

2 回答 2

8
sbb edx, edx

您对该指令的分析是正确的。SBB意思是“借位减法”。它以一种考虑进位标志 ( CF) 的方式从目标中减去源。

因此,它等价于dst = dst - (src + CF),所以这是edx = edx - (edx + CF),或者简单地说edx = -CF

不要让它欺骗你源和目标操作数都在edx这里!SBB same, same在编译器生成的代码中隔离进位标志CF(有其他方法可以做到这一点,即SETC指令,这在大多数 x86 架构上可能更快(请参阅评论以获得更彻底的剖析),但数量不多。来自不同供应商(甚至可能是不同版本)的编译器倾向于偏爱其中一种,并且在您不进行特定于架构的调整时到处使用它。

neg edx

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

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

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

test edx, edx

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

但这是另一个特例。TEST same, same优化代码中的一个标准习惯用法,用于有效地确定寄存器中的值是否为 0。您可以编写CMP edx, 0,这是人类程序员天真地做的事情,但test速度更快。(为什么会这样?因为按位与的真值表。唯一的情况value & value == 0是何时value为 0。)

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

因此,如果CF原来是这样设置的,ZF现在就清楚了;否则,它将被设置。也许更简单的看待它的方式是这三个指令设置ZF为原始值的相反值CF

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

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

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

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

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

于 2016-12-08T11:37:30.650 回答
4

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

是的,edx = edx - (edx + CF) = -CF。所以当 CF=0 时sbb edx,edx设置edx为,当 CF=1 时设置为( )。减法本身也会产生新的 CF 值,如果我不太困惑的话,它等于旧值。0-10xFFFFFFFF

然后将-CF负入CF,并测试CF是否等于0??

几乎是但不是。它否定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 确实包含0after last test,与 before 的初始 CF 值完全无关sbb。另一方面,ZF 与原始 CF 值直接相关,如果以 CF=1 开头的代码,jz则不取最后一个(ZF=0),如果以 CF= 开头的代码0,jz将取最后一个(ZF=1)。

于 2016-12-08T11:15:09.617 回答