Skip to content
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

Possible to hook sys_kill(pid, signal)? #3

Closed
alichtman opened this issue Apr 8, 2019 · 28 comments
Closed

Possible to hook sys_kill(pid, signal)? #3

alichtman opened this issue Apr 8, 2019 · 28 comments

Comments

@alichtman
Copy link

alichtman commented Apr 8, 2019

Just wanted to start out by saying this is an awesome project! Nice work!


I'm having a problem hooking sys_kill, and I was hoping you'd be able to help me out.

sys_kill is defined here in linux/syscalls.h.

I have been trying to hook this function for a while and can not get anything to compile. I was able to hook kill_pid, however, that did not hook the kill syscall.

// BUG: COMPILES BUT DOESN'T HOOK KILL.
KHOOK(kill_pid);
static int khook_kill_pid(struct pid *pid, int sig, int priv) {
	if (pid->numbers->nr == MAGIC_NUM) {
		return do_the_thing();
	} else {
		return KHOOK_ORIGIN(kill_pid, pid, sig, priv);
	}
}

Is there a way to hook the actual kill syscall? Or can it not be done since the syscall table is no longer exported, post-Kernel 2.6?

@milabs
Copy link
Owner

milabs commented Apr 8, 2019

Hey, thank you for using KHOOK.

As for hooking system calls you have to hook sys_XXX symbols like sys_kill. See the example below:

KHOOK_EXT(long, sys_kill, long, long);
static long khook_sys_kill(long pid, long sig) {
        printk("sys_kill -- %s pid %ld sig %ld\n", current->comm, pid, sig);
        return KHOOK_ORIGIN(sys_kill, pid, sig);
}

Just use the right prototype for this sys_kill...

@alichtman
Copy link
Author

Oh, whoops. My brain was not working correctly last night. Thanks for the help!

However, the example did not work for me. When I run $ kill <some_pid> in bash, this debug statement isn't printed.

I tried changing the first parameter to a pid_t type, and that did not solve the problem either.

I'm pretty sure that $ kill calls sys_kill(), but I'm not sure if that's true.

@milabs
Copy link
Owner

milabs commented Apr 8, 2019

It's weird. I've tried to kill -9 1 and the dmesg shows me:
[ 4630.052329] sys_kill -- bash pid 1 sig 9

By the way, my kernel is 4.8.0-53-generic

@alichtman
Copy link
Author

I'm working on 4.18.0-17-generic. Still not seeing the same behavior you're describing. Hmm.

@milabs
Copy link
Owner

milabs commented Apr 9, 2019

OK, it makes sense then. From some point they changed syscalls notation and argument passing. So, now you have to look for __x64_sys_kill with long (*)(const struct pt_regs *) proto instead of sys_kill. The code may look like this (didn't test):

KHOOK_EXT(long, __x64_sys_kill, const struct pt_regs *);
static long khook___x64_sys_kill(const struct pt_regs *regs) {
        printk("sys_kill -- %s pid %ld sig %ld\n", current->comm, regs->di, regs->si);
        return KHOOK_ORIGIN(__x64_sys_kill, regs);
}

@alichtman
Copy link
Author

Oh, alright, cool! I'll try this out when I have a minute.

@alichtman
Copy link
Author

Worked perfectly. Thanks for the help!

@milabs
Copy link
Owner

milabs commented Apr 15, 2019

Updated the README with this example. Thank you for the question.

@alichtman
Copy link
Author

alichtman commented Apr 16, 2019

Can you explain where you found this new function signature?

KHOOK_EXT(long, __x64_sys_kill, const struct pt_regs *);
static long khook___x64_sys_kill(const struct pt_regs *regs)

I'm having trouble reproducing this with other syscalls.

For example, I have this hook for msgctl, and it's not being tripped.

KHOOK_EXT(long, __x64_ksys_msgctl, int, int, struct msqid_ds __user *);
static long khook___x64_ksys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)

Here is a relevant LKML email: https://lore.kernel.org/lkml/[email protected]/

But I can't figure out where you found the prototype for the updated sys_kill function. As far as I can tell, it's not in the source code. But I also know that can't be right, so I'm a little stuck. Google has turned up nothing so far.

Edit: I found this: https://github.com/torvalds/linux/blob/618d919cae2fcaadc752f27ddac8b939da8b441a/arch/x86/include/asm/syscall_wrapper.h#L125

and updated my code to:

KHOOK_EXT(long, __x64_ksys_msgctl, const struct pt_regs *);
static long khook___x64_ksys_msgctl(const struct pt_regs * regs) {
	// int msqid, int cmd, struct msqid_ds __user *buf
	action_task* task;
	// Read first two arguments
	if (regs->di == -1 && regs->si == -1) {
		if (condition) { 
			printk(KERN_EMERG "sys_msgctl -- preparing...\n");
			...
			return 0;
		} else {
			printk(KERN_EMERG "sys_msgctl\n");
			task = (action_task*) regs->dx;
			...
			return 0;
		}
	} else {
		return KHOOK_ORIGIN(__x64_ksys_msgctl, regs);
	}
}

However, this still does not hook msgctl calls.

@alichtman alichtman reopened this Apr 16, 2019
@milabs
Copy link
Owner

milabs commented Apr 16, 2019

TLDR: __x64_ksys_msgctl -> __x64_sys_msgctl

https://elixir.bootlin.com/linux/latest/source/ipc/msg.c#L614

SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
{
	return ksys_msgctl(msqid, cmd, buf);
}

You could ether hook ksys_msgctl(int, int, struct msqid_ds *) as a kernel function (in case if it is exported as a ksym with name -- check by grepping /proc/kallsyms) or hook __x64_sys_msgctl/sys_msgctl as a system call handler.

@alichtman
Copy link
Author

alichtman commented Apr 16, 2019

ksys_msgctl does appear in /proc/kallsyms, and I've made this change: __x64_ksys_msgctl -> __x64_sys_msgctl.

It's still not being hooked properly.

The syscall definition you pasted in was the reason I initially had sys_msgctl hooked like this:

KHOOK_EXT(long, __x64_ksys_msgctl, int, int, struct msqid_ds __user *);
static long khook___x64_ksys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)

Should I change that ^ to __x64_sys_msgctl?

I feel like I’m missing some core understanding of what I’m doing. Do you have any recommendations for topics I should read up on?

@milabs
Copy link
Owner

milabs commented Apr 16, 2019

Should I change that ^ to __x64_sys_msgctl?

YES (but I didn't test it)

@milabs
Copy link
Owner

milabs commented Apr 16, 2019

This works well for me:

KHOOK_EXT(long, __x64_sys_msgctl, const struct pt_regs *);
static long khook___x64_sys_msgctl(const struct pt_regs *regs)
{
	printk("%s -- msqid:%ld cmd:%ld ptr:%p\n", current->comm, regs->di, regs->si, (void *)regs->dx);
	return KHOOK_ORIGIN(__x64_sys_msgctl, regs);
}

main.c

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int main(int argc, const char *argv[])
{
	msgctl(100, 200, (void *)300);
	return 0;
}

$ dmesg
[108112.452874] a.out -- msqid:100 cmd:200 ptr:000000008b1f8f3d

@alichtman
Copy link
Author

What you posted did work for me. Funny enough, I actually posted that signature in a previous comment. I think my error was that the line below was never hit. I passed a -1 in to msgctl for the first two parameters and it was turned into INT_MAX (although I don't understand why, since ints are signed...):

if (regs->di == -1 && regs->si == -1) {

Anyways, the issue is now resolved. Thank you again for the help.

@KTalinki
Copy link

KTalinki commented May 3, 2019

hi,
I am trying to hook sys_open and some other syscalls to intercept the file system activity, on kernel 4.17/4.18 on x86_64.

/proc/kallsyms shows __x64_sys_open, but not ksys_open.
Not sure if I need to hook ksys_open or __x64_sys_open
I am getting compilation error of 'undelcared identifier' for sys_open & __x64_sys_open.

What are the right headers I need to include ?

Along with sys_open, I need to hook sys_creat, sys_openat, sys_execve, sys_truncate, sys_ftruncate, sys_write, etc...

Before kernel 4.17, I was able to find the symbol for sys_xyz, but from Kernel 4.17 onward, I am not sure if I need to hook __x64_sys_xyz or ksys_xyz or sys_xyz.

In syscalls.h Some system call are declared with /__ARCH_WANT_SYSCALL_DEPRECATED/

Please help,
thank you,
Kumar T

@milabs
Copy link
Owner

milabs commented May 3, 2019

@KTalinki Use the following code for every "modern" syscall handler (replace XXX with open or so):

KHOOK_EXT(long, __x64_sys_XXX, const struct pt_regs *);
static long khook___x64_sys_XXX(const struct pt_regs *regs)
{
	// do the job
	return KHOOK_ORIGIN(__x64_sys_XXX, regs);
}

@KTalinki
Copy link

KTalinki commented May 3, 2019

Thank you milabs.

If I were to use hook in a traditional way of finding a specific syscall table address and hook it,
which specific syscall API to hook
sys_open or __x64_sys_open or ksys_open
sys_open or __x64_sys_creat or ksys_creat
sys_openat or __x64_sys_openat or ksys_openat
sys_execve or __x64_sys_exeeve or ksys_execve
etc ...

and what are the header files __x64_sys_creat or __x64_sys_openat are declared ?

It will be great if someone can share a sample with what is needed to hook syscalls on kernels from 4.17 onward.

thank you,
Kumar T

@milabs
Copy link
Owner

milabs commented May 3, 2019

Dear @KTalinki, you don't need to have headers to hook __x64_sys_XXX as they all have the same prototype (like I shown before). As for the ksys_xxx() symbols I'd suggest you to look for the Linux kernel source code to see how they are used. Finally, you're the kernel developer and you have to be able to dig the source code.

@KTalinki
Copy link

KTalinki commented May 7, 2019

thank you @milabs , the way our existing code is structured, it uses the declaration of the syscalls from .../include/linux/syscalls.h
But on kernel 4.17 with __x64_sys_open, is not declared in <syscalls.h>
And <arch/x85/include/generated/asm/syscalls_64.h> has the declaration something like
__SYSCALL_64(2, __X64_sys_open,)

The issue I am running in to is compilation errors for __x64_sys_open and __SYSCALL_64.
Including <arch/x85/include/generated/asm/syscalls_64.h> is not helping.

Any help is appreciated,
thank you,
Kumar

@milabs
Copy link
Owner

milabs commented May 8, 2019

@KTalinki Again, you don't have to include ANY header to hook __x64_XXX, just use the KHOOK_EXT macro:

KHOOK_EXT(long, __x64_sys_XXX, const struct pt_regs *);
static long khook___x64_sys_XXX(const struct pt_regs *regs)
{
	// do the job
	return KHOOK_ORIGIN(__x64_sys_XXX, regs);
}

Which is the error with using this code?

@wbt165
Copy link

wbt165 commented May 12, 2020

@milabs hello,I want to hook sys_exevce. I'm using the KHOOK_EXT macro,which work well on the 64-bit machines,for example Redhat6.8 x86_64 or CentOS8 x86_64.The code is as follows:
Linux Redhat6.8 2.6.32-642.el6.x86_64:

KHOOK_EXT(long, sys_execve, char __user *, char __user * __user *, char __user * __user *, struct pt_regs *);
static long khook_sys_execve(char __user *name, char __user * __user *argv, char __user * __user *envp, struct pt_regs *regs)
{
	long ret = 0;
	ret = KHOOK_ORIGIN(sys_execve, name, argv, envp, regs);
	return ret;
}

CentOS8 x86_64:

KHOOK_EXT(long, __x64_sys_execve, const struct pt_regs *);
static long khook___x64_sys_execve(const struct pt_regs *regs)
{
	long ret = 0;
	ret = KHOOK_ORIGIN(__x64_sys_execve, regs);
	return ret;
}

BUT,I have a problem on the 32-bit machine. I test it on Linux Redhat 6.8 2.6.32-642.el6.i686.The code is as follows:

KHOOK_EXT(int, sys_execve, struct pt_regs *);
static int khook_sys_execve(struct pt_regs *regs)
{
	int ret = 0;
	ret = KHOOK_ORIGIN(sys_execve, regs);
	return ret;
}

I Found the sys_execve() symbols from the source code as follow:
image

After I insmod, I execute "ls" or any other commands. ERROR as following:
image

I suspect there is a problem with this code executing on a 32-bit machine.I hope you can take a look in your busy schedule.
Thanks for your help!

@milabs
Copy link
Owner

milabs commented May 12, 2020

@wbt165 I'd recommend you to hook do_execve instead of sys_execve for exec case. Could you try to hook it that way?

@wbt165
Copy link

wbt165 commented May 13, 2020

@milabs Thanks for your response!
I test do_execve,and the results as follows:

  • [do_execve work well on Linux Redhat6.8 2.6.32-642.el6.x86_64 ]
#include <linux/sched.h>
KHOOK(do_execve);
static int khook_do_execve(const char * filename,
	char __user *__user *argv,
	char __user *__user *envp,
	struct pt_regs * regs)
{
	int ret = 0;
	ret = KHOOK_ORIGIN(do_execve, filename, argv, envp, regs);
	pr_info("%s - Pass %s = %d\n", __func__, filename, ret);
	return ret;
}
  • [do_execve CANNOT hook on CentOS8 4.18.0-147.8.1.el8_1.x86_64, because the debug info is not printed out]
#include <linux/binfmts.h>
KHOOK(do_execve);
static int khook_do_execve(struct filename *filename,
	const char __user *const __user *__argv,
	const char __user *const __user *__envp)
{
	int ret = 0;
	ret = KHOOK_ORIGIN(do_execve, filename, __argv, __envp);
	pr_info("%s - Pass %s = %d\n", __func__, filename->name, ret);
	return ret;
}
  • [ do_execve CANNOT work on Linux Redhat6.8 2.6.32-642.el6.i686]
#include <linux/sched.h>
KHOOK(do_execve);
static int khook_do_execve(const char * filename,
	char __user *__user *argv,
	char __user *__user *envp,
	struct pt_regs * regs)
{
	int ret = 0;
	ret = KHOOK_ORIGIN(do_execve, filename, argv, envp, regs);
	pr_info("%s - Pass %s = %d\n", __func__, filename, ret);
	return ret;
}

After I insmod, I execute "ls" or any other commands. ERROR as following:
image
The debug info is as follow:
image

Thanks for your help!

@milabs
Copy link
Owner

milabs commented May 15, 2020

@wbt165 Unfortunately, 32-bit stub is not implemented as I never had such requirement for myself (see https://github.com/milabs/khook/blob/master/khook/x86/stub.S#L24). It would be great if you'll try to implement missing part of the macro.

@milabs
Copy link
Owner

milabs commented May 15, 2020

If you could test that code on 32-bit system?

.macro CALL_COPY_N_ARGS n
	sub $(\n * 4), %esp
	.set i, 0
	.rept \n
		mov ((\n + i + 1) * 4)(%esp), %eax
		mov %eax, (i * 4)(%esp)
		.set i, i + 1
	.endr
	mov $0xcacacaca, %eax
	call *%eax
	add $(\n * 4), %esp
.endm

@wbt165
Copy link

wbt165 commented May 18, 2020

If you could test that code on 32-bit system?

.macro CALL_COPY_N_ARGS n
	sub $(\n * 4), %esp
	.set i, 0
	.rept \n
		mov ((\n + i + 1) * 4)(%esp), %eax
		mov %eax, (i * 4)(%esp)
		.set i, i + 1
	.endr
	mov $0xcacacaca, %eax
	call *%eax
	add $(\n * 4), %esp
.endm

@milabs Unfortunately,this code has NO effect……

@milabs
Copy link
Owner

milabs commented May 18, 2020

@milabs Unfortunately,this code has NO effect……

Made a branch for you to test:
https://github.com/milabs/khook/tree/i686-fix
Could you please test it?

@wbt165
Copy link

wbt165 commented May 19, 2020

@milabs Thank you for creating a branch! I tested it on Linux Redhat6.8 2.6.32-642.el6.i686, but it didn't work. The code is as follow:

#include <linux/uaccess.h>
#include <linux/slab.h>
static char *duplicate_filename(const char __user *filename)
{
	char *kernel_filename;
	int nRet;

	kernel_filename = kmalloc(4096, GFP_KERNEL);
	if (!kernel_filename)
	{
		pr_info("%s - NULL!\n", __func__);
		return NULL;
	}

	nRet = strncpy_from_user(kernel_filename, filename, 4096);
	if (nRet < 0) {
		pr_info("%s - nRet = %d!\n", __func__, nRet);
		kfree(kernel_filename);
		return NULL;
	}

	return kernel_filename;
}

KHOOK_EXT(int, sys_execve, struct pt_regs *);
static int khook_sys_execve(struct pt_regs *regs)
{
	int ret = 0;
	char *filename;

	filename = duplicate_filename((char __user *)regs->bx);
	pr_info("%s - %s!\n", __func__, filename);

	ret = KHOOK_ORIGIN(sys_execve, regs);
	pr_info("%s ret = %d\n", __func__, ret);

	kfree(filename);

	return ret;
}

After I insmod, I execute "ls" or any other commands. ERROR as following:
image
The debug info is as follow:
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants