Android x64环境下INOTIFY文件审计中区分进程PID的问题
解决Android x64下区分进程访问文件的问题
问题分析
inotify本身不会在事件中携带触发进程的PID,所以无法直接通过inotify区分自身和外部进程的访问行为。下面提供两种无需root权限的解决方案:
方案一:利用进程标记(简单但存在极小时间窗口)
在自身进程打开目标文件前后设置原子标记,避免线程竞争,当inotify事件触发时,通过标记判断是否为自身操作:
修改原代码如下:
#include <iostream> #include <fstream> #include <sys/inotify.h> #include <thread> #include <unistd> #include <atomic> std::atomic<bool> is_self_accessing(false); // 原子标记,防止线程竞争 void FILER() { std::this_thread::sleep_for(std::chrono::seconds(10)); is_self_accessing = true; // 标记开始访问文件 std::ifstream file("/data/user/0/com.termux/files/home/ok.txt"); if (file.is_open()) { std::string line; while (std::getline(file, line)) { std::cout << "File data: " << line << std::endl; } file.close(); } is_self_accessing = false; // 标记结束访问 } int main() { int fd = inotify_init(); int wd = inotify_add_watch(fd, "/data/user/0/com.termux/files/home/ok.txt", IN_OPEN | IN_ACCESS); char buffer[4096]; ssize_t bytesRead; std::thread printThread(FILER); pid_t self_pid = getpid(); // 获取自身进程PID while (true) { bytesRead = read(fd, buffer, sizeof(buffer)); if (bytesRead <= 0) continue; struct inotify_event* event = (struct inotify_event*)buffer; if (event->mask & IN_ACCESS) { if (is_self_accessing) { std::cout << "File accessed by self (PID: " << self_pid << ")" << std::endl; } else { std::cout << "File accessed by external process" << std::endl; } } sleep(2); } printThread.join(); close(wd); close(fd); return 0; }
这种方法的局限性是存在极小时间窗口:如果外部进程在自身标记激活的时间段内访问文件,会被误判为自身操作,但对于大多数普通场景已经够用。
方案二:遍历/proc文件系统获取打开文件的进程(准确但实现复杂)
Android的/proc文件系统无需root即可部分访问,我们可以遍历/proc下的进程目录,检查每个进程的文件描述符符号链接,找到打开目标文件的进程PID,再排除自身PID:
首先实现工具函数,获取打开指定文件的所有进程PID:
#include <vector> #include <dirent.h> #include <string> #include <unistd.h> #include <limits.h> #include <cctype> std::vector<pid_t> get_pids_accessing_file(const std::string& target_path) { std::vector<pid_t> pids; DIR* proc_dir = opendir("/proc"); if (!proc_dir) return pids; struct dirent* entry; char resolved_path[PATH_MAX]; std::string target_realpath; // 获取目标文件的真实路径,处理符号链接 if (realpath(target_path.c_str(), resolved_path)) { target_realpath = resolved_path; } else { target_realpath = target_path; } while ((entry = readdir(proc_dir)) != nullptr) { // 过滤非进程数字目录 if (!isdigit(entry->d_name[0])) continue; pid_t pid = atoi(entry->d_name); if (pid == getpid()) continue; // 跳过自身进程 std::string fd_dir = "/proc/" + std::string(entry->d_name) + "/fd"; DIR* fd_dir_ptr = opendir(fd_dir.c_str()); if (!fd_dir_ptr) continue; struct dirent* fd_entry; while ((fd_entry = readdir(fd_dir_ptr)) != nullptr) { if (fd_entry->d_name[0] == '.') continue; std::string fd_path = fd_dir + "/" + fd_entry->d_name; ssize_t len = readlink(fd_path.c_str(), resolved_path, sizeof(resolved_path)-1); if (len == -1) continue; resolved_path[len] = '\0'; std::string fd_realpath(resolved_path); if (fd_realpath == target_realpath) { pids.push_back(pid); break; // 该进程已打开目标文件,无需继续检查其他fd } } closedir(fd_dir_ptr); } closedir(proc_dir); return pids; }
修改主函数,在inotify事件触发时调用该函数:
int main() { int fd = inotify_init(); std::string target_file = "/data/user/0/com.termux/files/home/ok.txt"; int wd = inotify_add_watch(fd, target_file.c_str(), IN_OPEN | IN_ACCESS); char buffer[4096]; ssize_t bytesRead; std::thread printThread(FILER); pid_t self_pid = getpid(); while (true) { bytesRead = read(fd, buffer, sizeof(buffer)); if (bytesRead <= 0) continue; struct inotify_event* event = (struct inotify_event*)buffer; if (event->mask & IN_ACCESS) { std::vector<pid_t> accessing_pids = get_pids_accessing_file(target_file); if (accessing_pids.empty()) { // 事件触发时文件已关闭,大概率是自身进程访问 std::cout << "File accessed by self (PID: " << self_pid << ")" << std::endl; } else { std::cout << "File accessed by external process(es): "; for (pid_t pid : accessing_pids) { std::cout << pid << " "; } std::cout << std::endl; } } sleep(2); } printThread.join(); close(wd); close(fd); return 0; }
这种方法更准确,但需要注意:
- 遍历
/proc会有一定性能开销,频繁触发事件时可能影响效率 - 对于短时间打开后立即关闭的文件,可能无法捕获到PID,可结合方案一的标记补充判断
注意事项
- Android 11及以上版本的沙箱机制会限制对其他应用
/proc目录的访问,但Termux环境下的文件若能被其他进程(如文件管理器)访问,其/proc/[pid]/fd通常可被读取 - 两种方案均无需root权限,符合需求
内容的提问来源于stack exchange,提问作者Aimar
相关产品推荐
相关产品推荐

