http://www.cc.gatech.edu/classes/AY2002/cs3210_spring/p1.html

 

CS 3210 Operating System Design (Linux)

Fall 2001 • College of Computing, Georgia Tech

 

Project 1: Let the Games Begin

DUE: Tu 12 Feb (Sign up for a demo time on the Swiki)

 

Objective

 

We are going to be implementing a system call (lets call it cloak) and also use a file in the proc filesystem ( /proc/stealth ). The implementation will be done in a single kernel module, which will be inserted in at runtime into the kernel. The aim of the system call would be to hide a given process from the ps command, and also turn this functionality on/off with the help of a file in the /proc filesystem.

 

Initially, I will give a brief description of modules and how they work, how to implement a system call as a module and some information about the /proc filesystem.

There is a reading list right at the end of the writeup,which may prove useful. Also you are expected to have been attending class and understanding what was happening there! J

 

Grading Criteria (100 Points)

 

1.      Code and documentation that implement the stealth system call. You must sign up for a demo time with one of the TA's to show your working code. You are not required to turn in a hard copy of your code. This will be worth 85 Points of the project grade, broken down as:

 

a.      35 Points for a working read/write /proc/stealth.

b.      35 Points for a working cloak system call.

c.      30 points for answering question during the demo.

 

The Project

           

After completing this project you should be able to answer the following questions.  We will ask these questions during your demo and everyone in your team is expected to be able to answer them:

 

1.      How did you implement the "cloak" system call? What kernel source files and routines you used as well as how your implementation met the specification explained below.

 

2.      How did you implement /proc/stealth?  What functions did you have to write to interface with the proc file system and how your code transferred data to and from the proc file?

 

3.      Specifically, what is the journey of the cloak system call through the kernel?  Especially the details involving how the user level library call gets converted to an actual system call.  Be sure to include the sys_call_table, the syscallX() macro, int x80, how values are returned from the system call and how errors are indicated.  Useful resources are Bovet & Cesati,     man 2 –intro man page, and the kernel #include files dealing with system calls.

 

 

Modules

Modules allow for dynamic additions to the Linux kernel. You can simply add a small piece of code to the kernel without re-compiling the entire kernel each time you want to add some kernel functionality.

 

Modules are written in C, like the rest of the Linux kernel, with two important differences from programming user level programs:

 

  1. Modules don't have access to the standard C library (string.h, math.h, etc.).
  2. There is no main() function, instead there is an init_module() function.

 

When you compile a kernel module a module.o object file is produce.  This is what you “insert” into a running kernel.  To do this you would run:

           

            /sbin/insmod module.o

 

To remove the module, run:

           

            /sbin/rmmod module

 

Note that the name of the module to remove does not end in “.o”.  To see a list of the currently installed modules run:

 

            /sbin/lsmod

 

init_module() is the function that is called when the module is installed into the kernel via /sbin/insmod. There is also a cleanup_module() function that is called when the module is removed from the kernel via /sbin/rmmod.

 

init_module() and cleanup_module() are the only two functions needed to have a completely functioning module. Again, to see which modules are currently loaded in the kernel, run /sbin/lsmod, or do a 'cat' on /proc/modules.

Re-read the first couple of chapters of Rubini for an excellent introduction to implementing kernel modules.

 

 

System Calls

Linux system calls provide the interface between user level programs and the kernel.  Every operating system, including Windows, has a similar system call mechanism to provide controlled entry into the kernel. (Remember that system calls are sometimes called “protected procedure calls”.)


This interface will be much clearer after you read Chapter 8 (System Calls) in Bovet & Cesati.


Define your own system call


The system call table (
sys_call_table) is an array of function pointers containing all of the system calls in the kernel. It is defined in /arch/arm/kernel/calls.S in the ARM architecture.  Every system call has a number and the kernel indexes into this table to match the function call to the system call number.

 

So the first step in creating a system call is to choose an available number in the sys_call_table to use.  You can either enter your system call into this array using a module or by putting it directly into the kernel source.  For all system calls you implement in this class you will use a module. It is easier to use modules because you will not have to re-compile the entire kernel each time you modify your system call.

 

How to implement a system call as a module

 

To access the sys_call_table array you need to define it as an extern at the top of your module source file. When the module is initialized, in init_module(), you will need to first save the old entry of the array.  You may be wondering why you have to save the current function pointer in the array because you chose an array index that was unused.  The reason is the kernel has a default function there that simply returns an error saying the system call with this number is not implemented. When the module is unloaded you will restore that default function.

 

Here is a skeleton module for a system call.

 

 

/proc File System

T
he /proc file system acts as an interface to internal data structures in the Linux kernel. The files in the /proc file system don't correspond to an actual file on a physical device.  Instead, they are “magic objects” that behave like files but provide access to parameters, data structures, and statistics in the kernel.  The "contents" of these files are not fixed blocks of data, as ordinary file contents are.  Instead, they are generated on demand by the Linux kernel when you read from the file.  You can also change the configuration of the running kernel by writing to certain files in the /proc file system.

 

Proc files can be used to obtain information about the system (i.e. /proc/interrupts) or to change certain kernel parameters at runtime. Look at the man page for proc files and also the documentation for them under linux/Documentation/filesystems/proc.txt.

 

The /proc files we are interested in for this project provide information about running processes.   The directory name, under /proc, to identify each running process is its PID number. Here is an example of a special /proc/<pid> entry named /proc/self.  Obviously "self" is not an actual system PID but it will correspond to whichever process is currently reading /proc/self.  For example, the command:

 

            ls /proc/self

           

produces:

           

            cmdline  cwd  environ  exe  fd  maps  mem  root  stat  statm  status

 

which is the information about the  process running the ls command which read /proc/self.  Try this out.  Is self the shell process or is it something different? The reason the self file is in /proc is so that applications can get useful information about themselves.

 

The ps command also gets process information from the /proc/<pid> entries.  Specifically, ps looks at the stat, statm and status proc files in each <pid> directory. So to prevent a process from showing up when we run the ps command , we will hide the process from the proc filesystem.

 

 

Rather than explain how to create and use a /proc file here, follow the link below to an excellent Howto that concisely details how to create, use, and destroy /proc files.

 

http://www.kernelnewbies.org/documents/kdoc/procfs-guide/lkprocfsguide.html

 

 

Implementing the Cloak System Call

 

As mentioned earlier, we are going to create a "cloak" system call that hides a process from the system.  Actually, to hide a process completely is very hard to implement because there are a number of ways to detect a process.  For example, if you knew a possible PID for a cloaked process you could send that PID a signal and see if it responded. So we are just going to hide a process from /proc, which will also hide the process from the ps command.

 

Your group needs to implement a system call to hide the specified process from the /proc file system. Implement the system call as a kernel module. Here's how a process would use the call to hide itself:

 

            main(){

          ....

          hide_process(getpid());

          ....

          unhide_process(getpid());

          ....

 

After calling the hide_process() system call, this process would be hidden from /proc as well as the ps command. The unhide_process() call would bring the process back; i.e.  you have to make hiding reversible.  If either of these system calls is called with a PID that does not exist you need to return the proper error code, you can find which error code this is by searching through the kernel #include files (linux/include/linux).

 

The task list is a circular linked list of task structures (process control blocks or PCBs).  The /proc file system is implemented in linux/fs/proc.  A scan of the task list is made to generate the /proc/<pid> entries on demand.  A simple modification to this code will allow a process to not be detected.  The system must be able to keep track of which processes are cloaked.

 

You must do this in the kernel; you're not allowed to modify the ps command!

 

 

PART 2: Implement the Read/write /proc/stealth File

 

A) You need to have a read/write proc file where you can turn the entire stealth system on or off. For example, writing a "1" to /proc/stealth would allow you to call hide_process() and writing "0" would disable this feature as well as make any currently hidden processes visible again.  It is valid to call hide_process() on process X when the system is turned off, when the system is turned back on process X should now be hidden.

 

B) Create another proc file that prints the current list of hidden processes when read.  You might want to do this anyway for debugging, obviously you wouldn't want this if you were deploying stealth J

 

Some Specifics

 

Whenever you do anything with the task list you need to do the proper locking, to ensure the list is not corrupted.  For example, if you only want to read information in a task struct you only need a read lock:

 

            read_lock(&tasklist_lock);

          /* do some reading */

     read_unlock(&tasklist_lock);

 

but if you want to write to any value in a task struct need a write lock:

                                                                                                                                   

            write_lock(&tasklist_lock);

          /* do some writing */

     write_unlock(&tasklist_lock);

 

 Hopefully your modifications won’t require locking , but it is advised to do it to be completely safe!

 

 

BONUS (Have Some Fun)  

 

·        Create another set of system calls that take in a user name and hide all of those users’ processes.  This cannot break the earlier system and they should all work together, e.g. hiding all of a user's processes and then selectively un-hiding a few should be possible. (5 Points)

 

·        Modify your system call module such that only root can use it. ( 5 Points )

 

·        Extend above to be able to hide and unhide a specific users process based on the process' executable name. e.g.: hide_user_process(linus,gcc) (5 Points)

 

·        Make a system which install new system calls for the user. For eg. A call like

 

install_sys_call( pointer to func , int num_args , args…..)

 

 

Helpful Reading

 

·        Bovet & Cesati Chapter 8 (System Calls)

 

·        linux/fs/proc/*.c

 

·        http://www.kernelnewbies.org/documents

"Procfs Guide" under "Kerneldoc".  This will tell you everything you need to know to create a proc file that can be read or written.

 

·        linux/Documentation/filesystems/proc.txt

This is a thorough overview of all the proc files, section 1.1 is a short bit about process-specific data.

 

 

A Last Bit of Advice

 

·        Make sure to follow the Procfs Guide on kernelnewbies.org and understand where in memory you are reading and writing data from/to.

 

·        If you haven't already, learn the "tags" feature of your favorite editor (vim and emacs support this). This is not necessary, but being able to do a:

 

                                    make tags

in the top of the kernel source tree and easily jump around the kernel source will save you a great deal of time.

 


Reply via email to