Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/58.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 尝试对函数进行反向工程_C_Assembly_X86_Reverse Engineering_Att - Fatal编程技术网

C 尝试对函数进行反向工程

C 尝试对函数进行反向工程,c,assembly,x86,reverse-engineering,att,C,Assembly,X86,Reverse Engineering,Att,我正在尝试更多地理解x86中的汇编。我知道这里有一个神秘函数,它返回一个int并接受一个int参数。 所以它看起来像是intsummary(intn){}。然而,我无法理解C中的函数。大会是: mov %edi, %eax lea 0x0(,%rdi, 8), %edi sub %eax, %edi add $0x4, %edi callq < mystery _util > repz retq < mystery _util > mov %edi, %eax

我正在尝试更多地理解x86中的汇编。我知道这里有一个神秘函数,它返回一个
int
并接受一个
int
参数。 所以它看起来像是
intsummary(intn){}
。然而,我无法理解C中的函数。大会是:

mov  %edi, %eax
lea  0x0(,%rdi, 8), %edi
sub  %eax, %edi
add  $0x4, %edi
callq < mystery _util >
repz retq

< mystery _util >
mov  %edi, %eax
shr  %eax
and  $0x1, %edi
and  %edi, %eax
retq
mov%edi,%eax
lea 0x0(,%rdi,8),%edi
子%eax,%edi
添加$0x4,%edi
callq
雷普兹雷特
<神秘_util>
mov%edi,%eax
shr%eax
和$0x1,%edi
和%edi,%eax
retq

我不明白lea在这里做什么,它可能是什么样的功能

LEA只是左移3,并将结果截断为32位(即,零将EDI隐式扩展为RDI)。x86-64系统V传递RDI中的第一个整数arg,因此所有这些都与一个
int
arg一致。LEA使用内存操作数语法和机器编码。将其用作乘以常数的乘法运算的一部分

生成此函数的编译器错过了此处的优化;第一个
mov
可以通过

lea  0x0(,%rdi, 8), %eax     # n << 3 = n*8
sub  %edi, %eax              # eax = n*7
lea  4(%rax), %edi           # rdi = 4 + n*7

lea0x0(,%rdi,8),%eax#nlea
执行地址计算,但不是取消对地址的引用,而是将计算出的地址存储到目标寄存器中。 在AT&T语法中,
leac(b,C,d),reg
表示
reg=C+b+C*d
,其中
C
是常数,
b
C
是寄存器,
d
是{1,2,4,8}中的标量。因此,您可以看到为什么LEA在简单的数学运算中很受欢迎:它在一条指令中执行了相当多的操作。(*包括prl以下评论的更正)

此汇编代码有一些奇怪的特性:
repz
前缀仅在应用于某些指令时才严格定义,
retq
不是其中之一(尽管处理器的一般行为是忽略它)。有关更多信息,请参见下面迈克尔·佩奇的评论和链接。使用
lea(,rdi,8),edi
,然后使用
sub eax,edi
来计算
arg1*7
似乎也很奇怪,但一旦prl注意到标量
d
必须是2的恒定幂,就有意义了。在任何情况下,我都是这样读这段代码的:

mov  %edi, %eax          ; eax = arg1
lea  0x0(,%rdi, 8), %edi ; edi = arg1 * 8
sub  %eax, %edi          ; edi = (arg1 * 8) - arg1 = arg1 * 7
add  $0x4, %edi          ; edi = (arg1 * 7) + 4
callq < mystery _util >  ; call mystery_util(arg1 * 7 + 4)
repz retq                ; repz prefix on return is de facto nop.


< mystery _util >
mov  %edi, %eax          ; eax = arg1
shr  %eax                ; eax = arg1 >> 1
and  $0x1, %edi          ; edi = 1 iff arg1 was odd, else 0
and  %edi, %eax          ; eax = 1 iff smallest 2 bits of arg1 were both 1.
retq
mov%edi,%eax;eax=arg1
LEA0x0(,%rdi,8),%edi;edi=arg1*8
低于%eax,%edi;edi=(arg1*8)-arg1=arg1*7
添加$0x4,%edi;edi=(arg1*7)+4
callq<神秘_util>;调用神秘_util(arg1*7+4)
雷普兹雷特克;返回时的repz前缀实际上是nop。
<神秘_util>
mov%edi,%eax;eax=arg1
shr%eax;eax=arg1>>1
和$0x1,%edi;如果arg1为奇数,则edi=1,否则为0
和%edi,%eax;eax=1如果arg1的最小2位均为1。
retq
注意第4行的
+4
完全是假的。它不能影响神秘的结果


因此,总的来说,这个ASM代码段计算布尔值(arg1*7)%4==3。

汇编代码似乎是由计算机生成的,而且可能是由GCC编译的,因为在无条件分支(
调用
)之后有一个
repz retq
)。还有一个迹象表明,由于在转到
summary\u util
时没有尾部调用(
jmp
)而不是
调用,因此代码是使用
-O1
编译的(更高的优化级别可能会内联此处未发生的函数)。缺少帧指针和额外的加载/存储表明它不是用
-O0

x
乘以7与将
x
乘以8并减去
x
相同。下面的代码就是这么做的:

lea  0x0(,%rdi, 8), %edi
sub  %eax, %edi
可以计算地址,但也可以用于简单的算术。内存操作数的语法是位移(基、索引、刻度)。刻度可以是1、2、4、8。计算方法为位移+基底+指数*标度。在您的情况下,
lea0x0(,%rdi,8),%edi
实际上是edi=0x0+rdi*8或edi=rdi*8。完整计算为n*7-4

summary\u util
的计算似乎简单

n &= (n>>1) & 1;
如果我把所有这些因素加在一起,我们就有一个函数
summary
,它将n*7-4传递给一个名为
summary\u util
的函数,该函数返回
n&=(n>>1)&1

由于
summary\u util
返回单个位值(0或1),因此合理的做法是将
bool
作为返回类型

我很好奇是否可以得到一个优化级别为1(
-O1
)的特定版本的GCC来重现这个汇编代码。我发现GCC4.9.x将为这个给定的C程序生成一个精确的汇编代码:

您可以在上玩此代码


重要更新-没有bool的版本 我显然在解释这个问题时犯了错误。我假设问这个问题的人自己决定了
神秘
的原型是
int神秘(int n)
。我想我可以改变这一点。根据一天后在Stackoverflow上被问到的问题,似乎
int-summary(int-n)
是作为任务的一部分提供给你的原型。这很重要,因为这意味着必须进行修改

需要进行的更改与
summary\u util
有关。要进行反向工程的代码中有以下几行:

mov  %edi, %eax
shr  %eax
EDI是第一个参数。SHR是逻辑右移。只有当EDI是一个
无符号int
(或等效项)时,编译器才会生成此代码
int
是一种有符号类型,可以生成SAR(算术右移)。这意味着
unsigned_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);
}
神秘
现在有了你教授给出的原型(mov %edi, %eax shr %eax
unsigned int mystery_util(unsigned int n)
{
    n &= (n>>1) & 1;
    return n;
}

int mystery(int n)
{
    return mystery_util (7*n+4);
}
mystery_util:
        movl    %edi, %eax
        sarl    %eax          ; <------- SAR (arithmetic shift right) is not SHR
        andl    $1, %edi
        andl    %edi, %eax
        ret