【问题标题】:ELF - Getting a SEGFAULT when changing the entry pointELF - 更改入口点时获取 SEGFAULT
【发布时间】:2019-04-20 04:18:49
【问题描述】:

我正在尝试通过e_entry 字段直接修补 ELF 文件的入口点:

Elf64_Ehdr *ehdr = NULL;
Elf64_Phdr *phdr = NULL;
Elf64_Shdr *shdr = NULL;

if (argc < 2)
{
    printf("Usage: %s <executable>\n", argv[0]);
    exit(EXIT_SUCCESS);
}

fd = open(argv[1], O_RDWR);

if (fd < 0)
{
    perror("open");
    exit(EXIT_FAILURE);
}

if (fstat(fd, &st) < 0)
{
    perror("fstat");
    exit(EXIT_FAILURE);
}

/* map whole executable into memory */
mapped_file = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

if (mapped_file < 0)
{
    perror("mmap");
    exit(EXIT_FAILURE);
}

// check for an ELF file
check_elf(mapped_file, argv);

ehdr = (Elf64_Ehdr *) mapped_file;
phdr = (Elf64_Phdr *) &mapped_file[ehdr->e_phoff];
shdr = (Elf64_Shdr *) &mapped_file[ehdr->e_shoff];

mprotect((void *)((uintptr_t)&ehdr->e_entry & ~(uintptr_t)4095), 4096, PROT_READ | PROT_WRITE);

if (ehdr->e_type != ET_EXEC)
{
    fprintf(stderr, "%s is not an ELF executable.\n", argv[1]);
    exit(EXIT_FAILURE);
}

printf("Program entry point: %08x\n", ehdr->e_entry);



int text_found = 0;
uint64_t test_addr;
uint64_t text_end;
size_t test_len = strlen(shellcode);
int text_idx;
for (i = 0; i < ehdr->e_phnum; ++i)
{

    if (text_found)
    {
        phdr[i].p_offset += PAGE_SIZE;
        continue;
    }


    if (phdr[i].p_type == PT_LOAD && phdr[i].p_flags == ( PF_R | PF_X))
    {

        test_addr = phdr[i].p_vaddr + phdr[i].p_filesz;
        text_end = phdr[i].p_vaddr + phdr[i].p_filesz;

        printf("TEXT SEGMENT ends at 0x%x\n", text_end);


        puts("Changing entry point...");
        ehdr->e_entry = (Elf64_Addr *) test_addr;

        memmove(test_addr, shellcode, test_len);


        phdr[i].p_filesz += test_len;
        phdr[i].p_memsz += test_len;    

        text_found++;
    }


}

//patch sections

for (i = 0; i < ehdr->e_shnum; ++i)
{
    if (shdr->sh_offset >= test_addr)
        shdr->sh_offset += PAGE_SIZE;

    else
        if (shdr->sh_size + shdr->sh_addr == test_addr)
            shdr->sh_size += test_len;
}

ehdr->e_shoff += PAGE_SIZE;
close(fd);


}

在这种情况下,shellcode 只是一堆带有 int3 指令的 NOP。
我确保调整了这个新代码之后的段和节,但问题是,一旦我修补了入口点,程序就会崩溃,这是为什么呢?

【问题讨论】:

  • 你能发布 gdb stacktrace 吗?
  • @yugr #0 0x00007ffff7e7cb09 in __memmove_sse2_unaligned_erms()
  • 所以当你修改入口点或执行生成的 ELF 时它会中止?
  • 请提供stackoverflow.com/help/mcve。没有它,任何人都会猜到您做错了什么(到目前为止您提供的输出似乎彼此不一致)。
  • 问题出在memmove(test_addr, shellcode, test_len);这一行,因为test_addr不是程序地址空间中的指针,而是目标ELF文件中的某种偏移量。

标签: c x86-64 elf mmap


【解决方案1】:

我怀疑您没有启用对程序标头的写访问。你可以通过类似的方式来做到这一点

const uintptr_t page_size = 4096;
mprotect((void *)((uintptr_t)&ehdr->e_entry & ~(uintptr_t)4095), 4096, PROT_READ | PROT_WRITE);
ehdr->e_entry = test_addr;

【讨论】:

  • 恐怕情况并非如此,因为我使用以下内容映射文件:mmap(NULL, st.st_size, PROT |READ | PROT_WRITE | MAP_SHARED, fd, 0); 我认为,与 mprotect 具有相同的效果,对吧?
  • 另外,我刚刚尝试使用 mprotect 但它仍然出现故障
【解决方案2】:

改变:

memmove(test_addr, shellcode, test_len);

到:

memmove(mapped_file + phdr[i].p_offset + phdr[i].p_filesz, shellcode, test_len);

似乎可以解决您的问题。 test_addr 是属于您映射的文件的虚拟地址;您不能直接将其用作指针。您要处理的位是文件映射地址、p_offset 和 p_filesz。

【讨论】:

    猜你喜欢
    • 2017-10-13
    • 2023-03-04
    • 2011-12-28
    • 2017-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-20
    • 1970-01-01
    相关资源
    最近更新 更多