x86 反汇编/分支示例
此函数接受哪些参数?它使用什么调用约定?它返回什么类型的值?编写此函数的完整 C 语言原型。假设所有值都是无符号值。
push ebp
mov ebp, esp
mov eax, 0
mov ecx, [ebp + 8]
cmp ecx, 0
jne _Label_1
inc eax
jmp _Label_2
:_Label_1
dec eax
: _Label_2
mov ecx, [ebp + 12]
cmp ecx, 0
jne _Label_3
inc eax
: _Label_3
mov esp, ebp
pop ebp
ret
此函数在 [ebp + 8] 和 [ebp + 12] 处的堆栈上访问参数。这两个值都被加载到 ecx 中,因此我们可以假设它们是 4 字节值。此函数不清理自己的堆栈,并且值不是在寄存器中传递的,所以我们知道该函数是 CDECL。eax 中的返回值是 4 字节值,我们被告知假设所有值都是无符号的。将所有这些组合在一起,我们可以构建函数原型
unsigned int CDECL MyFunction(unsigned int param1, unsigned int param2);
此函数中有多少个独立的分支结构?它们是什么类型?你能根据这些分支的结构,给_Label_1、_Label_2 和_Label_3 更具描述性的名称吗?
push ebp
mov ebp, esp
mov eax, 0
mov ecx, [ebp + 8]
cmp ecx, 0
jne _Label_1
inc eax
jmp _Label_2
:_Label_1
dec eax
: _Label_2
mov ecx, [ebp + 12]
cmp ecx, 0
jne _Label_3
inc eax
: _Label_3
mov esp, ebp
pop ebp
ret
此函数中有多少个独立的分支结构?剥离掉入口和退出序列后,我们剩下的代码如下:
mov ecx, [ebp + 8]
cmp ecx, 0
jne _Label_1
inc eax
jmp _Label_2
:_Label_1
dec eax
: _Label_2
mov ecx, [ebp + 12]
cmp ecx, 0
jne _Label_3
inc eax
: _Label_3
观察代码,我们看到 2 个cmp 语句。第一个 cmp 语句将 ecx 与零比较。如果 ecx 不为零,我们就跳转到 _Label_1,递减 eax,然后直接执行到 _Label_2。如果 ecx 为零,我们就递增 eax,直接跳转到 _Label_2。写出一些伪代码,我们对第一部分有以下结果:
if(ecx doesnt equal 0) goto _Label_1 eax++; goto _Label_2 :_Label_1 eax--; :_Label_2
由于 _Label_2 发生在这个结构的末尾,我们可以将它重命名为更具描述性的名称,例如 "End_of_Branch_1" 或 "Branch_1_End"。第一个比较测试 ecx 与 0 的关系,然后在不等于时跳转。我们可以反转条件,说 _Label_1 是一个else 块
if(ecx == 0) ;ecx is param1 here
{
eax++;
}
else
{
eax--;
}
因此,我们可以将 _Label_1 重命名为其他具描述性的名称,例如 "Else_1"。Branch_1_End (_Label_2) 之后代码块的其余部分如下:
mov ecx, [ebp + 12]
cmp ecx, 0
jne _Label_3
inc eax
: _Label_3
我们可以立即看到 _Label_3 是此分支结构的末尾,因此我们可以立即将其称为 "Branch_2_End" 或其他名称。在这里,我们再次将 ecx 与 0 进行比较,如果不相等,我们就跳转到块的末尾。但是,如果它等于零,我们就递增 eax,然后从分支的底部退出。我们可以看到此分支结构中没有else 块,因此我们不需要反转条件。我们可以直接写一个if 语句
if(ecx == 0) ;ecx is param2 here
{
eax++;
}
编写此函数的等效 C 语言代码。假设所有参数和返回值都是无符号值。
push ebp
mov ebp, esp
mov eax, 0
mov ecx, [ebp + 8]
cmp ecx, 0
jne _Label_1
inc eax
jne _Label_2
:_Label_1
dec eax
: _Label_2
mov ecx, [ebp + 12]
cmp ecx, 0
jne _Label_3
inc eax
: _Label_3
mov esp, ebp
pop ebp
ret
从答案 1 中的 C 语言函数原型和答案 2 中的条件块开始,我们可以将一个伪代码函数拼凑起来,不包含变量声明或返回值
unsigned int CDECL MyFunction(unsigned int param1, unsigned int param2)
{
if(param1 == 0)
{
eax++;
}
else
{
eax--;
}
if(param2 == 0)
{
eax++;
}
}
现在,我们只需要创建一个变量来存储 eax 中的值,我们将它称为 "a",并将其声明为register 类型
unsigned int CDECL MyFunction(unsigned int param1, unsigned int param2)
{
register unsigned int a = 0;
if(param1 == 0)
{
a++;
}
else
{
a--;
}
if(param2 == 0)
{
a++;
}
return a;
}
当然,此函数不是一个特别有用的函数,但至少我们知道它在做什么。