更改 ELF 文件的入口点

逆向工程 linux 小精灵 x86-64
2021-06-15 21:24:53

我正在尝试进行一个小实验,它基本上包括更改 ELF 文件的 EP 和执行 exit(9) 系统调用,而不返回到 OEP。如图所示,一切似乎都很好在此处输入图片说明

但是,当我运行原始程序时,它会出现段错误而不是通过exit(9) 退出,我不知道为什么。这里到底发生了什么?

编辑: 这是我如何更改入口点:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <elf.h>
#include <stdint.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>


#define INVALID_IDX -32
#define PAGE_SIZE   4096

int check_elf(uint8_t *, char **);




// exit(9) 
unsigned char shellcode[] = "\x48\x31\xc0"
                "\xb0\x3c"
                "\x40\xb7\x09"
                "\x0f\x05";


unsigned char nop_sled[] = "\x90\x90\x90\x90\x90"
               "\x90\x90\x90\x90\x90"
               "\x90\x90\xcc\x90\x90"
               "\x90\x90\x90\x90\x90";


int main(int argc, char **argv)
{
    int fd, i;
    uint8_t *mapped_file;
    struct stat st;
    char *interp;
    int magic = 1337;

    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_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];


    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);


    char jmp_code[7];
    int text_found = 0;
    uint64_t parasite_addr;
    uint64_t text_end;
    size_t parasite_len = strlen(shellcode);
    int text_idx = INVALID_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))
        {
            // set parasite  to the end of the text segment
            parasite_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);
            text_idx = i;

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

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




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

            text_found++;
        }


    }

    //patch sections

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

        else
            if (shdr->sh_size + shdr->sh_addr == parasite_addr)
                shdr->sh_size += parasite_len;
    }

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


}



int check_elf(uint8_t *mapped_file, char **argv)
{
    const uint8_t magic[] = {0x7F, 'E', 'L', 'F'};

    if (memcmp(magic, mapped_file, sizeof(magic)))
    {
        fprintf(stderr, "%s is not an ELF file.\n", argv[1]);
        exit(EXIT_FAILURE);
    }

    return 1;
}

至于我试图更改其入口点的程序,它只是一个简单的 hello world。

1个回答

注释掉或简单地删除行

ehdr->e_shoff += PAGE_SIZE;

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

并改变 shdr->sh_offsets到 shdr[i].sh_offset小号

说明: 您可以运行readelf -a hello以获取更多详细信息。它将输出以下问题:

readelf: Error: Reading 1856 bytes extends past end of file for section headers
readelf: Error: Section headers are not available!
readelf: Warning: note with invalid namesz and/or descsz found at offset 0xc
readelf: Warning:  type: 0x600ff0, namesize: 0x00000000, descsize: 0x00150003, alignment: 4

这意味着您的section headers地址也增加PAGE_SIZE了。事实上,修改后它等于:

Start of section headers:          10544 (bytes into file)

之前:

Start of section headers:          6448 (bytes into file)

所以你不想添加PAGE_SIZEehdr->e_shoff,否则section headers现在的开始指向一些随机数据。

关于phdr[i].p_offset's,我认为您根本不必更改它们,因为它们只是段偏移量。

注意: GCC默认情况下编译为位置独立的可执行文件,因此它将具有e_type相等3ET_DYN而不是ET_EXEC),但仍将是一个ELF可执行文件。