如何在不修改目标进程的前提下监控ProcessBuilder启动进程的文件写入路径
可以实现,以下是具体方案
你可以通过Java结合JNA调用Windows API,在不修改目标进程的前提下监控其生成的文件路径,核心思路是获取目标进程PID后,监控文件系统的创建事件并匹配进程ID。
步骤说明
- 获取目标进程PID:通过Java API或反射拿到你启动的子进程ID。
- 监控文件系统事件:用JNA调用Windows的
ReadDirectoryChangesWAPI捕获文件创建事件。 - 匹配进程ID:对每个新创建的文件,调用Windows API获取其创建进程的PID,与目标PID匹配则记录路径。
代码实现
1. JNA接口定义(Kernel32.java)
import com.sun.jna.Library; import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.Structure; import com.sun.jna.WString; import com.sun.jna.platform.win32.WinBase; import com.sun.jna.platform.win32.WinNT; import com.sun.jna.ptr.IntByReference; import java.util.Arrays; import java.util.List; public interface Kernel32 extends Library { Kernel32 INSTANCE = Native.load("kernel32", Kernel32.class); int FILE_INFO_BY_HANDLE_CLASS_FileProcessIdsUsageInformation = 9; WinNT.HANDLE OpenProcess(int dwDesiredAccess, boolean bInheritHandle, int dwProcessId); boolean ReadDirectoryChangesW( WinNT.HANDLE hDirectory, Pointer lpBuffer, int nBufferLength, boolean bWatchSubtree, int dwNotifyFilter, IntByReference lpBytesReturned, WinBase.OVERLAPPED lpOverlapped, WinBase.LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine ); WinNT.HANDLE CreateFileW( WString lpFileName, int dwDesiredAccess, int dwShareMode, Pointer lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, WinNT.HANDLE hTemplateFile ); boolean GetFileInformationByHandleEx( WinNT.HANDLE hFile, int FileInformationClass, Pointer lpFileInformation, int dwBufferSize ); boolean CloseHandle(WinNT.HANDLE hObject); class FILE_PROCESS_IDS_USAGE_INFORMATION extends Structure { public long NumberOfProcessIdsInList; public long[] ProcessIdList = new long[1]; @Override protected List<String> getFieldOrder() { return Arrays.asList("NumberOfProcessIdsInList", "ProcessIdList"); } } }
2. 文件监控实现(FileMonitor.java)
import com.sun.jna.platform.win32.WinNT; import com.sun.jna.ptr.IntByReference; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; public class FileMonitor { public static void monitorForProcess(long targetPid, String directory) { // 打开监控目录的句柄 WinNT.HANDLE dirHandle = Kernel32.INSTANCE.CreateFileW( new WString(directory), WinNT.FILE_LIST_DIRECTORY, WinNT.FILE_SHARE_READ | WinNT.FILE_SHARE_WRITE | WinNT.FILE_SHARE_DELETE, null, WinNT.OPEN_EXISTING, WinNT.FILE_FLAG_BACKUP_SEMANTICS, null ); if (dirHandle.equals(WinNT.INVALID_HANDLE_VALUE)) { throw new RuntimeException("无法打开监控目录句柄"); } ByteBuffer buffer = ByteBuffer.allocate(4096); IntByReference bytesReturned = new IntByReference(); // 后台线程持续监控 new Thread(() -> { while (true) { boolean success = Kernel32.INSTANCE.ReadDirectoryChangesW( dirHandle, Pointer.nativeValue(buffer), buffer.capacity(), true, WinNT.FILE_NOTIFY_CHANGE_FILE_NAME | WinNT.FILE_NOTIFY_CHANGE_CREATION, bytesReturned, null, null ); if (!success) break; buffer.position(0); while (buffer.remaining() > 0) { int action = buffer.getInt(); int fileNameLength = buffer.getShort() & 0xFFFF; byte[] fileNameBytes = new byte[fileNameLength]; buffer.get(fileNameBytes); String fileName = new String(fileNameBytes, StandardCharsets.UTF_16LE).trim(); String fullPath = directory + "\\" + fileName; // 打开文件获取创建进程PID WinNT.HANDLE fileHandle = Kernel32.INSTANCE.CreateFileW( new WString(fullPath), WinNT.GENERIC_READ, WinNT.FILE_SHARE_READ, null, WinNT.OPEN_EXISTING, 0, null ); if (!fileHandle.equals(WinNT.INVALID_HANDLE_VALUE)) { Kernel32.FILE_PROCESS_IDS_USAGE_INFORMATION info = new Kernel32.FILE_PROCESS_IDS_USAGE_INFORMATION(); boolean infoSuccess = Kernel32.INSTANCE.GetFileInformationByHandleEx( fileHandle, Kernel32.FILE_INFO_BY_HANDLE_CLASS_FileProcessIdsUsageInformation, info.getPointer(), info.size() ); if (infoSuccess && info.NumberOfProcessIdsInList > 0 && info.ProcessIdList[0] == targetPid) { System.out.println("目标进程生成的文件路径: " + fullPath); } Kernel32.INSTANCE.CloseHandle(fileHandle); } // 缓冲区对齐处理 int padding = (4 - (fileNameLength % 4)) % 4; if (buffer.remaining() >= padding) { buffer.position(buffer.position() + padding); } else { break; } } buffer.clear(); } Kernel32.INSTANCE.CloseHandle(dirHandle); }).start(); } }
3. 主程序调用
import java.lang.reflect.Field; public class Main { public static void main(String[] args) throws Exception { final String cmd = "your_command_here"; // 替换为实际要执行的命令 final ProcessBuilder processBuilder = new ProcessBuilder("cmd", "/c", cmd); final Process process = processBuilder.start(); // 获取子进程PID long pid; if (System.getProperty("java.version").startsWith("1.8")) { // Java 8及以下通过反射获取PID try { Field pidField = process.getClass().getDeclaredField("pid"); pidField.setAccessible(true); pid = (long) pidField.get(process); } catch (Exception e) { throw new RuntimeException("获取PID失败", e); } } else { // Java 9+直接调用API pid = process.pid(); } // 监控C盘根目录及所有子目录(可根据实际情况调整监控范围) FileMonitor.monitorForProcess(pid, "C:\\"); // 等待子进程执行完成 process.waitFor(); } }
注意事项
- 权限要求:必须以管理员权限运行Java程序,否则无法正常获取进程信息或监控目录。
- 监控范围:如果不确定目标进程的文件生成路径,建议缩小监控范围(比如用户目录、临时目录),避免性能损耗。
- 边缘情况:若目标进程创建文件后立即删除,可能会错过捕获,可考虑结合进程的文件句柄监控优化。
- JNA依赖:需要引入JNA及JNA Platform依赖,Maven配置示例:
<dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna</artifactId> <version>5.13.0</version> </dependency> <dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna-platform</artifactId> <version>5.13.0</version> </dependency>
内容的提问来源于stack exchange,提问作者carrj-nm
相关产品推荐
相关产品推荐

