├── doc └── images │ └── deployment1.png ├── .gitignore ├── NOTICE.txt ├── src └── main │ └── java │ └── com │ └── omnissa │ └── software_forensic_kit │ ├── java_gadget │ ├── VizJSNode.java │ ├── local │ │ ├── JarCallGraph.java │ │ ├── JarCallGraphClassVisitor.java │ │ ├── LocalFileVisitor.java │ │ ├── JarCallGraphMethodVisitor.java │ │ └── LocalCalls.java │ ├── agent │ │ ├── Java_Gadget_Agent.java │ │ ├── HelpClassVisitor.java │ │ ├── Java_Gadget_AgentTransformer.java │ │ └── HelpMethodVisitor.java │ ├── remote │ │ └── RemoteCalls.java │ ├── Java_Gadget_Dynamic.java │ ├── Java_Gadget.java │ ├── CallGraphNode.java │ ├── Java_Gadget_Injector.java │ └── CallGraphAnalysis.java │ └── Java_Gadget_Kit.java ├── Dockerfile ├── LICENSE.txt ├── CONTRIBUTING.md ├── pom.xml └── README.md /doc/images/deployment1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnissa-archive/software-forensic-kit/HEAD/doc/images/deployment1.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.out 3 | *.swp 4 | *.jar 5 | *.DS_Store 6 | *.lock 7 | *~ 8 | target/ 9 | bin/ 10 | .classpath 11 | .project 12 | .settings/ 13 | 14 | 15 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | Software_Forensic_Kit 2 | Copyright (c) 2019 Omnissa, LLC. All Rights Reserved. 3 | 4 | This product is licensed to you under the BSD-2 license (the "License"). You may not use this product except in compliance with the BSD-2 License. 5 | 6 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 7 | 8 | -------------------------------------------------------------------------------- /src/main/java/com/omnissa/software_forensic_kit/java_gadget/VizJSNode.java: -------------------------------------------------------------------------------- 1 | /*************************************************** 2 | * Copyright 2019 Omnissa, LLC. 3 | * SPDX-License-Identifier: BSD-2-Clause 4 | ***************************************************/ 5 | package com.omnissa.software_forensic_kit.java_gadget; 6 | 7 | public class VizJSNode{ 8 | public String label = ""; 9 | public int id; 10 | public String color = ""; 11 | public String title = ""; 12 | public VizJSNode(String label, int id, String color, String title) { 13 | this.label = label; 14 | this.id = id; 15 | this.color = color; 16 | this.title = title; 17 | } 18 | public String toString() { 19 | 20 | String output = String.format("{\"label\":\"%s\", \"id\":%d", label, id); 21 | if(color != null) 22 | output += String.format(", \"color\":\"%s\"", color); 23 | if(title != null) 24 | output += String.format(", \"title\":\"%s\"", title); 25 | output += "}"; 26 | return output; 27 | 28 | } 29 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | #HOW TO BUILD 2 | #docker build -t software-forensic-kit . 3 | 4 | FROM centos:7 5 | RUN yum -y update 6 | RUN yum -y group install 'Development Tools' 7 | RUN yum -y install java-1.8.0-openjdk wget java-1.8.0-openjdk-devel 8 | RUN wget https://downloads.apache.org/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz 9 | RUN tar xvf apache-maven-3.6.3-bin.tar.gz && mv apache-maven-3.6.3 /usr/local/apache-maven && export M2_HOME=/usr/local/apache-maven && export M2=$M2_HOME/bin && export PATH=$M2:$PATH && export JAVA_HOME=$(find /usr/lib/jvm -name "*java*openjdk-*") && source ~/.bashrc 10 | COPY . /software_forensic_kit 11 | #FIRST ADD TOOLS AS A JAR IN LOCAL REPOSITORY 12 | RUN cd /software_forensic_kit && export JAVA_HOME=$(find /usr/lib/jvm -name "*java*openjdk-*") && /usr/local/apache-maven/bin/mvn install:install-file -Dfile="${JAVA_HOME}/lib/tools.jar" -DgroupId=com.sun -DartifactId=tools -Dversion=1.8.0 -Dpackaging=jar && /usr/local/apache-maven/bin/mvn install -X 13 | RUN cp software_forensic_kit/target/software_forensic_kit-0.0.1-SNAPSHOT-jar-with-dependencies.jar /tmp/software_forensic_kit-0.0.1-SNAPSHOT-jar-with-dependencies.jar 14 | 15 | #docker run -it -d --privileged software-forensic-kit:latest /usr/sbin/init 16 | #docker exec -it /bin/bash 17 | #docker cp :/tmp/software_forensic_kit-0.0.1-SNAPSHOT-jar-with-dependencies.jar ./software_forensic_kit-0.0.1-SNAPSHOT-jar-with-dependencies.jar -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Software_Forensic_Kit 2 | Copyright (c) 2019 Omnissa, LLC. All rights reserved 3 | 4 | The BSD-2 license (the "License") set forth below applies to all parts of the Software_Forensic_Kit. You may not use this file except in compliance with the License. 5 | 6 | BSD-2 License 7 | 8 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 9 | 10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 11 | 12 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/main/java/com/omnissa/software_forensic_kit/java_gadget/local/JarCallGraph.java: -------------------------------------------------------------------------------- 1 | package com.omnissa.software_forensic_kit.java_gadget.local; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.Enumeration; 6 | import java.util.jar.JarEntry; 7 | import java.util.jar.JarFile; 8 | 9 | import org.apache.bcel.classfile.ClassParser; 10 | import org.apache.bcel.classfile.JavaClass; 11 | 12 | 13 | public class JarCallGraph{ 14 | 15 | public static void main(String[] args) { 16 | JarCallGraph.parseJars(args); 17 | } 18 | public static void parseJars(String[] jarList) { 19 | 20 | for (String jarFile : jarList) { 21 | parseJar(jarFile); 22 | } 23 | } 24 | public static void parseJar(String jarFile) { 25 | try { 26 | 27 | JarCallGraphClassVisitor javaClassV; 28 | JarFile jarF = new JarFile(new File(jarFile)); 29 | 30 | Enumeration jarEntries = jarF.entries(); 31 | JarEntry jarEntry = jarEntries.nextElement(); 32 | while(jarEntry != null) { 33 | if(jarEntry.isDirectory() == false && jarEntry.getName().endsWith(".class")) { 34 | JavaClass javaClazz = new ClassParser(jarFile, jarEntry.getName()).parse(); 35 | javaClassV = new JarCallGraphClassVisitor(javaClazz); 36 | javaClassV.visitJavaClass(); 37 | } 38 | if(jarEntries.hasMoreElements()) 39 | jarEntry = jarEntries.nextElement(); 40 | else 41 | jarEntry = null; 42 | } 43 | } catch (IOException e) { 44 | // TODO Auto-generated catch block 45 | e.printStackTrace(); 46 | } 47 | 48 | } 49 | 50 | 51 | } -------------------------------------------------------------------------------- /src/main/java/com/omnissa/software_forensic_kit/java_gadget/local/JarCallGraphClassVisitor.java: -------------------------------------------------------------------------------- 1 | package com.omnissa.software_forensic_kit.java_gadget.local; 2 | 3 | import java.lang.reflect.Modifier; 4 | 5 | import org.apache.bcel.classfile.Constant; 6 | import org.apache.bcel.classfile.ConstantPool; 7 | import org.apache.bcel.classfile.EmptyVisitor; 8 | import org.apache.bcel.classfile.JavaClass; 9 | import org.apache.bcel.classfile.Method; 10 | import org.apache.bcel.generic.ConstantPoolGen; 11 | import org.apache.bcel.generic.MethodGen; 12 | 13 | public class JarCallGraphClassVisitor extends EmptyVisitor{ 14 | 15 | private JavaClass javaClazz; 16 | private ConstantPoolGen constants; 17 | 18 | public JarCallGraphClassVisitor(JavaClass javaClazz) { 19 | this.javaClazz = javaClazz; 20 | constants = new ConstantPoolGen(javaClazz.getConstantPool()); 21 | } 22 | public void visitJavaClass() { 23 | this.javaClazz.getConstantPool().accept(this); 24 | Method[] methods = this.javaClazz.getMethods(); 25 | for (int i = 0; i < methods.length; i++) 26 | methods[i].accept(this); 27 | } 28 | 29 | public void visitConstantPool(ConstantPool constantPool) { 30 | for (int i = 0; i < constantPool.getLength(); i++) { 31 | Constant constant = constantPool.getConstant(i); 32 | if (constant == null) 33 | continue; 34 | if (constant.getTag() == 7) { 35 | String referencedClass = 36 | constantPool.constantToString(constant); 37 | System.out.println(String.format("C:" + Modifier.toString(javaClazz.getModifiers()).replaceAll(" ", ",") + ":" + javaClazz.getClassName() + " %s", 38 | referencedClass)); 39 | } 40 | } 41 | } 42 | 43 | public void visitMethod(Method method) { 44 | 45 | MethodGen classMethodGen = new MethodGen(method, javaClazz.getClassName(), constants); 46 | if(classMethodGen.isNative() == false && classMethodGen.isAbstract() == false) { 47 | new JarCallGraphMethodVisitor(classMethodGen, javaClazz).startParsing(); 48 | } 49 | 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /src/main/java/com/omnissa/software_forensic_kit/java_gadget/local/LocalFileVisitor.java: -------------------------------------------------------------------------------- 1 | /*************************************************** 2 | * Copyright 2020 Omnissa, LLC. 3 | * SPDX-License-Identifier: BSD-2-Clause 4 | ***************************************************/ 5 | package com.omnissa.software_forensic_kit.java_gadget.local; 6 | 7 | import java.io.IOException; 8 | import java.nio.file.FileVisitResult; 9 | import java.nio.file.FileVisitor; 10 | import java.nio.file.Path; 11 | import java.nio.file.attribute.BasicFileAttributes; 12 | import java.util.ArrayList; 13 | 14 | class LocalFileVisitor implements FileVisitor{ 15 | 16 | public boolean printToConsole = false; 17 | public String endsWith = ""; 18 | public String filter = ""; 19 | ArrayList jarList; 20 | public LocalFileVisitor(boolean printToConsole, String endsWith, String filter) { 21 | this.printToConsole = printToConsole; 22 | this.filter = filter; 23 | this.endsWith = endsWith; 24 | this.jarList = new ArrayList(); 25 | } 26 | private long filesReviewed = 0; 27 | private long filesPassed = 0; 28 | @Override 29 | public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { 30 | return FileVisitResult.CONTINUE; 31 | } 32 | 33 | @Override 34 | public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 35 | filesReviewed++; 36 | String fileString = file.toString(); 37 | 38 | if(fileString.endsWith(endsWith) && fileString.contains(filter)) { 39 | filesPassed++; 40 | jarList.add(fileString); 41 | } 42 | return FileVisitResult.CONTINUE; 43 | } 44 | 45 | @Override 46 | public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { 47 | return FileVisitResult.CONTINUE; 48 | } 49 | 50 | @Override 51 | public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { 52 | return FileVisitResult.CONTINUE; 53 | } 54 | 55 | public long getFilesPassedCount() { 56 | return filesPassed; 57 | } 58 | public long getFilesReviewedCount() { 59 | return filesReviewed; 60 | } 61 | public ArrayList jarList() { 62 | return jarList; 63 | } 64 | } -------------------------------------------------------------------------------- /src/main/java/com/omnissa/software_forensic_kit/java_gadget/agent/Java_Gadget_Agent.java: -------------------------------------------------------------------------------- 1 | /*************************************************** 2 | * Copyright 2020 Omnissa, LLC. 3 | * SPDX-License-Identifier: BSD-2-Clause 4 | ***************************************************/ 5 | package com.omnissa.software_forensic_kit.java_gadget.agent; 6 | 7 | import java.io.IOException; 8 | import java.lang.instrument.Instrumentation; 9 | import java.lang.instrument.UnmodifiableClassException; 10 | import java.util.ArrayList; 11 | 12 | 13 | public class Java_Gadget_Agent { 14 | 15 | public static void premain(String args, Instrumentation instrumentation) throws ClassNotFoundException, UnmodifiableClassException { 16 | 17 | //"com.simple.test.App;printText" 18 | String[] newArgs = {args}; 19 | ArrayList> newClasses = new ArrayList>(); 20 | if(args.contains("~")) 21 | newArgs = args.split("~"); 22 | for(String combo : newArgs) { 23 | String[] classMethod = combo.split(";"); 24 | newClasses = new ArrayList>(); 25 | System.out.println(String.format("Searching for %s", classMethod[0])); 26 | for(Class clazz: instrumentation.getAllLoadedClasses()) { 27 | if(clazz.getName().equals(classMethod[0])) { 28 | System.out.println(String.format("ADDED: %s", clazz.getName())); 29 | newClasses.add(clazz); 30 | } 31 | } 32 | 33 | for (Class classObj : newClasses) { 34 | 35 | instrumentation.addTransformer(new Java_Gadget_AgentTransformer(classObj.getName(), classMethod[1]), true ); 36 | 37 | try { 38 | instrumentation.retransformClasses(classObj); 39 | } catch (Exception e) { 40 | System.out.println("Failed to redefine class!"); 41 | System.out.println(e); 42 | e.printStackTrace(); 43 | 44 | } 45 | System.out.println(String.format("Software Forensic Kit: Class %s found! Transformed!", classObj.getName())); 46 | 47 | } 48 | } 49 | } 50 | public static void agentmain(String args, Instrumentation instrumentation) throws IOException, ClassNotFoundException, UnmodifiableClassException { 51 | premain(args, instrumentation); 52 | } 53 | 54 | 55 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Contributing to software-forensic-kit 4 | 5 | The software-forensic-kit project team welcomes contributions from the community. 6 | 7 | 8 | ## Contribution Flow 9 | 10 | This is a rough outline of what a contributor's workflow looks like: 11 | 12 | - Create a topic branch from where you want to base your work 13 | - Make commits of logical units 14 | - Make sure your commit messages are in the proper format (see below) 15 | - Push your changes to a topic branch in your fork of the repository 16 | - Submit a pull request 17 | 18 | Example: 19 | 20 | ``` shell 21 | git remote add upstream https://github.com/omnissa-archive/software-forensic-kit.git 22 | git checkout -b my-new-feature master 23 | git commit -a 24 | git push origin my-new-feature 25 | ``` 26 | 27 | ### Staying In Sync With Upstream 28 | 29 | When your branch gets out of sync with the omnissa-archive/master branch, use the following to update: 30 | 31 | ``` shell 32 | git checkout my-new-feature 33 | git fetch -a 34 | git pull --rebase upstream master 35 | git push --force-with-lease origin my-new-feature 36 | ``` 37 | 38 | ### Updating pull requests 39 | 40 | If your PR fails to pass CI or needs changes based on code review, you'll most likely want to squash these changes into 41 | existing commits. 42 | 43 | If your pull request contains a single commit or your changes are related to the most recent commit, you can simply 44 | amend the commit. 45 | 46 | ``` shell 47 | git add . 48 | git commit --amend 49 | git push --force-with-lease origin my-new-feature 50 | ``` 51 | 52 | If you need to squash changes into an earlier commit, you can use: 53 | 54 | ``` shell 55 | git add . 56 | git commit --fixup 57 | git rebase -i --autosquash master 58 | git push --force-with-lease origin my-new-feature 59 | ``` 60 | 61 | Be sure to add a comment to the PR indicating your new changes are ready to review, as GitHub does not generate a 62 | notification when you git push. 63 | 64 | ### Formatting Commit Messages 65 | 66 | We follow the conventions on [How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/). 67 | 68 | Be sure to include any related GitHub issue references in the commit message. See 69 | [GFM syntax](https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown) for referencing issues 70 | and commits. 71 | 72 | ## Reporting Bugs and Creating Issues 73 | 74 | When opening a new issue, try to roughly follow the commit message format conventions above. 75 | -------------------------------------------------------------------------------- /src/main/java/com/omnissa/software_forensic_kit/java_gadget/agent/HelpClassVisitor.java: -------------------------------------------------------------------------------- 1 | /*************************************************** 2 | * Copyright 2020 Omnissa, LLC. 3 | * SPDX-License-Identifier: BSD-2-Clause 4 | ***************************************************/ 5 | package com.omnissa.software_forensic_kit.java_gadget.agent; 6 | 7 | import java.lang.reflect.Method; 8 | import java.lang.reflect.Parameter; 9 | 10 | import org.objectweb.asm.ClassVisitor; 11 | import org.objectweb.asm.MethodVisitor; 12 | import org.objectweb.asm.Opcodes; 13 | 14 | public class HelpClassVisitor extends ClassVisitor { 15 | private String className; 16 | private String methodName; 17 | 18 | public HelpClassVisitor(ClassVisitor cv, String pClassName, String methodName) { 19 | super(Opcodes.ASM5, cv); 20 | this.className = pClassName; 21 | this.methodName = methodName; 22 | 23 | 24 | } 25 | 26 | @Override 27 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 28 | MethodVisitor mv = super.visitMethod(access, name, desc, signature, 29 | exceptions); 30 | 31 | 32 | if(true) {//name.equals(this.methodName)) { 33 | Parameter paramM[] = null; 34 | 35 | System.out.println(String.format("Software_Forensic method %s - %s", name, desc)); 36 | 37 | try { 38 | String newClassName = className.replace("/", "."); 39 | Class act = Class.forName(newClassName); 40 | System.out.println(newClassName); 41 | System.out.println(act.getName()); 42 | System.out.println(act.getDeclaredMethods()); 43 | Method fld[] = act.getDeclaredMethods(); 44 | for (int i = 0; i < fld.length; i++) 45 | { 46 | if(fld[i].getName().equals(this.methodName)) { 47 | System.out.println("Method Name is : " + fld[i].getName()); 48 | paramM = fld[i].getParameters(); 49 | 50 | for (int g = 0; g 2 | 4.0.0 3 | com.omnissa.software_forensic_kit 4 | software_forensic_kit 5 | 0.0.1-SNAPSHOT 6 | 7 | UTF-8 8 | 1.8 9 | 1.8 10 | 11 | 12 | 13 | com.jcraft 14 | jsch 15 | 0.1.54 16 | 17 | 18 | commons-cli 19 | commons-cli 20 | 1.4 21 | 22 | 23 | commons-io 24 | commons-io 25 | 2.8 26 | 27 | 28 | com.google.code.gson 29 | gson 30 | 2.8.9 31 | 32 | 33 | org.apache.bcel 34 | bcel 35 | 6.7.0 36 | 37 | 38 | org.ow2.asm 39 | asm 40 | 8.0.1 41 | 42 | 43 | org.ow2.asm 44 | asm-util 45 | 8.0.1 46 | 47 | 48 | org.ow2.asm 49 | asm-commons 50 | 8.0.1 51 | 52 | 53 | com.sun 54 | tools 55 | 1.8.0 56 | 58 | 59 | 60 | 61 | 62 | 63 | org.apache.maven.plugins 64 | maven-assembly-plugin 65 | 66 | 67 | 68 | single 69 | 70 | package 71 | 72 | 73 | jar-with-dependencies 74 | 75 | 76 | 77 | true 78 | com.omnissa.software_forensic_kit.Java_Gadget_Kit 79 | 80 | 81 | com.omnissa.software_forensic_kit.java_gadget.agent.Java_Gadget_Agent 82 | com.omnissa.software_forensic_kit.java_gadget.agent.Java_Gadget_Agent 83 | 84 | true 85 | true 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /src/main/java/com/omnissa/software_forensic_kit/java_gadget/local/JarCallGraphMethodVisitor.java: -------------------------------------------------------------------------------- 1 | package com.omnissa.software_forensic_kit.java_gadget.local; 2 | 3 | import java.lang.reflect.Modifier; 4 | import java.util.stream.Stream; 5 | 6 | import org.apache.bcel.classfile.JavaClass; 7 | import org.apache.bcel.generic.ConstantPoolGen; 8 | import org.apache.bcel.generic.ConstantPushInstruction; 9 | import org.apache.bcel.generic.EmptyVisitor; 10 | import org.apache.bcel.generic.INVOKEDYNAMIC; 11 | import org.apache.bcel.generic.INVOKEINTERFACE; 12 | import org.apache.bcel.generic.INVOKESPECIAL; 13 | import org.apache.bcel.generic.INVOKESTATIC; 14 | import org.apache.bcel.generic.INVOKEVIRTUAL; 15 | import org.apache.bcel.generic.Instruction; 16 | import org.apache.bcel.generic.InstructionConst; 17 | import org.apache.bcel.generic.InstructionHandle; 18 | import org.apache.bcel.generic.InvokeInstruction; 19 | import org.apache.bcel.generic.MethodGen; 20 | import org.apache.bcel.generic.ReturnInstruction; 21 | import org.apache.bcel.generic.Type; 22 | 23 | public class JarCallGraphMethodVisitor extends EmptyVisitor{ 24 | JavaClass javaClazz; 25 | private String outputFormat; 26 | private MethodGen classMethodGen; 27 | private ConstantPoolGen classConstantPoolGen; 28 | 29 | 30 | public JarCallGraphMethodVisitor(MethodGen classMethodGen, JavaClass javaClazz ) { 31 | this.javaClazz = javaClazz; 32 | this.classMethodGen = classMethodGen; 33 | this.classConstantPoolGen = this.classMethodGen.getConstantPool(); 34 | this.outputFormat = "M:" + Modifier.toString(classMethodGen.getModifiers()).replaceAll(" ", ",") + ":" + javaClazz.getClassName() + ":" + classMethodGen.getName() + "(" + argumentList(classMethodGen.getArgumentTypes()) + ")" 35 | + " " + "(%s)%s:%s(%s)"; 36 | 37 | } 38 | private String argumentList(Type[] arguments) { 39 | String[] typeStrings = Stream.of(arguments).map(Type::toString).toArray(String[]::new); 40 | return String.join(" ", typeStrings); 41 | } 42 | 43 | public void startParsing() { 44 | 45 | InstructionHandle instH = classMethodGen.getInstructionList().getStart(); 46 | Instruction instruction; 47 | while(instH != null) { 48 | instruction = instH.getInstruction(); 49 | 50 | if (visitInstruction(instruction) == false) 51 | instruction.accept(this); 52 | instH = instH.getNext(); 53 | } 54 | 55 | } 56 | private boolean visitInstruction(Instruction i) { 57 | short opcode = i.getOpcode(); 58 | return ((InstructionConst.getInstruction(opcode) != null) 59 | && !(i instanceof ConstantPushInstruction) 60 | && !(i instanceof ReturnInstruction)); 61 | } 62 | public void genericSystemOut(InvokeInstruction i, String letter) { 63 | if(i != null && classConstantPoolGen != null) 64 | System.out.println(String.format(outputFormat,letter,i.getReferenceType(classConstantPoolGen),i.getMethodName(classConstantPoolGen),argumentList(i.getArgumentTypes(classConstantPoolGen)))); 65 | } 66 | 67 | @Override 68 | public void visitINVOKEVIRTUAL(INVOKEVIRTUAL i) { 69 | genericSystemOut(i, "M"); 70 | } 71 | 72 | @Override 73 | public void visitINVOKEINTERFACE(INVOKEINTERFACE i) { 74 | genericSystemOut(i, "I"); 75 | } 76 | 77 | @Override 78 | public void visitINVOKESPECIAL(INVOKESPECIAL i) { 79 | genericSystemOut(i, "O"); 80 | } 81 | 82 | @Override 83 | public void visitINVOKESTATIC(INVOKESTATIC i) { 84 | genericSystemOut(i, "S"); 85 | } 86 | 87 | @Override 88 | public void visitINVOKEDYNAMIC(INVOKEDYNAMIC i) { 89 | genericSystemOut(i, "D"); 90 | } 91 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # software-forensic-kit 4 | 5 | ## Overview 6 | Software-Forensic-Kit is a software tool kit to help identify quickly what's inside your binary files and is code reachable in your application. Often times, your binaries, including your open source libraries, will encapsulate other open source software. This makes it difficult to determine if a particular function in a certain library is being used by an application on your system. This tool kit will help you find plausible paths to functions of interest by searching through your already built binaries and generating filtered callgraphs and by injecting into the target function and providing details when that function is invoked. 7 | 8 | ![Software-Forensic-Kit Deployment](doc/images/deployment1.png) 9 | 10 | 11 | ### Prerequisites 12 | 13 | * Requires Java 1.8+ 14 | * Requires Maven 3.x+ 15 | 16 | ### Build & Run 17 | 18 | 1. Setup maven 19 | 2. Build Project 20 | 3. Run 21 | 22 | ### Setup maven 23 | Requires Java 1.8+ and Maven 3.x+ 24 | 25 | 1. Download maven from and extract it. 26 | 2. Install JDK 8 and setup JAVA_HOME 27 | 3. Add maven bin directory to PATH 28 | 29 | ### Build Project 30 | Ensure java and maven are setup then run the following: 31 | 32 | ```` bash 33 | mvn initialize 34 | 35 | mvn clean package 36 | ```` 37 | ### Run 38 | 39 | ``` 40 | usage: Software_Forensic_Kit [-h] [-u ] [-p ] [-d 41 | ] [-f ] [-s ] [-cd ] [-rp ] 42 | [-rd] [-jp] [-pp] [-g] [-gm] [-hi] [-hm] [-cgf] [-I] [-v] [-o 43 | ] 44 | -h,--help 45 | -u,--username Remote server username 46 | -p,--password Remote server password 47 | -d,--domain Remote server ip 48 | -f,--filter Jar path must contain this term to be 49 | included 50 | -s,--searchFunction Function to search for 51 | -cd,--depth Max callgraph depth (default is 8) 52 | -rp,--removePrefix Remove Prefix from callgraph text 53 | -rd,--removeDuplicates Remove Duplicate Paths 54 | -jp,--justPrint Print output to console 55 | -pp,--prettyPrint Pretty Print output to console 56 | -g,--graphViz Output for Graphviz 57 | -gm,--graphVizM Output for Graphviz Multiple 58 | -hi,--html Output for HTML - https://visjs.org 59 | -hm,--htmlM Output for HTML Multiple - 60 | https://visjs.org 61 | -cgf,--callgraphFile Passing in CallGraph file instead of 62 | jar(s) 63 | -I,--interactive Interactive Mode 64 | -v,--verbose Verbose 65 | -o,--output Output to folder 66 | Example Usage: 67 | 68 | >java -jar software_forensic_kit.jar -u root -d 10.160.157.187 -p test123 -s "ExampleClass:functionA" -f /var/www -hm -rp my.class.path. 69 | 70 | Or using wildcards * for filter 71 | 72 | >java -jar software_forensic_kit.jar -u root -d 10.160.157.187 -p test123 -s "ExampleClass*functionA" -f /var/www -pp -rp my.class.path. -o 73 | C:\test\output 74 | 75 | If you want to run software forensic kit locally simply exclude (user, domain and password variables) 76 | 77 | >java -jar software_forensic_kit.jar -s "ExampleClass:functionA" -f C:\test -pp 78 | 79 | For Interactive Mode: 80 | >java -jar software_forensic_kit.jar -I 81 | 82 | ``` 83 | 84 | ## Contributing 85 | 86 | The software-forensic-kit project team welcomes contributions from the community. For more detailed information, refer to [CONTRIBUTING.md](CONTRIBUTING.md). 87 | 88 | Be sure to end all commits with Signed-off-by: 89 | 90 | ## License 91 | software-forensic-kit is available under the BSD-2 License. 92 | For more detailed information, refer to [LICENSE.txt](LICENSE.txt). 93 | -------------------------------------------------------------------------------- /src/main/java/com/omnissa/software_forensic_kit/java_gadget/remote/RemoteCalls.java: -------------------------------------------------------------------------------- 1 | /*************************************************** 2 | * Copyright 2019 Omnissa, LLC. 3 | * SPDX-License-Identifier: BSD-2-Clause 4 | ***************************************************/ 5 | package com.omnissa.software_forensic_kit.java_gadget.remote; 6 | 7 | import java.io.File; 8 | import java.io.FileInputStream; 9 | import java.io.FileNotFoundException; 10 | import java.io.InputStream; 11 | import java.util.Vector; 12 | 13 | import com.jcraft.jsch.Channel; 14 | import com.jcraft.jsch.ChannelExec; 15 | import com.jcraft.jsch.ChannelSftp; 16 | import com.jcraft.jsch.JSch; 17 | import com.jcraft.jsch.JSchException; 18 | import com.jcraft.jsch.Session; 19 | import com.jcraft.jsch.SftpException; 20 | import com.jcraft.jsch.ChannelSftp.LsEntry; 21 | 22 | public class RemoteCalls{ 23 | 24 | private static RemoteCalls instance; 25 | private JSch sshChannel; 26 | private Session conn; 27 | public boolean isConnected = false; 28 | public static RemoteCalls getInstance() { 29 | if (instance == null) { 30 | instance = new RemoteCalls(); 31 | } 32 | return instance; 33 | } 34 | 35 | private RemoteCalls() { 36 | sshChannel = new JSch(); 37 | } 38 | 39 | public void connect(String user, String ip, String pass) { 40 | try { 41 | 42 | conn = sshChannel.getSession(user, ip, 22); 43 | conn.setPassword(pass); 44 | java.util.Properties config = new java.util.Properties(); 45 | config.put("StrictHostKeyChecking", "no"); 46 | conn.setConfig(config); 47 | conn.connect(); 48 | isConnected = true; 49 | } catch (JSchException e) { 50 | // TODO Auto-generated catch block 51 | //e.printStackTrace(); 52 | } 53 | } 54 | private void transferFiles(String srcPath, String dstPath, ChannelSftp channel) throws SftpException, FileNotFoundException { 55 | File[] files; 56 | File newFile = new File(srcPath); 57 | 58 | if(newFile.isDirectory()) { 59 | files = newFile.listFiles(); 60 | } 61 | else { 62 | files = new File[] {newFile}; 63 | } 64 | 65 | for (File file : files) { 66 | if (file.isDirectory()) { 67 | transferFiles(file.getAbsolutePath(), dstPath, channel); 68 | } else { 69 | System.out.println("File: " + file.getName()); 70 | channel.cd(dstPath); 71 | channel.put(new FileInputStream(file), file.getName()); 72 | } 73 | } 74 | } 75 | 76 | private void folderCopy(String srcPath, String dstPath, ChannelSftp channel) throws SftpException, FileNotFoundException { 77 | String sep = System.getProperty("file.separator"); 78 | 79 | Vector srcList = channel.ls(srcPath); 80 | for (LsEntry srcItem : srcList) { 81 | String srcFN = srcItem.getFilename(); 82 | 83 | if(srcItem.getAttrs().isDir() == false) { 84 | new File(dstPath + sep + srcFN); 85 | channel.get(srcPath + "/" + srcFN, dstPath + sep + srcFN); 86 | } 87 | else if(".".equals(srcFN) == false && "..".equals(srcFN) == false ) { 88 | new File(dstPath + sep + srcFN).mkdirs(); 89 | folderCopy(srcPath + sep + srcFN, dstPath + sep + srcFN, channel ); 90 | } 91 | } 92 | 93 | } 94 | public void getFiles(String srcPath, String dstPath) { 95 | if(isConnected == false) { 96 | System.out.println("Not connected connect first"); 97 | System.exit(0); 98 | } 99 | try { 100 | ChannelSftp channel = (ChannelSftp)conn.openChannel("sftp"); 101 | 102 | channel.connect(); 103 | new File(dstPath).mkdirs(); 104 | channel.lcd(dstPath); 105 | folderCopy(srcPath, dstPath, channel); 106 | channel.exit(); 107 | channel.disconnect(); 108 | 109 | } catch (Exception e) { 110 | // TODO Auto-generated catch block 111 | e.printStackTrace(); 112 | } 113 | 114 | } 115 | public void sendFiles(String srcPath, String dstPath) { 116 | if(isConnected == false) { 117 | System.out.println("Not connected connect first"); 118 | System.exit(0); 119 | } 120 | try { 121 | ChannelSftp channel = (ChannelSftp)conn.openChannel("sftp"); 122 | 123 | channel.connect(); 124 | transferFiles(srcPath, dstPath, channel); 125 | channel.exit(); 126 | channel.disconnect(); 127 | 128 | } catch (Exception e) { 129 | // TODO Auto-generated catch block 130 | e.printStackTrace(); 131 | } 132 | 133 | } 134 | public String sendCommand(String cmd) { 135 | return _sendCommand(cmd, false); 136 | } 137 | public String _sendCommand(String cmd, boolean seeOutput) { 138 | if(isConnected == false) { 139 | System.out.println("Not connected connect first"); 140 | System.exit(0); 141 | } 142 | StringBuilder outputBuffer = new StringBuilder(); 143 | 144 | try { 145 | Channel channel = conn.openChannel("exec"); 146 | ((ChannelExec)channel).setCommand(cmd); 147 | 148 | InputStream out = channel.getInputStream(); 149 | channel.connect(); 150 | 151 | int readByte = out.read(); 152 | while(readByte != 0xffffffff) { 153 | if(seeOutput == true) 154 | System.out.print((char)readByte); 155 | outputBuffer.append((char)readByte); 156 | readByte = out.read(); 157 | 158 | } 159 | 160 | channel.disconnect(); 161 | String outBuf = outputBuffer.toString(); 162 | out.close(); 163 | return outBuf; 164 | 165 | } 166 | catch(Exception e) { 167 | System.out.println(e.getMessage()); 168 | System.exit(0); 169 | } 170 | 171 | return ""; 172 | 173 | } 174 | 175 | 176 | } -------------------------------------------------------------------------------- /src/main/java/com/omnissa/software_forensic_kit/java_gadget/Java_Gadget_Dynamic.java: -------------------------------------------------------------------------------- 1 | /*************************************************** 2 | * Copyright 2019 Omnissa, LLC. 3 | * SPDX-License-Identifier: BSD-2-Clause 4 | ***************************************************/ 5 | package com.omnissa.software_forensic_kit.java_gadget; 6 | 7 | import java.io.File; 8 | import java.lang.management.ManagementFactory; 9 | import java.util.ArrayList; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | import java.util.stream.Collectors; 13 | 14 | import org.apache.commons.cli.CommandLine; 15 | import org.apache.commons.cli.CommandLineParser; 16 | import org.apache.commons.cli.DefaultParser; 17 | import org.apache.commons.cli.HelpFormatter; 18 | import org.apache.commons.cli.Option; 19 | import org.apache.commons.cli.Options; 20 | import org.apache.commons.cli.ParseException; 21 | 22 | import com.omnissa.software_forensic_kit.java_gadget.local.LocalCalls; 23 | 24 | public class Java_Gadget_Dynamic{ 25 | 26 | public static void printUsage(HelpFormatter formatter, Options options) { 27 | String footer = "Example Usage:\r\n" + 28 | " >java -cp software_forensic_kit.jar com.omnissa.software_forensic_kit.java_gadget.Java_Gadget_Dynamic -s \"ExampleClass:functionA\" -im -rp my.class.path. /home/test/testfile.jar\r\n" + 29 | " or using wildcards * for filter\r\n" + 30 | " >java -cp software_forensic_kit.jar com.omnissa.software_forensic_kit.java_gadget.Java_Gadget_Dynamic -s \"ExampleClass*functionA\" -pp -rp my.class.path. /var/test/example.jar "; 31 | formatter.setOptionComparator(null); 32 | formatter.printHelp("Java_Gadget_Dynamic [OPTIONS] \nOptions include:", "", options, footer, true); 33 | 34 | System.exit(1); 35 | } 36 | public static Options createOptions() { 37 | Options options = new Options(); 38 | options.addOption(Option.builder("s").longOpt("searchFunction") 39 | .desc("Function to search for") 40 | .hasArg() 41 | .argName("FUNCTION") 42 | .build()); 43 | 44 | return options; 45 | } 46 | public static void main(String[] args) { 47 | 48 | Options options = createOptions(); 49 | 50 | CommandLineParser parser = new DefaultParser(); 51 | HelpFormatter formatter = new HelpFormatter(); 52 | CommandLine cmd = null; 53 | 54 | try { 55 | cmd = parser.parse(options, args); 56 | } catch (ParseException e) { 57 | System.out.println(e.getMessage()); 58 | printUsage(formatter, options); 59 | } 60 | 61 | if(cmd.getOptions().length < 1) 62 | printUsage(formatter, options); 63 | 64 | HashMap map = new HashMap(); 65 | for (Option opt : cmd.getOptions()) { 66 | map.put(opt.getLongOpt(), cmd.getOptionValue(opt.getOpt())); 67 | } 68 | 69 | executeRequest(map); 70 | 71 | } 72 | 73 | public static void executeRequest(HashMap options) { 74 | 75 | String sep = System.getProperty("file.separator"); 76 | String localCG = "files" + sep + "callgraph"; 77 | String callgraphFullPath = LocalCalls.getJarPath() + sep + localCG; 78 | 79 | String funcName = options.get("searchFunction"); 80 | 81 | ArrayList output = LocalCalls._runCommand(new String[] {"/bin/sh", "-c", "pgrep -f java | xargs -I '{}' lsof -p '{}' | grep 'jar' | awk '{print $2\";\"$9}'"}); 82 | ArrayList uniqueJars = new ArrayList(); 83 | HashMap> pidJarMap = new HashMap>(); 84 | String mypid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; 85 | 86 | String[] items; 87 | for(String line: output) { 88 | items = line.split(";"); 89 | if(items[0] != mypid) { 90 | if(uniqueJars.contains(items[1]) == false) 91 | uniqueJars.add(items[1]); 92 | 93 | if(pidJarMap.containsKey(items[0]) == false) { 94 | ArrayList newJarList = new ArrayList(); 95 | newJarList.add(items[1]); 96 | pidJarMap.put(items[0], newJarList); 97 | } 98 | else { 99 | ArrayList tempJarList = pidJarMap.get(items[0]); 100 | tempJarList.add(items[1]); 101 | pidJarMap.put(items[0], tempJarList); 102 | } 103 | } 104 | } 105 | System.out.println(String.format("PID running java: \n%s", String.join("\n", pidJarMap.keySet()))); 106 | 107 | System.out.println(String.format("Creating CallGraphs for %d jars: \n", uniqueJars.size())); 108 | LocalCalls.createCallGraphs(uniqueJars, localCG); 109 | 110 | Map jarsToCG = uniqueJars.stream().collect(Collectors.toMap(x -> String.format("%s%s", callgraphFullPath + sep, LocalCalls.jarFileToCallGraphFile(x)), x -> x.toString())); 111 | //LocalCalls._runCommand(new String[] {"/bin/sh", "-c", "cwd"}); 112 | //TODO replace * with .* for grep 113 | System.out.println(String.format("'grep -rl %s %s'", funcName, callgraphFullPath)); 114 | output = LocalCalls._runCommand(new String[] {"/bin/sh", "-c", String.format("grep -rl %s %s", funcName, callgraphFullPath)}); 115 | System.out.println(String.format("\n%s", String.join("\n", output) )); 116 | 117 | 118 | HashMap jarMapMatches = new HashMap(); 119 | for(String key : jarsToCG.keySet()) { 120 | if(output.contains(key)){ 121 | jarMapMatches.put(key, jarsToCG.get(key)); 122 | } 123 | } 124 | 125 | ArrayList matchedPids = new ArrayList(); 126 | 127 | for(String key : pidJarMap.keySet()) { 128 | for (String value: jarMapMatches.values()) { 129 | if(pidJarMap.get(key).contains(value)) { 130 | //add it and break 131 | matchedPids.add(key); 132 | break; 133 | } 134 | } 135 | } 136 | System.out.println(String.format("Matched PIDs: \n%s", String.join("\n", matchedPids))); 137 | } 138 | 139 | 140 | } -------------------------------------------------------------------------------- /src/main/java/com/omnissa/software_forensic_kit/java_gadget/Java_Gadget.java: -------------------------------------------------------------------------------- 1 | /*************************************************** 2 | * Copyright 2019 Omnissa, LLC. 3 | * SPDX-License-Identifier: BSD-2-Clause 4 | ***************************************************/ 5 | package com.omnissa.software_forensic_kit.java_gadget; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | 12 | import org.apache.commons.cli.CommandLine; 13 | import org.apache.commons.cli.CommandLineParser; 14 | import org.apache.commons.cli.DefaultParser; 15 | import org.apache.commons.cli.HelpFormatter; 16 | import org.apache.commons.cli.Option; 17 | import org.apache.commons.cli.Options; 18 | import org.apache.commons.cli.ParseException; 19 | 20 | import com.omnissa.software_forensic_kit.java_gadget.local.LocalCalls; 21 | 22 | public class Java_Gadget{ 23 | 24 | public static void printUsage(HelpFormatter formatter, Options options) { 25 | String footer = "Example Usage:\r\n" + 26 | " >java -cp software_forensic_kit.jar com.omnissa.software_forensic_kit.java_gadget.Java_Gadget -s \"ExampleClass:functionA\" -hm -rp my.class.path. /home/test/testfile.jar\r\n" + 27 | " or using wildcards * for filter\r\n" + 28 | " >java -cp software_forensic_kit.jar com.omnissa.software_forensic_kit.java_gadget.Java_Gadget -s \"ExampleClass*functionA\" -pp -rp my.class.path. /var/test/example.jar "; 29 | formatter.setOptionComparator(null); 30 | formatter.printHelp("Java_Gadget [OPTIONS] \nOptions include:", "", options, footer, true); 31 | 32 | System.exit(1); 33 | } 34 | public static Options createOptions() { 35 | Options options = new Options(); 36 | options.addOption(Option.builder("s").longOpt("searchFunction") 37 | .desc("Function to search for") 38 | .hasArg() 39 | .argName("FUNCTION") 40 | .build()); 41 | options.addOption(Option.builder("cd").longOpt("depth") 42 | .desc("Max callgraph depth (default is 8)") 43 | .hasArg() 44 | .argName("NUM") 45 | .build()); 46 | options.addOption(Option.builder("rp").longOpt("removePrefix") 47 | .desc("Remove Prefix from callgraph text") 48 | .hasArg() 49 | .build()); 50 | options.addOption(Option.builder("rd").longOpt("removeDuplicates") 51 | .desc("Remove Duplicate Paths") 52 | .build()); 53 | options.addOption(Option.builder("jp").longOpt("justPrint") 54 | .desc("Print output to console") 55 | .build()); 56 | options.addOption(Option.builder("pp").longOpt("prettyPrint") 57 | .desc("Pretty Print output to console") 58 | .build()); 59 | options.addOption(Option.builder("g").longOpt("graphViz") 60 | .desc("Output for Graphviz") 61 | .build()); 62 | options.addOption(Option.builder("gm").longOpt("graphVizM") 63 | .desc("Output for Graphviz Multiple") 64 | .build()); 65 | options.addOption(Option.builder("hi").longOpt("html") 66 | .desc("Output for HTML - https://visjs.org") 67 | .build()); 68 | options.addOption(Option.builder("hm").longOpt("htmlM") 69 | .desc("Output for HTML Multiple - https://visjs.org") 70 | .build()); 71 | options.addOption(Option.builder("cgf").longOpt("callgraphFile") 72 | .desc("Passing in CallGraph file instead of jar(s)") 73 | .build()); 74 | options.addOption(Option.builder("I").longOpt("interactive") 75 | .desc("Interactive Mode") 76 | .build()); 77 | options.addOption(Option.builder("v").longOpt("verbose") 78 | .desc("Verbose") 79 | .build()); 80 | 81 | return options; 82 | } 83 | public static void main(String[] args) { 84 | 85 | Options options = createOptions(); 86 | 87 | CommandLineParser parser = new DefaultParser(); 88 | HelpFormatter formatter = new HelpFormatter(); 89 | CommandLine cmd = null; 90 | 91 | try { 92 | cmd = parser.parse(options, args); 93 | } catch (ParseException e) { 94 | System.out.println(e.getMessage()); 95 | printUsage(formatter, options); 96 | } 97 | 98 | if(cmd.getOptions().length < 1) 99 | printUsage(formatter, options); 100 | 101 | //String user = cmd.getOptionValue("username"); 102 | 103 | //TODO: parse options send options to static method. 104 | // The static method will execute the request. 105 | HashMap map = new HashMap(); 106 | for (Option opt : cmd.getOptions()) { 107 | map.put(opt.getLongOpt(), cmd.getOptionValue(opt.getOpt())); 108 | } 109 | 110 | List leftover = cmd.getArgList(); 111 | for(String l : leftover) { 112 | System.out.println(l); 113 | } 114 | executeRequest(map, leftover.get(0)); 115 | 116 | } 117 | 118 | public static void executeRequest(HashMap options, String jarFile) { 119 | 120 | String sep = System.getProperty("file.separator"); 121 | String localCG = "files" + sep + "callgraph"; 122 | 123 | String callgraphFullPath = ""; 124 | ArrayList jarList = new ArrayList(Arrays.asList(jarFile)); 125 | 126 | if(options.containsKey("callgraphFile") == false) { 127 | LocalCalls.createCallGraphs(jarList, localCG); 128 | 129 | String callgraphOut = LocalCalls.jarFileToCallGraphFile(jarFile); 130 | 131 | String jarPath = LocalCalls.getJarPath(); 132 | 133 | 134 | callgraphFullPath = jarPath + sep + localCG + sep + callgraphOut; 135 | } 136 | else { 137 | callgraphFullPath = jarFile; 138 | } 139 | System.out.println("PATH: " + callgraphFullPath); 140 | executeRequestUsingCGFile(options, callgraphFullPath); 141 | System.out.println("DONE"); 142 | 143 | 144 | } 145 | public static void executeRequestUsingCGFile(HashMap options, String localCG){ 146 | //System.out.println(options); 147 | CallGraphAnalysis gca = new CallGraphAnalysis(localCG, options); 148 | gca.run(); 149 | } 150 | } -------------------------------------------------------------------------------- /src/main/java/com/omnissa/software_forensic_kit/java_gadget/CallGraphNode.java: -------------------------------------------------------------------------------- 1 | /*************************************************** 2 | * Copyright 2019 Omnissa, LLC. 3 | * SPDX-License-Identifier: BSD-2-Clause 4 | ***************************************************/ 5 | package com.omnissa.software_forensic_kit.java_gadget; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.HashSet; 10 | import java.util.List; 11 | import java.util.Set; 12 | 13 | public class CallGraphNode { 14 | 15 | public CallGraphNode parent; 16 | public String data; 17 | public ArrayList children = new ArrayList(); 18 | 19 | public CallGraphNode(String data) { 20 | this.data = data; 21 | } 22 | public String toString() { 23 | return this.data.toString(); 24 | } 25 | 26 | public void addChild(CallGraphNode child) { 27 | child.parent = this; 28 | children.add(child); 29 | } 30 | 31 | public String cleanData(String removeFromStart) { 32 | String output = this.toString(); 33 | if(removeFromStart != null && output.startsWith(removeFromStart)) { 34 | output = output.substring(removeFromStart.length()); 35 | } 36 | List specialChars = Arrays.asList("()", "()"); 37 | for(String chars : specialChars) { 38 | output = output.replaceAll(chars, ""); 39 | } 40 | output = output.replaceAll(",", "_"); 41 | output = output.replaceAll("__", "_"); 42 | return output; 43 | } 44 | private ArrayList findLeafs() { 45 | ArrayList leafs = new ArrayList(); 46 | 47 | if(children.size() == 0) { 48 | return new ArrayList(Arrays.asList(this)); 49 | } 50 | else{ 51 | for(CallGraphNode child : children) { 52 | leafs.addAll(child.findLeafs()); 53 | } 54 | } 55 | 56 | 57 | return leafs; 58 | } 59 | public void prettyPrint(int maxDepth, String removeFromStart, boolean removeDuplicates) { 60 | ArrayList leafs = findLeafs(); 61 | 62 | for(CallGraphNode leafNode : leafs) { 63 | String output = leafNode.data; 64 | if(removeFromStart != null && output.startsWith(removeFromStart)) { 65 | output = output.substring(removeFromStart.length()); 66 | } 67 | 68 | int i = 0; 69 | while(leafNode.parent != null) { 70 | String parentOut = leafNode.parent.data; 71 | if(removeFromStart != null && parentOut.startsWith(removeFromStart)) { 72 | parentOut = parentOut.substring(removeFromStart.length()); 73 | } 74 | output += "\n\t -> " + parentOut; 75 | leafNode = leafNode.parent; 76 | 77 | } 78 | System.out.println(output + "\n#####################################################################################################################\n\n"); 79 | 80 | } 81 | 82 | } 83 | public void justPrint(int maxDepth, String removeFromStart, boolean removeDuplicates) { 84 | ArrayList leafs = findLeafs(); 85 | 86 | for(CallGraphNode leafNode : leafs) { 87 | String output = leafNode.data; 88 | if(removeFromStart != null && output.startsWith(removeFromStart)) { 89 | output = output.substring(removeFromStart.length()); 90 | } 91 | 92 | int i = 0; 93 | while(leafNode.parent != null) { 94 | String parentOut = leafNode.parent.data; 95 | if(removeFromStart != null && parentOut.startsWith(removeFromStart)) { 96 | parentOut = parentOut.substring(removeFromStart.length()); 97 | } 98 | output += "\n" + parentOut; 99 | leafNode = leafNode.parent; 100 | 101 | } 102 | System.out.println(output + ";"); 103 | } 104 | 105 | } 106 | 107 | public ArrayList htmlandgraphvizMultiple(boolean returnJSON, int maxDepth, String removeFromStart, boolean removeDuplicates) { 108 | 109 | ArrayList leafNodes = findLeafs(); 110 | ArrayList totals = new ArrayList(); 111 | ArrayList outlines = new ArrayList(); 112 | int i = 0; 113 | String output = ""; 114 | 115 | 116 | 117 | 118 | for (CallGraphNode leafNode : leafNodes) { 119 | i += 1; 120 | if(returnJSON == false) 121 | output = String.format("digraph leaf%d {\nratio=compress\n", i); 122 | else 123 | output = "["; 124 | outlines = new ArrayList(); 125 | 126 | while(leafNode.parent != null) { 127 | 128 | String leafdata = leafNode.cleanData(removeFromStart); 129 | String pleafdata = leafNode.parent.cleanData(removeFromStart); 130 | String line = ""; 131 | if(!leafdata.equals(pleafdata) ) { 132 | if(returnJSON == false) { 133 | line = String.format("\n%s -> %s ;" , leafdata, pleafdata); 134 | } 135 | else { 136 | line = String.format("{\"from\": \"%s\", \"to\": \"%s\"}," , leafdata, pleafdata); 137 | } 138 | if(outlines.contains(line) == false) { 139 | outlines.add(line); 140 | } 141 | } 142 | leafNode = leafNode.parent; 143 | } 144 | 145 | if(removeDuplicates) { 146 | Set set = new HashSet(); 147 | set.addAll(outlines); 148 | outlines.clear(); 149 | outlines.addAll(set); 150 | 151 | } 152 | 153 | output += String.join("", outlines); 154 | 155 | if(returnJSON) { 156 | totals.add(output.substring(0, output.length() -1) + "]"); 157 | } 158 | else { 159 | totals.add(output + "\n}\n"); 160 | } 161 | } 162 | return totals; 163 | } 164 | public ArrayList htmlandgraphviz(boolean returnJSON, int maxDepth, String removeFromStart, boolean removeDuplicates) { 165 | 166 | ArrayList leafNodes = findLeafs(); 167 | ArrayList totals = new ArrayList(); 168 | ArrayList outlines = new ArrayList(); 169 | int i = 0; 170 | String output = ""; 171 | 172 | if(returnJSON == false) 173 | output = String.format("digraph leaf%d {\nratio=compress\n", i); 174 | else 175 | output = "["; 176 | 177 | 178 | for (CallGraphNode leafNode : leafNodes) { 179 | i += 1; 180 | 181 | while(leafNode.parent != null) { 182 | 183 | String leafdata = leafNode.cleanData(removeFromStart); 184 | String pleafdata = leafNode.parent.cleanData(removeFromStart); 185 | if(!leafdata.equals(pleafdata) ) { 186 | if(returnJSON == false) 187 | outlines.add(String.format("\n%s -> %s ;" , leafdata, pleafdata)); 188 | else 189 | outlines.add(String.format("{\"from\": \"%s\", \"to\": \"%s\"}," , leafdata, pleafdata)); 190 | } 191 | leafNode = leafNode.parent; 192 | } 193 | } 194 | if(removeDuplicates) { 195 | Set set = new HashSet(); 196 | set.addAll(outlines); 197 | outlines.clear(); 198 | outlines.addAll(set); 199 | 200 | } 201 | 202 | output += String.join("", outlines); 203 | 204 | if(returnJSON) { 205 | totals.add(output.substring(0, output.length() -1) + "]"); 206 | } 207 | else { 208 | totals.add(output + "\n}\n"); 209 | } 210 | return totals; 211 | } 212 | } -------------------------------------------------------------------------------- /src/main/java/com/omnissa/software_forensic_kit/java_gadget/local/LocalCalls.java: -------------------------------------------------------------------------------- 1 | /*************************************************** 2 | * Copyright 2019 Omnissa, LLC. 3 | * SPDX-License-Identifier: BSD-2-Clause 4 | ***************************************************/ 5 | package com.omnissa.software_forensic_kit.java_gadget.local; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.BufferedWriter; 9 | import java.io.File; 10 | import java.io.FileWriter; 11 | import java.io.IOException; 12 | import java.io.InputStreamReader; 13 | import java.io.PrintStream; 14 | import java.net.URISyntaxException; 15 | import java.nio.file.Files; 16 | import java.nio.file.Path; 17 | import java.nio.file.Paths; 18 | import java.util.ArrayList; 19 | import java.util.Collections; 20 | import java.util.List; 21 | import java.util.concurrent.Callable; 22 | import java.util.concurrent.ExecutorService; 23 | import java.util.concurrent.Executors; 24 | import java.util.concurrent.TimeUnit; 25 | import java.util.regex.Matcher; 26 | import java.util.stream.Stream; 27 | 28 | public class LocalCalls{ 29 | 30 | public static List outputList = Collections.synchronizedList(new ArrayList()); 31 | public static String OS = System.getProperty("os.name"); 32 | public static int createdJarNumber = 0; 33 | public static String getJarPath() { 34 | try { 35 | return new File(LocalCalls.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getParent(); 36 | } catch (URISyntaxException e) { 37 | // TODO Auto-generated catch block 38 | return ""; 39 | } 40 | 41 | } 42 | public static Stream walkPath(Path path){ 43 | if(Files.isReadable(path) == false) 44 | return Stream.of(path); 45 | 46 | if(Files.isDirectory(path) == false) 47 | return Stream.of(path); 48 | try{ 49 | return Stream.concat(Stream.of(path), Files.list(path)); 50 | } 51 | catch (IOException ioe){ 52 | return Stream.of(path); 53 | } 54 | 55 | } 56 | public static ArrayList findJars(String filter) { 57 | return _findJars(filter, false); 58 | } 59 | public static ArrayList _findJars(String filter, boolean printToConsole) { 60 | Runtime runtime = Runtime.getRuntime(); 61 | 62 | String linuxDirectory = "/"; 63 | String windowsDirectory = "C:\\"; 64 | 65 | String directory = linuxDirectory; 66 | if(LocalCalls.OS.startsWith("Windows")) 67 | directory = windowsDirectory; 68 | 69 | ArrayList jarList = new ArrayList(); 70 | String prefixPath = getJarPath(); 71 | 72 | // Process proc = windowsbuilder.start(); 73 | //DEBUG REMOVE 74 | Stream mapped; 75 | LocalFileVisitor visit = new LocalFileVisitor(printToConsole, ".jar", filter); 76 | try { 77 | 78 | Files.walkFileTree(Paths.get(directory), visit); 79 | 80 | return visit.jarList(); 81 | //mapped = Files.walk(Paths.get(directory)) 82 | //.flatMap(LocalCalls::walkPath) 83 | 84 | //mapped = Files.walkFileTree(Paths.get(directory), visit) 85 | //.filter(x -> x.toString().endsWith(".jar")) 86 | //.filter(x -> x.toString().contains(filter)); 87 | //mapped.forEach(x -> {jarList.add(x.toString()); if(printToConsole)System.out.println(String.format("Jar_Found:[%s]", x)); }); 88 | } catch (IOException e) { 89 | // TODO Auto-generated catch block 90 | e.printStackTrace(); 91 | } 92 | 93 | //.filter(Files::isRegularFile) 94 | return null; 95 | 96 | } 97 | public static ArrayList _runCommand(String[] commands) { 98 | Runtime runTime = Runtime.getRuntime(); 99 | ArrayList results = new ArrayList(); 100 | 101 | try { 102 | 103 | Process pid = runTime.exec(commands); 104 | BufferedReader stdInput = new BufferedReader(new InputStreamReader(pid.getInputStream())); 105 | BufferedReader stdError = new BufferedReader(new InputStreamReader(pid.getErrorStream())); 106 | String s = null; 107 | 108 | while ((s = stdInput.readLine()) != null) { results.add(s); } 109 | while ((s = stdError.readLine()) != null) { System.out.println(s); } 110 | } catch (IOException e) { 111 | // TODO Auto-generated catch block 112 | e.printStackTrace(); 113 | } 114 | return results; 115 | 116 | 117 | 118 | } 119 | public static String jarFileToCallGraphFile(String jar) { 120 | String sep = System.getProperty("file.separator"); 121 | String outputFile = jar.replaceAll(Matcher.quoteReplacement(File.separator), "_"); 122 | outputFile = outputFile.replaceAll(":", "_"); 123 | outputFile = outputFile.replace(".jar", ".txt"); 124 | return outputFile; 125 | } 126 | 127 | public static ArrayList createCallGraphs(ArrayList jarList , String fileLoc) { 128 | //java -cp software_forensic_kit.jar gr.gousiosg.javacg.stat.JCallGraph 129 | final String jarPath = getJarPath(); 130 | ArrayList outList = new ArrayList(); 131 | String jarName = new java.io.File(LocalCalls.class.getProtectionDomain() 132 | .getCodeSource() 133 | .getLocation() 134 | .getPath()) 135 | .getName(); 136 | 137 | 138 | String sep = System.getProperty("file.separator"); 139 | ExecutorService executorService = Executors.newFixedThreadPool(20); 140 | 141 | PrintStream console = System.out; 142 | ArrayList options = new ArrayList(); 143 | options.add("-m"); 144 | int total = jarList.size(); 145 | createdJarNumber = 0; 146 | makeDirsNearJar(fileLoc); 147 | 148 | try { 149 | for (String jar : jarList) { 150 | 151 | executorService.execute(() -> { 152 | String outputFile = LocalCalls.jarFileToCallGraphFile(jar); 153 | 154 | if(new File(jarPath + sep + fileLoc + sep + outputFile).exists() == false) { 155 | Runtime runtime = Runtime.getRuntime(); 156 | String[] commands = {"java", "-cp", jarPath + sep + jarName, "com.omnissa.software_forensic_kit.java_gadget.local.JarCallGraph", jar}; 157 | 158 | try { 159 | 160 | Process proc = runtime.exec(commands); 161 | BufferedReader output = new BufferedReader(new 162 | InputStreamReader(proc.getInputStream())); 163 | BufferedWriter input = new BufferedWriter(new FileWriter(jarPath + sep + fileLoc + sep + outputFile)); 164 | outputList.add(jarPath + sep + fileLoc + sep + outputFile); 165 | 166 | String s = output.readLine(); 167 | while (s != null) { 168 | input.write(s); 169 | input.newLine(); 170 | s = output.readLine(); 171 | } 172 | createdJarNumber += 1; 173 | System.out.println(String.format("%d of %d", createdJarNumber, total)); 174 | output.close(); 175 | input.close(); 176 | 177 | } catch (IOException e) { 178 | // TODO Auto-generated catch block 179 | System.out.println("Error?"); 180 | System.out.println(e); 181 | e.printStackTrace(); 182 | } 183 | } 184 | else { 185 | outputList.add(jarPath + sep + fileLoc + sep + outputFile); 186 | } 187 | }); 188 | 189 | } 190 | } 191 | finally { 192 | executorService.shutdown(); 193 | } 194 | try { 195 | executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); 196 | } catch (InterruptedException e) { 197 | // TODO Auto-generated catch block 198 | e.printStackTrace(); 199 | } 200 | 201 | 202 | return new ArrayList(outputList); 203 | } 204 | 205 | public static boolean makeDirsNearJar(String dirs) { 206 | 207 | String jarPath = getJarPath(); 208 | String sep = System.getProperty("file.separator"); 209 | return new File(jarPath + sep + dirs).mkdirs(); 210 | 211 | } 212 | 213 | } 214 | 215 | -------------------------------------------------------------------------------- /src/main/java/com/omnissa/software_forensic_kit/java_gadget/Java_Gadget_Injector.java: -------------------------------------------------------------------------------- 1 | package com.omnissa.software_forensic_kit.java_gadget; 2 | 3 | import java.io.Console; 4 | import java.io.EOFException; 5 | import java.io.IOException; 6 | import java.io.ObjectInputStream; 7 | import java.net.ServerSocket; 8 | import java.net.Socket; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | import org.apache.commons.cli.CommandLine; 14 | import org.apache.commons.cli.CommandLineParser; 15 | import org.apache.commons.cli.DefaultParser; 16 | import org.apache.commons.cli.HelpFormatter; 17 | import org.apache.commons.cli.Option; 18 | import org.apache.commons.cli.Options; 19 | import org.apache.commons.cli.ParseException; 20 | 21 | import com.sun.tools.attach.VirtualMachine; 22 | import com.sun.tools.attach.VirtualMachineDescriptor; 23 | import com.omnissa.software_forensic_kit.java_gadget.local.LocalCalls; 24 | 25 | public class Java_Gadget_Injector{ 26 | public static boolean exitserver = false; 27 | 28 | public static void printUsage(HelpFormatter formatter, Options options) { 29 | String footer = "Example Usage:\r\n" + 30 | " >java -cp software_forensic_kit.jar com.omnissa.software_forensic_kit.java_gadget.Java_Gadget_Injector -s \"ExampleClass;functionA\" -pid 123\r\n" + 31 | " or using wildcards * for filter\r\n" + 32 | " >java -cp software_forensic_kit.jar com.omnissa.software_forensic_kit.java_gadget.Java_Gadget_Injector -s \"ExampleClass;functionA\" -pid \"123;124;1235\""; 33 | formatter.setOptionComparator(null); 34 | formatter.printHelp("Java_Gadget_Dynamic [OPTIONS] \nOptions include:", "", options, footer, true); 35 | 36 | System.exit(1); 37 | } 38 | public static Options createOptions() { 39 | Options options = new Options(); 40 | options.addOption(Option.builder("s").longOpt("searchFunction") 41 | .desc("Function to search for") 42 | .hasArg() 43 | .argName("FUNCTION") 44 | .build()); 45 | options.addOption(Option.builder("pid").longOpt("PID") 46 | .desc("PID(s) to inject into") 47 | .hasArg() 48 | .argName("pid") 49 | .build()); 50 | options.addOption(Option.builder("lpid").longOpt("LPID") 51 | .desc("List Java PID(s)") 52 | .build()); 53 | return options; 54 | } 55 | public static void main(String[] args) { 56 | 57 | Options options = createOptions(); 58 | 59 | CommandLineParser parser = new DefaultParser(); 60 | HelpFormatter formatter = new HelpFormatter(); 61 | CommandLine cmd = null; 62 | 63 | try { 64 | cmd = parser.parse(options, args); 65 | } catch (ParseException e) { 66 | System.out.println(e.getMessage()); 67 | printUsage(formatter, options); 68 | } 69 | 70 | if(cmd.getOptions().length < 1) 71 | printUsage(formatter, options); 72 | 73 | if(cmd.hasOption("lpid")) { 74 | getJavaPIDList(); 75 | System.exit(1); 76 | } 77 | ArrayList pidList = new ArrayList (); 78 | String pids = cmd.getOptionValue("PID"); 79 | if(pids.matches("^(0|[1-9][0-9]*)$")) { 80 | pidList.add(pids); 81 | } 82 | else if(pids.contains(";")) { 83 | pidList.addAll(Arrays.asList(pids.split(";"))); 84 | } 85 | 86 | inject(pidList, cmd.getOptionValue("searchFunction")); 87 | 88 | } 89 | public static void getJavaPIDList() { 90 | 91 | //if linux 92 | //ps aux | grep java | awk {'print $2 " " $1 " " $11'} 93 | //windows 94 | //powershell -c "Get-WmiObject Win32_Process -Filter \"name = 'java.exe'\" | Select-Object ProcessId,CommandLine" 95 | ArrayList output = new ArrayList(); 96 | 97 | if(LocalCalls.OS.startsWith("Windows")) { output = LocalCalls._runCommand(new String[] {"powershell", "-c", "\"Get-WmiObject Win32_Process -Filter \\\"name = 'java.exe'\\\" | Select-Object ProcessId,CommandLine\""});} 98 | else { output = LocalCalls._runCommand(new String[] {"/bin/sh", "-c", "ps aux | grep java | awk {'print $2 \" \" $1 \" \" $11'}"});} 99 | 100 | for(String line: output) {System.out.println(line);} 101 | 102 | //VirtualMachine doesn't list jvms not started by this user. 103 | //List jvms = VirtualMachine.list(); 104 | //System.out.println("Processes Running Java:"); 105 | //for (VirtualMachineDescriptor jvm : jvms) {System.out.println(jvm.id() + " - " + jvm.displayName());} 106 | } 107 | public static void inject(ArrayList matchedPids, String classMethodsPath) { 108 | List jvms = VirtualMachine.list(); 109 | String sep = System.getProperty("file.separator"); 110 | exitserver = false; 111 | //Server must be created before attaching javaagent 112 | try { 113 | ServerSocket server = new ServerSocket(31337); 114 | Runnable runnable = () -> { 115 | System.out.println("Server started on port 31337."); 116 | Console console = System.console(); 117 | 118 | while(exitserver == false) { 119 | String out = console.readLine("Listening For Client Input: [E]xit"); 120 | exitserver = out.matches("(?i)e"); 121 | System.out.println(exitserver); 122 | } 123 | System.out.println("Exited"); 124 | try { 125 | server.close(); 126 | System.exit(0); 127 | } catch (IOException e) { 128 | // TODO Auto-generated catch block 129 | e.printStackTrace(); 130 | } 131 | 132 | }; 133 | Thread t = new Thread(runnable); 134 | t.start(); 135 | 136 | VirtualMachine vm = null; 137 | 138 | String jarName = new java.io.File(Java_Gadget_Dynamic.class.getProtectionDomain() 139 | .getCodeSource() 140 | .getLocation() 141 | .getPath()) 142 | .getName(); 143 | System.out.println(String.format("[[%s]]", classMethodsPath)); 144 | for(String pid : matchedPids) { 145 | 146 | System.out.println(String.format("{%s}", pid)); 147 | } 148 | for(String pid : matchedPids) { 149 | try { 150 | 151 | System.out.println(String.format("{%s}", pid)); 152 | vm = VirtualMachine.attach(pid); 153 | vm.loadAgent(LocalCalls.getJarPath() + sep + jarName, classMethodsPath); 154 | vm.detach(); 155 | } catch (Exception e) { 156 | System.out.println(e); 157 | System.out.println(String.format("Couldn't attach to: {%s}", pid)); 158 | } 159 | 160 | } 161 | 162 | Runnable runnable2 = () -> { 163 | try { 164 | 165 | while(exitserver == false){ 166 | 167 | Socket socket = server.accept(); 168 | ObjectInputStream ois = new ObjectInputStream(socket.getInputStream()); 169 | String message = ""; 170 | while(true) { 171 | try { 172 | //try to read all objects and catch fail if no more objects 173 | message = (String) ois.readObject(); 174 | System.out.println("" + message); 175 | } 176 | catch (EOFException e) { 177 | break; 178 | } catch (ClassNotFoundException e) { 179 | // TODO Auto-generated catch block 180 | e.printStackTrace(); 181 | } 182 | } 183 | 184 | ois.close(); 185 | socket.close(); 186 | if(message.equalsIgnoreCase("exit")) break; 187 | 188 | } 189 | System.out.println("Shutting down socket server!!"); 190 | //close the ServerSocket object 191 | server.close(); 192 | } 193 | catch(Exception e) {} 194 | }; 195 | Thread t2 = new Thread(runnable2); 196 | t2.start(); 197 | } catch (IOException e1) { 198 | // TODO Auto-generated catch block 199 | e1.printStackTrace(); 200 | } 201 | 202 | } 203 | } -------------------------------------------------------------------------------- /src/main/java/com/omnissa/software_forensic_kit/java_gadget/agent/HelpMethodVisitor.java: -------------------------------------------------------------------------------- 1 | package com.omnissa.software_forensic_kit.java_gadget.agent; 2 | 3 | import static org.objectweb.asm.Opcodes.ALOAD; 4 | import static org.objectweb.asm.Opcodes.ILOAD; 5 | import static org.objectweb.asm.Opcodes.DLOAD; 6 | import static org.objectweb.asm.Opcodes.ASTORE; 7 | import static org.objectweb.asm.Opcodes.FSTORE; 8 | import static org.objectweb.asm.Opcodes.LSTORE; 9 | import static org.objectweb.asm.Opcodes.DSTORE; 10 | import static org.objectweb.asm.Opcodes.ISTORE; 11 | import static org.objectweb.asm.Opcodes.DUP; 12 | import static org.objectweb.asm.Opcodes.GETSTATIC; 13 | import static org.objectweb.asm.Opcodes.INVOKESPECIAL; 14 | import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; 15 | import static org.objectweb.asm.Opcodes.LLOAD; 16 | import static org.objectweb.asm.Opcodes.NEW; 17 | import static org.objectweb.asm.Opcodes.SIPUSH; 18 | 19 | import java.lang.reflect.Parameter; 20 | 21 | import org.objectweb.asm.Label; 22 | import org.objectweb.asm.MethodVisitor; 23 | import org.objectweb.asm.Opcodes; 24 | import org.objectweb.asm.Type; 25 | import org.objectweb.asm.commons.GeneratorAdapter; 26 | 27 | public class HelpMethodVisitor extends GeneratorAdapter { 28 | 29 | public String methodName; 30 | public String desc; 31 | public String className; 32 | public Parameter paramM[]; 33 | public boolean isAnnotationPresent = false; 34 | 35 | public Label tryStart = new Label(); 36 | public Label tryEnd = new Label(); 37 | public Label catchStart = new Label(); 38 | public Label catchEnd = new Label(); 39 | public HelpMethodVisitor(int access, MethodVisitor mv, String name, String className, String desc, Parameter paramM[]) { 40 | super(Opcodes.ASM5, mv, access, name, desc); 41 | this.methodName = name; 42 | this.desc = desc; 43 | this.className = className; 44 | this.paramM = paramM; 45 | System.out.println(this.methodName); 46 | System.out.println(this.desc); 47 | } 48 | 49 | /*public void visitLocalVariable​(java.lang.String name, java.lang.String descriptor, java.lang.String signature, Label start, Label end, int index) { 50 | super.visitLocalVariable(name, descriptor, signature, start, end, index); 51 | System.out.println(String.format("THERE??? %s %d", name, index)); 52 | this.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); 53 | this.visitVarInsn(LLOAD, index); 54 | this.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(J)V", false); 55 | }*/ 56 | 57 | public void visitVarInsn​(int opcode, int var) { 58 | super.visitVarInsn(opcode, var); 59 | //if(var > 8) { 60 | 61 | 62 | if(opcode == ASTORE || opcode == LSTORE || opcode == ISTORE || opcode == DSTORE ) { 63 | 64 | Type local = null; 65 | try { 66 | local = this.getLocalType(var); 67 | 68 | } 69 | catch(Exception e) { 70 | local = this.getReturnType(); 71 | } 72 | if(local != null) { 73 | System.out.println(String.format("HERE??? %d %d", opcode, var)); 74 | System.out.println(this.getLocalType(var).getDescriptor()); 75 | 76 | if(opcode == ISTORE) { 77 | this.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); 78 | this.visitVarInsn(Opcodes.ILOAD, var); 79 | this.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", String.format("(%s)V", Type.INT_TYPE.getDescriptor()), false); 80 | } 81 | else if(opcode == DSTORE) { 82 | this.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); 83 | this.visitVarInsn(Opcodes.DLOAD, var); 84 | this.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", String.format("(%s)V", Type.DOUBLE_TYPE.getDescriptor()), false); 85 | } 86 | else if(opcode == ASTORE) { 87 | 88 | if(local.getDescriptor().startsWith("L")) { 89 | this.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); 90 | this.visitVarInsn(Opcodes.ALOAD, var); 91 | this.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println",String.format("(%s)V", local.getDescriptor()), false); 92 | } 93 | 94 | } 95 | else if(opcode == LSTORE) { 96 | this.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); 97 | this.visitVarInsn(Opcodes.LLOAD, var); 98 | this.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", String.format("(%s)V", Type.LONG_TYPE.getDescriptor()), false); 99 | 100 | } 101 | //if(opcode == DSTORE) then println Double.. etc... 102 | 103 | //this.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", String.format("(%s)V", this.getLocalType(var).getDescriptor()), false); 104 | } 105 | }//} 106 | 107 | 108 | 109 | } 110 | @Override 111 | public void visitCode() { 112 | 113 | super.visitCode(); 114 | 115 | System.out.println("visitCode"); 116 | this.visitTryCatchBlock(tryStart, tryEnd, catchStart, "java/lang/Exception"); 117 | this.visitLabel(tryStart); 118 | 119 | int socketVal = this.newLocal(Type.LONG_TYPE); 120 | int oosVal = this.newLocal(Type.LONG_TYPE); 121 | System.out.println("::"+socketVal +"::"+oosVal); 122 | 123 | // create socket 124 | this.visitTypeInsn(NEW, "java/net/Socket"); 125 | this.visitInsn(DUP); 126 | this.visitLdcInsn("127.0.0.1"); 127 | this.visitIntInsn(SIPUSH, 31337); 128 | this.visitMethodInsn(INVOKESPECIAL, "java/net/Socket", "", "(Ljava/lang/String;I)V", false); 129 | 130 | this.visitVarInsn(ASTORE, socketVal); 131 | 132 | this.visitTypeInsn(NEW, "java/io/ObjectOutputStream"); 133 | this.visitInsn(DUP); 134 | this.visitVarInsn(ALOAD, socketVal); 135 | this.visitMethodInsn(INVOKEVIRTUAL, "java/net/Socket", "getOutputStream", "()Ljava/io/OutputStream;", false); 136 | this.visitMethodInsn(INVOKESPECIAL, "java/io/ObjectOutputStream", "", "(Ljava/io/OutputStream;)V", false); 137 | this.visitVarInsn(ASTORE, oosVal); 138 | this.visitVarInsn(ALOAD, oosVal); 139 | 140 | //output enter method {'method':'', 'state':'enter'...} 141 | this.visitLdcInsn(String.format("{'Method':'%s','State':'Enter', 'Desc':'%s'}", this.methodName, this.desc)); 142 | this.visitMethodInsn(INVOKEVIRTUAL, "java/io/ObjectOutputStream", "writeObject", "(Ljava/lang/Object;)V", false); 143 | this.visitVarInsn(ALOAD, oosVal); 144 | 145 | //print object parameters 146 | //TODO: support int/char/long/etc...StringBuilder.append functions 147 | //Z for boolean, C for char, B for byte, S for short, I for int, F for float, J for long and D for double. 148 | if(socketVal > 0) { 149 | 150 | System.out.println(socketVal); 151 | Type[] args = Type.getArgumentTypes(this.desc); 152 | for(int i = 0; i", "(Ljava/lang/String;)V", false); 164 | this.loadArg(i); 165 | this.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", String.format("(%s)Ljava/lang/StringBuilder;", args[i].getDescriptor()), false); 166 | 167 | 168 | this.visitLdcInsn("'}"); 169 | this.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false); 170 | this.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false); 171 | this.visitMethodInsn(INVOKEVIRTUAL, "java/io/ObjectOutputStream", "writeObject", "(Ljava/lang/Object;)V", false); 172 | this.visitVarInsn(ALOAD, oosVal); 173 | } 174 | } 175 | //output enter method {'method':'', 'state':'exit'...} 176 | this.visitLdcInsn(String.format("{'Method':'%s','State':'Exit', 'Desc':'%s'}", this.methodName, this.desc)); 177 | 178 | this.visitMethodInsn(INVOKEVIRTUAL, "java/io/ObjectOutputStream", "writeObject", "(Ljava/lang/Object;)V", false); 179 | this.visitVarInsn(ALOAD, oosVal); 180 | this.visitMethodInsn(INVOKEVIRTUAL, "java/io/ObjectOutputStream", "close", "()V", false); 181 | this.visitLabel(tryEnd); 182 | // visit normal execution exit block 183 | this.visitJumpInsn(Opcodes.GOTO, catchEnd); 184 | 185 | // visit catch exception block 186 | this.visitLabel(catchStart); 187 | this.visitVarInsn(ASTORE, oosVal); 188 | this.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); 189 | this.visitLdcInsn("Error: Software_Forensic_Kit Agent Code"); 190 | this.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); 191 | 192 | //On error we either exit with null or ex 193 | /*if(this.desc.endsWith("V")) 194 | this.visitInsn(RETURN); 195 | else { 196 | this.visitInsn(ACONST_NULL); 197 | this.visitInsn(ARETURN); 198 | }*/ 199 | // exit from this dynamic block 200 | this.visitLabel(catchEnd); 201 | 202 | //this.visitJumpInsn(Opcodes.GOTO, catchEnd); 203 | 204 | } 205 | 206 | 207 | @Override 208 | public void visitMaxs(int maxStack, int maxLocals) { 209 | super.visitMaxs(maxStack+8, maxLocals); 210 | } 211 | } 212 | 213 | -------------------------------------------------------------------------------- /src/main/java/com/omnissa/software_forensic_kit/java_gadget/CallGraphAnalysis.java: -------------------------------------------------------------------------------- 1 | /*************************************************** 2 | * Copyright 2019 Omnissa, LLC. 3 | * SPDX-License-Identifier: BSD-2-Clause 4 | ***************************************************/ 5 | package com.omnissa.software_forensic_kit.java_gadget; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.BufferedWriter; 9 | import java.io.File; 10 | import java.io.FileInputStream; 11 | import java.io.FileWriter; 12 | import java.io.IOException; 13 | import java.io.InputStreamReader; 14 | import java.net.URISyntaxException; 15 | import java.util.ArrayList; 16 | import java.util.Arrays; 17 | import java.util.HashMap; 18 | import java.util.List; 19 | import java.util.regex.Pattern; 20 | import java.util.regex.PatternSyntaxException; 21 | 22 | import com.google.gson.Gson; 23 | import com.google.gson.reflect.TypeToken; 24 | import com.omnissa.software_forensic_kit.java_gadget.local.LocalCalls; 25 | 26 | public class CallGraphAnalysis{ 27 | 28 | 29 | private String jarFile; 30 | private HashMap optionMap; 31 | private HashMap> cgMap; 32 | private HashMap> reverse_cgMap; 33 | private ArrayList discoveredFuncs; 34 | private int maxDepth = 8; 35 | private boolean removeDuplicates = false; 36 | private String removeFromStart; 37 | 38 | public CallGraphAnalysis(String jarFile, HashMap optionMap) { 39 | this.jarFile = jarFile; 40 | this.optionMap = optionMap; 41 | 42 | } 43 | public void run() { 44 | 45 | String funcName = optionMap.get("searchFunction"); 46 | 47 | String newfuncName = funcName.replaceAll("\\*", ".+"); 48 | 49 | reverse_cgMap = new HashMap>(); 50 | createMap(); 51 | //fixMap(); 52 | Pattern regex = null; 53 | try{ 54 | regex = Pattern.compile(newfuncName); 55 | } 56 | catch(PatternSyntaxException e) { 57 | 58 | } 59 | ArrayList newfuncNames = new ArrayList(); 60 | for (String key : reverse_cgMap.keySet()) { 61 | if(newfuncNames.contains(key) == false && ((regex != null && regex.matcher((CharSequence)key).find()) || key.contains(newfuncName))){ 62 | newfuncNames.add(key); 63 | 64 | } 65 | } 66 | HashMap reverseFilterMap = filterAndFindMap(newfuncNames, 0, new ArrayList()); 67 | discoveredFuncs = newfuncNames; 68 | 69 | if(discoveredFuncs.size() == 0) { 70 | //System.out.println("Function Name Not Found"); 71 | //System.exit(0); 72 | } 73 | this.parseOptionsAndExit(reverseFilterMap); 74 | } 75 | 76 | 77 | public HashMap filterAndFindMap(ArrayList funcNames, int depth, ArrayList itemsFound) { 78 | 79 | HashMap mapped = new HashMap (); 80 | if(funcNames == null || depth > 25 ) 81 | return mapped; 82 | 83 | depth += 1; 84 | for(String funcName : funcNames) { 85 | 86 | 87 | //System.out.println(funcName + " " + Integer.toString(depth)); 88 | ArrayList temp = (ArrayList)itemsFound.clone(); 89 | 90 | if(!itemsFound.contains(funcName)) { 91 | //System.out.println(funcName); 92 | temp.add(funcName); 93 | String tempFuncName = funcName; 94 | if(funcName.substring(funcName.indexOf(":") + 1).indexOf(":") > -1) { 95 | 96 | tempFuncName = tempFuncName.substring(tempFuncName.indexOf(":") + 1); 97 | } 98 | 99 | mapped.put(funcName, filterAndFindMap(reverse_cgMap.get(tempFuncName), depth, temp)); 100 | } 101 | 102 | } 103 | 104 | return mapped; 105 | } 106 | 107 | 108 | public void createMap() { 109 | 110 | try { 111 | 112 | BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(jarFile))); 113 | 114 | String line = reader.readLine(); 115 | cgMap = new HashMap>(); 116 | 117 | while (line != null) { 118 | if(line.startsWith("M:")) { 119 | String [] parts = line.split("\\s+"); 120 | String key = parts[0].substring(2); 121 | String value = parts[1].substring(3).replaceAll("[\\t\\n\\r]+", ""); 122 | if(!cgMap.containsKey(key)) 123 | cgMap.put(key, new ArrayList()); 124 | cgMap.get(key).add(value); 125 | if(!reverse_cgMap.containsKey(value)) 126 | reverse_cgMap.put(value, new ArrayList()); 127 | reverse_cgMap.get(value).add(key); 128 | } 129 | 130 | line = reader.readLine(); 131 | } 132 | } catch (IOException e) { 133 | // TODO Auto-generated catch block 134 | e.printStackTrace(); 135 | } 136 | 137 | } 138 | 139 | private CallGraphNode createSearch(CallGraphNode node, HashMap mapped) { 140 | 141 | CallGraphNode newNode; 142 | 143 | for (String key : mapped.keySet()) { 144 | newNode = new CallGraphNode(key); 145 | newNode = createSearch(newNode, (HashMap)mapped.get(key)); 146 | node.addChild(newNode); 147 | } 148 | return node; 149 | 150 | } 151 | 152 | private void parseOptionsAndExit(HashMap reverseFilterMap) { 153 | 154 | if(optionMap.containsKey("depth")) { 155 | maxDepth = Integer.parseInt(optionMap.get("depth")); 156 | } 157 | if(optionMap.containsKey("removePrefix")) { 158 | removeFromStart = optionMap.get("removePrefix"); 159 | } 160 | if(optionMap.containsKey("removeDuplicates")) { 161 | removeDuplicates = Boolean.getBoolean(optionMap.get("removeDuplicates")); 162 | } 163 | 164 | ArrayList valList; 165 | for (String funcName : discoveredFuncs) { 166 | 167 | CallGraphNode newNode = new CallGraphNode(funcName); 168 | newNode = createSearch(newNode, reverseFilterMap); 169 | //newNode = startSearch(newNode, 1); 170 | if(optionMap.containsKey("prettyPrint")) { 171 | newNode.prettyPrint(maxDepth, removeFromStart, removeDuplicates); 172 | } 173 | else if(optionMap.containsKey("justPrint")) { 174 | newNode.justPrint(maxDepth, removeFromStart, removeDuplicates); 175 | } 176 | else if(optionMap.containsKey("graphViz")) { 177 | valList = newNode.htmlandgraphviz(false, maxDepth, removeFromStart, removeDuplicates); 178 | System.out.println(String.join("",valList)); 179 | } 180 | else if(optionMap.containsKey("graphVizM")) { 181 | valList = newNode.htmlandgraphvizMultiple(false, maxDepth, removeFromStart, removeDuplicates); 182 | System.out.println(String.join("",valList)); 183 | } 184 | else if(optionMap.containsKey("html")) { 185 | valList = newNode.htmlandgraphviz(true, maxDepth, removeFromStart, removeDuplicates); 186 | //outToFile(newNode.cleanData(removeFromStart), valList); 187 | if(optionMap.containsKey("callgraphFile") == false) { 188 | outToFile(new File(this.jarFile).getName(), valList); 189 | } 190 | else { 191 | outToFile(funcName, valList); 192 | } 193 | } 194 | else { 195 | //htmlM 196 | valList = newNode.htmlandgraphvizMultiple(true, maxDepth, removeFromStart, removeDuplicates); 197 | //outToFile(newNode.cleanData(removeFromStart), valList); 198 | if(optionMap.containsKey("callgraphFile") == false) { 199 | outToFile(new File(this.jarFile).getName(), valList); 200 | } 201 | else { 202 | outToFile(funcName, valList); 203 | } 204 | 205 | } 206 | } 207 | } 208 | public ArrayList findAndRemoveType(String dataVal) { 209 | HashMap colorMap = new HashMap(); 210 | 211 | colorMap.put("public", "green"); 212 | colorMap.put("private", "red"); 213 | colorMap.put("protected", "orange"); 214 | colorMap.put("synchronized", "blue"); 215 | colorMap.put("final", "grey"); 216 | 217 | String color = null; 218 | boolean found = true; 219 | ArrayList titles = new ArrayList(); 220 | 221 | while (found == true) { 222 | found = false; 223 | for(String key: colorMap.keySet()) { 224 | if(dataVal.startsWith(String.format("%s:", key))) { 225 | dataVal = dataVal.substring(key.length() + 1); 226 | color = colorMap.get(key); 227 | titles.add(key); 228 | found = true; 229 | } 230 | } 231 | } 232 | return new ArrayList(Arrays.asList(dataVal, color, String.join(" ", titles))); 233 | } 234 | public ArrayList getNodeId(ArrayList nodes, String dataVal) { 235 | int id = -1; 236 | ArrayList stuff = findAndRemoveType(dataVal); 237 | for(VizJSNode node: nodes) { 238 | if(node.label.equals(dataVal)) { 239 | id = node.id; 240 | } 241 | } 242 | if(id == -1) { 243 | if(nodes.size() > 0) 244 | id = nodes.get(nodes.size() -1).id + 1; 245 | 246 | else 247 | id = 1; 248 | 249 | VizJSNode obj = new VizJSNode(dataVal, id, null, null); 250 | if(stuff.get(1) != null ) 251 | obj.color = stuff.get(1).toString(); 252 | if(stuff.get(2) != null && stuff.get(2).toString().length() > 0) 253 | obj.title = stuff.get(2); 254 | nodes.add(obj); 255 | } 256 | return new ArrayList(Arrays.asList(id, nodes)); 257 | } 258 | 259 | private void outToFile(String funcName, ArrayList dataList) { 260 | int i = 0; 261 | ArrayList nodes = new ArrayList(); 262 | ArrayList edges = new ArrayList(); 263 | List dataArray; 264 | ArrayList toNodeData; 265 | ArrayList fromNodeData; 266 | String line = ""; 267 | for (String data : dataList) { 268 | dataArray = new Gson().fromJson(data, new TypeToken>() {}.getType() ); 269 | i += 1; 270 | for(basicJSON bJSON : dataArray) { 271 | 272 | toNodeData = getNodeId(nodes, bJSON.to); 273 | nodes = (ArrayList)toNodeData.get(1); 274 | fromNodeData = getNodeId(nodes, bJSON.from); 275 | nodes = (ArrayList)fromNodeData.get(1); 276 | line = String.format("{\"from\":%d, \"to\":%d}", fromNodeData.get(0), toNodeData.get(0)); 277 | if(edges.contains(line) == false) { 278 | edges.add(line); 279 | } 280 | } 281 | } 282 | 283 | String out = "\n\n" + 285 | "\n" + 286 | "\n" + 287 | "\n\n" + 288 | "Software_Forensic_Kit CallGraph: %s\n" + 289 | "\n" + 290 | "\n" + 291 | "\n"+ 292 | "\n" + 293 | "\n" + 294 | "
\n" + 295 | "
\n" + 296 | "\n" + 297 | "
\n" + 298 | "\n" + 336 | "\n" + 337 | ""; 338 | String path; 339 | try { 340 | path = LocalCalls.getJarPath(); 341 | String sep = System.getProperty("file.separator"); 342 | funcName= funcName.replace("(", "_"); 343 | funcName = funcName.replace(")", "_"); 344 | funcName = funcName.replace(":", "_"); 345 | System.out.println(String.format("OUTFILE:[%s]", path + sep + "files" + sep + "output" + sep + funcName + ".html")); 346 | BufferedWriter writer = new BufferedWriter(new FileWriter(path + sep + "files" + sep + "output" + sep + funcName + ".html")); 347 | String outStr = String.format(out, funcName, Arrays.toString(nodes.toArray()), Arrays.toString(edges.toArray())); 348 | writer.write(outStr); 349 | writer.close(); 350 | 351 | } catch (IOException e) { 352 | // TODO Auto-generated catch block 353 | e.printStackTrace(); 354 | } 355 | 356 | } 357 | class basicJSON{ 358 | public String to; 359 | public String from; 360 | public basicJSON(String to, String from) { 361 | this.to = to; 362 | this.from = from; 363 | } 364 | public String toString() { 365 | 366 | return String.format("{\"from\":\"%s\", \"to\":\"%s\"", from, to); 367 | 368 | } 369 | } 370 | 371 | } -------------------------------------------------------------------------------- /src/main/java/com/omnissa/software_forensic_kit/Java_Gadget_Kit.java: -------------------------------------------------------------------------------- 1 | /*************************************************** 2 | * Copyright 2019 Omnissa, LLC. 3 | * SPDX-License-Identifier: BSD-2-Clause 4 | ***************************************************/ 5 | package com.omnissa.software_forensic_kit; 6 | 7 | import java.io.BufferedWriter; 8 | import java.io.Console; 9 | import java.io.File; 10 | import java.io.FileWriter; 11 | import java.io.IOException; 12 | import java.net.URISyntaxException; 13 | import java.nio.file.Path; 14 | import java.nio.file.Paths; 15 | import java.util.ArrayList; 16 | import java.util.Arrays; 17 | import java.util.Calendar; 18 | import java.util.HashMap; 19 | 20 | import org.apache.commons.cli.CommandLine; 21 | import org.apache.commons.cli.CommandLineParser; 22 | import org.apache.commons.cli.DefaultParser; 23 | import org.apache.commons.cli.HelpFormatter; 24 | import org.apache.commons.cli.Option; 25 | import org.apache.commons.cli.Options; 26 | import org.apache.commons.cli.ParseException; 27 | import org.apache.commons.io.FileUtils; 28 | 29 | import com.omnissa.software_forensic_kit.java_gadget.Java_Gadget; 30 | import com.omnissa.software_forensic_kit.java_gadget.Java_Gadget_Dynamic; 31 | import com.omnissa.software_forensic_kit.java_gadget.Java_Gadget_Injector; 32 | import com.omnissa.software_forensic_kit.java_gadget.local.LocalCalls; 33 | import com.omnissa.software_forensic_kit.java_gadget.remote.RemoteCalls; 34 | 35 | public class Java_Gadget_Kit { 36 | public static boolean exitprocess = false; 37 | public static void printUsage(HelpFormatter formatter, Options options) { 38 | String footer = "Example Usage:\r\n" + 39 | " >java -jar software_forensic_kit.jar -u root -d 10.160.157.187 -p test123 -s \"ExampleClass:functionA\" -f /var/www -hm -rp my.class.path.\r\n" + 40 | " Or using wildcards * for filter\r\n" + 41 | " >java -jar software_forensic_kit.jar -u root -d 10.160.157.187 -p test123 -s \"ExampleClass*functionA\" -f /var/www -pp -rp my.class.path. -o C:\\test\\output\r\n\n" + 42 | " If you want to run software forensic kit locally simply exclude (user, domain and password variables)\r\n\n" + 43 | " >java -jar software_forensic_kit.jar -s \"ExampleClass:functionA\" -f C:\\test -pp \r\n\n" + 44 | "For Interactive Mode:\r\n" + 45 | " >java -jar software_forensic_kit.jar -I"; 46 | formatter.setOptionComparator(null); 47 | formatter.printHelp("Software_Forensic_Kit", "", options, footer, true); 48 | 49 | System.exit(1); 50 | } 51 | 52 | public static void main(String[] args) { 53 | 54 | CommandLine cmd = generateCMD(args); 55 | 56 | 57 | if(cmd.hasOption("I")) { 58 | interactiveMode(cmd); 59 | } 60 | 61 | String user = cmd.getOptionValue("username"); 62 | String pass = cmd.getOptionValue("password"); 63 | String ip = cmd.getOptionValue("domain"); 64 | 65 | File jar = new java.io.File(Java_Gadget_Kit.class.getProtectionDomain() 66 | .getCodeSource() 67 | .getLocation() 68 | .getPath()); 69 | //.getName(); 70 | String jarName = jar.getName(); 71 | 72 | System.out.println("Jar Location: " + jar.toString()); 73 | if(user != null && pass != null && ip != null) { 74 | //remote call here 75 | String outFolder = cmd.getOptionValue("output"); 76 | RemoteCalls rc = RemoteCalls.getInstance(); 77 | rc.connect(user, ip, pass); 78 | rc.sendCommand("rm -rf /var/kit/files/output"); 79 | rc.sendCommand("mkdir -p /var/kit/files/output"); 80 | String jarpath = "unknown.jar"; 81 | try { 82 | jarpath = new File(Java_Gadget_Kit.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getPath(); 83 | } catch (URISyntaxException e1) { 84 | 85 | // TODO Auto-generated catch block 86 | e1.printStackTrace(); 87 | System.exit(3); 88 | } 89 | rc.sendFiles(jarpath, "/var/kit"); 90 | 91 | 92 | Option[] opts = cmd.getOptions(); 93 | String newLocalCmd = ""; 94 | for (Option opt : opts) { 95 | String optStr = opt.getOpt(); 96 | if(optStr != "u" && optStr != "p" && optStr != "d") { 97 | if(opt.getValue() != null && opt.getValues().length > 0 && opt.hasArg()) 98 | newLocalCmd += "-" + optStr + " " + String.join(" ", opt.getValues()) + " "; 99 | else 100 | newLocalCmd += "-" + optStr + " "; 101 | } 102 | } 103 | newLocalCmd = String.format("java -jar /var/kit/%s %s", jarName, newLocalCmd); 104 | System.out.println(newLocalCmd); 105 | String output = rc.sendCommand(newLocalCmd); 106 | boolean getRemoteOutput = false; 107 | if(cmd.hasOption("-gm") || cmd.hasOption("-pp") || cmd.hasOption("-g")) { 108 | getRemoteOutput = true; 109 | } 110 | 111 | String sep = System.getProperty("file.separator"); 112 | 113 | String outDir = "output_%d_%d_%d"; 114 | Calendar c = Calendar.getInstance(); 115 | int hour = c.get(Calendar.HOUR_OF_DAY); 116 | int minute = c.get(Calendar.MINUTE); 117 | int second = c.get(Calendar.SECOND); 118 | outDir = LocalCalls.getJarPath() + sep + String.format(outDir, hour, minute, second); 119 | 120 | 121 | if(cmd.hasOption("output")) { 122 | outDir = cmd.getOptionValue("output"); 123 | } 124 | rc.getFiles("/var/kit/files/output", outDir); 125 | 126 | if(getRemoteOutput == true) { 127 | try { 128 | outDir = outDir + sep + String.format("output_%d_%d_%d", hour, minute, second) + ".txt"; 129 | BufferedWriter outFile = new BufferedWriter(new FileWriter(outDir)); 130 | outFile.write(output); 131 | outFile.close(); 132 | 133 | } catch (IOException e) { 134 | // TODO Auto-generated catch block 135 | e.printStackTrace(); 136 | System.out.println(output); 137 | 138 | } 139 | 140 | } 141 | System.out.println(String.format("Files copied to: %s", outDir)); 142 | 143 | } 144 | else { 145 | //Local call 146 | /* 147 | * Takes too long 148 | * WildcardFileFilter fileFilter = new WildcardFileFilter("*.jar"); 149 | WildcardFileFilter dirFilter = new WildcardFileFilter("*"); 150 | //local execute find all jars 151 | File dir = new File("/"); 152 | 153 | List files = (List) FileUtils.listFiles(dir, fileFilter, dirFilter); 154 | for (File file : files) { 155 | System.out.println("file: " + file.getAbsolutePath()); 156 | }*/ 157 | mainLocalCall(Java_Gadget.createOptions(), cmd); 158 | 159 | 160 | } 161 | 162 | System.out.println("Finished!"); 163 | System.exit(0); 164 | 165 | 166 | } 167 | public static void mainLocalCall(Options javaGadgetOptions, CommandLine cmd) { 168 | 169 | String filter = cmd.getOptionValue("filter"); 170 | String sep = System.getProperty("file.separator"); 171 | String localOutput = "files" + sep + "output"; 172 | String localCG = "files" + sep + "callgraph"; 173 | 174 | 175 | ArrayList jarList = LocalCalls._findJars(filter, cmd.hasOption("verbose")); 176 | LocalCalls.makeDirsNearJar(localOutput); 177 | LocalCalls.makeDirsNearJar(localCG); 178 | 179 | ArrayList callGraphList = LocalCalls.createCallGraphs(jarList, localCG); 180 | 181 | HashMap map = new HashMap(); 182 | for (Option opt : cmd.getOptions()) { 183 | if(javaGadgetOptions.hasOption(opt.getOpt())) 184 | map.put(opt.getLongOpt(), cmd.getOptionValue(opt.getOpt())); 185 | } 186 | boolean verbose = cmd.hasOption("verbose"); 187 | callGraphList.forEach(x -> {if(verbose)System.out.println(String.format("Callgraph_Created:[%s] ", x));}); 188 | System.out.println("Callgraph_Out:{"); 189 | callGraphList.forEach(x -> {Java_Gadget.executeRequestUsingCGFile(map, x);}); 190 | System.out.println("}:Callgraph_Out_End"); 191 | 192 | if(cmd.hasOption("output")) { 193 | String destDir = cmd.getOptionValue("output"); 194 | String outDir = LocalCalls.getJarPath() + sep + "files" + sep + "output"; 195 | 196 | Path srcPath = Paths.get(outDir); 197 | Path destPath = Paths.get(destDir); 198 | System.out.println("MOVE " + outDir + " to " + destDir); 199 | try { 200 | FileUtils.moveDirectory(new File(outDir), new File(destDir)); 201 | } catch (IOException e) { 202 | // TODO Auto-generated catch block 203 | e.printStackTrace(); 204 | } 205 | } 206 | } 207 | public static CommandLine generateCMD(String[] args) { 208 | Options options = new Options(); 209 | options.addOption(Option.builder("h").longOpt("help") 210 | .build()); 211 | options.addOption(Option.builder("u").longOpt("username") 212 | .desc("Remote server username") 213 | .hasArg() 214 | .argName("USERNAME") 215 | .build()); 216 | options.addOption(Option.builder("p").longOpt("password") 217 | .desc("Remote server password") 218 | .hasArg() 219 | .argName("PASSWORD") 220 | .build()); 221 | options.addOption(Option.builder("d").longOpt("domain") 222 | .desc("Remote server ip") 223 | .hasArg() 224 | .argName("DOMAIN") 225 | .build()); 226 | options.addOption(Option.builder("f").longOpt("filter") 227 | .desc("Jar path must contain this term to be included") 228 | .hasArg() 229 | .argName("FILTER") 230 | .build()); 231 | Options javaGadgetOptions = Java_Gadget.createOptions(); 232 | for(Option opt : javaGadgetOptions.getOptions()) { 233 | options.addOption(opt); 234 | } 235 | options.addOption(Option.builder("o").longOpt("output") 236 | .desc("Output to folder") 237 | .hasArg() 238 | .argName("FOLDER") 239 | .build()); 240 | 241 | CommandLineParser parser = new DefaultParser(); 242 | HelpFormatter formatter = new HelpFormatter(); 243 | CommandLine cmd = null; 244 | 245 | try { 246 | cmd = parser.parse(options, args); 247 | } catch (ParseException e) { 248 | System.out.println(e.getMessage()); 249 | printUsage(formatter, options); 250 | } 251 | if(cmd.getOptions().length < 1) 252 | printUsage(formatter, options); 253 | 254 | return cmd; 255 | 256 | } 257 | public static void interactiveMode(CommandLine cmd) { 258 | Console console = System.console(); 259 | String scanType; 260 | String functionName; 261 | String folderPath; 262 | String action; 263 | String output; 264 | String outputFolder; 265 | String newLocalCmd = null; 266 | String removePrefix; 267 | String localOrRemote; 268 | while((localOrRemote = console.readLine("[L]ocal or [R]emote: (L)")).matches("(?i)l|r||") == false); 269 | 270 | File jar = new java.io.File(Java_Gadget_Kit.class.getProtectionDomain() 271 | .getCodeSource() 272 | .getLocation() 273 | .getPath()); 274 | String jarName = jar.getName(); 275 | final RemoteCalls rc = RemoteCalls.getInstance(); 276 | 277 | if(localOrRemote.matches("(?i)r")) { 278 | String user; 279 | String password; 280 | String ip; 281 | while(rc.isConnected == false) { 282 | //clear screen 283 | user = console.readLine("User: "); 284 | password = new String(console.readPassword("Password: ")); 285 | ip = console.readLine("Server (ex:10.0.0.35): "); 286 | rc.connect(user, ip, password); 287 | if(rc.isConnected == false) { 288 | System.out.println("Connection Failed"); 289 | } 290 | } 291 | 292 | //System.out.println("Jar Location: " + jar.toString()); 293 | 294 | //remote call here 295 | String outFolder = cmd.getOptionValue("output"); 296 | 297 | rc.sendCommand("rm -rf /var/kit/files/output"); 298 | rc.sendCommand("mkdir -p /var/kit/files/output"); 299 | rc.sendCommand("mkdir -p /var/kit/files/callgraph"); 300 | String jarpath = "unknown.jar"; 301 | try { 302 | jarpath = new File(Java_Gadget_Kit.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getPath(); 303 | } catch (URISyntaxException e1) { 304 | 305 | // TODO Auto-generated catch block 306 | e1.printStackTrace(); 307 | System.exit(3); 308 | } 309 | rc.sendFiles(jarpath, "/var/kit"); 310 | System.out.println("Files uploaded complete!"); 311 | } 312 | 313 | ArrayList argsArray = new ArrayList(); 314 | 315 | while(true) { 316 | 317 | while((scanType = console.readLine("[S]tatic or [D]ynamic Scan or [I]nject into PID or [E]xit: (S)")).matches("(?i)s|i|d|e") == false); 318 | 319 | if(scanType.matches("(?i)e")){ 320 | System.out.println("Finished!"); 321 | System.exit(0); 322 | } 323 | else if(scanType.matches("(?i)s")) { 324 | System.out.println("Static\n"); 325 | functionName = console.readLine("Target Function (ex: AClass:functionA): "); 326 | folderPath = console.readLine("Directory To Search (ex: /var C:\\test): "); 327 | 328 | 329 | while((action = console.readLine("Action: [S]earch or output as [H]tml or [G]raphViz or [P]rettyprint: (S)")).matches("(?i)s|h|g|p||") == false); 330 | 331 | if(action.matches("(?i)s||")){ 332 | System.out.println("Searching..."); 333 | argsArray = new ArrayList(Arrays.asList("-jp", "-v", "-s", functionName, "-f", folderPath)); 334 | newLocalCmd = String.join(" ", argsArray); 335 | 336 | 337 | } 338 | else { 339 | //TODO VALIDATE FOLDER EXISTS ON SYSTEM 340 | outputFolder = console.readLine("Output Folder (ex: C:\test or /var/test): "); 341 | removePrefix = console.readLine("Remote Prefix: "); 342 | argsArray = new ArrayList(Arrays.asList("-rp", removePrefix, "-rd", "-o", outputFolder, "-s", functionName, "-f", folderPath)); 343 | 344 | System.out.println("Searching And Generating Output..."); 345 | if(action.matches("(?i)h")){ 346 | argsArray.add(0, "-i"); 347 | } 348 | else if(action.matches("(?i)g||")){ 349 | argsArray.add(0, "-gm"); 350 | } 351 | else if(action.matches("(?i)p||")){ 352 | argsArray.add(0, "-pp"); 353 | } 354 | newLocalCmd = String.join(" ", argsArray); 355 | } 356 | 357 | if (localOrRemote.matches("(?i)r")){ 358 | newLocalCmd = String.format("java -jar /var/kit/%s %s", jarName, newLocalCmd); 359 | System.out.println(newLocalCmd); 360 | output = rc._sendCommand(newLocalCmd, true); 361 | } 362 | else { 363 | 364 | Options javaGadgetOptions = Java_Gadget.createOptions(); 365 | String[] args = argsArray.toArray(new String[argsArray.size()]); 366 | cmd = generateCMD(args); 367 | mainLocalCall(javaGadgetOptions, cmd); 368 | } 369 | } 370 | 371 | else if(scanType.matches("(?i)d")) { 372 | System.out.println("Dynamic\n"); 373 | if (localOrRemote.matches("(?i)r")){ 374 | functionName = console.readLine("Target Function (ex: AClass:functionA): "); 375 | newLocalCmd = String.format("java -cp /var/kit/%s com.omnissa.software_forensic_kit.java_gadget.Java_Gadget_Dynamic -s \"%s\"", jarName, functionName); 376 | 377 | System.out.println(rc.sendCommand(newLocalCmd)); 378 | 379 | } 380 | else { 381 | functionName = console.readLine("Target Function (ex: AClass:functionA): "); 382 | HashMap options = new HashMap(); 383 | options.put("searchFunction", functionName); 384 | 385 | Java_Gadget_Dynamic.executeRequest(options); 386 | } 387 | 388 | } 389 | else { 390 | if (localOrRemote.matches("(?i)r")){ 391 | String listPIDCommand = String.format("java -cp /var/kit/%s com.omnissa.software_forensic_kit.java_gadget.Java_Gadget_Injector -lpid", jarName); 392 | System.out.println(rc._sendCommand(listPIDCommand, true)); 393 | } 394 | else { 395 | Java_Gadget_Injector.getJavaPIDList(); 396 | 397 | } 398 | 399 | String pids = console.readLine("Enter PID (ex 123, 123;124;125): "); 400 | functionName = console.readLine("Target Function (ex: com.test.App;printText~com.test.App;main): "); 401 | ArrayList pidList = new ArrayList(); 402 | 403 | if(pids.matches("^(0|[1-9][0-9]*)$")) { 404 | //numeric pid 405 | pidList.add(pids); 406 | } 407 | else if(pids.contains(";")) { 408 | //multiple pids 409 | pidList.addAll(Arrays.asList(pids.split(";"))); 410 | } 411 | else { 412 | System.out.println("Can't handle entry for pid."); 413 | } 414 | if(pidList.size() > 0) { 415 | if (localOrRemote.matches("(?i)r")){ 416 | //get user of process 417 | //ps -o user= -p 418 | //su - newuser -c 'process' 419 | //echo "2439;2433" | tr ";" "\n" | xargs -I '{}' ps -o user= -o pid= -p '{}' 420 | //test 1233 421 | //test2 223 422 | 423 | //if user nologin 424 | //chsh -s /bin/bash 425 | 426 | //tdnf -y install openjdk8 427 | //sudo yum install java-1.8.0-openjdk-devel 428 | 429 | String getUserNames = "echo '"+pids+"' | tr \";\" \"\\n\" | xargs -I '{}' ps -o user:25= -o pid= -p '{}'"; 430 | //System.out.println(getUserNames); 431 | getUserNames = rc.sendCommand(getUserNames); 432 | String[] items; 433 | ArrayList newPids; 434 | HashMap> pidUserMap = new HashMap>(); 435 | 436 | int len = getUserNames.split("\n").length; 437 | System.out.println(len); 438 | for (String pidStr : getUserNames.split("\n")) { 439 | items = pidStr.split("\\s+"); 440 | 441 | //if user in list 442 | if(pidUserMap.containsKey(items[0])) { 443 | pidUserMap.get(items[0]).add(items[1]); 444 | } 445 | else { 446 | newPids = new ArrayList(); 447 | newPids.add(items[1]); 448 | pidUserMap.put(items[0], newPids); 449 | } 450 | } 451 | 452 | //su - newuser -c 'process' 453 | String fullCommand = ""; 454 | for (String user: pidUserMap.keySet()) { 455 | newLocalCmd = String.format("java -cp /var/kit/%s com.omnissa.software_forensic_kit.java_gadget.Java_Gadget_Injector -s \"%s\" -pid \"%s\"", jarName, functionName, String.join(";", pidUserMap.get(user))); 456 | fullCommand = String.format("su - %s -c '%s'", user, newLocalCmd); 457 | System.out.println(fullCommand); 458 | exitprocess = false; 459 | Runnable listenForInput = () -> { 460 | while(exitprocess == false) { 461 | String out = console.readLine("Listening For Client Input ([E]xit): "); 462 | exitprocess = out.matches("(?i)e"); 463 | } 464 | System.out.println("Exited"); 465 | rc.sendCommand("pkill -9 -f \"software_forensic_kit\""); 466 | }; 467 | Thread listenForInputThread = new Thread(listenForInput); 468 | listenForInputThread.start(); 469 | System.out.println(rc._sendCommand(fullCommand, true)); 470 | 471 | //pkill -9 -f "software_forensic_kit" 472 | } 473 | } 474 | else { 475 | Java_Gadget_Injector.inject(pidList, functionName); 476 | } 477 | } 478 | } 479 | 480 | } 481 | } 482 | } --------------------------------------------------------------------------------