Shofel2_T124_python/venv/lib/python3.10/site-packages/pyhidra/script.py

263 lines
7.8 KiB
Python

import importlib
import importlib.util
import inspect
import logging
import sys
import traceback
from collections.abc import ItemsView, KeysView
from importlib.machinery import ModuleSpec, SourceFileLoader
from pathlib import Path
from jpype import JClass, JImplementationFor
from typing import List
_NO_ATTRIBUTE = object()
_headless_interpreter = None
class _StaticMap(dict):
__slots__ = ('script',)
def __init__(self, script: "PyGhidraScript"):
super().__init__()
self.script = script
def __getitem__(self, key):
res = self.get(key, _NO_ATTRIBUTE)
if res is not _NO_ATTRIBUTE:
return res
raise KeyError(key)
def get(self, key, default=None):
res = self.script.get_static(key)
return res if res is not _NO_ATTRIBUTE else default
def __iter__(self):
yield from self.script
def keys(self):
return KeysView(self)
def items(self):
return ItemsView(self)
class _JavaProperty(property):
def __init__(self, field):
super().__init__()
self._field = field
def __get__(self, obj, cls):
return self._field.fget(obj)
def __set__(self, obj, value):
self._field.fset(obj, value)
#pylint: disable=too-few-public-methods
@JImplementationFor("dc3.pyhidra.plugin.PythonFieldExposer")
class _PythonFieldExposer:
#pylint: disable=no-member
def __jclass_init__(self):
exposer = JClass("dc3.pyhidra.plugin.PythonFieldExposer")
if self.class_ == exposer:
return
try:
for k, v in exposer.getProperties(self.class_).items():
self._customize(k, _JavaProperty(v))
# allowing any exception to escape here may cause the jvm to terminate
# pylint: disable=bare-except
except:
logger = logging.getLogger(__name__)
logger.error("Failed to add property customizations for %s", self, exc_info=1)
class _GhidraScriptModule:
def __init__(self, spec: ModuleSpec):
super().__setattr__("__dict__", spec.loader_state["script"])
def __setattr__(self, attr, value):
if hasattr(self, attr):
raise AttributeError(f"readonly attribute {attr}")
super().__setattr__(attr, value)
class _GhidraScriptLoader(SourceFileLoader):
def __init__(self, script: "PyGhidraScript", spec: ModuleSpec):
super().__init__(spec.name, spec.origin)
spec.loader_state = {"script": script}
def create_module(self, spec: ModuleSpec):
return _GhidraScriptModule(spec)
# pylint: disable=missing-function-docstring
class PyGhidraScript(dict):
"""
Python GhidraScript Wrapper
"""
def __init__(self, jobj=None):
super().__init__()
if jobj is None:
jobj = JClass("dc3.pyhidra.plugin.PyScriptProvider").PyhidraHeadlessScript()
self._script = jobj
global _headless_interpreter
from ghidra.util import SystemUtilities
if SystemUtilities.isInHeadlessMode() and _headless_interpreter is None:
_headless_interpreter = jobj
# ensure the builtin set takes precedence over GhidraScript.set
super().__setitem__("set", set)
super().__setitem__("__this__", self._script)
# this is injected since Ghidra commit e66e72577ded1aeae53bcc3f361dfce1ecf6e24a
super().__setitem__("this", self._script)
def __missing__(self, k):
attr = getattr(self._script, k, _NO_ATTRIBUTE)
if attr is not _NO_ATTRIBUTE:
return attr
raise KeyError(k)
def __getattr__(self, item):
return getattr(self._script, item)
def __setitem__(self, k, v):
attr = inspect.getattr_static(self._script, k, _NO_ATTRIBUTE)
if attr is not _NO_ATTRIBUTE and isinstance(attr, property):
setattr(self._script, k, v)
else:
super().__setitem__(k, v)
def __iter__(self):
yield from super().__iter__()
yield from dir(self._script)
def get_static(self, key):
res = self.get(key, _NO_ATTRIBUTE)
if res is not _NO_ATTRIBUTE:
return res
return inspect.getattr_static(self._script, key, _NO_ATTRIBUTE)
def get_static_view(self):
return _StaticMap(self)
def set(self, state, monitor, writer):
"""
see GhidraScript.set
"""
self._script.set(state, monitor, writer)
def run(self, script_path: str = None, script_args: List[str] = None):
"""
Run this GhidraScript
:param script_path: The path of the python script
:param script_args: The arguments for the python script
"""
sf = self._script.getSourceFile()
if sf is None and script_path is None:
return
if script_path is None:
script_path = sf.getAbsolutePath()
script_args = self._script.getScriptArgs()
if script_args is None:
script_args = []
else:
self._script.setScriptArgs(script_args)
orig_argv = sys.argv
script_root = str(Path(script_path).parent)
# honor the python safe_path flag introduced in 3.11
safe_path = bool(getattr(sys.flags, "safe_path", 0))
try:
# Temporarily set command line arguments.
sys.argv = [script_path] + list(script_args)
if not safe_path:
# add the directory containing the script to the start of the path
# this provides the same import behavior as if the script was run normally
sys.path.insert(0, script_root)
spec = importlib.util.spec_from_file_location('__main__', script_path)
spec.loader = _GhidraScriptLoader(self, spec)
m = importlib.util.module_from_spec(spec)
try:
spec.loader.exec_module(m)
# pylint: disable=bare-except
except:
# filter the traceback so that it stops at the script
exc_type, exc_value, exc_tb = sys.exc_info()
i = 0
tb = traceback.extract_tb(exc_tb)
for fs in tb:
if fs.filename == script_path:
break
i += 1
ss = traceback.StackSummary.from_list(tb[i:])
e = traceback.TracebackException(exc_type, exc_value, exc_tb)
e.stack = ss
self._script.printerr(''.join(e.format()))
finally:
sys.argv = orig_argv
if not safe_path:
sys.path.remove(script_root)
def get_current_interpreter():
"""
Gets the underlying GhidraScript for the focused Pyhidra InteractiveConsole.
This will always return None unless it is being access from a function
called from within the interactive console.
:return: The GhidraScript for the active interactive console.
"""
try:
from ghidra.util import SystemUtilities
from ghidra.framework.main import AppInfo
global _headless_interpreter
if SystemUtilities.isInHeadlessMode():
if _headless_interpreter is None:
# one hasn't been created yet so make one now
PyScriptProvider = JClass("dc3.pyhidra.plugin.PyScriptProvider")
_headless_interpreter = PyScriptProvider.PyhidraHeadlessScript()
return _headless_interpreter
project = AppInfo.getActiveProject()
if project is None:
return None
ts = project.getToolServices()
tool = None
for t in ts.getRunningTools():
if t.getActiveWindow().isFocused():
tool = t
break
if tool is None:
return None
for plugin in tool.getManagedPlugins():
if plugin.name == 'PyhidraPlugin':
return plugin.script
except ImportError:
return None