我尝试使用libelf来编辑ELF二进制文件中的一些东西,但到目前为止,我甚至无法在不破坏它的情况下写出二进制文件。该样品:
#include <libelf.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <assert.h>
#include <gelf.h>
int main() {
elf_version(EV_CURRENT);
int fd = open("elftest", O_RDWR, 0);
assert(fd >= 0);
Elf *elf = elf_begin(fd, ELF_C_RDWR, NULL);
assert(elf != NULL);
assert(elf_kind(elf) == ELF_K_ELF);
assert(elf_update(elf, ELF_C_WRITE) != -1);
elf_end(elf);
close(fd);
}
它应该只读入elftest
并将其原封不动地写回,而不是将一个工作的C hello world转换为一个立即segfaults的程序(根据gdb,甚至在main被调用之前)。我注意到readelf -h
的第一个差异是,它将节头的开始位置向后移动了一些,并且还报告说
readelf: Warning: the .dynamic section is not contained within the dynamic segment
是什么导致libelf
在实际上没有任何更改的情况下更改可执行文件?
2条答案
按热度按时间im9ewurl1#
它应该只是读取elftest并将其不改变地写回,而不是将工作的C hello world转换为立即segfault的程序
这看起来像是
libelf
中的一个bug。在
elf_open()
之后立即添加这一行可以修复程序:但我觉得没必要
P.S.我知道这只是一个例子,但是把你程序的 * 功能 * 部分放在
assert()
中是一个非常糟糕的主意。这样做:迟早会让你后悔的
68bkxrlz2#
我一直在尝试
libelf
,发现了同样的问题。经过调查,似乎libelf
在使用“库布局”模式时移动了部分,而没有重新处理程序段。根据我的理解,当一个程序启动时,程序头被读取以告知应该如何加载文件的各个部分。例如,包含代码的
.text
段最终Map到具有执行权限的内存区域,而包含静态数据的.rodata
段最终Map到只读段中。请看这个例子:
readelf -l -S
在工作的可执行文件上显示以下内容:腐败之后:
关注
.text
部分,它最初位于偏移量0x00001040
处,该偏移量Map到段3(从偏移量0x00001000
开始,可执行位设置为E
)。libelf
处理后,.text
被移动到偏移0x00000630
,不再Map到可执行部分。查看readelf
报告的“Section to Segment mapping”,我们可以清楚地看到有些地方出错了。正如在另一个答案中提到的,你可以通过使用“应用程序控制的布局”模式来解决这个问题,但是如果你对ELF部分进行修改,你就要做所有烦人的更新偏移和大小的工作。
我希望这是有用的,我学到了很多,而调查。