Virus in Linux - C Code That Changes it's Process Name and its Process ID at Run Time

This is an article on Virus in Linux - C Code That Changes it's Process Name and its Process ID at Run Time in C.

Rated 5.00 By 1 users

After writing my previous article Virus Code in Linux - C Code That Changes it's Process Name Run Time, I wondered if a process can change its name then why cannot it change its PID. And what if a process can change both its name and PID after lets say every 1 second? Well it will be a difficult task for a system administrator to kill that process. SO I decided to go ahead with the Idea.

Lets see what I did...

The Code

Here is the code for a program that changes its name and PID after every 5 seconds. I chose 5 seconds as its relatively easy for a user to kill this program once its run. If some one likes this code and wants to use it. He/she can reduce the sleep() time to 1 second and then run the code.

Code:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
void change_pid()
{
pid_t pid;
// This function will replace the existing process image by
// a new process image. For all those who are new to fork()
// please refer to its man page and try out a few basic
// exercise to understand what this system call does. Just
// from the fact that this system call is made once but it
// returns twice, I must say that its not a trivial system
// call to understand.
pid = fork();
if(0 == pid)
{
// pid ==0 means that this is child. Just print a log and
// do nothing here. Our motive was to create a new PID
// Which was only possible if we could create a new process
// image using fork. We did it successfully here.
printf("\n Child process created!!!!!\n");
}
else
{
// This is the parent process, with old PID. We do not need
// it anymore so just exit from this.
printf("\n Created a new process, Exiting!!!!!\n");
exit(0);
}
}
void change_name(char *argv[])
{
char buff[1024]; // Buffer to read lines
char new_name[1028]; // Buffer to store new process name
char *ptr = NULL;
FILE *fp = NULL;
a: memset(buff,'\0', sizeof(buff)); // Setting the memory with NULLs
memset(new_name,'\0', sizeof(new_name)); // Setting the memory with NULLs
// Introduce constant 3 bytes '123' in the begining
// of every process name that we change our process name to.
// So that we can at least easily track our process name
// when we check it using ps command. Note that
// this is only for practice purpose otherwise there
// is no need for a set of constant bytes like these.
new_name[0] = '1';
new_name[1] = '2';
new_name[2] = '3';
// Run the command 'ps -aef > ps.txt'
// This command will store the result of 'ps -aef' in a text file 'ps.txt'
// The files would have entries like :
// UID PID PPID C STIME TTY TIME CMD
// root 1 0 0 20:49 ? 00:00:00 /sbin/init
// root 2 0 0 20:49 ? 00:00:00 [kthreadd]
// root 3 2 0 20:49 ? 00:00:00 [migration/0]
// root 4 2 0 20:49 ? 00:00:00 [ksoftirqd/0]
system("/bin/sh -c 'ps -aef > ps.txt'");
// Open the file 'ps.txt'
fp = fopen("ps.txt", "r");
if(NULL == fp)
{
printf("\n File open failed\n");
//return -1;
}
// Get each line from file until the whole file is read or some error occurs
while(NULL != fgets(buff, sizeof(buff), fp))
{
// Search for the character '[' in the line fetched from file.
// This is because most of the process names are enclosed in '[' brackets.
// For example :
// root 2 0 0 20:49 ? 00:00:00 [kthreadd]
ptr = strchr(buff, '[');
unsigned int len = strlen(buff);
if(NULL == ptr)
{
// Search for the character '/' in the line fetched from file.
// This is because many of the process names are start with '/'.
// For example :
// root 1 0 0 20:49 ? 00:00:00 /sbin/init
ptr = strchr(buff, '/');
}
if(NULL != ptr)
{
// If any one of '[' or '/' is found than copy the complete process
// name in the buffer which already holds 123 as its first three bytes.
// Make sure that you do not overwrite the first three bytes of the buffer
// new_name which contains 123 as its first three bytes
strncat((new_name+3), ptr, ((buff + len-1) - ptr));
}
else
{
// If the line fetched does not contain either of '[' or '/'
// Then use a default process name '/bin/bash'
ptr = "/bin/bash";
strncpy((new_name+3), ptr, strlen(ptr));
}
// Since by now we have the new_name buffer filled with
// new process name so copy this name to arg[0] so as to
// change our process name.
strncpy(argv[0], new_name, sizeof(new_name));
printf("\n %s \n", new_name);
// Call this function to change the PID of the process by
// forking a new process. When the flow would return from
// this function, the process would be different and hence
// its PID would be different.
change_pid();
//A delay of one second so that you can run the command 'ps -aef'
// and check the new name of our process and its new PID. Note
// that it is very now difficult to kill this process through
// ctrl+c as somehow its getting ignored (I will research more
// on this and will come back with analysis soon). I have kept
// the sleep interval to be 5 sec because the more I reduce it,
// the more difficult is for the user to kill it using :
// kill -9 <PID>
// as PID keeps on changing within seconds.
sleep(5);
//Time to fetch a new line from ps.txt so just reset
// the buffer new_name with NULL bytes except the first
// three bytes which are '123'.
memset((new_name+3),'\0', sizeof(new_name));
}
// Seems like either we are done reading all the lines
// from ps.txt or fgets() encountered some error. In either
// of the case, just close the file descriptor
fclose(fp);
// Since we do not want to stop even now, so lets re run the
// whole cycle again from running the command 'ps -aef > ps.txt'
// to reading each line using fgets() and changing the our process
// name accordingly
goto a;
}
int main(int argc, char* argv[])
{
change_name(argv);
return 0;
}

In this code, I created two functions :

change_name() -> for continuously changing the name

change_pid() -> for continuously changing the PID of the process

Now, once a name is changes successfully, a call to change_pid() is made so that the a new process with new pid which will execute the same code gets created while the old process is terminated.

Rest of the explanation I have provided as comments in the code itself.

Output

Now, the interesting part, lets run the code and see the output :

Code:

~/practice $ ./namepid
123/bin/bash
Created a new process, Exiting!!!!!
Child process created!!!!!
~/practice $
123/sbin/init
Created a new process, Exiting!!!!!
Child process created!!!!!
123[kthreadd]
Created a new process, Exiting!!!!!
Child process created!!!!!
123[migration/0]
Created a new process, Exiting!!!!!
Child process created!!!!!
123[ksoftirqd/0]
Created a new process, Exiting!!!!!
Child process created!!!!!
123[watchdog/0]
Created a new process, Exiting!!!!!
Child process created!!!!!
123[migration/1]
Created a new process, Exiting!!!!!
Child process created!!!!!
123[ksoftirqd/1]
Created a new process, Exiting!!!!!
Child process created!!!!!
123[watchdog/1]
Created a new process, Exiting!!!!!
Child process created!!!!!
123[events/0]
Created a new process, Exiting!!!!!
Child process created!!!!!
123[events/1]
Created a new process, Exiting!!!!!
Child process created!!!!

When the code was executed, it started changing its name and its conveying that it is successfully changing its PID. Now I opened another terminal and checked the PID using the command ps -aef and running this command after every 5 seconds and I conformed that the PID was actually changing.

But as you can see from the output, it kept on executing. This is something which I need to investigate that why SIGINT is being ignored here. Anyways after multiple tries, I was able to kill the process using :

kill -9 <PID-of-process>

Why it took multiple tries was because by the time I found the PID and wrote the above command, the PID use to get changed. But any how I got success. Thats why I was thinking that If the sleep() time is reduced to '1' sec then it will become very very difficult to kill the process.

Conclusion

To Conclude, In this article I extended my previous article where a process could change its name continuously on run time to a process which can now change its PID along with its name at run time.