Loadable Kernel Module Programming and System Call Interception - c

Assume that we want to intercept the exit system call and print a message on the console when any process invokes it. In order to do this, we have to write our own fake exit system call, then make the kernel call our fake exit function instead of the original exit call. At the end of our fake exit call, we can invoke the original exit call. In order to do this, we must manipulate the system call table array (sys_call_table).
Armed with the sys_call_table array, we can manipulate it to make the sys_exit entry point to our new fake exit call. We must store a pointer to the original sys_exit call and call it when we are done printing our message to the console. Source code :
#include <linux/kernel.h>
#include <linux/module.h>
#include <sys/syscall.h>
extern void *sys_call_table[];
asmlinkage int (*original_sys_exit)(int);
asmlinkage int our_fake_exit_function(int error_code)
{
/*print message on console every time we
*are called*/
printk("HEY! sys_exit called with error_code=%d\n",error_code);
/*call the original sys_exit*/
return original_sys_exit(error_code);
}
/*this function is called when the module is
*loaded (initialization)*/
int init_module()
{
/*store reference to the original sys_exit*/
original_sys_exit=sys_call_table[__NR_exit];
/*manipulate sys_call_table to call our
*fake exit function instead
*of sys_exit*/
sys_call_table[__NR_exit]=our_fake_exit_function;
}
/*this function is called when the module is
*unloaded*/
void cleanup_module()
{
/*make __NR_exit point to the original
*sys_exit when our module
*is unloaded*/
sys_call_table[__NR_exit]=original_sys_exit;
}
When I compile this program I got warning :
WARNING: "sys_call_table" [/home/roiht/driver/one.ko] undefined!
As I did search, I found that kernel version after 2.5 changed the concept of sys_call table.
So, my question is what is alternative method to do this in new kernel version ?

Any kernel variable can be used in a module if it has been explicitly exported in the kernel using EXPORT_SYMBOL(). Since kernel version 2.6, export for sys_call_table has been removed. So if you want to use this approach, explicitly export the variable. As a convention, theis export is done right after the variable declaration, but I guess exporting from any file where this variable is defined will also do. To check if the approach worked, simply look in the output of "cat /proc/kallsyms".
Another approach to capture the exit syscall will be to put a hook in the sysenter part of syscall execution. Look here for more details: http://articles.manugarg.com/systemcallinlinux2_6.html

You can read the address of sys_call_table from System.map-xxx file corresponding to your kernel. The file is usually in /boot directory, and the name is System.map-<kernel-version>, where kernel-version is the result of command uname -r. You can use module parameter to pass the address to your module.

Related

Trying to understand UNIX system calls on XV6

I'm trying to write a simple system call on XV6 (documentation available here and Github here) in order to understand how they're implemented. I've used these steps
In syscall.c, declared extern int sys_hello(void) and added [SYS_hello] sys_hello into static int (*syscalls[])(void) array
In syscall.h, defined SYS_hello as call number 22
In user.h, declared the function prototype as int hello (void);
In usys.S, added SYSCALL(hello) to the macro
In sysproc.c, added the function sys_hello(void) at the bottom
int sys_hello(void)
{
cprintf ("Hello World System Call\n");
return 0;
}
Created hello.c which simply calls the hello() system call
Added hello.c to the Makefile and ran the code
It worked as expected.
Now, my question is that it seems that the array in syscall.c matches the indexes of the commands with the system call numbers in syscall.h file
However, if I move the hello position to the second spot in the syscall.c and let the system command number in syscall.h stay 22 the system command works as before. Where as, I expected that it'd break. Can you help me understand how the array syscall.c maps (if that's even the correct word) to the syscall.h system call number?
I'm fairly new to XV6 and C so please don't get mad at me if this question seems silly, I'm only trying to learn.
Here is the Github link to my fork of the XV6 repository if that helps: github.com/AdityaSingh/XV6
The array of syscalls is syscall.c makes use of the designated initialization syntax where you can specify at which index to assign the value.
static int (*syscalls[])(void) = {
[SYS_fork] sys_fork,
}
SYS_fork, defined as 1, specifies the index where to assign sys_fork. Thus the order of the elements doesn't matter using this syntax.

Error compiling Linux kernel module using custom system calls

I'll walk you through step by step
First I edit 3 files in my Linux kernel directory
Open LINUX_DIRECTORY/arch/x86/syscalls/syscall_64.tbl and add the custom calls i'm implementing – using the appropriate format
Declare them here: LINUX_DIRECTORY/include/linux/syscalls.h – using the appropriate format:
Open LINUX_DIRECTORY/Makefile and add the directory I'm storing my new system calls to the core-y line:
core-y := usr/ my_system_call_directory/
Here's where I'm having issues. Inside LINUX_DIRECTORY/my_system_call_directory I add a C file with my custom system call definitions and its corresponding Makefile. I leave the definitions empty because inside the C file for my kernel module I declare an extern function (my custom system call) and define a separate function which is set to my extern function:
extern long (*start_shuttle)(void);
long my_start_shuttle(void) {
// stuff here
}
int init_module(void) {
// stuff here
start_shuttle = my_start_shuttle;
// more stuff
}
After recompiling the kernel I try to make the kernel module and get a no definition for start_shuttle error.
Is this because I left the definition for start_shuttle blank in my_system_call_directory? Should it match exactly the my_start_shuttle I defined in the kernel module or is there something special I'm supposed to add? I'm asking dumb questions in advance because it takes so long for my machine to recompile Linux and I'm not sure what to change. Thanks
Figured it out. For anyone as slow as me, you have to use a wrapper and a stub.
So for this example, in the my_system_call_directory, in the c file for your new system call definitions, you need something like this:
#include <linux/linkage.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/export.h>
// initialize the stub function to be NULL
long (*start_shuttle)(void) = NULL;
EXPORT_SYMBOL(start_shuttle);
// wrapper
asmlinkage long sys_start_shuttle(void)
{
if (start_shuttle)
return start_shuttle();
else
return -ENOSYS;
}

Call a specific function when the console program terminates

I'm on windows and I want to call a specific function when the program terminates.
For example:
void close_program()
{
/*do stuff*/
printf("Goodbye.\n");
}
I tried atexit() but it only worked on casual and regular exits such as a call to the function exit() or when the main returns a value to the OS.
I found out that HandlerRoutine() seems like a solution as windows sends a CTRL_CLOSE_EVENT value signal to the process closed by the user (either just close button or end task through task manager). The problem is I tried a really basic piece of code and it said 'undefined reference to HandlerRoutine' and that it returned 1.
The piece of code:
#include <stdio.h>
#include <windows.h>
int main()
{
while(1)
{
if(HandlerRoutine(CTRL_CLOSE_EVENT))
{
printf("Program is being terminated...\n");
}
}
return 0;
}
I use MinGW.
Any idea what is the problem might be ?
According to MSDN there is no need for linkage.
HandlerRoutine is the callback function that will be invoked when console will be terminated. It's not the function you have to call but the signature (defined as HANDLER_ROUTINE) of your function (that will be invoked by Windows itself):
BOOL WINAPI HandlerRoutine(DWORD dwCtrlType);
You'll inspect dwCtrlType to check for CTRL_CLOSE_EVENT returning (usually) TRUE. To attach your function and make it called you have to use SetConsoleCtrlHandler() API function, like this:
BOOL YourHandler(DWORD dwCtrlType)
{
if (CTRL_CLOSE_EVENT == dwCtrlType)
{
}
return TRUE;
}
Now you have your function but you instruct Windows to call it:
int main()
{
SetConsoleCtrlHandler((PHANDLER_ROUTINE)YourHandler, TRUE);
// Do your stuff here
return 0;
}
Please note that you can register more than one handler, they'll be called in chain up to the one that returns TRUE. For a complete example just consult MSDN.
From the MSDN page you linked
HandlerRoutine is a placeholder for the application-defined function name.
What you need to do is create a callback (of PHANDLER_ROUTINE type) and then use SetConsoleCtrlHandler to register this callback.
Try including wincon.h explicitly. I know there are a number of child header files that are automatically included with windows.h but many of these files cannot simply be included by themselves (they are not self-contained), because of dependencies. wincon.h is one such child header file used for console services.

error: asm/uaccess.h: No such file or directory

I'm running linux kernel no. 2.6.15.51 on a ubuntu system.
I created a custom system call and added it to the kernel (containing a struct), compiled, and booted into the new kernel. Now I'm trying to create a c file that calls that system call (for testing purposes). Because I have a struct declared in my system call file, I am including the header in my test file so I can use that same struct. However, when I try including my system call file (which makes a call to access_ok() method), I get an error saying asm/uaccess.h (the file where access_ok() is declared) does not exist. Any ideas why I could be having this problem? Thank you!

How to write system calls on debian/ubuntu

I am trying to write a system call of my own. It would just return the current time. I know the concept of what should I do and I did go through a couple of links like these:
Implementing a System Call on Linux 2.6 for i386
Another of the System call implementation
But I am still confused and have not got the desired result. The kernel is not compiling and its crashing due to problems. I have tried it on debian latest stable release of 3.X.X
Could someone point me out to a clean hello world kind of program to develop system calls?
EDIT
To the below answer, here are my problems:
File 3: linux-x.x.x/arch/x86/kernel/syscall_table_32.S is not found in my linux folder. I had to improvise and so modified the following file: linux-x.x.x/arch/x86/syscalls/syscall_64.tbl
The above (1) new file mentioned had different pattern of <number> <64/x32/common> <name> <entry point> and my entry was `313 common
The kernel image did compile successfully, but I couldnt call the function. It gives an undefined reference" error when i compile it with gcc. Why?
This is just example how to write a simple kernel system call.
Consider the following C function system_strcpy() that simply copies one string into another: similar to what strcpy() does.
#include<stdio.h>
long system_strcpy(char* dest, const char* src)
{
int i=0;
while(src[i]!=0)
dest[i]=src[i++];
dest[i]=0;
return i;
}
Before writing, get a kernel source tar and untar it to get a linux-x.x.x directory.
File 1: linux-x.x.x/test/system_strcpy.c
Create a directory within the linux-x.x.x, named test and save this code as file system_strcpy.c in it.
#include<linux/linkage.h>
#include<linux/kernel.h>
asmlinkage long system_strcpy(char*dest, const char* src)
{
int i=0;
while(src[i]!=0)
dest[i]=src[i++];
dest[i]=0;
return i;
}
File 2: linux-x.x.x/test/Makefile
Create a Makefile within the same test directory you created above and put this line in it:
obj-y := system_strcpy.o
File 3: linux-x.x.x/arch/x86/kernel/syscall_table_32.S
Now, you have to add your system call to the system call table.
Append to the file the following line:
.long system_strcpy
NOTE: For Kernel 3.3 and higher versions.
*Refer:linux-3.3.xx/arch/x86/syscalls/syscall_64.tbl*
And in there, now add at the end of the following series of lines:
310 64 process_vm_readv sys_process_vm_readv
311 64 process_vm_writev sys_process_vm_writev
312 64 kcmp sys_kcmp
313 64 system_strcpy system_strcpy
The format for the 3.3 version is in:
number abi name entry point
File 4: linux-x.x.x/arch/x86/include/asm/unistd_32.h
NOTE: This section is redundant for 3.3 and higher kernel versions
In this file, the names of all the system calls will be associated with a unique number. After the last system call-number pair, add a line
#define __NR_system_strcpy 338
(if 337 was the number associated with the last system call in the system call-number pair).
Then replace NR_syscalls value, stating total number of system calls with (the existing number incremented by 1) i.e. in this case the NR_syscalls should've been 338 and the new value is 339.
#define NR_syscalls 339
File 5: linux-x.x.x/include/linux/syscalls.h
Append to the file the prototype of our function.
asmlinkage long system_strcpy(char *dest,char *src);
just before the #endif line in the file.
File 6: Makefile at the root of source directory.
Open Makefile and find the line where core-y is defined and add the directory test to the end of that line.
core-y += kernel/ mm/ fs/ test/
Now compile the kernel. Issue:
make bzImage -j4
Install the kernel by executing the following command as root(or with root permissions):
make install
Reboot the system.
To use the recently created system call use:
syscall(338,dest,src); (or syscall(313,dest,src); for kernel 3.3+) instead of the regular strcpy library function.
#include "unistd.h"
#include "sys/syscall.h"
int main()
{
char *dest=NULL,*src="Hello";
dest=(char*)malloc(strlen(src)+1);
syscall(338,dest,src);//syscall(313,dest,src); for kernel 3.3+
printf("%s \n %s\n",src,dest);
return 0;
}
Instead of numbers like 313,etc in syscall, you can also directly use __NR_system_strcpy
This is a generic example. You will need to do a little experimentation to see what works for your specific kernel version.
The above answer does not work for kernel 3.5.0 and 3.7.6, producing undefined reference compiling error. To fix the problem linux/syscalls.h should be included in system_strcpy.c instead of linux/linkage.h. Also, it's better to use SYSCALL_DEFINE2(strcpy, dest, src) to define a system call.

Resources