alexandernst / monks Goto Github PK
View Code? Open in Web Editor NEWProcmon alternative for Linux
Procmon alternative for Linux
I'm trying to remove the (UN)HOOK macros and instead hook/unhook all the syscalls iterating over a section with the relevant data.
I made a patch that should work, but it's failing for some strange reason.
This is the patch:
diff --git a/procmon_kmodule/hookfns.c b/procmon_kmodule/hookfns.c
index 2984afd..5faec86 100644
--- a/procmon_kmodule/hookfns.c
+++ b/procmon_kmodule/hookfns.c
@@ -10,13 +10,25 @@
asm(".section .counters, \"aw\""); //set section allocatable and writable
void hook_calls(void){
+
+ counter_info_t *iter;
+
if(get_sct() && set_sct_rw()){
-/* __NR_read / __NR32_read */
- HOOK(read);
-#ifdef __NR32_read
- HOOK_IA32(read);
-#endif
+ iter = __start_counters;
+ for(; iter < __stop_counters; ++iter){
+ DEBUG(KERN_INFO "ITER DIR %p\n", iter);
+ if(iter->is32){
+ DEBUG(KERN_INFO "HOOK_IA32 %s\n", iter->name);
+ iter->rf = (void *)ia32_sys_call_table[iter->__NR_];
+ ia32_sys_call_table[iter->__NR_] = (void *)iter->ff;
+ }else{
+ DEBUG(KERN_INFO "HOOK %s\n", iter->name);
+ iter->rf = (void *)sys_call_table[iter->__NR_];
+ sys_call_table[iter->__NR_] = (void *)iter->ff;
+
+ }
+ }
set_sct_ro();
}
@@ -33,13 +45,21 @@ void hook_calls(void){
\*****************************************************************************/
void unhook_calls(void){
+
+ counter_info_t *iter;
+
if(get_sct() && set_sct_rw()){
-/* __NR_read / __NR_read32 */
- UNHOOK(read);
-#ifdef __NR32_read
- UNHOOK_IA32(read);
-#endif
+ iter = __start_counters;
+ for(; iter < __stop_counters; ++iter){
+ if(iter->is32){
+ DEBUG(KERN_INFO "UNHOOK_IA32 %s\n", iter->name);
+ ia32_sys_call_table[iter->__NR_] = (void *)iter->rf;
+ }else{
+ DEBUG(KERN_INFO "UNHOOK %s\n", iter->name);
+ sys_call_table[iter->__NR_] = (void *)iter->rf;
+ }
+ }
set_sct_ro();
}
diff --git a/procmon_kmodule/hookfns.h b/procmon_kmodule/hookfns.h
index 79c4ecb..d61dadc 100644
--- a/procmon_kmodule/hookfns.h
+++ b/procmon_kmodule/hookfns.h
@@ -6,6 +6,10 @@
typedef struct counter_info {
atomic_t counter;
char *name;
+ int is32;
+ int __NR_;
+ void *ff;
+ void *rf;
} __attribute__((packed)) counter_info_t;
extern counter_info_t __start_counters[];
@@ -19,20 +23,14 @@ extern counter_info_t __stop_counters[];
| hooked_sys_##F = FAKE FUNCTION as in the function which we'll be using to fake __NR_##F |
\*****************************************************************************************/
-#define HOOK(F) \
- DEBUG(KERN_INFO "HOOK __NR_" #F "\n"); \
- real_sys_##F = (void *)sys_call_table[__NR_##F]; \
- sys_call_table[__NR_##F] = (void *)hooked_sys_##F;
-
-#define UNHOOK(F) \
- DEBUG(KERN_INFO "UNHOOK __NR_" #F "\n"); \
- sys_call_table[__NR_##F] = (void *)real_sys_##F;
-
#define REGISTER_SYSCALL(F) \
static counter_info_t __counter_info___NR_##F \
- __attribute((unused, section(".counters"))) = { \
+ __attribute((section(".counters"))) = { \
.counter = ATOMIC_INIT(0), \
.name = "__NR_" #F, \
+ .is32 = 0, \
+ .__NR_ = __NR_##F, \
+ .ff = hooked_sys_##F, \
};
#define __INCR(F) \
@@ -43,20 +41,14 @@ extern counter_info_t __stop_counters[];
#ifdef CONFIG_IA32_EMULATION
-#define HOOK_IA32(F) \
- DEBUG(KERN_INFO "HOOK_IA32 __NR32_" #F "\n"); \
- real_sys32_##F = (void *)ia32_sys_call_table[__NR32_##F]; \
- ia32_sys_call_table[__NR32_##F] = (void *)hooked_sys32_##F;
-
-#define UNHOOK_IA32(F) \
- DEBUG(KERN_INFO "UNHOOK_IA32 __NR32_" #F "\n"); \
- ia32_sys_call_table[__NR32_##F] = (void *)real_sys32_##F;
-
#define REGISTER_SYSCALL32(F) \
static counter_info_t __counter_info___NR32_##F \
- __attribute((unused, section(".counters"))) = { \
+ __attribute((section(".counters"))) = { \
.counter = ATOMIC_INIT(0), \
- .name = "__NR32_" #F "_32", \
+ .name = "__NR32_" #F, \
+ .is32 = 1, \
+ .__NR_ = __NR32_##F, \
+ .ff = hooked_sys32_##F, \
};
#define __INCR32(F) \
It seems that the loop iterates 3 times but there are only 2 structs saved in the section. I have noticed that if I remove the __NR_
element from the struct, the loop iterates only 2 times.
@milabs Any ideas?
Filtering data in the module instead of in the client will be faster (as no data will be sent anywhere, copied, pushed, etc...).
Probably the best way is to create some kind of macro and a sysctl
interface for each hijacked syscall. Example: sysctl procmon.read=1
or sysctl procmon.write32=0
Currently the real syscall is ran and then the fake syscall is ran. In the time between the real and the fake syscalls execution, data can get invalidated. A good example of that is the __NR_read / __NR_write
interceptors and the __NR_close
interceptor.
The first two can get the path of the fd
, while the third one can't because the fd
doesn't exist anymore by the time the fake syscall is called.
@milabs Any idea how to trick that? It seems hard :/
Right now net_init()
sends an empty message to the kernel module so the module can know to which PID it should send messages. Replace this with something like the autonegotiation port (sysctl).
This will make the code a little bit cleaner.
Kernel has about 32 available netlink slots. Some of them may be used by another modules. I'll suggest to iterate all the slots from the last to the first and try to register netlink socket for each number. First number that passed can be exported by proc
or sysctl
for our application. What do you think?
[673032.890669] [monks] ================
[673032.890672] [monks] Starting monks
[673032.890921] [monks] Acquired NETLINK socket (29)
[673032.890929] [monks] Found syscall_table addr at 0xffffffff81a001c0
[673032.890944] [monks] syscall_table is NULL, quitting...
[673032.890947] sysctl table check failed: monks//syscalls No proc_handler
[673032.890952] sysctl table check failed: monks//syscalls bogus .mode 0555
[673032.890956] CPU: 4 PID: 95734 Comm: insmod Tainted: G OEL 4.4.0-70-generic #91-Ubuntu
[673032.890958] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 07/02/2015
[673032.890961] 0000000000000286 00000000205d0186 ffff880151c2fba0 ffffffff813f82b3
[673032.890963] ffffffff81e899a0 ffff880100cc9000 ffff880151c2fc00 ffffffff81288bea
[673032.890965] 0000000000000000 ffffffff811ed213 ffffffffc07e6180 0000000000000000
[673032.890967] Call Trace:
[673032.890983] [] dump_stack+0x63/0x90
[673032.890993] [] __register_sysctl_table+0x29a/0x570
[673032.891005] [] ? kmem_cache_alloc_trace+0x183/0x1f0
[673032.891008] [] __register_sysctl_paths+0x1cd/0x1f0
[673032.891010] [] ? 0xffffffffc0760000
[673032.891012] [] register_sysctl_table+0x1f/0x30
[673032.891017] [] register_monks_controls+0x53/0x70 [monks]
[673032.891021] [] hook_init+0x71/0x1000 [monks]
[673032.891028] [] do_one_initcall+0xb3/0x200
[673032.891031] [] ? kmem_cache_alloc_trace+0x183/0x1f0
[673032.891037] [] do_init_module+0x5f/0x1cf
[673032.891043] [] load_module+0x166f/0x1c10
[673032.891046] [] ? __symbol_put+0x60/0x60
[673032.891051] [] ? kernel_read+0x50/0x80
[673032.891054] [] SYSC_finit_module+0xb4/0xe0
[673032.891056] [] SyS_finit_module+0xe/0x10
[673032.891066] [] entry_SYSCALL_64_fastpath+0x16/0x71
[673032.891068] [monks] Error creating fs control interface in /proc/sys/monks/
I just spent some time trying to figure out how to also hook the write system call. I did not manage to do it. Do you plan to provide some generic functions/macros to hook any kind of system call? I could keep trying implementing this, but maybe you are already up to it..
Sometimes, when unloading the module with rmmod, procmon will crash the console from which it was activated
EDIT: and maybe other apps too.
Completed 9a166ae
Give support for as old kernels as possible.
Create wiki with details about:
Add support for filtering all messages based on name, path, pid, etc...
Main features are:
It's impossible to hook several system calls by just replacing correspinding sys_call_table
values. So, we need to blacklist that numbers which can be found by the command:
cat /proc/kallsyms | grep -e 'T stub'
My x86_64
system shows the result:
0000000000000000 T stub_clone
0000000000000000 T stub_fork
0000000000000000 T stub_vfork
0000000000000000 T stub_sigaltstack
0000000000000000 T stub_iopl
0000000000000000 T stub_execve
0000000000000000 T stub_rt_sigreturn
0000000000000000 T stub32_rt_sigreturn
0000000000000000 T stub32_sigreturn
0000000000000000 T stub32_sigaltstack
0000000000000000 T stub32_execve
0000000000000000 T stub32_fork
0000000000000000 T stub32_clone
0000000000000000 T stub32_vfork
0000000000000000 T stub32_iopl
x86_32
?Create an UI for Procmon, probably in Qt5, with a view and a model.
NETLINK is the one efficient way to do the kernel-user communication while sending event information from the kernel. Don't use dmesg
for that purposes as it slows system's performance at high load.
Repeated calls on hook and unhook cause a kernel (at all levels) freeze. Machine gets completely frozen and the only way to gain access back to it is to restart physically the machine.
Grep for __NR_syscall_max
It's more useful to use sysctl
as a control interface.
By suggestions in #19, think about if Procmon should have the possibility to support multiple client/viewers.
Procmon should check if the syscall table is already patched by something else (or even by a remaining stub from procmon itself, as of #31). If the syscall table is patched, procmon should do nothing.
All over the code there are things like ia32
(referring to 32 emulation), 32
(referring to x86) and <nothing>
(referring to x86 or x64). I'd really like to clear all this mess before v0.1
fca2039 introduced some help code to load/unload/start/stop procmon's kernel module from the viewer itself. That code uses kmod
(or libkmod
).
That set of libraries/tools weren't present until very recent distro versions. In Ubuntu's case, that's 13.04.
Procmon's viewer should be patched in a way so that it's able to compile it even if kmod/libkmod
is not availabe. If those aren't available, some menu options should be disabled.
When a hijacked syscall is called, it should store the PID of the process which called it, and when it's done, it should remove that PID.
This way we'll be able to tell which process(es) is(are) blocking procmon's unloading process.
EDIT: Depends on #31
Procmon should be abe to log all the data to a file. Maybe something like:
-L
- Log everything to a file.
-l
- Log just what's visible after applying all the filters to a file.
Check each sys_call before hijack-ing it, maybe something else hijack-ed it, in which case bad things will happen)
Moving up/down in the list of hijacked syscalls is kind of buggy. It won't re-render, it won't go up/down and then it will skip a few lines at once, etc...
I'd like to move away from the entire NetLink madness to mmap. I think procmon will gain in both speed and stability.
Anyways, I have been looking at the link you gave me ( http://people.ee.ethz.ch/~arkeller/linux/kernel_user_space_howto.html#ss8.1 ) here http://stackoverflow.com/questions/19233717/sharing-or-sending-data-from-lkm-to-userland and it seems as I'll have to face two big problems.
The first one is how to notify userland that there is more data availabe. I kept reading the link and I found I could use http://people.ee.ethz.ch/~arkeller/linux/kernel_user_space_howto.html#ss6.1 to notify userland when it should read more data.
But then, here it goes the second big problem: mmap is just writing to a buffer, like a file. Right now I'm saving and sending each message (containing name of program, pid, operation, details, etc... the syscall_info
struct) like a piece of data, encoded and decoded with the de/serialize.c
files. Each message looks like:
(size_t msg_size)(char msg_data)
So it's really easy to just read the msg_size
from there, and then read that size and cast it to a syscall_info
struct.
The problem is that I won't be able to do the same thing when I'm doing mmap, as everything will be written continuosly.
Or will I?
Anyways, I'm open to any suggestions about this @milabs :)
https://github.com/alexandernst/monks/blob/master/monks_kmodule/sct_hook.c#L207 is wrong. We do not restore real functions, we modify stubs!
for(; iter < __stop_syscalls; ++iter){
procmon_info("Unloading syscall %s\n", iter->name);
if(iter->counter && atomic_read(iter->counter) > 0){
return 0;
}else if(iter->counter && atomic_read(iter->counter) == 0){
del(iter->counter);
}
}
running it some time (one hour or more);
then rmmod it, You will found the syslog Increases indefinitely.
We're currently saving all the data in a list formed by a prev/next pointer and a pointer to the data itself. We should keep the total number of nodes from that list below or equal to N.
The way Procmon works makes it possible to use it as a rootkit detector. It shouldn't be hard to implement it, so why not?
Some discussion about this occurred in #37
Merge udis86 into the mainline to use it for sys_call_table
searching etc.
Procmon-viewer crashes randomly while resizing.
The current hook method will block the module's unloading till the last sleeping process issues a call to all the syscalls it used at some point of it's execution. Using stubs should fix the situation, as per comments in #17 .
Microsoft has a similar tool with a similar name for Windows. Before the final release, you might want to find another name, otherwise MS will probably force you to. :)
Clearly indicate in the README that this module, while it could be used to actually do something productive, it's an academic project.
Check if the number of intercepted and sent syscalls from the module is equal to the number of messages that the viewer received.
I think the viewer is missing something.
First of all: Thanks a lot. Pretty much what I just needed.
The Readme should be changed to:
echo 1 > /proc/procmon
echo 0 > /proc/procmon
for starting and stopping
All the functions inside the serializer and the deserializer expect (k)malloc/(k)realloc to always return a new valid mem direction, but they could also return NULL
. This should be handled.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.