Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for configuration file #41

Merged
merged 7 commits into from
Apr 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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