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

doc: no support for subclassing Java classes using Python classes #55

Open
c3rb3ru5d3d53c opened this issue Jun 24, 2023 · 5 comments
Open
Labels
documentation Improvements or additions to documentation jep Issue related to jep wontfix This will not be worked on

Comments

@c3rb3ru5d3d53c
Copy link

c3rb3ru5d3d53c commented Jun 24, 2023

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?

@c3rb3ru5d3d53c c3rb3ru5d3d53c changed the title Odd Behavior Extending Classes Odd Behavior with Class inheritance or Extending Classes Jun 24, 2023
@c3rb3ru5d3d53c c3rb3ru5d3d53c changed the title Odd Behavior with Class inheritance or Extending Classes Odd Behavior with Class Inheritance or Extending Classes Jun 24, 2023
@mike-hunhoff
Copy link
Collaborator

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.

@mike-hunhoff mike-hunhoff added the jep Issue related to jep label Jul 10, 2023
@cyberkaida
Copy link

cyberkaida commented Aug 3, 2023

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"])

@mike-hunhoff
Copy link
Collaborator

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.

@mike-hunhoff
Copy link
Collaborator

@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.

@mike-hunhoff mike-hunhoff changed the title Odd Behavior with Class Inheritance or Extending Classes doc: no support for subclassing Java classes using Python classes Aug 18, 2023
@mike-hunhoff mike-hunhoff added documentation Improvements or additions to documentation wontfix This will not be worked on labels Aug 18, 2023
@cyberkaida
Copy link

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation jep Issue related to jep wontfix This will not be worked on
Projects
None yet
Development

No branches or pull requests

3 participants