汇编代码似乎是计算机生成的,并且可能是由 GCC 编译的,因为有一个repz retq
在无条件分支之后(call
)。还有迹象表明,因为没有尾部调用(jmp
) 代替call
当去mystery_util
该代码是用-O1
(更高的优化级别可能会内联该函数,但这里没有发生)。缺少帧指针和额外的加载/存储表明它不是用-O0
乘法x
乘以 7 与相乘相同x
乘以 8 并减去x
。这就是下面的代码正在做的事情:
lea 0x0(,%rdi, 8), %edi
sub %eax, %edi
LEA可以计算地址,但也可以用于简单的算术。内存操作数的语法是位移(基数、索引、标度)。比例尺可以是 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
.
Since mystery_util
返回单个位值(0 或 1) 这是合理的bool
是返回类型。
我很好奇是否可以获得特定版本GCC优化级别 1 (-O1
)来重现此汇编代码。我发现 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
was int mystery(int n)
。我以为我可以改变这一点。根据一个相关问题一天后在 Stackoverflow 上询问,看来int mystery(int n)
作为作业的一部分提供给您作为原型。这很重要,因为这意味着必须进行修改。
需要进行的更改与mystery_util
。在要进行逆向工程的代码中有以下几行:
mov %edi, %eax
shr %eax
EDI是第一个参数。SHR是逻辑右移。编译器只会生成这个如果EDI was an 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