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

Question: how does the RPC bridge handle class callbacks? #72

Closed
mahaloz opened this issue Jun 18, 2023 · 3 comments
Closed

Question: how does the RPC bridge handle class callbacks? #72

mahaloz opened this issue Jun 18, 2023 · 3 comments

Comments

@mahaloz
Copy link

mahaloz commented Jun 18, 2023

Thank you for running such an awesome project! I've recently been looking back into using Python3 in Ghidra in a background process type of way and it seems you've got it handled pretty well with the background processing.

However, I was confused about how you handle something like class callbacks that Ghidra does. For instance, in one of my Java projects I am thinking of porting to Python3, I implement the DomainObjectListener class:
https://github.com/mahaloz/decomp2dbg/blob/69ad8ede239cdb5eab8a776f33426b980277cd2c/decompilers/d2d_ghidra/src/main/java/decomp2dbg/D2DPlugin.java#L49

I do this because it's a special class that will get callbacks from Ghidra whenever a DomainObject changes (like a function name).
It's clear to me that I can do this:

class MyListener(DomainObjectListener):
    pass

But I have no idea how I register this class with Ghidra... i.e., I don't know how to inject this into the Python 2 globals area on Ghidra.

I was curious if you had any advice or tried to do something similar with this project.

Thanks!

@mahaloz
Copy link
Author

mahaloz commented Jun 18, 2023

Ok, I've answered my own question. THIS IS AMAZING! Truly @justfoxing, amazing project. This works:

[ins] In [1]: import ghidra_bridge
         ...: b = ghidra_bridge.GhidraBridge(namespace=globals()) # creates the bridge and loads the fl
         ...: at API into the global namespace
         ...: print(getState().getCurrentAddress().getOffset())
4196125

[ins] In [2]: module = b.remote_import("ghidra.framework.model")

[ins] In [6]: class MyListener(module.DomainObjectListener):
         ...:     def domainObjectChanged(self, ev):
         ...:         print(ev)
         ...: 

[ins] In [7]: l = MyListener()

[ins] In [8]: l
Out[8]: <_bridged___main__.MyListener('org.python.proxies.__main__$MyListener$7@4082bddb', type=__main__.MyListener, handle=93b68807-cf8e-4a3e-9452-76a43a2198a7)>

[ins] In [9]: currentProgram.addListener(l)

ghidra.framework.model.DomainObjectChangedEvent[source=fauxware - .ProgramDB]

That last line you are seeing is a callback being handled after I changed a name in Ghidra. Wow, I'm honestly amazing.

I did however get one crash on the Ghidra side:

AttributeError: 'ghidra.program.database.ProgramDB' object has no attribute '__module__'
Traceback (most recent call last):
  File "/home/mahaloz/ghidra_scripts/jfx_bridge/bridge.py", line 1003, in local_get
    result = getattr(target, name)
AttributeError: 'ghidra.program.database.ProgramDB' object has no attribute '__module__'
Traceback (most recent call last):
  File "/home/mahaloz/ghidra_scripts/jfx_bridge/bridge.py", line 1003, in local_get
    result = getattr(target, name)
AttributeError: 'instancemethod' object has no attribute '_partialmethod'
Traceback (most recent call last):
  File "/home/mahaloz/ghidra_scripts/jfx_bridge/bridge.py", line 1003, in local_get
    result = getattr(target, name)
AttributeError: 'instancemethod' object has no attribute '__code__'
Traceback (most recent call last):
  File "/home/mahaloz/ghidra_scripts/jfx_bridge/bridge.py", line 1003, in local_get
    result = getattr(target, name)
AttributeError: 'instancemethod' object has no attribute '__defaults__'
Traceback (most recent call last):
  File "/home/mahaloz/ghidra_scripts/jfx_bridge/bridge.py", line 1003, in local_get
    result = getattr(target, name)
AttributeError: 'instancemethod' object has no attribute '__kwdefaults__'
Traceback (most recent call last):
  File "/home/mahaloz/ghidra_scripts/jfx_bridge/bridge.py", line 1003, in local_get
    result = getattr(target, name)
AttributeError: 'instancemethod' object has no attribute '__text_signature__'

@justfoxing
Copy link
Owner

Yup, you can subclass Java classes remotely, this is super cursed :P Glad you've got your code sorted. Be aware that you may end up with issues if you close your ghidra bridge client while you have a callback like this registered - Ghidra will keep trying to send events to the callback, and the ghidra bridge server will throw exceptions when it finds the connection from the client closed, which can make things very sad if it's a callback that blocks the UI thread. If you do start having problems with that, check out https://github.com/justfoxing/jfx_bridge/#remoteify-and-remote-exec for an example of pushing a custom callback class onto the Ghidra-side to allow you to handle client disconnects safely.

As for the crash - hard to say without seeing the code you're using. Is it actually crashing your code, or is it just logging a bunch of AttributeErrors? IPython in particular queries for a bunch of attributes that may or may not exist as part of its amazing featureset, so a bunch of AttributeErrors are expected there.

@mahaloz
Copy link
Author

mahaloz commented Jun 19, 2023

Yeah @justfoxing, the code I used to trigger this is exactly the code you see above in an IPython instance.
As for the disconnection causing crashes after you disconnect, you are totally right; It does, lol. Thanks for the advice, I'll close the issue since those crashes aren't causing anything bad :).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants