Skip to content

Adds module version for arm32 #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
# Linux Kernel Hook
This is just a small project to hook syscalls in an x86_64 Linux kernel. I've tested it on `4.9.0-3-amd64`. It's mostly just a project for me to explore writing kernel modules.

Most of the other kernel hooks I've seen on the public internet use an outdated mechanism to grab the syscall table - the oldest tutorials relied on `sys_call_table` being exported as a public symbol, and slightly newer ones had a brute-forcing approach where they would try to find the syscall table in between two different symbols. This one doesn't do anything that fancy - the `load.sh` script just greps `/proc/kallsyms` for the syscall table addresses.
Syscall hooks for x86_64 and arm32.

## Usage
It comes built in with a `mkdir` hook that just proxies the syscall over to the original syscall. `module.c:69` is responsible for adding the hook, and the code at `hooks.c:8` is the actual hooked function which does the proxying
```bash
make && sh ./load.sh
# Usage
```
cd <arch>
make
cd ..
./load.sh
```

The example hook for mkdir prints the argument of the created folder in the kernel logs.

To unload the module: `rmmod lkh`
File renamed without changes.
16 changes: 16 additions & 0 deletions arm/hooks.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include <linux/module.h>
#include "hooks.h"
#include "sys_hook.h"

extern struct sys_hook *lkh_sys_hook;

asmlinkage int
mkdir_hook(const char *path, int mode)
{
sys_mkdir_t sys_mkdir;
printk(KERN_INFO "mk_dir hook triggered %s\n", path);

sys_mkdir = (sys_mkdir_t)sys_hook_get_orig(lkh_sys_hook, __NR_mkdir);

return sys_mkdir(path, mode);
}
File renamed without changes.
81 changes: 81 additions & 0 deletions arm/module.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
#include "sys_hook.h"
#include "hooks.h"

struct sys_hook *lkh_sys_hook;

static uintptr_t
hex_addr_to_pointer(const char *str)
{
uintptr_t sum;

/* Go through str char by char and accumulate into sum */
for (sum = 0; *str != '\0'; str++) {
sum *= 16;
if (*str >= '0' && *str <= '9')
sum += (*str - '0');
else if (*str >= 'a' && *str <= 'f')
sum += (*str - 'a') + 10;
else if (*str >= 'A' && *str <= 'F')
sum += (*str - 'A') + 10;
else
return 0;
}

return sum;
}

/* Module parameter macros */
static char *kbaseArm = NULL;
module_param(kbaseArm, charp, 0);
MODULE_PARM_DESC(kbaseArm, "Base address of the arm syscall table, in hex");

static int __init
module_entry(void)
{
uintptr_t k_arm;

printk(KERN_INFO "lkh initializing...\n");

/* Validate that we got parameters */
if (kbaseArm == NULL || kbaseArm[0] == '\0') {
printk(KERN_INFO "failed to get arm syscall table\n");
return 1;
}

/* Validate that we got valid syscall base addresses */
if ((k_arm = hex_addr_to_pointer(kbaseArm)) == 0) {
printk(KERN_INFO "invalid arm syscall address %p\n", (void *)k_arm);
return 1;
}

if ((lkh_sys_hook = sys_hook_init(k_arm)) == NULL) {
printk(KERN_INFO "failed to initialize sys_hook\n");
return 1;
}

sys_hook_add(lkh_sys_hook, __NR_mkdir, (void *)mkdir_hook);

printk(KERN_INFO "lkh loaded\n");
return 0;
}

static void __exit
module_cleanup(void)
{
sys_hook_free(lkh_sys_hook);
printk(KERN_INFO "lkh has finished\n");
}

/* Declare the entry and exit points of our module */
module_init(module_entry);
module_exit(module_cleanup);

/* Shut up kernel warnings about tainted kernels due to non-free software */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("github:jha");
MODULE_DESCRIPTION("Linux Kernel Hook");
102 changes: 102 additions & 0 deletions arm/sys_hook.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#include <linux/module.h>
#include <linux/syscalls.h>
#include <linux/fcntl.h>
#include <linux/string.h>
#include <linux/slab.h>
#include "sys_hook.h"

struct sys_hook *
sys_hook_init(uintptr_t k_arm)
{
struct sys_hook *sh;

sh = kmalloc(sizeof (struct sys_hook), GFP_KERNEL);
if (IS_ERR(sh)) {
printk(KERN_INFO "not enough memory for hooks\n");
return NULL;
}

sh->arm_sct = (unsigned int *)k_arm;

sh->head = sh->tail = NULL;

return sh;
}

bool_t
sys_hook_add(struct sys_hook *hook, unsigned int syscall_id, void *func)
{
struct sys_hook_ent *ent;

ent = kmalloc(sizeof (struct sys_hook_ent), GFP_KERNEL);
if (IS_ERR(ent)) {
printk(KERN_INFO "not enough memory for sys hook\n");
return FALSE;
}

/* Create our new hook entry */
ent->next = NULL;
ent->syscall_id = syscall_id;
ent->original = hook->arm_sct[syscall_id];
ent->hooked = (uintptr_t)func;
ent->type = SHT_ARM;

/* Overwrite the entry in the syscall table */
hook->arm_sct[syscall_id] = (unsigned int)ent->hooked;

/* Update the hook list */
if (hook->head == NULL)
hook->head = hook->tail = ent;
else {
hook->tail->next = ent;
hook->tail = ent;
}

return TRUE;
}

bool_t
sys_hook_del(struct sys_hook *hook, unsigned int syscall_id)
{
return TRUE;
}

uintptr_t
sys_hook_get_orig(struct sys_hook *hook, unsigned int syscall_id)
{
struct sys_hook_ent *curr;

for (curr = hook->head; curr != NULL; curr = curr->next) {
if (curr->type == SHT_ARM && curr->syscall_id == syscall_id)
return curr->original;
}


return 0;
}

void
sys_hook_free(struct sys_hook *hook)
{
struct sys_hook_ent *curr, *tmp;

if (hook == NULL)
return;

for (curr = hook->head; curr != NULL;) {
switch (curr->type) {
case SHT_ARM:
hook->arm_sct[curr->syscall_id] = (unsigned int)curr->original;
break;
default:
printk(KERN_EMERG "possible memory corruption in syscall hooks - invalid hook state\n");
break;
}

tmp = curr->next;
kfree(curr);
curr = tmp;
}

kfree(hook);
}
34 changes: 34 additions & 0 deletions arm/sys_hook.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once

#include "types.h"

struct sys_hook {
unsigned int *arm_sct;
struct sys_hook_ent *head, *tail;
};

enum sys_hook_type {
SHT_ARM,
};

struct sys_hook_ent {
struct sys_hook_ent *next;
unsigned int syscall_id;
uintptr_t original, hooked;
enum sys_hook_type type;
};

struct sys_hook *
sys_hook_init(uintptr_t k_arm);

bool_t
sys_hook_add(struct sys_hook *, unsigned int syscall_id, void *func);

bool_t
sys_hook_del(struct sys_hook *, unsigned int syscall_id);

uintptr_t
sys_hook_get_orig(struct sys_hook *, unsigned int syscall_id);

void
sys_hook_free(struct sys_hook *hook);
File renamed without changes.
7 changes: 6 additions & 1 deletion load.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,10 @@ fi

KBASE32=$(cat /proc/kallsyms | grep " ia32_sys_call_table" | awk '{ print $1 }')
KBASE64=$(cat /proc/kallsyms | grep " sys_call_table" | awk '{ print $1 }')
KBASEARM=$(cat /proc/kallsyms | grep " sys_call_table" | awk '{ print $1 }')

insmod ./lkh.ko kbase32="$KBASE32" kbase64="$KBASE64"
if [ "$(uname -m)" = "x86_64" ]; then
insmod ./x86_64/lkh.ko kbase32="$KBASE32" kbase64="$KBASE64"
else
insmod ./arm/lkh.ko kbaseArm="$KBASEARM"
fi
10 changes: 10 additions & 0 deletions x86_64/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
obj-m := lkh.o
lkh-objs += module.o
lkh-objs += sys_hook.o
lkh-objs += hooks.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
File renamed without changes.
9 changes: 9 additions & 0 deletions x86_64/hooks.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once

#include <linux/syscalls.h>
#include "types.h"

typedef asmlinkage int (*sys_mkdir_t)(const char *, int);

asmlinkage int
mkdir_hook(const char *, int);
File renamed without changes.
File renamed without changes.
File renamed without changes.
5 changes: 5 additions & 0 deletions x86_64/types.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#pragma once

typedef unsigned int bool_t;
#define FALSE 0
#define TRUE 1