mandiant/Ghidrathon

doc: no support for subclassing Java classes using Python classes

c3rb3ru5d3d53c opened this issue · 5 comments

Example 1:

from ghidra.app.tablechooser import StringColumnDisplay

class Example(StringColumnDisplay):
    def __init__(self, title, index):
        self.title = title
        self.index = index

    def getColumnName(self):
        return self.title
# Error: TypeError: cannot create 'jep.PyJClass' instances

Example 2:

from ghidra.app.tablechooser import StringColumnDisplay

class Example(StringColumnDisplay.__pytype__):
    def __init__(self, title, index):
        self.title = title
        self.index = index

    def getColumnName(self):
        return self.title
# Error: TypeError: cannot create 'Example' instances

I'm not certain what is happening here, I'm thinking it's an inheritance limitation of JEP.

However, the old python interface using Python 2 this is useful for creating GUI interface elements, which to my knowledge works just fine.

https://github.com/pombredanne/findcrypt-ghidra-python/blob/ccf00d3acbe3c31446da8cf92302cf363e96b915/findcrypt.py#L2134-L2142

If you extend the class as is, you get the error TypeError: cannot create 'jep.PyJClass' instances and when you extend it using the exposed __pytype__ you can create the class but you cannot create an instance of it.

Any thoughts on this? is there a workaround perhaps?

Thank you for reporting @c3rb3ru5d3d53c. We will need to investigate further to identify if this is a Jep limitation and, if true, float it upstream to see what the Jep developers say.

Hi! I also ran into this problem today. You can try using the jep jproxy function like so:

If you have a Java interface:

package my_ghidra;
public interface MyInterface {
	public String echoBack(String thing);
}
import jep
# The import works, but you can't implement it :(
from my_ghidra import MyInterface, MyPluginsService

# class MyImplementation(MyInterface): # This does not work
class MyImplemention:
    def echoBack(thing: str) -> str:
        return thing

# If we implement all the methods like we were implementing the interface
# we can create a proxy object to register with Ghidra.
# We tell jep the interfaces we're implementing and create the proxy like so:
proxy = jep.jproxy(MyImplementation(), ["my_ghidra.MyInterface"])

# Now we can call the Ghidra API and pass it our python object
# Maybe we have our plugin exposes a register method that takes
# something that implements MyInterface. We look up the plugin that
# implements MyPluginService and call register with our proxy object
getTool().getService(MyPluginsService).register(proxy)

I found this test which implements this from the Python side:
https://github.com/ninia/jep/blob/master/src/test/python/test_jproxy.py#L34


In your example the following might work (I have only tried with interface implementation, not subclassing)

from ghidra.app.tablechooser import StringColumnDisplay
class Example:
    def __init__(self, title, index):
        self.title = title
        self.index = index

    def getColumnName(self):
        return self.title

import jep
proxy = jep.jproxy(Example(...), ["ghidra.app.tablechooser. StringColumnDisplay"])

thanks for the update @cyberkaida ! jep.jproxy does not appear to work for Java class implementations 😞 . I bumped this upstream to see what the Jep developers have to say about it, see ninia/jep#486.

@c3rb3ru5d3d53c the Jep developers described in ninia/jep#486 that this is not possible and may never be something that Jep implements. This will remain a limitation in Ghidrathon unless the Jep developers decide to implement it because Ghidrathon relies on Jep to bridge the Java <-> Python gap.

On my radar is extending Ghidrathon to expose a Java-based API that enables Ghidra users to write Java code for Ghidra that can quickly spin up a Python interpreter, run some Python code, grab the results, and shut down the interpreter. I picture this being used to develop Ghidra plugins where the GUI is written in Java and the backend data processing is written in Python.

I have written some terrible code to do this in my reverse-engineering-assistant repo. I am about to refactor the Ghidra extension to move to a run once model like you are describing as I ran into threading issues calling between Java and Python.

I found the best way to do things was to implement an interface for the python to call into that would expose various UI components (in my example there I expose an interpreter to python so it can read and print text). This way the threading is easier as python is calling to java, not java to python.

Spinning up an interpreter was a little bit of work, I was not able to directly import and create a Ghidrathon interpreter from my Java side (not sure how to depend on the extension properly). But I was able to make use of Ghidrathon’s -1 priority level on python code to execute my python side.

I think if you implement a service in Ghidra that another plugin can get a reference to, this would be best as you won’t need to depend on the entire extension. Maybe similar to the console service.

Thank you for your continued work on Ghidrathon, I look forward to building more on top of it in the future!