ghidra_bridge icon indicating copy to clipboard operation
ghidra_bridge copied to clipboard

Get isinstance to return reasonable results for BridgedObjects for type, callable, etc

Open fmagin opened this issue 6 years ago • 2 comments

I am currently working on this but it is in no shape for a PR yet.

The idea is that BridgedCallables should behave as close as possible to actual local Callables to allow the inspect features and IPython features building on top of that to work as good as possible.

The concrete goal is the following: Assume a function like:

def add(x: int, y:int ) -> int:
    return x + y

IPython help returns the following.

In [34]: add?
Signature: add(x: int, y: int) -> int
Docstring: <no docstring>
File:      ~/Projects/ghidra_bridge/dev.py
Type:      function

This behavior should be replicated with BrigdedCallables. What is needed for this is that is after several layers of IPython code the inspect module can generate a valid signature for it by using Signature.from_callable. This currently fails with the following:

from inspect import Signature
f = currentProgram.functionManager.getFunctionAt
Signature.from_callable(f)   
ValueError                                Traceback (most recent call last)
<ipython-input-36-6e450cf1e523> in <module>
----> 1 Signature.from_callable(f)

/usr/lib64/python3.7/inspect.py in from_callable(cls, obj, follow_wrapped)
   2831         """Constructs Signature for the given callable object."""
   2832         return _signature_from_callable(obj, sigcls=cls,
-> 2833                                         follow_wrapper_chains=follow_wrapped)
   2834 
   2835     @property

/usr/lib64/python3.7/inspect.py in _signature_from_callable(obj, follow_wrapper_chains, skip_bound_arg, sigcls)
   2286     if _signature_is_builtin(obj):
   2287         return _signature_from_builtin(sigcls, obj,
-> 2288                                        skip_bound_arg=skip_bound_arg)
   2289 
   2290     if isinstance(obj, functools.partial):

/usr/lib64/python3.7/inspect.py in _signature_from_builtin(cls, func, skip_bound_arg)
   2110     s = getattr(func, "__text_signature__", None)
   2111     if not s:
-> 2112         raise ValueError("no signature found for builtin {!r}".format(func))
   2113 
   2114     return _signature_fromstr(cls, func, s, skip_bound_arg)

ValueError: no signature found for builtin <BridgedCallable('<bound method ghidra.program.database.function.FunctionManagerDB.getFunctionAt of ghidra.program.database.function.FunctionManagerDB@5f0ef8c4>', type=instancemethod, handle=99f4707d-9f4a-4205-b820-7dac1b5a811b)>

The first hint is that no signature found for builtin is weird because a BridgedCallable is in no way a builtin so something is going quite wrong.

The first divergence from the Jython shell is that in the Jython shell isinstance(f, types.MethodType) is True while in the bridge client it is false. I am unsure how to fix this exactly as isinstance is a builtin that might be hard to trick.

An alternative to bypass this all is to fake __signature__ directly and just build one ourselves. Slightly annoying and ignores the potential actual problem.

It gets recognized as a builtin because ismethoddescriptor returns True, which in happens because ismethod returns False for currentProgram.functionManager.getFunctionAt

One core issue is: Is there some way to make isinstance(obj, type) go over to the bridge and is that even the correct way to do it? I will look into how other environments like rpyc and Jython handle this and might have more concrete ideas then.

fmagin avatar Nov 09 '19 12:11 fmagin

Turns out building our own Signature is fairly easy, so I don't quite care about isinstance working for all those types. I'll leave this issue open in case someone else runs into this, but won't work on it.

fmagin avatar Nov 09 '19 14:11 fmagin

More or less accidentally gathered results: If hacking e.g. inspect.isclass to recognize BridgedObjects of Java Classes things break in IPython anyway.

One possible solution to fix isinstance in general though might be overriding the builtin (which I think works and should get picked up by the inspect module). Currently the builtin is used in https://github.com/justfoxing/ghidra_bridge/blob/master/ghidra_bridge/bridge.py#L985-L997 so that code would need to be changed to save the original version once and then change it.

fmagin avatar Nov 22 '19 09:11 fmagin