Question

I was trying to make the .text section of an elf binary writable using objcopy --writable-text executable_name. The command executes normally without any errors.

On checking the section permission through readelf I can see that the text section is still has only read and execute permissions.

On going through the objcopy man pages for this particular option is is mentioned that the option is not meaningful for all the binary formats. (Is this the reason I am not able to do so ?).

Can anyone point out what I am missing here.

Thanks

(Ubuntu x86_64 bit machine, GNU objcopy (GNU Binutils for Ubuntu) 2.22.90.20120924)

Était-ce utile?

La solution

On going through the objcopy man pages for this particular option is is mentioned that the option is not meaningful for all the binary formats. (Is this the reason I am not able to do so ?).

Yes.

At this rather detailed description of special sections of the ELF format, you see that .text has the SHF_ALLOC + SHF_EXECINSTR attributes (has space allocated for it and the space has executable code in it), but not SHF_WRITE (space can be written to). What you are asking objcopy to do simply isn't valid for ELF .text sections.

Autres conseils

First complete this: objcopy --writable-text --set-section-flags .text=CONTENTS,ALLOC,LOAD,CODE

Then objdump -x or readelf -a to view the loading section table which are typically after the program header. Please see the man page for ELF. For 32-bit executables for example:

Open the binary with hexedit and look at the value at file offset 0x1C (often 0x34), then traverse through the 0x20 byte structures (a size listed at file offset 0x2a) until you find the one which you identified in the previous dump as containing the .text section. The second to last long value will be 00000005 (05 00 00 00) and needs to have write added which will become thereby 00000007 (07 00 00 00). Now it will work as expected without any limitations such as shared library issues with -Wl,--omagic. A bit technical, but takes a few seconds to do.

Regardless, this one bit flag has caused countless problems and no explanation has clarified this small point which allows it to work flawlessly.

A code solution can be easily compiled with GCC to do the change which is likely easier and a better solution if doing it routinely:

#include <stdlib.h>
#include <stdio.h>
#include <elf.h>

int main(int argc, char** argv)
{
    if (argc <= 1) return -1;
    FILE* fp = fopen(argv[1], "r+");
    Elf64_Ehdr teh;
    fread(&teh, sizeof(teh), 1, fp);
    fseek(fp, 0, SEEK_SET);
    if (teh.e_ident[EI_CLASS] == ELFCLASS64) {
        Elf64_Ehdr eh;
        fread(&eh, sizeof(eh), 1, fp);
        Elf64_Phdr* ph = malloc(eh.e_phnum * eh.e_phentsize);
        Elf64_Shdr* sh = malloc(eh.e_shnum * eh.e_shentsize);
        fseek(fp, eh.e_phoff, SEEK_SET);
        fread(ph, eh.e_phentsize, eh.e_phnum, fp);
        fseek(fp, eh.e_shoff, SEEK_SET);
        fread(sh, eh.e_shentsize, eh.e_shnum, fp);
        for (int i = 0; i < eh.e_phnum; i++) {
            if (ph[i].p_vaddr <= eh.e_entry && ph[i].p_vaddr + ph[i].p_memsz > eh.e_entry) {
            fseek(fp, eh.e_phoff + i * eh.e_phentsize + (unsigned int)&((Elf64_Phdr*)0)->p_flags, SEEK_SET);
            ph[i].p_flags |= PF_W;
            fwrite(&ph[i].p_flags, sizeof(ph[i].p_flags), 1, fp);
            }
        }
        for (int i = 0; i < eh.e_shnum; i++) {
            if (sh[i].sh_addr <= eh.e_entry && sh[i].sh_addr + sh[i].sh_size > eh.e_entry) {
            fseek(fp, eh.e_shoff + i * eh.e_shentsize + (unsigned int)&((Elf64_Shdr*)0)->sh_flags, SEEK_SET);
            sh[i].sh_flags |= SHF_WRITE;
            fwrite(&sh[i].sh_flags, sizeof(sh[i].sh_flags), 1, fp);
            }       
        }
        free(ph);
        free(sh);
    } else {
        Elf32_Ehdr eh;
        fread(&eh, sizeof(eh), 1, fp);
        Elf32_Phdr* ph = malloc(eh.e_phnum * eh.e_phentsize);
        Elf32_Shdr* sh = malloc(eh.e_shnum * eh.e_shentsize);
        fseek(fp, eh.e_phoff, SEEK_SET);
        fread(ph, eh.e_phentsize, eh.e_phnum, fp);
        fseek(fp, eh.e_shoff, SEEK_SET);
        fread(sh, eh.e_shentsize, eh.e_shnum, fp);
        for (int i = 0; i < eh.e_phnum; i++) {
            if (ph[i].p_vaddr <= eh.e_entry && ph[i].p_vaddr + ph[i].p_memsz > eh.e_entry) {
            fseek(fp, eh.e_phoff + i * eh.e_phentsize + (unsigned int)&((Elf32_Phdr*)0)->p_flags, SEEK_SET);
            ph[i].p_flags |= PF_W;
            fwrite(&ph[i].p_flags, sizeof(ph[i].p_flags), 1, fp);
            }
        }
        for (int i = 0; i < eh.e_shnum; i++) {
            if (sh[i].sh_addr <= eh.e_entry && sh[i].sh_addr + sh[i].sh_size > eh.e_entry) {
            fseek(fp, eh.e_shoff + i * eh.e_shentsize + (unsigned int)&((Elf32_Shdr*)0)->sh_flags, SEEK_SET);
            sh[i].sh_flags |= SHF_WRITE;
            fwrite(&sh[i].sh_flags, sizeof(sh[i].sh_flags), 1, fp);
            }       
        }
        free(ph);
        free(sh);
    }
    fflush(fp);
    fclose(fp);
    return 0;
}

On Debian i can just link with -N and that produces an executable with a writable .text

so: ld -N obj.o

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top