Insert a new section to a binary file?

1

I try to coding a program to insert a empty (as the first step) phdr struct to any elf binary, so I coding like below:

cat add_phdr.c

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

static void calc_dyntables(void **ehdr_ptr, size_t bound_size)
{
    void *ptr = *ehdr_ptr;
    Elf32_Ehdr *Elf32_ptr = NULL;

    Elf32_ptr = (Elf32_Ehdr *)ptr;
    Elf32_Phdr *elf32_phdr = NULL;
    int n, found = 0;

    n = Elf32_ptr->e_phnum;
    elf32_phdr = ptr + Elf32_ptr->e_phoff;
    while (n--) {
        if (elf32_phdr->p_type == PT_DYNAMIC) {
            found = 1;
            break;
        }
        elf32_phdr++;
    }

    if (found) {
        /* printf("=== PT_DYNAMIC: offset: 0x%x, size: 0x%x\n",  */
                /* elf32_phdr->p_offset, elf32_phdr->p_filesz); */
        /* printf("sizeof(Elf32_Dyn): %d\n", sizeof(Elf32_Dyn)); */
    }
    else
        return;

    /* list all of dynamic sections */
    found = 0;
    Elf32_Dyn *dyn_entry = ptr + elf32_phdr->p_offset;
    while (dyn_entry->d_tag != DT_NULL) {
        switch (dyn_entry->d_tag) {
            case 0x6ffffef5:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case 0x6ffffff0:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case 0x6ffffffe:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case 0x0000000c:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case 0x0000000d:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case 0x00000019:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case 0x0000001a:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case 0x00000005:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case 0x00000006:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case 0x00000003:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case 0x00000017:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case 0x00000011:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            default:
                break;
        }
        /* printf("== tag: 0x%x, value: 0x%x\n", dyn_entry->d_tag, */
                /* dyn_entry->d_un.d_val); */
        dyn_entry++;
    }

}

static int write_phdr(int fd_dst, void *ptr, size_t filesize)
{
    /* ptr for Ehdr */
    Elf32_Ehdr *Elf32_ptr = NULL;
    Elf32_ptr = (Elf32_Ehdr *)ptr;

    /* ptr for Phdr */
    Elf32_Phdr *elf32_phdr = NULL;
    /* ptr for Shdr */
    Elf32_Shdr *elf32_shdr = NULL;

    int n;

    /*
     * We must find the phdr array's border, then recalcuate 
     * the offset of phdrs & shdrs which's offset beyond the
     * offset of border.
     */
    off_t old_phdr_border = Elf32_ptr->e_phoff + Elf32_ptr->e_phentsize * Elf32_ptr->e_phnum;
    printf("=== border 0x%x\n", (unsigned int) (0x8048000 + old_phdr_border));
    off_t phdr_size = Elf32_ptr->e_phentsize;

    /* recalcuate the hash table address */
    calc_dyntables(&ptr, phdr_size);
    /* pdhr */
    n = Elf32_ptr->e_phnum;
    elf32_phdr = ptr + Elf32_ptr->e_phoff;

    /* recalculate phdr offset */
    while (n-- > 0) {
        if (elf32_phdr->p_offset  >= old_phdr_border) {
            elf32_phdr->p_offset += phdr_size;
            elf32_phdr->p_vaddr += phdr_size;
            elf32_phdr->p_paddr += phdr_size;
        }
        else {
            if ((elf32_phdr->p_offset + elf32_phdr->p_filesz) >= old_phdr_border) {
                elf32_phdr->p_filesz += phdr_size;
                elf32_phdr->p_memsz += phdr_size;
            }
        }
        elf32_phdr++;
    }

    /* recalculate shdr offset */
    n = Elf32_ptr->e_shnum;
    elf32_shdr = ptr + Elf32_ptr->e_shoff;
    while (n-- > 0) {
        if (elf32_shdr->sh_offset >= old_phdr_border) {
            elf32_shdr->sh_offset += phdr_size;
            elf32_shdr->sh_addr += phdr_size;
        }
        else {
            if ((elf32_shdr->sh_offset + elf32_shdr->sh_size) >= old_phdr_border) {
                elf32_shdr->sh_size += phdr_size;
            }
        }
        elf32_shdr++;
    }

    Elf32_ptr->e_shoff += phdr_size;

    Elf32_Phdr *new_ptr = malloc(sizeof(Elf32_Phdr));
    if (new_ptr == NULL)
        return -1;

    Elf32_ptr->e_phnum += 1;

    /* recalculate the entry */
    if (Elf32_ptr->e_entry > old_phdr_border)
        Elf32_ptr->e_entry += phdr_size;


    write(fd_dst, ptr, old_phdr_border);
    memset(new_ptr, 0, sizeof(Elf32_Phdr));
    new_ptr->p_type = PT_NULL;
    new_ptr->p_offset = 0xffff;
    new_ptr->p_vaddr = 0xffff;
    new_ptr->p_paddr = 0xffff;
    new_ptr->p_filesz = 0x1111;
    new_ptr->p_memsz = 0;
    new_ptr->p_flags = PF_R;
    new_ptr->p_align = 0x4;
    write(fd_dst, new_ptr, phdr_size);
    write(fd_dst, ptr + old_phdr_border, filesize - old_phdr_border);

    free(new_ptr);

    return 0;
}

/*
 * this version of write_phdr will apend phdr to
 * the tail of file, so it wont move any sections
 * or offset, we just need change the entry address.
 */
static int write_phdr2(int fd_dst, void *ptr, size_t filesize)
{
    /* ptr for Ehdr */
    Elf32_Ehdr *Elf32_ptr = NULL;
    Elf32_ptr = (Elf32_Ehdr *)ptr;

    /* ptr for Phdr */
//    Elf32_Phdr *elf32_phdr = NULL;

    Elf32_Phdr *new_ptr = malloc(sizeof(Elf32_Phdr));
    if (new_ptr == NULL)
        return -1;

    off_t phdr_size = Elf32_ptr->e_phentsize;

    /* recalculate the entry */
//    Elf32_ptr->e_entry += phdr_size;

    write(fd_dst, ptr, filesize);

    /* append the phdr to the tail of file */
    memset(new_ptr, 0, sizeof(Elf32_Phdr));
    new_ptr->p_type = PT_NULL;
    new_ptr->p_offset = 0xffff;
    new_ptr->p_vaddr = 0xffff;
    new_ptr->p_paddr = 0xffff;
    new_ptr->p_filesz = 0;
    new_ptr->p_memsz = 0;
    new_ptr->p_flags = PF_R;
    new_ptr->p_align = 0x4;
    write(fd_dst, new_ptr, phdr_size);

    free(new_ptr);

    return 0;
}

/*
 * we'll add a new phdr to the binary file
 */
int main(int argc, char *argv[])
{
    int fd_src, fd_dst;
    //size_t len = 0;
    size_t filesize = 0;
    void *ptr = NULL;   /* ptr to binary file which mapped in memory */
    if (argc != 3) {
        printf("Usage: %s [ src bin ] [ dst bin ]...\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    /*
     * we'll calculate the file size then map to memory
     */
    fd_src = open(argv[1], O_RDONLY);

    if (fd_src < 0) {
        printf("Failed to open %s!\n", argv[1]);
        exit(EXIT_FAILURE);
    }

    fd_dst = open(argv[2], O_CREAT | O_WRONLY | O_TRUNC, 0755);

    if (fd_dst < 0) {
        printf("Failed to open %s!\n", argv[2]);
        exit(EXIT_FAILURE);
    }

    /* get file size with lseek SEEK_END */
    filesize = lseek(fd_src, 0, SEEK_END);
    if (filesize < 0) {
        perror("lseek failed!");
        close(fd_src);
        exit(EXIT_FAILURE);
    }

    ptr = mmap(0, filesize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd_src, 0);
    if (ptr < 0) {
        perror("mmap failed!");
        close(fd_src);
        exit(EXIT_FAILURE);
    }

    if (1)
    write_phdr(fd_dst, ptr, filesize);

    if (0)
    write_phdr2(fd_dst, ptr, filesize);

    /* copy the modified file to dst */
    /* do the clean work */
    munmap(ptr, filesize);
    close(fd_src);
    close(fd_dst);

    return EXIT_SUCCESS;
}

then the Makefile:

cat Makefile
CC  = $(CROSS_COMPILE)gcc
LD  = $(CROSS_COMPILE)ld
LDFLAGS = -m elf_i386
CFLAGS  = -m32 -Wall -fPIE

TARGETS = elf_parser show_addr without_libc add_phdr read_elf hello

all: $(TARGETS)

elf_parser:elf_parser.c
    $(CC) $(CFLAGS) -o $@ $<

show_addr:show_addr.c
    $(CC) $(CFLAGS) -o $@ $<

without_libc:without_libc.c
    $(CC) $(CFLAGS) -nostdlib -o $@ $<

add_phdr:add_phdr.c
    $(CC) $(CFLAGS) -o $@ $<

hello:hello.c
    $(CC) $(CFLAGS) -o $@ $<

read_elf:read_elf.o
    $(LD) $(LDFLAGS) -o $@ $<

read_elf.o:read_elf.asm
    nasm -f elf32 -o $@ $<

clean:
    rm -rf $(TARGETS)
    rm -rf *.o

OK, there are many other code include asm code, I just list the read_elf.asm and hello.c as comparison: cat read_elf.asm:

    global _start
_start:
    call    main
    xor     eax, eax
    inc     eax
    xor     ebx, ebx
    int     0x80

main:
    call    funA
    ret

funA:
    call    funB
    ret
funB:
    call    funC
    ret
funC:
    push    byte 4
    pop     eax
    xor     ebx, ebx
    inc     ebx
    mov     ecx, 0x08048001
    push    byte 3
    pop     edx
    int     0x80
    ret

cat hello.c:

int main()
{
    return 10;
}

Next, let's compile the codes, then run add_phdr program:

./add_phdr read_elf test

then run the test, it works well, but when I try to patch the hello program like below:

./add_phdr hello hello_test
./hello_test

I got this:

Inconsistency detected by ld.so: dl-lookup.c: 876: _dl_setup_hash: Assertion `(bitmask_nwords & (bitmask_nwords - 1)) == 0' failed!

I could not run it any more!
So any tips?

Update:

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

static void calc_symtab(void **ehdr_ptr, size_t bound_size)
{
    void *ptr = *ehdr_ptr;
    Elf32_Shdr *shdr_ptr = NULL;
    Elf32_Ehdr *Elf32_ptr = (Elf32_Ehdr *)ptr;


    int n = Elf32_ptr->e_shnum;
    char *shstrtab = NULL;

    /*
     * first of all, let's get the shstrtab, we'll get 
     * the name of each section with it
     */
    shdr_ptr = (Elf32_Shdr *)(ptr + Elf32_ptr->e_shoff);
    while (n--) {
        if (shdr_ptr->sh_type == SHT_STRTAB) {
            shstrtab = (char *)(ptr + shdr_ptr->sh_offset);
            if (!strcmp(&shstrtab[shdr_ptr->sh_name], ".shstrtab"))
                break;
        }
        shdr_ptr++;
    }

    /* list all of sections */
    shdr_ptr = (Elf32_Shdr *)(ptr + Elf32_ptr->e_shoff);
    n = Elf32_ptr->e_shnum;
    while (n--) {
        if (shdr_ptr->sh_type == SHT_SYMTAB) {
            int n2 = (shdr_ptr->sh_size / sizeof(Elf32_Sym)); 
            Elf32_Sym *sym = (Elf32_Sym *)(ptr + shdr_ptr->sh_offset);
            while (n2--) {
                if (sym->st_value > 0)
                    sym->st_value += bound_size;
                sym++;
            }
        }
        shdr_ptr++;
    }
}

static void calc_dynsym(void **ehdr_ptr, size_t bound_size)
{
    void *ptr = *ehdr_ptr;
    Elf32_Shdr *shdr_ptr = NULL;
    Elf32_Ehdr *Elf32_ptr = (Elf32_Ehdr *)ptr;


    int n = Elf32_ptr->e_shnum;
    char *shstrtab = NULL;

    /*
     * first of all, let's get the shstrtab, we'll get 
     * the name of each section with it
     */
    shdr_ptr = (Elf32_Shdr *)(ptr + Elf32_ptr->e_shoff);
    while (n--) {
        if (shdr_ptr->sh_type == SHT_STRTAB) {
            shstrtab = (char *)(ptr + shdr_ptr->sh_offset);
            if (!strcmp(&shstrtab[shdr_ptr->sh_name], ".shstrtab"))
                break;
        }
        shdr_ptr++;
    }

    /* list all of sections */
    shdr_ptr = (Elf32_Shdr *)(ptr + Elf32_ptr->e_shoff);
    n = Elf32_ptr->e_shnum;
    while (n--) {
        if (shdr_ptr->sh_type == SHT_DYNSYM) {
            int n2 = (shdr_ptr->sh_size / sizeof(Elf32_Sym)); 
            Elf32_Sym *sym = (Elf32_Sym *)(ptr + shdr_ptr->sh_offset);
            while (n2--) {
                if (sym->st_value > 0)
                    sym->st_value += bound_size;
                sym++;
            }
        }
        shdr_ptr++;
    }
}

static void calc_relocs(void **ehdr_ptr, size_t bound_size)
{
    void *ptr = *ehdr_ptr;
    Elf32_Shdr *shdr_ptr = NULL;
    Elf32_Ehdr *Elf32_ptr = (Elf32_Ehdr *)ptr;


    int n = Elf32_ptr->e_shnum;
    char *shstrtab = NULL;

    /*
     * first of all, let's get the shstrtab, we'll get 
     * the name of each section with it
     */
    shdr_ptr = (Elf32_Shdr *)(ptr + Elf32_ptr->e_shoff);
    while (n--) {
        if (shdr_ptr->sh_type == SHT_STRTAB) {
            shstrtab = (char *)(ptr + shdr_ptr->sh_offset);
            if (!strcmp(&shstrtab[shdr_ptr->sh_name], ".shstrtab"))
                break;
        }
        shdr_ptr++;
    }

    /* list all of sections */
    shdr_ptr = (Elf32_Shdr *)(ptr + Elf32_ptr->e_shoff);
    n = Elf32_ptr->e_shnum;
    while (n--) {
        if (shdr_ptr->sh_type == SHT_REL || shdr_ptr->sh_type == SHT_RELA) {

            int n2 = (shdr_ptr->sh_size / sizeof(Elf32_Rel)); 
            Elf32_Rel *rel = (Elf32_Rel *)(ptr + shdr_ptr->sh_offset);
            while (n2--) {
                rel->r_offset += bound_size;
                rel++;
            }
        }
        shdr_ptr++;
    }
}

static void calc_dyntables(void **ehdr_ptr, size_t bound_size)
{
    void *ptr = *ehdr_ptr;
    Elf32_Ehdr *Elf32_ptr = NULL;

    Elf32_ptr = (Elf32_Ehdr *)ptr;
    Elf32_Phdr *elf32_phdr = NULL;
    int n, found = 0;

    n = Elf32_ptr->e_phnum;
    elf32_phdr = ptr + Elf32_ptr->e_phoff;
    while (n--) {
        if (elf32_phdr->p_type == PT_DYNAMIC) {
            found = 1;
            break;
        }
        elf32_phdr++;
    }

    if (found) {
        /* printf("=== PT_DYNAMIC: offset: 0x%x, size: 0x%x\n",  */
                /* elf32_phdr->p_offset, elf32_phdr->p_filesz); */
        /* printf("sizeof(Elf32_Dyn): %d\n", sizeof(Elf32_Dyn)); */
    }
    else
        return;

    /* list all of dynamic sections */
    found = 0;
    Elf32_Dyn *dyn_entry = ptr + elf32_phdr->p_offset;
    while (dyn_entry->d_tag != DT_NULL) {
        switch (dyn_entry->d_tag) {
            case DT_GNU_HASH:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case DT_VERSYM:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case DT_VERNEED:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case DT_INIT:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case DT_FINI:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case DT_INIT_ARRAY:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case DT_FINI_ARRAY:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case DT_STRTAB:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case DT_SYMTAB:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case DT_PLTGOT:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case DT_JMPREL:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            case DT_REL:
                dyn_entry->d_un.d_ptr += bound_size;
                break;
            default:
                break;
        }
        /* printf("== tag: 0x%x, value: 0x%x\n", dyn_entry->d_tag, */
                /* dyn_entry->d_un.d_val); */
        dyn_entry++;
    }

}

static int write_phdr(int fd_dst, void *ptr, size_t filesize)
{
    /* ptr for Ehdr */
    Elf32_Ehdr *Elf32_ptr = NULL;
    Elf32_ptr = (Elf32_Ehdr *)ptr;

    /* ptr for Phdr */
    Elf32_Phdr *elf32_phdr = NULL;
    /* ptr for Shdr */
    Elf32_Shdr *elf32_shdr = NULL;

    int n;

    /*
     * We must find the phdr array's border, then recalcuate 
     * the offset of phdrs & shdrs which's offset beyond the
     * offset of border.
     */
    off_t old_phdr_border = Elf32_ptr->e_phoff + Elf32_ptr->e_phentsize * Elf32_ptr->e_phnum;
    printf("=== border 0x%x\n", (unsigned int) (0x8048000 + old_phdr_border));
    off_t phdr_size = Elf32_ptr->e_phentsize;

    /* recalcuate the hash table address */
    calc_dyntables(&ptr, phdr_size);
    calc_relocs(&ptr, phdr_size);
    calc_symtab(&ptr, phdr_size);
    calc_dynsym(&ptr, phdr_size);
    /* pdhr */
    n = Elf32_ptr->e_phnum;
    elf32_phdr = ptr + Elf32_ptr->e_phoff;

    /* recalculate phdr offset */
    while (n-- > 0) {
        if (elf32_phdr->p_offset  >= old_phdr_border) {
            elf32_phdr->p_offset += phdr_size;
            elf32_phdr->p_vaddr += phdr_size;
            elf32_phdr->p_paddr += phdr_size;
        }
        else {
            if ((elf32_phdr->p_offset + elf32_phdr->p_filesz) >= old_phdr_border) {
                elf32_phdr->p_filesz += phdr_size;
                elf32_phdr->p_memsz += phdr_size;
            }
        }
        elf32_phdr++;
    }

    /* recalculate shdr offset */
    n = Elf32_ptr->e_shnum;
    elf32_shdr = ptr + Elf32_ptr->e_shoff;
    while (n-- > 0) {
        if (elf32_shdr->sh_offset >= old_phdr_border) {
            elf32_shdr->sh_offset += phdr_size;
            if (elf32_shdr->sh_addr > 0)
                elf32_shdr->sh_addr += phdr_size;
        }
        else {
            if ((elf32_shdr->sh_offset + elf32_shdr->sh_size) >= old_phdr_border) {
                elf32_shdr->sh_size += phdr_size;
            }
        }
        elf32_shdr++;
    }

    Elf32_ptr->e_shoff += phdr_size;

    Elf32_Phdr *new_ptr = malloc(sizeof(Elf32_Phdr));
    if (new_ptr == NULL)
        return -1;

    Elf32_ptr->e_phnum += 1;

    /* recalculate the entry */
    if (Elf32_ptr->e_entry >= old_phdr_border)
        Elf32_ptr->e_entry += phdr_size;


    write(fd_dst, ptr, old_phdr_border);
    memset(new_ptr, 0, sizeof(Elf32_Phdr));
    new_ptr->p_type = PT_NULL;
    new_ptr->p_offset = 0xffff;
    new_ptr->p_vaddr = 0xffff;
    new_ptr->p_paddr = 0xffff;
    new_ptr->p_filesz = 0x1111;
    new_ptr->p_memsz = 0;
    new_ptr->p_flags = PF_R;
    new_ptr->p_align = 0x4;
    write(fd_dst, new_ptr, phdr_size);
    write(fd_dst, ptr + old_phdr_border, filesize - old_phdr_border);

    free(new_ptr);

    return 0;
}

/*
 * this version of write_phdr will apend phdr to
 * the tail of file, so it wont move any sections
 * or offset, we just need change the entry address.
 */
static int write_phdr2(int fd_dst, void *ptr, size_t filesize)
{
    /* ptr for Ehdr */
    Elf32_Ehdr *Elf32_ptr = NULL;
    Elf32_ptr = (Elf32_Ehdr *)ptr;

    /* ptr for Phdr */
//    Elf32_Phdr *elf32_phdr = NULL;

    Elf32_Phdr *new_ptr = malloc(sizeof(Elf32_Phdr));
    if (new_ptr == NULL)
        return -1;

    off_t phdr_size = Elf32_ptr->e_phentsize;

    /* recalculate the entry */
//    Elf32_ptr->e_entry += phdr_size;

    write(fd_dst, ptr, filesize);

    /* append the phdr to the tail of file */
    memset(new_ptr, 0, sizeof(Elf32_Phdr));
    new_ptr->p_type = PT_NULL;
    new_ptr->p_offset = 0xffff;
    new_ptr->p_vaddr = 0xffff;
    new_ptr->p_paddr = 0xffff;
    new_ptr->p_filesz = 0;
    new_ptr->p_memsz = 0;
    new_ptr->p_flags = PF_R;
    new_ptr->p_align = 0x4;
    write(fd_dst, new_ptr, phdr_size);

    free(new_ptr);

    return 0;
}

/*
 * we'll add a new phdr to the binary file
 */
int main(int argc, char *argv[])
{
    int fd_src, fd_dst;
    //size_t len = 0;
    size_t filesize = 0;
    void *ptr = NULL;   /* ptr to binary file which mapped in memory */
    if (argc != 3) {
        printf("Usage: %s [ src bin ] [ dst bin ]...\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    /*
     * we'll calculate the file size then map to memory
     */
    fd_src = open(argv[1], O_RDONLY);

    if (fd_src < 0) {
        printf("Failed to open %s!\n", argv[1]);
        exit(EXIT_FAILURE);
    }

    fd_dst = open(argv[2], O_CREAT | O_WRONLY | O_TRUNC, 0755);

    if (fd_dst < 0) {
        printf("Failed to open %s!\n", argv[2]);
        exit(EXIT_FAILURE);
    }

    /* get file size with lseek SEEK_END */
    filesize = lseek(fd_src, 0, SEEK_END);
    if (filesize < 0) {
        perror("lseek failed!");
        close(fd_src);
        exit(EXIT_FAILURE);
    }

    ptr = mmap(0, filesize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd_src, 0);
    if (ptr < 0) {
        perror("mmap failed!");
        close(fd_src);
        exit(EXIT_FAILURE);
    }

    if (1)
    write_phdr(fd_dst, ptr, filesize);

    if (0)
    write_phdr2(fd_dst, ptr, filesize);

    /* copy the modified file to dst */
    /* do the clean work */
    munmap(ptr, filesize);
    close(fd_src);
    close(fd_dst);

    return EXIT_SUCCESS;
}
c
linux
nasm
elf
asked on Stack Overflow Aug 29, 2013 by liunx • edited Sep 3, 2013 by liunx

2 Answers

2

So any tips?

ELF format has many pointers inside of it, and if you move data around (as your program does), you must adjust all of these pointers to point to new location/offset (which your program fails to do).

ELF is really not designed for the kinds of post-link processing you are trying to perform.

Your immediate problem is that hello contains a DT_HASH or DT_GNU_HASH (or both), that still point to the old offset of .hash or .gnu.hash section, but you've moved the bits around, and so the old offset no longer contains a valid hash table that the runtime loader expects to find there.

Running

for exe in hello hello_test; do
  readelf -d $exe | grep HASH
  readelf -WS $exe | grep hash
done

should prove that this is indeed the case.

EDIT:

case 0x6ffffef5:
           dyn_entry->d_un.d_ptr += bound_size;
           break;

Don't ever do that: it makes your code impossible to read and understand. Do this instead:

#include <elf.h>
...
  case DT_GNU_HASH:
            dyn_entry->d_un.d_ptr += bound_size;
            break;

Since you have 12 such cases, I refuse to map them back to their symbolic names.

In any case, my answer stands: you are not updating some pointer somewhere, which makes your resulting ELF self-inconsistent, which makes the dynamic loader unhappy.

If you really want to persist in this direction, install glibc debug symbols, and debug the loader like this:

gdb /path/to/ld-linux.so ./hello_test

That should allow you to identify which pointer you've failed to update.

answered on Stack Overflow Aug 31, 2013 by Employed Russian • edited Sep 2, 2013 by Employed Russian
0

Did you look at the new executable using "readelf"?

I myself would check if the phdr is (already) at the end of the file (phoff + phentsize * phnum == file_size). If no I'd simply append a copy of the phdr table to the end of the file and modify the phoff member in the elf header.

Then I can append phdr sections at the end of the file (and I do not have to move sections).

The original phdr table would remain in the file but be inactive.

answered on Stack Overflow Aug 29, 2013 by Martin Rosenau

User contributions licensed under CC BY-SA 3.0