内核模块工作在内核空间(supervisor space),而应用程序工作在用户空间(user space)。内核模块是一个由多个回调函数组成的“被动”代码集合体,采用了“事件驱动模型”;而应用程序总是从头至尾的执行单个任务。内核模块不能调用C标准函数库(glibc库),只能调用linux内核导出的内核函数 内核模块在编程时必须考虑可重入性(reentrant),内核模块可使用的栈很小(一般只有4096字节) 。
内核驱动模块的创建与加载
创建动态模块源码 修改Makefile文件生成编译规则 编译创建的模块源码,生成驱动模块 安装驱动模块 查看是否安装成功 使用驱动模块 卸载驱动模块 在编写驱动程序的时候,需要注意的一点是,老版本的内核驱动编译可以允许不写入modinfo数据。高版本内核是需要进行填写的,如果不写,那么将会编译不能通过。
MODULE_AUTHOR(""); 模块作者MODULE_DESCRIPTION(""); 模块描述MODULE_ALIAS(""); 模块别名MODULE_LICENSE(""); 开源协议
模块的操作
模块的编译
V2.4 #gcc −O2 −g −Wall −DMODULE −D KERNEL −c filename.c // filename.c为自己编写的模块程序源代码文件
V2.6 当前目录建立Makefile文件 执行make即可按照Makefile的规定进行编译,形成.ko模块文件
模块的加载 insmod命令 如: insmod filename.ko 或insmod filename.o
模块的查看 lsmod more /proc/modules dmesg ——查看日志(printk)
模块的卸载 rmmod命令 如:rmmod filename
编写简单模块mymodules.c
#include <linux/init.h> /*必须要包含的头文件*/
#include <linux/kernel.h>
#include <linux/module.h> /*必须要包含的头文件*/
static int mymodule_init(void) //模块初始化函数
{
printk("hello,my module wored! \n");
/*输出信息到内核日志*/
return 0;
}
static void mymodule_exit(void) //模块清理函数
{
printk("goodbye,unloading my module.\n");
/*输出信息到内核日志*/
}
module_init(mymodule_init); //注册初始化函数
module_exit(mymodule_exit);
//注册清理函数
MODULE_LICENSE("GPL");
//模块许可声明
makefile文件
ifneq ($(KERNELRELEASE),)
obj-m := mymodules.o
#obj-m指编译成外部模块
else
KERNELDIR := /lib/modules/$(shell uname -r)/build
#定义一个变量,指向内核目录
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
#编译内核模块
endif
编译模块
#make
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hCMCBR5X-1639148863375)(https://xz2k2i3v0u.feishu.cn/space/api/box/stream/download/asynccode/?code=YjY1YTUzMzA1YmU2ZjBjMzNhZmIxNWJjNjVhOTU0OTVfWmdQRHF5c1B6OElhaVFQT3J4SDRJYWJYWUNHQjkzV2ZfVG9rZW46Ym94Y25IS2o4RGF6ZmVpdGo5cFdtbklXdTB6XzE2MzkxNDg4Mjk6MTYzOTE1MjQyOV9WNA)]
#ls
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lYcI8BGg-1639148863376)(https://xz2k2i3v0u.feishu.cn/space/api/box/stream/download/asynccode/?code=Yjg2NGU1YzNmN2RkYzg2ZTA4MmVjYjU1ZmE2YWU5MTFfUWF1aHQwS1J2MXh5bGZKVEhhdENCckVxQnFtSElYamJfVG9rZW46Ym94Y244dmhtRThUdkV5c2J1a0ROa0I5V0NnXzE2MzkxNDg4Mjk6MTYzOTE1MjQyOV9WNA)]
文件列表包含 mymodules.ko
加载内核模块
#insmod ./mymodules.ko
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SVzuAd9P-1639148863377)(https://xz2k2i3v0u.feishu.cn/space/api/box/stream/download/asynccode/?code=NmZjMGQwNDJhOWZkYWJmYzUwYmVhYWVkMGY4MDUxYTJfdm90VWVndXBkR3c3MDlLNXV3UWV3aTg4MW5DUWhUMjdfVG9rZW46Ym94Y25SZnladkJKZ2hDVnVDalllR2tmOW9oXzE2MzkxNDg4Mjk6MTYzOTE1MjQyOV9WNA)]
需要root权限
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bHKxKdHq-1639148863377)(https://xz2k2i3v0u.feishu.cn/space/api/box/stream/download/asynccode/?code=ODEyM2NmM2UwN2RjZjFlYWMxY2ZlZGVhNjZlZDAwZGJfNVFtcjBBQkZrc2RoMU41cGxxZkhjT3hXOUh5SkFXUDhfVG9rZW46Ym94Y25ldnlDVnZERVZCV3ZCamdPQUk3dnNmXzE2MzkxNDg4Mjk6MTYzOTE1MjQyOV9WNA)]
使用 lsmod 命令来查看系统内的模块列表,以观察新设计的模块加载情况
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0p9tcuHr-1639148863378)(https://xz2k2i3v0u.feishu.cn/space/api/box/stream/download/asynccode/?code=Y2ZhODYxYTNhNTM3NTA1YjM5NjYyZDdjNzRiMzRmZWZfeGRNVkZOZk1HaFNFWWFHWGo0TG1yMFhrd05LaldSUXlfVG9rZW46Ym94Y24zdmpveTl5c3hhRVJHdlR6TEpWZkJiXzE2MzkxNDg4Mjk6MTYzOTE1MjQyOV9WNA)]
mymodules 即为新加载的内核动态模块
或者查看内核日志信息,执行:
#dmesg –c
此时可以看到内核日志信息:hello,my module was loaded!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UoqMTb8M-1639148863378)(https://xz2k2i3v0u.feishu.cn/space/api/box/stream/download/asynccode/?code=NWNhNDM0NTE5MmQyY2FmMGMxZTk1NmViMTE2YjJlMDdfNU0za25tdEVZb2JLRHoycHV1MjZXdTlQRmlxYXpzeUZfVG9rZW46Ym94Y25lYm0yMWVkZnZCM0JNQUNsc3pPZUZnXzE2MzkxNDg4Mjk6MTYzOTE1MjQyOV9WNA)]
卸载动态模块
#rmmod mymodules
lsmod查看,已经没有了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-83FTBuLa-1639148863379)(https://xz2k2i3v0u.feishu.cn/space/api/box/stream/download/asynccode/?code=MjIzMzc2Y2VjOTVkMGQ2MGZlZWU1MDE4MDBhMTMyNjVfcVR0bjZHV3pENGJMQjZJbVk4WEt0RmcwUnZ4TFlQNUdfVG9rZW46Ym94Y25LQmdXVlJhVENyTkNJelRRamo2b3FlXzE2MzkxNDg4Mjk6MTYzOTE1MjQyOV9WNA)]
查看dmesg,已经卸载。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-esJQn2jv-1639148863379)(https://xz2k2i3v0u.feishu.cn/space/api/box/stream/download/asynccode/?code=OGQ1ODZlZjA4MWIwZjFkOWU2ZmZlMjQ3NTRhZTdkNzdfZDVtbUVUMFNKS09xVlFISWd1QzdQNWhoQnMzOTFYdmtfVG9rZW46Ym94Y25Fc3NFVnB6T2gyQmhyakxZWkh1M1JlXzE2MzkxNDg4Mjk6MTYzOTE1MjQyOV9WNA)]
篡改系统调用
(39条消息) Linux 系统调用(二)——使用内核模块添加系统调用(无需编译内核)_金荣的个人技术博客-CSDN博客_内核模块添加系统调用
本实验在Ubuntu12.04下进行。
系统调用号
Linux操作系统利用系统调用号来标识系统调用,每一个系统调用对应唯一的一个系统调用号。使用sudo find / -name unistd_64.h
查找预留系统调用号。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lLMx6SuE-1639148863380)(https://xz2k2i3v0u.feishu.cn/space/api/box/stream/download/asynccode/?code=ZTEyZmM5ZmI3ODYwMjk0YjE3MWMxYmExMjUzMmNlYTdfVGk2bUN6WWVOT0JQSnloV1diOWlNMWp0WmpEaHY1bERfVG9rZW46Ym94Y25FdHlJQVEyb3c2QllwS2h6TE1LRVFoXzE2MzkxNDg4Mjk6MTYzOTE1MjQyOV9WNA)]
使用uname -r查看内核版
打开unistd_64.h,内容如下。
#ifndef __ASM_SH_UNISTD_64_H
#define __ASM_SH_UNISTD_64_H
/*
* include/asm-sh/unistd_64.h
*
* This file contains the system call numbers.
*
* Copyright (C) 2000, 2001 Paolo Alberelli
* Copyright (C) 2003 - 2007 Paul Mundt
* Copyright (C) 2004 Sean McGoogan
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#define __NR_restart_syscall 0
#define __NR_exit 1
#define __NR_fork 2
#define __NR_read 3
#define __NR_write 4
#define __NR_open 5
#define __NR_close 6
#define __NR_waitpid 7
#define __NR_creat 8
#define __NR_link 9
#define __NR_unlink 10
#define __NR_execve 11
#define __NR_chdir 12
#define __NR_time 13
#define __NR_mknod 14
#define __NR_chmod 15
#define __NR_lchown 16
/* 17 was sys_break */
#define __NR_oldstat 18
#define __NR_lseek 19
#define __NR_getpid 20
#define __NR_mount 21
#define __NR_umount 22
#define __NR_setuid 23
#define __NR_getuid 24
#define __NR_stime 25
#define __NR_ptrace 26
#define __NR_alarm 27
#define __NR_oldfstat 28
#define __NR_pause 29
#define __NR_utime 30
/* 31 was sys_stty */
/* 32 was sys_gtty */
#define __NR_access 33
#define __NR_nice 34
/* 35 was sys_ftime */
#define __NR_sync 36
#define __NR_kill 37
#define __NR_rename 38
#define __NR_mkdir 39
#define __NR_rmdir 40
#define __NR_dup 41
#define __NR_pipe 42
#define __NR_times 43
/* 44 was sys_prof */
#define __NR_brk 45
#define __NR_setgid 46
#define __NR_getgid 47
#define __NR_signal 48
#define __NR_geteuid 49
#define __NR_getegid 50
#define __NR_acct 51
#define __NR_umount2 52
/* 53 was sys_lock */
#define __NR_ioctl 54
#define __NR_fcntl 55
/* 56 was sys_mpx */
#define __NR_setpgid 57
/* 58 was sys_ulimit */
/* 59 was sys_olduname */
#define __NR_umask 60
#define __NR_chroot 61
#define __NR_ustat 62
#define __NR_dup2 63
#define __NR_getppid 64
#define __NR_getpgrp 65
#define __NR_setsid 66
#define __NR_sigaction 67
#define __NR_sgetmask 68
#define __NR_ssetmask 69
#define __NR_setreuid 70
#define __NR_setregid 71
#define __NR_sigsuspend 72
#define __NR_sigpending 73
#define __NR_sethostname 74
#define __NR_setrlimit 75
#define __NR_getrlimit 76 /* Back compatible 2Gig limited rlimit */
#define __NR_getrusage 77
#define __NR_gettimeofday 78
#define __NR_settimeofday 79
#define __NR_getgroups 80
#define __NR_setgroups 81
/* 82 was sys_select */
#define __NR_symlink 83
#define __NR_oldlstat 84
#define __NR_readlink 85
#define __NR_uselib 86
#define __NR_swapon 87
#define __NR_reboot 88
#define __NR_readdir 89
#define __NR_mmap 90
#define __NR_munmap 91
#define __NR_truncate 92
#define __NR_ftruncate 93
#define __NR_fchmod 94
#define __NR_fchown 95
#define __NR_getpriority 96
#define __NR_setpriority 97
/* 98 was sys_profil */
#define __NR_statfs 99
#define __NR_fstatfs 100
/* 101 was sys_ioperm */
#define __NR_socketcall 102 /* old implementation of socket systemcall */
#define __NR_syslog 103
#define __NR_setitimer 104
#define __NR_getitimer 105
#define __NR_stat 106
#define __NR_lstat 107
#define __NR_fstat 108
#define __NR_olduname 109
/* 110 was sys_iopl */
#define __NR_vhangup 111
/* 112 was sys_idle */
/* 113 was sys_vm86old */
#define __NR_wait4 114
#define __NR_swapoff 115
#define __NR_sysinfo 116
#define __NR_ipc 117
#define __NR_fsync 118
#define __NR_sigreturn 119
#define __NR_clone 120
#define __NR_setdomainname 121
#define __NR_uname 122
#define __NR_cacheflush 123
#define __NR_adjtimex 124
#define __NR_mprotect 125
#define __NR_sigprocmask 126
/* 127 was sys_create_module */
#define __NR_init_module 128
#define __NR_delete_module 129
/* 130 was sys_get_kernel_syms */
#define __NR_quotactl 131
#define __NR_getpgid 132
#define __NR_fchdir 133
#define __NR_bdflush 134
#define __NR_sysfs 135
#define __NR_personality 136
/* 137 was sys_afs_syscall */
#define __NR_setfsuid 138
#define __NR_setfsgid 139
#define __NR__llseek 140
#define __NR_getdents 141
#define __NR__newselect 142
#define __NR_flock 143
#define __NR_msync 144
#define __NR_readv 145
#define __NR_writev 146
#define __NR_getsid 147
#define __NR_fdatasync 148
#define __NR__sysctl 149
#define __NR_mlock 150
#define __NR_munlock 151
#define __NR_mlockall 152
#define __NR_munlockall 153
#define __NR_sched_setparam 154
#define __NR_sched_getparam 155
#define __NR_sched_setscheduler 156
#define __NR_sched_getscheduler 157
#define __NR_sched_yield 158
#define __NR_sched_get_priority_max 159
#define __NR_sched_get_priority_min 160
#define __NR_sched_rr_get_interval 161
#define __NR_nanosleep 162
#define __NR_mremap 163
#define __NR_setresuid 164
#define __NR_getresuid 165
/* 166 was sys_vm86 */
/* 167 was sys_query_module */
#define __NR_poll 168
#define __NR_nfsservctl 169
#define __NR_setresgid 170
#define __NR_getresgid 171
#define __NR_prctl 172
#define __NR_rt_sigreturn 173
#define __NR_rt_sigaction 174
#define __NR_rt_sigprocmask 175
#define __NR_rt_sigpending 176
#define __NR_rt_sigtimedwait 177
#define __NR_rt_sigqueueinfo 178
#define __NR_rt_sigsuspend 179
#define __NR_pread64 180
#define __NR_pwrite64 181
#define __NR_chown 182
#define __NR_getcwd 183
#define __NR_capget 184
#define __NR_capset 185
#define __NR_sigaltstack 186
#define __NR_sendfile 187
/* 188 reserved for getpmsg */
/* 189 reserved for putpmsg */
#define __NR_vfork 190
#define __NR_ugetrlimit 191 /* SuS compliant getrlimit */
#define __NR_mmap2 192
#define __NR_truncate64 193
#define __NR_ftruncate64 194
#define __NR_stat64 195
#define __NR_lstat64 196
#define __NR_fstat64 197
#define __NR_lchown32 198
#define __NR_getuid32 199
#define __NR_getgid32 200
#define __NR_geteuid32 201
#define __NR_getegid32 202
#define __NR_setreuid32 203
#define __NR_setregid32 204
#define __NR_getgroups32 205
#define __NR_setgroups32 206
#define __NR_fchown32 207
#define __NR_setresuid32 208
#define __NR_getresuid32 209
#define __NR_setresgid32 210
#define __NR_getresgid32 211
#define __NR_chown32 212
#define __NR_setuid32 213
#define __NR_setgid32 214
#define __NR_setfsuid32 215
#define __NR_setfsgid32 216
#define __NR_pivot_root 217
#define __NR_mincore 218
#define __NR_madvise 219
/* Non-multiplexed socket family */
#define __NR_socket 220
#define __NR_bind 221
#define __NR_connect 222
#define __NR_listen 223
#define __NR_accept 224
#define __NR_getsockname 225
#define __NR_getpeername 226
#define __NR_socketpair 227
#define __NR_send 228
#define __NR_sendto 229
#define __NR_recv 230
#define __NR_recvfrom 231
#define __NR_shutdown 232
#define __NR_setsockopt 233
#define __NR_getsockopt 234
#define __NR_sendmsg 235
#define __NR_recvmsg 236
/* Non-multiplexed IPC family */
#define __NR_semop 237
#define __NR_semget 238
#define __NR_semctl 239
#define __NR_msgsnd 240
#define __NR_msgrcv 241
#define __NR_msgget 242
#define __NR_msgctl 243
#define __NR_shmat 244
#define __NR_shmdt 245
#define __NR_shmget 246
#define __NR_shmctl 247
#define __NR_getdents64 248
#define __NR_fcntl64 249
/* 250 is reserved for tux */
/* 251 is unused */
#define __NR_gettid 252
#define __NR_readahead 253
#define __NR_setxattr 254
#define __NR_lsetxattr 255
#define __NR_fsetxattr 256
#define __NR_getxattr 257
#define __NR_lgetxattr 258
#define __NR_fgetxattr 269
#define __NR_listxattr 260
#define __NR_llistxattr 261
#define __NR_flistxattr 262
#define __NR_removexattr 263
#define __NR_lremovexattr 264
#define __NR_fremovexattr 265
#define __NR_tkill 266
#define __NR_sendfile64 267
#define __NR_futex 268
#define __NR_sched_setaffinity 269
#define __NR_sched_getaffinity 270
/* 271 is reserved for set_thread_area */
/* 272 is reserved for get_thread_area */
#define __NR_io_setup 273
#define __NR_io_destroy 274
#define __NR_io_getevents 275
#define __NR_io_submit 276
#define __NR_io_cancel 277
#define __NR_fadvise64 278
/* 279 is unused */
#define __NR_exit_group 280
#define __NR_lookup_dcookie 281
#define __NR_epoll_create 282
#define __NR_epoll_ctl 283
#define __NR_epoll_wait 284
#define __NR_remap_file_pages 285
#define __NR_set_tid_address 286
#define __NR_timer_create 287
#define __NR_timer_settime (__NR_timer_create+1)
#define __NR_timer_gettime (__NR_timer_create+2)
#define __NR_timer_getoverrun (__NR_timer_create+3)
#define __NR_timer_delete (__NR_timer_create+4)
#define __NR_clock_settime (__NR_timer_create+5)
#define __NR_clock_gettime (__NR_timer_create+6)
#define __NR_clock_getres (__NR_timer_create+7)
#define __NR_clock_nanosleep (__NR_timer_create+8)
#define __NR_statfs64 296
#define __NR_fstatfs64 297
#define __NR_tgkill 298
#define __NR_utimes 299
#define __NR_fadvise64_64 300
/* 301 is reserved for vserver */
/* 302 is reserved for mbind */
/* 303 is reserved for get_mempolicy */
/* 304 is reserved for set_mempolicy */
#define __NR_mq_open 305
#define __NR_mq_unlink (__NR_mq_open+1)
#define __NR_mq_timedsend (__NR_mq_open+2)
#define __NR_mq_timedreceive (__NR_mq_open+3)
#define __NR_mq_notify (__NR_mq_open+4)
#define __NR_mq_getsetattr (__NR_mq_open+5)
/* 311 is reserved for kexec */
#define __NR_waitid 312
#define __NR_add_key 313
#define __NR_request_key 314
#define __NR_keyctl 315
#define __NR_ioprio_set 316
#define __NR_ioprio_get 317
#define __NR_inotify_init 318
#define __NR_inotify_add_watch 319
#define __NR_inotify_rm_watch 320
/* 321 is unused */
#define __NR_migrate_pages 322
#define __NR_openat 323
#define __NR_mkdirat 324
#define __NR_mknodat 325
#define __NR_fchownat 326
#define __NR_futimesat 327
#define __NR_fstatat64 328
#define __NR_unlinkat 329
#define __NR_renameat 330
#define __NR_linkat 331
#define __NR_symlinkat 332
#define __NR_readlinkat 333
#define __NR_fchmodat 334
#define __NR_faccessat 335
#define __NR_pselect6 336
#define __NR_ppoll 337
#define __NR_unshare 338
#define __NR_set_robust_list 339
#define __NR_get_robust_list 340
#define __NR_splice 341
#define __NR_sync_file_range 342
#define __NR_tee 343
#define __NR_vmsplice 344
#define __NR_move_pages 345
#define __NR_getcpu 346
#define __NR_epoll_pwait 347
#define __NR_utimensat 348
#define __NR_signalfd 349
#define __NR_timerfd_create 350
#define __NR_eventfd 351
#define __NR_fallocate 352
#define __NR_timerfd_settime 353
#define __NR_timerfd_gettime 354
#define __NR_signalfd4 355
#define __NR_eventfd2 356
#define __NR_epoll_create1 357
#define __NR_dup3 358
#define __NR_pipe2 359
#define __NR_inotify_init1 360
#define __NR_preadv 361
#define __NR_pwritev 362
#define __NR_rt_tgsigqueueinfo 363
#define __NR_perf_event_open 364
#define __NR_recvmmsg 365
#define __NR_accept4 366
#define __NR_fanotify_init 367
#define __NR_fanotify_mark 368
#define __NR_prlimit64 369
#define __NR_name_to_handle_at 370
#define __NR_open_by_handle_at 371
#define __NR_clock_adjtime 372
#define __NR_syncfs 373
#define __NR_sendmmsg 374
#define __NR_setns 375
#define __NR_process_vm_readv 376
#define __NR_process_vm_writev 377
#define __NR_kcmp 378
#define __NR_finit_module 379
#define NR_syscalls 380
#endif /* __ASM_SH_UNISTD_64_H */
其中321号系统调用没有使用,可以篡改此调用号。
系统调用表
Linux操作系统利用函数指针数组保存系统调用服务程序的地址,这个数组成为系统调用表(sys_call_table)。使用/proc/kallsyms
获取系统调用表的首地址,命令为sudo cat /proc/kallsyms | grep sys_call_table
,结果如图。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xn130guP-1639148863380)(https://xz2k2i3v0u.feishu.cn/space/api/box/stream/download/asynccode/?code=ZDU0NDY5MGUzOGU0MDFjMGExNDUxOTA4NGNhM2Q2OGZfdk5ocUNrZm9GZEhQMHdhY2htVDVZcm9lNUR4N05KcG5fVG9rZW46Ym94Y25kOTBNM1J3b2tWdXhTTDJ0UEk2TmhmXzE2MzkxNDg4Mjk6MTYzOTE1MjQyOV9WNA)]
可以看到地址为ffffffff81801420
建立模块源
编写程序 modify_syscall.c
修改控制寄存器
在上面符号表中查阅sys_call_table的地址时,c1630060 R sys_call_table里面的R代表的是可读属性,所以为了能顺利篡改系统调用的地址,需要提前对控制寄存器CR0 的第16 位WP(写保护位)进行修改,在程序退出时则进行还原。
修改控制寄存器的函数为clear_cr0,代码如下。
unsigned int clear_cr0(void)
{
unsigned int cr0 = 0;
unsigned int ret;
//move the value in reg cr0 to reg rax
//movl moves a 32-bits operand
//movq moves a 64-bits operand
//rax is a 64-bits register
//an assembly language code
//asm volatile ("movl %%cr0, %%eax" : "=a"(cr0));//32-bits
asm volatile ("movq %%cr0, %%rax" : "=a"(cr0)); //64-bits
ret = cr0;
//var cr0 is rax
cr0 &= 0xfffeffff; //set 0 to the 17th bit
//asm volatile ("movl %%eax, %%cr0" :: "a"(cr0));//32-bits
//note that cr0 above is a variable while cr0 below is a reg.
asm volatile ("movq %%rax, %%cr0" :: "a"(cr0));
return ret;
}
注意32位和64位的汇编代码部分指令不一样。
还原控制寄存器
修改系统调用后要还原控制寄存器,还原代码如下。
//recover the value of WP
void setback_cr0(unsigned int val)
{
//asm volatile ("movl %%eax, %%cr0" :: "a"(val));//32-bits
asm volatile ("movq %%rax, %%cr0" :: "a"(val));//64-bits
}
自己的系统调用函数
编写自己的系统调用函数my_sys_func,输出我的班级学号,返回输入参数之和。
static int my_sys_func(int a,int b,int c)
{
printk("Change syscall successfully!\nReturn a+b+c\nBy cwj-class 93-No 2194214336\n");
return a+b+c;
}
模块初始化
编写模块初始化函数init_sys_call,包括获取系统调用表、备份原系统调用函数地址、修改写保护位、修改系统调用地址、改回写保护位。
注意这里没有直接写入系统调用表地址,而是使用了kallsyms_lookup_name("sys_call_table");
自动获取地址。
static int __init init_sys_call(void)
{
printk("Begin changing syscall...\n");
//Automatically get sys_call_table address
sys_call_table = (unsigned long *)kallsyms_lookup_name("sys_call_table");
//print sys_call_table address
printk("sys_call_table: 0x%p\n", sys_call_table);
//save original syscall func
old_sys_call_func = (int(*)(void))(sys_call_table[__NR_syscall]);
//modify the value of WP in CR0
orig_cr0 = clear_cr0();
//change the syscall address
sys_call_table[__NR_syscall] = (unsigned long)&my_sys_func;
//setback the value of WP in CR0
//to read only
setback_cr0(orig_cr0);
return 0;
}
退出模块
编写退出模块函数exit_sys_call,包括修改写保护位、恢复系统调用函数、恢复写保护位。
static void __exit exit_sys_call(void)
{
//modify the value of WP in CR0
orig_cr0 = clear_cr0();
//change the syscall address
sys_call_table[__NR_syscall] =old_sys_call_func;
//setback the value of WP in CR0
//to read only
setback_cr0(orig_cr0);
printk("Recovering syscall...\n");
}
完整模块代码
综合以上部分形成完整代码如下。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/unistd.h>
#include <linux/time.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/kallsyms.h>
//using syscall 321
#define __NR_syscall 321
unsigned long *sys_call_table;
unsigned int clear_cr0(void);
void setback_cr0(unsigned int val);
static int sys_mycall(int a, int b, int c);
//to save the original value of the register cr0
unsigned long orig_cr0;
unsigned long *sys_call_table = 0;
//to save the original syscall func
unsigned long old_sys_call_func;
//set 0 to the 17th bit (WP) in reg cr0
unsigned int clear_cr0(void)
{
unsigned int cr0 = 0;
unsigned int ret;
//move the value in reg cr0 to reg rax
//movl moves a 32-bits operand
//movq moves a 64-bits operand
//rax is a 64-bits register
//an assembly language code
//asm volatile ("movl %%cr0, %%eax" : "=a"(cr0));//32-bits
asm volatile ("movq %%cr0, %%rax" : "=a"(cr0)); //64-bits
ret = cr0;
//var cr0 is rax
cr0 &= 0xfffeffff; //set 0 to the 17th bit
//asm volatile ("movl %%eax, %%cr0" :: "a"(cr0));//32-bits
//note that cr0 above is a variable while cr0 below is a reg.
asm volatile ("movq %%rax, %%cr0" :: "a"(cr0));
return ret;
}
//recover the value of WP
void setback_cr0(unsigned int val)
{
//asm volatile ("movl %%eax, %%cr0" :: "a"(val));//32-bits
asm volatile ("movq %%rax, %%cr0" :: "a"(val));//64-bits
}
//my syscall function
static int sys_mycall(int a,int b,int c)
{
printk("Change syscall successfully!\nReturn a+b+c\nBy cwj-class 93-No 2194214336\n");
return a+b+c;
}
static int __init init_addsyscall(void)
{
printk("Begin changing syscall...\n");
//Automatically get sys_call_table address
sys_call_table = (unsigned long *)kallsyms_lookup_name("sys_call_table");
//print sys_call_table address
printk("sys_call_table: 0x%p\n", sys_call_table);
//save original syscall func
old_sys_call_func = (int(*)(void))(sys_call_table[__NR_syscall]);
//modify the value of WP in CR0
orig_cr0 = clear_cr0();
//change the syscall address
sys_call_table[__NR_syscall] = (unsigned long)&sys_mycall;
//setback the value of WP in CR0
//to read only
setback_cr0(orig_cr0);
return 0;
}
static void __exit exit_addsyscall(void)
{
//modify the value of WP in CR0
orig_cr0 = clear_cr0();
//change the syscall address
sys_call_table[__NR_syscall] =old_sys_call_func;
//setback the value of WP in CR0
//to read only
setback_cr0(orig_cr0);
printk("Recovering syscall...\n");
}
module_init(init_addsyscall);
module_exit(exit_addsyscall);
MODULE_LICENSE("GPL");
建立makefile文件
建立makefile文件如下
ifneq ($(KERNELRELEASE),)
obj-m := modify_syscall.o
else
KERNELDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
编译模块
使用make
编译模块
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N78pwTkb-1639148863381)(https://xz2k2i3v0u.feishu.cn/space/api/box/stream/download/asynccode/?code=ZDFkMjE4NDU5ZGQyM2JmYzQzODMxN2ZiMjZiMDc0NGZfTllyNG1BVDRXeEl6RDM0OHBEejVFd0laMXZaMDZSWnNfVG9rZW46Ym94Y25yRWJFM1VIS0xIUW16ZWlidk5Xb29mXzE2MzkxNDg4Mjk6MTYzOTE1MjQyOV9WNA)]
编写测试程序test.c
#include<stdio.h>
#include<unistd.h>
#include<sys/syscall.h>
#include<time.h>
int main()
{
printf("Set parameters 78,10,20\nReturn:");
int ret=syscall(321,78,10,20);
if(ret == -1)
printf("syscall failed!\n");
else
printf("%d\n",ret);
return 0;
}
编译运行得到下图。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-haFzPOkR-1639148863381)(https://xz2k2i3v0u.feishu.cn/space/api/box/stream/download/asynccode/?code=MTc3NDlkMTAyOTJkZDk0NTNmNGIzYmQ5ZGM3NmY2N2FfM0N4dzV0NWRqVkRUWXlvM0lON1BsTXF2VkFNREUxQWtfVG9rZW46Ym94Y25WT0R2MEU2SHh5WGlmUTFWMmJPRVhhXzE2MzkxNDg4Mjk6MTYzOTE1MjQyOV9WNA)]
安装模块
使用sudo insmod modify_syscall.ko
安装模块,安装完后使用test文件测试,结果如图。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZHYI5q94-1639148863381)(https://xz2k2i3v0u.feishu.cn/space/api/box/stream/download/asynccode/?code=ZDQwM2RmNDI0OWVhMjE3ODZmM2YyYTVkZWY3YTcyYmNfbFV3c2plcDFjaW5KbWlWMUF6M1JUM0xUNVV1WXkyeWFfVG9rZW46Ym94Y25uSkEwZUZyRlcwRlY5MEd0Tm8zVE9iXzE2MzkxNDg4Mjk6MTYzOTE1MjQyOV9WNA)]
可以看到返回值为108,是输入参数之和,说明系统调用篡改成功。
卸载模块
使用sudo rmmod modify_syscall
卸载模块,卸载后再次使用test测试如图。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OlGbx4VZ-1639148863382)(https://xz2k2i3v0u.feishu.cn/space/api/box/stream/download/asynccode/?code=ZDFjMjY2Nzk0MGZhODIyZmVhM2Q3YTliNTY4NjM4MzVfaGM2SEdUdURjbzBybkZlQjRjZEZnbHpzYjhIQjJRUFBfVG9rZW46Ym94Y25SMm1pUDF2Y09KSmlGQ1NXamo1enhlXzE2MzkxNDg4Mjk6MTYzOTE1MjQyOV9WNA)]
可见这是系统调用已经恢复。
使用dmesg -c
查看日志如图。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qEl3aUXJ-1639148863382)(https://xz2k2i3v0u.feishu.cn/space/api/box/stream/download/asynccode/?code=YmZlYmQzYzA5ZmM3Y2MyYTcwMDgzNWM4ZTczNzMxODNfZ2dOZE54YTB0dG1LZWVXYmliSlYySllJalY5S2RJTzJfVG9rZW46Ym94Y25uQlVXeHU5SFpTaWJXVG1FSGg1MGxjXzE2MzkxNDg4Mjk6MTYzOTE1MjQyOV9WNA)]
可以看到日志中打印出了篡改过程,说明实验成功。
出现的问题
Make时错误
make时出现如下报错
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WmWXg0PO-1639148863382)(https://xz2k2i3v0u.feishu.cn/space/api/box/stream/download/asynccode/?code=YjMwNjQzN2RkZDMzN2Q4MmQ4YWY1ZGVkOWIzNDQzZTlfRFlqekEzaHJEY3d0Wm5xSUZPYUozWVVzUmJ6SGJKZkpfVG9rZW46Ym94Y25MRk1WT3l4TnVuMnhyM3E2RUNtMkJUXzE2MzkxNDg4Mjk6MTYzOTE1MjQyOV9WNA)]
这是因为sys_call_table的地址是一个十六进制数,需要在前面加上0x。将modify_syscall.c中的地址改为unsigned long p_sys_call_table=0xffffffff81801420;
int ret=syscall(321,78,10,20);
if(ret == -1)
printf("syscall failed!\n");
else
printf("%d\n",ret);
return 0;
}
编译运行得到下图。
[外链图片转存中...(img-haFzPOkR-1639148863381)]
### 安装模块
使用`sudo insmod modify_syscall.ko`安装模块,安装完后使用test文件测试,结果如图。
[外链图片转存中...(img-ZHYI5q94-1639148863381)]
可以看到返回值为108,是输入参数之和,说明系统调用篡改成功。
### 卸载模块
使用`sudo rmmod modify_syscall`卸载模块,卸载后再次使用test测试如图。
[外链图片转存中...(img-OlGbx4VZ-1639148863382)]
可见这是系统调用已经恢复。
使用`dmesg -c`查看日志如图。
[外链图片转存中...(img-qEl3aUXJ-1639148863382)]
可以看到日志中打印出了篡改过程,说明实验成功。
# 出现的问题
## Make时错误
make时出现如下报错
[外链图片转存中...(img-WmWXg0PO-1639148863382)]
这是因为sys_call_table的地址是一个十六进制数,需要在前面加上0x。将modify_syscall.c中的地址改为`unsigned long p_sys_call_table=0xffffffff81801420; `
##