C 如何合并两个二进制可执行文件?
这个问题是我以前问过的。简而言之,这是我将两个完全链接的可执行文件合并为一个完全链接的可执行文件的尝试之一。区别在于,前面的问题涉及将对象文件合并到完整链接的可执行文件,这更难,因为这意味着我需要手动处理重定位 我拥有以下文件:C 如何合并两个二进制可执行文件?,c,linux,linker,elf,bfd,C,Linux,Linker,Elf,Bfd,这个问题是我以前问过的。简而言之,这是我将两个完全链接的可执行文件合并为一个完全链接的可执行文件的尝试之一。区别在于,前面的问题涉及将对象文件合并到完整链接的可执行文件,这更难,因为这意味着我需要手动处理重定位 我拥有以下文件: 示例target.c: #include <stdlib.h> #include <stdio.h> int main(void) { puts("1234"); return EXIT_SUCCESS; } #include
示例target.c
:
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
puts("1234");
return EXIT_SUCCESS;
}
#include <stdlib.h>
#include <stdio.h>
/*
* Fake main. Never used, just there so we can perform a full link.
*/
int main(void)
{
return EXIT_SUCCESS;
}
void func1(void)
{
puts("asdf");
}
我的目标是合并这两个可执行文件,以生成最终的可执行文件,该文件与示例目标
相同,但另外还有另一个main
和func1
从BFD库的角度来看,每个二进制文件(除其他外)由一组部分组成。我面临的第一个问题是,这些部分有冲突的加载地址(这样,如果我要合并它们,这些部分就会重叠)
我所做的是以编程方式分析example target
,以获得每个部分的加载地址和大小列表。然后我对示例嵌入
做了同样的操作,并使用此信息动态生成了一个for示例嵌入.c
,以确保其所有部分都链接到与示例目标
中的任何部分都不重叠的地址。因此,example embed
实际上在这个过程中完全链接了两次:一次是为了确定节的数量和大小,另一次是为了确保节与example target
没有冲突
在我的系统上,生成的链接器命令是:
-Wl,--section-start=.new.interp=0x1004238,--section-start=.new.note.ABI-tag=0x1004254,
--section-start=.new.note.gnu.build-id=0x1004274,--section-start=.new.gnu.hash=0x1004298,
--section-start=.new.dynsym=0x10042B8,--section-start=.new.dynstr=0x1004318,
--section-start=.new.gnu.version=0x1004356,--section-start=.new.gnu.version_r=0x1004360,
--section-start=.new.rela.dyn=0x1004380,--section-start=.new.rela.plt=0x1004398,
--section-start=.new.init=0x10043C8,--section-start=.new.plt=0x10043E0,
--section-start=.new.text=0x1004410,--section-start=.new.fini=0x10045E8,
--section-start=.new.rodata=0x10045F8,--section-start=.new.eh_frame_hdr=0x1004604,
--section-start=.new.eh_frame=0x1004638,--section-start=.new.ctors=0x1204E28,
--section-start=.new.dtors=0x1204E38,--section-start=.new.jcr=0x1204E48,
--section-start=.new.dynamic=0x1204E50,--section-start=.new.got=0x1204FE0,
--section-start=.new.got.plt=0x1204FE8,--section-start=.new.data=0x1205010,
--section-start=.new.bss=0x1205020,--section-start=.new.comment=0xC04000
(注意,我使用objcopy--prefix sections=.new example embeddeobj
为节名添加了.new
前缀,以避免节名冲突。)
然后我编写了一些代码来生成一个新的可执行文件(从objcopy
和securitywarrior
书中借用了一些代码)。新的可执行文件应具有:
和示例目标的所有部分
示例嵌入的所有部分
- 一个符号表,其中包含来自
的所有符号以及示例目标
示例嵌入
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <bfd.h>
#include <libiberty.h>
struct COPYSECTION_DATA {
bfd * obfd;
asymbol ** syms;
int symsize;
int symcount;
};
void copy_section(bfd * ibfd, asection * section, PTR data)
{
struct COPYSECTION_DATA * csd = data;
bfd * obfd = csd->obfd;
asection * s;
long size, count, sz_reloc;
if((bfd_get_section_flags(ibfd, section) & SEC_GROUP) != 0) {
return;
}
/* get output section from input section struct */
s = section->output_section;
/* get sizes for copy */
size = bfd_get_section_size(section);
sz_reloc = bfd_get_reloc_upper_bound(ibfd, section);
if(!sz_reloc) {
/* no relocations */
bfd_set_reloc(obfd, s, NULL, 0);
} else if(sz_reloc > 0) {
arelent ** buf;
/* build relocations */
buf = xmalloc(sz_reloc);
count = bfd_canonicalize_reloc(ibfd, section, buf, csd->syms);
/* set relocations for the output section */
bfd_set_reloc(obfd, s, count ? buf : NULL, count);
free(buf);
}
/* get input section contents, set output section contents */
if(section->flags & SEC_HAS_CONTENTS) {
bfd_byte * memhunk = NULL;
bfd_get_full_section_contents(ibfd, section, &memhunk);
bfd_set_section_contents(obfd, s, memhunk, 0, size);
free(memhunk);
}
}
void define_section(bfd * ibfd, asection * section, PTR data)
{
bfd * obfd = data;
asection * s = bfd_make_section_anyway_with_flags(obfd,
section->name, bfd_get_section_flags(ibfd, section));
/* set size to same as ibfd section */
bfd_set_section_size(obfd, s, bfd_section_size(ibfd, section));
/* set vma */
bfd_set_section_vma(obfd, s, bfd_section_vma(ibfd, section));
/* set load address */
s->lma = section->lma;
/* set alignment -- the power 2 will be raised to */
bfd_set_section_alignment(obfd, s,
bfd_section_alignment(ibfd, section));
s->alignment_power = section->alignment_power;
/* link the output section to the input section */
section->output_section = s;
section->output_offset = 0;
/* copy merge entity size */
s->entsize = section->entsize;
/* copy private BFD data from ibfd section to obfd section */
bfd_copy_private_section_data(ibfd, section, obfd, s);
}
void merge_symtable(bfd * ibfd, bfd * embedbfd, bfd * obfd,
struct COPYSECTION_DATA * csd)
{
/* set obfd */
csd->obfd = obfd;
/* get required size for both symbol tables and allocate memory */
csd->symsize = bfd_get_symtab_upper_bound(ibfd) /********+
bfd_get_symtab_upper_bound(embedbfd) */;
csd->syms = xmalloc(csd->symsize);
csd->symcount = bfd_canonicalize_symtab (ibfd, csd->syms);
/******** csd->symcount += bfd_canonicalize_symtab (embedbfd,
csd->syms + csd->symcount); */
/* copy merged symbol table to obfd */
bfd_set_symtab(obfd, csd->syms, csd->symcount);
}
bool merge_object(bfd * ibfd, bfd * embedbfd, bfd * obfd)
{
struct COPYSECTION_DATA csd = {0};
if(!ibfd || !embedbfd || !obfd) {
return FALSE;
}
/* set output parameters to ibfd settings */
bfd_set_format(obfd, bfd_get_format(ibfd));
bfd_set_arch_mach(obfd, bfd_get_arch(ibfd), bfd_get_mach(ibfd));
bfd_set_file_flags(obfd, bfd_get_file_flags(ibfd) &
bfd_applicable_file_flags(obfd));
/* set the entry point of obfd */
bfd_set_start_address(obfd, bfd_get_start_address(ibfd));
/* define sections for output file */
bfd_map_over_sections(ibfd, define_section, obfd);
/******** bfd_map_over_sections(embedbfd, define_section, obfd); */
/* merge private data into obfd */
bfd_merge_private_bfd_data(ibfd, obfd);
/******** bfd_merge_private_bfd_data(embedbfd, obfd); */
merge_symtable(ibfd, embedbfd, obfd, &csd);
bfd_map_over_sections(ibfd, copy_section, &csd);
/******** bfd_map_over_sections(embedbfd, copy_section, &csd); */
free(csd.syms);
return TRUE;
}
int main(int argc, char **argv)
{
bfd * ibfd;
bfd * embedbfd;
bfd * obfd;
if(argc != 4) {
perror("Usage: infile embedfile outfile\n");
xexit(-1);
}
bfd_init();
ibfd = bfd_openr(argv[1], NULL);
embedbfd = bfd_openr(argv[2], NULL);
if(ibfd == NULL || embedbfd == NULL) {
perror("asdfasdf");
xexit(-1);
}
if(!bfd_check_format(ibfd, bfd_object) ||
!bfd_check_format(embedbfd, bfd_object)) {
perror("File format error");
xexit(-1);
}
obfd = bfd_openw(argv[3], NULL);
bfd_set_format(obfd, bfd_object);
if(!(merge_object(ibfd, embedbfd, obfd))) {
perror("Error merging input/obj");
xexit(-1);
}
bfd_close(ibfd);
bfd_close(embedbfd);
bfd_close(obfd);
return EXIT_SUCCESS;
}
在我的系统上,elf.c
的1708
是:
BFD_ASSERT (elf_dynsymtab (abfd) == 0);
elf_dynsymtab
是elf bfd.h
中的宏,用于:
#define elf_dynsymtab(bfd) (elf_tdata(bfd) -> dynsymtab_section)
我不熟悉ELF层,但我认为这是读取动态符号表(或者说它不存在)的问题。目前,除非有必要,我尽量避免直接接触ELF层。有人能告诉我在代码或概念上我做错了什么吗
如果有帮助,我还可以发布链接器命令生成的代码或示例二进制文件的编译版本
我意识到这是一个非常大的问题,出于这个原因,我想适当地奖励任何能够帮助我的人。如果我能在别人的帮助下解决这个问题,我很乐意奖励500+奖金。为什么要手动完成所有这些?考虑到您拥有所有符号信息(如果您想以合理的方式编辑二进制文件,则必须提供这些信息),将可执行文件拆分为单独的对象文件(例如,每个函数一个对象文件),进行编辑并重新链接,难道不更容易吗?您为什么要这样做?动机是什么?你有这两个二进制文件的源代码吗?看起来相当愚蠢。@EdHeal看到了他另一个问题顶部的链接问题,这有一定的道理。@EdHeal:我正在制作一个静态可执行编辑器,它可以将用户定义的例程插入到目标中(“代码>示例嵌入”)然后静态地绕过新二进制文件的代码,将原始代码与注入的代码连接起来(我已经编写了一个反汇编程序/CFG分析引擎,我还可以编辑任意指令,因此此注入是拼图的最后一部分)。对于我需要关心的用例,可以假设我们可以访问用户定义例程的源代码,但不能访问目标。@DanFego-谢谢-我会的。您的最终可执行文件是否有
.dynsymtab
部分?(readelf-WS-exename
)没有源代码,如何将可执行文件拆分为目标文件?我可以假设符号信息可用于嵌入对象,但不可用于目标(尽管如果我能让它首先使用此假设,那就好了)。ELF可执行文件可以保留重定位信息和符号表。当这两个部分都存在时,将可执行文件拆分为对象文件相对简单,因为符号表还说明符号是数据还是代码。还有,为什么要合并可执行文件?注入一个对象文件会更容易。我尝试合并可执行文件,因为我认为这会更容易,因为我不再需要处理重新定位。当我尝试注入一个对象文件时,我没有找到一种方法。如何将可执行文件拆分为对象文件?顺便说一句,感谢您的关注。如果没有重新定位信息,您无法有意义地调整跳转指令和数据引用。你不能仅仅依靠BFD——你最终必须了解ELF的细节。简单地说,重新拆分:对于每个函数符号(地址+长度),输出从指向函数代码的重定位递归可访问的函数+代码。您还必须复制数据重新定位。完整的数据段可以放在自己的文件中。我不熟悉对象文件的格式,但我会详细阅读。我不太明白你所说的“可从重定位递归访问的代码”是什么意思。您是否建议使用以符号为根的函数生成CFG?你的建议以前做过吗?或者有没有一个名字叫th
#define elf_dynsymtab(bfd) (elf_tdata(bfd) -> dynsymtab_section)