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..56617529ef --- /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 Exception + */ + 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 Exception + */ + 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; + } +} diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java index 4256b352d9..1448740ff8 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java @@ -393,6 +393,7 @@ class ExtractRegistry extends Extract { result = result.replaceAll("\\r", ""); //NON-NLS result = result.replaceAll("'", "'"); //NON-NLS result = result.replaceAll("&", "&"); //NON-NLS + result = result.replace('\0', ' '); // NON-NLS String enddoc = ""; //NON-NLS String stringdoc = startdoc + result + enddoc; DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); diff --git a/docs/doxygen/platformConcepts.dox b/docs/doxygen/platformConcepts.dox index 87f68813b5..8b943e6a01 100644 --- a/docs/doxygen/platformConcepts.dox +++ b/docs/doxygen/platformConcepts.dox @@ -55,6 +55,7 @@ The followig are basic services that are available to any module: - Pop-up Windows: If you have a background task that needs the provide the user with feedback, you can use the org.sleuthkit.autopsy.coreutils.MessageNotifyUtil.Notify.show() method to make a message in the lower right hand area. - Module Settings: If you want to persist settings between invocations of Autopsy, you can use org.sleuthkit.autopsy.coreutils.ModuleSettings. +- Content Utilities: The org.sleuthkit.autopsy.datamodel.ContentUtils class has utility methods to write files from Autopsy to local disk. Specifically the org.sleuthkit.autopsy.datamodel.ContentUtils.writeToFile() method. - Platform Utilities: The org.sleuthkit.autopsy.coreutils.PlatformUtil class allows you to save resources into the user folder and determine paths for the user folders. Specifically: - PlatformUtil.extractResourceToUserConfigDir() - PlatformUtil.isWindowsOS()