Skip to content

Commit

Permalink
add support for configuration file (#41)
Browse files Browse the repository at this point in the history
* init commit Ghidrathon configuration

* adding default configuration file

* adding comments and unit tests
  • Loading branch information
mike-hunhoff authored Apr 25, 2023
1 parent d1182c0 commit 939952c
Show file tree
Hide file tree
Showing 9 changed files with 400 additions and 62 deletions.
12 changes: 12 additions & 0 deletions data/GhidrathonConfig.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<GHIDRATHON_CONFIG>
<ARRAY NAME="JAVA_EXCLUDE_LIBS" TYPE="string">
<A VALUE="pdb" />
</ARRAY>
<ARRAY NAME="PYTHON_SHARED_MODULES" TYPE="string">
<A VALUE="numpy" />
</ARRAY>
<ARRAY NAME="PYTHON_INCLUDE_PATHS" TYPE="string">
</ARRAY>
</GHIDRATHON_CONFIG>

26 changes: 26 additions & 0 deletions data/python/tests/test_cpython.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright (C) 2022 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt
# Unless required by applicable law or agreed to in writing, software distributed under the License
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and limitations under the License.

"""Unit tests to verify CPython modules
Note: you must run these tests from the Ghidra script manager or headless mode
"""

import unittest
import warnings


class TestCPython(unittest.TestCase):
def test_numpy(self):
try:
import numpy

a = numpy.array(["cat", "dog"])
except ImportError:
warnings.warn("numpy module is not installed - ignoring test")
pass
11 changes: 11 additions & 0 deletions data/python/tests/test_jepbridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ def assertIsJavaObject(self, o):
if not (o is None or isinstance(o, Object)):
raise AssertionError("Object %s is not valid" % str(o))

def assertIsNotJavaObject(self, o):
from java.lang import Object

if isinstance(o, Object):
raise AssertionError("Object %s is not valid" % str(o))

def test_type_instance(self):
# see Jep: https://github.com/ninia/jep/blob/15e36a7ba54eb7d8f7ffd85f16675fa4fd54eb1d/src/test/python/test_import.py#L54-L65
from java.lang import Object
Expand All @@ -46,3 +52,8 @@ def test_ghidra_script_variables(self):

def test_ghidra_script_methods(self):
self.assertIsInstance(getGhidraVersion(), str)

def test_java_excluded_packages(self):
import pdb

self.assertIsNotJavaObject(pdb)
50 changes: 50 additions & 0 deletions src/main/java/ghidrathon/GhidrathonClassEnquirer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (C) 2022 Mandiant, Inc. All Rights Reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at: [package root]/LICENSE.txt
// Unless required by applicable law or agreed to in writing, software distributed under the License
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and limitations under the License.

package ghidrathon;

import java.util.List;
import java.util.ArrayList;

import jep.ClassList;
import jep.ClassEnquirer;

/**
* Implements Jep ClassEnquirer used to handle Java imports from Python - specifically we
* use this class to handle naming conflicts, e.g. pdb
*/
public class GhidrathonClassEnquirer implements ClassEnquirer {

private final List<String> javaExcludeLibs = new ArrayList<String>();
private final ClassEnquirer classList = ClassList.getInstance();

public void addJavaExcludeLib(String name) {
javaExcludeLibs.add(name);
}

public void addJavaExcludeLibs(List<String> names) {
javaExcludeLibs.addAll(names);
}

public boolean isJavaPackage(String name) {
if (javaExcludeLibs.contains(name)) {
return false;
}

return classList.isJavaPackage(name);
}

public String[] getClassNames(String name) {
return classList.getClassNames(name);
}

public String[] getSubPackages(String name) {
return classList.getSubPackages(name);
}

}
85 changes: 85 additions & 0 deletions src/main/java/ghidrathon/GhidrathonConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright (C) 2022 Mandiant, Inc. All Rights Reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at: [package root]/LICENSE.txt
// Unless required by applicable law or agreed to in writing, software distributed under the License
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and limitations under the License.

package ghidrathon;

import java.util.List;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;

/**
* Ghidrathon's configuration class
*
* Stores
* - stdout and stderr
* - Python modules to handle as shared modules - relevant to CPython modules
* - Java package names to exclude from Python imports
* - Python include paths to add to Python interpreter environment
*/
public class GhidrathonConfig {

private final List<String> javaExcludeLibs = new ArrayList<String>();
private final List<String> pyIncludePaths = new ArrayList<String>();
private final List<String> pySharedModules = new ArrayList<String>();

private PrintWriter out = null;
private PrintWriter err = null;

public void addStdOut(PrintWriter out) {
this.out = out;
}

public void addStdErr(PrintWriter err) {
this.err = err;
}

public PrintWriter getStdOut() {
return out;
}

public PrintWriter getStdErr() {
return err;
}

public void addPythonSharedModule(String name) {
pySharedModules.add(name);
}

public void addPythonSharedModules(List<String> names) {
pySharedModules.addAll(names);
}

public Iterable<String> getPythonSharedModules() {
return Collections.unmodifiableList(pySharedModules);
}

public void addJavaExcludeLib(String name) {
javaExcludeLibs.add(name);
}

public void addJavaExcludeLibs(List<String> names) {
javaExcludeLibs.addAll(names);
}

public Iterable<String> getJavaExcludeLibs() {
return Collections.unmodifiableList(javaExcludeLibs);
}

public void addPythonIncludePath(String path) {
pyIncludePaths.add(path);
}

public void addPythonIncludePaths(List<String> paths) {
pyIncludePaths.addAll(paths);
}

public Iterable<String> getPythonIncludePaths() {
return Collections.unmodifiableList(pyIncludePaths);
}
}
30 changes: 18 additions & 12 deletions src/main/java/ghidrathon/GhidrathonConsoleInputThread.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,44 @@

package ghidrathon;

import java.io.BufferedReader;
import java.io.File;
import java.io.PrintWriter;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicBoolean;

import generic.jar.ResourceFile;
import ghidra.app.plugin.core.interpreter.InterpreterConsole;
import ghidra.app.script.GhidraState;

import ghidra.util.Msg;
import ghidra.app.script.GhidraState;
import ghidra.app.plugin.core.interpreter.InterpreterConsole;

import ghidrathon.GhidrathonUtils;
import ghidrathon.GhidrathonConfig;
import ghidrathon.interpreter.GhidrathonInterpreter;

public class GhidrathonConsoleInputThread extends Thread {

private static int generationCount = 0;

private GhidrathonPlugin plugin = null;
private InterpreterConsole console = null;
private AtomicBoolean shouldContinue = new AtomicBoolean(true);
private GhidrathonInterpreter python = null;
private PrintWriter err = null;
private PrintWriter out = null;

private AtomicBoolean shouldContinue = new AtomicBoolean(true);
private GhidrathonConfig config = GhidrathonUtils.getDefaultGhidrathonConfig();

GhidrathonConsoleInputThread(GhidrathonPlugin plugin) {

super("Ghidrathon console input thread (generation " + ++generationCount + ")");

this.plugin = plugin;
this.console = plugin.getConsole();
this.err = console.getErrWriter();
this.out = console.getOutWriter();

// init Ghidrathon configuration
config.addStdErr(console.getErrWriter());
config.addStdOut(console.getOutWriter());

}

Expand All @@ -56,7 +63,7 @@ public void run() {

try {

python = GhidrathonInterpreter.get(out, err);
python = GhidrathonInterpreter.get(config);

python.printWelcome();

Expand All @@ -66,7 +73,7 @@ public void run() {
python.close();
}

e.printStackTrace(err);
e.printStackTrace(config.getStdErr());
return;

}
Expand Down Expand Up @@ -95,7 +102,6 @@ public void run() {
continue;
}


boolean moreInputWanted = evalPython(line);

this.plugin.flushConsole();
Expand Down
29 changes: 17 additions & 12 deletions src/main/java/ghidrathon/GhidrathonScript.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import ghidra.framework.plugintool.PluginTool;
import ghidra.app.script.GhidraScriptProvider;

import ghidrathon.GhidrathonUtils;
import ghidrathon.GhidrathonConfig;
import ghidrathon.interpreter.GhidrathonInterpreter;

public class GhidrathonScript extends GhidraScript {
Expand All @@ -28,24 +30,26 @@ public class GhidrathonScript extends GhidraScript {
protected void run() {

GhidrathonInterpreter python = null;

final PrintWriter out = getStdOut();
final PrintWriter err = getStdErr();
GhidrathonConfig config = GhidrathonUtils.getDefaultGhidrathonConfig();

// init Ghidrathon configuration
config.addStdOut(getStdOut());
config.addStdErr(getStdErr());

try {

python = GhidrathonInterpreter.get(out, err);
python = GhidrathonInterpreter.get(config);

// run Python script from Python interpreter
python.runScript(getSourceFile(), this);

// flush stdout and stderr to ensure all is printed to console window
err.flush();
out.flush();
config.getStdErr().flush();
config.getStdOut().flush();

} catch (RuntimeException e) {

e.printStackTrace(err);
e.printStackTrace(config.getStdErr());

} finally {

Expand All @@ -68,13 +72,14 @@ protected void run() {
public void runScript(String name, GhidraState scriptState) {

GhidrathonInterpreter python = null;

final PrintWriter out = getStdOut();
final PrintWriter err = getStdErr();
GhidrathonConfig config = GhidrathonUtils.getDefaultGhidrathonConfig();

config.addStdOut(getStdOut());
config.addStdErr(getStdErr());

try {

python = GhidrathonInterpreter.get(out, err);
python = GhidrathonInterpreter.get(config);

ResourceFile source = GhidraScriptUtil.findScriptByName(name);
if (source == null) {
Expand Down Expand Up @@ -109,7 +114,7 @@ public void runScript(String name, GhidraState scriptState) {

} catch (Exception e) {

e.printStackTrace(err);
e.printStackTrace(config.getStdErr());

} finally {

Expand Down
Loading

0 comments on commit 939952c

Please sign in to comment.