当前位置: 首页 > news >正文

Linux内核之文件驱动随笔

前言

        近期需要实现linux系统文件防护功能,故此调研了些许知识,如何实现文件防护功能从而实现针对文件目录防护功能。当被保护的目录,禁止增删改操作。通过内核层面实现相关功能,另外在通过跟应用层面交互从而实现具体的业务功能。好了,话不多说,直接开始本文的主题,详情请见下文(下文实现介绍为ubuntu18.x版本)。

一.查看系统内核函数定义

如上所示,在系统中,每个系统函数都定义一个函数对应的原型号,如上mkdir方法定义的类型宏号表示。红框中,当时终端使用mkdir,内核中最终通过_SYSCALL调用sys_mkdir方法。

sys_mkdir方法的定义:

如上为函数原型的定义,可通过上述的函数定义进行拦截从而实现相关的业务功能。(注:在新的系统架构中,现在都通过寄存器来实现相关的函数定义,例如:

asmlinkage long hook_sys_mkdir(struct pt_regs *regs) {char __user *pathname = (const char __user *)regs->di; // x86_64 的第一个参数umode_t mode = (umode_t)regs->si; // 第二个参数// 钩子逻辑printk("mkdir: path=%s, mode=%o\n", pathname, mode);// 调用原始系统调用return orig_sys_mkdir(regs);
}

上述的方法定义,省略了函数的个数定义,从而使系统的拦截调用更加的方便灵活,所需的参数直接从pt_regs结构中直接获取,灵活方便,不用再定义每个方法各自的参数个数以及相关的定义方法。 

二.系统拦截调用实现

        如下通过自定义钩子函数来实现系统函数mkdir的拦截调用步骤

        1.定义钩子函数

asmlinkage long hook_sys_mkdir(struct pt_regs *regs);
typedef asmlinkage long (*sys_mkdir_ptr)(struct pt_regs *regs);
static sys_mkdir_ptr origin_mkdir;

        2.获取系统函数符号表   

        注:不同系统可能获取符号表方式存在偏差,另外如果和应用层交互时,也可以通过应用层通过netlink将对应的符号表地址传入内核当中。  

kallsyms_lookup_name_t get_kallsyms_lookup_name(void)
{int ret;kallsyms_lookup_name_t pfun;static struct kprobe kp ={.symbol_name = "kallsyms_lookup_name",};ret = register_kprobe(&kp);if (ret < 0){printk(KERN_INFO "register_kprobe failed, returned %d\n", ret);return NULL;}pfun = (kallsyms_lookup_name_t)kp.addr;unregister_kprobe(&kp);return pfun;
}static int obtain_sys_call_table_addr(unsigned long *sys_call_table_addr)
{unsigned long temp_sys_call_table_addr;kallsyms_lookup_name_t fn_kallsyms_lookup_name = 0;fn_kallsyms_lookup_name = get_kallsyms_lookup_name();if (fn_kallsyms_lookup_name == NULL){printk("Fail to get_allsyms_lookup_name\n");return -1;}temp_sys_call_table_addr = fn_kallsyms_lookup_name("sys_call_table");/* Return error if the symbol doesn't exist */if (0 == temp_sys_call_table_addr){printk("Can not found sys_call_table\n");return -1;}printk("Found sys_call_table: %p", (void *)temp_sys_call_table_addr);*sys_call_table_addr = temp_sys_call_table_addr;return 0;
}

        3.进行钩子操作

        因系统具备写保护,进行hook时需要先关闭写保护,钩子完成之后再将写保护恢复(函数的hook放在如下disable_cr0和enable_cr0之间)。

写保护和恢复保护函数处理:   

unsigned int disable_cr0(void)
{unsigned int cr0 = 0;unsigned int ret;asm volatile ("movq %%cr0, %%rax": "=a"(cr0));ret = cr0;//清理第16位的标志位cr0 &= 0xfffeffff;asm volatile ("movq %%rax, %%cr0"::"a"(cr0));return ret;
}void enable_cr0(unsigned int val)
{asm volatile ("movq %%rax, %%cr0": : "a"(val));
}

        函数钩子更换

// sys_call_table_addr为上述步骤获取的函数符号表地址void set_hook(){disable_cr0();origin_mkdir = ((unsigned long *)(sys_call_table_addr))[__NR_mkdir];enable_cr0();
}

        4.mkdir钩子运行流程

        自定义函数进行对mkdir函数的拦截,进行自己的业务处理,正常情况再调用原始内核函数

        注:在某些系统上,可能在内核中调用了其他的函数,例如麒麟系统,mkdir命令,内核中调用的不是sys_mkdir,虽然可以通过上述查询到,但是实际上调用的是sys_mkdirat.所以在ko中直接进行sys_mkdirat的hook即可。如何查看某个命令内核真正调用哪个内核函数,可以通过strace命令进行查看,例如mkdir命令查看,如下:(ubuntu18.04)

root@ubuntu:~# strace -e trace=mkdir,mkdirat mkdir Test
mkdir("Test", 0777)                     = 0
+++ exited with 0 +++
root@ubuntu:~# 

如上,输出执行输出结果,mkdir,那么说明调用内核的sys_mkdir方法。 

     麒麟v10,aarch64架构,跟上述不一致,详情见:

root@ubuntu:~# strace -e trace=mkdir,mkdirat mkdir Test
mkdirat(AT_FDCWD,"Test", 0777)                     = 0
+++ exited with 0 +++

上述结果输出mkdirat,跟上述ubuntu差别很大,表示mkdir命令对应调用的是系统内核的sys_mkdirat函数。

综上两种情况,再编写系统拦截方法时,一定需要注意不同结构系统存在的偏差,依据实际情况而定再行决定处理相关hook方式函数。 

asmlinkage long origin_mkdir (struct pt_regs *regs)
{int ret;char *filename = NULL;char *regs_filename = (char *)(regs->di);int file_len = strnlen_user(regs_filename, MAX_PATH);filename = kmalloc(file_len, GFP_KERNEL);if (filename == NULL){printk("Fail to kmalloc\n");goto end;}ret = strncpy_from_user(filename, regs_filename, file_len);if (ret < 0){printk("Fail to strncpy_from_user\n");goto end;}printk("current opetation filename:  %s\n", filename);
end:if (filename){kfree(filename);}return old_sys_mkdir(regs);
}

        5.恢复系统原始函数   

        卸载该驱动文件记得恢复系统原始函数,避免发生崩溃问题。

// sys_call_table_addr为上述步骤获取的函数符号表地址void restore_hook()
{disable_cr0();((unsigned long *)(sys_call_table_addr))[__NR_mkdir] = origin_mkdir ;enable_cr0();
}

如上就是一个mkdir函数的完成hook过程的步骤,上述的分布代码将在如下第三标题中完美整合从而实现一个完整的调用并且实现生成一个ko文件可以进行使用。

相关文章:

  • Windows远程注入的一些问题
  • 从 0 到 1 打通 AI 工作流:Dify+Zapier 实现工具自动化调用实战
  • 25.4.22学习总结
  • Linux——基于socket编程实现简单的Tcp通信
  • 如何在 Java 中从 PDF 文件中删除页面(教程)
  • 删除不了jar包-maven clean package失败
  • 10.建造者模式:思考与解读
  • C++学习之游戏服务器开发十二nginx和http
  • Linux:简单自定义shell
  • 界面控件DevExpress WPF v25.1预览 - 支持Windows 11系统强调色
  • 【图像识别改名】如何批量识别多个图片的区域内容给图片改名,批量图片区域文字识别改名,基于WPF和腾讯OCR的实现方案和步骤
  • PLC互连全攻略:Profinet和EthernetIP实操演示
  • 极狐GitLab 项目功能和权限解读
  • GMS认证之 CTS Verifier认证新变化
  • 【前端】【业务逻辑】【面试】JSONP处理跨域原理与封装
  • Python 设计模式:回调模式
  • WebGis与WebGL是什么,两者之间的关系?
  • 【MCP Node.js SDK 全栈进阶指南】初级篇(6):MCP传输层配置与使用
  • 基于LightGBM-TPE算法对交通事故严重程度的分析与可视化
  • java 设计模式 原型模式
  • 新证据表明:地球水或为“自产”而非“外来”
  • 张又侠董军分别与印尼国防部长会见会谈
  • 《哪吒2》票房已达157亿,光线传媒一季度净利增至20亿元
  • 诸葛燕喃出任中央文化和旅游管理干部学院党委书记
  • 美国土安全部长餐厅遇窃,重要证件被盗走
  • 一条水脉串起七个特色区块,上海嘉定发布2025年新城行动方案