如何在内核模块中获取用户态程序SO库的基地址?
内核模块中获取用户态进程指定.so基地址的最优方式
一、内核原生遍历内存映射的高效方法
不需要依赖/proc文件系统,直接遍历目标进程的内存映射结构vm_area_struct——这是最直接高效的方式,毕竟/proc/[pid]/maps的内容本质就是从这些内核结构生成的。
实现步骤
- 通过PID定位目标进程的
task_struct,需用RCU锁避免进程退出导致的野指针问题。 - 获取进程的内存管理结构
mm_struct,并通过读锁保护内存映射链表的遍历。 - 遍历
mm->mmap链表中的每个vm_area_struct:- 检查
vm_file是否非空(说明该内存区域对应一个文件映射)。 - 通过
file_path()获取映射文件的路径,与目标.so文件名精确匹配。 - 匹配成功后,
vma->vm_start即为该.so在用户态的基地址。
- 检查
代码示例
#include <linux/sched.h> #include <linux/mm.h> #include <linux/fs.h> #include <linux/rculist.h> #include <linux/string.h> unsigned long get_target_so_base(pid_t target_pid, const char *target_so) { struct task_struct *task; struct mm_struct *mm; struct vm_area_struct *vma; char file_path[256]; unsigned long so_base = 0; const char *filename; rcu_read_lock(); // 通过PID查找进程task_struct task = pid_task(find_vpid(target_pid), PIDTYPE_PID); if (!task) { rcu_read_unlock(); return 0; } // 获取进程的mm_struct,需调用mmput释放 mm = get_task_mm(task); if (!mm) { rcu_read_unlock(); return 0; } // 加读锁保护vma链表,防止并发修改 down_read(&mm->mmap_sem); for (vma = mm->mmap; vma; vma = vma->vm_next) { if (vma->vm_file) { if (file_path(vma->vm_file, file_path, sizeof(file_path)) >= 0) { // 提取文件名(路径最后一个/之后的部分),避免子串匹配错误 filename = strrchr(file_path, '/'); if (filename && strcmp(filename + 1, target_so) == 0) { so_base = vma->vm_start; break; } } } } up_read(&mm->mmap_sem); mmput(mm); rcu_read_unlock(); return so_base; }
注意事项
- 必须严格持有锁:RCU锁保护
task_struct的访问,mmap_sem读锁保护内存映射链表的遍历,避免并发场景下的竞态问题。 - 文件名匹配用
strrchr提取路径末尾的文件名,比strstr更精确,防止误匹配包含目标so名的其他文件。
二、访问/proc/[pid]/maps的更优方式
如果一定要通过/proc路径实现,不要直接手动用kernel_read读取文件内容后解析,内核提供了更规范的接口:
- 使用
filp_open打开/proc/[pid]/maps文件(权限设为O_RDONLY)。 - 通过
kernel_read读取文件内容到内核缓冲区。 - 解析缓冲区内容,按照
/proc/maps的格式(每行以地址区间开头,包含路径)提取目标so的基地址。
但这种方式本质是绕回了内核生成/proc/maps的逻辑,效率远低于直接遍历vm_area_struct,仅适合必须依赖/proc的特殊场景。
内容的提问来源于stack exchange,提问作者Parvo
相关产品推荐
相关产品推荐

