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

How do you access the flat API in ghidra_scripts? #1919

Open
ExpHP opened this issue Jun 1, 2020 · 9 comments
Open

How do you access the flat API in ghidra_scripts? #1919

ExpHP opened this issue Jun 1, 2020 · 9 comments
Labels

Comments

@ExpHP
Copy link

ExpHP commented Jun 1, 2020

I tried to move a helper function into a script in ghidra_scripts, and am being defeated by what would seem to be the simplest possible thing. Following examples I see elsewhere online, I tried just writing the same code I would write in ghidra's python interpreter:

ghidra_scripts/better.py

from ghidra.program.model.symbol import SourceType

def nameFunction(addr, name):
    """ Set the name of a function regardless of whether one currently exists at that address. """
    addr = toAddr(addr)
    if not createFunction(addr, name):
        getFunctionAt(addr).setName(name, SourceType.USER_DEFINED)

ghidra's interpreter

>>> import better as b
>>> b.nameFunction(0x444140, 'testing123testing')
Traceback (most recent call last):
  File "python", line 1, in <module>
  File "C:\Users\diago\ghidra_scripts\better.py", line 10, in nameFunction
    addr = toAddr(addr)
NameError: global name 'toAddr' is not defined

Okay, so the API funcs are not available to me for whatever reason. Maybe the examples I found were old? I locate the functions on FlatProgramAPI, however, they are instance methods, so if I want to use them in my module I need to create an instance first.

ghidra's interpreter

>>> ghidra.program.flatapi.FlatProgramAPI()
Traceback (most recent call last):
  File "python", line 1, in <module>
TypeError: ghidra.program.flatapi.FlatProgramAPI(): expected 1-2 args; got 0

>>> ghidra.program.flatapi.FlatProgramAPI(currentProgram, monitor)
ghidra.program.flatapi.FlatProgramAPI@28c313e

Okay, so in order for my python module to create a FlatProgramAPI, it will need access to currentProgram and monitor, which are... properties of FlatProgramAPI, making this a total bird-or-the-egg situation! What am I missing here?

@ryanmkurtz
Copy link
Collaborator

ryanmkurtz commented Jun 1, 2020

When you start the Ghidra python interpreter or run a Ghidra python script, all of the methods and instance variables from GhidraScript and FlatProgram get injected into the environment. If you called toAddr right from the interpreter, it would work. The problem seems to be that when you import an outside module, that module doesn't know about the injected methods and variables. This surprises me, but it's possible that it's a limitation of Jython.

@ryanmkurtz
Copy link
Collaborator

I just remembered how to do this. Add the following import to better.py:

from __main__ import *

This will bring in everything the interpreter knows about.

@astrelsky
Copy link
Contributor

I just remembered how to do this. Add the following import to better.py:

from __main__ import *

This will bring in everything the interpreter knows about.

I wonder if a custom ClasspathPyImporter could be used to make this occur automagically.

@ExpHP
Copy link
Author

ExpHP commented Jun 1, 2020

My 2c as a frequent Python user:

The problem seems to be that when you import an outside module, that module doesn't know about the injected methods and variables. This surprises me, but it's possible that it's a limitation of Jython.

To be honest, this doesn't surprise me too much, as it is characteristic of Python that even global variables are still always scoped to a single module (in this case, __main__). In fact, when I first saw example scripts online that have no import statements, I was wondering what sort of wild hacks were making that possible!

(I see now that those scripts have special annotations like #@toolbar and are presumably not intended to be imported, but rather called directly by ghidra where it can inject those variables)

To me, the __main__ workaround sounds fairly reasonable. (the main problem being that it's just not very discoverable at the moment)

I wonder if a custom ClasspathPyImporter could be used to make this occur automagically.

My main concerns would be:

  • Whether it can be done without potentially adversely affecting imports of standard library modules like json.
  • Whether it should apply when a module in ghidra_scripts is imported by another module in ghidra_scripts. (my guess is, probably yes?)

@ryanmkurtz
Copy link
Collaborator

ryanmkurtz commented Jun 1, 2020

(I see now that those scripts have special annotations like #@ToolBar and are presumably not intended to be imported, but rather called directly by ghidra where it can inject those variables)

These comments are used to provide the Ghidra GUI with some metadata about the script. They don't affect the script's functionality at all.

@mumbel
Copy link
Contributor

mumbel commented Jun 1, 2020

Not sure if its documented elsewhere, but at least this is shown in example scripts: https://github.com/NationalSecurityAgency/ghidra/blob/master/Ghidra/Features/Python/ghidra_scripts/external_module_callee.py and https://github.com/NationalSecurityAgency/ghidra/blob/master/Ghidra/Features/Python/ghidra_scripts/external_module_caller.py

@ryanmkurtz
Copy link
Collaborator

Thanks, I wrote those and totally forgot they existed.

@astrelsky
Copy link
Contributor

astrelsky commented Jun 1, 2020

I wonder if a custom ClasspathPyImporter could be used to make this occur automagically.

My main concerns would be:

  • Whether it can be done without potentially adversely affecting imports of standard library modules like json.
  • Whether it should apply when a module in ghidra_scripts is imported by another module in ghidra_scripts. (my guess is, probably yes?)

While explaining my initial idea I realized the method of detecting ghidra_scripts wouldn't work as GhidraScript isn't explicitly extended. 😂

Basically it didn't work the way I thought it did.

@ExpHP
Copy link
Author

ExpHP commented Jun 1, 2020

To me, the __main__ workaround sounds fairly reasonable. (the main problem being that it's just not very discoverable at the moment)

Actually, I want to backtrack on this slightly:

The __main__ workaround has a problem that it not only imports the names injected by ghidra, but it also imports everything else defined in the caller. This is quite a bit more than bargained for, and can easily lead to user mistakes where a module works when imported by one script but not when imported by another.

It would be nice if there were a module that existed exclusively for the purpose of holding all of ghidra's injected names, similar to how all of Python's builtin names are available from the builtins module. Then we could do e.g.

from __ghidra__ import *

and keep things a bit more portable.

@ryanmkurtz ryanmkurtz added the Type: Enhancement New feature or request label Mar 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants