176 lines
7.8 KiB
Python
176 lines
7.8 KiB
Python
"""
|
|
Ties the Ghidra documentation into the builtin Python help.
|
|
"""
|
|
|
|
import __builtin__
|
|
import java
|
|
import re
|
|
import json
|
|
import os
|
|
import zipfile
|
|
|
|
from ghidra.framework import Application
|
|
from ghidra.util import SystemUtilities
|
|
|
|
class _Helper:
|
|
def __init__(self):
|
|
self.orig_help = __builtin__.help
|
|
if SystemUtilities.isInHeadlessMode():
|
|
# ./pythonRun scenario
|
|
self.msg = "\nExample workflow:\n"
|
|
self.msg += " # Import headless analyzer\n"
|
|
self.msg += " from ghidra.app.util.headless import HeadlessAnalyzer\n\n"
|
|
self.msg += " # View HeadlessAnalyzer API\n"
|
|
self.msg += " help(HeadlessAnalyzer)\n\n"
|
|
self.msg += " # Get a HeadlessAnalyzer instance\n"
|
|
self.msg += " headless = HeadlessAnalyzer.getInstance()\n\n"
|
|
self.msg += " # Get headless options\n"
|
|
self.msg += " options = headless.getOptions()\n\n"
|
|
self.msg += " # View HeadlessOptions API and set options accordingly\n"
|
|
self.msg += " help(options)\n\n"
|
|
self.msg += " # View processLocal method API\n"
|
|
self.msg += " help(headless.processLocal)\n\n"
|
|
self.msg += " # Perform headless processing\n"
|
|
self.msg += " headless.processLocal(...)\n\n"
|
|
else:
|
|
# PythonPlugin scenario
|
|
self.msg = "Press 'F1' for usage instructions"
|
|
|
|
def __call__(self, param=None):
|
|
|
|
def get_class_and_method(param):
|
|
if param is None and not SystemUtilities.isInHeadlessMode():
|
|
# Enable help() in PythonPlugin scenario to show help for GhidraScript
|
|
return "ghidra.app.script.GhidraScript", None
|
|
class_name = None
|
|
method_name = None
|
|
if type(param) in [type(1), type(1j), type(1L), type(1.0), type(None), type(True), type([]), type({}), type(()), type({1})]:
|
|
# These are instances of builtin types, so skip
|
|
pass
|
|
elif type(param) == type(str):
|
|
# These are builtin Python types, so skip
|
|
pass
|
|
elif type(param) == type(str.split):
|
|
# These are python functions, so skip
|
|
pass
|
|
elif type(param) == type(java):
|
|
# These are java packages, which we don't don't document, so skip
|
|
pass
|
|
elif type(param) == type(java.lang.Object):
|
|
# This is a java class, so extract its class name
|
|
match = re.search("'(.*)'", str(param))
|
|
if match is not None:
|
|
class_name = match.group(1)
|
|
elif type(param) == type(java.lang.Object().toString):
|
|
# This is a java method, so extract its class name and method name
|
|
tokens = str(param).split(" ")[2].split(".")
|
|
class_name = ".".join(tokens[:-1])
|
|
method_name = tokens[-1]
|
|
else:
|
|
# Assuming this is a java object, so extract its class name
|
|
match = re.search("'(.*)'", str(type(param)))
|
|
if match is not None:
|
|
class_name = match.group(1)
|
|
return class_name, method_name
|
|
|
|
def get_jsondoc(class_name):
|
|
jsondoc = None
|
|
try:
|
|
root = Application.getApplicationRootDirectory().getFile(False).getParentFile().getAbsolutePath()
|
|
javadoc_zip_name = "GhidraAPI_javadoc.zip"
|
|
if SystemUtilities.isInDevelopmentMode():
|
|
javadoc_zip = root + "/build/tmp/" + javadoc_zip_name
|
|
else:
|
|
javadoc_zip = root + "/docs/" + javadoc_zip_name
|
|
if os.path.exists(javadoc_zip):
|
|
json_path = "api/" + class_name.replace('.', '/') + '.json'
|
|
with zipfile.ZipFile(javadoc_zip, "r").open(json_path) as f:
|
|
jsondoc = json.load(f)
|
|
except (IOError, KeyError) as e:
|
|
pass
|
|
return jsondoc
|
|
|
|
def format_class(cls):
|
|
sig = "class " + cls["name"] + "\n"
|
|
if "extends" in cls:
|
|
sig += " extends " + cls["extends"] + "\n"
|
|
implements = ""
|
|
for interface in cls["implements"]:
|
|
if len(implements) > 0:
|
|
implements += ", "
|
|
implements += interface
|
|
if len(implements) > 0:
|
|
sig += " implements " + implements + " \n"
|
|
sig += "\n" + cls["comment"]
|
|
return sig
|
|
|
|
def format_field(field):
|
|
sig = "%s %s" % (field["type_long"], field["name"])
|
|
if field["static"]:
|
|
sig = "static " + sig
|
|
if field["constant_value"]:
|
|
sig += " = " + field["constant_value"]
|
|
sig += "\n"
|
|
desc = " %s\n" % (field["comment"]) if len(field["comment"]) > 0 else ""
|
|
return sig + desc
|
|
|
|
def format_method(method):
|
|
paramsig = ""
|
|
args = ""
|
|
for param in method["params"]:
|
|
if len(paramsig) > 0:
|
|
paramsig += ", "
|
|
paramsig += "%s %s" % (param["type_short"], param["name"])
|
|
args += " @param %s (%s): %s\n" % (param["name"], param["type_long"], param["comment"])
|
|
throws = ""
|
|
for exception in method["throws"]:
|
|
throws += " @throws %s: %s\n" % (exception["type_short"], exception["comment"])
|
|
sig = "%s %s(%s)\n" % (method["return"]["type_short"], method["name"], paramsig)
|
|
if method["static"]:
|
|
sig = "static " + sig
|
|
desc = " %s\n\n" % (method["comment"]) if len(method["comment"]) > 0 else ""
|
|
ret = ""
|
|
if method["return"]["type_short"] != "void":
|
|
ret = " @return %s: %s\n" % (method["return"]["type_long"], method["return"]["comment"])
|
|
return sig + desc + args + ret + throws
|
|
|
|
class_name, method_name = get_class_and_method(param)
|
|
if class_name is None:
|
|
self.orig_help(param)
|
|
else:
|
|
try_again = True
|
|
while try_again:
|
|
try_again = False
|
|
print "Searching API for " + class_name + ("" if method_name is None else "." + method_name + "()") + "..."
|
|
jsondoc = get_jsondoc(class_name)
|
|
if jsondoc is None:
|
|
print "No API found for " + class_name
|
|
elif method_name is None:
|
|
print "#####################################################"
|
|
print format_class(jsondoc)
|
|
print "#####################################################\n"
|
|
for field in jsondoc["fields"]:
|
|
print format_field(field)
|
|
print "-----------------------------------------------------"
|
|
for method in jsondoc["methods"]:
|
|
print format_method(method)
|
|
print "-----------------------------------------------------"
|
|
else:
|
|
found_method = False
|
|
for method in jsondoc["methods"]:
|
|
if method["name"] == method_name:
|
|
print "-----------------------------------------------------"
|
|
print format_method(method)
|
|
print "-----------------------------------------------------"
|
|
found_method = True
|
|
if not found_method:
|
|
# The method may be inherited, so check for a super class and try again
|
|
if "extends" in jsondoc:
|
|
class_name = jsondoc["extends"]
|
|
try_again = True
|
|
|
|
def __repr__(self):
|
|
return self.msg
|
|
|
|
__builtin__.help = _Helper()
|