source: trunk/src/samhain_hide.c@ 52

Last change on this file since 52 was 51, checked in by rainer, 18 years ago

Fix problems with linux kernel 2.6.17

File size: 17.8 KB
Line 
1/***************************************************************************
2 *
3 * Purpose:
4 * -------
5 * (1) Hide files with the string MAGIC_HIDE in filename,
6 * where MAGIC_HIDE is defined below.
7 * By default, MAGIC_HIDE is defined as "samhain".
8 *
9 * (2) Hide all processes, if the executable has the string MAGIC_HIDE
10 * in its name.
11 *
12 *
13 * Configuration:
14 * -------------
15 * If not building within the samhain system, you may remove the
16 * line '#include "config.h"' and in the line
17 * '#define MAGIC_HIDE SH_MAGIC_HIDE', replace SH_MAGIC_HIDE with
18 * "someString" (in quotes !).
19 */
20
21/* #define _(string) string */
22#include "config.h"
23
24#undef _
25#define _(string) string
26
27/* define if this is a 2.6 kernel */
28/* #define LINUX26 */
29
30#define MAGIC_HIDE SH_MAGIC_HIDE
31
32/* #define MAGIC_HIDE "someString" */
33
34/* define this if you have a modversioned kernel */
35/* #define MODVERSIONS */
36
37/* the address of the sys_call_table (not exported in 2.5 kernels) */
38#define MAGIC_ADDRESS SH_SYSCALLTABLE
39
40/*
41 * Install:
42 * -------
43 * gcc -Wall -O2 -c samhain_hide.c
44 * mv samhain_hide.o /lib/modules/KERNEL_VERSION/misc/
45 *
46 * (Replace KERNEL_VERSION with your kernel's version.)
47 *
48 * Usage:
49 * -----
50 * To load the module:
51 * insmod samhain_hide (for improved safety: 'sync && insmod samhain_hide')
52 *
53 * To unload the module
54 * rmmod samhain_hide (for improved safety: 'sync && rmmod samhain_hide')
55 *
56 *
57 * Details:
58 * -------
59 * The following kernel syscalls are replaced:
60 * sys_getdents [hide files/directories/processes (/proc/PID)]
61 *
62 * Tested on:
63 * ---------
64 * Linux 2.2, 2.4, 2.6
65 *
66 * Copyright:
67 * ---------
68 * Copyright (C) 2001, 2002 Rainer Wichmann (http://la-samhna.de)
69 *
70 * License:
71 * -------
72 * This program is free software; you can redistribute it and/or modify
73 * it under the terms of the GNU General Public License, version 2, as
74 * published by the Free Software Foundation.
75 *
76 * This program is distributed in the hope that it will be useful,
77 * but WITHOUT ANY WARRANTY; without even the implied warranty of
78 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
79 * GNU General Public License for more details.
80 *
81 * You should have received a copy of the GNU General Public License
82 * along with this program; if not, write to the Free Software
83 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
84 *
85 ***************************************************************************/
86
87
88
89/*****************************************************
90 *
91 * The defines:
92 *
93 *****************************************************/
94
95/* This is a Linux Loadable Kernel Module.
96 */
97
98#ifndef LINUX26
99#define __KERNEL__
100#define MODULE
101#endif
102#define LINUX
103
104/* Define for debugging.
105 */
106/* #define HIDE_DEBUG */ /* query_module */
107/* #define FILE_DEBUG */ /* getdents */
108/* #define READ_DEBUG */ /* read */
109/* #define PROC_DEBUG */ /* procfs */
110
111
112/*****************************************************
113 *
114 * The include files:
115 *
116 *****************************************************/
117
118
119/* The configure options (#defines) for the Kernel
120 */
121#include <linux/config.h>
122
123#ifndef LINUX26
124#ifdef CONFIG_MODVERSIONS
125#include <linux/modversions.h>
126#endif
127#endif
128
129
130#ifdef LINUX26
131#include <linux/init.h>
132#endif
133
134#include <linux/module.h>
135
136/* File tables structures. If directory caching is used,
137 * <linux/dcache.h> will be included here, and __LINUX_DCACHE_H
138 * will thus be defined.
139 */
140#include <linux/fs.h>
141#include <linux/proc_fs.h>
142
143/* Include the SYS_syscall defines.
144 */
145#ifndef LINUX26
146#include <sys/syscall.h>
147#else
148#define SYS_getdents 141
149#define SYS_getdents64 220
150#endif
151
152
153/* Includes for 'getdents' per the manpage.
154 */
155#include <linux/types.h>
156#include <linux/dirent.h>
157#include <linux/unistd.h>
158
159/* To access userspace memory.
160 */
161#include <asm/uaccess.h>
162
163/* Include for lock_kernel().
164 */
165#include <linux/smp_lock.h>
166
167/* Include for fget().
168 */
169#include <linux/file.h>
170
171/*****************************************************
172 *
173 * The global variables:
174 *
175 *****************************************************/
176
177/* The kernel syscall table. Not exported anymore in 2.5 ff., and also
178 * not in the RedHat 2.4 kernel.
179 */
180
181#if 0
182extern void * sys_call_table[];
183#define sh_sys_call_table sys_call_table
184#endif
185
186unsigned long * sh_sys_call_table = (unsigned long *) MAGIC_ADDRESS;
187
188/* The old address of the sys_getdents syscall.
189 */
190int (*old_getdents)(unsigned int, struct dirent *, unsigned int);
191#ifdef __NR_getdents64
192long (*old_getdents64)(unsigned int, struct dirent64 *, unsigned int);
193#endif
194
195char hidden[] = MAGIC_HIDE;
196
197
198/*****************************************************
199 *
200 * The functions:
201 *
202 *****************************************************/
203
204
205MODULE_AUTHOR("Rainer Wichmann");
206MODULE_DESCRIPTION("Hide files/processes/modules with MAGIC_HIDE in name.");
207#if defined(MODULE_LICENSE) || defined(LINUX26)
208MODULE_LICENSE("GPL");
209#endif
210
211#ifdef LINUX26
212/* Default is to hide ourselves.
213 */
214static int removeme = 1;
215
216#ifdef MODULE_PARM
217MODULE_PARM (removeme, "i");
218#else
219module_param(removeme, int, 0444);
220#endif
221
222#ifdef MODULE_PARM_DESC
223MODULE_PARM_DESC(removeme, "Choose zero for not hiding.");
224#endif
225
226/* LINUX26 */
227#endif
228
229
230/*
231 * struct task_struct is defined in linux/sched.h
232 *
233 * as of 2.4.20, the vanilla kernel holds (among others):
234 * struct task_struct *next_task, *prev_task;
235 *
236 * Redhat kernel seems to have a different scheduler.
237 * use:
238 * struct task_struct * find_task_by_pid (int pid);
239 */
240
241#if defined(SH_VANILLA_KERNEL) && !defined(LINUX26)
242/*
243 * Fetch the task struct for a given PID.
244 */
245struct task_struct * fetch_task_struct (int pid)
246{
247 struct task_struct * task_ptr;
248
249#ifdef PROC_DEBUG
250 printk("FETCH TASK %d\n", pid);
251#endif
252
253 task_ptr = current;
254
255 do
256 {
257 if (task_ptr->pid == (pid_t) pid )
258 return (task_ptr);
259 task_ptr = task_ptr->next_task;
260 }
261 while (task_ptr != current);
262
263#ifdef PROC_DEBUG
264 printk("FETCH TASK: NOT FOUND !!!\n");
265#endif
266
267 return (NULL);
268}
269
270#else
271/*
272 * RedHat 2.4.20 kernel
273 */
274struct task_struct * fetch_task_struct (int pid)
275{
276 struct task_struct * task_ptr = NULL;
277 task_ptr = find_task_by_pid (pid);
278 return (task_ptr);
279}
280#endif
281
282/* Convert a string to an int.
283 * Does not recognize integers with a sign (+/-) in front.
284 */
285int my_atoi(char * in_str)
286{
287 int i = 0;
288 int retval = 0;
289 int conv = 0;
290
291 if (in_str == NULL)
292 return (-1);
293
294 while(in_str[i] != '\0')
295 {
296 /* Break if not numeric.
297 */
298 if (in_str[i] < '0' || in_str[i] > '9')
299 break;
300
301 ++conv;
302
303 /* Leading zeroes (should not happen in /proc)
304 */
305 if (retval == 0 && in_str[i] == '0')
306 retval = retval;
307 else
308 retval = retval * 10;
309
310 retval = retval + (in_str[i] - '0');
311
312 i++;
313 }
314
315 if (conv == 0)
316 return (-1);
317 else
318 return (retval);
319}
320
321/* Purpose:
322 *
323 * Hide all files/dirs that include the string MAGIC_HIDE in their
324 * name.
325 */
326int new_getdents (unsigned int fd, struct dirent *dirp, unsigned int count)
327{
328 int status = 0; /* Return value from original getdents */
329 struct inode * dir_inode;
330 struct file * fd_file;
331 int dir_is_proc = 0;
332
333 struct dirent * dirp_prev;
334 struct dirent * dirp_new;
335 struct dirent * dirp_current;
336
337 int dir_table_bytes;
338 int forward_bytes;
339 struct task_struct * task_ptr;
340 int hide_it = 0;
341 long dirp_offset;
342
343 lock_kernel();
344
345 status = (*old_getdents)(fd, dirp, count);
346
347#ifdef FILE_DEBUG
348 printk("STATUS %d\n", status);
349#endif
350
351 /* 0: end of directory.
352 * -1: some error
353 */
354 if (status <= 0)
355 {
356 unlock_kernel();
357 return (status);
358 }
359
360 /* Handle directory caching. dir_inode is the inode of the directory.
361 */
362#if defined(files_fdtable)
363 {
364 struct fdtable *fdt = files_fdtable(current->files);
365 fd_file = rcu_dereference(fdt->fd[fd]);
366 }
367#else
368 {
369 fd_file = current->files->fd[fd];
370 }
371#endif
372
373#if defined(__LINUX_DCACHE_H)
374 dir_inode = fd_file->f_dentry->d_inode;
375#else
376 dir_inode = fd_file->f_inode;
377#endif
378
379 /* Check for the /proc directory
380 */
381 if (dir_inode->i_ino == PROC_ROOT_INO
382#ifndef LINUX26
383 && !MAJOR(dir_inode->i_dev) &&
384 MINOR(dir_inode->i_dev) == 1
385#endif
386 )
387 dir_is_proc = 1;
388
389 /* Allocate space for new dirent table. Can't use GFP_KERNEL
390 * (kernel oops)
391 */
392 dirp_new = (struct dirent *) kmalloc (status, GFP_ATOMIC);
393
394 if (dirp_new == NULL)
395 {
396 unlock_kernel();
397 return (status);
398 }
399
400 /* Copy the dirp table to kernel space.
401 */
402 copy_from_user(dirp_new, dirp, status);
403
404#ifdef FILE_DEBUG
405 printk("COPY to kernel\n");
406#endif
407
408 /* Loop over the dirp table to find entries to hide.
409 */
410 dir_table_bytes = status;
411 dirp_current = dirp_new;
412 dirp_prev = NULL;
413
414 while (dir_table_bytes > 0)
415 {
416 hide_it = 0;
417
418 if (dirp_current->d_reclen == 0)
419 break;
420
421 dirp_offset = dirp_current->d_off;
422
423#ifdef FILE_DEBUG
424 printk("DIRENT %d %d %ld\n",
425 dir_table_bytes,
426 dirp_current->d_reclen,
427 dirp_current->d_off);
428#endif
429
430 dir_table_bytes -= dirp_current->d_reclen;
431 forward_bytes = dirp_current->d_reclen;
432
433#ifdef FILE_DEBUG
434 printk("ENTRY %s\n", dirp_current->d_name);
435#endif
436
437 /* If /proc is scanned (e.g. by 'ps'), hide the entry for
438 * any process where the executable has MAGIC_HIDE in its name.
439 */
440 if (dir_is_proc == 1)
441 {
442 task_ptr = fetch_task_struct(my_atoi(dirp_current->d_name));
443 if (task_ptr != NULL)
444 {
445 if (strstr(task_ptr->comm, hidden) != NULL)
446 hide_it = 1;
447 }
448 }
449 /* If it is a regular directory, hide any entry with
450 * MAGIC_HIDE in its name.
451 */
452 else
453 {
454 if (strstr (dirp_current->d_name, hidden) != NULL)
455 hide_it = 1;
456 }
457
458 if (hide_it == 1)
459 {
460#ifdef FILE_DEBUG
461 printk(" -->HIDDEN %s\n", dirp_current->d_name);
462#endif
463 if (dir_table_bytes > 0)
464 {
465 status -= dirp_current->d_reclen;
466 memmove (dirp_current,
467 (char *) dirp_current + dirp_current->d_reclen,
468 dir_table_bytes);
469
470 /* Set forward_bytes to 0, because now dirp_current is the
471 * (previously) next entry in the dirp table.
472 */
473 forward_bytes = 0;
474 dirp_prev = dirp_current;
475 }
476 else
477 {
478 status -= dirp_current->d_reclen;
479 if (dirp_prev != NULL)
480 dirp_prev->d_off = dirp_offset;
481 }
482
483 }
484 else
485 {
486 dirp_prev = dirp_current;
487 if (dir_table_bytes == 0 && dirp_prev != NULL)
488 dirp_prev->d_off = dirp_offset;
489 }
490
491 /* Next entry in dirp table.
492 */
493 if (dir_table_bytes > 0)
494 dirp_current = (struct dirent *) ( (char *) dirp_current +
495 forward_bytes);
496 }
497
498 /* Copy our modified dirp table back to user space.
499 */
500 copy_to_user(dirp, dirp_new, status);
501#ifdef FILE_DEBUG
502 printk("COPY to user\n");
503#endif
504
505 kfree (dirp_new);
506#ifdef FILE_DEBUG
507 printk("KFREE\n");
508#endif
509
510 unlock_kernel();
511 return (status);
512}
513
514
515/* For 2.4 kernel
516 */
517#ifdef __NR_getdents64
518long new_getdents64 (unsigned int fd, struct dirent64 *dirp,
519 unsigned int count)
520{
521 long status = 0; /* Return value from original getdents */
522 struct inode * dir_inode;
523 struct file * fd_file;
524 int dir_is_proc = 0;
525
526 struct dirent64 * dirp_prev;
527 struct dirent64 * dirp_new;
528 struct dirent64 * dirp_current;
529
530 int dir_table_bytes;
531 int forward_bytes;
532 struct task_struct * task_ptr;
533 int hide_it = 0;
534 __s64 dirp_offset;
535
536 lock_kernel();
537
538 status = (*old_getdents64)(fd, dirp, count);
539
540#ifdef FILE_DEBUG
541 printk("STATUS64 %ld\n", status);
542#endif
543
544 /* 0: end of directory.
545 * -1: some error
546 */
547 if (status <= 0)
548 {
549 unlock_kernel();
550 return (status);
551 }
552
553 /* Handle directory caching. dir_inode is the inode of the directory.
554 */
555#if defined(files_fdtable)
556 {
557 struct fdtable *fdt = files_fdtable(current->files);
558 fd_file = rcu_dereference(fdt->fd[fd]);
559 }
560#else
561 {
562 fd_file = current->files->fd[fd];
563 }
564#endif
565
566#if defined(__LINUX_DCACHE_H)
567 dir_inode = fd_file->f_dentry->d_inode;
568#else
569 dir_inode = fd_file->f_inode;
570#endif
571
572#ifdef FILE_DEBUG
573 printk("INODE64\n");
574#endif
575
576 /* Check for the /proc directory
577 */
578 if (dir_inode->i_ino == PROC_ROOT_INO
579#ifndef LINUX26
580 && !MAJOR(dir_inode->i_dev) /* &&
581 MINOR(dir_inode->i_dev) == 1 */
582 /* MINOR commented out because of problems with 2.4.17 */
583#endif
584 )
585 {
586 dir_is_proc = 1;
587
588#ifdef PROC_DEBUG
589 printk("PROC_CHECK64\n");
590#endif
591 }
592
593 /* Allocate space for new dirent table. Can't use GFP_KERNEL
594 * (kernel oops)
595 */
596 dirp_new = kmalloc ((size_t)status, GFP_ATOMIC);
597
598#ifdef FILE_DEBUG
599 printk("KMALLOC64_0\n");
600#endif
601
602 if (dirp_new == NULL)
603 {
604 unlock_kernel();
605 return (status);
606 }
607
608#ifdef FILE_DEBUG
609 printk("KMALLOC64\n");
610#endif
611
612 /* Copy the dirp table to kernel space.
613 */
614 copy_from_user(dirp_new, dirp, status);
615
616#ifdef FILE_DEBUG
617 printk("COPY64 to kernel\n");
618#endif
619
620 /* Loop over the dirp table to find entries to hide.
621 */
622 dir_table_bytes = status;
623 dirp_current = dirp_new;
624 dirp_prev = NULL;
625
626 while (dir_table_bytes > 0)
627 {
628 hide_it = 0;
629
630 if (dirp_current->d_reclen == 0)
631 break;
632
633 dirp_offset = dirp_current->d_off;
634
635#ifdef FILE_DEBUG
636 printk("DIRENT %d %d %lld\n",
637 dir_table_bytes,
638 dirp_current->d_reclen,
639 dirp_current->d_off);
640#endif
641
642 dir_table_bytes -= dirp_current->d_reclen;
643 forward_bytes = dirp_current->d_reclen;
644
645#ifdef FILE_DEBUG
646 printk("ENTRY %s\n", dirp_current->d_name);
647#endif
648
649 /* If /proc is scanned (e.g. by 'ps'), hide the entry for
650 * any process where the executable has MAGIC_HIDE in its name.
651 */
652 if (dir_is_proc == 1)
653 {
654#ifdef PROC_DEBUG
655 printk("PROC %s\n", dirp_current->d_name);
656#endif
657 task_ptr = fetch_task_struct(my_atoi(dirp_current->d_name));
658 if (task_ptr != NULL)
659 {
660#ifdef PROC_DEBUG
661 printk("PROC %s <> %s\n", task_ptr->comm, hidden);
662#endif
663 if (strstr(task_ptr->comm, hidden) != NULL)
664 hide_it = 1;
665 }
666 }
667 /* If it is a regular directory, hide any entry with
668 * MAGIC_HIDE in its name.
669 */
670 else
671 {
672 if (strstr (dirp_current->d_name, hidden) != NULL)
673 hide_it = 1;
674 }
675
676 if (hide_it == 1)
677 {
678#ifdef FILE_DEBUG
679 printk(" -->HIDDEN %s\n", dirp_current->d_name);
680#endif
681 if (dir_table_bytes > 0)
682 {
683 status -= dirp_current->d_reclen;
684 memmove (dirp_current,
685 (char *) dirp_current + dirp_current->d_reclen,
686 dir_table_bytes);
687
688 /* Set forward_bytes to 0, because now dirp_current is the
689 * (previously) next entry in the dirp table.
690 */
691 forward_bytes = 0;
692 dirp_prev = dirp_current;
693 }
694 else
695 {
696 status -= dirp_current->d_reclen;
697 if (dirp_prev != NULL)
698 dirp_prev->d_off = dirp_offset;
699 }
700
701 }
702 else
703 {
704 dirp_prev = dirp_current;
705 if (dir_table_bytes == 0 && dirp_prev != NULL)
706 dirp_prev->d_off = dirp_offset;
707 }
708
709 /* Next entry in dirp table.
710 */
711 if (dir_table_bytes > 0)
712 dirp_current = (struct dirent64 *) ( (char *) dirp_current +
713 forward_bytes);
714 }
715
716 /* Copy our modified dirp table back to user space.
717 */
718 copy_to_user(dirp, dirp_new, status);
719 kfree (dirp_new);
720 unlock_kernel();
721 return (status);
722}
723#endif
724
725#ifdef LINUX26
726static struct module *find_module(const char *name)
727{
728 struct module *mod;
729 struct list_head * modules = (struct list_head *) SH_LIST_MODULES;
730
731 list_for_each_entry(mod, modules, list) {
732 if (strcmp(mod->name, name) == 0)
733 return mod;
734 }
735 return NULL;
736}
737#endif
738
739/* The initialisation function. Automatically called when module is inserted
740 * via the 'insmod' command.
741 */
742#ifdef LINUX26
743static int __init samhain_hide_init(void)
744#else
745int init_module(void)
746#endif
747{
748
749 lock_kernel();
750
751 /* Unfortunately this does not fully prevent the module from appearing
752 * in /proc/ksyms.
753 */
754#ifndef LINUX26
755 EXPORT_NO_SYMBOLS;
756#endif
757
758 /* Replace the 'sys_getdents' syscall with the new version.
759 */
760 old_getdents = (void*) sh_sys_call_table[SYS_getdents];
761 sh_sys_call_table[SYS_getdents] = (unsigned long) new_getdents;
762
763#ifdef __NR_getdents64
764 old_getdents64 = (void*) sh_sys_call_table[SYS_getdents64];
765 sh_sys_call_table[SYS_getdents64] = (unsigned long) new_getdents64;
766#endif
767
768#ifdef LINUX26
769 {
770 spinlock_t * modlist_lock = (spinlock_t * ) SH_MODLIST_LOCK;
771 struct module *mod = find_module(SH_INSTALL_NAME"_hide");
772 if (mod) {
773 /* Delete from various lists */
774 spin_lock_irq(modlist_lock);
775 if (removeme == 1)
776 {
777 list_del(&mod->list);
778 }
779 spin_unlock_irq(modlist_lock);
780 }
781 }
782#endif
783
784 unlock_kernel();
785 return (0);
786}
787
788/* The cleanup function. Automatically called when module is removed
789 * via the 'rmmod' command.
790 */
791#ifdef LINUX26
792static void __exit samhain_hide_cleanup(void)
793#else
794void cleanup_module(void)
795#endif
796{
797 lock_kernel();
798
799 /* Restore the new syscalls to the original version.
800 */
801 sh_sys_call_table[SYS_getdents] = (unsigned long) old_getdents;
802#ifdef __NR_getdents64
803 sh_sys_call_table[SYS_getdents64] = (unsigned long) old_getdents64;
804#endif
805
806 unlock_kernel();
807}
808
809#ifdef LINUX26
810module_init(samhain_hide_init);
811module_exit(samhain_hide_cleanup);
812#endif
813
814
Note: See TracBrowser for help on using the repository browser.