You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 21:44:55