You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

修改静态链接ELF程序头拼接虚拟内存页失败,如何实现?

问题描述

我正在修补静态链接的ELF可执行文件,希望从二进制文件的多个不同部分创建虚拟地址空间中的内存页。以下是我想要实现的简单示例可视化(|标记内存页边界):

file: |aabb|ccdd|
mem:  |aadd|

该内存页的前半部分来自文件第一个页大小范围的前半段,后半部分来自文件第二个页大小范围的后半段,确保各部分在自身范围内对齐正确。

我是否可以实现这一需求?

我修改了二进制文件的程序头表(通过readelf -l打印):

Elf file type is EXEC (Executable file)
Entry point 0x401520
There are 12 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000560000 0x0000000000400000 0x0000000000400000
                 0x0000000000000040 0x0000000000000040  R      0x1000
  LOAD           0x0000000000001270 0x0000000000400270 0x0000000000400270
                 0x00000000000002c8 0x00000000000002c8  R      0x1000
  LOAD           0x0000000000002000 0x0000000000401000 0x0000000000401000
                 0x0000000000075ac1 0x0000000000075ac1  R E    0x1000
  LOAD           0x0000000000078000 0x0000000000477000 0x0000000000477000
                 0x0000000000027053 0x0000000000027053  R      0x1000
  LOAD           0x000000000009f7d8 0x000000000049f7d8 0x000000000049f7d8
                 0x0000000000005ab8 0x000000000000b288  RW     0x1000
  LOAD           0x0000000000000000 0x00000000004ab000 0x00000000004ab000
                 0x00000000000002e0 0x00000000000002e0  R      0x1000
  NOTE           0x0000000000001270 0x0000000000400270 0x0000000000400270
                 0x0000000000000040 0x0000000000000040  R      0x8
  NOTE           0x00000000000012b0 0x00000000004002b0 0x00000000004002b0
                 0x0000000000000044 0x0000000000000044  R      0x4
  TLS            0x000000000009f7d8 0x000000000049f7d8 0x000000000049f7d8
                 0x0000000000000018 0x0000000000000060  R      0x8
  GNU_PROPERTY   0x0000000000001270 0x0000000000400270 0x0000000000400270
                 0x0000000000000040 0x0000000000000040  R      0x8
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x000000000009f7d8 0x000000000049f7d8 0x000000000049f7d8
                 0x0000000000003828 0x0000000000003828  R      0x1

如你所见,我尝试从文件的两个独立“页”拼接虚拟地址0x400000处的内存页:前0x40字节来自文件偏移0x560000的“页”,剩余部分来自文件偏移0x1000的“页”。

当我在GDB中运行该可执行文件时,info proc mappings的输出如下:

Start Addr           End Addr       Size     Offset  Perms  objfile
            0x400000           0x401000     0x1000     0x1000  r--p   <the executable>
            0x401000           0x477000    0x76000     0x2000  r-xp   <the executable>
            0x477000           0x49f000    0x28000    0x78000  r--p   <the executable>
            0x49f000           0x4a6000     0x7000    0x9f000  rw-p   <the executable>
            0x4a6000           0x4ab000     0x5000        0x0  rw-p   
            0x4ab000           0x4ac000     0x1000        0x0  r--p   <the executable>
      0x7ffff7ff9000     0x7ffff7ffd000     0x4000        0x0  r--p   [vvar]
      0x7ffff7ffd000     0x7ffff7fff000     0x2000        0x0  r-xp   [vdso]
      0x7ffffffde000     0x7ffffffff000    0x21000        0x0  rw-p   [stack]
  0xffffffffff600000 0xffffffffff601000     0x1000        0x0  --xp   [vsyscall]

看起来我的第一个程序头(phdr)条目没有生效。请问如何实现我的需求?


解决方案

你的第一个LOAD程序头未生效的核心原因是:ELF加载器会合并或覆盖重叠的虚拟地址范围,且第二个LOAD段(VirtAddr=0x400270)的地址范围完全落在0x400000-0x401000内,直接覆盖了第一个段的内容。此外,部分加载器对跨页拼接的LOAD段组合有严格限制。

要实现目标需求,有两种可行路径:

1. 预拼接文件内容,用单个LOAD段映射(推荐)

这是兼容性最好的方法,完全符合ELF加载器的预期:

  • 在ELF文件中创建一个新的页对齐文件偏移(比如0xXX0000),将文件偏移0x560000的前0x40字节,与文件偏移0x1000的后0xFC0字节复制到这个新页中。
  • 添加一个LOAD段:Offset设为新页起始地址,VirtAddr=0x400000,FileSiz=MemSiz=0x1000,权限R,Align=0x1000。
  • 调整或删除原第二个LOAD段(VirtAddr=0x400270),确保其虚拟地址不与0x400000-0x401000重叠。

2. 拆分虚拟页为多个对齐的LOAD段(兼容性有限)

部分Linux内核的ELF加载器支持将单个虚拟页拆分为多个LOAD段,前提是满足VirtAddr - Offset为Align的倍数:

  • 第一个LOAD段:Offset=0x560000,VirtAddr=0x400000,FileSiz=0x40,MemSiz=0x40,权限R,Align=0x1000(验证:0x400000 - 0x560000 = -0x160000,是0x1000的倍数)。
  • 第二个LOAD段:Offset=0x1040(文件偏移0x1000的0x40偏移处),VirtAddr=0x400040,FileSiz=0xFC0,MemSiz=0xFC0,权限R,Align=0x1000(验证:0x400040 - 0x1040 = 0x3FF000,是0x1000的倍数)。
  • 必须删除或调整原第二个LOAD段(VirtAddr=0x400270),避免地址重叠。

注意:这种方法依赖加载器实现,部分系统可能不支持,因此优先选择第一种方案。

内容的提问来源于stack exchange,提问作者FoxCrafter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 01:12:03