使所有页面 readable/writable/executable

Make all pages readable/writable/executable

我想授予对 ELF 二进制文件中所有内存页的完全权限(读取、写入和执行)。理想情况下,我希望能够将此作为对二进制文件或目标文件的转换来执行,就像可以使用 objcopy 更改符号一样。我还没有找到一个好的方法来做到这一点。我也可以接受在启动时涉及 运行 代码的解决方案,该代码在每个页面上调用 mprotect 并带有标志 PROT_READ | PROT_WRITE | PROT_EXEC。我已经简单地尝试过这个,但我还没有找到一个好方法来知道哪些页面被映射,因此哪些页面需要 mprotected。

不需要动态分配的页面拥有所有权限,只需要在程序启动时映射的页面。

I would like to grant full permissions (read, write, and execute) to all memory pages in an ELF binary.

请注意,某些安全策略,例如 selinux 中的 W^X 会阻止您的二进制文件 运行。

Ideally, I'd like to be able to do this as a transformation on a binary or object file

运行 readelf -Wl 在你的二进制文件上。您会看到类似于:

$ readelf -Wl /bin/date

Elf file type is EXEC (Executable file)
Entry point 0x4021cf
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 0x00dde4 0x00dde4 R E 0x200000
  LOAD           0x00de10 0x000000000060de10 0x000000000060de10 0x0004e4 0x0006b0 RW  0x200000
  DYNAMIC        0x00de28 0x000000000060de28 0x000000000060de28 0x0001d0 0x0001d0 RW  0x8
  NOTE           0x000254 0x0000000000400254 0x0000000000400254 0x000044 0x000044 R   0x4
  GNU_EH_FRAME   0x00cb8c 0x000000000040cb8c 0x000000000040cb8c 0x0002f4 0x0002f4 R   0x4
  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10
  GNU_RELRO      0x00de10 0x000000000060de10 0x000000000060de10 0x0001f0 0x0001f0 R   0x1

然后您要做的是将 LOAD 段上的 Flags 更改为 PF_X|PF_W|PF_R。标志是 Elf{32,64}_Phdr table 的一部分,table 的偏移量存储在 Elf{32,64}_Ehdre_phoff 中(存储在每个 ELF 文件)。

查看/usr/include/elf.h。解析这里涉及的固定大小的ELF结构并不复杂。

您不太可能找到任何可以为您执行此操作的标准工具(鉴于这是一件非常不寻常且不安全的事情),但是在 C 中编写更改标志的程序是微不足道的, PythonPerl.

P.S。您可能还需要 "zap" RELRO 段,这可以通过将其 p_type 更改为 PT_NULL.

来完成

I haven't found a good way to know which pages are mapped, and therefore which pages need to be mprotected.

在 Linux 上,您可以解析 /proc/self/maps 以获取该信息。其他操作系统可能会提供不同的方法来实现相同的目的。

以下脚本在代码中实现了 Employed Russian 的回答:

  • RELRO 段的 p_type 设置为 PT_NULL
  • LOAD 段上的 Flags 设置为 PF_X|PF_W|PF_R

python3依赖pyelftools,可以用pip3 install pyelftools安装。

#!/usr/bin/env python3

import sys

from elftools.elf.elffile import ELFFile
from elftools.elf.descriptions import describe_p_type

if len(sys.argv) != 2:
    print("Usage: elf_rwe <elf>")

name = sys.argv[1]
with open(name, "rb") as f:
    elf = ELFFile(f)

    rwe_offsets = []
    relro_offsets = []
    for i in range(elf['e_phnum']):
        program_offset = elf['e_phoff'] + i * elf['e_phentsize']
        f.seek(program_offset)
        program_header = elf.structs.Elf_Phdr.parse_stream(f)

        if program_header['p_type'] == "PT_LOAD":
            rwe_offsets.append(program_offset)
        if program_header['p_type'] == "PT_GNU_RELRO":
            relro_offsets.append(program_offset)

    f.seek(0)
    b = list(f.read())

    # Zap RELRO
    pt_null = 0
    for off in relro_offsets:
        b[off] = pt_null

    # Fix permissions
    p_flags_offset = 4
    for off in rwe_offsets:
        b[off + p_flags_offset] = 0x7 # PF_X|PF_W|PF_R

with open(name, "wb") as f:
    f.write(bytes(b))