zoukankan      html  css  js  c++  java
  • 《Linux及安全》实践2

    《Linux及安全》实践2

    【edited by 5216lwr】

    一、Linux基本内核模块

    1.1理解什么是内核模块

    1. linux模块是一些可以作为独立程序来编译的函数和数据类型的集合。之所以提供模块机制,是因为Linux本身是一个单内核。单内核由于所有内容都集成在一起,效率很高,但可扩展性和可维护性相对较差,模块机制可弥补这一缺陷。
    2. Linux模块可以通过静态或动态的方法加载到内核空间,静态加载是指在内核启动过程中加载;动态加载是指在内核运行的过程中随时加载。(我们要做的实践内容就是动态加载内核模块)
    3. 一个模块被加载到内核中时,就成为内核代码的一部分。模块加载入系统时,系统修改内核中的符号表,将新加载的模块提供的资源和符号添加到内核符号表中,以便进行模块间的通信。

    1.2.c程序编写

    • 具体代码如下:

      • 代码——

          #include <linux/init.h>
          #include <linux/module.h>
          
          MODULE_LICENSE("Dual BSD/GPL");
          
          static char *name =" lwr";
          static int __init name_init(void)
          {
          	printk("==hello,world==
        ");
          	printk("==hello,%s
        ",name);
          	return 0;
          }
          static void __exit name_exit(void)
          {
          	printk(KERN_INFO"name module exit
        ");
          }
          
          module_init(name_init);
          module_exit(name_exit);
          
          module_param(name,charp,S_IRUGO);
        
    • 解释:
      1.关于 #include <linux/init.h>以及#include <linux/module.h>是编写内核模块程序所必须的2个头文件。由于内核编程和用户层编程所用的库函数不一样,所以它的头文件也和在用户层编写程序所用的头文件不一样。比如,内核头文件的位置 : /usr/src/linux-2.6.x/include/;而用户层头文件的位置 : /usr/include/

      1. 编写内核模块时必须要有的两个函数 :
        • 加载函数:

           static int init_fun(void)
           {
           // 初始化代码
           } 
           函数实例:
           static int hello_init(void)// 不加void在调试时会出现报警
           {
           printk("hello world!/n");
           return 0;
           }
          
        • 卸载函数(出口函数) 无返回值

           static void cleaup_fun(void)
           {
           // 释放代码
           }
          
      2. printk是内核态信息打印函数,功能和比标准C库的printf类似。printk还有信息打印级别。其中的参数KERN_INFO的定义如下:
        • define KERN_INFO "<6>" /* 提示信息,如驱动程序启动时,打印硬件信息 */

      3. 为什么内核态使用 printk() 函数,而在用户态使用 printf() 函数?
        • printk() 函数是直接使用了向终端写函数 tty_write() 。而 printf() 函数是调用 write() 系统调用函数向标准输出设备写。
        • 所以在用户态(如进程 0 )不能够直接使用 printk() 函数,而在内核态由于它已是特权级,所以无需系统调用来改变特权级,因而能够直接使用 printk() 函数。 - printk 是内核输出,在终端是看不见的。我们使用 dmesg 命令看一下输出的信息(也就是下面要用到的命令)。
      4. module_init(fun):告诉内核模块程序从何处开始执行。module_exit(fun):告诉内核编写模块程序从何处离开。
      5. 模块许可声明:函数原型是MODULE_LICENSE(),告诉内核该程序使用的许可证,一般是GPL。

    1.3Makefile编写

    1. 代码如下:

       obj-m:= lwr.o// 产生 lwr 模块的目标文件
       CURRENT_PATH:= $(shell pwd) // 定义内核源文件目录
       LINUX_KERNEL_PATH:=$usr/src/linux-headers-3.19.0-25-generic
       all:
       	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH)  modules
       // 生成内核模块参数为内核源代码目录以及模块所在目录
       clean:
       	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
       // 清除生成的模块文件以及中间文件
      
    2. 解释:

      • 其中make -C $(LINUX_KERNEL_PATH) 指明跳转到内核源码目录下读取Makefile
        M=$(CURRENT_PATH) 表明返回到当前目录继续执行当前的Makefile(M= 后指定的是 lwr.c 和 Makefile 所在的目录)
      • 关于个人虚拟机的内核版本号,可以再usr/src中查看

    1.4操作

    1. 输入make进行自动编译

    2. 编译之后,可以看到生成了很多lwr.xx等文件

    3. 输入sudo insmod lwr.ko,加载模块(会要求输入当前用户的密码)

    4. 输入dmesg,测试模块输出

    5. 输入sudo rmmod lwr之后,就将内核中之前加载的lwr模块删除,此时再输入dmesg测试,可以看到显示了exit信息

    二、Linux内核模块的功能实现

    1. 在上述步骤的基础上,可以对内核模块的.c文件进行修改以实现更多的功能

    2. 具体代码加下

    3. 解释:

      • for_each_process()的函数本质即for循环,从第一个PCB(init_task)开始,顺着pcurrent链表进行遍历,输出所有当前进程的信息(pid,tgid等)。
    4. Makefile编写如下

    5. 进行make之后,顺利生成模块

    6. 再进行sudo insmod lwr.ko,之后dmesg,就可以查看进程链表的信息

    三、Linux内核模块功能拓展

    1. 用如下代码进行文件信息的读取

    2. lwr.c代码如下:

       #include<linux/module.h>
       #include<linux/kernel.h>
       #include<linux/init.h>
       #include<linux/sched.h>
      
       MODULE_LICENSE("Dual BSD/GPL");
      
       static int __init hello_init(void)
       {
       	FILE *fp;
       	if((fp = fopen( "/proc/cpuinfo","r" ))==NULL){
                printk("File can not be opened
      ");
          }
          else{
             fseek(fp,79l,0);
             fgets(information,48,fp);      
             printk("CPU is:%s
      ",information);
          } 
          fclose(fp);   
           printk("hello world!
      ");
           return 0;
       }
       static void __exit hello_exit(void)
       {
           printk("GoodBye!
      ");
       }
       module_init(hello_init);
       module_exit(hello_exit);
      
    3. Makefile如上

    4. make编译之后,进行insomd之后dmesg查看信息

  • 相关阅读:
    自定义CopyOnWriteHashMap
    NIO中Buffer缓冲区的实现
    TOMCAT原理详解及请求过程
    XSS的原理分析与解剖
    mysql分页查询优化
    java如何正确停止一个线程
    Centos搭建ElasticSearch
    redis集群原理
    Idea-每次修改JS文件都需要重启Idea才能生效解决方法
    java 加密 解密 Illegal key size
  • 原文地址:https://www.cnblogs.com/lwr-/p/5513253.html
Copyright © 2011-2022 走看看