memset/memcpy on mmap region fails

走远了吗. 提交于 2019-12-11 08:51:58

问题


I'm trying to load a statically linked program from another one and execute it. My steps are:

  • Parse the ELF
  • Parse the segments from the program headers
  • For each PT_LOAD
  • Load it
  • Jump to the starting address

If elf_bytes is the mmap'ed ELF file, loading a PT_LOAD segment is load(&p, elf_bytes + p.p_offset).

The load function:

int load(const Elf64_Phdr *phdr, const void *elf_bytes_for_phdr) {
    fprintf(stderr, "loading phdr of type %x from 0x%x to +=%zu bytes\n", phdr->p_type, phdr->p_vaddr, phdr->p_memsz);
    const size_t pagesize = getpagesize();
    const size_t unaligned_bytes = phdr->p_vaddr % pagesize;

    void *base_addr = phdr->p_vaddr - unaligned_bytes;
    size_t total_bytes = phdr->p_memsz + unaligned_bytes;

    void *region = mmap(
            base_addr,
            total_bytes,
            phdr->p_flags,
            MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
            0, 0
    );
    if (region != MAP_FAILED) {
        memset(region, 0, unaligned_bytes);
        // return memcpy(region + unaligned_bytes, elf_bytes_for_phdr, phdr->p_filesz) == region + unaligned_bytes;
        return memset(region + unaligned_bytes, /*elf_bytes_for_phdr*/0, 1) == region + unaligned_bytes;
    }
    return 1;
}

Both memset and memcpy fail; the kernel sends a SIGSEGV at address 0x400000, which happens to be exactly region. There is nothing there:

gdb$ shell pmap 10751
00007ff000000000      8K r-x-- ld_simple_loader
00007ff000201000      4K r---- ld_simple_loader
00007ff000202000      4K rw--- ld_simple_loader
00007ffff79e4000   1948K r-x-- libc-2.27.so
00007ffff7bcb000   2048K ----- libc-2.27.so
00007ffff7dcb000     16K r---- libc-2.27.so
00007ffff7dcf000      8K rw--- libc-2.27.so
00007ffff7dd1000     16K rw---   [ anon ]
00007ffff7dd5000    156K r-x-- ld-2.27.so
00007ffff7fdc000      8K rw---   [ anon ]
00007ffff7ff7000     12K r----   [ anon ]
00007ffff7ffa000      8K r-x--   [ anon ]
00007ffff7ffc000      4K r---- ld-2.27.so
00007ffff7ffd000      4K rw--- ld-2.27.so
00007ffff7ffe000      4K rw---   [ anon ]
00007ffffffde000    132K rw---   [ stack ]
ffffffffff600000      4K r-x--   [ anon ]
 total             4384K

because the loader starts at a very high address (to avoid this issue, actually). This by linking with -Wl,-Ttext-segment=00007ff000000000.

(I also tried munmap'ing the region first.)


回答1:


void *region = mmap(
        base_addr,
        total_bytes,
        phdr->p_flags,
        MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
        0, 0
);

The first segment you mmap usually covers .text and has R-X (but no write) permissions.

Without the write permission in .p_flags, attempts to write to that memory (naturally) fail.

You probably want to use phdr->p_flags | PROT_WRITE instead.

Beware: certain security policies (such as SELinux) disallow writeable and executable mappings. On such a system, you would need to map the memory with PROT_WRITE, copy data as appropriate, and then mprotect with desired protections.



来源:https://stackoverflow.com/questions/55552126/memset-memcpy-on-mmap-region-fails

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!