跳转到内容

x86 反汇编/循环示例

来自维基教科书,开放的书籍,为开放的世界

示例:识别目的

[编辑 | 编辑源代码]

这个函数的功能是什么?它接受什么类型的参数,并且它返回什么类型的结果(如果有)?

 push ebp
 mov ebp, esp
 mov esi, [ebp + 8]
 mov ebx, 0
 mov eax, 0
 mov ecx, 0
 _Label_1:
 mov ecx, [esi + ebx * 4]
 add eax, ecx
 inc ebx
 cmp ebx, 100
 jne _Label_1
 mov esp, ebp
 pop ebp
 ret 4

该函数循环遍历一个由 esi 指向的 4 字节整数数组,并将每个条目相加。它在 eax 中返回总和。唯一的参数(位于 [ebp + 8] 中)是指向整数数组的指针。ebx 和 100 之间的比较表明输入数组包含 100 个条目。指针偏移量 [esi + ebx * 4] 表明数组中的每个条目都是 4 字节宽的。

示例:完整的 C 原型

[编辑 | 编辑源代码]

这个函数的 C 原型是什么?确保包含参数、返回值和调用约定。

 push ebp
 mov ebp, esp
 mov esi, [ebp + 8]
 mov ebx, 0
 mov eax, 0
 mov ecx, 0
 _Label_1:
 mov ecx, [esi + ebx * 4]
 add eax, ecx
 inc ebx
 cmp ebx, 100
 jne _Label_1
 mov esp, ebp
 pop ebp
 ret 4

请注意 **ret** 函数如何从堆栈中清除其参数?这意味着该函数是一个 STDCALL 函数。我们知道该函数以指向整数数组的指针作为其唯一参数。然而,我们不知道这些整数是有符号的还是无符号的,因为 **je** 命令用于两种类型的值。我们可以假设其中一个,为了简单起见,我们可以假设无符号值(在这个函数中,无符号值和有符号值实际上将以相同的方式工作)。我们还知道返回值是一个 4 字节整数,与参数数组中找到的类型相同。由于该函数没有名称,我们可以将其简单地称为 “MyFunction”,并且我们可以将参数称为 “array”,因为它是一个数组。从这些信息中,我们可以确定 C 中的以下原型

 unsigned int STDCALL MyFunction(unsigned int *array);

示例:反编译成 C 代码

[编辑 | 编辑源代码]

将此代码反编译成等效的 C 源代码。

 push ebp
 mov ebp, esp
 mov esi, [ebp + 8]
 mov ebx, 0
 mov eax, 0
 mov ecx, 0
 _Label_1:
 mov ecx, [esi + ebx * 4]
 add eax, ecx
 inc ebx
 cmp ebx, 100
 jne _Label_1
 mov esp, ebp
 pop ebp
 ret 4

从上面的函数原型和对该函数功能的描述开始,我们可以开始为该函数编写 C 代码。我们知道该函数在循环之前初始化了 eax、ebx 和 ecx。但是,我们可以看到 ecx 只是用作中间存储位置,从数组中接收连续的值,然后将其添加到 eax。

我们将创建两个无符号整数,a(用于 eax)和 b(用于 ebx)。我们将在 **register** 限定符的帮助下定义 a 和 b,这样我们就可以指示编译器不要在堆栈上为它们创建空间。对于每个循环迭代,我们都将数组在位置 ebx*4 处的值添加到运行总和 eax。将此转换为我们的 a 和 b 变量,并使用 C 语法,我们看到

 a = a + array[b];

循环可以是 **for** 循环或 **while** 循环。我们看到循环控制变量 b 在循环之前初始化为 0,并且在每次循环迭代中递增 1。循环在 *递增后* 对 b 进行 100 的测试,因此我们知道 b 在循环主体内部从不等于 100。利用这些简单的事实,我们将以 3 种不同的方式编写循环

首先,使用 **while** 循环。

 unsigned int STDCALL MyFunction(unsigned int *array)
 {
    register unsigned int b = 0;
    register unsigned int a = 0;
    while(b != 100)
    {
       a = a + array[b];
       b++;
    }
    return a;
 }

或者,使用 **for** 循环

unsigned int STDCALL MyFunction(unsigned int *array)
 {
    register unsigned int b;
    register unsigned int a = 0;
    for(b = 0; b != 100; b++)
    {
       a = a + array[b];
    }
    return a;
 }

最后,使用 **do-while** 循环

unsigned int STDCALL MyFunction(unsigned int *array)
 {
    register unsigned int b = 0;
    register unsigned int a = 0;
    do
    {
       a = a + array[b];
       b++;
    }while(b != 100);
    return a;
 }
华夏公益教科书