“ELF phdr: 从可执行文件的多个范围组合内存页”

huangapple go评论72阅读模式
英文:

ELF phdr: Piecing together memory pages from multiple ranges of an executable

问题

I am patching (statically linked) ELF executables.
我正在修补(静态链接的)ELF可执行文件。

I would like to create pages in virtual address space from multiple different parts of the binary.
我想从二进制文件的多个不同部分在虚拟地址空间中创建页面。

Here is a visualization of a simple case of what I am trying to achieve.
这是我试图实现的一个简单情况的可视化示例。

(| marks memory page boundaries):
|表示内存页面边界):

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

The first half of the page is from the first half of the first page sized range of the file, and the second half is from the second half of the second page sized range, so the alignment of each piece is correct within its range.
页面的前半部分来自文件的第一个页面大小范围的前半部分,而后半部分来自第二个页面大小范围的后半部分,因此每个部分在其范围内的对齐都是正确的。

Is it possible to achieve this any way?
有没有可能以任何方式实现这一点?

I modified a binary to have the following program header table (printed with readelf -l)
我修改了一个二进制文件,使其具有以下程序头表(使用 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

As you can see I am trying to piece together the memory page at 0x400000 from two separate "pages" of the file: the first 0x40 bytes from the "page" at offset 0x560000 and the rest from the one at offset 0x1000.
正如您所看到的,我正在尝试从文件的两个单独的“页面”中组合在0x400000处的内存页面:第一个“页面”来自偏移0x560000处的前0x40字节,其余来自偏移0x1000处的页面。

When I run the executable in GDB, info proc mappings has the following output:
当我在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        0

<details>
<summary>英文:</summary>

I am patching (statically linked) ELF executables.
I would like to create pages in virtual address space from multiple different parts of the binary.

Here is a visualization of a simple case of what I am trying to achieve.

(`|` marks memory page boundaries):

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


The first half of the page is from the first half of the first page sized range of the file, and the second half is from the second half of the second page sized range, so the alignment of each piece is correct within its range.

Is it possible to achieve this any way?

I modified a binary to have the following program header table (printed with `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


As you can see I am trying to piece together the memory page at `0x400000` from two separate &quot;pages&quot; of the file: the first `0x40` bytes from the &quot;page&quot; at offset `0x560000` and the rest from the one at offset `0x1000`.

When I run the executable in GDB, `info proc mappings` has the following output:

      Start Addr           End Addr       Size     Offset  Perms  objfile
        0x400000           0x401000     0x1000     0x1000  r--p   &lt;the executable&gt;
        0x401000           0x477000    0x76000     0x2000  r-xp   &lt;the executable&gt;
        0x477000           0x49f000    0x28000    0x78000  r--p   &lt;the executable&gt;
        0x49f000           0x4a6000     0x7000    0x9f000  rw-p   &lt;the executable&gt;
        0x4a6000           0x4ab000     0x5000        0x0  rw-p   
        0x4ab000           0x4ac000     0x1000        0x0  r--p   &lt;the executable&gt;
  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]


It seems like my first phdr entry has no effect.

</details>


# 答案1
**得分**: 0

```plaintext
我正在尝试将内存页从0x400000的两个单独的“页面”拼接在一起
这样是行不通的:内核将逐个解释和执行“LOAD”指令。给定:
```plaintext
  LOAD           0x0000000000560000 0x0000000000400000 0x0000000000400000
                 0x0000000000000040 0x0000000000000040  R      0x1000
  LOAD           0x0000000000001270 0x0000000000400270 0x0000000000400270
                 0x00000000000002c8 0x00000000000002c8  R      0x1000

内核将执行(相当于)

mmap(0x400000, 0x400, PROT_READ, MAP_FIXED, fd, 0x560000);
mmap(0x400000, 0x400, PROT_READ, MAP_FIXED, fd,   0x1000);

然后第二个mmap会取代第一个,这正是你在GDB中观察到的。


<details>
<summary>英文:</summary>

&gt; I am trying to piece together the memory page at 0x400000 from two separate &quot;pages&quot; of the file

That&#39;s not going to work: the kernel will &quot;interpret&quot; and execute the `LOAD` &quot;instructions&quot; one at a time. Given:

LOAD 0x0000000000560000 0x0000000000400000 0x0000000000400000
0x0000000000000040 0x0000000000000040 R 0x1000
LOAD 0x0000000000001270 0x0000000000400270 0x0000000000400270
0x00000000000002c8 0x00000000000002c8 R 0x1000

the kernel will do (equivalent of)

mmap(0x400000, 0x400, PROT_READ, MAP_FIXED, fd, 0x560000);
mmap(0x400000, 0x400, PROT_READ, MAP_FIXED, fd, 0x1000);

The second `mmap` will then replace the first one, which is exactly what you observed in GDB.

</details>



huangapple
  • 本文由 发表于 2023年5月7日 16:23:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/76192865.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定