Linux 访问进程地址空间函数 access_process_vm
文章目录
- 一、源码解析
- 二、Linux内核 用途
- 2.1 ptrace请求
- 2.2 进程的命令行
- 参考资料
一、源码解析
/*** get_task_mm - acquire a reference to the task's mm** Returns %NULL if the task has no mm. Checks PF_KTHREAD (meaning* this kernel workthread has transiently adopted a user mm with use_mm,* to do its AIO) is not set and if so returns a reference to it, after* bumping up the use count. User must release the mm via mmput()* after use. Typically used by /proc and ptrace.*/
struct mm_struct *get_task_mm(struct task_struct *task)
{struct mm_struct *mm;task_lock(task);mm = task->mm;if (mm) {if (task->flags & PF_KTHREAD)mm = NULL;elseatomic_inc(&mm->mm_users);}task_unlock(task);return mm;
}
EXPORT_SYMBOL_GPL(get_task_mm);
/** Access another process' address space.* Source/target buffer must be kernel space,* Do not walk the page table directly, use get_user_pages*/
int access_process_vm(struct task_struct *tsk, unsigned long addr,void *buf, int len, int write)
{struct mm_struct *mm;int ret;mm = get_task_mm(tsk);if (!mm)return 0;ret = __access_remote_vm(tsk, mm, addr, buf, len, write);mmput(mm);return ret;
}
其功能主要在__access_remote_vm函数:
/** Access another process' address space as given in mm. If non-NULL, use the* given task for page fault accounting.*/
static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,unsigned long addr, void *buf, int len, int write)
{struct vm_area_struct *vma;void *old_buf = buf;down_read(&mm->mmap_sem);/* ignore errors, just check how much was successfully transferred */while (len) {int bytes, ret, offset;void *maddr;struct page *page = NULL;ret = get_user_pages(tsk, mm, addr, 1,write, 1, &page, &vma);if (ret <= 0) {/** Check if this is a VM_IO | VM_PFNMAP VMA, which* we can access using slightly different code.*/
#ifdef CONFIG_HAVE_IOREMAP_PROTvma = find_vma(mm, addr);if (!vma || vma->vm_start > addr)break;if (vma->vm_ops && vma->vm_ops->access)ret = vma->vm_ops->access(vma, addr, buf,len, write);if (ret <= 0)
#endifbreak;bytes = ret;} else {bytes = len;offset = addr & (PAGE_SIZE-1);if (bytes > PAGE_SIZE-offset)bytes = PAGE_SIZE-offset;maddr = kmap(page);if (write) {copy_to_user_page(vma, page, addr,maddr + offset, buf, bytes);set_page_dirty_lock(page);} else {copy_from_user_page(vma, page, addr,buf, maddr + offset, bytes);}kunmap(page);page_cache_release(page);}len -= bytes;buf += bytes;addr += bytes;}up_read(&mm->mmap_sem);return buf - old_buf;
}
__access_remote_vm 函数用于访问另一个进程的地址空间。以下是对该函数的中文说明:
函数开始时使用 down_read(&mm->mmap_sem) 获取内存描述符的 mmap 信号量的读取锁。这确保了同步,并防止对内存映射的并发修改。
接着,函数进入一个循环,直到传输完整个长度 len 的数据或发生错误为止。
在循环内部,函数调用 get_user_pages 来从目标进程的地址空间中检索与给定地址 addr 对应的页面。get_user_pages 函数尝试锁定页面并检索它们。如果返回值 ret 小于等于零,则表示出现错误或地址空间的末尾。
如果 ret 小于等于零,则函数检查地址处的虚拟内存区域(VMA)是否是一种特殊类型(VM_IO | VM_PFNMAP),需要使用不同的访问方式。这个检查用于处理无法直接访问页面的特定情况。如果 VMA 是特殊类型,并且有关联的 access 函数,则调用该函数执行访问操作。如果 access 函数返回的值也是非正数,则终止循环。
如果页面检索成功(ret 是正数),函数继续执行数据传输操作。它根据剩余长度 len 和当前页面的偏移量计算要传输的字节数。然后使用 kmap 将页面映射到内核地址空间以进行直接访问。
如果设置了 write 标志,函数使用 copy_to_user_page 将数据从缓冲区 buf 复制到指定地址 addr 处的映射页面。它还使用 set_page_dirty_lock 将页面标记为脏页。
如果未设置 write 标志,函数使用 copy_from_user_page 将数据从映射页面复制到指定地址 addr 处的缓冲区 buf。
在完成数据传输后,函数使用 kunmap 解除页面在内核地址空间的映射,并使用 page_cache_release 释放页面。
然后,函数更新下一次循环迭代的剩余长度 len、缓冲区指针 buf 和地址 addr。
循环结束后,函数使用 up_read(&mm->mmap_sem) 释放对 mmap 信号量的读取锁。
最后,函数返回传输的字节数(buf - old_buf),表示成功传输的总大小。
__access_remote_vm 函数的作用是允许在内核中访问另一个进程的地址空间。通过该函数,内核可以直接读取或写入指定进程的内存数据,而无需通过用户空间或进程间通信来实现。
这种功能在某些情况下非常有用,例如:
(1)调试:允许调试器在内核级别访问目标进程的内存,以查看其状态、变量值和数据结构。
(2)进程间通信:某些进程间通信机制可能需要在内核中进行数据交换,例如通过共享内存或管道进行高效的数据传输。
(3)内核模块开发:内核模块可能需要读取或修改其他进程的内存数据,以实现特定的功能或扩展性。
(4)效率优化:在某些情况下,直接在内核中访问另一个进程的内存可以提高性能,避免了用户空间和内核空间之间的数据复制开销。
二、Linux内核 用途
2.1 ptrace请求
PTRACE_PEEKTEXT和PTRACE_PEEKDATA是用于在被跟踪进程的内存中读取数据的ptrace系统调用的请求选项。
PTRACE_POKETEXT和PTRACE_POKEDATA是用于向被跟踪进程的内存写入数据的ptrace系统调用的请求选项。
这两个选项父进程读取子进程内存地址空间数据或者写数据到子进程内存地址空间都是用到了access_process_vm函数。
SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr,unsigned long, data)
{struct task_struct *child;child = ptrace_get_task_struct(pid);arch_ptrace(child, request, addr, data);
}
long arch_ptrace(struct task_struct *child, long request,unsigned long addr, unsigned long data)
{ptrace_request(child, request, addr, data);
}
int ptrace_request(struct task_struct *child, long request,unsigned long addr, unsigned long data)
{switch (request) {case PTRACE_PEEKTEXT:case PTRACE_PEEKDATA:return generic_ptrace_peekdata(child, addr, data);case PTRACE_POKETEXT:case PTRACE_POKEDATA:return generic_ptrace_pokedata(child, addr, data);}
}
generic_ptrace_peekdata用来读取子进程的虚拟地址空间地址:
int generic_ptrace_peekdata(struct task_struct *tsk, unsigned long addr,unsigned long data)
{unsigned long tmp;int copied;copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), 0);if (copied != sizeof(tmp))return -EIO;return put_user(tmp, (unsigned long __user *)data);
}
对于读取子进程的虚拟地址空间,调用access_process_vm时,第五个参数 write = 0,代表是从该进程的虚拟地址空间读取内容。
函数的主要逻辑如下:
(1)声明一个局部变量 tmp,用于存储从目标进程内存中读取的数据。
(2)调用 access_process_vm 函数,传递目标进程的 task_struct 指针(tsk)、目标虚拟地址(addr)、存储数据的缓冲区指针(&tmp)、要读取的数据大小(sizeof(tmp))和读取标志(0)。
(3)access_process_vm 函数尝试访问目标进程的内存,并将读取的数据存储到 tmp 变量中。它返回实际复制的字节数。
(4)检查返回的 copied 是否等于 sizeof(tmp),即判断是否成功复制了全部数据。如果不等于,则表示读取失败,返回错误码 -EIO。
(5)调用 put_user 函数,将 tmp 变量的值复制到用户空间的 data 变量中。
(6)返回复制操作的结果。
这个函数的作用是从另一个进程的指定虚拟地址处读取数据,并将其存储到 data 变量中。它使用了 access_process_vm 函数来访问目标进程的内存,并使用 put_user 函数将读取的数据复制到用户空间。
generic_ptrace_pokedata用来写入数据到子进程的虚拟地址空间地址:
int generic_ptrace_pokedata(struct task_struct *tsk, unsigned long addr,unsigned long data)
{int copied;copied = access_process_vm(tsk, addr, &data, sizeof(data), 1);return (copied == sizeof(data)) ? 0 : -EIO;
}
对于写入数据到子进程的虚拟地址空间地址,调用access_process_vm时,第五个参数 write = 1,代表是向该进程的虚拟地址空间写入新内容。
函数的主要逻辑如下:
(1)声明一个局部变量 copied,用于存储复制的字节数。
(2)调用 access_process_vm 函数,传递目标进程的 task_struct 指针(tsk)、目标虚拟地址(addr)、源数据的指针(&data)、要复制的数据大小(sizeof(data))和写入标志(1)。
(3)access_process_vm 函数尝试访问目标进程的内存,并将源数据复制到目标进程的指定地址处。它返回实际复制的字节数。
(4)检查返回的 copied 是否等于 sizeof(data),即判断是否成功复制了全部数据。如果相等,则表示写入操作成功,返回0;否则,返回错误码 -EIO。
这个函数的作用是向另一个进程的指定虚拟地址处写入数据。它使用了 access_process_vm 函数来访问目标进程的内存,并尝试将源数据复制到目标进程中。函数返回0表示写入操作成功,返回非零错误码表示写入失败。
2.2 进程的命令行
该函数用来获取进程的命令行:
static int proc_pid_cmdline(struct task_struct *task, char * buffer)
{int res = 0;unsigned int len;struct mm_struct *mm = get_task_mm(task);if (!mm)goto out;if (!mm->arg_end)goto out_mm; /* Shh! No looking before we're done */len = mm->arg_end - mm->arg_start;if (len > PAGE_SIZE)len = PAGE_SIZE;res = access_process_vm(task, mm->arg_start, buffer, len, 0);// If the nul at the end of args has been overwritten, then// assume application is using setproctitle(3).if (res > 0 && buffer[res-1] != '\0' && len < PAGE_SIZE) {len = strnlen(buffer, res);if (len < res) {res = len;} else {len = mm->env_end - mm->env_start;if (len > PAGE_SIZE - res)len = PAGE_SIZE - res;res += access_process_vm(task, mm->env_start, buffer+res, len, 0);res = strnlen(buffer, res);}}
out_mm:mmput(mm);
out:return res;
}
proc_pid_cmdline用于获取指定进程的命令行参数。
struct mm_struct {.....unsigned long arg_start, arg_end, env_start, env_end;.....
}
struct mm_struct 用于描述进程的地址空间,该结构体包含了和进程地址空间有关的全部细信息。每个进程都有一个对应的 mm_struct 结构体,它存储了进程的地址空间布局、内存映射、页表等关键信息。
arg_start、arg_end、env_start 和 env_end 是用于描述进程的命令行参数和环境变量的起始地址和结束地址。以便访问和操作进程的命令行参数和环境变量。
函数的主要逻辑如下:
(1)调用 get_task_mm 函数获取目标进程的内存描述符(mm_struct)。
(2)计算命令行参数的长度,即 arg_end - arg_start。如果长度超过了页面大小(PAGE_SIZE),将长度截断为页面大小。
(3)调用 access_process_vm 函数,传递目标进程的 task_struct 指针(task)、命令行参数的起始地址(mm->arg_start)、存储命令行参数的缓冲区指针(buffer)、要复制的数据大小(len)和读取标志(0)。
(4)检查返回的 res 是否大于0,并且缓冲区中最后一个字节是否不等于空字符(‘\0’),以及长度是否小于页面大小。如果满足这些条件,说明可能发生了 setproctitle 的情况,需要进一步处理:
使用 strnlen 函数在缓冲区中查找第一个空字符(‘\0’),并将其位置存储在 len 变量中。
如果找到的空字符位置小于 res,说明命令行参数中存在空字符,将其作为有效长度。
否则,计算环境变量的长度(env_end - env_start)。
如果环境变量长度超过了剩余缓冲区的大小(PAGE_SIZE - res),将长度截断为剩余缓冲区的大小。
调用 access_process_vm 函数,从环境变量的起始地址(mm->env_start)开始,将环境变量的内容复制到缓冲区中(buffer+res),并返回实际复制的字节数。
使用 strnlen 函数在缓冲区中查找第一个空字符(‘\0’),并将其位置存储在 res 变量中。
这个函数的作用是获取指定进程的命令行参数,并将其存储在提供的缓冲区中。它首先获取进程的内存描述符,然后通过访问进程的内存来获取命令行参数。如果发现命令行参数中的空字符被覆盖,它还会尝试获取环境变量并将其添加到缓冲区中。最后,函数返回存储的命令行参数的长度。
参考资料
Linux 3.10.0
相关文章:
Linux 访问进程地址空间函数 access_process_vm
文章目录 一、源码解析二、Linux内核 用途2.1 ptrace请求2.2 进程的命令行 参考资料 一、源码解析 /*** get_task_mm - acquire a reference to the tasks mm** Returns %NULL if the task has no mm. Checks PF_KTHREAD (meaning* this kernel workthread has transiently a…...

selenium 动态爬取页面使用教程以及使用案例
Selenium 介绍 概述 Selenium是一款功能强大的自动化Web浏览器交互工具。它可以模拟真实用户在网页上的操作,例如点击、滚动、输入等等。Selenium可以爬取其他库难以爬取的网站,特别是那些需要登录或使用JavaScript的网站。Selenium可以自动地从Web页面…...

小程序中如何查看会员的积分和变更记录
积分是会员卡的一个重要功能,可以用于激励会员消费和提升用户粘性。在小程序中,商家可以方便地查看会员卡的积分和变更记录,以便更好地了解会员的消费行为和积分变动情况。下面将介绍如何在小程序中查看会员卡的积分和变更记录。 1. 找到指…...
音视频 ffmpeg命令直播拉流推流
直播拉流 ffplay rtmp://server/live/streamName ffmpeg -i rtmp://server/live/streamName -c copy dump.flv对于不是rtmp的协议 -c copy要谨慎使用 直播推流 ffmpeg -re -i out.mp4 -c copy flvrtmp://server/live/streamName参数:-re,表示按时间戳读取文件 参…...

Python钢筋混凝土结构计算.pdf-T001-混凝土强度设计值
以下是使用Python求解上述问题的完整代码: # 输入参数 f_ck 35 # 混凝土的特征抗压强度(单位:MPa) f_cd 25 # 混凝土的强度设计值(单位:MPa) # 求解安全系数 gamma_c f_ck / f_cd # …...

长风破浪会有时,直挂云帆济沧海!(工作室年会总结)
前言 我也是有段时间没写过总结性的博客了。最近是很忙的,尤其是年会那两天,我甚至可以说这是我这辈子目前最忙的两天。但这段经历还是很值得我记录下来的,也是给后面有需要的人提供的一些建议。我个人也是第一次筹办这种大型些的活动&#x…...

(数字图像处理MATLAB+Python)第十一章图像描述与分析-第五、六节:边界描述和矩描述
文章目录 一:边界描述(1)边界链码A:概述B:边界链码改进C:程序 (2)傅里叶描绘子A:概述B:程序 二:矩描述(1)矩A:…...

Redis之bigkey问题解读
目录 什么是bigkey? bigkey引发的问题 如何查找bigkey redis-cli --bigkeys MEMORY USAGE bigKey如何删除 渐进式删除 unlink bigKey生产调优 什么是bigkey? bigkey简单来说就是存储本身的key值空间太大,或者hash,list&…...
ElementUI浅尝辄止27:Steps 步骤条
引导用户按照流程完成任务的分步导航条,可根据实际应用场景设定步骤,步骤不得少于 2 步。 1.如何使用? 设置active属性,接受一个Number,表明步骤的 index,从 0 开始。需要定宽的步骤条时,设置s…...
React 18 迁移状态逻辑至 Reducer 中
参考文章 迁移状态逻辑至 Reducer 中 对于拥有许多状态更新逻辑的组件来说,过于分散的事件处理程序可能会令人不知所措。对于这种情况,可以将组件的所有状态更新逻辑整合到一个外部函数中,这个函数叫作 reducer。 使用 reducer 整合状态逻…...
【SA8295P 源码分析】89 - QNX AIS Camera qcarcam_test 可执行程序 main() 函数 源代码流程分析
【SA8295P 源码分析】89 - QNX AIS Camera qcarcam_test 可执行程序 main 函数 源代码流程分析 一、qcarcam_test.cpp main() 函数源码分析二、qcarcam_test_setup_input_ctxt_thread( ) :初始化 gCtxt.inputs[input_idx] 上下文环境三、process_cb_event_thread( ) :负责处理…...

STM32屏幕计时器
目录 一、最终效果二、实现思想三、实现过程3.1 屏幕显示3.2 中断处理 一、最终效果 显示屏显示计时时间,格式为 00:00:00,依次为 时:分:秒,程序运行之后自动计时,当按下按键,计时清零,按下按键采用外部中…...

MRI多任务技术及应用
目录 一、定量心血管磁共振成像(CMR)的改进方法二、磁共振多任务三、磁共振多任务的成像框架四、磁共振多任务的图像模型和采样和重建策略五、利用MR多任务进行快速三维稳态CEST(ss-CEST)成像5.1 利用MR多任务进行快速三维稳态CEST(ss-CEST)成像介绍5.2 …...

app自动化测试(Android)
Capability 是一组键值对的集合(比如:"platformName": "Android")。Capability 主要用于通知 Appium 服务端建立 Session 需要的信息。客户端使用特定语言生成 Capabilities,最终会以 JSON 对象的形式发送给 …...

【力扣每日一题】2023.9.3 消灭怪物的最大数量
目录 题目: 示例: 分析: 代码: 题目: 示例: 分析: 题目比较长,我概括一下就是有一群怪物,每只怪物离城市的距离都不一样,并且靠近的速度也不一样&#x…...

Python入门教程 | Python3 列表(List)
Python3 列表 序列是 Python 中最基本的数据结构。 序列中的每个值都有对应的位置值,称之为索引,第一个索引是 0,第二个索引是 1,依此类推。 Python 有 6 个序列的内置类型,但最常见的是列表和元组。 列表都可以进…...

Java低代码开发:jvs-list(列表引擎)功能(一)配置说明
在低代码开发平台中,列表页是一个用于显示数据列表的页面。它通常用于展示数据库中的多条记录,并提供搜索、排序和筛选等功能,以方便用户对数据进行查找和浏览。 jvs-list是jvs快速开发平台的列表页的配置引擎,它和普通的crud 具…...

UI自动化之关键字驱动
关键字驱动框架:将每一条测试用例分成四个不同的部分 测试步骤(Test Step):一个测试步骤的描述或者是测试对象的一个操作说明测试步骤中的对象(Test Object):指页面的对象或者元素对象执行的动…...
前端高性能渲染 — 虚拟列表
虚拟列表,实际上就是在首屏加载的时候,只加载可视区域内需要的列表项,当滚动发生时,动态通过计算获得可视区域内的列表项,并将非可视区域内存在的列表项删除。该技术是解决渲染大量数据的一种解决方法。 实现虚拟列表&…...

防水出色的骨传导耳机,更适合户外运动,南卡Runner Pro 4S体验
已经接近尾声的夏季依然酷热,对于运动爱好者来说,这确实也是锻炼的好时机,无论是一会儿就能大汗淋漓的HIIT,还是是各种清凉的水上运动,在健身的同时,戴上一副耳机享受音乐,都会更加痛快一些。 相…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...

TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...

智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...

人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...