有没有办法做到这一点?我使用过objdump,但它不会产生我所知道的任何汇编程序都能接受的汇编输出.我希望能够在可执行文件中更改指令,然后对其进行测试.

推荐答案

我认为没有任何可靠的方法可以做到这一点.机器代码格式非常复杂,比汇编文件更复杂.获取一个已编译的二进制文件(比如ELF格式)并生成一个将编译为相同(或足够相似)二进制文件的源汇编程序是不可能的.为了了解差异,将GCC直接编译到汇编程序(gcc -S)的输出与可执行文件(objdump -D)上的objdump的输出进行比较.

我能想到两个主要的复杂问题.首先,由于指针偏移等原因,机器代码本身与汇编代码不是一一对应的.

例如,考虑C代码到Hello World:

int main()
{
    printf("Hello, world!\n");
    return 0;
}

这将编译为x86汇编代码:

.LC0:
    .string "hello"
    .text
<snip>
    movl    $.LC0, %eax
    movl    %eax, (%esp)
    call    printf

哪里LCO是一个命名常量,printf是共享库符号表中的一个符号.与objdump的输出进行比较:

80483cd:       b8 b0 84 04 08          mov    $0x80484b0,%eax
80483d2:       89 04 24                mov    %eax,(%esp)
80483d5:       e8 1a ff ff ff          call   80482f4 <printf@plt>

首先是常数.LC0现在只是内存中某个地方的某个随机偏移量——由于汇编程序和链接程序可以自由 Select 这些常量的位置,因此很难创建一个在正确位置包含该常量的汇编源文件.

其次,我不完全确定这一点(这取决于位置无关的代码),但我相信printf的引用实际上根本不是在代码中的指针地址处编码的,但ELF头包含一个查找表,该表在运行时动态替换其地址.因此,反汇编代码与源汇编代码并不完全对应.

总之,源程序集有symbols个,而编译的机器代码有addresses个,这很难逆转.

第二个主要问题是,程序集源文件不能包含原始ELF文件头中存在的所有信息,例如要动态链接的库,以及原始编译器放置在其中的其他元数据.很难重建这个.

正如我所说,一个特殊的工具可能会处理所有这些信息,但不太可能简单地生成可以重新组装回可执行文件的汇编代码.

如果您只想修改可执行文件的一小部分,我推荐一种比重新编译整个应用程序更微妙的方法.使用objdump获取您感兴趣的函数的汇编代码.手动将其转换为"源程序集语法"(在这里,我希望有一个工具能够以与输入相同的语法实际生成反汇编),并根据需要修改它.完成后,只需重新编译这些函数,并使用objdump为修改后的程序计算机器代码.然后,使用十六进制编辑器手动将新机器代码粘贴到原始程序相应部分的顶部,注意新代码的字节数与旧代码的字节数完全相同(否则所有偏移量都会出错).如果新代码较短,可以使用NOP指令填充它.如果时间更长,您可能会遇到麻烦,可能需要创建新函数并调用它们.

Linux相关问答推荐

GO:当使用NewManager调用创建cgroup时,权限被拒绝

无法下载Centos 7上的存储库的元数据

std::chrono::time_zone 在不同操作系统上不可用

如何使用 Golang 清除终端中的最后一行

构建 python 映像时 Docker compose 问题,访问被拒绝或存储库不存在

如何使用 Bash 将随机数据块写入文件

try 使用 patchelf 修补 MuJoCo 二进制文件时出现执行格式错误

如何使用 __attribute__((visibility("default")))?

DMA 和内存映射 IO 有什么区别?

如何使用 gcc 编译为程序集

为什么 Linux (x86) 的页面大小是 4 KB,这是如何计算的?

Eclipse 的 C# 插件

哪个程序在给定任何文件的情况下创建一个 C 数组?

KDE 桌面效果中的 OpenGL 和 XRender 有什么区别?

如何在 Linux 中命名线程?

为什么`du`的输出通常与`du -b`如此不同

crt1.o:在函数_start中:-Linux 中未定义对main的引用

Bash 将 awk 的输出捕获到数组中

svn over HTTP 代理

如何制作和应用SVN补丁?