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;
}
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.
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.
User contributions licensed under CC BY-SA 3.0