memset/memcpy 在 mmap 区域上失败
memset/memcpy on mmap region fails
我正在尝试从另一个程序加载静态链接 程序并执行它。我的步骤是:
- 解析 ELF
- 从程序头中解析段
- 每个 PT_LOAD
- 加载它
- 跳转到起始地址
如果 elf_bytes
是 mmap 的 ELF 文件,则加载 PT_LOAD 段是 load(&p, elf_bytes + p.p_offset)
。
加载函数:
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;
}
memset和memcpy均失败;内核在地址 0x400000 发送一个 SIGSEGV,恰好是 region
。
什么都没有:
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
因为加载程序从一个非常高的地址开始(实际上是为了避免这个问题)。这通过链接 -Wl,-Ttext-segment=00007ff000000000
.
(我也尝试过先对该区域进行 munmap。)
void *region = mmap(
base_addr,
total_bytes,
phdr->p_flags,
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
0, 0
);
你mmap
通常的第一段涵盖.text
并且有R-X
(但没有写) 权限。
在 .p_flags
中没有 write 权限,尝试写入该内存(自然)失败。
您可能想改用 phdr->p_flags | PROT_WRITE
。
注意:某些安全策略(例如 SELinux)不允许可写和可执行映射。在这样的系统上,您需要使用 PROT_WRITE
映射内存,根据需要复制数据,然后 mprotect
进行所需的保护。
我正在尝试从另一个程序加载静态链接 程序并执行它。我的步骤是:
- 解析 ELF
- 从程序头中解析段
- 每个 PT_LOAD
- 加载它
- 跳转到起始地址
如果 elf_bytes
是 mmap 的 ELF 文件,则加载 PT_LOAD 段是 load(&p, elf_bytes + p.p_offset)
。
加载函数:
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;
}
memset和memcpy均失败;内核在地址 0x400000 发送一个 SIGSEGV,恰好是 region
。
什么都没有:
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
因为加载程序从一个非常高的地址开始(实际上是为了避免这个问题)。这通过链接 -Wl,-Ttext-segment=00007ff000000000
.
(我也尝试过先对该区域进行 munmap。)
void *region = mmap(
base_addr,
total_bytes,
phdr->p_flags,
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
0, 0
);
你mmap
通常的第一段涵盖.text
并且有R-X
(但没有写) 权限。
在 .p_flags
中没有 write 权限,尝试写入该内存(自然)失败。
您可能想改用 phdr->p_flags | PROT_WRITE
。
注意:某些安全策略(例如 SELinux)不允许可写和可执行映射。在这样的系统上,您需要使用 PROT_WRITE
映射内存,根据需要复制数据,然后 mprotect
进行所需的保护。