汇编代码似乎是计算机生成的,并且可能是由 GCC 编译的,因为在无条件分支 (call) 之后有一个 repz retq。还有一个迹象表明,因为没有尾调用 (jmp) 而不是 call 在转到 mystery_util 时,代码是用 -O1 编译的(更高的优化级别可能会内联函数这里没有发生)。缺少帧指针和额外的加载/存储表明它不是用-O0编译的
x 乘以 7 与 x 乘以 8 并减去 x 相同。这就是以下代码的作用:
lea 0x0(,%rdi, 8), %edi
sub %eax, %edi
LEA 可以计算地址,但也可以用于简单的算术运算。内存操作数的语法是位移(base, index, scale)。比例可以是1、2、4、8。计算是位移+基数+索引*比例。在您的情况下,lea 0x0(,%rdi, 8), %edi 实际上是 EDI = 0x0 + RDI * 8 或 EDI = RDI * 8。完整计算为 n * 7 - 4;
mystery_util 的计算似乎很简单
n &= (n>>1) & 1;
如果我将所有这些因素放在一起,我们有一个函数 mystery 将 n * 7 - 4 传递给一个名为 mystery_util 的函数,该函数返回 n &= (n>>1) & 1。
由于mystery_util 返回单个位值(0 或1),因此bool 是返回类型是合理的。
我很好奇能否获得具有优化级别 1 (-O1) 的特定版本的 GCC 来重现此汇编代码。我发现 GCC 4.9.x 将为这个给定的 C 程序生成这个精确的汇编代码:
#include<stdbool.h>
bool mystery_util(unsigned int n)
{
n &= (n>>1) & 1;
return n;
}
bool mystery(unsigned int n)
{
return mystery_util (7*n+4);
}
汇编输出为:
mystery_util:
movl %edi, %eax
shrl %eax
andl $1, %edi
andl %edi, %eax
ret
mystery:
movl %edi, %eax
leal 0(,%rdi,8), %edi
subl %eax, %edi
addl $4, %edi
call mystery_util
rep ret
您可以在godbolt 上使用此代码。
重要更新 - 不带 bool 的版本
我显然错误地解释了这个问题。我假设问这个问题的人自己确定mystery 的原型是int mystery(int n)。我以为我可以改变这一点。根据一天后在 Stackoverflow 上询问的related question,看来int mystery(int n) 是作为任务的一部分给你的原型。这很重要,因为这意味着必须进行修改。
需要进行的更改与mystery_util 有关。在要逆向工程的代码中有以下几行:
mov %edi, %eax
shr %eax
EDI 是第一个参数。 SHR 是逻辑右移。只有当 EDI 是 unsigned int(或等效项)时,编译器才会生成这个。 int 是一个有符号类型,会生成 SAR(算术右移)。这意味着mystery_util 的参数必须是unsigned int(因此返回值很可能是unsigned int。这意味着代码将如下所示:
unsigned int mystery_util(unsigned int n)
{
n &= (n>>1) & 1;
return n;
}
int mystery(int n)
{
return mystery_util (7*n+4);
}
mystery 现在有你的教授给出的原型(bool 被删除),我们使用unsigned int 作为mystery_util 的参数和返回类型。为了使用 GCC 4.9.x 生成此代码,我发现您需要使用 -O1 -fno-inline。此代码可在godbolt 上找到。汇编输出与使用bool 的版本相同。
如果你使用unsigned int mystery_util(int n),你会发现它并不能完全输出我们想要的:
mystery_util:
movl %edi, %eax
sarl %eax ; <------- SAR (arithmetic shift right) is not SHR
andl $1, %edi
andl %edi, %eax
ret