轉自:https://www.cnblogs.com/LittleHann/p/3927316.html
目錄
0. 引言 1. 基於進程內存鏡像信息struct mm_struct獲取struct path調用d_path()獲取當前進程的"絕對路徑" 2. 基於文件描述符(fd)、task_struct調用d_path()獲取當前進程所打開文件的"絕對路徑" 3. 基於dentry、vfsmount調用d_path()獲取當前進程的"當前目錄" 4. 基於jprobe監控load_module()系統調用獲取當前正在加載的LKM文件的絕對路徑 5. 基於get_fs_pwd獲取當前目錄
0. 引言
本文涉及的是ring0下的獲取當前進程工作目錄的方法,LKM位於linux的內核內存區域,任何進程都可以通過LKM的導出函數指定當前LKM的代碼,所以,我們需要在LKM中獲取當前調用進程的當前工作目錄
1. 基於進程內存鏡像信息struct mm_struct獲取struct path調用d_path()獲取當前進程的"絕對路徑"
d_path是內核提供的根據dentry和vfsmount獲取絕對路徑函數
此函數有2個版本,以內核版本2.6.25為分界 1. extern char *d_path(const struct path *, char *, int); 2. extern char * d_path(struct dentry *, struct vfsmount *, char *, int);
get_absolute_path.c
#include <linux/module.h> #include <linux/fcntl.h>//for O_RDONLY #include <linux/fs.h>//for filp_open #include <linux/uaccess.h>//for get_fs #include <linux/limits.h>//for PATH_MAX #include <linux/sched.h> #include <linux/mm.h> char* get_absolute_path(struct task_struct * task) { char * ret_ptr = NULL; char * tpath = NULL ; struct vm_area_struct * vma = NULL; struct path base_path; tpath = (char*)kmalloc(512, 0); if(NULL == tpath || NULL == task) { return NULL; } memset(tpath,'\0',512); task_lock(task); /* 獲取當前進程的內存空間信息(通過內存空間) */ if(task->mm && task->mm->mmap) { vma = task->mm->mmap; } else { task_unlock(task); kfree(tpath); return NULL; } /* 取得path(a struct含dentry和vfsmount) */ while(vma) { if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { base_path = vma->vm_file->f_path; break; } vma = vma->vm_next; } task_unlock(task); /* * 調用 d_path, 得到絕對路徑 */ ret_ptr = d_path(&base_path, tpath, 512); return ret_ptr; } int init_module(void) { struct task_struct * task = current; char *path = get_absolute_path(task); printk("FULLPATH: %s\n", path); return 0; } void cleanup_module(void) { } MODULE_LICENSE("GPL");
Makefile
# # Variables needed to build the kernel module # name = get_absolute_path obj-m += $(name).o all: build .PHONY: build install clean build: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules CONFIG_DEBUG_SECTION_MISMATCH=y install: build -mkdir -p /lib/modules/`uname -r`/kernel/arch/x86/kernel/ cp $(name).ko /lib/modules/`uname -r`/kernel/arch/x86/kernel/ depmod /lib/modules/`uname -r`/kernel/arch/x86/kernel/$(name).ko clean: [ -d /lib/modules/$(shell uname -r)/build ] && \ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Relevant Link:
http://blog.csdn.net/cenziboy/article/details/8761621
2. 基於文件描述符(fd)、task_struct調用d_path()獲取當前進程所打開文件的"絕對路徑"
#include <linux/fdtable.h>
/* 根據task_struct、fd(文件描述符)獲取當前工作路徑 */ int get_path(struct task_struct *mytask, int fd) { struct file *myfile = NULL; struct files_struct *files = NULL; char path[100] = {'\0'}; char *ppath = path; files = mytask->files; if (!files) { printk("files is null..\n"); return -1; } myfile = files->fdt->fd[fd]; if (!myfile) { printk("myfile is null..\n"); return -1; } ppath = d_path(&(myfile->f_path), ppath, 100); printk("FULLPATH: %s\n", ppath); return 1; }
Relevant Link:
http://edsionte.com/techblog/archives/4406 http://xd03071149.blog.163.com/blog/static/12350636320129822841854/
3. 基於dentry、vfsmount調用d_path()獲取當前進程的"當前目錄"
1. 首先得到文件對應的struct file結構 2. 后再用結構中的f_dentry和f_vfsmnt字段充當d_path的前兩個參數 3. 得到了這個文件的絕對路徑了具體步驟如下
關於linux內核中和文件系統、VFS相關的數據結構,請參閱另一篇文章
http://www.cnblogs.com/LittleHann/p/3865490.html
code
#include <linux/module.h> #include <linux/fcntl.h>//for O_RDONLY #include <linux/fs.h>//for filp_open #include <linux/uaccess.h>//for get_fs #include <linux/limits.h>//for PATH_MAX #include <linux/sched.h> #include <linux/mm.h> #include <linux/fdtable.h> #include <linux/types.h> #include <linux/stat.h> #include <linux/unistd.h> #include <linux/dcache.h> #include <linux/fs_struct.h> #include <linux/mount.h> #define MAX_TMPPATH 1024 void get_absolute_path(char *mod_name) { /* 假設 1024 已經足夠了 */ char tmp_path[MAX_TMPPATH] = {0}; char * ptr = NULL; struct dentry *dentry = NULL; struct vfsmount * mnt = NULL; memset(tmp_path, 0, MAX_TMPPATH); ptr = tmp_path + MAX_TMPPATH - 256; task_lock(current); /* Get the Process working dentry */ dentry = current->fs->pwd.dentry; /* Get the Process working vfsmount */ mnt = current->fs->pwd.mnt; do { /* Process the dentry */ while(dentry && dentry->d_name.name && strcmp(dentry->d_name.name,"/")) { ptr = ptr - strlen(dentry->d_name.name); if(ptr <= tmp_path + strlen(dentry->d_name.name) + 1) { break; } memcpy(ptr, dentry->d_name.name, strlen(dentry->d_name.name)); *(--ptr) = '/'; dentry= dentry->d_parent; } /* Process the filesystem mountpoint, 突破掛載點 */ if(mnt && mnt->mnt_mountpoint && mnt->mnt_mountpoint->d_name.name && strcmp(mnt->mnt_mountpoint->d_name.name,"/")) { dentry = mnt->mnt_mountpoint; ptr = ptr - strlen(dentry->d_name.name); if(ptr <= tmp_path + strlen(dentry->d_name.name) + 1) { break; } memcpy(ptr,dentry->d_name.name,strlen(dentry->d_name.name)); *(--ptr) = '/'; mnt = mnt->mnt_parent; dentry= dentry->d_parent; } } while( 0 != strcmp(mnt->mnt_mountpoint->d_name.name,"/")); /* end do */ /* 直到文件系統的掛載點為 / */ task_unlock(current); //concat the full path strcat(ptr, "/"); strcat(ptr, mod_name); strcat(ptr, ".ko"); printk("full path: %s\n",ptr); } int init_module(void) { get_absolute_path(THIS_MODULE->name); } void cleanup_module(void) { } MODULE_LICENSE("GPL");
Makefile
# # Variables needed to build the kernel module # name = fd_d_path obj-m += $(name).o all: build .PHONY: build install clean build: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules CONFIG_DEBUG_SECTION_MISMATCH=y install: build -mkdir -p /lib/modules/`uname -r`/kernel/arch/x86/kernel/ cp $(name).ko /lib/modules/`uname -r`/kernel/arch/x86/kernel/ depmod /lib/modules/`uname -r`/kernel/arch/x86/kernel/$(name).ko clean: [ -d /lib/modules/$(shell uname -r)/build ] && \ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Relevant Link:
http://blog.csdn.net/cenziboy/article/details/7999217
4. 基於jprobe監控load_module()系統調用獲取當前正在加載的LKM文件的絕對路徑
關於jprobe的基本原理和內核代碼原理,請參與其他的文章
http://www.cnblogs.com/LittleHann/p/3854977.html http://www.cnblogs.com/LittleHann/p/3920387.html
what we have?
1. jprobe可以在執行監控的系統調用的入口處得到回調通知進行執行 2. jprobe可以拿到和原始系統調用相同的參數列表,因為jprobe復制原始系統調用入口處的寄存器狀態(注意: 是寄存器狀態,不是內核棧狀態) 3. 因為jprobe復制了寄存器狀態,我們可以拿到的數據結構有 1) 當前正在加載的LKM的ELF鏡像 2) CURRENT宏指向的地址是原始進程的task_struct(因為CURRENT是通過寄存器尋址的) 4. 通過task_struct可以間接地得到原始進程"打開的文件句柄列表"
那是不是說我們在監控的時候就可以直接得到原始insmod進程所加載的LKM的絕對路徑呢?結論是否定的。
code
//打印當前進程的open files for (j = 0; j < 32; ++j) { get_path(current, j); } int get_path(struct task_struct *mytask, int fd) { struct file *myfile = NULL; struct files_struct *files = NULL; char path[100] = {'\0'}; char *ppath = path; files = mytask->files; if (!files) { printk("files is null..\n"); return -1; } //myfile = files->fdt->fd[fd]; myfile = files->fd_array[fd]; if (!myfile) { printk("myfile is null..\n"); return -1; } ppath = d_path(&(myfile->f_path), ppath, 100); printk("FULLPATH: %s\n", ppath); return 1; }
打印的結果是全null,0、1、2的是標准輸入、輸出、錯誤輸出的file discriptor
為什么會這樣呢?難道insmod不需要打開.ko文件再來加載嗎?使用strace insmod hello.ko跟蹤一下
從代碼流程中可以清楚地看到,insmod在打開、讀取了.ko文件之后,立刻關閉了原始文件,然后把ELF鏡像直接傳入系統調用init_module中,而我們知道,每個文件在內核中都有一個引用數,當前.ko文件只有insmod在打開它,則當前引用數減為0,這個文件在內核中的數據結構就會被釋放,相應的,進程的文件句柄表fd_array也就不存在這個fd了,我們自然無法得到加載文件的路徑
關於open、close的內核代碼原理,可以參閱另一篇文章,對內核代碼的分析可以明白,想要通過文件描述符這個渠道去獲得當前進程之前打開過的文件的絕對路徑是走不通的
http://www.cnblogs.com/LittleHann/p/3932624.html
這從某種程序上也體現了linux的設計思想,文件的存儲和進程的運行是兩個分立的東西,都是很細粒度的概念,我們通過進程要想回溯到磁盤上的文件,就需要有一個中間紐帶,而在這個場景下,就是"文件句柄表",如果insmod的代碼不要close這個文件,我們也就可以很容易地拿到了
Relevant Link:
http://www.makelinux.net/books/lkd2/ch12lev1sec10
5. 基於get_fs_pwd獲取當前目錄
2.6.39以上才能用 //獲取.ko文件路徑 /* get_fs_pwd(current->fs, &ko_pwd); ko_buf = (char*)__get_free_page(GFP_USER); ko_path = dentry_path_raw(ko_pwd.dentry, ko_buf, PAGE_SIZE); free_page((unsigned long)ko_buf); */ /* getcwd(buf, sizeof(buf)); */
待研究
Copyright (c) 2014 LittleHann All rights reserved