从给定的 x86 程序集编写 C 函数

Posted

技术标签:

【中文标题】从给定的 x86 程序集编写 C 函数【英文标题】:Writing a C function from given x86 assembly 【发布时间】:2018-08-27 06:58:15 【问题描述】:

我正在尝试对这个神秘功能进行逆向工程。 该函数返回一个整数,并以一个结构节点作为参数

#include "mystery.h"
int mystery(struct e4_struct *s)

头文件是一个简单的结构体声明

struct my_struct 
    int a;
    int b; 
;

我要逆向工程的程序集是

400596:    8b 07                    mov    (%rdi),%eax
400598:    8d 04 40                 lea    (%rax,%rax,2),%eax
40059b:    89 07                    mov    %eax,(%rdi)
40059d:    83 47 04 07              addl   $0x7,0x4(%rdi)
4005a1:    c3                       retq  

到目前为止,我认为该功能是这样的:

int mystery(struct m_struct *s)
    int i = s->a;
    i = 3*i;
    int j = s->b;
    j += 7;
    return i;

但这是不正确的。我不明白 mov %eax,(%rdi) 到底做了什么以及函数最终返回了什么,因为它应该返回整数。

【问题讨论】:

显然j += 7 没有效果,所以它不可能是正确的。 addl 直接对内存内容进行操作,所以这就是s->b += 7,而你根本没有j。另外,您忘记了mov %eax, (%rdi) s->a = s->a * 3 【参考方案1】:

鉴于 RDI 是指向结构开头的指针(函数的第一个参数),以下行将获取 s->a 的值并将其放入临时寄存器 EAX .

mov    (%rdi),%eax

合理地可能是int x = s->a。这一行:

lea    (%rax,%rax,2),%eax

与将温度值乘以 3 相同,因为 RAX+RAX*2=3*RAX(因此 s->a * 3)。所以前两行汇编可以表示为:

int x = s->a * 3;

mov %eax,(%rdi) 行将获取临时值 x 并将其存储回 s->a 以便可以表示为:

s->a = x;

addl $0x7,0x4(%rdi) 行将 7 加到 4(RDI) 处的值。 4(RDI)是s->b的地址。这条线可以表示为s->b += 7;

那么返回的值是什么?由于在上面分析的代码之后没有对 EAX 进行任何其他操作,因此 EAX 仍然是我们之前执行 x = s->a * 3; 时的值。这意味着该函数正在返回临时值x

然后代码将如下所示:

int mystery(struct my_struct *s)

    int x = s->a * 3;
    s->a = x;
    s->b += 7;
    return x;    

如果您使用 GCC 4.9.x 在 godbolt 上以 -O1 优化级别编译此代码,我们会得到这个生成的程序集:

mystery:
        movl    (%rdi), %eax
        leal    (%rax,%rax,2), %eax
        movl    %eax, (%rdi)
        addl    $7, 4(%rdi)
        ret

具有不同优化级别的不同编译器将生成不同的程序集,它们都将执行相同的操作。 GCC 4.9.x 恰好生成了我们最初逆向工程的精确 汇编代码。


注意:我猜测编译器的版本和优化级别,因为最近的 SO question 具有不同的 mystery 函数,我发现 GCC 4.9.x 优化级别 -O1 生成了我正在寻找的确切代码为了。似乎为这些神秘练习生成汇编文件的人正在使用此类设置和类似的编译器。

【讨论】:

以上是关于从给定的 x86 程序集编写 C 函数的主要内容,如果未能解决你的问题,请参考以下文章

我可以将 x86 程序集的 Intel 语法与 GCC 一起使用吗?

为 Windows x86 编写程序集

未能从程序集 C:Program Files (x86)MSBuild14.0inMicrosoft.Data.Entity.Build.Tasks.dll 加载任务“EntityClea(示例代码

GCC 生成的程序集 - C 函数调用时的段错误

X86 程序集:为啥一个基本块有 cmp 和测试指令,似乎重复工作 [重复]

将 scanf 与 x86-64 GAS 程序集一起使用