├── Makefile ├── hash.c ├── hash.h ├── hide.c ├── hide.h ├── main.c ├── sysfs.c └── sysfs.h /Makefile: -------------------------------------------------------------------------------- 1 | obj-m += camb.o 2 | camb-objs += main.o sysfs.o hash.o 3 | 4 | # We need headers to build against a specific kernel version 5 | ifndef KDIR 6 | KDIR = /lib/modules/$(shell uname -r)/build 7 | # @echo "Using default kernel directory: ${KDIR}" 8 | endif 9 | 10 | # If user specifies a System.map, get addresses from there 11 | ifdef SMAP 12 | OPTS += -DTEXT_SEGMENT_START="0x$(shell grep '\s\+T\s\+_stext\b' ${SMAP} | cut -f1 -d' ')" 13 | OPTS += -DTEXT_SEGMENT_END="0x$(shell grep '\s\+T\s\+_etext\b' ${SMAP} | cut -f1 -d' ')" 14 | OPTS += -DSYSCALL_BASE_ADDR="0x$(shell grep '\s\+R\s\+sys_call_table\b' ${SMAP} | cut -f1 -d' ')" 15 | 16 | # Otherwise, they must be present on the build line 17 | else 18 | OPTS += -DTEXT_SEGMENT_START="${TEXT_SEGMENT_START}" 19 | OPTS += -DTEXT_SEGMENT_END="${TEXT_SEGMENT_END}" 20 | OPTS += -DSYSCALL_BASE_ADDR="${SYSCALL_BASE_ADDR}" 21 | endif 22 | 23 | ifdef HIDE_ME 24 | OPTS += -DHIDE_ME 25 | camb-objs += hide.o 26 | endif 27 | 28 | all: 29 | 30 | ifndef SMAP 31 | ifndef TEXT_SEGMENT_START 32 | @echo "Missing parameter: TEXT_SEGMENT_START" 33 | @exit 1 34 | endif 35 | 36 | ifndef TEXT_SEGMENT_END 37 | @echo "Missing parameter: TEXT_SEGMENT_END" 38 | @exit 1 39 | endif 40 | 41 | ifndef SYSCALL_BASE_ADDR 42 | @echo "Missing parameter: SYSCALL_BASE_ADDR" 43 | @exit 1 44 | endif 45 | endif 46 | 47 | $(MAKE) -C $(KDIR) M=$(shell pwd) EXTRA_CFLAGS="${OPTS}" modules 48 | -------------------------------------------------------------------------------- /hash.c: -------------------------------------------------------------------------------- 1 | // Copyright 2004-present Facebook. All Rights Reserved. 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | /* Crypto */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "hash.h" 14 | 15 | unsigned char *kernel_text_hash(void) { 16 | return (unsigned char *) hash_data((void *) TEXT_SEGMENT_START, 17 | TEXT_SEGMENT_END - TEXT_SEGMENT_START); 18 | } 19 | 20 | /** 21 | * @brief Generic function for performing a SHA-1 hash of a memory range 22 | * 23 | * @param data - Beginning memory address to perform hash 24 | * @param len - size in bytes of the address range to hash 25 | * 26 | * @return allocated buffer containing the hash string; or NULL upon error. 27 | */ 28 | unsigned char *hash_data(const void *data, size_t len) { 29 | struct scatterlist sg; 30 | struct hash_desc desc; 31 | size_t out_len = SHA1_DIGEST_SIZE * 2 + 1; 32 | unsigned char hashtext[SHA1_DIGEST_SIZE]; 33 | unsigned char *hashtext_out = kmalloc(out_len, GFP_KERNEL); 34 | 35 | if (!hashtext_out) { 36 | printk(KERN_INFO "Could not allocate space for hash\n"); 37 | return NULL; 38 | } 39 | 40 | sg_init_one(&sg, data, len); 41 | desc.flags = 0; 42 | desc.tfm = crypto_alloc_hash("sha1", 0, CRYPTO_ALG_ASYNC); 43 | 44 | crypto_hash_init(&desc); 45 | crypto_hash_update(&desc, &sg, sg.length); 46 | crypto_hash_final(&desc, hashtext); 47 | 48 | snprintf(hashtext_out, 49 | out_len, 50 | "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" 51 | "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", 52 | hashtext[0], hashtext[1], hashtext[2], hashtext[3], 53 | hashtext[4], hashtext[5], hashtext[6], hashtext[7], 54 | hashtext[8], hashtext[9], hashtext[10], hashtext[11], 55 | hashtext[12], hashtext[13], hashtext[14], hashtext[15], 56 | hashtext[16], hashtext[17], hashtext[18], hashtext[19] 57 | ); 58 | 59 | if (desc.tfm) { 60 | crypto_free_hash(desc.tfm); 61 | } 62 | 63 | return hashtext_out; 64 | } 65 | 66 | /** 67 | * @brief Callback for the sysfs object read. This happens when a file is 68 | * read(2) (or equivalent) from within sysfs. E.g. cat /sys/foo/bar will 69 | * call bar's *_show callback method. 70 | * 71 | * @param obj - reference to a kernel object within the sysfs filesystem 72 | * @param attr - attribute of said kernel object 73 | * @param buf - buffer that will be allocated and filled with the hash 74 | * 75 | * @return size in bytes of the hash string; or -1 upon error. 76 | */ 77 | ssize_t text_segment_hash_show(struct kobject *obj, 78 | struct attribute *attr, 79 | char *buf) { 80 | ssize_t ret; 81 | char *hash = kernel_text_hash(); 82 | 83 | if (hash) { 84 | ret = scnprintf(buf, PAGE_SIZE, "%s\n", hash); 85 | kfree(hash); 86 | } else { 87 | ret = -1; 88 | } 89 | 90 | return ret; 91 | } 92 | -------------------------------------------------------------------------------- /hash.h: -------------------------------------------------------------------------------- 1 | // Copyright 2004-present Facebook. All Rights Reserved. 2 | 3 | unsigned char *kernel_text_hash(void); 4 | unsigned char *hash_data(const void *, size_t); 5 | -------------------------------------------------------------------------------- /hide.c: -------------------------------------------------------------------------------- 1 | // Copyright 2004-present Facebook. All Rights Reserved. 2 | 3 | #include 4 | 5 | #include "hide.h" 6 | 7 | extern char *module_str; 8 | 9 | void rm_mod_from_list(void) { 10 | THIS_MODULE->list.next->prev = THIS_MODULE->list.prev; 11 | THIS_MODULE->list.prev->next = THIS_MODULE->list.next; 12 | } 13 | 14 | void rm_mod_from_sysfs(void) { 15 | kobject_del(THIS_MODULE->holders_dir->parent); 16 | } 17 | 18 | void rm_mod_from_ddebug_tables(void) { 19 | ddebug_remove_module(module_str); 20 | } 21 | 22 | void hide_me(void) { 23 | rm_mod_from_list(); 24 | rm_mod_from_sysfs(); 25 | rm_mod_from_ddebug_tables(); 26 | } 27 | -------------------------------------------------------------------------------- /hide.h: -------------------------------------------------------------------------------- 1 | // Copyright 2004-present Facebook. All Rights Reserved. 2 | 3 | void rm_mod_from_list(void); 4 | void rm_mod_from_sysfs(void); 5 | void rm_mod_from_ddebug_tables(void); 6 | void hide_me(void); 7 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | // Copyright 2004-present Facebook. All Rights Reserved. 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "sysfs.h" 23 | #include "hash.h" 24 | #ifdef HIDE_ME 25 | #include "hide.h" 26 | #endif 27 | 28 | extern struct kobject *camb_kobj; 29 | char *module_str = "camb"; 30 | 31 | static unsigned long **syscall_table = (unsigned long **) SYSCALL_BASE_ADDR; 32 | static unsigned long *syscall_table_copy[NR_syscalls]; 33 | 34 | /* Allow writes to executable memory pages */ 35 | void en_mem_wr(void) { 36 | write_cr0(read_cr0() & (~0x10000)); 37 | } 38 | 39 | /* Disallow writes to executable memory pages */ 40 | void dis_mem_wr(void) { 41 | write_cr0(read_cr0() | 0x10000); 42 | } 43 | 44 | int syscall_addr_modified_show(struct kobject *obj, 45 | struct attribute *attr, 46 | char *buf) { 47 | unsigned int i = -1, mod = 0, ret; 48 | 49 | while(++i < NR_syscalls) 50 | if (syscall_table[i] != syscall_table_copy[i]) 51 | mod = 1; 52 | ret = scnprintf(buf, PAGE_SIZE, "%d\n", mod); 53 | 54 | return ret; 55 | } 56 | 57 | /* Copy the system call pointer table */ 58 | void grab_syscall_table(void) { 59 | unsigned int i; 60 | for (i = 0; i < NR_syscalls; i++) 61 | syscall_table_copy[i] = syscall_table[i]; 62 | } 63 | 64 | static int __init camb_init(void) { 65 | printk(KERN_INFO "[%s] init\n", module_str); 66 | 67 | if (expose_sysfs()) { 68 | printk(KERN_ERR "Cannot expose self to sysfs\n"); 69 | return -1; 70 | } 71 | 72 | /* Hide the fact that we're monitoring the system for tampering */ 73 | #ifdef HIDE_ME 74 | hide_me(); 75 | #endif 76 | 77 | grab_syscall_table(); 78 | 79 | return 0; 80 | } 81 | 82 | static void __exit camb_exit(void) { 83 | printk(KERN_INFO "[%s] exit\n", module_str); 84 | 85 | if (camb_kobj) { 86 | kobject_put(camb_kobj); 87 | } 88 | 89 | } 90 | 91 | module_init(camb_init); 92 | module_exit(camb_exit); 93 | 94 | MODULE_LICENSE("GPL"); 95 | MODULE_AUTHOR("@unixist"); 96 | MODULE_DESCRIPTION("Detect kernel tampering"); 97 | -------------------------------------------------------------------------------- /sysfs.c: -------------------------------------------------------------------------------- 1 | // Copyright 2004-present Facebook. All Rights Reserved. 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "hash.h" 9 | #include "sysfs.h" 10 | 11 | struct kobject *camb_kobj; 12 | 13 | extern ssize_t syscall_addr_modified_show(struct kobject *obj, 14 | struct attribute *attr, 15 | char *buf); 16 | extern ssize_t text_segment_hash_show(struct kobject *obj, 17 | struct attribute *attr, 18 | char *buf); 19 | 20 | struct kobj_attribute attr_syscall_addr_modified = 21 | __ATTR(syscall_addr_modified, 0444, syscall_addr_modified_show, NULL); 22 | 23 | struct kobj_attribute attr_text_segment_hash = 24 | __ATTR(text_segment_hash, 0444, text_segment_hash_show, NULL); 25 | 26 | struct attribute *camb_attrs[] = { 27 | &attr_text_segment_hash.attr, 28 | &attr_syscall_addr_modified.attr, 29 | NULL, 30 | }; 31 | 32 | struct attribute_group attr_group = { 33 | .attrs = camb_attrs 34 | }; 35 | 36 | int expose_sysfs(void) { 37 | int err = 0; 38 | camb_kobj = kobject_create_and_add("camb", kernel_kobj); 39 | if (camb_kobj) { 40 | if ((err = sysfs_create_group(camb_kobj, &attr_group)) != 0) { 41 | kobject_put(camb_kobj); 42 | } 43 | } 44 | return err; 45 | } 46 | 47 | MODULE_LICENSE("GPL"); 48 | MODULE_AUTHOR("@unixist"); 49 | MODULE_DESCRIPTION("Detect kernel tampering"); 50 | -------------------------------------------------------------------------------- /sysfs.h: -------------------------------------------------------------------------------- 1 | // Copyright 2004-present Facebook. All Rights Reserved. 2 | 3 | int expose_sysfs(void); 4 | --------------------------------------------------------------------------------