You are not logged in.

#1 2012-04-11 03:04:23

niriven
Member
Registered: 2012-04-11
Posts: 17

linux 3.14 sys_call_table interception module

So I got bored last night and decided I wanted to learn C and some Linux programming. Didn't really have any experience so I started with a basic template of what how to write a kernel module that will printk "Hello world"  so it can be viewed in dmesg. After I was finished I sat and thought, what can I create? Well I thought a way to intercept understand how the system calls were working would be fun. I did not want to do anything malicious as this was my own machine. I also know this is not really something that can be used to hack into a system as once someone has root access the machine is theirs to do what they want with anyway.

So I went off writing a module that what I thought would be as simple as having an "extern void *sys_call_table;" declared and manipulating the addresses in the table to swap out the function pointer to my own function. It didn't work so I googled around a bit and noticed this is no longer an exported symbol. More googling later, I started looking for a System.map in /boot/ to see if there were any exported addresses there, but arch does not seem to include the System.map anymore. Apparently (which i thought was really neat) there is /proc/kallsyms to show all the exported symbols in the Linux kernel that I could easily access manually though a pointer. Searching through this there was no sys_call_table either.

[niriven@(none) ~]$ cat /proc/kallsyms | grep sys_call_table
[niriven@(none) ~]$ 

Given that kernel source no longer exports this I guess this makes sense. But I want access to the sys_call_table. How do I do it? I know it is sitting in the kernel memory space somewhere. I than realized I have access to all of the kernel space memory and I could search it to find an array that contains a pointer to a couple system calls i am interested in.  Googling around some more it looks like some others have took this approach before so I copied some code out and modified it a bit to do the search for the sys_call_table. Voila, I found the address of the table! This means I can start changing the tables system call references and intercept some code, or so I thought. I tried swapping out a system call (eg. sys_exit) for my own (new_sys_exit) and the kernel had no part in this. I took a look at the output in dmesg and noticed that this memory was not addressable. What does this this mean? Onto more googling! I saw people modifying the sys_call_table with no problems in the 2.6 series of kernels, so what changed? It looked as though kernel developers changed this memory to be protected so others could not modify it. At this point I was ready to give up because I figured read-only memory is something I could not change but I decided to persist and find out if theres any neat hacks to get around this. I found that others where calling some kernel level API's to change certain pages of memory from ro to rw, changing the memory and changing it back. Unfortunitally it looked as though when i tried to call them the kernel module compiled but insmod reported that there were undefined symbol errors which meant I could not access these functions anymore.

I thought I was trapped and could no longer get into this memory and start modifying it to do what I wanted. Just before giving up I found someone post on a yahoo answers comment about disabling page protection at a processor level by changing the 16th bit in the cr0 register (I think this is intel specific). I decided to try it and finally, I could change any memory in the kernel space that I wanted!

Ah what a fun way to spend 6 hours to learn C and some of the internals of how Linux works. I did google a lot but who doesn't? I also learned much more than expected about these topics in the process which I am happy with. Anyway, Here is the code I created as it might be of some use to others. As some others have said this is not a hack, you need root to try this out. If you have root, you essentially own the system anyway so this is not too malicious, but fun if you want to do some system call interception on your own system on a 3.3 kernel.

interceptor.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/delay.h>
#include <asm/paravirt.h>

unsigned long **sys_call_table;
unsigned long original_cr0;

asmlinkage long (*ref_sys_read)(unsigned int fd, char __user *buf, size_t count);
asmlinkage long new_sys_read(unsigned int fd, char __user *buf, size_t count)
{
	long ret;
	ret = ref_sys_read(fd, buf, count);

	if(count == 1 && fd == 0)
		printk(KERN_INFO "intercept: 0x%02X", buf[0]);

	return ret;
}

static unsigned long **aquire_sys_call_table(void)
{
	unsigned long int offset = PAGE_OFFSET;
	unsigned long **sct;

	while (offset < ULLONG_MAX) {
		sct = (unsigned long **)offset;

		if (sct[__NR_close] == (unsigned long *) sys_close) 
			return sct;

		offset += sizeof(void *);
	}
	
	return NULL;
}

static int __init interceptor_start(void) 
{
	if(!(sys_call_table = aquire_sys_call_table()))
		return -1;
	
	original_cr0 = read_cr0();

	write_cr0(original_cr0 & ~0x00010000);
	ref_sys_read = (void *)sys_call_table[__NR_read];
	sys_call_table[__NR_read] = (unsigned long *)new_sys_read;
	write_cr0(original_cr0);
	
	return 0;
}

static void __exit interceptor_end(void) 
{
	if(!sys_call_table) {
		return;
	}
	
	write_cr0(original_cr0 & ~0x00010000);
	sys_call_table[__NR_read] = (unsigned long *)ref_sys_read;
	write_cr0(original_cr0);
	
	msleep(2000);
}

module_init(interceptor_start);
module_exit(interceptor_end);

MODULE_LICENSE("GPL");

Makefile

obj-m += interceptor.o

all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

As for the output:

[root@RRL125-ARCH niriven]# dmesg
[ 3108.147363] intercept: 0x64
[ 3108.241925] intercept: 0x6D
[ 3108.287888] intercept: 0x65
[ 3108.380025] intercept: 0x73
[ 3108.443986] intercept: 0x67
[ 3108.540326] intercept: 0x0D
[ 3109.697865] intercept: 0x1B
[ 3109.697883] intercept: 0x5B
[ 3109.697893] intercept: 0x41
[ 3109.916600] intercept: 0x0D
[ 3113.848476] intercept: 0x63
[ 3113.936372] intercept: 0x6C
[ 3113.997244] intercept: 0x65
[ 3114.082137] intercept: 0x61
[ 3114.145038] intercept: 0x72
[ 3114.272825] intercept: 0x0D
[ 3115.129938] intercept: 0x64
[ 3115.216999] intercept: 0x6D
[ 3115.341207] intercept: 0x73

To install on Arch Linux, make sure you have linux-headers and base-devel is installed

pacman -S linux-headers base-devel

Ensure interceptor.c and Makefile are in a directory, compile, and install as root, check dmesg, then remove module

make
insmod interceptor.ko
dmesg
rmmod interceptor

Hopefully this will be a help to someone else looking to learn similar topics!

-Niriven

Last edited by niriven (2014-09-30 02:19:20)

Offline

#2 2012-04-12 04:40:45

tavianator
Member
From: Waterloo, ON, Canada
Registered: 2007-08-21
Posts: 858
Website

Re: linux 3.14 sys_call_table interception module

Cool!  There's always ptrace() for intercepting system calls too smile

Offline

#3 2012-04-12 20:19:05

dg
Member
Registered: 2012-02-01
Posts: 1

Re: linux 3.14 sys_call_table interception module

Nice post!

You save me some hours to locate the syscall table!

For the info, you can get rid of the asm, the kernel already provides that! Have a look at /usr/src/linux-$(uname -r)/arch/x86/include/asm/paravirt.h
write_cr0 and read_cr0 wink

Cheers!

Offline

#4 2012-04-12 21:45:26

niriven
Member
Registered: 2012-04-11
Posts: 17

Re: linux 3.14 sys_call_table interception module

tavianator wrote:

Cool!  There's always ptrace() for intercepting system calls too smile

Thanks i'll take a look! BTW very interesting website you have there, I've always wanted to do nice simulations like you are doing though I lack the math knowledge.

Offline

#5 2012-04-12 21:46:40

niriven
Member
Registered: 2012-04-11
Posts: 17

Re: linux 3.14 sys_call_table interception module

dg wrote:

Nice post!

You save me some hours to locate the syscall table!

For the info, you can get rid of the asm, the kernel already provides that! Have a look at /usr/src/linux-$(uname -r)/arch/x86/include/asm/paravirt.h
write_cr0 and read_cr0 wink

Cheers!

Good point, I wasn't aware those existed. The direct assembly might be a little more flexible as it is immune to kernel API changes

Offline

#6 2012-04-14 15:40:50

tavianator
Member
From: Waterloo, ON, Canada
Registered: 2007-08-21
Posts: 858
Website

Re: linux 3.14 sys_call_table interception module

niriven wrote:
tavianator wrote:

Cool!  There's always ptrace() for intercepting system calls too smile

Thanks i'll take a look! BTW very interesting website you have there, I've always wanted to do nice simulations like you are doing though I lack the math knowledge.

Thanks!  It's nothing you can't pick up by reading a few books, if you're really interested.

Offline

#7 2012-07-23 03:20:51

niriven
Member
Registered: 2012-04-11
Posts: 17

Re: linux 3.14 sys_call_table interception module

Updated post for linux 3.4 and included a Makefile if anyone wanted to try it.

Offline

#8 2012-10-26 02:27:42

niriven
Member
Registered: 2012-04-11
Posts: 17

Re: linux 3.14 sys_call_table interception module

Updated for 3.6, added a *very* basic key log example instead of sys_exit.

Offline

#9 2012-11-19 10:32:17

iulius
Member
Registered: 2012-11-19
Posts: 1

Re: linux 3.14 sys_call_table interception module

With Ubuntu 12.04, this code gives segfaults, with the execution of some commands like 'sudo service ssh stop' or 'make'. Debugging showed it has to do with the trick to enable writing in the page to modify the sys_call table.

I couldn't figure out exactly what is going.. .any of you has an idea what is happing?

Offline

#10 2012-11-19 16:17:49

niriven
Member
Registered: 2012-04-11
Posts: 17

Re: linux 3.14 sys_call_table interception module

Ouch, sorry about that. I have ubuntu installed at home and will take a look in about 8 hours (after work) and post back here. I'll send you my email in a PM if you have anymore details on why it does not work.

Offline

#11 2013-04-07 02:09:21

alexandernst
Member
Registered: 2010-04-02
Posts: 61

Re: linux 3.14 sys_call_table interception module

This is a really interesting subject!

I have a couple of questions:

1. Isn't it possible to use xchg to swap the functions?
Something like:

ref_sys_open = (void *) xchg(sys_call_table[__NR_open], new_sys_open);   <---- switch to custom function

xchg(sys_call_table[__NR_open], ref_sys_open);  <----- siwtch back to original function

2. I'm getting "BUG: unable to handle kernel paging request" and functions aren't switched back to original ones. That's probably from the functions that try to set RW/RO, but I'm not sure why it's happening. (I'm on x64). Have you had the same problem?

Offline

#12 2013-04-07 02:23:28

cris9288
Member
Registered: 2013-01-07
Posts: 348

Re: linux 3.14 sys_call_table interception module

This is cool, but way over my head. The other day I felt like learning some C too. I wrote a server/client that passed a char * back and forth. I like what you did better.

Offline

#13 2013-04-23 01:12:22

niriven
Member
Registered: 2012-04-11
Posts: 17

Re: linux 3.14 sys_call_table interception module

Thanks cris9288. I even have an example in the forums somewhere about creating a TCP client with non blocking sockets in a Linux kernel module. Why? Dunno, but seemed fun to try.

alexandernst, I was acutally going review this again and see how i can make it better tonight, with kernel 3.8. I'll see if i can reproduce your problem. I am not sure of the advantages of using xchg, but i'll take a look at that as well

Last edited by niriven (2013-04-23 01:13:04)

Offline

#14 2013-04-23 18:31:11

alexandernst
Member
Registered: 2010-04-02
Posts: 61

Re: linux 3.14 sys_call_table interception module

niriven, maybe you'll be interested in having a look at my project: https://github.com/alexandernst/procmon

Offline

#15 2013-04-23 23:08:41

niriven
Member
Registered: 2012-04-11
Posts: 17

Re: linux 3.14 sys_call_table interception module

Very nice alexandernst, this is what i was initially going to try to do, but obviously i did not get as far as you smile I was going to write a way to access the data like you did in userland, then expose webservices with node.js, and have a webgl UI for visualization, though havn't gotten too far yet smile I'll look more at that code tonight smile

Offline

#16 2013-04-24 07:45:06

alexandernst
Member
Registered: 2010-04-02
Posts: 61

Re: linux 3.14 sys_call_table interception module

niriven, thank you smile Maybe we can join forces? I'm planning to do everything on a model-view basis, (Qt5 for the view part), but as there will be a strong separation between data and UI you'll be able to write a NodeJS + WebGL UI.

Offline

#17 2013-04-29 04:55:15

csa.zhang
Member
Registered: 2013-04-29
Posts: 1

Re: linux 3.14 sys_call_table interception module

Thanks ,   it's  useful  for me.

Offline

#18 2014-04-10 03:44:12

niriven
Member
Registered: 2012-04-11
Posts: 17

Re: linux 3.14 sys_call_table interception module

Updated for Linux 3.14. Switched to _cr0 functions instead of using hacky ASM.

Offline

#19 2017-04-23 08:44:08

sleepy_cat
Member
From: CHN
Registered: 2015-12-07
Posts: 43

Re: linux 3.14 sys_call_table interception module

@niriven what for msleep(2000); in interceptor_end()

i don't catch that.


Things being equal, the simplest explanation tends to be the right.

Offline

#20 2017-04-23 13:08:08

WorMzy
Forum Moderator
From: Scotland
Registered: 2010-06-16
Posts: 11,788
Website

Re: linux 3.14 sys_call_table interception module

Please do not necrobump.

niriven hasn't been on the forums in three years, you're unlikely to get a response from them.

Closing.


Sakura:-
Mobo: MSI MAG X570S TORPEDO MAX // Processor: AMD Ryzen 9 5950X @4.9GHz // GFX: AMD Radeon RX 5700 XT // RAM: 32GB (4x 8GB) Corsair DDR4 (@ 3000MHz) // Storage: 1x 3TB HDD, 6x 1TB SSD, 2x 120GB SSD, 1x 275GB M2 SSD

Making lemonade from lemons since 2015.

Offline

Board footer

Powered by FluxBB