Uninformed: Informative Information for the Uninformed

Vol 3» 2006.Jan


Improvising a Userland Scheduler

An application that does implement a userland scheduler in it, provides the functions and support to do so in the code. This is a privilege that a virus could not easily implement smoothly. So improvising takes places. This raises two major problems: how and when. How to perform the context switching task within a code that has no previous support, and when the userland scheduler code can run to begin supervising this in the first place.

There are a few ways to do it. For example putting a hook on a function is one way. Once the program will call the function that has been hooked, the virus will activate and afterwards return control to the program. But it's not an ideal solution as there is no guarantee that the program will continue using it, and for how often or long. In order to get a wider scope that could cover the entire program, signals could be used.

Looking at the signal mechanism in Linux, it's similar to the interrupts mechanism, in the way that that the kernel allows a program to process a signal within any place in the program code without any special preparation and resume back to the program flow once the signal handler function is done. It gives a very good way to perform context switching with little effort. This answers the "how" question, in how to perform the context switching task, using the signal handler function as the base function of the virus which will be invoked while the SIGALRM signal will be processed.

Adopting the signal model to our needs is supported by the alarm() syscall. The alarm() syscall allows the process to schedule the alarm signal (SIGALRM) to be delivered, thus making it kernel responsibility. Having the kernel constantly delivering a signal to the process hosting the virus, saves the virus the effort of doing it. This answers the when question for when the userland scheduler code would run. Using the alarm() syscall to schedule a SIGALRM to be delivered to the process, that in turn will call the virus function. This code demonstrates the functionality of alarm() and SIGALRM:

/*
* sigalrm-poc.c, SIGALRM Proof of Concept
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

// SIGALRM Handler

void shapebreaker(int ignored) {

        // Break the cycle

        printf("\nX\n");

        // Schedule another one

        alarm(5);

        return ;
}

int main(int argc, char **argv) {

        int shape_selector = 0;
        char shape;

        // Register for SIGALRM

        if (signal(SIGALRM, shapebreaker) < 0) {
                perror("signal");
                return -1;
        }

        // Schedule SIGALRM for 5 secs

        alarm(5);

        while(1) {

                // Shape selector

                switch (shape_selector % 2) {

                        case 0:
                                shape = '.';
                                break;

                        case 1:
                                shape = 'o';
                                break;

                        case 2:
                                shape = 'O';
                                break;
                }

                // Print given shape

                printf("%c\r", shape);

                // Incerase shape index

                shape_selector++;

        }

        // NEVER REACHED

        return 1;
}

The program concept is pretty simple, it prints a char from a loop, selecting the char via an index variable. Every five seconds or so, a SIGALRM is being scheduled to be delivered using the alarm() syscall. Once the signal has been processed the signal handler, which is the shapebreaker() function in this case, is being called and is breaking the char sequence. Afterwards the program continues as if nothing happened. From within the signal handler function, a virus can operate and once it returns, the program will continue flawlessly.