试图对一个函数进行反向工程

use*_*612 2 c x86 assembly reverse-engineering att

我试图更多地了解x86中的汇编.我在这里有一个神秘的功能,我知道它会返回int并接受一个int参数.所以它看起来像int mystery(int n){}.但我无法弄清楚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
Run Code Online (Sandbox Code Playgroud)

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

Mic*_*tch 8

汇编代码似乎是由计算机生成的,并且可能由GCC编译,因为有repz retq一个无条件分支(call).还有一个迹象表明,因为没有一个尾调用(jmp),而不是call去当mystery_util代码被编译-O1(较高的优化级别可能会内嵌其中并没有发生在这里的功能).缺少框架指针和额外的加载/存储表明它没有编译-O0

乘以x7与乘以x8并减去相同x.这就是以下代码正在做的事情:

lea  0x0(,%rdi, 8), %edi
sub  %eax, %edi
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)

如果我把所有这些因素放在一起,我们就有一个函数mystery将n*7 - 4传递给一个名为mystery_utilreturn 的函数n &= (n>>1) & 1.

由于mystery_util返回单个位值(0或1),因此bool返回类型是合理的.

我很好奇是否可以获得具有优化级别1()的特定版本的GCC-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);
}
Run Code Online (Sandbox Code Playgroud)

程序集输出是:

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
Run Code Online (Sandbox Code Playgroud)

你可以在godbolt上玩这个代码.


重要更新 - 没有bool的版本

我在解释这个问题时显然错了.我以为问自己确定原型这个问题的人mysteryint mystery(int n).我以为我可以改变它.根据一天后在Stackoverflow上提出的相关问题,似乎int mystery(int n)是作为作业的一部分给你作为原型.这很重要,因为这意味着必须进行修改.

需要做出的改变与mystery_util.在要反向工程的代码中有以下几行:

mov  %edi, %eax
shr  %eax
Run Code Online (Sandbox Code Playgroud)

EDI是第一个参数.SHR是合乎逻辑的右移.如果EDIunsigned 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);
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)