跳转到内容

x86 反汇编/循环

来自维基教科书,自由的教科书,为自由的世界

为了完成重复的任务,程序员通常会实现循环。循环有很多种类,但它们在汇编代码中都可以简化为几个相似的格式。本章将讨论循环,如何识别它们,以及如何将它们“反编译”回高级表示。

Do-While 循环

[编辑 | 编辑源代码]

看起来这部分会首先考虑Do-While循环,这似乎违反直觉,因为在实践中,它们可能是所有变体中最少使用的。但是,我们的疯狂是有方法的,所以请继续阅读。

考虑以下通用的 Do-While 循环

 do
 {
    action;
 } while(condition);

这个循环做什么?循环体简单地执行,条件在循环结束时进行测试,如果条件满足,则循环跳转回循环的开头。与if语句不同,Do-While条件不会反转。

现在让我们看一下以下 C 代码

 do
 {
   x++;
 } while(x != 10);

它可以被翻译成这样的汇编语言

 mov eax, $x
 beginning:
 inc eax
 cmp eax, 0x0A ;0x0A = 10
 jne beginning
 mov $x, eax

While 循环

[编辑 | 编辑源代码]

While循环看起来几乎和Do-While循环一样简单,但实际上它们并不简单。让我们检查一个通用的 while 循环

 while(x)
 {
    //loop body
 }

这个循环做什么?首先,循环检查以确保 x 为真。如果 x 不为真,则跳过循环。然后执行循环体,然后进行另一次检查:x 仍然为真吗?如果 x 仍然为真,执行跳转回循环顶部,继续执行。请记住,在循环底部需要有一个跳转(返回循环顶部),但在条件被发现为假的情况下,跳回顶部、重新测试条件,然后再跳回循环底部是没有意义的。while 循环然后执行以下步骤

  1. 检查条件。如果为假,则转到末尾
  2. 执行循环体
  3. 检查条件,如果为真,则跳转到步骤 2。
  4. 如果条件不为真,则从循环末尾穿透。

以下是在 C 代码中的 while 循环

 while(x <= 10)
 {
    x++;
 }

下面是同一个循环翻译成汇编代码

 mov eax, $x
 cmp eax, 0x0A
 jg end
 beginning:
 inc eax
 cmp eax, 0x0A
 jle beginning
 end:

如果我们将该汇编代码反翻译回 C,我们将得到以下代码

 if(x <= 10) //remember: in If statements, we reverse the condition from the asm
 {
   do
   {
     x++;
   } while(x <= 10)
 }

看到我们为什么先介绍 Do-While 循环了吗?因为当 While 循环被汇编时,它变成了 Do-While。

那么为什么跳转标签不能出现在测试之前呢?

mov eax, $x
beginning:
cmp eax, 0x0A
jg end
inc eax
jmp beginning
end:
mov $x, eax

For 循环

[编辑 | 编辑源代码]

什么是 For 循环?从本质上讲,它是一个带有初始状态、条件和迭代指令的 While 循环。例如,以下通用的 For 循环

 for(initialization; condition; increment)
 {
   action
 }

被翻译成以下伪代码 while 循环

 initialization;
 while(condition)
 {
   action;
   increment;
 }

这反过来又会被翻译成以下 Do-While 循环

 initialization;
 if(condition)
 {
    do
    {
       action;
       increment;
    } while(condition);
 }

请注意,在 for() 循环中,你通常在 A 中分配一个初始常量值(例如 x = 0),然后将该值与 B 中的另一个常量进行比较(例如 x < 10)。大多数优化编译器将能够注意到 x 第一次确实小于 10,因此不需要初始 if(B) 语句。在这种情况下,编译器将简单地生成以下序列

 initialization;
 do
 {
    action
    increment;
 } while(condition);

使代码与 while() 循环无法区分。

其他循环类型

[编辑 | 编辑源代码]

C 只有 Do-While、While 和 For 循环,但其他一些语言很可能实现了它们自己的类型。此外,一个优秀的 C 程序员可以很容易地使用一系列好的宏来“自制”一种新的循环类型,因此它们值得考虑。

Do-Until 循环

[编辑 | 编辑源代码]

一个常见的 Do-Until 循环将采用以下形式

 do
 {
   //loop body
 } until(x);

它本质上变成了以下 Do-While 循环

 do
 {
   //loop body
 } while(!x);

Until 循环

[编辑 | 编辑源代码]

与 Do-Until 循环一样,标准的 Until 循环看起来像这样

 until(x)
 {
   //loop body
 }

它(同样)被翻译成以下 While 循环

 while(!x)
 {
   //loop body
 }

Do-Forever 循环

[编辑 | 编辑源代码]

Do-Forever 循环只是一个没有限定条件的循环,它的条件始终为真。例如,以下伪代码

 doforever
 {
   //loop body
 }

将变成以下 while 循环

 while(1)
 {
   //loop body
 }

这实际上可以简化为一个简单的无条件跳转语句

 beginning:
 ;loop body
 jmp beginning

请注意,一些非优化编译器将为此生成无意义的代码

 mov ax, 1
 cmp ax, 1 
 jne loopend
 beginning:
 ;loop body
 cmp ax, 1
 je beginning
 loopend:

请注意,这里很多比较是不必要的,因为条件是一个常量。大多数编译器会优化这种情况。

华夏公益教科书