x86 反汇编/循环
为了完成重复的任务,程序员通常会实现循环。循环有很多种类,但它们在汇编代码中都可以简化为几个相似的格式。本章将讨论循环,如何识别它们,以及如何将它们“反编译”回高级表示。
看起来这部分会首先考虑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循环看起来几乎和Do-While循环一样简单,但实际上它们并不简单。让我们检查一个通用的 while 循环
while(x)
{
//loop body
}
这个循环做什么?首先,循环检查以确保 x 为真。如果 x 不为真,则跳过循环。然后执行循环体,然后进行另一次检查:x 仍然为真吗?如果 x 仍然为真,执行跳转回循环顶部,继续执行。请记住,在循环底部需要有一个跳转(返回循环顶部),但在条件被发现为假的情况下,跳回顶部、重新测试条件,然后再跳回循环底部是没有意义的。while 循环然后执行以下步骤
- 检查条件。如果为假,则转到末尾
- 执行循环体
- 检查条件,如果为真,则跳转到步骤 2。
- 如果条件不为真,则从循环末尾穿透。
以下是在 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 循环?从本质上讲,它是一个带有初始状态、条件和迭代指令的 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
{
//loop body
} until(x);
它本质上变成了以下 Do-While 循环
do
{
//loop body
} while(!x);
与 Do-Until 循环一样,标准的 Until 循环看起来像这样
until(x)
{
//loop body
}
它(同样)被翻译成以下 While 循环
while(!x)
{
//loop body
}
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:
请注意,这里很多比较是不必要的,因为条件是一个常量。大多数编译器会优化这种情况。