Linux内核之struct pt_regs结构

前沿

       项目开发最近进行系统hook功能实现相关业务,主要在centos7和8系列环境开发下关功能。调研了相关知识点,发现在系统7和8上内核版本差别比较大,7-3.10.x系列版本,8-4.18.x系列版本。依据两个系统的内核情况根对应的内核符号表进行数据业务的钩子功能开发。开发过程发现两个系统虽然都定义对应的钩子目标函数定义,但是实际过程发现在7系统上直接定义于目标函数相同的函数进行hook即可,而系统8系列可以直接将参数定义成pt_regs进行处理。那么问题来了,这个结构是个什么?有什么用?如何使用?带着这么几个问题来逐个来介绍。

1.struct pt_regs是什么

        pt_regs内核定义一个结构,其文件定义路径通常为arch/x86/include/asm/ptrace.h,如下结构定义:

(__i386__)

struct pt_regs {unsigned long bx;// bx	%ebx	通用寄存器,通常用作基址寄存器(Base Register),保存数据或地址。unsigned long cx;// cx	%ecx	通用寄存器,常用作计数器(Counter),如循环计数或字符串操作。unsigned long dx;// dx	%edx	通用寄存器,通常与 %eax 配合使用,存放数据或 I/O 端口地址。unsigned long si;// si	%esi	源索引寄存器(Source Index),用于内存操作的源地址(如 movs 指令)。unsigned long di;// di	%edi	目的索引寄存器(Destination Index),用于内存操作的目的地址。unsigned long bp;// bp	%ebp	基址指针寄存器(Base Pointer),指向当前栈帧的基地址,用于函数调用。unsigned long ax;// ax	%eax	累加寄存器(Accumulator),用于算术运算和系统调用的返回值。unsigned long ds;// ds	%ds	数据段寄存器(Data Segment),指向当前数据段的段选择子。unsigned long es;// es	%es	附加段寄存器(Extra Segment),用于某些内存操作(如字符串操作)。unsigned long fs;// fs	%fs	附加段寄存器,Linux 内核中通常用于线程本地存储(TLS)或特定内核用途。unsigned long gs;// gs	%gs	附加段寄存器,用途与 %fs 类似,可能用于特定扩展。unsigned long orig_ax;// orig_ax	-	原始系统调用号或中断错误码:- 系统调用时保存系统调用号(如 __NR_read)。- 中断或异常时保存错误码或中断向量号。unsigned long ip;// ip	%eip	指令指针寄存器(Instruction Pointer),指向下一条要执行的指令地址。unsigned long cs;// cs	%cs	代码段寄存器(Code Segment),保存当前代码段的段选择子。unsigned long flags;// flags	%eflags	标志寄存器,保存 CPU 状态标志(如中断使能、方向标志、溢出标志等)。unsigned long sp;// sp	%esp	栈指针寄存器(Stack Pointer),指向当前栈顶地址。unsigned long ss;// ss	%ss	栈段寄存器(Stack Segment),指向当前栈段的段选择子。
};

x86_64位:

struct pt_regs {unsigned long r15; // r15	%r15	通用寄存器,通常用于保存临时数据或地址。在系统调用中可能作为第 6 个参数(若需要)。unsigned long r14; // r14	%r14	通用寄存器,用途同上。unsigned long r13; // r13	%r13	通用寄存器,用途同上。unsigned long r12; // r12	%r12	通用寄存器,用途同上。unsigned long bp; // bp	%rbp	基址指针寄存器,指向当前栈帧的基地址,用于函数调用栈回溯。unsigned long bx; // bx	%rbx	基址寄存器,常用于保存数据或地址(如内存操作的基地址)。unsigned long r11; // r11	%r11	通用寄存器,可能用于临时存储或特定指令(如 syscall 指令会破坏 %r11)。unsigned long r10; // r10	%r10	通用寄存器,在系统调用中作为第 4 个参数(若需要)。unsigned long r9; // r9	%r9	通用寄存器,在系统调用中作为第 5 个参数(若需要)。unsigned long r8; // r8	%r8	通用寄存器,在系统调用中作为第 6 个参数(若需要)。unsigned long ax; // ax	%rax	累加寄存器,用于系统调用的返回值(ret 前内核会设置 regs->ax)。unsigned long cx; // cx	%rcx	计数器寄存器,在 syscall 指令中保存返回地址(被破坏),某些场景下可能作为第 4 个参数。unsigned long dx; // dx	%rdx	数据寄存器,通常用于系统调用的第 3 个参数。unsigned long si; // si	%rsi	源索引寄存器,通常用于系统调用的第 2 个参数。unsigned long di; // di	%rdi	目的索引寄存器,通常用于系统调用的第 1 个参数。unsigned long orig_ax; // orig_ax-原始系统调用号或中断错误码:- 系统调用时保存系统调用号(如 __NR_read),- 中断或异常时保存中断向量号或错误码。unsigned long ip; // ip	%rip	指令指针寄存器,指向触发中断/异常/系统调用的下一条指令地址(即用户空间的返回地址)。unsigned long cs; // cs	%cs	代码段寄存器,保存当前代码段的段选择子(用户态为 0x33,内核态为 0x10)。unsigned long flags; // flags	%rflags	标志寄存器,保存 CPU 状态标志(如中断使能、方向标志、溢出标志等)。unsigned long sp; // sp	%rsp	栈指针寄存器,指向用户空间的栈顶地址。unsigned long ss; // ss	%ss	栈段寄存器,保存当前栈段的段选择子(用户态为 0x2b)。
};

 上述两个结构均为x86架构定义,但是实际次做中发现在arm重有偏差,这里简要列出结构,后有文章详细介绍相关用途,结构如下:

arm64架构pt_regs结构定义:

/** This struct defines the way the registers are stored on the stack during an* exception. Note that sizeof(struct pt_regs) has to be a multiple of 16 (for* stack alignment). struct user_pt_regs must form a prefix of struct pt_regs.*此结构定义了异常期间寄存器在堆栈上的存储方式。需要注意的是,sizeof(struct pt_regs)必须是16的倍数(用于堆栈对齐)。*另外,结构user_pt_regs必须构成结构pt_regs的前缀。*/struct pt_regs {union {struct user_pt_regs user_regs;struct {u64 regs[31];  // X0-X30(通用寄存器)系统调用参数通过 regs[0]-regs[7](X0-X7)传递。u64 sp;        // 栈指针 (SP_EL0) 保存用户空间栈顶地址,用于恢复用户态执行。u64 pc;        // 程序计数器 (PC)保存触发中断/异常/系统调用的指令地址(即返回地址)。u64 pstate;    // 处理器状态 (PSTATE)};};u64 orig_x0; //保存系统调用 第一个参数的原始值。某些系统调用(如 restart_syscall)需要恢复原始参数
#ifdef __AARCH64EB__u32 unused2;s32 syscallno; //在传统 ARM64 系统调用中,系统调用号通过 X8 寄存器传递,但内核可能将其复制到 syscallno 字段。
#elses32 syscallno;u32 unused2;
#endifu64 orig_addr_limit; //保存用户态进程的地址访问限制(如 USER_DS)。在内核态执行时临时扩大地址限制(如 KERNEL_DS),执行完毕后恢复/* Only valid when ARM64_HAS_IRQ_PRIO_MASKING is enabled. */u64 pmr_save; //中断优先级掩码保存:仅当启用 ARM64_HAS_IRQ_PRIO_MASKING 时有效。保存中断处理前的 PMR 值,处理完成后恢复。u64 stackframe[2]; //父函数的帧指针(FP)。父函数的返回地址(LR)。
};

2.pt_regs有什么用

       通过该结构,当需要进行内核函数hook时,可以简化对应目标钩子函数的定义以及接口函数的标准化,避免大量的函数指针来制定具体的函数定义(参数个数和参数类型差别)。从而统一接口类型,使钩子函数实现简化执行。例如:

原函类型:

typedef asmlinkage long (*sys_call_file_mkdirat_t)(int dfd, const char __user * pathname, umode_t mode);
static sys_call_file_mkdirat_t old_sys_mkdirat;typedef asmlinkage long (*sys_call_file_unlinkat_t)(int dfd, const char __user * pathname, int flag);
static sys_call_file_unlinkat_t old_sys_unlinkat;

pt_regs:

typedef asmlinkage long (*sys_call_file_t)(const struct pt_regs *);static sys_call_file_t[2];// 统一hook函数接口,具体参数依据实际函数定义从regs结构中按照寄存器位获取。// 例如
static sys_call_file_t[2]; // 初始化是会将需要hook的函数地址进行存储处理asmlinkage long mkdir_hook(const struct pt_regs *regs)
{int dfd = regs->di;char *filename = (char *)regs->si;umode_t mode = regs->dx;sys_call_file_t origin_mkdirat = sys_call_file_t[__NR_mkdirat]; //获取hook后的原函数地址//todo ...return origin_mkdirat(regs); // 原函数}asmlinkage long unlinkat_hook(const struct pt_regs *regs)
{int dfd = regs->di;char *filename = (char *)regs->si;int flag = regs->dx;sys_call_file_t origin_unlinkat = sys_call_file_t[__NR_unlinkat]; //获取hook后的原函数地址//todo ...return origin_unlinkat(regs); // 原函数
}

3.pt_regs如何使用

例如__NR_write对应32位机器调用内核函数原型如下:

asmlinkage long sys_write(unsigned int fd, const char __user *buf,size_t count);

通过自定义函数结合pt_regs进行hook处理:

asmlinkage long self_write_hook(const struct pt_regs* regs)
{int fd = regs->bx;           // 第一个参数 fd 通过bx 传递char *buf = (char *)regs->cx;// 第二个参数 buf 通过ecxsize_t count = regs->dx;     // 第三个参数 count 通过edx// to ...return origin_read(regs); // origin_read为hook时存储的原始函数地址
}

上述为针对sys_write函数的hook调用时通过regs结构来实现相关参数的获取处理。

4.实际使用案例

    通过对openat为例子进行hook,具体代码如下:

my_openat_hook.c:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/unistd.h>
#include <linux/sched.h>
#include <linux/syscalls.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/fdtable.h>
#include <linux/uaccess.h>
#include <linux/kallsyms.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/kprobes.h>
#include <linux/fs_struct.h>
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/namei.h>
#include <asm/syscall.h>
#include <uapi/linux/mount.h>//符号表获取
typedef unsigned long (*kallsyms_lookup_name_t)(const char *name);
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;
}static unsigned long sys_call_table_ptr;
typedef asmlinkage long (*self_openat_hook_t)(const struct pt_regs*);
static self_openat_hook_t old_self_openat;
unsigned int disable_cr0(void)
{unsigned int cr0 = 0;unsigned int ret;asm volatile ("movq %%cr0, %%rax": "=a"(cr0));ret = cr0;cr0 &= 0xfffeffff;asm volatile ("movq %%rax, %%cr0"::"a"(cr0));return ret;
}
void enable_cr0(unsigned int val)
{asm volatile ("movq %%rax, %%cr0": : "a"(val));
}asmlinkage long self_openat_hook(const struct pt_regs* regs)
{int dfd = regs->di;char* filename = (char*)regs->si;int flag = regs->dx;umode_t mode = regs->r10;printk(KERN_INFO "dfd = %d,filename = %s,flag = %d,mode = %d",dfd,filename,flag,mode);return old_self_openat(regs);
}static int __init self_init(void) 
{int cr0;obtain_sys_call_table_addr(&sys_call_table_ptr);cr0 = disable_cr0();old_self_openat = (self_openat_hook_t)((unsigned long*)sys_call_table_ptr)[__NR_openat];// 保留旧函数((unsigned long*)sys_call_table_ptr)[__NR_openat] = (unsigned long)self_openat_hook; // 设置新函数enable_cr0(cr0);printk("Success to hook openat func !!!");return 0;
}static void __exit self_exit(void) {int cr0;cr0 = disable_cr0();((unsigned long*)sys_call_table_ptr)[__NR_openat] = old_self_openat;enable_cr0(cr0);printk("Exit to hook openat func !!!");return;
}module_init(self_init)
module_exit(self_exit)
MODULE_LICENSE("GPL");

Makefile:

obj-m := my_openat_hook.oPWD := $(shell pwd)KERNEL_DIR := "/lib/modules/$(shell uname -r)/build"EXTRA_CFLAGS += -I$(src)/includemodules:@$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modulesclean:@rm -rf *.ko *.o *.mod.c *symvers *order .*cmd *cmd

将ko文件写入到系统:

insmod my_openat.ko,通过dmesg -w查看系统日志:


 以上为使用pt_retgs结构实现的内核函数hook功能案例。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/80275.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《从混乱到有序:ArkUI项目文件结构改造指南》

在ArkUI开发的广袤天地里&#xff0c;构建一个清晰、有序的文件结构&#xff0c;是打造优质应用的关键。一个合理的文件结构&#xff0c;就像为开发者精心绘制的地图&#xff0c;在项目的各个阶段&#xff0c;都能提供明确的指引&#xff0c;让开发过程顺畅无阻。今天&#xff…

C#基于Sunnyui框架和MVC模式实现用户登录管理

C#基于Sunnyui框架和MVC模式实现用户登录管理 1 Controller1.1 UserManagementController.cs&#xff08;控制器入口&#xff09; 2 Model2.1 UserRepository.cs&#xff08;用户管理模型&#xff09;2.2 User.cs&#xff08;用户结构体&#xff09;2.3 SQLiteHelper.cs&#x…

自然语言处理(NLP)技术的实例

自然语言处理&#xff08;NLP&#xff09;技术在各个领域都有广泛的应用&#xff0c;以下是几个例子&#xff1a; 语音识别&#xff1a;通过NLP技术&#xff0c;计算机可以识别和理解语音指令&#xff0c;例如智能助手如Siri和Alexa就是通过语音识别技术实现与用户的交互。 机…

Spring Boot实战(三十六)编写单元测试

目录 一、什么是单元测试&#xff1f;二、Spring Boot 中的单元测试依赖三、举例 Spring Boot 中不同层次的单元测试3.1 Service层3.2 Controller 层3.3 Repository层 四、Spring Boot 中 Mock、Spy 对象的使用4.1 使用Mock对象的背景4.2 什么是Mock对象&#xff0c;有哪些好处…

aws服务(四)文件存储服务S3 介绍使用代码集成

一、介绍 1、简介 Amazon S3 是 Amazon Web Services 提供的一种对象存储服务(Object Storage),用于在云中存储和检索任意数量的数据。它以高可用性、高扩展性和高持久性著称,非常适合用来存储网站资源、数据备份、日志文件、大数据、机器学习输入输出等。 2、主要特性 …

应用信息1.13.0发布

增加工具箱 增加启动器功能 增加布局查看器 增加手动安装和卸载应用 增加APK文件解析 增加应用多选功能 增加查看应用预装版本 增加应用信息和ADB命令导出 修复其它问题... 百度下载&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;1234

【Vue3 实战】插槽封装与懒加载

一、为什么需要插槽&#xff1f;从一个面板组件说起 在电商首页开发中&#xff0c;经常遇到这样的场景&#xff1a; 「新鲜好物」「人气推荐」同样类型模块都需要相同的标题栏&#xff0c;但内容区布局不同 这时候&#xff0c;插槽&#xff08;Slot&#xff09;就像一个「内容…

虚无隧穿产生宇宙(true nothing tunneling) 是谁提出的

是 亚历克斯.维连金 英文名&#xff08;alex vilenkin 或者 Alexander Vilenkin)提出来的。 “虚无隧穿产生宇宙”&#xff08;true nothing tunneling&#xff09;这一概念并非一个标准的物理学术语&#xff0c;它更像是对某些现代宇宙学理论的描述&#xff0c;尤其是涉及宇宙…

postgis:添加索引时提示“对访问方法 gist 数据类型 geometry 没有默认的操作符表“

问题 在对gis表的geom字段创建空间索引时&#xff0c;出现“对访问方法 "gist" 数据类型 geometry 没有默认的操作符表”的提示报错。 解决方案 按系列步骤进行排查并解决。 1.先确认已安装postgis -- 查看postgis版本 SELECT postgis_full_version() 若安装了则…

图论---Prim堆优化(稀疏图)

题目通常会提示数据范围&#xff1a; 若 V ≤ 500&#xff0c;两种方法均可&#xff08;朴素Prim更稳&#xff09;。 若 V ≤ 1e5&#xff0c;必须用优先队列Prim vector 存图。 #include <iostream> #include <vector> #include <queue> #include <…

代码随想录算法训练营第一天:数组part1

今日学习的文章链接和视频链接 ● 自己看到题目的第一想法 ● 看完代码随想录之后的想法 ● 自己实现过程中遇到哪些困难 ● 今日收获&#xff0c;记录一下自己的学习时长 状态 思路理解完成 30% 代码debug完成 60% 代码模板总结并抽象出来 100% 题目 704 二分查找 题目链接…

企业为何要求禁用缺省口令?安全风险及应对措施分析

在当今数字化时代&#xff0c;企业网络安全面临着前所未有的挑战。缺省口令的使用是网络安全中的一个重要隐患&#xff0c;许多企业在制定网络安全红线时&#xff0c;明确要求禁用缺省口令。本文将探讨这一要求的原因及其对企业安全的重要性。 引言&#xff1a;一个真实的入侵场…

PostgreSQL 中的权限视图

PostgreSQL 中的权限视图 PostgreSQL 提供了多个系统视图来查询权限信息&#xff0c;虽然不像 Oracle 的 DBA_SYS_PRIVS 那样集中在一个视图中&#xff0c;但可以通过组合以下视图获取完整的系统权限信息。 一 主要权限相关视图 Oracle 视图PostgreSQL 对应视图描述DBA_SYS_…

【防火墙 pfsense】1简介

&#xff08;1&#xff09; pfSense 有以下可能的用途&#xff1a; 边界防火墙 路由器 交换机 无线路由器 / 无线接入点 &#xff08;2&#xff09;边界防火墙 ->要充当边界防火墙&#xff0c;pfSense 系统至少需要两个接口&#xff1a;一个广域网&#xff08;WAN&#xff0…

数据库+Docker+SSH三合一!深度评测HexHub的全栈开发体验

作为一名技术博主&#xff0c;我最近一直被各种开发工具切换搞得焦头烂额。数据库要用Navicat&#xff0c;服务器管理得开Termius&#xff0c;Docker操作还得切到命令行&#xff0c;每天光在不同工具间切换就浪费了大量时间。直到团队里的一位架构师向我推荐了HexHub这个一体化…

第十天 Shader编程:编写简单表面着色器 Addressable资源管理系统 DOTS(面向数据技术栈)入门

前言 作为Unity初学者&#xff0c;在实现复杂场景时经常会遇到性能瓶颈。本文将带你通过四个关键技术的实战学习&#xff0c;掌握现代Unity开发的核心优化方案&#xff1a; Shader编程 - 编写表面着色器控制物体渲染Addressable系统 - 实现高效资源管理DOTS技术栈 - 解锁百万…

项目自动化测试

一.设计测试用例(细致全面) 二.先引入所需要的pom.xml依赖 1.selenium依赖 2.webdrivermanager依赖 3.commons-io依赖 编写测试用例–按照页面对用例进行划分,每个页面是Java文件,页面下的所有用例统一管理 三.common包(放入公用包) 类1utils 可以调用driver对象,访问url …

ap无法上线问题定位(交换机发包没有剥掉pvid tag)

一中学&#xff0c;新开的40台appoe交换机核心交换机旁挂ac出口路由的组网&#xff0c;反馈ap无法上线&#xff0c;让协助解决。 组网如下&#xff1a; 排查过程&#xff1a; 检查ac的配置&#xff0c;没有发现问题 发现配置没有问题&#xff0c;vlan1000配置子接口&#xff…

第十七届山东省职业院校技能大赛 中职组网络建设与运维赛项

第十七届山东省职业院校技能大赛 中职组网络建设与运维赛项 赛题 B 卷 第十七届山东省职业院校技能大赛中职组网络建设与运维赛项 1 赛题说明 一、竞赛项目简介 “网络建设与运维”竞赛共分为以下三个模块&#xff1a;  网络理论测试&#xff1b;  网络建设与调试&#xf…

关于QT信号、槽、槽函数的讲解

也是好久没有发帖子了&#xff0c;最近博主主要还是在边学QT边完成任务&#xff0c;所以进度很慢&#xff0c;但确实在这几天对于QT自身槽和信号这类特殊的机制有了一定简单的理解&#xff0c;所以还是想记录下来&#xff0c;如果有初学者看到帖子对他有一定的帮助&#xff0c;…