Printk process information - c

I have the following module which I tried to print PCB information. However I couldn't figure out the output. It was supposed to print process related data. I appreciate any help.
modpcb.c
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/fs_struct.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
void retrieve(struct task_struct *task) {
struct task_struct *ts = current;
struct list_head *list;
if(task->state == 1)
printk(KERN_INFO "Process Name%s", ts->comm);
printk(KERN_INFO "Process Name%lu", ts->min_flt);
printk(KERN_INFO "Process Name%lu", ts->maj_flt);
printk(KERN_INFO "Process Name%li", ts->utime);
printk(KERN_INFO "Process Name%li", ts->stime);
printk(KERN_INFO "Process Name%d", ts->pid);
printk(KERN_INFO "Process Name%u", ts->flags);
printk(KERN_INFO "Process Name%u", ts->start_time);
printk(KERN_INFO "Process Name%ld", ts->state);
list_for_each(list, &current->children) {
ts = list_entry(list, struct task_struct, sibling);
retrieve(ts);
}
}
static int __init pcbinfo_init(void)
{
struct task_struct *node = current;
printk(KERN_INFO "Starting module...\n");
for(;node->pid != 1; node = node->parent)
retrieve(node);
return 0;
}
static void __exit pcbinfo_cleanup(void)
{
printk(KERN_INFO "Cleaning module...\n");
}
module_init(pcbinfo_init);
module_exit(pcbinfo_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("arioglu");
MODULE_DESCRIPTION("Prints PCB info");
Makefile
obj-m += modpcb.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
dmesg | tail
sudo insmod modpcb.ko
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
EDIT:Output. Error is gone by updating Makefile with insmod.
make -C /lib/modules/4.4.0-45-generic/build M=/home/emre-ubuntu/Desktop/21202135/finalB modules
make[1]: Entering directory '/usr/src/linux-headers-4.4.0-45-generic'
Building modules, stage 2.
MODPOST 1 modules
make[1]: Leaving directory '/usr/src/linux-headers-4.4.0-45-generic'
dmesg | tail
[ 926.839624] cfg80211: (2457000 KHz - 2482000 KHz # 40000 KHz), (N/A, 2000 mBm), (N/A)
[ 926.839626] cfg80211: (2474000 KHz - 2494000 KHz # 20000 KHz), (N/A, 2000 mBm), (N/A)
[ 926.839628] cfg80211: (5170000 KHz - 5250000 KHz # 80000 KHz, 160000 KHz AUTO), (N/A, 2000 mBm), (N/A)
[ 926.839631] cfg80211: (5250000 KHz - 5330000 KHz # 80000 KHz, 160000 KHz AUTO), (N/A, 2000 mBm), (0 s)
[ 926.839633] cfg80211: (5490000 KHz - 5730000 KHz # 160000 KHz), (N/A, 2000 mBm), (0 s)
[ 926.839635] cfg80211: (5735000 KHz - 5835000 KHz # 80000 KHz), (N/A, 2000 mBm), (N/A)
[ 926.839637] cfg80211: (57240000 KHz - 63720000 KHz # 2160000 KHz), (N/A, 0 mBm), (N/A)
[ 926.840351] wlo1: associate with 04:18:d6:19:b7:61 (try 1/3)
[ 926.843708] wlo1: RX AssocResp from 04:18:d6:19:b7:61 (capab=0x421 status=0 aid=21)
[ 926.863483] wlo1: associated
sudo insmod modpcb.ko

Related

Properly getting parent and children process pids from a linux kernel

I have used linux for a few months now, and just recently got acquainted with LKM. I have a small module (or program) that is supposed to print the parent and children's pid for every process running, however, I keep getting negative values for children's pids which I don't think is a valid value for a pid.
So far this is what I have:
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/printk.h>
#include <linux/list.h>
#include <linux/sched/signal.h>
int mod_init(void){
struct task_struct *task;
struct list_head *cursor;
struct task_struct *child;
printk(KERN_INFO "Custom mod Loaded");
for_each_process(task) {
printk(KERN_INFO "Parent %d \n", task->pid);
list_for_each(cursor, &task->children) {
child = list_entry(cursor, struct task_struct, children);
printk(KERN_INFO "\t Child %s | PID: %d \n", child->comm, child->pid);
}
}
return 0;
}
void mod_exit(void){
printk(KERN_INFO "Custom mod loaded");
}
module_init( mod_init );
module_exit( mod_exit );
MODULE_DESCRIPTION("Trying not to be a noob, and failing hard");
MODULE_AUTHOR("jlarator");
MODULE_LICENSE("GPL");
this is the make file
obj-m += mod.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
This is what some of the output looks like
when I insert the module, and then call dmesg
[ 3835.687212] Parent 2029
[ 3835.687213] Child | PID: -1903591424
[ 3835.687214] Child | PID: -1903591424
[ 3835.687215] Child | PID: -1903591424
From this I can tell that I am missing something big, my main concern is why am I getting negative numbers.

register_reboot_notifier doesn't notify

Playing with LKMs (out-of-tree) I'm trying to perform some work before system reboot using register_reboot_notifier, but cant make even this simple code to work:
#include <linux/notifier.h>
#include <linux/module.h>
#include <linux/reboot.h>
static int notify_me(struct notifier_block *nb, unsigned long action, void *data) {
printk(KERN_INFO "Hey! I'm your reboot notification!\n");
return NOTIFY_OK;
}
static struct notifier_block reboot_notifier = {
.notifier_call = notify_me,
.next = NULL,
.priority = 0,
};
static int __init notifier_init(void) {
register_reboot_notifier(&reboot_notifier);
printk(KERN_INFO "Reboot notifier registered\n");
return 0;
}
static void __exit notifier_exit(void) {
unregister_reboot_notifier(&reboot_notifier);
}
MODULE_LICENSE("GPL");
module_init(notifier_init);
module_exit(notifier_exit);
The module successfully compiles and loads indicating that the notifier is registered
# insmod reboot_notifier.ko
# dmesg
...
Jun 09 13:26:09 ubuntu kernel: Reboot notifier registered
# reboot
, but the "Hey! I'm your reboot notification!\n" message never appears in logs after reboot:
# journalctl -n 10000 | grep kernel | grep notif
Jun 09 13:26:09 ubuntu kernel: reboot_notifier: loading out-of-tree module taints kernel.
Jun 09 13:26:09 ubuntu kernel: reboot_notifier: module verification failed: signature and/or required key missing - tainting kernel
Jun 09 13:26:09 ubuntu kernel: Reboot notifier registered
Tested it on different systems/kernels:
Linux centos 3.10.0-1160.59.1.el7.x86_64 #1 SMP
Linux ubuntu 5.15.0-37-generic #39-Ubuntu SMP
Linux desktop 5.15.41-1-MANJARO #1 SMP PREEMPT
Still no luck. Google doesn't help me either.
Here is a full journalctl -k -b -1 output after reboot.

How to add module to add system call for linux kernel 5.17.7

in my kernel's arch/x86/entry/syscalls/syscall_64.tbl, there is its content:
# ...
447 common memfd_secret sys_memfd_secret
448 common process_mrelease sys_process_mrelease
449 common futex_waitv sys_futex_waitv
450 common set_mempolicy_home_node sys_set_mempolicy_home_node
#
# Due to a historical design error, certain syscalls are numbered differently
# in x32 as compared to native x86_64. These syscalls have numbers 512-547.
# Do not add new syscalls to this range. Numbers 548 and above are available
# for non-x32 use.
#
512 x32 rt_sigaction compat_sys_rt_sigaction
513 x32 rt_sigreturn compat_sys_x32_rt_sigreturn
# ...
As you see, no system call's id is 451 and I want to create a linux module, which can add a system call, named sys_helloworld and its id is 451.
Following a book (the book is for kernel 4.16.10 but my kernel's version is 5.17.7), I write code below:
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/unistd.h>
#include <linux/sched.h>
#include <linux/syscalls.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Peterlits Zo 1927405082");
MODULE_DESCRIPTION("Just Hello World");
#define NUM 451
#define SYS_CALL_TABLE_ADDRESS 0xffffffffb1600240
#define HW_INFO "[helloworld INFO] "
int orig_cr0;
unsigned long *sys_call_table_my = NULL;
static int (*anything_saved)(void);
static int clear_cr0(void) {
unsigned int cr0 = 0;
unsigned int ret;
asm volatile("movq %%cr0, %%rax" : "=a"(cr0));
ret = cr0;
cr0 &= 0xfffffffffffeffff;
asm volatile("movq %%rax, %%cr0" :: "a"(cr0));
return ret;
}
static void setback_cr0(int val) {
asm volatile("movq %%rax, %%cr0" :: "a"(val));
}
// SYSCALL_DEFINE0(helloworld) { // sys_helloworld
asmlinkage long sys_helloworld(void) {
printk(HW_INFO "Hello world, from Peterlits Zo (1927405082)\n");
printk(HW_INFO "pid: %d, comm: %s\n", current->pid, current->comm);
return current->pid;
}
static int __init helloworld_init(void) {
printk(HW_INFO "Hello World From Module Helloworld by 1927405082.\n");
sys_call_table_my = (unsigned long*) (SYS_CALL_TABLE_ADDRESS);
anything_saved = (int (*)(void)) sys_call_table_my[NUM];
orig_cr0 = clear_cr0();
sys_call_table_my[NUM] = (unsigned long) &sys_helloworld;
setback_cr0(orig_cr0);
return 0;
}
static void __exit helloworld_exit(void) {
printk(HW_INFO "Byebye From Module Helloworld by 1927405082.\n");
orig_cr0 = clear_cr0();
sys_call_table_my[NUM] = (unsigned long) anything_saved;
setback_cr0(orig_cr0);
}
module_init(helloworld_init);
module_exit(helloworld_exit);
There are some macros:
NUM: The id of the new system call.
SYS_CALL_TABLE_ADDRESS: I run sudo cat /proc/kallsyms | grep sys_call_table to get its address.
It is easy to get its meaning:
Set the cr0 to make the kernel's memory to be writable.
Write the system call table.
Set back the cr0.
So I try to compile this module and install:
# In Makefile
obj-m += helloworld.o
PWD := $(CURDIR)
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
# In bash
$ make
$ sudo insmod helloworld.ko
zsh: killed sudo insmod helloworld.ko
I do not know why zsh want to kill my process, but the module is now in my kernel:
$ lsmod
Module Size Used by
helloworld 16384 1
efivarfs 16384 1
And I cannot remove it:
$ rmmod helloworld
rmmod: ERROR: Module helloworld is in use
$ rmmod -f helloworld
rmmod: ERROR: could not remove 'helloworld': Device or resourcee busy
rmmod: ERROR: could not remove module helloworld: Device or resource busy
But it looks failed and I cannot call the 451 system call:
long a = syscall(451); // -> -1
The log is:
May 20 13:24:56 PeterlitsArchVm kernel: [helloworld INFO] Hello World From Module Helloworld by 1927405082.
May 20 13:24:56 PeterlitsArchVm kernel: RIP: 0010:helloworld_init+0x17/0x1000 [helloworld]
May 20 13:24:56 PeterlitsArchVm kernel: Modules linked in: helloworld(O+) efivarfs
May 20 13:24:56 PeterlitsArchVm kernel: RIP: 0010:helloworld_init+0x17/0x1000 [helloworld]
May 20 13:27:25 PeterlitsArchVm dbus-daemon[234]: [system] Activating via systemd: service name='org.freedesktop.home1' unit='dbus-org.freedesktop.home1.service' requested by ':1.10' (uid=0 pid=809 comm="sudo rmmod helloworld" label="kernel")
May 20 13:27:25 PeterlitsArchVm sudo[809]: peterlits : TTY=pts/0 ; PWD=/home/peterlits/proj/linux/01-01-模块,但是是HelloWorld ; USER=root ; COMMAND=/usr/bin/rmmod helloworld
May 20 13:28:10 PeterlitsArchVm dbus-daemon[234]: [system] Activating via systemd: service name='org.freedesktop.home1' unit='dbus-org.freedesktop.home1.service' requested by ':1.11' (uid=0 pid=812 comm="sudo rmmod -f helloworld" label="kernel")
May 20 13:28:10 PeterlitsArchVm sudo[812]: peterlits : TTY=pts/0 ; PWD=/home/peterlits/proj/linux/01-01-模块,但是是HelloWorld ; USER=root ; COMMAND=/usr/bin/rmmod -f helloworld
And something looks not good (Why there is sys_call_table_my):
$ cat /proc/kallsyms | grep sys_call_table
0000000000000000 D sys_call_table
0000000000000000 D ia32_sys_call_table
0000000000000000 b sys_call_table_my [helloworld]
#craig-estey Hello, I try to use long for cr0, but it still do not work:
-int orig_cr0;
+long orig_cr0;
unsigned long *sys_call_table_my = NULL;
static int (*anything_saved)(void);
-static int clear_cr0(void) {
- unsigned int cr0 = 0;
- unsigned int ret;
+static long clear_cr0(void) {
+ unsigned long cr0 = 0;
+ unsigned long ret;
asm volatile("movq %%cr0, %%rax" : "=a"(cr0));
ret = cr0;
cr0 &= 0xfffffffffffeffff;

Is there a way to increase the maximum amount of messages that can be contained in a SysV message queue?

I just switched from POSIX to SysV because the limit is much higher on SysV (1024 vs 10). But I still need an higher limit. The limit should be changed at runtime, because it depends on the data the user chooses.
Using POSIX, it was possible to increase the limit but it was necessary to run the code as root every time and I cannot do that.
Is there a way to increase the limit in SysV?
As SYSV IPC are considered deprecated, it is a pity to design brand new applications with thoses old fashioned services.
POSIX message queues are based on a file system. It is usually mounted on /dev/mqueue:
$ ls -la /dev/mqueue
total 0
drwxrwxrwt 2 root root 40 dec. 25 21:02 .
drwxr-xr-x 20 root root 4600 dec. 25 20:51 ..
A message queue is created with mq_open(). The attr parameter provides the ability to set some attributes:
struct mq_attr {
long mq_flags; /* Flags (ignored for mq_open()) */
long mq_maxmsg; /* Max. # of messages on queue */
long mq_msgsize; /* Max. message size (bytes) */
long mq_curmsgs; /* # of messages currently in queue (ignored for mq_open()) */
};
According to the documentation, the /proc/sys/fs/mqueue/msg_max file defines the ceiling value for the maximum number of messages in a queue. In Linux 5.4, its default value is DFLT_MSGMAX (10) and its upper limit is HARD_MSGMAX (65536).
The defaults values are defined in the Linux source code (cf. /include/linux/ipc_namespace.h):
#define DFLT_QUEUESMAX 256
#define MIN_MSGMAX 1
#define DFLT_MSG 10U
#define DFLT_MSGMAX 10
#define HARD_MSGMAX 65536
#define MIN_MSGSIZEMAX 128
#define DFLT_MSGSIZE 8192U
#define DFLT_MSGSIZEMAX 8192
#define HARD_MSGSIZEMAX (16*1024*1024)
Here is an example program which creates a message queue. It receives as parameters the message queue name and the maximum number of messages in the queue:
#include <errno.h>
#include <pthread.h>
#include <mqueue.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
mqd_t mqdes;
struct mq_attr attr;
if (argc != 3) {
fprintf(stderr, "Usage: %s <mq-name> <msg_max>\n", argv[0]);
exit(EXIT_FAILURE);
}
attr.mq_flags = 0;
attr.mq_maxmsg = atoi(argv[2]);
attr.mq_msgsize = 2048;
attr.mq_curmsgs = 0;
mqdes = mq_open(argv[1], O_CREAT | O_RDWR, 0777, &attr);
if (mqdes == (mqd_t) -1) {
perror("mq_open");
return 1;
}
return 0;
}
At execution time we can verify that, by default, we can't go above 10 messages for the queue:
$ gcc mq.c -o mq -lrt
$ ./mq
Usage: ./mq <mq-name> <msg_max>
$ ./mq /q0 5
$ ls -la /dev/mqueue/
total 0
drwxrwxrwt 2 root root 60 dec. 25 21:09 .
drwxr-xr-x 20 root root 4600 dec. 25 20:51 ..
-rwxrwxr-x 1 xxxx xxxx 80 dec. 25 21:09 q0
$ ./mq /q1 9
$ ./mq /q2 10
$ ./mq /q3 11
mq_open: Invalid argument
$ ls -la /dev/mqueue/
total 0
drwxrwxrwt 2 root root 100 dec. 25 21:10 .
drwxr-xr-x 20 root root 4600 dec. 25 20:51 ..
-rwxrwxr-x 1 xxxx xxxx 80 dec. 25 21:09 q0
-rwxrwxr-x 1 xxxx xxxx 80 dec. 25 21:10 q1
-rwxrwxr-x 1 xxxx xxxx 80 dec. 25 21:10 q2
Let's change the ceiling value from 10 to 256 for msg_max:
$ cat /proc/sys/fs/mqueue/msg_max
10
$ sudo sh -c "echo 256 > /proc/sys/fs/mqueue/msg_max"
$ cat /proc/sys/fs/mqueue/msg_max
256
Now it is possible to create message queues with up to 256 messages:
$ ./mq /q3 11
$ ./mq /q4 256
$ ./mq /q5 257
mq_open: Invalid argument
$ ls -la /dev/mqueue/
total 0
drwxrwxrwt 2 root root 140 dec. 25 21:16 .
drwxr-xr-x 20 root root 4600 dec. 25 20:51 ..
-rwxrwxr-x 1 xxxx xxxx 80 dec. 25 21:09 q0
-rwxrwxr-x 1 xxxx xxxx 80 dec. 25 21:10 q1
-rwxrwxr-x 1 xxxx xxxx 80 dec. 25 21:10 q2
-rwxrwxr-x 1 xxxx xxxx 80 dec. 25 21:15 q3
-rwxrwxr-x 1 xxxx xxxx 80 dec. 25 21:16 q4
But as you say, increasing the ceiling value requires super user rights. It could be possible to create a "setuid helper" which increases the ceiling value. For example, the following program sets the ceiling value passed as parameter:
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[])
{
int fd;
int rc;
int msg_max;
if (argc != 2) {
fprintf(stderr, "Usage: %s <msg_max>\n", argv[0]);
return 1;
}
fd = open("/proc/sys/fs/mqueue/msg_max", O_RDWR);
if (fd < 0) {
perror("open()");
return 1;
}
rc = write(fd, argv[1], strlen(argv[1]));
if (rc != strlen(argv[1])) {
if (rc >= 0) {
errno = EIO;
}
perror("write()");
return 1;
}
close(fd);
return 0;
}
We can build it, change its owner/group to root and add the setuid bit:
$ gcc mq_helper.c -o mq_helper
$ sudo chown root mq_helper
$ sudo chgrp root mq_helper
$ sudo chmod 4555 mq_helper
$ ls -l mq_helper
-r-sr-xr-x 1 root root 17016 dec. 25 21:45 mq_helper
Then, it is possible to run this program from a non super user account to change the ceiling value of msg_max:
$ cat /proc/sys/fs/mqueue/msg_max
256
$ ./mq_helper 98
$ cat /proc/sys/fs/mqueue/msg_max
98

lkm func hijacking BUG

I've written a little linux kernel module, to see, how nowadays implement kernel function hijacking.
https://pastebin.com/99YJFnaq
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/syscalls.h>
#include <linux/version.h>
#include <linux/unistd.h>
#include <linux/time.h>
#include <linux/preempt.h>
#include <asm/uaccess.h>
#include <asm/paravirt.h>
#include <asm-generic/bug.h>
#include <asm/segment.h>
#define BUFFER_SIZE 512
#define MODULE_NAME "hacked_read"
#define dbg( format, arg... ) do { if ( debug ) pr_info( MODULE_NAME ": %s: " format , __FUNCTION__ , ## arg ); } while ( 0 )
#define err( format, arg... ) pr_err( MODULE_NAME ": " format, ## arg )
#define info( format, arg... ) pr_info( MODULE_NAME ": " format, ## arg )
#define warn( format, arg... ) pr_warn( MODULE_NAME ": " format, ## arg )
MODULE_DESCRIPTION( MODULE_NAME );
MODULE_VERSION( "0.1" );
MODULE_LICENSE( "GPL" );
MODULE_AUTHOR( "module author <mail#domain.com>" );
static char debug_buffer[ BUFFER_SIZE ];
unsigned long ( *original_read ) ( unsigned int, char *, size_t );
void **sct;
unsigned long icounter = 0;
static inline void rw_enable( void ) {
asm volatile ( "cli \n"
"pushq %rax \n"
"movq %cr0, %rax \n"
"andq $0xfffffffffffeffff, %rax \n"
"movq %rax, %cr0 \n"
"popq %rax " );
}
static inline uint64_t getcr0(void) {
register uint64_t ret = 0;
asm volatile (
"movq %%cr0, %0\n"
:"=r"(ret)
);
return ret;
}
static inline void rw_disable( register uint64_t val ) {
asm volatile(
"movq %0, %%cr0\n"
"sti "
:
:"r"(val)
);
}
static void* find_sym( const char *sym ) {
static unsigned long faddr = 0; // static !!!
// ----------- nested functions are a GCC extension ---------
int symb_fn( void* data, const char* sym, struct module* mod, unsigned long addr ) {
if( 0 == strcmp( (char*)data, sym ) ) {
faddr = addr;
return 1;
} else return 0;
};// --------------------------------------------------------
kallsyms_on_each_symbol( symb_fn, (void*)sym );
return (void*)faddr;
}
unsigned long hacked_read_test( unsigned int fd, char *buf, size_t count ) {
unsigned long r = 1;
if ( fd != 0 ) { // fd == 0 --> stdin (sh, sshd)
return original_read( fd, buf, count );
} else {
icounter++;
if ( icounter % 1000 == 0 ) {
info( "test2 icounter = %ld\n", icounter );
info( "strlen( debug_buffer ) = %ld\n", strlen( debug_buffer ) );
}
r = original_read( fd, buf, count );
strncat( debug_buffer, buf, 1 );
if ( strlen( debug_buffer ) > BUFFER_SIZE - 100 )
debug_buffer[0] = '\0';
return r;
}
}
int hacked_read_init( void ) {
register uint64_t cr0;
info( "Module was loaded\n" );
sct = find_sym( "sys_call_table" );
original_read = (void *)sct[ __NR_read ];
cr0 = getcr0();
rw_enable();
sct[ __NR_read ] = hacked_read_test;
rw_disable( cr0 );
return 0;
}
void hacked_read_exit( void ) {
register uint64_t cr0;
info( "Module was unloaded\n" );
cr0 = getcr0();
rw_enable();
sct[ __NR_read ] = original_read;
rw_disable( cr0 );
}
module_init( hacked_read_init );
module_exit( hacked_read_exit );
Makefile:
CURRENT = $(shell uname -r)
KDIR = /lib/modules/$(CURRENT)/build
PWD = $(shell pwd)
TARGET = hacked_read
obj-m := $(TARGET).o
default:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
#rm -f *.o .*.cmd .*.flags *.mod.c *.order
#rm -f .*.*.cmd *.symvers *~ *.*~ TODO.*
#rm -fR .tmp*
#rm -rf .tmp_versions
Thereafter, I'm making the module and inserting it.
Of-course, the better way is to do it - inside qemu machine. I'm using default Kali 2018.1 installed on image hdd.qcow2 [30Gb]. Kernel 4.14.13 is a default kernel built by me with DEBUG flags:
# diff /boot/config-4.14.13 /boot/config-4.14.0-kali3-amd64
3c3
< # Linux/x86_64 4.14.13 Kernel Configuration
---
> # Linux/x86 4.14.12 Kernel Configuration
7620c7620
< CONFIG_GDB_SCRIPTS=y
---
> # CONFIG_GDB_SCRIPTS is not set
7652,7655c7652
< CONFIG_DEBUG_KMEMLEAK=y
< CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=400
< CONFIG_DEBUG_KMEMLEAK_TEST=m
< # CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF is not set
---
> # CONFIG_DEBUG_KMEMLEAK is not set
CONFIG_DEBUG_KMEMLEAK - is useless on amd64, so there is only CONFIG_GDB_SCRIPTS plays a role.
Back to the game:
# make
# cp hacked_read.ko /lib/modules/4.14.13/hacked_read.ko
# depmod
# modprobe hacked_read
Thereafter, I'm typing different symbols, mostly a and left arrow and delete, as you can see from syslog: icounter = 44000, so it is 44k symbols was typed by me, before bug appears, sometimes more, sometimes less... To get this number faster I'm using /usr/bin/xset r rate 20 60,
or even insert false in if/else statement like this if ( fd != 0 && false ) { // fd == 0 --> stdin (sh, sshd) - this will automate the process.
The Bug
/var/log/syslog/
Aug 30 10:20:37 kali kernel: [ 1540.483650] hacked_read: test2 icounter = 44000
Aug 30 10:20:37 kali kernel: [ 1540.483654] hacked_read: strlen( debug_buffer ) = 202
Aug 30 10:20:42 kali kernel: [ 1546.187954] hacked_read: test2 icounter = 45000
Aug 30 10:20:42 kali kernel: [ 1546.187958] hacked_read: strlen( debug_buffer ) = 376
Aug 30 10:20:58 kali kernel: [ 1561.366421] BUG: unable to handle kernel paging request at ffffffffc071909b
Aug 30 10:20:58 kali kernel: [ 1561.366434] IP: 0xffffffffc071909b
Aug 30 10:20:58 kali kernel: [ 1561.366436] PGD b3a0e067 P4D b3a0e067 PUD b3a10067 PMD 2346c4067 PTE 0
Aug 30 10:20:58 kali kernel: [ 1561.366441] Oops: 0010 [#1] SMP PTI
Aug 30 10:20:58 kali kernel: [ 1561.366443] Modules linked in: hacked_read(O) 9p fscache fuse ppdev bochs_drm sg ttm 9pnet_virtio evdev joydev drm_kms_helper pcspkr serio_raw 9pnet drm parport_pc parport button binfmt_misc ip_tables x_tables autofs4 ext4 crc16 mbcache jbd2 crc32c_generic fscrypto ecb sr_mod cdrom sd_mod ata_generic crct10dif_pclmul crc32_pclmul crc32c_intel ghash_clmulni_intel pcbc ata_piix libata scsi_mod aesni_intel aes_x86_64 crypto_simd glue_helper cryptd psmouse floppy virtio_pci virtio_ring virtio e1000 i2c_piix4 [last unloaded: hacked_read]
Aug 30 10:20:58 kali kernel: [ 1561.366488] CPU: 0 PID: 1788 Comm: tee Tainted: G O 4.14.13 #1
Aug 30 10:20:58 kali kernel: [ 1561.366490] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1 04/01/2014
Aug 30 10:20:58 kali kernel: [ 1561.366491] task: ffff9939ac178000 task.stack: ffffb2570359c000
Aug 30 10:20:58 kali kernel: [ 1561.366493] RIP: 0010:0xffffffffc071909b
Aug 30 10:20:58 kali kernel: [ 1561.366494] RSP: 0018:ffffb2570359ff38 EFLAGS: 00010292
Aug 30 10:20:58 kali kernel: [ 1561.366496] RAX: 000000000000005e RBX: 00007ffe554f8940 RCX: 0000000000000000
Aug 30 10:20:58 kali kernel: [ 1561.366497] RDX: 0000000000000000 RSI: ffff9939a0af7c10 RDI: ffff9939c0a20bb8
Aug 30 10:20:58 kali kernel: [ 1561.366498] RBP: 0000000000002000 R08: 0000000000000000 R09: 0000000000000000
Aug 30 10:20:58 kali kernel: [ 1561.366499] R10: 000000000000005e R11: 00000000000003f1 R12: ffffffffc071b360
Aug 30 10:20:58 kali kernel: [ 1561.366501] R13: 000055ae361bb4a0 R14: 0000000000000010 R15: 00007ffe554faa98
Aug 30 10:20:58 kali kernel: [ 1561.366502] FS: 00007f60491184c0(0000) GS:ffff9939ffc00000(0000) knlGS:0000000000000000
Aug 30 10:20:58 kali kernel: [ 1561.366504] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
Aug 30 10:20:58 kali kernel: [ 1561.366505] CR2: ffffffffc071909b CR3: 00000001d9018005 CR4: 00000000000606f0
Aug 30 10:20:58 kali kernel: [ 1561.366514] Call Trace:
Aug 30 10:20:58 kali kernel: [ 1561.366524] ? system_call_fast_compare_end+0xc/0x6f
Aug 30 10:20:58 kali kernel: [ 1561.366526] Code: Bad RIP value.
Aug 30 10:20:58 kali kernel: [ 1561.366532] RIP: 0xffffffffc071909b RSP: ffffb2570359ff38
Aug 30 10:20:58 kali kernel: [ 1561.366532] CR2: ffffffffc071909b
Aug 30 10:20:58 kali kernel: [ 1561.366535] ---[ end trace ca74de96d373ac0b ]---
Could somebody, please, tell me which way to dig?
There is no overflows inside debug_buffer array - it is completely true.
There is no conflicts in asm code, while hijacking is carried out.
It is tiny, light script... Where is the BUG?
Update1:
Looks like I've found a reason why it starts crashing. The BUG appears right after command rmmod hacked_read. So module_exit() is wrong, probably asm's cli & sti not enough.
As the module is removed from the Linux kernel, all memory used by the module (data and code) is released. The exit() function of the module restores the pointer to the original function. However, the kernel may be executing the code of the substitute function at the time the module is removed. Suddenly, right in the middle of that the function disappears as the memory taken by the module's code is released. Hence the bug.
Obviously you can't remove the module after you restore the pointer to the original function until you're sure that there are no kernel threads that (may) execute the code of the substitute function. After the pointer is restored, all new kernel threads will execute the original function, so you need to wait until any current threads finish the execution of the substitute function. How to do that is another issue. You may need to employ some tricks like reference counters, etc.
As #Aleksey mentioned, the issue was outside of the module.
The tee command used read() in its sleeping manner. While I've removed module nothing happened, but there was my little bash script:
#!/bin/bash
logfile="micro-test.log"
while sleep 0;do
echo -n "$(date): $(uptime): "
echo "1 2" | awk '{print $1}'
sleep 60;
done | tee -a $logfile
How I've found the piece of BUG:
As I said, my guest's kernel was compiled with CONFIG_GDB_SCRIPTS=y. Now I'm attaching guest from the host's gdb:
# gdb
(gdb) set logging file gdbcmd2.out
(gdb) set logging on
Copying output to gdbcmd2.out.
(gdb)
Already logging to gdbcmd2.out.
(gdb) target remote :1234
Remote debugging using :1234
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0xffffffff99082e42 in ?? ()
(gdb) add-auto-load-safe-path /usr/src/linux-source-4.14/scripts/gdb/vmlinux-gdb.py
(gdb) file /usr/src/linux-source-4.14/vmlinux
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from /usr/src/linux-source-4.14/vmlinux...done
On the guest side, I'm extracting addresses:
root#kali:~# cat /sys/module/hacked_read/sections/.text
0xffffffffc06e9000
root#kali:~# cat /sys/module/hacked_read/sections/.bss
0xffffffffc06eb34
On the host side, adding module to debugging:
(gdb) add-symbol-file /usr/src/hacked_read/hacked_read.ko 0xffffffffc06e9000 -s .bss 0xffffffffc06eb34
(gdb) p hacked_read_test
$1 = {unsigned long (unsigned int, char *, size_t)} 0xffffffffc06e9030 <hacked_read_test>
(gdb) maintenance info line-table
... BIG-BIG-OUT-PUT ...
Thereafter, I can see in logfile: gdbcmd2.out - listing of my code with addresses. For example, 0xffffffffc06e9030 - the address of hacked_read_test function:
# grep 0xffffffffc06e9030 gdbcmd2.out
$1 = {unsigned long (unsigned int, char *, size_t)} 0xffffffffc06e9030 <hacked_read_test>
6 77 0xffffffffc06e9030
77 - line of code
$ head -n 77 hacked_read.c | tail -n 1
unsigned long hacked_read_test( unsigned int fd, char *buf, size_t count ) {
Bingo!
Now, on the guest side, I'm doing rmmod hacked_read. After 60+- seconds appears BUG:
Sep 9 06:35:28 kali kernel: [281996.592759] hacked_read: Module was unloaded
Sep 9 06:36:11 kali kernel: [282040.218523] BUG: unable to handle kernel paging request at ffffffffc06e909b
Sep 9 06:36:11 kali kernel: [282040.218530] IP: 0xffffffffc06e909b
Sep 9 06:36:11 kali kernel: [282040.218531] PGD 22980e067 P4D 22980e067 PUD 229810067 PMD 2356e3067 PTE 0
Sep 9 06:36:11 kali kernel: [282040.218534] Oops: 0010 [#9] SMP PTI
Sep 9 06:36:11 kali kernel: [282040.218536] Modules linked in: sctp_diag sctp libcrc32c tcp_diag udp_diag dccp_diag dccp inet_diag unix_diag 9p fscache fuse bochs_drm ttm ppdev drm_kms_helper joydev evdev serio_raw pcspkr sg 9pnet_virtio 9pnet parport_pc parport button drm binfmt_misc ip_tables x_tables autofs4 ext4 crc16 mbcache jbd2 crc32c_generic fscrypto ecb sr_mod cdrom sd_mod ata_generic crct10dif_pclmul crc32_pclmul crc32c_intel ghash_clmulni_intel pcbc aesni_intel aes_x86_64 crypto_simd glue_helper cryptd ata_piix psmouse floppy virtio_pci virtio_ring virtio e1000 i2c_piix4 libata scsi_mod [last unloaded: hacked_read]
Sep 9 06:36:11 kali kernel: [282040.218567] CPU: 0 PID: 32196 Comm: tee Tainted: G D O 4.14.13 #1
Comm: tee & BUG: unable to handle kernel paging request at ffffffffc06e909b
Host:
# grep ffffffffc06e909b gdbcmd2.out
18 88 0xffffffffc06e909b
88 - line of code:
$ head -n 88 hacked_read.c | tail -n 1
strncat( debug_buffer, buf, 1 );
It is easy to see, that kernel unable to give tee the address of the next line after original_read():
77:unsigned long hacked_read_test( unsigned int fd, char *buf, size_t count ) {
78. unsigned long r = 1;
79. if ( fd != 0 ) { // fd == 0 --> stdin (sh, sshd)
80. return original_read( fd, buf, count );
81. } else {
82. icounter++;
83. if ( icounter % 1000 == 0 ) {
84. info( "test2 icounter = %ld\n", icounter );
85. info( "strlen( debug_buffer ) = %ld\n", strlen( debug_buffer ) );
86. }
87. r = original_read( fd, buf, count );
88. strncat( debug_buffer, buf, 1 );
if ( strlen( debug_buffer ) > BUFFER_SIZE - 100 )
debug_buffer[0] = '\0';
return r;
}
}

Resources