Back to index

enigmail  1.4.3
Classes | Defines | Typedefs | Functions | Variables
elfhack.cpp File Reference
#include <assert.h>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include "elfxx.h"

Go to the source code of this file.

Classes

struct  wrapped< T >
class  Elf_Addr_Traits
class  Elf_RelHack_Traits
class  ElfRelHack_Section
class  ElfRelHackCode_Section
class  ElfRelHackCode_Section::pc32_relocation
class  ElfRelHackCode_Section::arm_plt32_relocation
class  ElfRelHackCode_Section::arm_thm_jump24_relocation
class  ElfRelHackCode_Section::gotoff_relocation

Defines

#define ver   "0"
#define elfhack_data   ".elfhack.data.v" ver
#define elfhack_text   ".elfhack.text.v" ver
#define R_ARM_V4BX   0x28
#define R_ARM_THM_JUMP24   0x1e
#define REL(machine, type)   (EM_ ## machine | (R_ ## machine ## _ ## type << 8))

Typedefs

typedef serializable
< Elf_RelHack_Traits
Elf_RelHack

Functions

template<typename Rel_Type >
int do_relocation_section (Elf *elf, unsigned int rel_type, unsigned int rel_type2)
static int backup_file (const char *name)
void do_file (const char *name, bool backup=false, bool force=false)
int main (int argc, char *argv[])

Variables

char * rundir = NULL

Class Documentation

struct wrapped

template<typename T>
struct wrapped< T >

Definition at line 59 of file elfhack.cpp.

Collaboration diagram for wrapped< T >:
Class Members
T value

Define Documentation

#define elfhack_data   ".elfhack.data.v" ver

Definition at line 46 of file elfhack.cpp.

#define elfhack_text   ".elfhack.text.v" ver

Definition at line 47 of file elfhack.cpp.

Definition at line 53 of file elfhack.cpp.

#define R_ARM_V4BX   0x28

Definition at line 50 of file elfhack.cpp.

#define REL (   machine,
  type 
)    (EM_ ## machine | (R_ ## machine ## _ ## type << 8))
#define ver   "0"

Definition at line 45 of file elfhack.cpp.


Typedef Documentation

Definition at line 86 of file elfhack.cpp.


Function Documentation

static int backup_file ( const char *  name) [inline, static]

Definition at line 497 of file elfhack.cpp.

{
    std::string fname(name);
    fname += ".bak";
    return rename(name, fname.c_str());
}

Here is the caller graph for this function:

void do_file ( const char *  name,
bool  backup = false,
bool  force = false 
)

Definition at line 504 of file elfhack.cpp.

{
    std::ifstream file(name, std::ios::in|std::ios::binary);
    Elf *elf = new Elf(file);
    unsigned int size = elf->getSize();
    fprintf(stderr, "%s: ", name);
    if (elf->getType() != ET_DYN) {
        fprintf(stderr, "Not a shared object. Skipping\n");
        delete elf;
        return;
    }

    for (ElfSection *section = elf->getSection(1); section != NULL;
         section = section->getNext()) {
        if (section->getName() &&
            (strncmp(section->getName(), ".elfhack.", 9) == 0)) {
            fprintf(stderr, "Already elfhacked. Skipping\n");
            delete elf;
            return;
        }
    }

    int exit = -1;
    switch (elf->getMachine()) {
    case EM_386:
        exit = do_relocation_section<Elf_Rel>(elf, R_386_RELATIVE, R_386_32);
        break;
    case EM_X86_64:
        exit = do_relocation_section<Elf_Rela>(elf, R_X86_64_RELATIVE, R_X86_64_64);
        break;
    case EM_ARM:
        exit = do_relocation_section<Elf_Rel>(elf, R_ARM_RELATIVE, R_ARM_ABS32);
        break;
    }
    if (exit == 0) {
        if (!force && (elf->getSize() >= size)) {
            fprintf(stderr, "No gain. Skipping\n");
        } else if (backup && backup_file(name) != 0) {
            fprintf(stderr, "Couln't create backup file\n");
        } else {
            std::ofstream ofile(name, std::ios::out|std::ios::binary|std::ios::trunc);
            elf->write(ofile);
            fprintf(stderr, "Reduced by %d bytes\n", size - elf->getSize());
        }
    }
    delete elf;
}

Here is the call graph for this function:

Here is the caller graph for this function:

template<typename Rel_Type >
int do_relocation_section ( Elf elf,
unsigned int  rel_type,
unsigned int  rel_type2 
)

Definition at line 387 of file elfhack.cpp.

{
    ElfDynamic_Section *dyn = elf->getDynSection();
    if (dyn ==NULL) {
        fprintf(stderr, "Couldn't find SHT_DYNAMIC section\n");
        return -1;
    }

    ElfSegment *relro = elf->getSegmentByType(PT_GNU_RELRO);

    ElfRel_Section<Rel_Type> *section = (ElfRel_Section<Rel_Type> *)dyn->getSectionForType(Rel_Type::d_tag);
    assert(section->getType() == Rel_Type::sh_type);

    Elf32_Shdr relhack32_section =
        { 0, SHT_PROGBITS, SHF_ALLOC, 0, (Elf32_Off)-1, 0, SHN_UNDEF, 0,
          Elf_RelHack::size(elf->getClass()), Elf_RelHack::size(elf->getClass()) }; // TODO: sh_addralign should be an alignment, not size
    Elf32_Shdr relhackcode32_section =
        { 0, SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, 0, (Elf32_Off)-1, 0,
          SHN_UNDEF, 0, 1, 0 };
    Elf_Shdr relhack_section(relhack32_section);
    Elf_Shdr relhackcode_section(relhackcode32_section);
    ElfRelHack_Section *relhack = new ElfRelHack_Section(relhack_section);
    ElfRelHackCode_Section *relhackcode = new ElfRelHackCode_Section(relhackcode_section, *elf);

    ElfSymtab_Section *symtab = (ElfSymtab_Section *) section->getLink();
    Elf_SymValue *sym = symtab->lookup("__cxa_pure_virtual");

    std::vector<Rel_Type> new_rels;
    Elf_RelHack relhack_entry;
    relhack_entry.r_offset = relhack_entry.r_info = 0;
    int entry_sz = (elf->getClass() == ELFCLASS32) ? 4 : 8;
    for (typename std::vector<Rel_Type>::iterator i = section->rels.begin();
         i != section->rels.end(); i++) {
        // We don't need to keep R_*_NONE relocations
        if (!ELF32_R_TYPE(i->r_info))
            continue;
        ElfSection *section = elf->getSectionAt(i->r_offset);
        // __cxa_pure_virtual is a function used in vtables to point at pure
        // virtual methods. The __cxa_pure_virtual function usually abort()s.
        // These functions are however normally never called. In the case
        // where they would, jumping to the NULL address instead of calling
        // __cxa_pure_virtual is going to work just as well. So we can remove
        // relocations for the __cxa_pure_virtual symbol and NULL out the
        // content at the offset pointed by the relocation.
        if (sym) {
            if (sym->defined) {
                // If we are statically linked to libstdc++, the
                // __cxa_pure_virtual symbol is defined in our lib, and we
                // have relative relocations (rel_type) for it.
                if (ELF32_R_TYPE(i->r_info) == rel_type) {
                    serializable<Elf_Addr_Traits> addr(&section->getData()[i->r_offset - section->getAddr()], entry_sz, elf->getClass(), elf->getData());
                    if (addr.value == sym->value.getValue()) {
                        memset((char *)&section->getData()[i->r_offset - section->getAddr()], 0, entry_sz);
                        continue;
                    }
                }
            } else {
                // If we are dynamically linked to libstdc++, the
                // __cxa_pure_virtual symbol is undefined in our lib, and we
                // have absolute relocations (rel_type2) for it.
                if ((ELF32_R_TYPE(i->r_info) == rel_type2) &&
                    (sym == &symtab->syms[ELF32_R_SYM(i->r_info)])) {
                    memset((char *)&section->getData()[i->r_offset - section->getAddr()], 0, entry_sz);
                    continue;
                }
            }
        }
        // Don't pack relocations happening in non writable sections.
        // Our injected code is likely not to be allowed to write there.
        if (!(section->getFlags() & SHF_WRITE) || (ELF32_R_TYPE(i->r_info) != rel_type) ||
            (relro && (i->r_offset >= relro->getAddr()) &&
                      (i->r_offset < relro->getAddr() + relro->getMemSize())))
            new_rels.push_back(*i);
        else {
            // TODO: check that i->r_addend == *i->r_offset
            if (i->r_offset == relhack_entry.r_offset + relhack_entry.r_info * entry_sz) {
                relhack_entry.r_info++;
            } else {
                if (relhack_entry.r_offset)
                    relhack->push_back(relhack_entry);
                relhack_entry.r_offset = i->r_offset;
                relhack_entry.r_info = 1;
            }
        }
    }
    if (relhack_entry.r_offset)
        relhack->push_back(relhack_entry);
    // Last entry must be NULL
    relhack_entry.r_offset = relhack_entry.r_info = 0;
    relhack->push_back(relhack_entry);

    relhackcode->insertAfter(section);
    relhack->insertAfter(relhackcode);

    unsigned int old_end = section->getOffset() + section->getSize();
    section->rels.assign(new_rels.begin(), new_rels.end());
    section->shrink(new_rels.size() * section->getEntSize());
    ElfLocation *init = new ElfLocation(relhackcode, relhackcode->getEntryPoint());
    dyn->setValueForType(DT_INIT, init);
    // TODO: adjust the value according to the remaining number of relative relocations
    if (dyn->getValueForType(Rel_Type::d_tag_count))
        dyn->setValueForType(Rel_Type::d_tag_count, new ElfPlainValue(0));

    if (relhack->getOffset() + relhack->getSize() >= old_end) {
        fprintf(stderr, "No gain. Skipping\n");
        return -1;
    }
    return 0;
}

Here is the call graph for this function:

int main ( int  argc,
char *  argv[] 
)

Definition at line 552 of file elfhack.cpp.

{
    int arg;
    bool backup = false;
    bool force = false;
    char *lastSlash = rindex(argv[0], '/');
    if (lastSlash != NULL)
        rundir = strndup(argv[0], lastSlash - argv[0]);
    for (arg = 1; arg < argc; arg++) {
        if (strcmp(argv[arg], "-f") == 0)
            force = true;
        else if (strcmp(argv[arg], "-b") == 0)
            backup = true;
        else
            do_file(argv[arg], backup, force);
    }

    free(rundir);
    return 0;
}

Here is the call graph for this function:


Variable Documentation

char* rundir = NULL

Definition at line 56 of file elfhack.cpp.