Ghidra: How do you access the flat API in ghidra_scripts?

Created on 1 Jun 2020  路  9Comments  路  Source: NationalSecurityAgency/ghidra

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?

Most helpful comment

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.

All 9 comments

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.

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

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?)

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

Thanks, I wrote those and totally forgot they existed.

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. :joy:

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

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

huettenhain picture huettenhain  路  3Comments

0x6d696368 picture 0x6d696368  路  3Comments

pd0wm picture pd0wm  路  3Comments

loudinthecloud picture loudinthecloud  路  3Comments

tambry picture tambry  路  3Comments