From 882343a60f6f99a729e5f1a207a31c50f7323d79 Mon Sep 17 00:00:00 2001 From: Eamonn Saunders Date: Wed, 5 Nov 2014 16:02:47 -0500 Subject: [PATCH] - Added Win32Process which uses JNA to give us access to Windows APIs that give us more control over processes. - Added killProcess() method to ExecUtil. This method can be used to terminate a process and its children. --- .../sleuthkit/autopsy/coreutils/ExecUtil.java | 33 ++++- .../autopsy/coreutils/Win32Process.java | 115 ++++++++++++++++++ 2 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/coreutils/Win32Process.java diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/ExecUtil.java b/Core/src/org/sleuthkit/autopsy/coreutils/ExecUtil.java index 536fa786a0..8005609470 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/ExecUtil.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/ExecUtil.java @@ -18,11 +18,13 @@ */ package org.sleuthkit.autopsy.coreutils; +import com.sun.javafx.PlatformUtil; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Writer; +import java.util.List; import java.util.concurrent.TimeUnit; import java.util.logging.Level; @@ -108,12 +110,12 @@ public final class ExecUtil { do { process.waitFor(timeOut, units); if (process.isAlive() && terminator.shouldTerminateProcess()) { - process.destroyForcibly(); + killProcess(process); } } while (process.isAlive()); } catch (InterruptedException ex) { if (process.isAlive()) { - process.destroyForcibly(); + killProcess(process); } Logger.getLogger(ExecUtil.class.getName()).log(Level.INFO, "Thread interrupted while running {0}", processBuilder.command().get(0)); Thread.currentThread().interrupt(); @@ -121,6 +123,33 @@ public final class ExecUtil { return process.exitValue(); } + /** + * Kill a process and its children + * @param process The parent process to kill + */ + public static void killProcess(Process process) { + if (process == null) + return; + + try { + if (PlatformUtil.isWindows()) { + Win32Process parentProcess = new Win32Process(process); + List children = parentProcess.getChildren(); + + children.stream().forEach((child) -> { + child.terminate(); + }); + parentProcess.terminate(); + } + else { + process.destroyForcibly(); + } + } + catch (Exception ex) { + logger.log(Level.WARNING, "Error occurred when attempting to kill process: {0}", ex.getMessage()); // NON-NLS + } + } + private static final Logger logger = Logger.getLogger(ExecUtil.class.getName()); private Process proc = null; private ExecUtil.StreamToStringRedirect errorStringRedirect = null; diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/Win32Process.java b/Core/src/org/sleuthkit/autopsy/coreutils/Win32Process.java new file mode 100644 index 0000000000..84cc47be09 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/coreutils/Win32Process.java @@ -0,0 +1,115 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2012-2014 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.coreutils; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Kernel32; +import com.sun.jna.platform.win32.Kernel32Util; +import com.sun.jna.platform.win32.Tlhelp32; +import com.sun.jna.platform.win32.WinDef.DWORD; +import com.sun.jna.platform.win32.WinNT; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +/** + * Class that represents a Windows process. + * It uses JNA to access the Win32 API. + * This code is based on http://stackoverflow.com/questions/10124299/how-do-i-terminate-a-process-tree-from-java + */ +public class Win32Process { + WinNT.HANDLE handle; + int pid; + + /** + * Create a Win32Process object for the given Process object. + * Reflection is used to construct a Windows process handle. + * @param process A Java Process object + * @throws IOException + */ + Win32Process (Process process) throws Exception + { + if (process.getClass().getName().equals("java.lang.Win32Process") || // NON-NLS + process.getClass().getName().equals("java.lang.ProcessImpl")) { // NON-NLS + try { + Field f = process.getClass().getDeclaredField("handle"); // NON-NLS + f.setAccessible(true); + long handleVal = f.getLong(process); + handle = new WinNT.HANDLE(Pointer.createConstant(handleVal)); + } + catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) { + throw new Exception(ex.getMessage()); // NON-NLS + } + } + this.pid = Kernel32.INSTANCE.GetProcessId(handle); + } + + /** + * Create a Win32Process object for the given process id. + * @param pid Process Id + * @throws IOException + */ + Win32Process (int pid) throws Exception + { + handle = Kernel32.INSTANCE.OpenProcess ( + 0x0400| /* PROCESS_QUERY_INFORMATION */ + 0x0800| /* PROCESS_SUSPEND_RESUME */ + 0x0001| /* PROCESS_TERMINATE */ + 0x00100000 /* SYNCHRONIZE */, + false, + pid); + if (handle == null) + throw new Exception (Kernel32Util.formatMessageFromLastErrorCode (Kernel32.INSTANCE.GetLastError ())); + this.pid = Kernel32.INSTANCE.GetProcessId(handle); + } + + @Override + protected void finalize () throws Throwable + { + Kernel32.INSTANCE.CloseHandle (handle); + super.finalize(); + } + + /** + * Kill the process. Note that this does not kill children. + */ + public void terminate () + { + Kernel32.INSTANCE.TerminateProcess (handle, 0); + } + + /** + * Get children of current process object. + * @return list of child processes + * @throws IOException + */ + public List getChildren () throws Exception + { + ArrayList result = new ArrayList<> (); + WinNT.HANDLE hSnap = Kernel32.INSTANCE.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPPROCESS, new DWORD(0)); + Tlhelp32.PROCESSENTRY32.ByReference ent = new Tlhelp32.PROCESSENTRY32.ByReference (); + if (!Kernel32.INSTANCE.Process32First (hSnap, ent)) return result; + do { + if (ent.th32ParentProcessID.intValue () == pid) result.add (new Win32Process (ent.th32ProcessID.intValue ())); + } while (Kernel32.INSTANCE.Process32Next (hSnap, ent)); + Kernel32.INSTANCE.CloseHandle (hSnap); + return result; + } +}