可以在特定部分中移动代码中的某些功能 在可执行文件?如果是这样,怎么样?
对于使用gcc编译的应用程序,我们有更多的源文件,包括 X.c.每个对象都是从关联的源代码编译的(X.o是从X.c获得的),并且链接器生成一个大的可执行文件。
我需要X.c中的两个函数位于其中的特定部分 可执行文件,例如.magic_section。我想要的原因是 该部分将被加载到另一个内存区域而不是其他部分。
我的问题是我无法更改源X.c,否则我会使用
特定标志,例如__attribute__ ((section ("magic_section")))
功能。
我在linker的文档中读到了一些内容,并为链接器编写了一个自定义脚本,但是我没有指定必须在哪个部分放置特定符号。我只设法移动整个部分。
答案 0 :(得分:3)
你可以做的事情可能做到(不是很好,但理论上应该工作)是使用--function-sections
和--data-sections
,假设你的GCC版本/架构支持这些选项,然后手动调用所有的功能&需要使用链接描述文件在给定文件中的变量。
这会创建名为.text.function_name
或.data.variable_name
的部分。如果您熟悉通过gcc属性分配部分,我会假设您知道在链接器中要做什么。
作为一个优势,如果你实际上并不希望整个文件进入神奇的部分,那么你可以选择功能。
答案 1 :(得分:0)
不幸的是,如果不修改二进制对象,动态链接器或动态加载器,您将无法完成此任务,无论如何,这是一项非常困难的任务。
选项1 - ELF操作
每个ELF可执行文件都是由部分组成的,这些部分包含实际的代码/数据/符号字符串/ ...以及帮助加载器决定在内存中加载代码的位置,这个ELF暴露的符号,符号它需要从其他位置,在哪里加载特定的代码/数据等。
您可以通过键入
来观察二进制文件中的细分readelf -l [你的二进制]
输出将类似于以下内容(我选择ls作为二进制文件):
[ishaypeled @ ishay-dev bin] $ readelf -l --wide ./ls
Elf file type is EXEC (Executable file)
Entry point 0x4048bf
There are 9 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000040 0x0000000000400040 0x0000000000400040 0x0001f8 0x0001f8 R E 0x8
INTERP 0x000238 0x0000000000400238 0x0000000000400238 0x00001c 0x00001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x01b694 0x01b694 R E 0x200000
LOAD 0x01bdf0 0x000000000061bdf0 0x000000000061bdf0 0x000864 0x0016d0 RW 0x200000
DYNAMIC 0x01be08 0x000000000061be08 0x000000000061be08 0x0001f0 0x0001f0 RW 0x8
NOTE 0x000254 0x0000000000400254 0x0000000000400254 0x000044 0x000044 R 0x4
GNU_EH_FRAME 0x01895c 0x000000000041895c 0x000000000041895c 0x00071c 0x00071c R 0x4
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
GNU_RELRO 0x01bdf0 0x000000000061bdf0 0x000000000061bdf0 0x000210 0x000210 R 0x1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
04 .dynamic
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
08 .init_array .fini_array .jcr .dynamic .got
现在让我们检查一下这个输出:
在第一个表(Program Headers)中:
[类型] - 段类型,本节的目的是什么
[偏移] - 此段开始的文件中的偏移量
[VirtAddr] - 我们要在流程地址空间中加载此部分(如果应该加载此段,而不是所有段都被加载)
[PhysAddr] - 与我遇到的所有现代操作系统的VirtAddr相同
[FileSiz] - 此部分存档有多大。这是指向您的部分的链接 - 当前部分包含Offset to Offset + FileSiz的所有部分
[MemSiz] - 虚拟内存中的这个部分有多大(这不一定与文件大小相同!如果它超出文件中的大小,则多余设置为0)
[Flg] - 权限标志,R读取E执行W写入。
[对齐] - 内存中所需的内存对齐。
您的重点是LOAD类型(PT_LOAD)。这些段对来自节的数据进行分组,指示加载器将它们放在进程地址空间中的位置,并确定指定它们的权限。
您可以在Section to Segment映射表中看到一个方便的分段映射部分。
让我们观察两个LOAD段2和3:
我们可以看到段2具有读取和执行权限,并且它跨越(以及其他).text和.rodata部分。
所以,为了达到使用ELF操作的目的:
如果你读到这里并了解一切,你应该知道这对于现实生活中的案件来说是一项非常繁琐,几乎不可能完成的任务。
选项2 - 动态链接器操作 请注意上例中的INTERP段类型。这是一个ASCII字符串,用于指定应使用的动态链接器 动态链接器角色是解析段并执行所有动态操作,例如在运行时解析符号,从.so文件加载段等。
这里可能的操作是修改动态链接器代码(注意:这是系统范围的更改!)将函数二进制数据加载到进程地址空间中的特定内存地址。请注意,这种方法有几个挫折:
选项3 - 动态加载程序操作 与选项2非常相似,但修改ld库设施而不是动态链接器。
<强>结论强>
你想要做的事情非常困难,而且确实是一项繁琐的工作。我正在开发一种工具,允许将任意函数注入到现有的共享对象文件中,我保证这至少可以在几个星期内工作。
你确定没有其他方法可以实现你想要的吗?为什么在单独的地址中需要这两个功能?也许有一个更简单的解决方案...