Linux 内核打补丁

实验环境

Ubuntu 14.04.5 LTS

Linux - 4.15.6

为单个文件进程补丁操作

  1. 在桌面 Desktop 建立文件夹 patch ,作为实验用,然后进入patch 文件夹。建立测试文件 test0test1

我们的目标:给 test0 打补丁 test1.patch ,使 test0test1 内容一致:

Screen Shot 2018-04-10 at 09.27.01

注:使用 diff 创建补丁 test1.patch ;源文件 test0 ;目标文件 test1 ;因为单个文件,所以不需要 -r 选项。选项顺序没有关系,即可以是 -uN ,也可以是 -Nu

Screen Shot 2018-04-10 at 09.28.01

  1. 给源文件 test0 打补丁与恢复: Screen Shot 2018-04-10 at 09.45.17Screen Shot 2018-04-10 at 09.48.20

注:-pNpatch 文件中列出的文件名有关,N 表示拿掉几个斜线(目录层级);

-E 选项说明如果发现了空文件,那么就删除它;

-R 选项说明在补丁文件中的“新”文件和“旧”文件现在要调换过来了(实际上就是给新版本打补丁,让它变成老版本)。

为多个文件进行补丁操作

  1. 在上文中的 /Desktop/patch 文件夹下创建测试文件夹 prj0

Screen Shot 2018-04-10 at 10.27.20

Screen Shot 2018-04-10 at 10.32.32

注:在patch文件夹下,创建文件夹prj0,并把test0拷入,再在文件夹prj0下创建文件prj0name;创建文件夹prj1,并把test1拷入,再在文件夹prj1下创建文件prj1name。然后返回patch目录下。

  1. 创建补丁

diff的功能就是用来比较两个文件的不同,然后记录下来,也就是所谓的diff补丁。语法格式:

diff 【选项】 源文件(夹) 目的文件(夹)

就是要给源文件(夹)打个补丁,使之变成目的文件(夹),术语也就是“升级”。下面介绍三个最为常用选项:

-r 是一个递归选项,设置了这个选项,diff会将两个不同版本源代码目录中的所有对应文件全部都进行一次比较,包括子目录文件。

-N 选项确保补丁文件将正确地处理已经创建或删除文件的情况。

-u 选项以统一格式创建补丁文件,这种格式比缺省格式更紧凑些。

Screen Shot 2018-04-10 at 10.38.26

Screen Shot 2018-04-10 at 10.39.35

注:把所有prj1目录下的文件,打补丁到prj0目录下。

Screen Shot 2018-04-10 at 10.56.34

注:复制 prj1.patch 到 prj0 文件夹下;

​ 进入 prj0 目录下,打补丁,-p1 表示 prj0/test0

下面变通一下,不复制 .patch 文件,执行:

Screen Shot 2018-04-10 at 11.03.42

总结:

  • 单个文件

diff -uN from_file to_file > to_file.patch

patch -p0 < to_file.patch

patch -RE -p0 < to_file.patch

  • 多个文件

diff -uNr from_docu to_docu > to_docu.patch

patch -p1 < to_docu.patch

patch -R -p1 < to_docu.patch

创建显示系统进程信息的 proc 模块

  • tasklist.c与修改后的 tasklist.c(我们下边命名为:tasklist1.c) 生成补丁

  • 首先看一下系统中 proc 目录下是否有 tasklist 这个文件

    Screen Shot 2018-04-10 at 16.10.29

  • 创建tasklist.ctasklist1.cMakefile 文件:

    Screen Shot 2018-04-10 at 16.16.36

    tasklist.c 代码:

    //-------------------------------------------------------------------
    //  tasklist.c: 本内核文件创建一个proc伪文件,'/proc/tasklist'
    //  通过如下命令可以显示系统中所有进程的部分信息
    //  注意:Makefile文件必须正确放置在当前目录下。
    //  编译命令: make 
    //  内核模块添加:$sudo insmod tasklist.ko
    //  添加内核模块后读取并信息tasklist内核信息: $ cat /proc/tasklist
    //  内核模块删除:$sudo rmmod tasklist
    //  NOTE: Written and tested with Linux kernel version 4.15.6 
    //  strace函数可用于追踪系统调用,命令格式如下所示:
    //  $ strace cat /proc/tasklist
    //-------------------------------------------------------------------
    
    #include <linux/module.h>   // for init_module() 
    #include <linux/proc_fs.h>  // for create_proc_info_entry() 
    #include <linux/sched/task.h>   // for init_task
    #include <linux/seq_file.h> // for sequence files
    #include <linux/slab.h>       // for kzalloc, kfree  
    #include <linux/sched/signal.h>   //for next_task
    char modname[] = "tasklist";
    struct task_struct  *task;
    int  taskcounts=0;          // 'global' so value will be retained
    
    static void * my_seq_start(struct seq_file *m, loff_t *pos)
    {
       ///printk(KERN_INFO"Invoke start\n");   //可以输出调试信息
       if ( *pos == 0 )  // 表示遍历开始
       {
           task = &init_task;   //遍历开始的记录地址
           return &task;   //返回一个非零值表示开始遍历
      }
      else //遍历过程中
      { 
        if (task == &init_task )    //重新回到初始地址,退出
            return NULL;
        return (void*)pos   ;//否则返回一个非零值
      }  
    }
    static int my_seq_show(struct seq_file *m, void *v)
    {//获取进程的相关信息
      //printk(KERN_INFO"Invoke show\n");
      //输出进程序号              
      seq_printf( m,  "#%-3d\t ", taskcounts++ );  
      //输出进程pid?
      //输出进程state?
      //输出进程名称(comm)?
      seq_puts( m, "\n" );                    
      return 0; 
    }
    
    static void * my_seq_next(struct seq_file *m, void *v, loff_t *pos)
    {
      //printk(KERN_INFO"Invoke next\n");  
      (*pos)++;   
      //task指向下一个进程?
      return NULL;
    
    }
    static void my_seq_stop(struct seq_file *m, void *v)
    {
        //printk(KERN_INFO"Invoke stop\n");     
        // do nothing        
    }
    
    static struct seq_operations my_seq_fops = {//序列文件记录操作函数集合
            .start  = my_seq_start,
            .next   = my_seq_next,
            .stop   = my_seq_stop,
            .show   = my_seq_show
    };
    
    static int my_open(struct inode *inode, struct file *file)  
    {  
        return seq_open(file, &my_seq_fops); //打开序列文件并关联my_seq_fops
    }  
    
    static const struct file_operations my_proc =   
    {  //proc文件操作函数集合
        .owner      = THIS_MODULE,  
        .open       = my_open,
        .read       = seq_read,     
        .llseek     = seq_lseek,
        .release    = seq_release       
    }; 
    
    int __init my_init( void )
    {
        struct proc_dir_entry* my_proc_entry;
        printk( "<1>\nInstalling \'%s\' module\n", modname );
        my_proc_entry = proc_create(modname, 0x644, NULL, &my_proc);//生成proc文件
        if (NULL == my_proc_entry)
        {
            return -ENOMEM;
        }
        return  0;  //SUCCESS
    }
    
    void __exit my_exit( void )
    {
        remove_proc_entry( modname, NULL );//删除proc文件
        printk( "<1>Removing \'%s\' module\n", modname );   
    }
    
    module_init(my_init);
    module_exit(my_exit);
    MODULE_LICENSE("GPL"); 

    tasklist1.c 代码:

    //-------------------------------------------------------------------
    //  tasklist.c: 本内核文件创建一个proc伪文件,'/proc/tasklist'
    //  通过如下命令可以显示系统中所有进程的部分信息
    //  注意:Makefile文件必须正确放置在当前目录下。
    //  编译命令: make 
    //  内核模块添加:$sudo insmod tasklist.ko
    //  添加内核模块后读取并信息tasklist内核信息: $ cat /proc/tasklist
    //  内核模块删除:$sudo rmmod tasklist
    //  NOTE: Written and tested with Linux kernel version 4.15.6 
    //  strace函数可用于追踪系统调用,命令格式如下所示:
    //  $ strace cat /proc/tasklist
    //-------------------------------------------------------------------
    
    #include <linux/module.h>   // for init_module() 
    #include <linux/proc_fs.h>  // for create_proc_info_entry() 
    #include <linux/sched/task.h>   // for init_task
    #include <linux/seq_file.h> // for sequence files
    #include <linux/slab.h>       // for kzalloc, kfree  
    #include <linux/sched/signal.h>   //for next_task
    char modname[] = "tasklist";
    struct task_struct  *task;
    int  taskcounts=0;          // 'global' so value will be retained
    
    static void * my_seq_start(struct seq_file *m, loff_t *pos)
    {
       ///printk(KERN_INFO"Invoke start\n");   //可以输出调试信息
       if ( *pos == 0 )  // 表示遍历开始
       {
           task = &init_task;   //遍历开始的记录地址
           return &task;   //返回一个非零值表示开始遍历
      }
      else //遍历过程中
      { 
        if (task == &init_task )    //重新回到初始地址,退出
            return NULL;
        return (void*)pos   ;//否则返回一个非零值
      }  
    }
    static int my_seq_show(struct seq_file *m, void *v)
    {//获取进程的相关信息
      //printk(KERN_INFO"Invoke show\n");
      //输出进程序号              
      seq_printf( m,  "#%-3d\t ", taskcounts++ );  
      //输出进程pid?
      seq_printf( m, "%5d\t", task->pid );
      //输出进程state?
      seq_printf( m, "%lu\t", task->state );
      //输出进程名称(comm)?
      seq_printf( m, "%-15s ", task->comm );
      seq_puts( m, "\n" );                    
      return 0; 
    }
    
    static void * my_seq_next(struct seq_file *m, void *v, loff_t *pos)
    {
      //printk(KERN_INFO"Invoke next\n");  
      (*pos)++;   
      //task指向下一个进程?
      task = next_task(task);
      return NULL;
    
    }
    static void my_seq_stop(struct seq_file *m, void *v)
    {
        //printk(KERN_INFO"Invoke stop\n");     
        // do nothing        
    }
    
    static struct seq_operations my_seq_fops = {//序列文件记录操作函数集合
            .start  = my_seq_start,
            .next   = my_seq_next,
            .stop   = my_seq_stop,
            .show   = my_seq_show
    };
    
    static int my_open(struct inode *inode, struct file *file)  
    {  
        return seq_open(file, &my_seq_fops); //打开序列文件并关联my_seq_fops
    }  
    
    static const struct file_operations my_proc =   
    {  //proc文件操作函数集合
        .owner      = THIS_MODULE,  
        .open       = my_open,
        .read       = seq_read,     
        .llseek     = seq_lseek,
        .release    = seq_release       
    }; 
    
    int __init my_init( void )
    {
        struct proc_dir_entry* my_proc_entry;
        printk( "<1>\nInstalling \'%s\' module\n", modname );
        my_proc_entry = proc_create(modname, 0x644, NULL, &my_proc);//生成proc文件
        if (NULL == my_proc_entry)
        {
            return -ENOMEM;
        }
        return  0;  //SUCCESS
    }
    
    void __exit my_exit( void )
    {
        remove_proc_entry( modname, NULL );//删除proc文件
        printk( "<1>Removing \'%s\' module\n", modname );   
    }
    
    module_init(my_init);
    module_exit(my_exit);
    MODULE_LICENSE("GPL"); 

    Makefile 代码:

    ifneq   ($(KERNELRELEASE),)
    obj-m   := tasklist.o 
    
    else
    KDIR    := /lib/modules/$(shell uname -r)/build
    PWD := $(shell pwd)
    default:    
        $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules 
        rm -r -f .tmp_versions *.mod.c .*.cmd *.o *.symvers 
    
    endif
  • tasklist.c 打补丁 tasklist1.patch

    Screen Shot 2018-04-10 at 16.21.32

  • ~/Desktop/module_test 文件夹下,编译:

    Screen Shot 2018-04-10 at 16.24.42

  • 模块 tasklist.ko 已经添加入内核,现在可以查看 tasklist 文件的内核信息了:

    Screen Shot 2018-04-10 at 16.25.42

  • 通过模块传参,传入进程名隐藏相应进程。

  • 卸载模块 tasklist.ko

    Screen Shot 2018-04-10 at 17.39.00

  • ~/Desktop/module_test 目录下,新建如下 tasklist2.c 目标文件:

    //-------------------------------------------------------------------
    //  tasklist.c: 本内核文件创建一个proc伪文件,'/proc/tasklist'
    //  通过如下命令可以显示系统中所有进程的部分信息
    //  注意:Makefile文件必须正确放置在当前目录下。
    //  编译命令: make 
    //  内核模块添加:$sudo insmod tasklist.ko
    //  添加内核模块后读取并信息tasklist内核信息: $ cat /proc/tasklist
    //  内核模块删除:$sudo rmmod tasklist
    //  NOTE: Written and tested with Linux kernel version 4.15.6 
    //  strace函数可用于追踪系统调用,命令格式如下所示:
    //  $ strace cat /proc/tasklist
    //-------------------------------------------------------------------
    
    #include <linux/module.h>   // for init_module() 
    #include <linux/proc_fs.h>  // for create_proc_info_entry() 
    #include <linux/sched/task.h>   // for init_task
    #include <linux/seq_file.h> // for sequence files
    #include <linux/slab.h>       // for kzalloc, kfree  
    #include <linux/sched/signal.h>   //for next_task
    #include <linux/string.h>
    char modname[] = "tasklist";
    struct task_struct  *task;
    int  taskcounts=0;          // 'global' so value will be retained
    
    static char* var;
    module_param(var,charp,0644);
    
    static void * my_seq_start(struct seq_file *m, loff_t *pos)
    {
       ///printk(KERN_INFO"Invoke start\n");   //可以输出调试信息
       if ( *pos == 0 )  // 表示遍历开始
       {
           task = &init_task;   //遍历开始的记录地址
           return &task;   //返回一个非零值表示开始遍历
      }
      else //遍历过程中
      { 
        if (task == &init_task )    //重新回到初始地址,退出
            return NULL;
        return (void*)pos   ;//否则返回一个非零值
      }  
    }
    static int my_seq_show(struct seq_file *m, void *v)
    {//获取进程的相关信息
      //printk(KERN_INFO"Invoke show\n");
      //输出进程序号              
      seq_printf( m,  "#%-3d\t ", taskcounts++ );  
      //输出进程pid?
      seq_printf( m, "%5d\t", task->pid );
      //输出进程state?
      seq_printf( m, "%lu\t", task->state );
      //输出进程名称(comm)?
      seq_printf( m, "%-15s ", task->comm );
      seq_puts( m, "\n" );                    
      return 0; 
    }
    
    static void * my_seq_next(struct seq_file *m, void *v, loff_t *pos)
    {
      //printk(KERN_INFO"Invoke next\n");  
      (*pos)++;   
      //task指向下一个进程?
      task = next_task(task);
      if(!strcmp(task->comm,var))
    {
      task = next_task(task);
    }
      return NULL;
    
    }
    static void my_seq_stop(struct seq_file *m, void *v)
    {
        //printk(KERN_INFO"Invoke stop\n");     
        // do nothing        
    }
    
    static struct seq_operations my_seq_fops = {//序列文件记录操作函数集合
            .start  = my_seq_start,
            .next   = my_seq_next,
            .stop   = my_seq_stop,
            .show   = my_seq_show
    };
    
    static int my_open(struct inode *inode, struct file *file)  
    {  
        return seq_open(file, &my_seq_fops); //打开序列文件并关联my_seq_fops
    }  
    
    static const struct file_operations my_proc =   
    {  //proc文件操作函数集合
        .owner      = THIS_MODULE,  
        .open       = my_open,
        .read       = seq_read,     
        .llseek     = seq_lseek,
        .release    = seq_release       
    }; 
    
    int __init my_init( void )
    {
        struct proc_dir_entry* my_proc_entry;
        printk( "<1>\nInstalling \'%s\' module\n", modname );
        printk( KERN_INFO"var=%s\n", var );
        my_proc_entry = proc_create(modname, 0x644, NULL, &my_proc);//生成proc文件
        if (NULL == my_proc_entry)
        {
            return -ENOMEM;
        }
        return  0;  //SUCCESS
    }
    
    void __exit my_exit( void )
    {
        remove_proc_entry( modname, NULL );//删除proc文件
        printk( "<1>Removing \'%s\' module\n", modname );   
    }
    
    module_init(my_init);
    module_exit(my_exit);
    MODULE_LICENSE("GPL"); 

    Screen Shot 2018-04-10 at 17.48.56

  • tasklist.c 打补丁 tasklist2.patch

  • ~/Desktop/module_test 文件夹下:

    $ make
    $ sudo insmod tasklist.ko var = "init"
    $ cat /proc/tasklist

    Screen Shot 2018-04-11 at 00.12.49

    注: init 进程就被隐藏了。

    注意:insmod 命令后一定要给 var 传递参数,否则可能会出现下面问题

    Screen Shot 2018-04-10 at 21.43.06

    解决办法是重启系统,重新加在模块(执行 insmod 命令)并记得给 var 传递参数。

  • 卸载 tasklist.ko 模块,重新 insmod 进内核,var 赋个空字符串。init 进程就显示了:

    Screen Shot 2018-04-11 at 00.15.27

  • 上述办法是在执行 insmod 命令时,给变量 var (进程名)赋值。归根结底是 tasklist.c 文件中变量 var 未初始化造成的,另一种办法是初始化 tasklist.c 文件中的变量 var 。比如:

    static char* var; // 将此行替换为下面这行
    static char* var = " "; // 给 var 初始化为空串

    然后:

    $ make
    $ sudo insmod tasklist.ko // 此处可不给var传参,因为代码中已经初始化了
    $ cat /proc/tasklist // 显示全部进程
    $ sudo rmmod tasklist.ko // 卸载tasklist.ko模块
    $ sudo insmod tasklist.ko var = "输入你想隐藏的进程名"
    $ cat /proc/tasklist // 发现指定的进程已被隐藏,显示隐藏的进程请看前一步

Reference

Linux下使用diff和patch制作及打补丁(已经实践可行!)

猜你喜欢

转载自www.cnblogs.com/narisu/p/9057853.html