mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-28 17:11:11 -09:00
Merge branch
'GP-7301_ryanmkurtz_PR-9642_Bortlesboat_fix_pyghidra-write-only-globals' (Closes #8978, Closes #9642)
This commit is contained in:
@@ -567,6 +567,9 @@ import pdb # imports Python's pdb
|
||||
import pdb_ # imports Ghidra's pdb
|
||||
```
|
||||
## Change History
|
||||
__3.3.0__
|
||||
* Eliminated `KeyError` exceptions that were generated while stepping with a debugger.
|
||||
|
||||
__3.2.0__
|
||||
* PyGhidra's `help()` override can now show `public/private/protected` on Ghidra Java fields and
|
||||
methods (available in Ghidra 12.2 and later).
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
__version__ = "3.2.0"
|
||||
__version__ = "3.3.0"
|
||||
|
||||
# stub for documentation and typing
|
||||
# this is mostly to hide the function parameter
|
||||
|
||||
@@ -51,7 +51,8 @@ class _StaticMap(dict):
|
||||
# this is necessary for completions on currentAddress, currentProgram, etc.
|
||||
try:
|
||||
return getattr(self.script, key)
|
||||
except AttributeError:
|
||||
except Exception:
|
||||
# getters like getFirstFunction() throw when there is no program
|
||||
return res
|
||||
return res
|
||||
raise KeyError(key)
|
||||
@@ -61,7 +62,9 @@ class _StaticMap(dict):
|
||||
return res if res is not _NO_ATTRIBUTE else default
|
||||
|
||||
def __iter__(self):
|
||||
yield from self.script
|
||||
# Completions include the bean properties that regular iteration leaves out.
|
||||
yield from dict.__iter__(self.script)
|
||||
yield from dir(self.script._script)
|
||||
|
||||
def keys(self):
|
||||
return KeysView(self)
|
||||
@@ -194,7 +197,15 @@ class PyGhidraScript(dict):
|
||||
|
||||
def __iter__(self):
|
||||
yield from super().__iter__()
|
||||
yield from dir(self._script)
|
||||
for name in dir(self._script):
|
||||
attr = inspect.getattr_static(self._script, name, _NO_ATTRIBUTE)
|
||||
# JPype bean properties may have no getter, or a getter that throws when
|
||||
# there is no program (firstFunction, memoryBlocks, ...). Keep them out of
|
||||
# iteration so debuggers inspecting globals don't evaluate them; they can
|
||||
# still be read by name. Exposed fields use _JavaProperty and are kept.
|
||||
if type(attr) is property:
|
||||
continue
|
||||
yield name
|
||||
|
||||
def get_static(self, key):
|
||||
res = self.get(key, _NO_ATTRIBUTE)
|
||||
|
||||
@@ -171,6 +171,71 @@ def test_import_script(capsys, shared_datadir: Path):
|
||||
assert captured.out.rstrip() == "imported successfully"
|
||||
|
||||
|
||||
def test_script_globals_iteration():
|
||||
pyghidra.start()
|
||||
from pyghidra.core import _setup_script
|
||||
|
||||
# no program is open, which is when getters like getFirstFunction() throw
|
||||
script = _setup_script(None, None)
|
||||
script["user_variable"] = 42
|
||||
names = list(script)
|
||||
|
||||
# a debugger reads every name it iterates
|
||||
for name in names:
|
||||
script[name]
|
||||
|
||||
for name in ("potentialPropertiesFileLocations", "propertiesFile", "referencePrimary"):
|
||||
assert name not in names
|
||||
with pytest.raises(KeyError):
|
||||
script[name]
|
||||
|
||||
for name in ("firstFunction", "memoryBlocks", "scriptName"):
|
||||
assert name not in names
|
||||
assert script["scriptName"] is not None
|
||||
|
||||
for name in ("user_variable", "currentProgram", "currentAddress", "monitor", "getCurrentProgram"):
|
||||
assert name in names
|
||||
assert script["user_variable"] == 42
|
||||
assert script["currentProgram"] is None
|
||||
assert script["currentAddress"] is None
|
||||
assert script["monitor"] is not None
|
||||
assert callable(script["getCurrentProgram"])
|
||||
|
||||
|
||||
def test_script_static_view_includes_bean_properties():
|
||||
pyghidra.start()
|
||||
from pyghidra.script import PyGhidraScript
|
||||
|
||||
view = PyGhidraScript().get_static_view()
|
||||
names = list(view)
|
||||
for name in ("potentialPropertiesFileLocations", "propertiesFile", "referencePrimary"):
|
||||
assert name in names
|
||||
assert isinstance(view[name], property)
|
||||
for name in ("firstFunction", "scriptName"):
|
||||
assert name in names
|
||||
|
||||
|
||||
def test_script_completion_without_program():
|
||||
pyghidra.start()
|
||||
from rlcompleter import Completer
|
||||
from pyghidra.core import _setup_script
|
||||
|
||||
# rlcompleter reads every value in the namespace, including firstFunction
|
||||
view = _setup_script(None, None).get_static_view()
|
||||
assert Completer(view).complete("currentPr", 0) == "currentProgram"
|
||||
|
||||
|
||||
def test_script_write_only_property_assignment():
|
||||
pyghidra.start()
|
||||
from java.util import ArrayList
|
||||
from pyghidra.script import PyGhidraScript
|
||||
|
||||
script = PyGhidraScript()
|
||||
locations = ArrayList()
|
||||
script["potentialPropertiesFileLocations"] = locations
|
||||
assert script["potentialPropertiesFileLocs"] == locations
|
||||
|
||||
|
||||
def test_import_ghidra_base_java_packages():
|
||||
|
||||
def get_runtime_top_level_java_packages(launcher) -> set:
|
||||
|
||||
Reference in New Issue
Block a user