mirror of
https://github.com/JHUAPL/CodeCut.git
synced 2026-01-09 13:28:06 -05:00
Initial commit of codecut-gui and deepcut from JHU/APL & DARPA open source review
This commit is contained in:
0
codecut-gui/codecut-gui/Module.manifest
Normal file
0
codecut-gui/codecut-gui/Module.manifest
Normal file
50
codecut-gui/codecut-gui/README.md
Normal file
50
codecut-gui/codecut-gui/README.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# CodeCut / DeepCut GUI Plugin
|
||||
|
||||
Ghidra Plugin for DeepCut / CodeCut GUI
|
||||
|
||||
## Building and Installation
|
||||
|
||||
JDK 11 (or newer) and Ghidra 9.1.0 (or newer) are required.
|
||||
|
||||
Ghidra's standard Gradle build system is used. Set the `GHIDRA_INSTALL_DIR` environment variable before building, or set it as a Gradle property (useful for building in an IDE).
|
||||
|
||||
### Environment Variable
|
||||
|
||||
```
|
||||
export GHIDRA_INSTALL_DIR="/path/to/ghidra"
|
||||
gradle
|
||||
```
|
||||
|
||||
### Gradle property
|
||||
|
||||
```
|
||||
echo GHIDRA_INSTALL_DIR="/path/to/ghidra" > gradle.properties
|
||||
```
|
||||
|
||||
### Python 3
|
||||
|
||||
The CodeCut GUI's Module Name Guessing tool requires Python 3. Before running the tool, you may need to modify the Python Path of the tool. To do this, **Edit -> Tool Options -> Python Executable**. Insert path to your Python 3 executable. Click apply and ok.
|
||||
|
||||
### Install nltk
|
||||
This module depends on the Python 3 dependency `nltk`. Run the following command to install:
|
||||
```
|
||||
pip install nltk
|
||||
```
|
||||
|
||||
### Build Output
|
||||
|
||||
The module ZIP will be output to `dist/`. Use **File > Install Extensions** and select the green plus to browse to the extension. Restart Ghidra when prompted.
|
||||
|
||||
For proper functionality, the plugin should be built with the same JRE used by your Ghidra installation. if you have multiple Java runtime environments installed, select the correct JRe by setting the `JAVA_HOME` environment variable before building.
|
||||
|
||||
### Running the Plugin
|
||||
|
||||
After using **File > Install Extensions** to install the plugin and restarting Ghidra, you may be prompted the next time Ghidra opens to enable the newly-detected plugin. In this case, simply check the box next to **CodeCutGUIPlugin**.
|
||||
|
||||
To enable the plugin normally, you can use **File > Configure...**. Click **Experimental** and check the box next to **CodeCutGUIPlugin**.
|
||||
|
||||
Once the plugin is enabled, use **Window > CodeCut Table** to open it. Note that it is intendend to be used after running the **CodeCut** or **DeepCut** analyzers.
|
||||
|
||||
Open the CodeCutGUI Window by clicking **Windows->CodeCut Table**. Alternatively use the hot key Ctrl-m.
|
||||
|
||||
The Module Name Guessing tool can be started by clicking **Analysis -> Guess Module Names**.
|
||||
33
codecut-gui/codecut-gui/build.gradle
Normal file
33
codecut-gui/codecut-gui/build.gradle
Normal file
@@ -0,0 +1,33 @@
|
||||
// Builds a Ghidra Extension for a given Ghidra installation.
|
||||
//
|
||||
// An absolute path to the Ghidra installation directory must be supplied either by setting the
|
||||
// GHIDRA_INSTALL_DIR environment variable or Gradle project property:
|
||||
//
|
||||
// > export GHIDRA_INSTALL_DIR=<Absolute path to Ghidra>
|
||||
// > gradle
|
||||
//
|
||||
// or
|
||||
//
|
||||
// > gradle -PGHIDRA_INSTALL_DIR=<Absolute path to Ghidra>
|
||||
//
|
||||
// Gradle should be invoked from the directory of the project to build. Please see the
|
||||
// application.gradle.version property in <GHIDRA_INSTALL_DIR>/Ghidra/application.properties
|
||||
// for the correction version of Gradle to use for the Ghidra installation you specify.
|
||||
|
||||
//----------------------START "DO NOT MODIFY" SECTION------------------------------
|
||||
def ghidraInstallDir
|
||||
|
||||
if (System.env.GHIDRA_INSTALL_DIR) {
|
||||
ghidraInstallDir = System.env.GHIDRA_INSTALL_DIR
|
||||
}
|
||||
else if (project.hasProperty("GHIDRA_INSTALL_DIR")) {
|
||||
ghidraInstallDir = project.getProperty("GHIDRA_INSTALL_DIR")
|
||||
}
|
||||
|
||||
if (ghidraInstallDir) {
|
||||
apply from: new File(ghidraInstallDir).getCanonicalPath() + "/support/buildExtension.gradle"
|
||||
}
|
||||
else {
|
||||
throw new GradleException("GHIDRA_INSTALL_DIR is not defined!")
|
||||
}
|
||||
//----------------------END "DO NOT MODIFY" SECTION-------------------------------
|
||||
15
codecut-gui/codecut-gui/data/README.txt
Normal file
15
codecut-gui/codecut-gui/data/README.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
The "data" directory is intended to hold data files that will be used by this module and will
|
||||
not end up in the .jar file, but will be present in the zip or tar file. Typically, data
|
||||
files are placed here rather than in the resources directory if the user may need to edit them.
|
||||
|
||||
An optional data/languages directory can exist for the purpose of containing various Sleigh language
|
||||
specification files and importer opinion files.
|
||||
|
||||
The data/buildLanguage.xml is used for building the contents of the data/languages directory.
|
||||
|
||||
The skel language definition has been commented-out within the skel.ldefs file so that the
|
||||
skeleton language does not show-up within Ghidra.
|
||||
|
||||
See the Sleigh language documentation (docs/languages/index.html) for details Sleigh language
|
||||
specification syntax.
|
||||
|
||||
348
codecut-gui/codecut-gui/data/modnaming.py
Normal file
348
codecut-gui/codecut-gui/data/modnaming.py
Normal file
@@ -0,0 +1,348 @@
|
||||
##############################################################################################
|
||||
# Copyright 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
# All rights reserved.
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
# software and associated documentation files (the "Software"), to deal in the Software
|
||||
# without restriction, including without limitation the rights to use, copy, modify,
|
||||
# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
||||
# OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# HAVE A NICE DAY.
|
||||
#
|
||||
# This material is based upon work supported by the Defense Advanced Research
|
||||
# Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
# under Contract Number N66001-20-C-4024.
|
||||
#
|
||||
|
||||
import sys
|
||||
import math
|
||||
import nltk
|
||||
import nltk.collocations
|
||||
import re
|
||||
|
||||
#uncomment "print" to get debug prints
|
||||
def debug_print(x):
|
||||
#print(x)
|
||||
return
|
||||
|
||||
### NLP Section ###
|
||||
|
||||
# This section of code attempts to name the modules based on common strings in the string references
|
||||
# Not really based on any sound science or anything - your mileage may heavily vary. :-D
|
||||
|
||||
#string_range_tokenize(t):
|
||||
#Take a long string and convert it into a list of tokens. If using a separator, this will appear in the token list
|
||||
def string_range_tokenize(t):
|
||||
|
||||
#print "string_range_tokenize: raw text:"
|
||||
#print t
|
||||
#remove printf/sprintf format strings
|
||||
#tc = re.sub("%[0-9A-Za-z]+"," ",t)
|
||||
#convert dash to underscore
|
||||
#tc = re.sub("-","_",tc)
|
||||
#replace _ and / with space - may want to turn this off sometimes
|
||||
#this will break up snake case and paths
|
||||
#problem is that if you have a path that is used throughout the binary it will probably dominate results
|
||||
#tc = re.sub("_"," ",tc)
|
||||
#replace / and \\ with a space
|
||||
#tc = re.sub("[/\\\\]"," ",tc)
|
||||
#remove anything except alphanumeric, spaces, . (for .c, .cpp, etc) and _
|
||||
#tc = re.sub("[^A-Za-z0-9_\.\s]"," ",tc)
|
||||
|
||||
#lowercase it - and store this as the original set of tokens to work with
|
||||
tokens = [tk.lower() for tk in t.split()]
|
||||
|
||||
#remove English stop words
|
||||
#this is the list from the MIT *bow project
|
||||
eng_stopw = {"about","all","am","an","and","are","as","at","be","been","but","by","can","cannot","did","do","does","doing","done","for","from","had","has","have","having","if","in","is","it","its","of","on","that","the","these","they","this","those","to","too","want","wants","was","what","which","will","with","would"}
|
||||
#remove "code" stop words
|
||||
#e.g. common words in debugging strings
|
||||
code_sw = {"error","err","errlog","log","return","returned","byte","bytes","status","len","length","size","ok","0x","warning","fail","failed","failure","invalid","illegal","param","parameter","done","complete","assert","assertion","cant","didnt","class","foundation","cdecl","stdcall","thiscall"}
|
||||
#remove code stop words (from Joxean Koret's "IDAMagicStrings")
|
||||
jk_sw = {"copyright", "char", "bool", "int", "unsigned", "long",
|
||||
"double", "float", "signed", "license", "version", "cannot", "error",
|
||||
"invalid", "null", "warning", "general", "argument", "written", "report",
|
||||
"failed", "assert", "object", "integer", "unknown", "localhost", "native",
|
||||
"memory", "system", "write", "read", "open", "close", "help", "exit", "test",
|
||||
"return", "libs", "home", "ambiguous", "internal", "request", "inserting",
|
||||
"deleting", "removing", "updating", "adding", "assertion", "flags",
|
||||
"overflow", "enabled", "disabled", "enable", "disable", "virtual", "client",
|
||||
"server", "switch", "while", "offset", "abort", "panic", "static", "updated",
|
||||
"pointer", "reason", "month", "year", "week", "hour", "minute", "second",
|
||||
'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday',
|
||||
'january', 'february', 'march', 'april', 'may', 'june', 'july', 'august',
|
||||
'september', 'october', 'november', 'december', "arguments", "corrupt",
|
||||
"corrupted", "default", "success", "expecting", "missing", "phrase",
|
||||
"unrecognized", "undefined"}
|
||||
|
||||
stopw = eng_stopw.union(code_sw)
|
||||
stopw = stopw.union(jk_sw)
|
||||
|
||||
c = 0
|
||||
|
||||
tokens_f = []
|
||||
|
||||
for t in tokens:
|
||||
if t not in stopw:
|
||||
tokens_f.append(t)
|
||||
|
||||
return tokens_f
|
||||
|
||||
#bracket_strings(t,b_brack,e_brack):
|
||||
#Return the most common string in the text that begins with b_brack and ends with e_brack
|
||||
# The count of how many times this string appeared is also returned
|
||||
#I find somewhat often people format debug strings like "[MOD_NAME] Function X did Y!"
|
||||
#This function is called by guess_module_names() - if you see this format with different brackets
|
||||
#you can edit that call
|
||||
def bracket_strings(t, b_brack,e_brack, sep):
|
||||
#sep = "tzvlw"
|
||||
#t = basicutils.CompileTextFromRange(start,end,sep)
|
||||
tokens = [tk.lower() for tk in t.split(sep)]
|
||||
#don't want to use tokenize here because it removes brackets
|
||||
|
||||
b=[]
|
||||
for tk in tokens:
|
||||
tk = tk.strip()
|
||||
|
||||
if tk.startswith(b_brack) :
|
||||
b_contents = tk[1:tk.find(e_brack)]
|
||||
#print("found bracket string, content: %s" % b_contents)
|
||||
#Hack to get rid of [-],[+],[*] - could also try to remove non alpha
|
||||
if (len(b_contents) > 3):
|
||||
#Hack for debug prints that started with [0x%x]
|
||||
if (b_contents != "0x%x"):
|
||||
b.append(b_contents)
|
||||
|
||||
debug_print("bracket_strings tokens:")
|
||||
debug_print(tokens)
|
||||
debug_print(b)
|
||||
|
||||
u_gram=""
|
||||
u_gram_score=0
|
||||
if (len(b) > 0):
|
||||
f = nltk.FreqDist(b)
|
||||
u_gram = f.most_common(1)[0][0]
|
||||
u_gram_score = f.most_common(1)[0][1]
|
||||
|
||||
return (u_gram,u_gram_score)
|
||||
|
||||
#is_source_file_str(f):
|
||||
#return True if the file string ends with one of the source file extensions
|
||||
#This uses structure borrowed from Joxean Koret's IDAMagicStrings
|
||||
LANGS = {}
|
||||
LANGS["C/C++"] = ["c", "cc", "cxx", "cpp", "h", "hpp"]
|
||||
LANGS["C"] = ["c"]
|
||||
LANGS["C++"] = ["cc", "cxx", "cpp", "hpp", "c++"]
|
||||
LANGS["Obj-C"] = ["m"]
|
||||
LANGS["Rust"] = ["rs"]
|
||||
LANGS["Golang"] = ["go"]
|
||||
LANGS["OCaml"] = ["ml"]
|
||||
def is_source_file_str(f):
|
||||
for key in LANGS:
|
||||
for ext in LANGS[key]:
|
||||
if f.endswith("." + ext):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
#source_file_strings(start,end):
|
||||
#Return the most common string that looks like a source file name in the given text string
|
||||
# The count of how many times this string appeared is also returned
|
||||
def source_file_strings(t,sep):
|
||||
#sep = "tzvlw"
|
||||
#t = basicutils.CompileTextFromRange(start,end,sep)
|
||||
#normally would do lower here to normalize but we lose camel case that way
|
||||
tokens = [tk for tk in t.split(sep)]
|
||||
|
||||
#for each string, remove quotes and commas, then tokenize based on spaces to generate the final list
|
||||
tokens2=[]
|
||||
for tk in tokens:
|
||||
tk = tk.strip()
|
||||
#strip punctuation, need to leave in _ for filenames and / and \ for paths
|
||||
tk = re.sub("[\"\'\,]"," ",tk)
|
||||
for tk2 in tk.split(" "):
|
||||
tokens2.append(tk2)
|
||||
|
||||
debug_print("source_file_strings tokens2:")
|
||||
debug_print(tokens2)
|
||||
|
||||
b=[]
|
||||
for tk in tokens2:
|
||||
tk = tk.strip()
|
||||
if is_source_file_str(tk):
|
||||
#If there's a dir path, only use the end filename
|
||||
#This could be tweaked if the directory structure is part of the software architecture
|
||||
#e.g. if there are multiple source directories with meaningful names
|
||||
if tk.rfind("/") != -1:
|
||||
ntk = tk[tk.rfind("/")+1:]
|
||||
elif tk.rfind("\\") != -1:
|
||||
ntk = tk[tk.rfind("\\")+1:]
|
||||
else:
|
||||
ntk = tk
|
||||
b.append(ntk)
|
||||
|
||||
debug_print("source_file_strings tokens:")
|
||||
debug_print(tokens)
|
||||
debug_print(b)
|
||||
|
||||
#a better way to do this (if there are multiple)
|
||||
#would be to sort, uniquify, and then make the name foo.c_and_bar.c
|
||||
u_gram=""
|
||||
u_gram_score=0
|
||||
if (len(b) > 0):
|
||||
f = nltk.FreqDist(b)
|
||||
u_gram = f.most_common(1)[0][0]
|
||||
u_gram_score = f.most_common(1)[0][1]
|
||||
|
||||
return (u_gram,u_gram_score)
|
||||
|
||||
#common_strings(t, sep):
|
||||
#Return a list of the common strings in the string "t" - lines separated by "sep"
|
||||
#Uses NLTK to generate a list of unigrams, bigrams, and trigrams (1 word, 2 word phrase, 3 word phrase)
|
||||
#If the trigram score > 1/2 * bigram score, the most common trigram is used
|
||||
#If the bigram score > 1/2 * unigram score, the most common bigram is used
|
||||
#Otherwise the most common unigram (single word is used)
|
||||
def common_strings(t,sep):
|
||||
CS_THRESHOLD = 6
|
||||
|
||||
tokens = string_range_tokenize(t)
|
||||
|
||||
#make a copy since we're going to edit it
|
||||
u_tokens = tokens
|
||||
c=0
|
||||
while (c<len(u_tokens)):
|
||||
if u_tokens[c] == sep:
|
||||
del u_tokens[c]
|
||||
else:
|
||||
c+=1
|
||||
|
||||
debug_print("common_strings tokens:")
|
||||
debug_print(tokens)
|
||||
|
||||
if len(u_tokens) < CS_THRESHOLD:
|
||||
#print("less than threshold")
|
||||
return ("",0)
|
||||
|
||||
f = nltk.FreqDist(u_tokens)
|
||||
u_gram = f.most_common(1)[0][0]
|
||||
u_gram_score = f.most_common(1)[0][1]
|
||||
|
||||
#print "Tokens:"
|
||||
#print tokens
|
||||
#print len(tokens)
|
||||
|
||||
bgs = list(nltk.bigrams(tokens))
|
||||
c=0
|
||||
while (c<len(bgs)):
|
||||
if sep in bgs[c]:
|
||||
del bgs[c]
|
||||
else:
|
||||
c+=1
|
||||
|
||||
debug_print("Bigrams:")
|
||||
debug_print(bgs)
|
||||
if (len(bgs) != 0):
|
||||
fs = nltk.FreqDist(bgs)
|
||||
b_gram = fs.most_common(1)[0][0]
|
||||
#print "Most Common:"
|
||||
#print b_gram
|
||||
b_str = b_gram[0] + "_" + b_gram[1]
|
||||
b_gram_score = fs.most_common(1)[0][1]
|
||||
else:
|
||||
b_str =""
|
||||
b_gram_score = 0
|
||||
|
||||
tgs = list(nltk.trigrams(tokens))
|
||||
c=0
|
||||
while (c<len(tgs)):
|
||||
if sep in tgs[c]:
|
||||
del tgs[c]
|
||||
else:
|
||||
c+=1
|
||||
debug_print("Trigrams:")
|
||||
debug_print(tgs)
|
||||
if (len(tgs) != 0):
|
||||
ft = nltk.FreqDist(tgs)
|
||||
t_gram = ft.most_common(1)[0][0]
|
||||
t_str = t_gram[0] + "_" + t_gram[1] + "_" + t_gram[2]
|
||||
t_gram_score = ft.most_common(1)[0][1]
|
||||
else:
|
||||
t_str = ""
|
||||
t_gram_score = 0
|
||||
|
||||
|
||||
debug_print("1: %s - %d 2: %s - %d 3: %s - %d\n" % (u_gram,u_gram_score,b_str,b_gram_score,t_str,t_gram_score))
|
||||
|
||||
if (b_gram_score > 1) and (b_gram_score * 2 >= u_gram_score):
|
||||
if (t_gram_score > 1) and (t_gram_score * 2 >= b_gram_score):
|
||||
ret = t_str
|
||||
ret_s = t_gram_score
|
||||
else:
|
||||
ret = b_str
|
||||
ret_s = b_gram_score
|
||||
else:
|
||||
ret = u_gram
|
||||
ret_s = u_gram_score
|
||||
|
||||
return (ret,ret_s)
|
||||
|
||||
### End of NLP Section ###
|
||||
|
||||
|
||||
|
||||
#guess_module_names():
|
||||
#Use the NLP section (above) to guess the names of modules and add them to the global module list
|
||||
#Attempts to find common bracket strings (e.g. "[MOD_NAME] Debug print!")
|
||||
#then source file names (most often left over from calls to assert())
|
||||
#then common trigram/bigram/unigrams
|
||||
#You can tweak the switchover thresholds below.
|
||||
|
||||
def guess_module_names(t,sep):
|
||||
#idea - make score threshold based on the size of the module
|
||||
# (e.g. smaller modules should have a smaller threshold
|
||||
C_SCORE_THRESHOLD = 4 #we need to see at least <N> occurrences of a string set in order to pick that name
|
||||
S_SCORE_THRESHOLD = 2 #if we see <N> occurrences of foo.c we'll pick "foo.c"
|
||||
B_SCORE_THRESHOLD = 2 #if we see <N> occurrences of [foo] we'll pick "foo"
|
||||
|
||||
# first look for strings that start with [FOO], (bracket strings)
|
||||
# then look for strings that contain source files (.c,.cpp,etc.)
|
||||
# then try common strings
|
||||
# above thresholds can be tweaked - they represent the number of strings that have to be repeated
|
||||
# in order to use that string as the module name
|
||||
(name,scr) = bracket_strings(t,"[","]",sep)
|
||||
debug_print("bracket name: %s score: %d" %(name, scr))
|
||||
#if (True):
|
||||
if (scr < B_SCORE_THRESHOLD):
|
||||
(name,scr) = source_file_strings(t,sep)
|
||||
debug_print("source name: %s score: %d" % (name, scr))
|
||||
#if (True):e
|
||||
if (scr < S_SCORE_THRESHOLD):
|
||||
(name,scr) = common_strings(t,sep)
|
||||
debug_print("common name: %s score: %d" % (name, scr))
|
||||
if (scr < C_SCORE_THRESHOLD):
|
||||
#Couldn't come up with a name
|
||||
name = "unknown"
|
||||
|
||||
return name
|
||||
|
||||
def main():
|
||||
#t=""
|
||||
sep = "tzvlw"
|
||||
# java side handles adding sep between strings,
|
||||
# read all in at once (no newlines between strings)
|
||||
#t = sys.stdin.readline()
|
||||
t = input()
|
||||
#print ("text in: %s" % t)
|
||||
name = guess_module_names(t,sep)
|
||||
print(name)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
5
codecut-gui/codecut-gui/extension.properties
Normal file
5
codecut-gui/codecut-gui/extension.properties
Normal file
@@ -0,0 +1,5 @@
|
||||
name=@extname@
|
||||
description=CodeCut / DeepCut GUI
|
||||
author=JHU/APL
|
||||
createdOn=
|
||||
version=@extversion@
|
||||
1
codecut-gui/codecut-gui/ghidra_scripts/README.txt
Normal file
1
codecut-gui/codecut-gui/ghidra_scripts/README.txt
Normal file
@@ -0,0 +1 @@
|
||||
Java source directory to hold module-specific Ghidra scripts.
|
||||
3
codecut-gui/codecut-gui/lib/README.txt
Normal file
3
codecut-gui/codecut-gui/lib/README.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
The "lib" directory is intended to hold Jar files which this module
|
||||
is dependent upon. This directory may be eliminated from a specific
|
||||
module if no other Jar files are needed.
|
||||
3
codecut-gui/codecut-gui/os/linux64/README.txt
Normal file
3
codecut-gui/codecut-gui/os/linux64/README.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
The "os/linux64" directory is intended to hold Linux native binaries
|
||||
which this module is dependent upon. This directory may be eliminated for a specific
|
||||
module if native binaries are not provided for the corresponding platform.
|
||||
3
codecut-gui/codecut-gui/os/osx64/README.txt
Normal file
3
codecut-gui/codecut-gui/os/osx64/README.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
The "os/osx64" directory is intended to hold macOS (OS X) native binaries
|
||||
which this module is dependent upon. This directory may be eliminated for a specific
|
||||
module if native binaries are not provided for the corresponding platform.
|
||||
3
codecut-gui/codecut-gui/os/win64/README.txt
Normal file
3
codecut-gui/codecut-gui/os/win64/README.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
The "os/win64" directory is intended to hold MS Windows native binaries (.exe)
|
||||
which this module is dependent upon. This directory may be eliminated for a specific
|
||||
module if native binaries are not provided for the corresponding platform.
|
||||
57
codecut-gui/codecut-gui/src/main/help/help/TOC_Source.xml
Normal file
57
codecut-gui/codecut-gui/src/main/help/help/TOC_Source.xml
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version='1.0' encoding='ISO-8859-1' ?>
|
||||
<!--
|
||||
|
||||
This is an XML file intended to be parsed by the Ghidra help system. It is loosely based
|
||||
upon the JavaHelp table of contents document format. The Ghidra help system uses a
|
||||
TOC_Source.xml file to allow a module with help to define how its contents appear in the
|
||||
Ghidra help viewer's table of contents. The main document (in the Base module)
|
||||
defines a basic structure for the
|
||||
Ghidra table of contents system. Other TOC_Source.xml files may use this structure to insert
|
||||
their files directly into this structure (and optionally define a substructure).
|
||||
|
||||
|
||||
In this document, a tag can be either a <tocdef> or a <tocref>. The former is a definition
|
||||
of an XML item that may have a link and may contain other <tocdef> and <tocref> children.
|
||||
<tocdef> items may be referred to in other documents by using a <tocref> tag with the
|
||||
appropriate id attribute value. Using these two tags allows any module to define a place
|
||||
in the table of contents system (<tocdef>), which also provides a place for
|
||||
other TOC_Source.xml files to insert content (<tocref>).
|
||||
|
||||
During the help build time, all TOC_Source.xml files will be parsed and validated to ensure
|
||||
that all <tocref> tags point to valid <tocdef> tags. From these files will be generated
|
||||
<module name>_TOC.xml files, which are table of contents files written in the format
|
||||
desired by the JavaHelp system. Additionally, the genated files will be merged together
|
||||
as they are loaded by the JavaHelp system. In the end, when displaying help in the Ghidra
|
||||
help GUI, there will be on table of contents that has been created from the definitions in
|
||||
all of the modules' TOC_Source.xml files.
|
||||
|
||||
|
||||
Tags and Attributes
|
||||
|
||||
<tocdef>
|
||||
-id - the name of the definition (this must be unique across all TOC_Source.xml files)
|
||||
-text - the display text of the node, as seen in the help GUI
|
||||
-target** - the file to display when the node is clicked in the GUI
|
||||
-sortgroup - this is a string that defines where a given node should appear under a given
|
||||
parent. The string values will be sorted by the JavaHelp system using
|
||||
a javax.text.RulesBasedCollator. If this attribute is not specified, then
|
||||
the text of attribute will be used.
|
||||
|
||||
<tocref>
|
||||
-id - The id of the <tocdef> that this reference points to
|
||||
|
||||
**The URL for the target is relative and should start with 'help/topics'. This text is
|
||||
used by the Ghidra help system to provide a universal starting point for all links so that
|
||||
they can be resolved at runtime, across modules.
|
||||
|
||||
|
||||
-->
|
||||
|
||||
|
||||
<tocroot>
|
||||
<!-- Uncomment and adjust fields to add help topic to help system's Table of Contents
|
||||
<tocref id="Ghidra Functionality">
|
||||
<tocdef id="HelpAnchor" text="My Feature" target="help/topics/my_topic/help.html" />
|
||||
</tocref>
|
||||
-->
|
||||
</tocroot>
|
||||
@@ -0,0 +1,64 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
WARNING!
|
||||
This file is copied to all help directories. If you change this file, you must copy it
|
||||
to each src/main/help/help/shared directory.
|
||||
|
||||
|
||||
Java Help Note: JavaHelp does not accept sizes (like in 'margin-top') in anything but
|
||||
px (pixel) or with no type marking.
|
||||
|
||||
*/
|
||||
|
||||
body { margin-bottom: 50px; margin-left: 10px; margin-right: 10px; margin-top: 10px; } /* some padding to improve readability */
|
||||
li { font-family:times new roman; font-size:14pt; }
|
||||
h1 { color:#000080; font-family:times new roman; font-size:36pt; font-style:italic; font-weight:bold; text-align:center; }
|
||||
h2 { margin: 10px; margin-top: 20px; color:#984c4c; font-family:times new roman; font-size:18pt; font-weight:bold; }
|
||||
h3 { margin-left: 10px; margin-top: 20px; color:#0000ff; font-family:times new roman; `font-size:14pt; font-weight:bold; }
|
||||
h4 { margin-left: 10px; margin-top: 20px; font-family:times new roman; font-size:14pt; font-style:italic; }
|
||||
|
||||
/*
|
||||
P tag code. Most of the help files nest P tags inside of blockquote tags (the was the
|
||||
way it had been done in the beginning). The net effect is that the text is indented. In
|
||||
modern HTML we would use CSS to do this. We need to support the Ghidra P tags, nested in
|
||||
blockquote tags, as well as naked P tags. The following two lines accomplish this. Note
|
||||
that the 'blockquote p' definition will inherit from the first 'p' definition.
|
||||
*/
|
||||
p { margin-left: 40px; font-family:times new roman; font-size:14pt; }
|
||||
blockquote p { margin-left: 10px; }
|
||||
|
||||
p.providedbyplugin { color:#7f7f7f; margin-left: 10px; font-size:14pt; margin-top:100px }
|
||||
p.ProvidedByPlugin { color:#7f7f7f; margin-left: 10px; font-size:14pt; margin-top:100px }
|
||||
p.relatedtopic { color:#800080; margin-left: 10px; font-size:14pt; }
|
||||
p.RelatedTopic { color:#800080; margin-left: 10px; font-size:14pt; }
|
||||
|
||||
/*
|
||||
We wish for a tables to have space between it and the preceding element, so that text
|
||||
is not too close to the top of the table. Also, nest the table a bit so that it is clear
|
||||
the table relates to the preceding text.
|
||||
*/
|
||||
table { margin-left: 20px; margin-top: 10px; width: 80%;}
|
||||
td { font-family:times new roman; font-size:14pt; vertical-align: top; }
|
||||
th { font-family:times new roman; font-size:14pt; font-weight:bold; background-color: #EDF3FE; }
|
||||
|
||||
/*
|
||||
Code-like formatting for things such as file system paths and proper names of classes,
|
||||
methods, etc. To apply this to a file path, use this syntax:
|
||||
<CODE CLASS="path">...</CODE>
|
||||
*/
|
||||
code { color: black; font-weight: bold; font-family: courier new, monospace; font-size: 14pt; white-space: nowrap; }
|
||||
code.path { color: #4682B4; font-weight: bold; font-family: courier new, monospace; font-size: 14pt; white-space: nowrap; }
|
||||
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<META name="generator" content=
|
||||
"HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net">
|
||||
<META http-equiv="Content-Language" content="en-us">
|
||||
<META http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
||||
<META name="GENERATOR" content="Microsoft FrontPage 4.0">
|
||||
<META name="ProgId" content="FrontPage.Editor.Document">
|
||||
|
||||
<TITLE>Skeleton Help File for a Module</TITLE>
|
||||
<LINK rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
|
||||
</HEAD>
|
||||
|
||||
<BODY>
|
||||
<H1><a name="HelpAnchor"></a>Skeleton Help File for a Module</H1>
|
||||
|
||||
<P>This is a simple skeleton help topic. For a better description of what should and should not
|
||||
go in here, see the "sample" Ghidra extension in the Extensions/Ghidra directory, or see your
|
||||
favorite help topic. In general, language modules do not have their own help topics.</P>
|
||||
</BODY>
|
||||
</HTML>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,119 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
import java.awt.datatransfer.*;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.SymbolTable;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.table.*;
|
||||
|
||||
public class CodeCutTransferHandler extends TransferHandler {
|
||||
|
||||
public boolean canImport(TransferHandler.TransferSupport support) {
|
||||
if (!support.isDrop()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!support.isDataFlavorSupported(DataFlavor.stringFlavor)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected Transferable createTransferable(JComponent c) {
|
||||
CodecutUtils.setTransferring(true);
|
||||
GhidraTable table = (GhidraTable)c;
|
||||
SymbolTableModel model = (SymbolTableModel)table.getModel();
|
||||
int selectedRow = table.getSelectedRow();
|
||||
Symbol sym = model.getRowObject(selectedRow); // will this work if order is changed in UI?
|
||||
Msg.info(this, "DnD createTransferable - selected symbol is " + sym.getName());
|
||||
|
||||
StringBuffer buff = new StringBuffer();
|
||||
String[] path = sym.getPath();
|
||||
Msg.info(this, "Symbol path is: " + path);
|
||||
buff.append(sym.getName());
|
||||
buff.append(",");
|
||||
buff.append(sym.getAddress());
|
||||
buff.append(",");
|
||||
buff.append(selectedRow);
|
||||
buff.append(",");
|
||||
buff.append(sym.getID());
|
||||
Msg.info(this, "DnD export: " + buff.toString());
|
||||
return new StringSelection(buff.toString());
|
||||
}
|
||||
|
||||
public int getSourceActions(JComponent c) {
|
||||
return TransferHandler.COPY_OR_MOVE;
|
||||
}
|
||||
|
||||
public boolean importData(TransferHandler.TransferSupport info) {
|
||||
Msg.info(this, "in transfer handler's importData");
|
||||
if (!info.isDrop()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Transferable t = info.getTransferable();
|
||||
String data;
|
||||
try {
|
||||
data = (String)t.getTransferData(DataFlavor.stringFlavor);
|
||||
}
|
||||
catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String[] values = data.split(",");
|
||||
if (values.length == 4) {
|
||||
GhidraTable table = (GhidraTable)info.getComponent();
|
||||
SymbolTableModel model = (SymbolTableModel)table.getModel();
|
||||
SymbolTable symTable = model.getProgram().getSymbolTable();
|
||||
|
||||
long symId = Long.parseLong(values[3]);
|
||||
Symbol symToUpdate = symTable.getSymbol(symId);
|
||||
|
||||
|
||||
Namespace targetNs = model.getRowObject(0).getParentNamespace();
|
||||
Msg.info(this, "Updating " + symToUpdate.getName() + " to ns " + targetNs.getName());
|
||||
int transactionID = table.getProgram().startTransaction("ns");
|
||||
try {
|
||||
symToUpdate.setNamespace(targetNs);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.info(this, "Could not change symbol " + symToUpdate.getName() + " to namespace " + targetNs + " in DnD operation - " + e.getClass() + ": " + e.getMessage());
|
||||
table.getProgram().endTransaction(transactionID, false);
|
||||
}
|
||||
table.getProgram().endTransaction(transactionID, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,833 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.SymbolPath;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressOverflowException;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.address.AddressRangeImpl;
|
||||
import ghidra.program.model.address.GlobalNamespace;
|
||||
import ghidra.program.model.listing.CircularDependencyException;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.FunctionManager;
|
||||
import ghidra.program.model.listing.GhidraClass;
|
||||
import ghidra.program.model.listing.Library;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.SymbolIterator;
|
||||
import ghidra.program.model.symbol.SymbolTable;
|
||||
import ghidra.program.model.symbol.SymbolType;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
|
||||
/**
|
||||
* A class to hold utility methods for working with namespaces.
|
||||
* <p>
|
||||
* <a id="examples"></a>
|
||||
* Example string format:
|
||||
* <ul>
|
||||
* <li>global{@link Namespace#DELIMITER ::}child1{@link Namespace#DELIMITER ::}child2
|
||||
* <li>child1
|
||||
* </ul>
|
||||
* <a id="assumptions"></a>
|
||||
* <b>Assumptions for creating namespaces from a path string: </b>
|
||||
* <ul>
|
||||
* <li>All elements of a namespace path should be namespace symbols and not other
|
||||
* symbol types.
|
||||
* <li>Absolute paths can optionally start with the global namespace.
|
||||
* <li>You can provide a relative path that will start at the given
|
||||
* parent namespace (or global if there is no parent provided).
|
||||
* <li>You can provide a path that has as its first entry the name of the
|
||||
* given parent. In this case, the first entry will not be created,
|
||||
* but rather the provided parent will be used.
|
||||
* <li>If you provide a path and a parent, but the first element of the
|
||||
* path is the global namespace, then the global namespace will be
|
||||
* used as the parent namespace and not the one that was provided.
|
||||
* <li>You cannot embed the global namespace in a path, but it can be at
|
||||
* the root.
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class CodecutUtils {
|
||||
|
||||
private static boolean updatingNamespace;
|
||||
private static boolean setUpdating;
|
||||
private static boolean transferring;
|
||||
private static boolean setTransferring;
|
||||
private static NewSymbolFilter filter;
|
||||
|
||||
private CodecutUtils() {
|
||||
// singleton utils class--no public construction
|
||||
updatingNamespace = false;
|
||||
setUpdating = false;
|
||||
transferring = false;
|
||||
setTransferring = false;
|
||||
filter = new NewSymbolFilter();
|
||||
}
|
||||
|
||||
public static void setFilter(NewSymbolFilter filt) {
|
||||
filter = new NewSymbolFilter(filt);
|
||||
}
|
||||
|
||||
public static NewSymbolFilter getFilter() {
|
||||
return CodecutUtils.filter;
|
||||
}
|
||||
/**
|
||||
* Get the normal namespace path excluding any library name. Global namespace will be
|
||||
* returned as empty string, while other namespace paths will be returned with trailing ::
|
||||
* suffix.
|
||||
* @param namespace namespace
|
||||
* @return namespace path excluding any library name
|
||||
*/
|
||||
public static String getNamespacePathWithoutLibrary(Namespace namespace) {
|
||||
String str = new String();
|
||||
while (namespace != null && !(namespace instanceof GlobalNamespace) &&
|
||||
!(namespace instanceof Library)) {
|
||||
str = namespace.getName() + Namespace.DELIMITER + str;
|
||||
namespace = namespace.getParentNamespace();
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get namespace qualified symbol name
|
||||
* @param namespace namespace object
|
||||
* @param symbolName name of symbol
|
||||
* @param excludeLibraryName if true any library name will be excluded from path returned,
|
||||
* otherwise it will be included
|
||||
* @return namespace qualified symbol name
|
||||
*/
|
||||
public static String getNamespaceQualifiedName(Namespace namespace, String symbolName,
|
||||
boolean excludeLibraryName) {
|
||||
String str = "";
|
||||
if (excludeLibraryName && namespace.isExternal()) {
|
||||
str = getNamespacePathWithoutLibrary(namespace);
|
||||
}
|
||||
else if (namespace != null && !(namespace instanceof GlobalNamespace)) {
|
||||
str = namespace.getName(true) + Namespace.DELIMITER;
|
||||
}
|
||||
str += symbolName;
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a standard method for splitting a symbol path into its
|
||||
* various namespace and symbol name elements. While the current implementation
|
||||
* uses a very simplistic approach, this may be improved upon in the future
|
||||
* to handle various grouping concepts.
|
||||
* @param path symbol namespace path (path will be trimmed before parse)
|
||||
* @return order list of namespace names
|
||||
* @deprecated use SymbolPath instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static List<String> splitNamespacePath(String path) {
|
||||
return Arrays.asList(path.trim().split(Namespace.DELIMITER));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the library associated with the specified namespace
|
||||
* @param namespace namespace
|
||||
* @return associated library or null if not associated with a library
|
||||
*/
|
||||
public static Library getLibrary(Namespace namespace) {
|
||||
Namespace ns = namespace;
|
||||
while (ns.isExternal()) {
|
||||
if (ns instanceof Library) {
|
||||
return (Library) ns;
|
||||
}
|
||||
ns = ns.getParentNamespace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<Namespace> getAllNamespaces(Program program) {
|
||||
List<Namespace> namespaceList = new ArrayList<>();
|
||||
SymbolIterator iter = program.getSymbolTable().getDefinedSymbols();
|
||||
while (iter.hasNext()) {
|
||||
Symbol symbol = iter.next();
|
||||
SymbolType type = symbol.getSymbolType();
|
||||
if (type == SymbolType.FUNCTION) {
|
||||
Namespace ns = ((Function)symbol.getObject()).getParentNamespace();
|
||||
if (!namespaceList.contains(ns)) {
|
||||
namespaceList.add(ns);
|
||||
}
|
||||
}
|
||||
}
|
||||
return namespaceList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all namespaces with the given name in the parent namespace
|
||||
*
|
||||
* @param program the program to search
|
||||
* @param parent the parent namespace from which to find all namespaces with the given name;
|
||||
* if null, the global namespace will be used
|
||||
* @param namespaceName the name of the namespaces to retrieve
|
||||
* @return a list of all namespaces that match the given name in the given parent namespace.
|
||||
*/
|
||||
public static List<Namespace> getNamespacesByName(Program program, Namespace parent,
|
||||
String namespaceName) {
|
||||
validate(program, parent);
|
||||
List<Namespace> namespaceList = new ArrayList<>();
|
||||
List<Symbol> symbols = program.getSymbolTable().getSymbols(namespaceName, parent);
|
||||
for (Symbol symbol : symbols) {
|
||||
if (symbol.getSymbolType().isNamespace()) {
|
||||
namespaceList.add((Namespace) symbol.getObject());
|
||||
}
|
||||
}
|
||||
return namespaceList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of namespaces that match the given path. The path can be
|
||||
* relative to the given root namespace or absolute if the path begins with
|
||||
* the global namespace name.
|
||||
*
|
||||
* <P>Note: this path must only contain Namespace names and no other symbol types.
|
||||
*
|
||||
* @param program the program to search
|
||||
* @param parent the namespace to use as the root for relative paths. If null, the
|
||||
* global namespace will be used
|
||||
* @param pathString the path to the desired namespace
|
||||
* @return a list of namespaces that match the given path
|
||||
*/
|
||||
public static List<Namespace> getNamespaceByPath(Program program, Namespace parent,
|
||||
String pathString) {
|
||||
|
||||
validate(program, parent);
|
||||
|
||||
parent = adjustForNullRootNamespace(parent, pathString, program);
|
||||
|
||||
SymbolPath path = new SymbolPath(parent.getSymbol());
|
||||
if (pathString != null) {
|
||||
path = path.append(new SymbolPath(pathString));
|
||||
}
|
||||
|
||||
List<String> namespaceNames = path.asList();
|
||||
List<Namespace> namespaces = doGetNamespaces(namespaceNames, parent, program);
|
||||
return namespaces;
|
||||
}
|
||||
|
||||
private static List<Namespace> doGetNamespaces(List<String> namespaceNames,
|
||||
Namespace root, Program program) {
|
||||
|
||||
if (root == null) {
|
||||
root = program.getGlobalNamespace();
|
||||
}
|
||||
|
||||
List<Namespace> parents = Arrays.asList(root);
|
||||
for (String name : namespaceNames) {
|
||||
List<Namespace> matches = getMatchingNamespaces(name, parents, program);
|
||||
parents = matches;
|
||||
}
|
||||
return parents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list all namespaces that have the given name in any of the given namespaces
|
||||
*
|
||||
* @param childName the name of the namespaces to retrieve
|
||||
* @param parents a list of all namespaces to search for child namespaces with the given name
|
||||
* @param program the program to search
|
||||
* @return a list all namespaces that have the given name in any of the given namespaces
|
||||
*/
|
||||
public static List<Namespace> getMatchingNamespaces(String childName, List<Namespace> parents,
|
||||
Program program) {
|
||||
validate(program, parents);
|
||||
List<Namespace> list = new ArrayList<>();
|
||||
for (Namespace parent : parents) {
|
||||
list.addAll(getNamespacesByName(program, parent, childName));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list all symbols that have the given name in any of the given
|
||||
* parent namespaces.
|
||||
*
|
||||
* @param parents a list of all namespaces to search for symbols with the given name.
|
||||
* @param symbolName the name of the symbols to retrieve.
|
||||
* @param program the program to search.
|
||||
* @return a list all symbols that have the given name in any of the given namespaces.
|
||||
*/
|
||||
private static List<Symbol> searchForAllSymbolsInAnyOfTheseNamespaces(List<Namespace> parents,
|
||||
String symbolName, Program program) {
|
||||
|
||||
List<Symbol> list = new ArrayList<>();
|
||||
for (Namespace parent : parents) {
|
||||
list.addAll(program.getSymbolTable().getSymbols(symbolName, parent));
|
||||
}
|
||||
|
||||
return list;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all symbols that match the given path. The path consists of a series
|
||||
* of namespaces names separated by "::" followed by a label or function name.
|
||||
*
|
||||
* @param symbolPath the names of namespaces and symbol separated by "::".
|
||||
* @param program the program to search
|
||||
* @return the list of symbols that match the given
|
||||
*/
|
||||
public static List<Symbol> getSymbols(String symbolPath, Program program) {
|
||||
|
||||
List<String> namespaceNames = new SymbolPath(symbolPath).asList();
|
||||
if (namespaceNames.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
String symbolName = namespaceNames.remove(namespaceNames.size() - 1);
|
||||
List<Namespace> parents =
|
||||
doGetNamespaces(namespaceNames, program.getGlobalNamespace(), program);
|
||||
return searchForAllSymbolsInAnyOfTheseNamespaces(parents, symbolName, program);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of Symbol that match the given symbolPath.
|
||||
*
|
||||
* @param symbolPath the symbol path that specifies a series of namespace and symbol names.
|
||||
* @param program the program to search for symbols with the given path.
|
||||
* @return a list of Symbol that match the given symbolPath.
|
||||
*/
|
||||
public static List<Symbol> getSymbols(SymbolPath symbolPath, Program program) {
|
||||
SymbolPath parentPath = symbolPath.getParent();
|
||||
if (parentPath == null) {
|
||||
return program.getSymbolTable().getGlobalSymbols(symbolPath.getName());
|
||||
}
|
||||
List<Namespace> parents = doGetNamespaces(parentPath.asList(), null, program);
|
||||
return searchForAllSymbolsInAnyOfTheseNamespaces(parents, symbolPath.getName(), program);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first namespace with the given name and that is NOT a function that
|
||||
* is within the parent namespace. (ie. the first namespace that is not tied to a program
|
||||
* address)
|
||||
*
|
||||
* @param parent the parent namespace to search
|
||||
* @param namespaceName the name of the namespace to find
|
||||
* @param program the program to search.
|
||||
* @return the first namespace that matches, or null if no match.
|
||||
*/
|
||||
public static Namespace getFirstNonFunctionNamespace(Namespace parent, String namespaceName,
|
||||
Program program) {
|
||||
validate(program, parent);
|
||||
List<Symbol> symbols = program.getSymbolTable().getSymbols(namespaceName, parent);
|
||||
for (Symbol symbol : symbols) {
|
||||
if (symbol.getSymbolType().isNamespace() &&
|
||||
symbol.getSymbolType() != SymbolType.FUNCTION) {
|
||||
return (Namespace) symbol.getObject();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a namespace path string and creates a namespace hierarchy to
|
||||
* match that string. This method ignores function namespaces so the path
|
||||
* should not contain any function names. If you want traverse down through
|
||||
* functions, then use the version that also takes an address that is used to distinguish
|
||||
* between multiple functions with the same name.
|
||||
* <P>
|
||||
* The root namespace can be a function.
|
||||
*
|
||||
*
|
||||
* @param namespacePath The namespace name or path string to be parsed.
|
||||
* This value should not include a trailing symbol name, only namespace names.
|
||||
* @param rootNamespace The parent namespace under which the desired
|
||||
* namespace or path resides. If this value is null, then the
|
||||
* global namespace will be used. This namespace can be a function name;
|
||||
* @param program The current program in which the desired namespace
|
||||
* resides.
|
||||
* @param source the source type of the namespace
|
||||
* @return The namespace that matches the given path. This can be either an existing
|
||||
* namespace or a newly created one.
|
||||
* @throws InvalidInputException If a given namespace name is in an
|
||||
* invalid format and this method attempts to create that
|
||||
* namespace, or if the namespace string contains the global
|
||||
* namespace name in a position other than the root.
|
||||
* @see <a href="#assumptions">assumptions</a>
|
||||
*/
|
||||
public static Namespace createNamespaceHierarchy(String namespacePath, Namespace rootNamespace,
|
||||
Program program, SourceType source) throws InvalidInputException {
|
||||
return createNamespaceHierarchy(namespacePath, rootNamespace, program, null, source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a namespace path string and creates a namespace hierarchy to
|
||||
* match that string. This method allows function namespaces in the path
|
||||
* and uses the given address to resolve functions with duplicate names. When
|
||||
* resolving down the namespace path, a function that matches a name will only
|
||||
* be used if the given address is contained in the body of that function.
|
||||
*
|
||||
* <p>The root namespace can be a function.
|
||||
*
|
||||
* <p>If an address is passed, then the path can contain a function name provided the
|
||||
* address is in the body of the function; otherwise the names must all be namespaces other
|
||||
* than functions.
|
||||
*
|
||||
* @param namespacePath The namespace name or path string to be parsed
|
||||
* This value should not include a trailing symbol name, only namespace names
|
||||
* @param rootNamespace The parent namespace under which the desired
|
||||
* namespace or path resides. If this value is null, then the
|
||||
* global namespace will be used.
|
||||
* @param program The current program in which the desired namespace
|
||||
* resides
|
||||
* @param address the address used to resolve possible functions with duplicate names; may
|
||||
* be null
|
||||
* @param source the source of the namespace
|
||||
* @return The namespace that matches the given path. This can be either an existing
|
||||
* namespace or a newly created one.
|
||||
* @throws InvalidInputException If a given namespace name is in an
|
||||
* invalid format and this method attempts to create that
|
||||
* namespace, or if the namespace string contains the global
|
||||
* namespace name in a position other than the root.
|
||||
* @see <a href="#assumptions">assumptions</a>
|
||||
*/
|
||||
public static Namespace createNamespaceHierarchy(String namespacePath, Namespace rootNamespace,
|
||||
Program program, Address address, SourceType source) throws InvalidInputException {
|
||||
validate(program, rootNamespace);
|
||||
rootNamespace = adjustForNullRootNamespace(rootNamespace, namespacePath, program);
|
||||
if (namespacePath == null) {
|
||||
return rootNamespace;
|
||||
}
|
||||
|
||||
SymbolPath path = new SymbolPath(namespacePath);
|
||||
List<String> namespacesList = path.asList();
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
Namespace namespace = rootNamespace;
|
||||
for (String namespaceName : namespacesList) {
|
||||
Namespace ns = getNamespace(program, namespace, namespaceName, address);
|
||||
if (ns == null) {
|
||||
try {
|
||||
ns = symbolTable.createNameSpace(namespace, namespaceName, source);
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
throw new AssertException(
|
||||
"Duplicate name exception should not be possible here since we checked first!");
|
||||
}
|
||||
}
|
||||
namespace = ns;
|
||||
}
|
||||
|
||||
return namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the existing Function at the given address if its {@link SymbolPath} matches the
|
||||
* given path
|
||||
*
|
||||
* @param program the program
|
||||
* @param symbolPath the path of namespace
|
||||
* @param address the address
|
||||
* @return the namespace represented by the given path, or null if no such namespace exists
|
||||
*/
|
||||
public static Namespace getFunctionNamespaceAt(Program program, SymbolPath symbolPath,
|
||||
Address address) {
|
||||
|
||||
if (symbolPath == null || address == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Symbol[] symbols = program.getSymbolTable().getSymbols(address);
|
||||
for (Symbol symbol : symbols) {
|
||||
if (symbol.getSymbolType() == SymbolType.FUNCTION &&
|
||||
symbolPath.matchesPathOf(symbol)) {
|
||||
return (Function) symbol.getObject();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the existing Function containing the given address if its
|
||||
* {@link SymbolPath} matches the given path
|
||||
*
|
||||
* @param program the program
|
||||
* @param symbolPath the path of namespace
|
||||
* @param address the address
|
||||
* @return the namespace represented by the given path, or null if no such namespace exists
|
||||
*/
|
||||
public static Namespace getFunctionNamespaceContaining(Program program, SymbolPath symbolPath,
|
||||
Address address) {
|
||||
|
||||
if (symbolPath == null || address == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
FunctionManager fm = program.getFunctionManager();
|
||||
Function f = fm.getFunctionContaining(address);
|
||||
if (f != null) {
|
||||
if (symbolPath.matchesPathOf(f.getSymbol())) {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the namespace for the given symbol path <b>that is not a function</b>
|
||||
*
|
||||
* @param program the program from which to get the namespace
|
||||
* @param symbolPath the path of namespace names including the name of the desired namespace
|
||||
* @return the namespace represented by the given path, or null if no such namespace exists or
|
||||
* the namespace is a function
|
||||
*/
|
||||
public static Namespace getNonFunctionNamespace(Program program, SymbolPath symbolPath) {
|
||||
|
||||
if (symbolPath == null) {
|
||||
return program.getGlobalNamespace();
|
||||
}
|
||||
|
||||
List<Symbol> symbols = getSymbols(symbolPath, program);
|
||||
for (Symbol symbol : symbols) {
|
||||
if (symbol.getSymbolType() != SymbolType.FUNCTION &&
|
||||
symbol.getSymbolType().isNamespace()) {
|
||||
return (Namespace) symbol.getObject();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Namespace getNamespace(Program program, Namespace parent, String name,
|
||||
Address address) {
|
||||
|
||||
if (parent == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Symbol> symbols = program.getSymbolTable().getSymbols(name, parent);
|
||||
|
||||
// first see if there are any functions and if they contain the given address
|
||||
if (address != null) {
|
||||
for (Symbol symbol : symbols) {
|
||||
if (symbol.getSymbolType() == SymbolType.FUNCTION) {
|
||||
Function function = (Function) symbol.getObject();
|
||||
if (function.getBody().contains(address)) {
|
||||
return function;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// otherwise just see if there is a non-function namespace
|
||||
for (Symbol symbol : symbols) {
|
||||
SymbolType type = symbol.getSymbolType();
|
||||
if (type != SymbolType.FUNCTION && type.isNamespace()) {
|
||||
return (Namespace) symbol.getObject();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Namespace adjustForNullRootNamespace(Namespace parentNamespace,
|
||||
String namespacePath, Program program) {
|
||||
Namespace globalNamespace = program.getGlobalNamespace();
|
||||
if (namespacePath != null && namespacePath.startsWith(globalNamespace.getName())) {
|
||||
return globalNamespace;
|
||||
}
|
||||
|
||||
if (parentNamespace != null) {
|
||||
return parentNamespace;
|
||||
}
|
||||
|
||||
return globalNamespace;
|
||||
}
|
||||
|
||||
private static void validate(Program program, Namespace namespace) {
|
||||
if (namespace != null && !namespace.isGlobal()) {
|
||||
if (program != namespace.getSymbol().getProgram()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Given namespace does not belong to the given program");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void validate(Program program, List<Namespace> parents) {
|
||||
for (Namespace namespace : parents) {
|
||||
validate(program, namespace);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a namespace to a class by copying all namespace children into a newly created class
|
||||
* and then removing the old namespace
|
||||
*
|
||||
* @param namespace namespace to be converted
|
||||
* @return new class namespace
|
||||
* @throws InvalidInputException if namespace was contained within a function and can not be
|
||||
* converted to a class
|
||||
*/
|
||||
public static GhidraClass convertNamespaceToClass(Namespace namespace)
|
||||
throws InvalidInputException {
|
||||
|
||||
Symbol namespaceSymbol = namespace.getSymbol();
|
||||
String name = namespaceSymbol.getName();
|
||||
SourceType originalSource = namespaceSymbol.getSource();
|
||||
|
||||
SymbolTable symbolTable = namespaceSymbol.getProgram().getSymbolTable();
|
||||
|
||||
// Temporarily rename old namespace (it will be removed at the end)
|
||||
int count = 1;
|
||||
while (true) {
|
||||
String n = name + "_" + count++;
|
||||
try {
|
||||
namespaceSymbol.setName(n, SourceType.ANALYSIS);
|
||||
break;
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
// continue
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
throw new AssertException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// create new class namespace
|
||||
GhidraClass classNamespace = null;
|
||||
try {
|
||||
classNamespace =
|
||||
symbolTable.createClass(namespace.getParentNamespace(), name, originalSource);
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
throw new AssertException(e);
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
// The only cause of this exception can be assumed but we need to
|
||||
// avoid showing the user a temporary name
|
||||
throw new InvalidInputException(
|
||||
"Namespace contained within Function may not be converted to a class: " + name);
|
||||
}
|
||||
|
||||
// move everything from old namespace into new class namespace
|
||||
try {
|
||||
for (Symbol s : symbolTable.getSymbols(namespace)) {
|
||||
s.setNamespace(classNamespace);
|
||||
}
|
||||
namespaceSymbol.delete();
|
||||
}
|
||||
catch (DuplicateNameException | InvalidInputException | CircularDependencyException e) {
|
||||
throw new AssertException(e);
|
||||
}
|
||||
return classNamespace;
|
||||
}
|
||||
|
||||
public static boolean isFirstSymbolInNamespace(Program p, Symbol s) {
|
||||
SymbolIterator iterator =
|
||||
p.getSymbolTable().getSymbolIterator(s.getAddress().subtract(1), false);
|
||||
if (iterator.hasNext()) {
|
||||
Symbol prevSym = iterator.next();
|
||||
String prevSymNS = prevSym.getParentNamespace().toString();
|
||||
if (s.getParentNamespace().toString().equals(prevSymNS)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public static boolean isLastSymbolInNamespace(Program p, Symbol s) {
|
||||
SymbolIterator iterator =
|
||||
p.getSymbolTable().getPrimarySymbolIterator(s.getAddress().add(1), true);
|
||||
if (iterator.hasNext()) {
|
||||
Symbol nextSym = iterator.next();
|
||||
String nextSymNS = nextSym.getParentNamespace().toString();
|
||||
if (s.getParentNamespace().toString().equals(nextSymNS)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static AddressRange getNamespaceRange(Program p, Symbol s) {
|
||||
//create backward & forward iterators
|
||||
SymbolIterator revIter;
|
||||
SymbolIterator fwdIter;
|
||||
Symbol prevUpSym = s, prevDnSym = s;
|
||||
Symbol upSym = s, dnSym = s;
|
||||
String upSymNS = s.getParentNamespace().toString();
|
||||
String dnSymNS = s.getParentNamespace().toString();
|
||||
String symNS = s.getParentNamespace().toString();
|
||||
|
||||
try {
|
||||
revIter = p.getSymbolTable().getSymbolIterator(s.getAddress().subtractNoWrap(1), false);
|
||||
//follow backward iterator until the NS doesn't match
|
||||
do {
|
||||
if (revIter.hasNext()) {
|
||||
//ignore non-primary symbols
|
||||
if (upSym.isPrimary()) {
|
||||
prevUpSym = upSym;
|
||||
}
|
||||
upSym = revIter.next();
|
||||
if (upSym.isPrimary()) {
|
||||
upSymNS = upSym.getParentNamespace().toString();
|
||||
}
|
||||
}
|
||||
|
||||
} while (upSymNS.equals(symNS) && upSym.getAddress().hasSameAddressSpace(s.getAddress()) && revIter.hasNext());
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
//prevUpSym already set to s
|
||||
}
|
||||
|
||||
try {
|
||||
fwdIter = p.getSymbolTable().getPrimarySymbolIterator(s.getAddress().addNoWrap(1), true);
|
||||
//follow forward iterator until NS doesn't match
|
||||
do {
|
||||
if (fwdIter.hasNext()) {
|
||||
//don't have to check for primary here because we will always hit a primary symbol in a new namespace
|
||||
prevDnSym = dnSym;
|
||||
dnSym = fwdIter.next();
|
||||
dnSymNS = dnSym.getParentNamespace().toString();
|
||||
}
|
||||
|
||||
} while (dnSymNS.equals(symNS) && dnSym.getAddress().hasSameAddressSpace(s.getAddress()) && fwdIter.hasNext());
|
||||
}
|
||||
catch (AddressOverflowException e){
|
||||
//dnSym already set to s;
|
||||
}
|
||||
//Technically we should check here if dnSym is the last symbol in the program
|
||||
//then the end should be the end of the program. But since we are applying
|
||||
//namespaces to symbols, this is ok. This is something to watch out for if
|
||||
//we're going to export modules based on namespaces.
|
||||
|
||||
|
||||
Address end = dnSym.getAddress();
|
||||
//if we rolled into a new address space, pick the end of the symbol's address space
|
||||
if (! dnSym.getAddress().hasSameAddressSpace(s.getAddress()) ) {
|
||||
end = s.getAddress().getAddressSpace().getMaxAddress();
|
||||
}
|
||||
//normal case - symbol within the current address space
|
||||
else if (end.compareTo(s.getAddress()) != 0) {
|
||||
end = dnSym.getAddress().subtract(1);
|
||||
}
|
||||
|
||||
String msg = "address range:" + prevUpSym.getAddress().toString() + " " + end.toString();
|
||||
Msg.info(msg, msg);
|
||||
AddressRange a = new AddressRangeImpl(prevUpSym.getAddress(),end);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
public static AddressRange getNamespaceRange(Program p, Namespace ns) {
|
||||
if (ns.getName().equals("<EXTERNAL>")) {
|
||||
return null;
|
||||
}
|
||||
Address start = p.getMaxAddress();
|
||||
Address end = p.getMinAddress();
|
||||
SymbolIterator iter = p.getSymbolTable().getSymbols(ns);
|
||||
while (iter.hasNext()) {
|
||||
Symbol sym = iter.next();
|
||||
if (sym.getParentNamespace().equals(ns)) {
|
||||
Address symAddr = sym.getAddress();
|
||||
end = Address.max(symAddr, end);
|
||||
start = Address.min(symAddr, start);
|
||||
}
|
||||
}
|
||||
AddressRange addrRange = new AddressRangeImpl(start, end);
|
||||
return addrRange;
|
||||
}
|
||||
|
||||
public static void renameNamespace(Program p, Namespace oldNamespace, Namespace newNamespace) throws DuplicateNameException, InvalidInputException, CircularDependencyException {
|
||||
if (oldNamespace.equals(newNamespace)) {
|
||||
return;
|
||||
}
|
||||
updatingNamespace = true;
|
||||
SymbolIterator iter = p.getSymbolTable().getSymbols(oldNamespace);
|
||||
Symbol currSym = null;
|
||||
int transactionID = p.startTransaction("nsRename");
|
||||
try {
|
||||
while (iter.hasNext()) {
|
||||
currSym = iter.next();
|
||||
currSym.setNamespace(newNamespace);
|
||||
}
|
||||
oldNamespace.getSymbol().delete();
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.info(new Object(), "Could not set namespace for " + currSym.getName());
|
||||
Msg.info(new Object(), e.getMessage());
|
||||
p.endTransaction(transactionID, false);
|
||||
}
|
||||
p.endTransaction(transactionID, true);
|
||||
updatingNamespace = false;
|
||||
|
||||
}
|
||||
|
||||
//This is assuming that the namespace is contiguous and only appears once
|
||||
//The ideal situation would be either
|
||||
//1) enforcing contiguousness (making new names _2, _3 etc.) when there are duplicates
|
||||
//2) actually making a "module" concept in the database which points to namespace but is separate
|
||||
public static void splitNamespace(Program p, Symbol s, Namespace newNamespace) {
|
||||
AddressRange a = getNamespaceRange(p,s);
|
||||
SymbolIterator iter = p.getSymbolTable().getSymbolIterator(s.getAddress(), true);
|
||||
Symbol currSym = s;
|
||||
updatingNamespace = true;
|
||||
|
||||
int transactionID = p.startTransaction("nsSplit");
|
||||
try {
|
||||
do {
|
||||
currSym.setNamespace(newNamespace);
|
||||
currSym = iter.next();
|
||||
Msg.info(new Object(), "currSym.getAddress() =" + currSym.getAddress());
|
||||
} while (currSym.getAddress().compareTo(a.getMaxAddress()) <= 0);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.info(new Object(), "Trouble setting ns for symbol " + currSym.toString() + " to " + newNamespace.getName() );
|
||||
p.endTransaction(transactionID, false);
|
||||
}
|
||||
updatingNamespace = false;
|
||||
p.endTransaction(transactionID, true);
|
||||
|
||||
}
|
||||
|
||||
public static boolean nsUpdating() {
|
||||
return (updatingNamespace || setUpdating);
|
||||
}
|
||||
|
||||
public static void setUpdating(boolean status) {
|
||||
setUpdating = status;
|
||||
}
|
||||
|
||||
public static boolean transferring() {
|
||||
return (transferring || setTransferring);
|
||||
}
|
||||
|
||||
public static void setTransferring(boolean status) {
|
||||
setTransferring = status;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.AbstractButton;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextField;
|
||||
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.WindowPosition;
|
||||
import ghidra.app.context.ProgramActionContext;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.SymbolIterator;
|
||||
import ghidra.program.model.symbol.SymbolTable;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
import resources.ResourceManager;
|
||||
|
||||
class CombineProvider extends ComponentProviderAdapter implements ActionListener {
|
||||
|
||||
private static final ImageIcon ICON = ResourceManager.loadImage("images/textfield.png");
|
||||
|
||||
private CodeCutGUIPlugin plugin;
|
||||
private JPanel boxPanel;
|
||||
private JTextField firstTextField;
|
||||
private JTextField secondTextField;
|
||||
private JTextField combinedTextField;
|
||||
private JButton button;
|
||||
|
||||
private Namespace firstNamespace;
|
||||
private Namespace secondNamespace;
|
||||
private Namespace combinedNamespace;
|
||||
|
||||
CombineProvider(CodeCutGUIPlugin plugin) {
|
||||
super(plugin.getTool(), "Combine Namespaces", plugin.getName(), ProgramActionContext.class);
|
||||
this.plugin = plugin;
|
||||
|
||||
setIcon(ICON);
|
||||
addToToolbar();
|
||||
setHelpLocation(new HelpLocation(plugin.getName(), "CodeCut_Table"));
|
||||
setWindowGroup("codecutTable");
|
||||
setIntraGroupPosition(WindowPosition.BOTTOM);
|
||||
|
||||
boxPanel = new JPanel();
|
||||
boxPanel.setLayout(new BoxLayout(boxPanel, BoxLayout.Y_AXIS));
|
||||
|
||||
JPanel entryPanel = new JPanel(new PairLayout(6,10));
|
||||
|
||||
JLabel firstLabel = new JLabel("Primary Namespace: ");
|
||||
JLabel secondLabel = new JLabel("Secondary Namespace: ");
|
||||
JLabel combinedLabel = new JLabel("Combined Namespace: ");
|
||||
|
||||
firstTextField = new JTextField(30);
|
||||
if (firstNamespace != null) {
|
||||
firstTextField.setText(firstNamespace.getName());
|
||||
}
|
||||
secondTextField = new JTextField(30);
|
||||
if (secondNamespace != null) {
|
||||
secondTextField.setText(secondNamespace.getName());
|
||||
}
|
||||
combinedTextField = new JTextField(30);
|
||||
if (combinedNamespace != null) {
|
||||
combinedTextField.setText(combinedNamespace.getName());
|
||||
}
|
||||
button = new JButton("Combine");
|
||||
button.setVerticalTextPosition(AbstractButton.CENTER);
|
||||
button.setHorizontalTextPosition(AbstractButton.CENTER);
|
||||
button.setMnemonic(KeyEvent.VK_ENTER);
|
||||
button.setActionCommand("submit");
|
||||
button.addActionListener(this);
|
||||
entryPanel.add(firstLabel);
|
||||
entryPanel.add(firstTextField);
|
||||
entryPanel.add(secondLabel);
|
||||
entryPanel.add(secondTextField);
|
||||
entryPanel.add(combinedLabel);
|
||||
entryPanel.add(combinedTextField);
|
||||
entryPanel.add(button);
|
||||
entryPanel.setSize(entryPanel.getPreferredSize().width, firstTextField.getPreferredSize().height);
|
||||
Dimension dim = entryPanel.getPreferredSize();
|
||||
boxPanel.add(entryPanel);
|
||||
boxPanel.setSize(dim);
|
||||
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (this.firstNamespace != null) {
|
||||
// update first namespace if user changed the text field
|
||||
String firstName = this.firstTextField.getText();
|
||||
if (!this.firstNamespace.getName().equals(firstName)) {
|
||||
List<Namespace> nL = CodecutUtils.getNamespacesByName(plugin.getProgram(), null, firstName);
|
||||
if (nL.size() > 0) {
|
||||
this.firstNamespace = nL.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
// look up second namespace by name
|
||||
String secondName = secondTextField.getText();
|
||||
List<Namespace> nL = CodecutUtils.getNamespacesByName(plugin.getProgram(), null, secondName);
|
||||
if (nL.size() > 0) {
|
||||
this.secondNamespace = nL.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.firstNamespace != null && this.secondNamespace != null) {
|
||||
if ("submit".equals(e.getActionCommand())) {
|
||||
Program program = plugin.getProgram();
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
String newNamespace = combinedTextField.getText();
|
||||
if (CodecutUtils.getMatchingNamespaces(newNamespace, Arrays.asList(program.getGlobalNamespace()), program).isEmpty()) {
|
||||
Namespace nS = null;
|
||||
int transactionID = program.startTransaction("nsCombine");
|
||||
try {
|
||||
nS = symbolTable.createNameSpace(program.getGlobalNamespace(), newNamespace, SourceType.USER_DEFINED);
|
||||
}
|
||||
catch (DuplicateNameException ex) {
|
||||
//NS was already created,
|
||||
List<Namespace> nL = CodecutUtils.getNamespacesByName(program, program.getGlobalNamespace(), newNamespace);
|
||||
if (nL == null) {
|
||||
return;
|
||||
}
|
||||
nS = nL.get(0);
|
||||
}
|
||||
catch (InvalidInputException ex) {
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
program.endTransaction(transactionID, true);
|
||||
}
|
||||
try {
|
||||
// Set the namespaces of all the symbols from namespaces to be combined
|
||||
CodecutUtils.renameNamespace(program, this.secondNamespace, nS);
|
||||
CodecutUtils.renameNamespace(program, this.firstNamespace, nS);
|
||||
Msg.info(this, "Namespace " + this.secondNamespace.getName() + " combined with " + this.firstNamespace.getName() + " into namespace " + newNamespace);
|
||||
} catch (Exception ex) {
|
||||
Msg.info(this, "Exception when combining namespace " + this.secondNamespace.getName() + " with " +
|
||||
this.firstNamespace.getName() + ": " + ex.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
Namespace nS = symbolTable.getNamespace(newNamespace, program.getGlobalNamespace());
|
||||
int transactionID = program.startTransaction("nsCombine");
|
||||
|
||||
// if namespace is empty
|
||||
if (!symbolTable.getSymbols(nS).hasNext()) {
|
||||
try {
|
||||
CodecutUtils.renameNamespace(program, this.secondNamespace, nS);
|
||||
CodecutUtils.renameNamespace(program, this.firstNamespace, nS);
|
||||
Msg.info(this, "Namespace " + this.secondNamespace.getName() + " combined with " + this.firstNamespace.getName() + " into namespace " + newNamespace);
|
||||
} catch (Exception ex) {
|
||||
Msg.info(this, "Exception when combining namespace " + this.secondNamespace.getName() + " with " +
|
||||
this.firstNamespace.getName() + ": " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
// if namespace is not empty
|
||||
else {
|
||||
if (nS == this.firstNamespace) {
|
||||
SymbolIterator iter = symbolTable.getSymbols(this.secondNamespace);
|
||||
Symbol currSym = null;
|
||||
try {
|
||||
while (iter.hasNext()) {
|
||||
currSym = iter.next();
|
||||
currSym.setNamespace(this.firstNamespace);
|
||||
}
|
||||
this.secondNamespace.getSymbol().delete();
|
||||
}
|
||||
catch (Exception exception) {
|
||||
Msg.info(new Object(), "Could not set namespace for " + currSym.getName());
|
||||
Msg.info(new Object(), exception.getMessage());
|
||||
}
|
||||
}
|
||||
else if (nS == this.secondNamespace) {
|
||||
SymbolIterator iter = symbolTable.getSymbols(this.firstNamespace);
|
||||
Symbol currSym = null;
|
||||
try {
|
||||
while (iter.hasNext()) {
|
||||
currSym = iter.next();
|
||||
currSym.setNamespace(this.secondNamespace);
|
||||
}
|
||||
this.firstNamespace.getSymbol().delete();
|
||||
}
|
||||
catch (Exception exception) {
|
||||
Msg.info(new Object(), "Could not set namespace for " + currSym.getName());
|
||||
Msg.info(new Object(), exception.getMessage());
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Combined Namespace entered already exists and is not part of current operation");
|
||||
}
|
||||
program.endTransaction(transactionID, true);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
this.closeComponent();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
void open() {
|
||||
if (!isVisible()) {
|
||||
setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
plugin = null;
|
||||
}
|
||||
|
||||
public void setFirstNamespace(Namespace ns) {
|
||||
this.firstNamespace = ns;
|
||||
this.firstTextField.setText(ns.getName());
|
||||
}
|
||||
|
||||
public void setSecondNamespace(Namespace ns) {
|
||||
this.secondNamespace = ns;
|
||||
this.secondTextField.setText(ns.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
Program program = plugin.getProgram();
|
||||
if (program == null) {
|
||||
return null;
|
||||
}
|
||||
return new ProgramActionContext(this, program);
|
||||
}
|
||||
|
||||
public void updateTitle() {
|
||||
setSubTitle("Combine Namespaces");
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return boxPanel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.AbstractButton;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextField;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.WindowPosition;
|
||||
import ghidra.app.context.ProgramActionContext;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.SymbolTable;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
import resources.ResourceManager;
|
||||
|
||||
class CreateProvider extends ComponentProviderAdapter implements ActionListener {
|
||||
|
||||
private static final ImageIcon ICON = ResourceManager.loadImage("images/textfield.png");
|
||||
|
||||
private CodeCutGUIPlugin plugin;
|
||||
private JPanel boxPanel;
|
||||
private JTextField textField;
|
||||
private JButton button;
|
||||
|
||||
private Namespace namespace;
|
||||
private Symbol symbol;
|
||||
|
||||
CreateProvider(CodeCutGUIPlugin plugin) {
|
||||
super(plugin.getTool(), "Create Namespace", plugin.getName(), ProgramActionContext.class);
|
||||
this.plugin = plugin;
|
||||
|
||||
setIcon(ICON);
|
||||
addToToolbar();
|
||||
setHelpLocation(new HelpLocation(plugin.getName(), "CodeCut_Table"));
|
||||
setWindowGroup("codecutTable");
|
||||
setIntraGroupPosition(WindowPosition.BOTTOM);
|
||||
|
||||
boxPanel = new JPanel();
|
||||
boxPanel.setLayout(new BoxLayout(boxPanel, BoxLayout.Y_AXIS));
|
||||
|
||||
JPanel entryPanel = new JPanel(new PairLayout(6,10));
|
||||
textField = new JTextField(30);
|
||||
if (namespace != null) {
|
||||
textField.setText(namespace.getName());
|
||||
}
|
||||
button = new JButton("Create Namespace");
|
||||
button.setVerticalTextPosition(AbstractButton.CENTER);
|
||||
button.setHorizontalTextPosition(AbstractButton.CENTER);
|
||||
button.setMnemonic(KeyEvent.VK_ENTER);
|
||||
button.setActionCommand("submit");
|
||||
button.addActionListener(this);
|
||||
entryPanel.add(textField);
|
||||
entryPanel.add(button);
|
||||
entryPanel.setSize(entryPanel.getPreferredSize().width, textField.getPreferredSize().height);
|
||||
Dimension dim = entryPanel.getPreferredSize();
|
||||
boxPanel.add(entryPanel);
|
||||
boxPanel.setSize(dim);
|
||||
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (this.namespace != null) {
|
||||
if ("submit".equals(e.getActionCommand())) {
|
||||
String newNamespace = textField.getText();
|
||||
Program program = plugin.getProgram();
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
if (CodecutUtils.getMatchingNamespaces(newNamespace, Arrays.asList(program.getGlobalNamespace()), program).isEmpty()) {
|
||||
Namespace nS=null;
|
||||
int transactionID = program.startTransaction("nsCreate");
|
||||
try {
|
||||
nS = symbolTable.createNameSpace(program.getGlobalNamespace(), newNamespace, SourceType.USER_DEFINED);
|
||||
}
|
||||
catch (DuplicateNameException ex) {
|
||||
//NS was already created,
|
||||
List<Namespace> nL = CodecutUtils.getNamespacesByName(program, program.getGlobalNamespace(), newNamespace);
|
||||
if (nL == null) {
|
||||
return;
|
||||
}
|
||||
nS = nL.get(0);
|
||||
}
|
||||
catch (InvalidInputException ex) {
|
||||
program.endTransaction(transactionID, false);
|
||||
return;
|
||||
}
|
||||
program.endTransaction(transactionID, true);
|
||||
|
||||
|
||||
try {
|
||||
CodecutUtils.splitNamespace(program, this.symbol, nS);
|
||||
} catch (Exception ex) {
|
||||
Msg.info(this, "Exception when attempting to change namespace of symbol "
|
||||
+ this.symbol.getName() + " to " + nS.getName());
|
||||
}
|
||||
|
||||
this.closeComponent();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
void open() {
|
||||
if (!isVisible()) {
|
||||
setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
plugin = null;
|
||||
}
|
||||
|
||||
public void setCurrentNamespace(Namespace ns) {
|
||||
this.namespace = ns;
|
||||
this.textField.setText(ns.getName());
|
||||
}
|
||||
|
||||
public void setCurrentSymbol(Symbol symbol) {
|
||||
this.symbol = symbol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
Program program = plugin.getProgram();
|
||||
if (program == null) {
|
||||
return null;
|
||||
}
|
||||
return new ProgramActionContext(this, program);
|
||||
}
|
||||
|
||||
public void updateTitle() {
|
||||
setSubTitle("Add to New Namespace");
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return boxPanel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,376 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.jdom.Element;
|
||||
|
||||
import docking.ComponentProvider;
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.widgets.checkbox.GCheckBox;
|
||||
import docking.widgets.label.GHtmlLabel;
|
||||
import docking.widgets.label.GIconLabel;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.layout.*;
|
||||
import resources.ResourceManager;
|
||||
|
||||
public class FilterDialog extends DialogComponentProvider {
|
||||
private NewSymbolFilter filter;
|
||||
private JPanel advancedPanel;
|
||||
private JPanel advancedFilterPanel;
|
||||
private Map<String, JCheckBox> checkBoxMap = new HashMap<>();
|
||||
private boolean ignoreCallbacks;
|
||||
private JCheckBox advancedFilterCheckbox;
|
||||
private SymbolTableModel keyModel;
|
||||
private boolean isChanged;
|
||||
private FilterCheckboxItemListener checkboxListener = new FilterCheckboxItemListener();
|
||||
private PluginTool tool;
|
||||
|
||||
public FilterDialog(PluginTool tool) {
|
||||
super("Symbol Table Filter", false);
|
||||
this.tool = tool;
|
||||
filter = new NewSymbolFilter();
|
||||
addWorkPanel(buildWorkPanel());
|
||||
addOKButton();
|
||||
addApplyButton();
|
||||
addDismissButton();
|
||||
setHelpLocation(new HelpLocation(HelpTopics.SYMBOL_TABLE, "Set Filter"));
|
||||
initCheckBoxes();
|
||||
setRememberSize(false);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatusText(String text) {
|
||||
// All status messages displayed as alerts
|
||||
super.setStatusText(text, MessageType.ALERT);
|
||||
}
|
||||
|
||||
// for testing
|
||||
void setFilter(NewSymbolFilter newFilter) {
|
||||
filter = new NewSymbolFilter(newFilter);
|
||||
initCheckBoxes();
|
||||
setChanged(true);
|
||||
}
|
||||
|
||||
Element saveFilter() {
|
||||
return filter.saveToXml();
|
||||
}
|
||||
|
||||
void restoreFilter(Element element) {
|
||||
filter.restoreFromXml(element);
|
||||
initCheckBoxes();
|
||||
}
|
||||
|
||||
private void initCheckBoxes() {
|
||||
setChanged(false);
|
||||
ignoreCallbacks = true;
|
||||
Iterator<String> it = checkBoxMap.keySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
String filterName = it.next();
|
||||
JCheckBox cb = checkBoxMap.get(filterName);
|
||||
cb.setSelected(filter.isActive(filterName));
|
||||
}
|
||||
ignoreCallbacks = false;
|
||||
advancedFilterCheckbox.setSelected(filter.getActiveAdvancedFilterCount() > 0);
|
||||
update();
|
||||
}
|
||||
|
||||
private JComponent buildWorkPanel() {
|
||||
advancedFilterCheckbox = new GCheckBox("Use Advanced Filters");
|
||||
advancedFilterCheckbox.setToolTipText(HTMLUtilities.toHTML(
|
||||
"Show advance filters. Advanced filters eliminate all appropriate\n" +
|
||||
"symbols that don't match the filter. Selecting mutually exclusive filters\n" +
|
||||
"(such as Globals and Locals) will totally eliminate entire types of symbols."));
|
||||
advancedFilterCheckbox.addItemListener(e -> {
|
||||
setStatusText("");
|
||||
JCheckBox cb = (JCheckBox) e.getSource();
|
||||
if (cb.isSelected()) {
|
||||
advancedPanel.add(advancedFilterPanel);
|
||||
}
|
||||
else {
|
||||
advancedPanel.removeAll();
|
||||
clearAdvancedFilters();
|
||||
}
|
||||
FilterDialog.this.repack();
|
||||
update();
|
||||
});
|
||||
|
||||
JPanel mainPanel = new JPanel(new VerticalLayout(15));
|
||||
|
||||
JPanel filterPanel = new JPanel(new BorderLayout());
|
||||
|
||||
JPanel leftPanel = new JPanel(new VerticalLayout(20));
|
||||
leftPanel.add(buildSourcePanel());
|
||||
leftPanel.add(buildTypesPanel());
|
||||
filterPanel.add(leftPanel, BorderLayout.WEST);
|
||||
filterPanel.add(buildAdvancedPanel(), BorderLayout.EAST);
|
||||
mainPanel.add(filterPanel);
|
||||
mainPanel.add(advancedFilterCheckbox);
|
||||
mainPanel.add(buildResetPanel());
|
||||
mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 5, 0, 5));
|
||||
return mainPanel;
|
||||
}
|
||||
|
||||
private Component buildSourcePanel() {
|
||||
ItemListener sourceItemListener = e -> {
|
||||
if (ignoreCallbacks) {
|
||||
return;
|
||||
}
|
||||
JCheckBox cb = (JCheckBox) e.getItem();
|
||||
String name = cb.getText();
|
||||
setChanged(true);
|
||||
filter.setFilter(name, cb.isSelected());
|
||||
update();
|
||||
};
|
||||
|
||||
String[] sourceNames = filter.getSourceFilterNames();
|
||||
JPanel panel = new JPanel(new GridLayout(0, 2));
|
||||
for (String sourceName : sourceNames) {
|
||||
JCheckBox cb = new GCheckBox(sourceName);
|
||||
checkBoxMap.put(sourceName, cb);
|
||||
cb.addItemListener(sourceItemListener);
|
||||
cb.setToolTipText(HTMLUtilities.toHTML(filter.getFilterDescription(sourceName)));
|
||||
panel.add(cb);
|
||||
}
|
||||
panel.setBorder(BorderFactory.createTitledBorder("Symbol Source"));
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Component buildAdvancedPanel() {
|
||||
advancedPanel = new JPanel(new BorderLayout());
|
||||
|
||||
JPanel infoPanel = new JPanel(new HorizontalLayout(20));
|
||||
Icon icon = ResourceManager.loadImage("images/information.png");
|
||||
|
||||
infoPanel.add(new GIconLabel(icon));
|
||||
infoPanel.add(new GHtmlLabel(
|
||||
HTMLUtilities.toHTML("Advanced filters do not apply to all symbol types.\n" +
|
||||
"All symbols without applicable advanced filters will\n" +
|
||||
"be included. If more than one advanced filter is\n" +
|
||||
"applicable to a symbol type, then those symbols will\n" +
|
||||
"be included if any of the applicable filters match. \n" +
|
||||
"Filters that are not applicable to any of the selected\n" +
|
||||
"symbol types are disabled.")));
|
||||
|
||||
JPanel filtersPanel = new JPanel(new GridLayout(0, 2));
|
||||
// Border outer = BorderFactory.createEmptyBorder(0,40,0,0);
|
||||
// Border inner = BorderFactory.createTitledBorder("Advanced Filters");
|
||||
filtersPanel.setBorder(BorderFactory.createEmptyBorder(0, 40, 0, 0));
|
||||
String[] filterNames = filter.getAdvancedFilterNames();
|
||||
for (String filterName : filterNames) {
|
||||
JCheckBox cb = new GCheckBox(filterName);
|
||||
checkBoxMap.put(filterName, cb);
|
||||
cb.addItemListener(checkboxListener);
|
||||
cb.setToolTipText(HTMLUtilities.toHTML(filter.getFilterDescription(filterName)));
|
||||
filtersPanel.add(cb);
|
||||
}
|
||||
advancedFilterPanel = new JPanel(new VerticalLayout(10));
|
||||
advancedFilterPanel.setBorder(BorderFactory.createTitledBorder("Advanced Filters"));
|
||||
advancedFilterPanel.add(filtersPanel);
|
||||
advancedFilterPanel.add(infoPanel);
|
||||
return advancedPanel;
|
||||
}
|
||||
|
||||
private Component buildTypesPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setBorder(BorderFactory.createTitledBorder("Symbol Types"));
|
||||
panel.add(buildLabelTypesPanel("Label Symbols", filter.getLabelTypeFilterNames()),
|
||||
BorderLayout.WEST);
|
||||
panel.add(buildLabelTypesPanel("Non-label Symbols", filter.getNonLabelTypeFilterNames()),
|
||||
BorderLayout.EAST);
|
||||
panel.add(buildSelectButtonPanel(), BorderLayout.SOUTH);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Component buildLabelTypesPanel(String title, String[] filterNames) {
|
||||
JPanel panel = new JPanel(new VerticalLayout(0));
|
||||
panel.setBorder(BorderFactory.createTitledBorder(title));
|
||||
for (String filterName : filterNames) {
|
||||
JCheckBox cb = new GCheckBox(filterName);
|
||||
cb.setName(filterName);
|
||||
checkBoxMap.put(filterName, cb);
|
||||
cb.addItemListener(checkboxListener);
|
||||
cb.setToolTipText(HTMLUtilities.toHTML(filter.getFilterDescription(filterName)));
|
||||
panel.add(cb);
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void setTypeFiltersActive(boolean active) {
|
||||
String[] typeNames = filter.getLabelTypeFilterNames();
|
||||
for (String typeName : typeNames) {
|
||||
JCheckBox cb = checkBoxMap.get(typeName);
|
||||
cb.setSelected(active);
|
||||
}
|
||||
typeNames = filter.getNonLabelTypeFilterNames();
|
||||
for (String typeName : typeNames) {
|
||||
JCheckBox cb = checkBoxMap.get(typeName);
|
||||
cb.setSelected(active);
|
||||
}
|
||||
}
|
||||
|
||||
private Component buildSelectButtonPanel() {
|
||||
JPanel panel = new JPanel(new MiddleLayout());
|
||||
JPanel innerPanel = new JPanel(new GridLayout(0, 2, 30, 30));
|
||||
panel.add(innerPanel);
|
||||
|
||||
JButton b1 = new JButton("Select All");
|
||||
JButton b2 = new JButton("Clear All");
|
||||
b1.addActionListener(e -> setTypeFiltersActive(true));
|
||||
b2.addActionListener(e -> setTypeFiltersActive(false));
|
||||
innerPanel.add(b1);
|
||||
innerPanel.add(b2);
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
|
||||
return panel;
|
||||
|
||||
}
|
||||
|
||||
private Component buildResetPanel() {
|
||||
JPanel panel = new JPanel(new MiddleLayout());
|
||||
JPanel panel2 = new JPanel(new GridLayout(1, 0, 20, 0));
|
||||
JButton button1 = new JButton("Reset Filters");
|
||||
button1.addActionListener(e -> {
|
||||
setStatusText("");
|
||||
filter.setFilterDefaults();
|
||||
initCheckBoxes();
|
||||
setChanged(true);
|
||||
});
|
||||
panel2.add(button1);
|
||||
panel.add(panel2);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void clearAdvancedFilters() {
|
||||
String[] filterNames = filter.getAdvancedFilterNames();
|
||||
for (String filterName : filterNames) {
|
||||
if (filter.isActive(filterName)) {
|
||||
JCheckBox cb = checkBoxMap.get(filterName);
|
||||
cb.setSelected(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void adjustFilter(ComponentProvider provider, SymbolTableModel model) {
|
||||
this.keyModel = model;
|
||||
filter = new NewSymbolFilter(model.getFilter());
|
||||
initCheckBoxes();
|
||||
tool.showDialog(this, provider);
|
||||
model = null;
|
||||
}
|
||||
|
||||
private void update() {
|
||||
updateStatus();
|
||||
updateAdvancedFilterEnablement();
|
||||
updateOkAndApply();
|
||||
}
|
||||
|
||||
private void updateStatus() {
|
||||
if (filter.getActiveSourceFilterCount() == 0) {
|
||||
setStatusText("You must have at least one source category selected!");
|
||||
}
|
||||
else if (filter.getActiveTypeFilterCount() == 0) {
|
||||
setStatusText("You must have at least one symbol type selected!");
|
||||
}
|
||||
else {
|
||||
setStatusText("");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAdvancedFilterEnablement() {
|
||||
String[] filterNames = filter.getAdvancedFilterNames();
|
||||
for (String filterName : filterNames) {
|
||||
JCheckBox cb = checkBoxMap.get(filterName);
|
||||
cb.setEnabled(filter.isEnabled(filterName));
|
||||
}
|
||||
}
|
||||
|
||||
private void updateOkAndApply() {
|
||||
boolean b = isChanged && filter.getActiveTypeFilterCount() > 0 &&
|
||||
filter.getActiveSourceFilterCount() > 0;
|
||||
setOkEnabled(b);
|
||||
setApplyEnabled(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
applyCallback();
|
||||
close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyCallback() {
|
||||
if (keyModel == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isChanged) {
|
||||
return;
|
||||
}
|
||||
// Apply the same filter to all of the symbol table models
|
||||
Iterator<SymbolPanel> iter = keyModel.getPanels();
|
||||
CodecutUtils.setFilter(filter);
|
||||
while (iter.hasNext()) {
|
||||
SymbolTableModel stm = iter.next().getModel();
|
||||
stm.setFilter(CodecutUtils.getFilter());
|
||||
}
|
||||
setChanged(false);
|
||||
|
||||
tool.setConfigChanged(true);
|
||||
}
|
||||
|
||||
private void setChanged(boolean b) {
|
||||
isChanged = b;
|
||||
updateOkAndApply();
|
||||
}
|
||||
|
||||
NewSymbolFilter getFilter() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
class FilterCheckboxItemListener implements ItemListener {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
if (ignoreCallbacks) {
|
||||
return;
|
||||
}
|
||||
JCheckBox cb = (JCheckBox) e.getItem();
|
||||
String name = cb.getText();
|
||||
setChanged(true);
|
||||
filter.setFilter(name, cb.isSelected());
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.util.stream.Collectors;
|
||||
import docking.widgets.dialogs.MultiLineMessageDialog;
|
||||
import ghidra.framework.Application;
|
||||
|
||||
public class ModNamingPython {
|
||||
public Runtime runtime;
|
||||
public String pythonExec;
|
||||
|
||||
public Process process;
|
||||
public OutputStream stdin;
|
||||
public InputStream stdout;
|
||||
public InputStream stderr;
|
||||
|
||||
public ModNamingPython(String pythonExec) {
|
||||
this.pythonExec = pythonExec;
|
||||
this.runtime = Runtime.getRuntime();
|
||||
}
|
||||
|
||||
public int startProcess() throws IOException {
|
||||
String pythonFile = Application.getModuleDataFile("modnaming.py").toString();
|
||||
|
||||
String[] exec = {pythonExec, pythonFile};
|
||||
|
||||
try {
|
||||
process = runtime.exec(exec);
|
||||
}
|
||||
catch (IOException e) {
|
||||
// show message about invalid python executable path
|
||||
MultiLineMessageDialog.showMessageDialog(null, "Invalid Python Path",
|
||||
"Python Error! Please check path under " +
|
||||
"\n Edit -> Tool Options -> Python Executable.",
|
||||
e.getMessage(), MultiLineMessageDialog.WARNING_MESSAGE);
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
// Yes this is confusing. stdin is a Java OutputStream, stdin is an InputStream
|
||||
stdin = process.getOutputStream();
|
||||
stdout = process.getInputStream();
|
||||
stderr = process.getErrorStream();
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void waitFor() throws InterruptedException {
|
||||
process.waitFor();
|
||||
}
|
||||
|
||||
public void writeProcess(String data) throws IOException {
|
||||
writeProcess(data.getBytes());
|
||||
}
|
||||
|
||||
public void writeProcess(byte[] data) throws IOException {
|
||||
stdin.write(data);
|
||||
stdin.flush();
|
||||
}
|
||||
|
||||
public String readProcessOutput() {
|
||||
return readProcess(stdout);
|
||||
}
|
||||
|
||||
public String readProcessError() {
|
||||
return readProcess(stderr);
|
||||
}
|
||||
|
||||
public String readProcess(InputStream stream) {
|
||||
String result = "";
|
||||
|
||||
try {
|
||||
if (stream != null && stream.available() > 0) {
|
||||
result = new BufferedReader(new InputStreamReader(stream))
|
||||
.lines().collect(Collectors.joining("\n"));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.SymbolIterator;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
import ghidra.util.table.GhidraTable;
|
||||
|
||||
class NamespacePanel extends JPanel {
|
||||
|
||||
private Program program;
|
||||
private PluginTool tool;
|
||||
private SymbolProvider symProvider;
|
||||
private SymbolRenderer renderer;
|
||||
private List<SymbolPanel> symPanels;
|
||||
private JPanel boxPanel;
|
||||
private JScrollPane listScroller;
|
||||
|
||||
NamespacePanel(Program program, PluginTool tool, SymbolProvider provider, SymbolRenderer renderer) {
|
||||
BoxLayout layout = new BoxLayout(this, BoxLayout.Y_AXIS);
|
||||
this.setLayout(layout);
|
||||
|
||||
this.program = program;
|
||||
this.tool = tool;
|
||||
this.symProvider = provider;
|
||||
this.symPanels = new ArrayList<>();
|
||||
this.boxPanel = null;
|
||||
this.listScroller = null;
|
||||
|
||||
}
|
||||
|
||||
public void doLoad() {
|
||||
if (program == null) {
|
||||
return;
|
||||
}
|
||||
this.removeAll();
|
||||
this.symPanels = new ArrayList<>();
|
||||
List<Namespace> nsList = CodecutUtils.getAllNamespaces(program);
|
||||
|
||||
if (nsList.size() > 2) {
|
||||
nsList.sort(new Comparator<Namespace>() {
|
||||
@Override
|
||||
public int compare(Namespace ns1, Namespace ns2) {
|
||||
Address ns1MaxAddress = ns1.getBody().getMaxAddress();
|
||||
Address ns2MaxAddress = ns2.getBody().getMaxAddress();
|
||||
if (ns1MaxAddress != null && ns2MaxAddress != null) {
|
||||
if (ns1MaxAddress.getAddressableWordOffset() < ns2MaxAddress.getAddressableWordOffset()) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
GoToService goToService = tool.getService(GoToService.class);
|
||||
|
||||
boxPanel = new JPanel();
|
||||
boxPanel.setLayout(new BoxLayout(boxPanel, BoxLayout.Y_AXIS));
|
||||
Iterator<Namespace> it = nsList.iterator();
|
||||
List<String> nsNames = nsList.stream().map(ns ->ns.getName())
|
||||
.collect(Collectors.toList());
|
||||
String longest = nsNames.stream().max(Comparator.comparingInt(String::length)).get();
|
||||
JLabel longestLabel = new JLabel(longest);
|
||||
int width = longestLabel.getPreferredSize().width;
|
||||
while(it.hasNext()) {
|
||||
Namespace ns = it.next();
|
||||
SymbolIterator symIter = this.program.getSymbolTable().getSymbols(ns);
|
||||
SymbolTableModel model = new SymbolTableModel(this.program, this.symProvider, this.tool, symIter, ns);
|
||||
|
||||
JPanel panel = new RowPanel(new PairLayout(6, 10), ns);
|
||||
SymbolPanel symPanel = new SymbolPanel(this.symProvider, model, this.renderer, this.tool, goToService, ns);
|
||||
JLabel label = new JLabel(ns.getName());
|
||||
int height = symPanel.getPreferredSize().height;
|
||||
int symWidth = symPanel.getPreferredSize().width;
|
||||
label.setPreferredSize(new Dimension(width, height));
|
||||
symPanel.setPreferredSize(new Dimension(symWidth, height));
|
||||
|
||||
panel.add(label);
|
||||
panel.add(symPanel, BorderLayout.EAST);
|
||||
|
||||
panel.setBorder(BorderFactory.createLoweredBevelBorder());
|
||||
|
||||
int totalWidth = panel.getPreferredSize().width;
|
||||
panel.setPreferredSize(new Dimension(totalWidth, height));
|
||||
symPanels.add(symPanel);
|
||||
|
||||
boxPanel.add(panel);
|
||||
}
|
||||
|
||||
listScroller = new JScrollPane(boxPanel);
|
||||
listScroller.setPreferredSize(new Dimension(576, 400));
|
||||
this.add(listScroller);
|
||||
this.setPreferredSize(new Dimension(576, 400));
|
||||
|
||||
revalidate();
|
||||
|
||||
}
|
||||
|
||||
List<Symbol> getSelectedSymbols() {
|
||||
Iterator<SymbolPanel> it = symPanels.iterator();
|
||||
List<Symbol> selectedSymbols = new ArrayList<>();
|
||||
while (it.hasNext()) {
|
||||
SymbolPanel panel = it.next();
|
||||
GhidraTable table = panel.getTable();
|
||||
int[] rows = table.getSelectedRows();
|
||||
SymbolTableModel model = (SymbolTableModel)table.getModel();
|
||||
List<Symbol> symbols = model.getRowObjects(rows);
|
||||
selectedSymbols.addAll(symbols);
|
||||
}
|
||||
return selectedSymbols;
|
||||
}
|
||||
|
||||
int getSelectedRowCount() {
|
||||
Iterator<SymbolPanel> it = symPanels.iterator();
|
||||
int count = 0;
|
||||
while (it.hasNext()) {
|
||||
SymbolPanel panel = it.next();
|
||||
GhidraTable table = panel.getTable();
|
||||
count += table.getSelectedRowCount();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void setFilter() {
|
||||
Iterator<SymbolPanel> it = symPanels.iterator();
|
||||
while (it.hasNext()) {
|
||||
SymbolPanel panel = it.next();
|
||||
panel.setFilter();
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
Iterator<SymbolPanel> it = symPanels.iterator();
|
||||
while (it.hasNext()) {
|
||||
SymbolPanel panel = it.next();
|
||||
panel.dispose();
|
||||
}
|
||||
symProvider = null;
|
||||
symPanels = null;
|
||||
boxPanel = null;
|
||||
listScroller = null;
|
||||
this.removeAll();
|
||||
}
|
||||
|
||||
GhidraTable getTable() {
|
||||
if (symPanels.size() > 0) {
|
||||
return symPanels.get(0).getTable();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
List<GhidraTable> getAllTables() {
|
||||
List<GhidraTable> tableList = new ArrayList<>();
|
||||
Iterator<SymbolPanel> it = symPanels.iterator();
|
||||
while (it.hasNext()) {
|
||||
SymbolPanel panel = it.next();
|
||||
tableList.add(panel.getTable());
|
||||
}
|
||||
return tableList;
|
||||
}
|
||||
|
||||
int getActualSymbolCount() {
|
||||
int count = 0;
|
||||
Iterator<SymbolPanel> it = symPanels.iterator();
|
||||
while (it.hasNext()) {
|
||||
SymbolPanel panel = it.next();
|
||||
count += panel.getActualSymbolCount();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void readConfigState(SaveState saveState) {
|
||||
Iterator<SymbolPanel> it = symPanels.iterator();
|
||||
while (it.hasNext()) {
|
||||
SymbolPanel panel = it.next();
|
||||
panel.readConfigState(saveState);
|
||||
}
|
||||
}
|
||||
|
||||
void writeConfigState(SaveState saveState) {
|
||||
Iterator<SymbolPanel> it = symPanels.iterator();
|
||||
while (it.hasNext()) {
|
||||
SymbolPanel panel = it.next();
|
||||
panel.writeConfigState(saveState);
|
||||
}
|
||||
}
|
||||
|
||||
NewSymbolFilter getFilter() {
|
||||
if (symPanels.size() > 0) {
|
||||
return symPanels.get(0).getFilter();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void setProgram(Program p) {
|
||||
program = p;
|
||||
}
|
||||
|
||||
void clearOtherSelections(Namespace ns) {
|
||||
Iterator<SymbolPanel> it = symPanels.iterator();
|
||||
while (it.hasNext()) {
|
||||
SymbolPanel panel = it.next();
|
||||
Namespace iterNs = panel.getNamespace();
|
||||
if (!iterNs.equals(ns)) {
|
||||
panel.getTable().clearSelection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Iterator<SymbolPanel> getPanels() {
|
||||
return this.symPanels.iterator();
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,93 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.event.TableModelListener;
|
||||
import javax.swing.table.TableColumn;
|
||||
import javax.swing.table.TableModel;
|
||||
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.util.table.GhidraTable;
|
||||
import ghidra.util.table.GhidraThreadedTablePanel;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
class ReferencePanel extends JPanel {
|
||||
|
||||
private ReferenceProvider referenceProvider;
|
||||
private GhidraTable refTable;
|
||||
private TableModelListener listener;
|
||||
private GhidraThreadedTablePanel<Reference> threadedTablePanel;
|
||||
|
||||
ReferencePanel(ReferenceProvider provider, SymbolReferenceModel model, SymbolRenderer renderer,
|
||||
GoToService gotoService) {
|
||||
|
||||
super(new BorderLayout());
|
||||
|
||||
referenceProvider = provider;
|
||||
|
||||
threadedTablePanel = new GhidraThreadedTablePanel<>(model);
|
||||
|
||||
refTable = threadedTablePanel.getTable();
|
||||
refTable.setAutoLookupColumn(SymbolReferenceModel.LABEL_COL);
|
||||
refTable.setName("ReferenceTable");//used by JUnit...
|
||||
refTable.setPreferredScrollableViewportSize(new Dimension(250, 200));
|
||||
refTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
refTable.installNavigation(gotoService, gotoService.getDefaultNavigatable());
|
||||
|
||||
this.listener = e -> referenceProvider.updateTitle();
|
||||
refTable.getModel().addTableModelListener(listener);
|
||||
|
||||
for (int i = 0; i < refTable.getColumnCount(); i++) {
|
||||
TableColumn column = refTable.getColumnModel().getColumn(i);
|
||||
if (column.getModelIndex() == SymbolReferenceModel.LABEL_COL) {
|
||||
column.setCellRenderer(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
add(threadedTablePanel, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
GhidraTable getTable() {
|
||||
return refTable;
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
TableModel model = refTable.getModel();
|
||||
model.removeTableModelListener(listener);
|
||||
threadedTablePanel.dispose();
|
||||
refTable.dispose();
|
||||
referenceProvider = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.WindowPosition;
|
||||
import ghidra.app.context.ProgramActionContext;
|
||||
import ghidra.app.util.SymbolInspector;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.table.GhidraTable;
|
||||
import resources.ResourceManager;
|
||||
|
||||
class ReferenceProvider extends ComponentProviderAdapter {
|
||||
|
||||
private static final ImageIcon ICON = ResourceManager.loadImage("images/table_go.png");
|
||||
|
||||
private CodeCutGUIPlugin plugin;
|
||||
private SymbolReferenceModel referenceKeyModel;
|
||||
private ReferencePanel referencePanel;
|
||||
private SymbolRenderer renderer;
|
||||
|
||||
ReferenceProvider(CodeCutGUIPlugin plugin) {
|
||||
super(plugin.getTool(), "Symbol References", plugin.getName(), ProgramActionContext.class);
|
||||
this.plugin = plugin;
|
||||
|
||||
setIcon(ICON);
|
||||
addToToolbar();
|
||||
setHelpLocation(new HelpLocation(plugin.getName(), "Symbol_References"));
|
||||
setWindowGroup("codecutTable");
|
||||
setIntraGroupPosition(WindowPosition.RIGHT);
|
||||
|
||||
renderer = new SymbolRenderer();
|
||||
|
||||
referenceKeyModel =
|
||||
new SymbolReferenceModel(plugin.getBlockModelService(), plugin.getTool());
|
||||
referencePanel =
|
||||
new ReferencePanel(this, referenceKeyModel, renderer, plugin.getGoToService());
|
||||
|
||||
addToTool();
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
referencePanel.dispose();
|
||||
plugin = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
Program program = plugin.getProgram();
|
||||
if (program == null) {
|
||||
return null;
|
||||
}
|
||||
return new ProgramActionContext(this, program);
|
||||
}
|
||||
|
||||
void setCurrentSymbol(Symbol symbol) {
|
||||
referenceKeyModel.setCurrentSymbol(symbol);
|
||||
}
|
||||
|
||||
void symbolChanged(Symbol symbol) {
|
||||
if (isVisible()) {
|
||||
referenceKeyModel.symbolChanged(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
void symbolRemoved(Symbol symbol) {
|
||||
if (isVisible()) {
|
||||
referenceKeyModel.symbolRemoved(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
void symbolAdded(Symbol sym) {
|
||||
if (isVisible()) {
|
||||
referenceKeyModel.symbolAdded(sym);
|
||||
}
|
||||
}
|
||||
|
||||
void setProgram(Program program, SymbolInspector inspector) {
|
||||
renderer.setSymbolInspector(inspector);
|
||||
if (isVisible()) {
|
||||
referenceKeyModel.setProgram(program);
|
||||
}
|
||||
}
|
||||
|
||||
void reload() {
|
||||
if (isVisible()) {
|
||||
referenceKeyModel.reload();
|
||||
}
|
||||
}
|
||||
|
||||
void showReferencesTo() {
|
||||
referenceKeyModel.showReferencesTo();
|
||||
}
|
||||
|
||||
void showInstructionsFrom() {
|
||||
referenceKeyModel.showInstructionReferencesFrom();
|
||||
}
|
||||
|
||||
void showDataFrom() {
|
||||
referenceKeyModel.showDataReferencesFrom();
|
||||
}
|
||||
|
||||
public GhidraTable getTable() {
|
||||
return referencePanel.getTable();
|
||||
}
|
||||
|
||||
private String generateSubTitle() {
|
||||
return "(" + referenceKeyModel.getDescription() + ")";
|
||||
}
|
||||
|
||||
void open() {
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentHidden() {
|
||||
referenceKeyModel.setProgram(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentShown() {
|
||||
referenceKeyModel.setProgram(plugin.getProgram());
|
||||
|
||||
// Note: this is a bit of a hack--if we do this during a tool's restore process, then
|
||||
// there is a chance that the Symbol Provider has not yet been re-loaded. This
|
||||
// is only needed due to the odd dependency of this provider upon the Symbol Provider.
|
||||
Swing.runLater(plugin::openSymbolProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return referencePanel;
|
||||
}
|
||||
|
||||
public void updateTitle() {
|
||||
setSubTitle(generateSubTitle());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.AbstractButton;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextField;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.WindowPosition;
|
||||
import ghidra.app.context.ProgramActionContext;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.SymbolIterator;
|
||||
import ghidra.program.model.symbol.SymbolTable;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
import resources.ResourceManager;
|
||||
|
||||
class RenameProvider extends ComponentProviderAdapter implements ActionListener {
|
||||
|
||||
private static final ImageIcon ICON = ResourceManager.loadImage("images/textfield.png");
|
||||
|
||||
private CodeCutGUIPlugin plugin;
|
||||
private JPanel boxPanel;
|
||||
private JTextField textField;
|
||||
private JButton button;
|
||||
|
||||
private Namespace namespace;
|
||||
|
||||
RenameProvider(CodeCutGUIPlugin plugin) {
|
||||
super(plugin.getTool(), "Rename Namespace", plugin.getName(), ProgramActionContext.class);
|
||||
this.plugin = plugin;
|
||||
|
||||
setIcon(ICON);
|
||||
addToToolbar();
|
||||
setHelpLocation(new HelpLocation(plugin.getName(), "CodeCut_Table"));
|
||||
setWindowGroup("codecutTable");
|
||||
setIntraGroupPosition(WindowPosition.BOTTOM);
|
||||
|
||||
boxPanel = new JPanel();
|
||||
boxPanel.setLayout(new BoxLayout(boxPanel, BoxLayout.Y_AXIS));
|
||||
|
||||
JPanel entryPanel = new JPanel(new PairLayout(6,10));
|
||||
textField = new JTextField(30);
|
||||
if (namespace != null) {
|
||||
textField.setText(namespace.getName());
|
||||
}
|
||||
button = new JButton("Rename");
|
||||
button.setVerticalTextPosition(AbstractButton.CENTER);
|
||||
button.setHorizontalTextPosition(AbstractButton.CENTER);
|
||||
button.setMnemonic(KeyEvent.VK_ENTER);
|
||||
button.setActionCommand("submit");
|
||||
button.addActionListener(this);
|
||||
entryPanel.add(textField);
|
||||
entryPanel.add(button);
|
||||
entryPanel.setSize(entryPanel.getPreferredSize().width, textField.getPreferredSize().height);
|
||||
Dimension dim = entryPanel.getPreferredSize();
|
||||
boxPanel.add(entryPanel);
|
||||
boxPanel.setSize(dim);
|
||||
|
||||
//addToTool();
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (this.namespace != null) {
|
||||
if ("submit".equals(e.getActionCommand())) {
|
||||
String newNamespace = textField.getText();
|
||||
Program program = plugin.getProgram();
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
if (CodecutUtils.getMatchingNamespaces(newNamespace, Arrays.asList(program.getGlobalNamespace()), program).isEmpty()) {
|
||||
Namespace nS=null;
|
||||
int transactionID = program.startTransaction("ns");
|
||||
try {
|
||||
nS = symbolTable.createNameSpace(program.getGlobalNamespace(), newNamespace, SourceType.USER_DEFINED);
|
||||
}
|
||||
catch (DuplicateNameException ex) {
|
||||
//NS was already created,
|
||||
List<Namespace> nL = CodecutUtils.getNamespacesByName(program, program.getGlobalNamespace(), newNamespace);
|
||||
if (nL == null) {
|
||||
return;
|
||||
}
|
||||
nS = nL.get(0);
|
||||
}
|
||||
catch (InvalidInputException ex) {
|
||||
program.endTransaction(transactionID, false);
|
||||
return;
|
||||
}
|
||||
program.endTransaction(transactionID, true);
|
||||
|
||||
|
||||
//rename all symbols in same module to new NS
|
||||
try {
|
||||
CodecutUtils.renameNamespace(program, namespace, nS);
|
||||
Msg.info(this, "Namespace " + namespace.getName() + " renamed to " + nS.getName());
|
||||
} catch (Exception ex) {
|
||||
Msg.info(this, "Exception when renaming namespace " + namespace.getName() + ": " + ex.getMessage());
|
||||
|
||||
}
|
||||
this.closeComponent();
|
||||
|
||||
}
|
||||
else {
|
||||
Namespace nS = symbolTable.getNamespace(newNamespace, program.getGlobalNamespace());
|
||||
if (symbolTable.getSymbols(nS).hasNext()) {
|
||||
throw new IllegalArgumentException("Namespace already exists");
|
||||
}
|
||||
else {
|
||||
CodecutUtils.setUpdating(true);
|
||||
SymbolIterator iter = symbolTable.getSymbols(namespace);
|
||||
Symbol currSym = null;
|
||||
int transactionID = program.startTransaction("nsRename");
|
||||
try {
|
||||
while (iter.hasNext()) {
|
||||
currSym = iter.next();
|
||||
currSym.setNamespace(nS);
|
||||
}
|
||||
namespace.getSymbol().delete();
|
||||
}
|
||||
catch (Exception exception) {
|
||||
Msg.info(new Object(), "Could not set namespace for " + currSym.getName());
|
||||
Msg.info(new Object(), exception.getMessage());
|
||||
program.endTransaction(transactionID, false);
|
||||
}
|
||||
program.endTransaction(transactionID, true);
|
||||
CodecutUtils.setUpdating(false);
|
||||
this.closeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
void open() {
|
||||
if (!isVisible()) {
|
||||
setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
plugin = null;
|
||||
}
|
||||
|
||||
public void setCurrentNamespace(Namespace ns) {
|
||||
this.namespace = ns;
|
||||
this.textField.setText(ns.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
Program program = plugin.getProgram();
|
||||
if (program == null) {
|
||||
return null;
|
||||
}
|
||||
return new ProgramActionContext(this, program);
|
||||
}
|
||||
|
||||
public void updateTitle() {
|
||||
setSubTitle("Rename Namespace");
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return boxPanel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
import java.awt.LayoutManager;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
|
||||
public class RowPanel extends JPanel {
|
||||
|
||||
private Namespace namespace;
|
||||
|
||||
RowPanel(LayoutManager layout, Namespace ns) {
|
||||
super(layout);
|
||||
this.namespace = ns;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
|
||||
public class SharedListSelectionListener implements ListSelectionListener {
|
||||
|
||||
private Namespace namespace;
|
||||
private SymbolProvider provider;
|
||||
|
||||
SharedListSelectionListener(Namespace namespace, SymbolProvider provider) {
|
||||
this.namespace = namespace;
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
//ListSelectionModel lsm = (ListSelectionModel)e.getSource();
|
||||
provider.clearOtherSelections(this.namespace);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
|
||||
import java.awt.Component;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
|
||||
class SymbolEditor extends DefaultCellEditor {
|
||||
|
||||
private JTextField symbolField = null;
|
||||
|
||||
SymbolEditor() {
|
||||
super(new JTextField());
|
||||
symbolField = (JTextField) super.getComponent();
|
||||
symbolField.setBorder(BorderFactory.createEmptyBorder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCellEditorValue() {
|
||||
return symbolField.getText().trim();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected,
|
||||
int row, int column) {
|
||||
|
||||
Symbol symbol = (Symbol) value;
|
||||
if (symbol != null) {
|
||||
symbolField.setText(symbol.getName());
|
||||
}
|
||||
else {
|
||||
symbolField.setText("");
|
||||
}
|
||||
return symbolField;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
|
||||
public interface SymbolFilter {
|
||||
|
||||
public boolean accepts(Symbol s, Program p);
|
||||
|
||||
public boolean acceptsOnlyCodeSymbols();
|
||||
|
||||
public boolean acceptsDefaultLabelSymbols();
|
||||
|
||||
public boolean acceptsAll();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.swing.event.TableModelListener;
|
||||
import javax.swing.table.TableColumn;
|
||||
|
||||
import org.jdom.Element;
|
||||
|
||||
import docking.widgets.checkbox.GCheckBox;
|
||||
import docking.widgets.table.DefaultRowFilterTransformer;
|
||||
import docking.widgets.table.RowFilterTransformer;
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.util.table.*;
|
||||
|
||||
class SymbolPanel extends JPanel {
|
||||
|
||||
private static final boolean FILTER_NAME_ONLY_DEFAULT = true;
|
||||
|
||||
private static final String FILTER_SETTINGS_ELEMENT_NAME = "FILTER_SETTINGS";
|
||||
|
||||
private SymbolProvider symProvider;
|
||||
private SymbolTableModel tableModel;
|
||||
private GhidraTable symTable;
|
||||
private TableModelListener listener;
|
||||
private FilterDialog filterDialog;
|
||||
private GhidraThreadedTablePanel<Symbol> threadedTablePanel;
|
||||
private GhidraTableFilterPanel<Symbol> tableFilterPanel;
|
||||
private Namespace namespace;
|
||||
private ListSelectionListener selectionListener;
|
||||
|
||||
SymbolPanel(SymbolProvider provider, SymbolTableModel model, SymbolRenderer renderer,
|
||||
final PluginTool tool, GoToService gotoService, Namespace namespace) {
|
||||
|
||||
super(new BorderLayout());
|
||||
|
||||
this.symProvider = provider;
|
||||
this.tableModel = model;
|
||||
this.namespace = namespace;
|
||||
this.selectionListener = new SharedListSelectionListener(namespace, provider);
|
||||
|
||||
threadedTablePanel = new GhidraThreadedTablePanel<>(model);
|
||||
|
||||
this.listener = e -> symProvider.updateTitle();
|
||||
|
||||
symTable = threadedTablePanel.getTable();
|
||||
symTable.setAutoLookupColumn(SymbolTableModel.LABEL_COL);
|
||||
symTable.setName("SymbolTable");//used by JUnit...
|
||||
symTable.setRowSelectionAllowed(true);
|
||||
symTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
symTable.getSelectionManager().addListSelectionListener(selectionListener);
|
||||
|
||||
symTable.getModel().addTableModelListener(listener);
|
||||
symTable.getSelectionModel().addListSelectionListener(e -> {
|
||||
if (!e.getValueIsAdjusting()) {
|
||||
handleTableSelection();
|
||||
tool.contextChanged(symProvider);
|
||||
}
|
||||
});
|
||||
|
||||
GoToService goToService = tool.getService(GoToService.class);
|
||||
symTable.installNavigation(goToService, goToService.getDefaultNavigatable());
|
||||
|
||||
symTable.setDragEnabled(true);
|
||||
symTable.setDropMode(DropMode.ON);
|
||||
symTable.setTransferHandler(new CodeCutTransferHandler());
|
||||
|
||||
for (int i = 0; i < symTable.getColumnCount(); i++) {
|
||||
TableColumn column = symTable.getColumnModel().getColumn(i);
|
||||
column.setCellRenderer(renderer);
|
||||
if (column.getModelIndex() == SymbolTableModel.LABEL_COL) {
|
||||
column.setCellEditor(new SymbolEditor());
|
||||
}
|
||||
}
|
||||
|
||||
add(threadedTablePanel, BorderLayout.CENTER);
|
||||
createFilterFieldPanel();
|
||||
|
||||
filterDialog = symProvider.filterDialog;
|
||||
}
|
||||
|
||||
private JPanel createFilterFieldPanel() {
|
||||
tableFilterPanel = new GhidraTableFilterPanel<>(symTable, tableModel);
|
||||
tableFilterPanel.setToolTipText("Filters the contents of the table on symbol " +
|
||||
"names that start with the given pattern");
|
||||
|
||||
tableFilterPanel.add(Box.createHorizontalStrut(5));
|
||||
|
||||
final JCheckBox nameColumnOnlyCheckbox = new GCheckBox("Name Only");
|
||||
nameColumnOnlyCheckbox.setName("NameOnly"); // used by JUnit
|
||||
nameColumnOnlyCheckbox.setToolTipText(
|
||||
"<html><b>Selected</b> causes filter to only consider the symbol's name.");
|
||||
nameColumnOnlyCheckbox.setFocusable(false);
|
||||
nameColumnOnlyCheckbox.setSelected(FILTER_NAME_ONLY_DEFAULT);
|
||||
tableFilterPanel.setFilterRowTransformer(
|
||||
updateRowDataTransformer(FILTER_NAME_ONLY_DEFAULT));
|
||||
nameColumnOnlyCheckbox.addItemListener(e -> {
|
||||
boolean nameOnly = nameColumnOnlyCheckbox.isSelected();
|
||||
tableFilterPanel.setFilterRowTransformer(updateRowDataTransformer(nameOnly));
|
||||
});
|
||||
|
||||
tableFilterPanel.add(nameColumnOnlyCheckbox);
|
||||
|
||||
return tableFilterPanel;
|
||||
}
|
||||
|
||||
protected RowFilterTransformer<Symbol> updateRowDataTransformer(boolean nameOnly) {
|
||||
if (nameOnly) {
|
||||
return new NameOnlyRowTransformer();
|
||||
}
|
||||
|
||||
return new DefaultRowFilterTransformer<>(tableModel, symTable.getColumnModel());
|
||||
}
|
||||
|
||||
ProgramSelection getProgramSelection() {
|
||||
return symTable.getProgramSelection();
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
symTable.getModel().removeTableModelListener(listener);
|
||||
symTable.dispose();
|
||||
threadedTablePanel.dispose();
|
||||
tableFilterPanel.dispose();
|
||||
symProvider = null;
|
||||
filterDialog.close();
|
||||
filterDialog = null;
|
||||
}
|
||||
|
||||
void setFilter() {
|
||||
if (filterDialog == null) {
|
||||
return;
|
||||
}
|
||||
if (symTable.isEditing()) {
|
||||
symTable.editingCanceled(null);
|
||||
}
|
||||
symProvider.setCurrentSymbol(null);
|
||||
symTable.clearSelection();
|
||||
filterDialog.adjustFilter(symProvider, tableModel);
|
||||
}
|
||||
|
||||
NewSymbolFilter getFilter() {
|
||||
return filterDialog.getFilter();
|
||||
}
|
||||
|
||||
FilterDialog getFilterDialog() {
|
||||
return filterDialog;
|
||||
}
|
||||
|
||||
void readConfigState(SaveState saveState) {
|
||||
Element filterElement = saveState.getXmlElement(FILTER_SETTINGS_ELEMENT_NAME);
|
||||
if (filterElement != null) {
|
||||
filterDialog.restoreFilter(filterElement);
|
||||
tableModel.setFilter(filterDialog.getFilter());
|
||||
}
|
||||
}
|
||||
|
||||
void writeConfigState(SaveState saveState) {
|
||||
Element filterElement = filterDialog.saveFilter();
|
||||
saveState.putXmlElement(FILTER_SETTINGS_ELEMENT_NAME, filterElement);
|
||||
}
|
||||
|
||||
private void handleTableSelection() {
|
||||
int selectedRowCount = symTable.getSelectedRowCount();
|
||||
|
||||
if (selectedRowCount == 1) {
|
||||
int selectedRow = symTable.getSelectedRow();
|
||||
Symbol symbol = symProvider.getSymbolForRow(selectedRow);
|
||||
symProvider.setCurrentSymbol(symbol);
|
||||
}
|
||||
else {
|
||||
symProvider.setCurrentSymbol(null);
|
||||
}
|
||||
}
|
||||
|
||||
int getActualSymbolCount() {
|
||||
return symTable.getRowCount();
|
||||
}
|
||||
|
||||
List<Symbol> getSelectedSymbols() {
|
||||
int[] rows = symTable.getSelectedRows();
|
||||
return tableModel.getRowObjects(rows);
|
||||
}
|
||||
|
||||
GhidraTable getTable() {
|
||||
return symTable;
|
||||
}
|
||||
|
||||
Namespace getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
private static class NameOnlyRowTransformer implements RowFilterTransformer<Symbol> {
|
||||
private List<String> list = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public List<String> transform(Symbol rowObject) {
|
||||
list.clear();
|
||||
if (rowObject != null) {
|
||||
// The toString() returns the name for the symbol, which may be cached. Calling
|
||||
// toString() will also avoid locking for cached values.
|
||||
list.add(rowObject.toString());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// not meant to put in hashing structures; the data for equals may change over time
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
SymbolTableModel getModel() {
|
||||
return this.tableModel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.SymbolType;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
class SymbolPlaceholder implements Symbol{
|
||||
|
||||
private long id;
|
||||
private Address addr;
|
||||
private Program prog;
|
||||
|
||||
SymbolPlaceholder(long id, Address addr, Program p){
|
||||
this.id = id;
|
||||
this.addr = addr;
|
||||
this.prog = p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof Symbol)) {
|
||||
return false;
|
||||
}
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// this class is only ever equal if the id matches
|
||||
Symbol s = (Symbol) obj;
|
||||
if (getID() == s.getID()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getAddress() {
|
||||
return addr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymbolType getSymbolType() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getProgramLocation() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExternal() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPrimary() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidParent(Namespace parent) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPath() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program getProgram() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName(boolean includeNamespace) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Namespace getParentNamespace() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Symbol getParentSymbol() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDescendant(Namespace namespace) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReferenceCount() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMultipleReferences() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasReferences() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Reference[] getReferences(TaskMonitor monitor) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Reference[] getReferences() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String newName, SourceType source) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNamespace(Namespace newNamespace) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNameAndNamespace(String newName, Namespace newNamespace, SourceType source) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPinned() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPinned(boolean pinned) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDynamic() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setPrimary() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExternalEntryPoint() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGlobal() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSource(SourceType source) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SourceType getSource() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeleted() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "[id=" + id + ", address=" + addr + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JComponent;
|
||||
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.DockingUtils;
|
||||
import docking.action.KeyBindingData;
|
||||
import ghidra.app.context.ProgramActionContext;
|
||||
import ghidra.app.context.ProgramSymbolActionContext;
|
||||
import ghidra.app.util.SymbolInspector;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.table.GhidraTable;
|
||||
import resources.ResourceManager;
|
||||
|
||||
class SymbolProvider extends ComponentProviderAdapter {
|
||||
|
||||
private static final ImageIcon ICON = ResourceManager.loadImage("images/table.png");
|
||||
|
||||
private CodeCutGUIPlugin plugin;
|
||||
private SymbolRenderer renderer;
|
||||
private SymbolTableModel symbolKeyModel;
|
||||
private NamespacePanel namespacePanel;
|
||||
public FilterDialog filterDialog;
|
||||
|
||||
SymbolProvider(CodeCutGUIPlugin plugin) {
|
||||
super(plugin.getTool(), "CodeCut Table", plugin.getName(), ProgramActionContext.class);
|
||||
this.plugin = plugin;
|
||||
|
||||
setIcon(ICON);
|
||||
addToToolbar();
|
||||
setKeyBinding(new KeyBindingData(KeyEvent.VK_M, DockingUtils.CONTROL_KEY_MODIFIER_MASK));
|
||||
|
||||
setHelpLocation(new HelpLocation(plugin.getName(), "CodeCut_Table"));
|
||||
setWindowGroup("codecutTable");
|
||||
renderer = new SymbolRenderer();
|
||||
filterDialog = new FilterDialog(plugin.getTool());
|
||||
|
||||
symbolKeyModel = new SymbolTableModel(plugin.getProgram(), this, plugin.getTool());
|
||||
namespacePanel = new NamespacePanel(plugin.getProgram(), plugin.getTool(), this, renderer);
|
||||
namespacePanel.doLoad();
|
||||
addToTool();
|
||||
}
|
||||
|
||||
|
||||
void updateTitle() {
|
||||
setSubTitle(generateSubTitle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
Program program = plugin.getProgram();
|
||||
if (program == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Symbol> symbols = namespacePanel.getSelectedSymbols();
|
||||
return new ProgramSymbolActionContext(this, program, symbols, getTable());
|
||||
}
|
||||
|
||||
void deleteSymbols() {
|
||||
List<Symbol> rowObjects = namespacePanel.getSelectedSymbols();
|
||||
symbolKeyModel.delete(rowObjects, plugin.getProgram());
|
||||
}
|
||||
|
||||
void setFilter() {
|
||||
namespacePanel.setFilter();
|
||||
}
|
||||
|
||||
Symbol getCurrentSymbol() {
|
||||
List<Symbol> rowObjects = namespacePanel.getSelectedSymbols();
|
||||
if (rowObjects != null && rowObjects.size() >= 1) {
|
||||
return rowObjects.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Symbol getSymbolForRow(int row) {
|
||||
return symbolKeyModel.getRowObject(row);
|
||||
}
|
||||
|
||||
void setCurrentSymbol(Symbol symbol) {
|
||||
plugin.getReferenceProvider().setCurrentSymbol(symbol);
|
||||
}
|
||||
|
||||
Symbol getSymbol(long id) {
|
||||
return symbolKeyModel.getSymbol(id);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
symbolKeyModel.dispose();
|
||||
namespacePanel.dispose();
|
||||
plugin = null;
|
||||
}
|
||||
|
||||
void reload() {
|
||||
if (isVisible()) {
|
||||
symbolKeyModel.reload();
|
||||
namespacePanel.setProgram(plugin.getProgram());
|
||||
namespacePanel.doLoad();
|
||||
}
|
||||
}
|
||||
|
||||
void symbolAdded(Symbol s) {
|
||||
if (isVisible()) {
|
||||
symbolKeyModel.symbolAdded(s);
|
||||
namespacePanel.doLoad();
|
||||
}
|
||||
}
|
||||
|
||||
void symbolRemoved(Symbol s) {
|
||||
if (isVisible()) {
|
||||
symbolKeyModel.symbolRemoved(s);
|
||||
namespacePanel.doLoad();
|
||||
}
|
||||
}
|
||||
|
||||
void symbolChanged(Symbol s) {
|
||||
if (isVisible()) {
|
||||
symbolKeyModel.symbolChanged(s);
|
||||
}
|
||||
if (CodecutUtils.transferring()) {
|
||||
namespacePanel.doLoad();
|
||||
CodecutUtils.setTransferring(false);
|
||||
}
|
||||
}
|
||||
|
||||
void setProgram(Program program, SymbolInspector inspector) {
|
||||
renderer.setSymbolInspector(inspector);
|
||||
namespacePanel.setProgram(program);
|
||||
if (isVisible()) {
|
||||
symbolKeyModel.reload(program);
|
||||
namespacePanel.doLoad();
|
||||
}
|
||||
}
|
||||
|
||||
GhidraTable getTable() {
|
||||
return namespacePanel.getTable();
|
||||
}
|
||||
|
||||
List<GhidraTable> getAllTables() {
|
||||
return namespacePanel.getAllTables();
|
||||
}
|
||||
|
||||
int getSelectedRowCount() {
|
||||
return namespacePanel.getSelectedRowCount();
|
||||
}
|
||||
|
||||
NewSymbolFilter getFilter() {
|
||||
return namespacePanel.getFilter();
|
||||
}
|
||||
|
||||
Iterator<SymbolPanel> getSymPanels(){
|
||||
return namespacePanel.getPanels();
|
||||
}
|
||||
|
||||
private String generateSubTitle() {
|
||||
SymbolFilter filter = symbolKeyModel.getFilter();
|
||||
|
||||
int rowCount = symbolKeyModel.getRowCount();
|
||||
int unfilteredCount = symbolKeyModel.getUnfilteredRowCount();
|
||||
|
||||
if (rowCount != unfilteredCount) {
|
||||
return " (Text filter matched " + rowCount + " of " + unfilteredCount + " symbols)";
|
||||
}
|
||||
if (filter.acceptsAll()) {
|
||||
return "(" + namespacePanel.getActualSymbolCount() + " Symbols)";
|
||||
}
|
||||
return "(Filter settings matched " + namespacePanel.getActualSymbolCount() + " Symbols)";
|
||||
|
||||
}
|
||||
|
||||
void open() {
|
||||
if (!isVisible()) {
|
||||
setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentHidden() {
|
||||
symbolKeyModel.reload(null);
|
||||
if (plugin != null) {
|
||||
plugin.closeReferenceProvider();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentShown() {
|
||||
symbolKeyModel.reload(plugin.getProgram());
|
||||
namespacePanel.setProgram(plugin.getProgram());
|
||||
namespacePanel.doLoad();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return namespacePanel;
|
||||
}
|
||||
|
||||
void readConfigState(SaveState saveState) {
|
||||
namespacePanel.readConfigState(saveState);
|
||||
}
|
||||
|
||||
void writeConfigState(SaveState saveState) {
|
||||
namespacePanel.writeConfigState(saveState);
|
||||
}
|
||||
|
||||
void clearOtherSelections(Namespace namespace) {
|
||||
namespacePanel.clearOtherSelections(namespace);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,439 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
|
||||
import docking.widgets.table.*;
|
||||
import ghidra.app.services.BlockModelService;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.block.CodeBlock;
|
||||
import ghidra.program.model.block.CodeBlockModel;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.datastruct.Accumulator;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.table.AddressBasedTableModel;
|
||||
import ghidra.util.table.column.AbstractGhidraColumnRenderer;
|
||||
import ghidra.util.table.column.GColumnRenderer;
|
||||
import ghidra.util.table.field.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class SymbolReferenceModel extends AddressBasedTableModel<Reference> {
|
||||
|
||||
static final int ADDRESS_COLUMN = 0;
|
||||
static final int LABEL_COL = 1;
|
||||
static final int SUBROUTINE_COL = 2;
|
||||
static final int ACCESS_COL = 3;
|
||||
static final int PREVIEW_COL = 4;
|
||||
|
||||
static final String ADDR_COL_NAME = "Address";
|
||||
static final String LABEL_COL_NAME = "Label";
|
||||
static final String SUBROUTINE_COL_NAME = "Subroutine";
|
||||
static final String ACCESS_COL_NAME = "Access";
|
||||
static final String PREVIEW_COL_NAME = "Preview";
|
||||
|
||||
static final int REFS_TO = 0;
|
||||
static final int INSTR_REFS_FROM = 1;
|
||||
static final int DATA_REFS_FROM = 2;
|
||||
|
||||
private Symbol currentSymbol;
|
||||
private ReferenceManager refManager;
|
||||
private int showRefMode = REFS_TO;
|
||||
private BlockModelService blockModelService;
|
||||
private boolean isDisposed;
|
||||
|
||||
SymbolReferenceModel(BlockModelService bms, PluginTool tool) {
|
||||
super("Symbol References", tool, null, null);
|
||||
|
||||
this.blockModelService = bms;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TableColumnDescriptor<Reference> createTableColumnDescriptor() {
|
||||
TableColumnDescriptor<Reference> descriptor = new TableColumnDescriptor<Reference>();
|
||||
|
||||
descriptor.addVisibleColumn(
|
||||
DiscoverableTableUtils.adaptColumForModel(this, new ReferenceFromAddressTableColumn()),
|
||||
1, true);
|
||||
descriptor.addVisibleColumn(
|
||||
DiscoverableTableUtils.adaptColumForModel(this, new ReferenceFromLabelTableColumn()));
|
||||
descriptor.addVisibleColumn(new SubroutineTableColumn());
|
||||
descriptor.addVisibleColumn(new AccessTableColumn());
|
||||
descriptor.addVisibleColumn(
|
||||
DiscoverableTableUtils.adaptColumForModel(this, new ReferenceFromPreviewTableColumn()));
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
String getDescription() {
|
||||
if (isDisposed) {
|
||||
return null;
|
||||
}
|
||||
String description = "";
|
||||
if (currentSymbol != null) {
|
||||
description += currentSymbol.getName() + ": ";
|
||||
}
|
||||
int count = filteredData.size();
|
||||
description += count + " Reference";
|
||||
if (count != 1) {
|
||||
description += "s";//make plural...
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
isDisposed = true;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgram(Program prog) {
|
||||
if (isDisposed) {
|
||||
return;
|
||||
}
|
||||
if (prog == null) {
|
||||
super.setProgram(null);
|
||||
refManager = null;
|
||||
}
|
||||
else {
|
||||
super.setProgram(prog);
|
||||
refManager = prog.getReferenceManager();
|
||||
}
|
||||
currentSymbol = null;
|
||||
reload();
|
||||
}
|
||||
|
||||
void setCurrentSymbol(Symbol symbol) {
|
||||
this.currentSymbol = symbol;
|
||||
reload();
|
||||
}
|
||||
|
||||
void symbolAdded(Symbol symbol) {
|
||||
checkRefs(symbol);
|
||||
}
|
||||
|
||||
void symbolRemoved(Symbol symbol) {
|
||||
if (currentSymbol != null && currentSymbol.getID() == symbol.getID()) {
|
||||
setCurrentSymbol(null);
|
||||
}
|
||||
}
|
||||
|
||||
void symbolChanged(Symbol symbol) {
|
||||
if (currentSymbol != null && currentSymbol.equals(symbol)) {
|
||||
setCurrentSymbol(symbol);
|
||||
return;
|
||||
}
|
||||
checkRefs(symbol);
|
||||
}
|
||||
|
||||
private void checkRefs(Symbol symbol) {
|
||||
Iterator<Reference> iter = filteredData.iterator();
|
||||
while (iter.hasNext()) {
|
||||
Reference ref = iter.next();
|
||||
if (ref.getFromAddress().equals(symbol.getAddress())) {
|
||||
reload();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void showReferencesTo() {
|
||||
showRefMode = REFS_TO;
|
||||
reload();
|
||||
}
|
||||
|
||||
void showInstructionReferencesFrom() {
|
||||
showRefMode = INSTR_REFS_FROM;
|
||||
reload();
|
||||
}
|
||||
|
||||
void showDataReferencesFrom() {
|
||||
showRefMode = DATA_REFS_FROM;
|
||||
reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoad(Accumulator<Reference> accumulator, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
if (currentSymbol == null || getProgram() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (showRefMode) {
|
||||
case REFS_TO:
|
||||
loadToReferences(accumulator, monitor);
|
||||
break;
|
||||
case INSTR_REFS_FROM:
|
||||
loadFromReferences(accumulator, true, monitor);
|
||||
break;
|
||||
case DATA_REFS_FROM:
|
||||
loadFromReferences(accumulator, false, monitor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void loadToReferences(Accumulator<Reference> accumulator, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
if (refManager == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference[] refs = currentSymbol.getReferences(monitor);
|
||||
for (Reference ref : refs) {
|
||||
monitor.checkCanceled();
|
||||
accumulator.add(ref);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadFromReferences(Accumulator<Reference> accumulator, boolean isInstr,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
CodeBlockModel blockModel = blockModelService.getActiveSubroutineModel(getProgram());
|
||||
CodeBlock block = blockModel.getCodeBlockAt(currentSymbol.getAddress(), TaskMonitor.DUMMY);
|
||||
if (block == null) {
|
||||
return;
|
||||
}
|
||||
InstructionIterator ii = getProgram().getListing().getInstructions(block, true);
|
||||
while (ii.hasNext()) {
|
||||
monitor.checkCanceled();
|
||||
Instruction instr = ii.next();
|
||||
Reference[] references = instr.getReferencesFrom();
|
||||
for (Reference reference : references) {
|
||||
RefType rt = reference.getReferenceType();
|
||||
if (isInstr) {
|
||||
if (rt.isFlow()) {
|
||||
accumulator.add(reference);
|
||||
}
|
||||
else if (instr.getFlowType().isComputed() && references.length == 1 &&
|
||||
rt == RefType.READ) {
|
||||
accumulator.add(reference);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (rt.isData()) {
|
||||
accumulator.add(reference);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String getReferenceType(RefType type) {
|
||||
if (type == RefType.THUNK) {
|
||||
return "Thunk";
|
||||
}
|
||||
|
||||
if (type.isRead() && type.isWrite()) {
|
||||
return "RW";
|
||||
}
|
||||
if (type.isRead()) {
|
||||
return "Read";
|
||||
}
|
||||
if (type.isWrite()) {
|
||||
return "Write";
|
||||
}
|
||||
if (type.isData()) {
|
||||
return "Data";
|
||||
}
|
||||
if (type.isCall()) {
|
||||
return "Call";
|
||||
}
|
||||
if (type.isJump()) {
|
||||
return (type.isConditional() ? "Branch" : "Jump");
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
private static Symbol getSymbol(Address fromAddress, String symbolName,
|
||||
BlockModelService blockModelService, Program program) {
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
SymbolIterator iterator = symbolTable.getSymbols(symbolName);
|
||||
while (iterator.hasNext()) {
|
||||
Symbol symbol = iterator.next();
|
||||
CodeBlockModel blockModel = blockModelService.getActiveSubroutineModel(program);
|
||||
CodeBlock[] blocks = getCodeBlocksContainingSymbol(symbol, blockModel);
|
||||
if (blocks == null || blocks.length == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (CodeBlock block : blocks) {
|
||||
if (block.contains(fromAddress)) {
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static CodeBlock[] getCodeBlocksContainingSymbol(Symbol symbol,
|
||||
CodeBlockModel blockModel) {
|
||||
return getCodeBlocksContainingAddress(symbol.getAddress(), blockModel);
|
||||
}
|
||||
|
||||
private static CodeBlock[] getCodeBlocksContainingAddress(Address address,
|
||||
CodeBlockModel blockModel) {
|
||||
try {
|
||||
return blockModel.getCodeBlocksContaining(address, TaskMonitor.DUMMY);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// can't happen--dummy monitor
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String getSubroutineName(Reference reference, BlockModelService service,
|
||||
Program program, CodeBlockModel model) {
|
||||
|
||||
Address address = reference.getFromAddress();
|
||||
CodeBlock[] blocks = getCodeBlocksContainingAddress(address, model);
|
||||
if (blocks != null && blocks.length > 0) {
|
||||
return blocks[0].getName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getAddress(int row) {
|
||||
return (Address) getValueAt(row, ADDRESS_COLUMN);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
private static class SubroutineTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<Reference, String>
|
||||
implements ProgramLocationTableColumn<Reference, String> {
|
||||
|
||||
private Program cachedProgram;
|
||||
private CodeBlockModel cachedModel;
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Subroutine";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(Reference rowObject, Settings settings, Program program,
|
||||
ServiceProvider serviceProvider) throws IllegalArgumentException {
|
||||
|
||||
BlockModelService service = serviceProvider.getService(BlockModelService.class);
|
||||
CodeBlockModel model = getCodeBlockModel(program, service);
|
||||
return getSubroutineName(rowObject, service, program, model);
|
||||
}
|
||||
|
||||
private CodeBlockModel getCodeBlockModel(Program program, BlockModelService service) {
|
||||
if (cachedModel == null || program != cachedProgram) {
|
||||
CodeBlockModel model = service.getActiveSubroutineModel(program);
|
||||
cachedModel = model;
|
||||
}
|
||||
|
||||
cachedProgram = program;
|
||||
return cachedModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getProgramLocation(Reference rowObject, Settings settings,
|
||||
Program program, ServiceProvider serviceProvider) {
|
||||
|
||||
BlockModelService service = serviceProvider.getService(BlockModelService.class);
|
||||
CodeBlockModel model = getCodeBlockModel(program, service);
|
||||
String subroutineName = getSubroutineName(rowObject, service, program, model);
|
||||
if (subroutineName == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Symbol symbol = getSymbol(rowObject.getFromAddress(), subroutineName, service, program);
|
||||
if (symbol != null) {
|
||||
return symbol.getProgramLocation();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class AccessTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<Reference, RefType> {
|
||||
|
||||
private AccessCellRenderer accessRenderer = new AccessCellRenderer();
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Access";
|
||||
}
|
||||
|
||||
@Override
|
||||
public RefType getValue(Reference rowObject, Settings settings, Program program,
|
||||
ServiceProvider serviceProvider) throws IllegalArgumentException {
|
||||
|
||||
Listing listing = program.getListing();
|
||||
RefType referenceType = rowObject.getReferenceType();
|
||||
if (referenceType == RefType.INDIRECTION) {
|
||||
Instruction instruction = listing.getInstructionAt(rowObject.getFromAddress());
|
||||
if (instruction != null) {
|
||||
FlowType flowType = instruction.getFlowType();
|
||||
return flowType;
|
||||
}
|
||||
}
|
||||
return referenceType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GColumnRenderer<RefType> getColumnRenderer() {
|
||||
return accessRenderer;
|
||||
}
|
||||
|
||||
private class AccessCellRenderer extends AbstractGhidraColumnRenderer<RefType> {
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
|
||||
JLabel label = (JLabel) super.getTableCellRendererComponent(data);
|
||||
|
||||
RefType refType = (RefType) data.getValue();
|
||||
label.setText(getReferenceType(refType));
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilterString(RefType t, Settings settings) {
|
||||
return getReferenceType(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
|
||||
import docking.widgets.table.GTableCellRenderingData;
|
||||
import ghidra.app.util.SymbolInspector;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.listing.Variable;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.VariableNameFieldLocation;
|
||||
import ghidra.util.table.GhidraTableCellRenderer;
|
||||
|
||||
class SymbolRenderer extends GhidraTableCellRenderer {
|
||||
private SymbolInspector inspector;
|
||||
|
||||
SymbolRenderer() {
|
||||
super();
|
||||
}
|
||||
|
||||
void setSymbolInspector(SymbolInspector inspector) {
|
||||
this.inspector = inspector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
|
||||
super.getTableCellRendererComponent(data);
|
||||
|
||||
Object value = data.getValue();
|
||||
int column = data.getColumnModelIndex();
|
||||
boolean isSelected = data.isSelected();
|
||||
|
||||
if (value == null && column == SymbolTableModel.LABEL_COL) {
|
||||
setText("<< REMOVED >>");
|
||||
}
|
||||
else if (value instanceof Symbol) {
|
||||
handleSymbol(value, isSelected);
|
||||
}
|
||||
else if (value instanceof Address) {
|
||||
setText(getAddressString((Address) value));
|
||||
}
|
||||
else if (value instanceof ProgramLocation) {
|
||||
setText(getLocationString((ProgramLocation) value));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private String getLocationString(ProgramLocation location) {
|
||||
if (location instanceof VariableNameFieldLocation) {
|
||||
VariableNameFieldLocation varLoc = (VariableNameFieldLocation) location;
|
||||
Variable variable = varLoc.getVariable();
|
||||
return variable.getVariableStorage().toString();
|
||||
}
|
||||
return getAddressString(location.getAddress());
|
||||
}
|
||||
|
||||
private void handleSymbol(Object value, boolean isSelected) {
|
||||
setBold();
|
||||
Color color =
|
||||
(inspector != null) && (value instanceof Symbol) ? inspector.getColor((Symbol) value)
|
||||
: Color.BLACK;
|
||||
|
||||
if (!isSelected) {
|
||||
setForeground(color);
|
||||
}
|
||||
}
|
||||
|
||||
private String getAddressString(Address address) {
|
||||
if (address.isStackAddress()) {
|
||||
return getStackAddressString(address);
|
||||
}
|
||||
else if (address.isRegisterAddress()) {
|
||||
return getRegisterAddressString(address);
|
||||
}
|
||||
else if (address.isExternalAddress() || address == Address.NO_ADDRESS) {
|
||||
return "";
|
||||
}
|
||||
return address.toString();
|
||||
}
|
||||
|
||||
private String getRegisterAddressString(Address address) {
|
||||
Program program = inspector.getProgram();
|
||||
if (program != null) {
|
||||
Register register = program.getRegister(address);
|
||||
if (register != null) {
|
||||
return register.toString();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private String getStackAddressString(Address address) {
|
||||
long offset = address.getOffset();
|
||||
if (offset < 0) {
|
||||
return "Stack[-0x" + Long.toHexString(-offset) + "]";
|
||||
}
|
||||
return "Stack[0x" + Long.toHexString(offset) + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.util.table.ProgramLocationTableRowMapper;
|
||||
|
||||
public class SymbolRowObjectToAddressTableRowMapper
|
||||
extends ProgramLocationTableRowMapper<Symbol, Address> {
|
||||
|
||||
@Override
|
||||
public Address map(Symbol rowObject, Program data, ServiceProvider serviceProvider) {
|
||||
if (rowObject == null) {
|
||||
return null;
|
||||
}
|
||||
return rowObject.getAddress();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.table.ProgramLocationTableRowMapper;
|
||||
|
||||
public class SymbolRowObjectToProgramLocationTableRowMapper
|
||||
extends ProgramLocationTableRowMapper<Symbol, ProgramLocation> {
|
||||
|
||||
@Override
|
||||
public ProgramLocation map(Symbol rowObject, Program data, ServiceProvider serviceProvider) {
|
||||
return rowObject.getProgramLocation();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
//This file is included mostly verbatim from the Ghidra distribution for dealing with ThreadedTableModel issues.
|
||||
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
import static docking.widgets.table.AddRemoveListItem.Type.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import docking.widgets.table.AddRemoveListItem;
|
||||
import docking.widgets.table.TableSortingContext;
|
||||
import docking.widgets.table.threaded.*;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* The symbol table users a {@link ThreadedTableModel}. This model does not correctly function
|
||||
* with data that can change outside of the table. The symbol table's row objects are the
|
||||
* {@link Symbol} db objects. These db objects can be changed by the user and by analysis
|
||||
* while table is loaded. The problem with this is that the table's sort can be broken when
|
||||
* symbols are to be added, removed or re-inserted, as this process requires a binary search which
|
||||
* will be broken if the criteria used to sort the data has changed. Effectively, a symbol
|
||||
* change can break the binary search if that symbol stays in a previously sorted position, but
|
||||
* has updated data that would put the symbol in a new position if sorted again. For example,
|
||||
* if the table is sorted on name and the name of a symbol changes, then future uses of the
|
||||
* binary search will be broken while that symbol is still in the position that matches its old
|
||||
* name.
|
||||
* <p>
|
||||
* This issue has been around for quite some time. To completely fix this issue, each row object
|
||||
* of the symbol table would need to be immutable, at least on the sort criteria. We could fix
|
||||
* this in the future if the *mostly correct* sorting behavior is not good enough. For now, the
|
||||
* client can trigger a re-sort (e.g., by opening and closing the table) to fix the slightly
|
||||
* out-of-sort data.
|
||||
* <p>
|
||||
* The likelihood of the sort being inconsistent now relates directly to how many changed symbols
|
||||
* are in the table at the time of an insert. The more changes symbols, the higher the chance
|
||||
* of a stale/misplaced symbol being used during a binary search, thus producing an invalid insert
|
||||
* position.
|
||||
* <p>
|
||||
* This strategy is setup to mitigate the number of invalid symbols in the table at the
|
||||
* time the inserts are applied. The basic workflow of this algorithm is:
|
||||
* <pre>
|
||||
* 1) condense the add / remove requests to remove duplicate efforts
|
||||
* 2) process all removes first
|
||||
* --all pure removes
|
||||
* --all removes as part of a re-insert
|
||||
* 3) process all items that failed to remove due to the sort data changing
|
||||
* 4) process all adds (this step will fail if the data contains mis-sorted items)
|
||||
* --all adds as part of a re-insert
|
||||
* --all pure adds
|
||||
* </pre>
|
||||
*
|
||||
* Step 3, processing failed removals, is done to avoid a brute force lookup at each removal
|
||||
* request.
|
||||
*
|
||||
* <P>This strategy has knowledge of client proxy object usage. The proxy objects
|
||||
* are coded such that the {@code hashCode()} and {@code equals()} methods will match those
|
||||
* methods of the data's real objects.
|
||||
*/
|
||||
public class SymbolTableAddRemoveStrategy implements TableAddRemoveStrategy<Symbol> {
|
||||
|
||||
@Override
|
||||
public void process(List<AddRemoveListItem<Symbol>> addRemoveList, TableData<Symbol> tableData,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
Set<AddRemoveListItem<Symbol>> items = coalesceAddRemoveItems(addRemoveList);
|
||||
|
||||
//
|
||||
// Hash map the existing values so that we can use any object inside the add/remove list
|
||||
// as a key into this map to get the matching existing value. Using the existing value
|
||||
// enables the binary search to work when the add/remove item is a proxy object, but the
|
||||
// existing item still has the data used to sort it. If the sort data has changed, then
|
||||
// even this step will not allow the TableData to find the item in a search.
|
||||
//
|
||||
Map<Symbol, Symbol> hashed = new HashMap<>();
|
||||
for (Symbol symbol : tableData) {
|
||||
hashed.put(symbol, symbol);
|
||||
}
|
||||
|
||||
Set<Symbol> failedToRemove = new HashSet<>();
|
||||
|
||||
int n = items.size();
|
||||
monitor.setMessage("Removing " + n + " items...");
|
||||
monitor.initialize(n);
|
||||
|
||||
Iterator<AddRemoveListItem<Symbol>> it = items.iterator();
|
||||
while (it.hasNext()) {
|
||||
AddRemoveListItem<Symbol> item = it.next();
|
||||
Symbol value = item.getValue();
|
||||
if (item.isChange()) {
|
||||
Symbol toRemove = hashed.remove(value);
|
||||
remove(tableData, toRemove, failedToRemove);
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
else if (item.isRemove()) {
|
||||
Symbol toRemove = hashed.remove(value);
|
||||
remove(tableData, toRemove, failedToRemove);
|
||||
it.remove();
|
||||
}
|
||||
monitor.checkCanceled();
|
||||
}
|
||||
|
||||
if (!failedToRemove.isEmpty()) {
|
||||
int size = failedToRemove.size();
|
||||
String message = size == 1 ? "1 old symbol..." : size + " old symbols...";
|
||||
monitor.setMessage("Removing " + message);
|
||||
|
||||
tableData.process((data, sortContext) -> {
|
||||
return expungeLostItems(failedToRemove, data, sortContext);
|
||||
});
|
||||
}
|
||||
|
||||
n = items.size();
|
||||
monitor.setMessage("Adding " + n + " items...");
|
||||
it = items.iterator();
|
||||
while (it.hasNext()) {
|
||||
AddRemoveListItem<Symbol> item = it.next();
|
||||
Symbol value = item.getValue();
|
||||
if (item.isChange()) {
|
||||
tableData.insert(value);
|
||||
hashed.put(value, value);
|
||||
}
|
||||
else if (item.isAdd()) {
|
||||
tableData.insert(value);
|
||||
hashed.put(value, value);
|
||||
}
|
||||
monitor.checkCanceled();
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
|
||||
monitor.setMessage("Done adding/removing");
|
||||
}
|
||||
|
||||
private Set<AddRemoveListItem<Symbol>> coalesceAddRemoveItems(
|
||||
List<AddRemoveListItem<Symbol>> addRemoveList) {
|
||||
|
||||
Map<Long, AddRemoveListItem<Symbol>> map = new HashMap<>();
|
||||
|
||||
for (AddRemoveListItem<Symbol> item : addRemoveList) {
|
||||
|
||||
if (item.isChange()) {
|
||||
handleChange(item, map);
|
||||
}
|
||||
else if (item.isAdd()) {
|
||||
handleAdd(item, map);
|
||||
}
|
||||
else {
|
||||
handleRemove(item, map);
|
||||
}
|
||||
}
|
||||
|
||||
return new HashSet<>(map.values());
|
||||
}
|
||||
|
||||
private void handleAdd(AddRemoveListItem<Symbol> item,
|
||||
Map<Long, AddRemoveListItem<Symbol>> map) {
|
||||
|
||||
long id = item.getValue().getID();
|
||||
AddRemoveListItem<Symbol> existing = map.get(id);
|
||||
if (existing == null) {
|
||||
map.put(id, item);
|
||||
return;
|
||||
}
|
||||
|
||||
if (existing.isChange()) {
|
||||
return; // change -> add; keep the change
|
||||
}
|
||||
if (existing.isAdd()) {
|
||||
return; // already an add
|
||||
}
|
||||
|
||||
// remove -> add; make a change
|
||||
map.put(id, new AddRemoveListItem<>(CHANGE, existing.getValue()));
|
||||
}
|
||||
|
||||
private void handleRemove(AddRemoveListItem<Symbol> item,
|
||||
Map<Long, AddRemoveListItem<Symbol>> map) {
|
||||
|
||||
long id = item.getValue().getID();
|
||||
AddRemoveListItem<Symbol> existing = map.get(id);
|
||||
if (existing == null) {
|
||||
map.put(id, item);
|
||||
return;
|
||||
}
|
||||
|
||||
if (existing.isChange()) {
|
||||
map.put(id, item); // change -> remove; just do the remove
|
||||
return;
|
||||
}
|
||||
if (existing.isRemove()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add -> remove; do no work
|
||||
map.remove(id);
|
||||
}
|
||||
|
||||
private void handleChange(AddRemoveListItem<Symbol> item,
|
||||
Map<Long, AddRemoveListItem<Symbol>> map) {
|
||||
|
||||
long id = item.getValue().getID();
|
||||
AddRemoveListItem<Symbol> existing = map.get(id);
|
||||
if (existing == null) {
|
||||
map.put(id, item);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!existing.isChange()) {
|
||||
// either add or remove followed by a change; keep the change
|
||||
map.put(id, item);
|
||||
}
|
||||
|
||||
// otherwise, we had a change followed by a change; keep just 1 change
|
||||
}
|
||||
|
||||
private void remove(TableData<Symbol> tableData, Symbol symbol, Set<Symbol> failedToRemove) {
|
||||
if (symbol == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tableData.remove(symbol)) {
|
||||
failedToRemove.add(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes the given set of items that were unsuccessfully removed from the table as part of
|
||||
* the add/remove process. These items could not be removed because some part of their
|
||||
* state has changed such that the binary search performed during the normal remove process
|
||||
* cannot locate the item in the table data. This algorithm will check the given set of
|
||||
* items against the entire list of table data, locating the item to be removed.
|
||||
*/
|
||||
private List<Symbol> expungeLostItems(Set<Symbol> toRemove, List<Symbol> data,
|
||||
TableSortingContext<Symbol> sortContext) {
|
||||
|
||||
if (sortContext.isUnsorted()) {
|
||||
// this can happen if the data is unsorted and we were asked to remove an item that
|
||||
// was never in the table for some reason
|
||||
return data;
|
||||
}
|
||||
|
||||
// Copy to a new list those items that are not marked for removal. This saves the
|
||||
// list move its items every time a remove takes place
|
||||
List<Symbol> newList = new ArrayList<>(data.size() - toRemove.size());
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
Symbol rowObject = data.get(i);
|
||||
if (!toRemove.contains(rowObject)) {
|
||||
newList.add(rowObject);
|
||||
}
|
||||
}
|
||||
|
||||
return newList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,757 @@
|
||||
//This file is included mostly verbatim from the Ghidra distribution.
|
||||
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
package codecutguiv2;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import docking.widgets.table.*;
|
||||
import docking.widgets.table.threaded.TableAddRemoveStrategy;
|
||||
import ghidra.app.cmd.function.DeleteFunctionCmd;
|
||||
import ghidra.app.cmd.label.DeleteLabelCmd;
|
||||
import ghidra.app.cmd.label.RenameLabelCmd;
|
||||
import ghidra.app.plugin.core.symtable.SymbolTableAddRemoveStrategy;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.framework.cmd.Command;
|
||||
import ghidra.framework.cmd.CompoundCmd;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.datastruct.Accumulator;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.table.AddressBasedTableModel;
|
||||
import ghidra.util.table.column.*;
|
||||
import ghidra.util.table.field.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
class SymbolTableModel extends AddressBasedTableModel<Symbol> {
|
||||
|
||||
private static final Comparator<Symbol> NAME_COL_COMPARATOR = (s1, s2) -> {
|
||||
return s1.toString().compareToIgnoreCase(s2.toString());
|
||||
};
|
||||
|
||||
static final int LABEL_COL = 0;
|
||||
static final int LOCATION_COL = 1;
|
||||
static final int TYPE_COL = 2;
|
||||
static final int DATA_TYPE_COL = 3;
|
||||
static final int NAMESPACE_COL = 4;
|
||||
static final int SOURCE_COL = 5;
|
||||
static final int REFS_COL = 6;
|
||||
|
||||
private SymbolProvider provider;
|
||||
private PluginTool tool;
|
||||
private SymbolTable symbolTable;
|
||||
private ReferenceManager refMgr;
|
||||
private Symbol lastSymbol;
|
||||
private SymbolFilter filter;
|
||||
private TableAddRemoveStrategy<Symbol> deletedDbObjectAddRemoveStrategy =
|
||||
new SymbolTableAddRemoveStrategy();
|
||||
private SymbolIterator symIter;
|
||||
private Program program;
|
||||
private Namespace ns;
|
||||
|
||||
SymbolTableModel(Program program, SymbolProvider provider, PluginTool tool) {
|
||||
super("Symbols", tool, null, null);
|
||||
this.provider = provider;
|
||||
this.tool = tool;
|
||||
this.program = program;
|
||||
this.filter = new NewSymbolFilter(CodecutUtils.getFilter());
|
||||
}
|
||||
|
||||
SymbolTableModel(Program program, SymbolProvider provider, PluginTool tool, SymbolIterator symIter, Namespace ns) {
|
||||
super("Symbols", tool, null, null);
|
||||
this.provider = provider;
|
||||
this.tool = tool;
|
||||
this.program = program;
|
||||
this.filter = new NewSymbolFilter(CodecutUtils.getFilter());
|
||||
this.symIter = symIter;
|
||||
this.ns = ns;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TableColumnDescriptor<Symbol> createTableColumnDescriptor() {
|
||||
TableColumnDescriptor<Symbol> descriptor = new TableColumnDescriptor<>();
|
||||
|
||||
descriptor.addVisibleColumn(new NameTableColumn());
|
||||
descriptor.addVisibleColumn(new LocationTableColumn(), 1, true);
|
||||
descriptor.addVisibleColumn(new SymbolTypeTableColumn());
|
||||
descriptor.addVisibleColumn(new SourceTableColumn());
|
||||
descriptor.addVisibleColumn(new ReferenceCountTableColumn());
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TableAddRemoveStrategy<Symbol> getAddRemoveStrategy(){
|
||||
return deletedDbObjectAddRemoveStrategy;
|
||||
}
|
||||
|
||||
void setFilter(SymbolFilter filter) {
|
||||
this.filter = filter;
|
||||
reload();
|
||||
}
|
||||
|
||||
Symbol getSymbol(long symbolID) {
|
||||
if (symbolTable != null) {
|
||||
return symbolTable.getSymbol(symbolID);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
symbolTable = null;
|
||||
refMgr = null;
|
||||
lastSymbol = null;
|
||||
provider = null;
|
||||
}
|
||||
|
||||
void reload(Program prog) {
|
||||
cancelAllUpdates();
|
||||
this.lastSymbol = null;
|
||||
if (prog != null) {
|
||||
this.setProgram(prog);
|
||||
this.symbolTable = prog.getSymbolTable();
|
||||
this.refMgr = prog.getReferenceManager();
|
||||
reload();
|
||||
}
|
||||
else {
|
||||
this.setProgram(null);
|
||||
this.symbolTable = null;
|
||||
this.refMgr = null;
|
||||
}
|
||||
}
|
||||
|
||||
public int getKeyCount() {
|
||||
if (symbolTable != null) {
|
||||
int cnt = symbolTable.getNumSymbols();
|
||||
if (filter.acceptsDefaultLabelSymbols()) {
|
||||
cnt += refMgr.getReferenceDestinationCount();
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoad(Accumulator<Symbol> accumulator, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
if (this.program == null) {
|
||||
return;
|
||||
}
|
||||
symIter = this.program.getSymbolTable().getSymbols(ns);
|
||||
if (symIter == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
monitor.initialize(getKeyCount());
|
||||
int value = 0;
|
||||
while (symIter.hasNext()) {
|
||||
monitor.setProgress(value++);
|
||||
monitor.checkCanceled();
|
||||
Symbol s = symIter.next();
|
||||
if (filter.accepts(s, this.program)) {
|
||||
accumulator.add(s);
|
||||
}
|
||||
}
|
||||
if (filter.acceptsDefaultLabelSymbols()) {
|
||||
AddressIterator addrIt = refMgr.getReferenceDestinationIterator(
|
||||
this.program.getAddressFactory().getAddressSet(), true);
|
||||
while (addrIt.hasNext()) {
|
||||
monitor.setProgress(value++);
|
||||
monitor.checkCanceled();
|
||||
Address a = addrIt.next();
|
||||
Symbol s = symbolTable.getPrimarySymbol(a);
|
||||
if (s.isDynamic() && filter.accepts(s, this.program)) {
|
||||
accumulator.add(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSortable(int columnIndex) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCellEditable(int key, int columnIndex) {
|
||||
return columnIndex == LABEL_COL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAt(Object aValue, int row, int columnIndex) {
|
||||
if (provider == null || symbolTable == null || aValue == null) {
|
||||
return;
|
||||
}
|
||||
if (row < 0 || row >= filteredData.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Symbol symbol = filteredData.get(row);
|
||||
if (symbol == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (columnIndex == LABEL_COL) {
|
||||
String newName = aValue.toString();
|
||||
if (!symbol.getName().equals(newName)) {
|
||||
Command renameCmd = new RenameLabelCmd(symbol.getAddress(), symbol.getName(),
|
||||
newName, SourceType.USER_DEFINED);
|
||||
|
||||
if (!tool.execute(renameCmd, this.program)) {
|
||||
Msg.showError(getClass(), provider.getComponent(), "Error Renaming Symbol",
|
||||
renameCmd.getStatusMsg());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getProgramLocation(int row, int column) {
|
||||
Symbol s = (Symbol) getValueAt(row, LABEL_COL);
|
||||
if (s != null) {
|
||||
return s.getProgramLocation();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ProgramLocation getProgramLocation(int row) {
|
||||
return (ProgramLocation) getValueAt(row, LOCATION_COL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramSelection getProgramSelection(int[] rows) {
|
||||
AddressSet set = new AddressSet();
|
||||
for (int element : rows) {
|
||||
AddressBasedLocation symbolLocation = getSymbolLocation(getRowObject(element));
|
||||
if (symbolLocation.isMemoryLocation()) {
|
||||
set.add(symbolLocation.getAddress());
|
||||
}
|
||||
}
|
||||
return new ProgramSelection(set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
lastSymbol = null;
|
||||
super.reload();
|
||||
}
|
||||
|
||||
void symbolAdded(Symbol s) {
|
||||
if (filter.accepts(s, this.program)) {
|
||||
addObject(s);
|
||||
lastSymbol = s;
|
||||
}
|
||||
}
|
||||
|
||||
void symbolRemoved(Symbol s) {
|
||||
if (lastSymbol != null && lastSymbol.getID() == s.getID()) {
|
||||
lastSymbol = null;
|
||||
}
|
||||
removeObject(s);
|
||||
}
|
||||
|
||||
void symbolChanged(Symbol s) {
|
||||
Symbol Symbol = s;
|
||||
if (s.getSource() != SourceType.DEFAULT) {
|
||||
updateObject(Symbol);
|
||||
}
|
||||
else {
|
||||
|
||||
removeObject(Symbol);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void delete(List<Symbol> rowObjects, Program p) {
|
||||
if (rowObjects == null || rowObjects.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
tool.setStatusInfo("");
|
||||
List<Symbol> deleteList = new LinkedList<>();
|
||||
CompoundCmd cmd = new CompoundCmd("Delete symbol(s)");
|
||||
for (Symbol symbol : rowObjects) {
|
||||
if (symbol.isDynamic()) {
|
||||
Symbol[] symbols = symbolTable.getSymbols(symbol.getAddress());
|
||||
if (symbols.length == 1) {
|
||||
tool.setStatusInfo("Unable to delete symbol: " + symbol.getName());
|
||||
continue;//can't delete dynamic symbols...
|
||||
}
|
||||
}
|
||||
|
||||
deleteList.add(symbol);
|
||||
String label = symbol.getName();
|
||||
if (symbol.getSymbolType() == SymbolType.FUNCTION) {
|
||||
Function function = (Function) symbol.getObject();
|
||||
boolean ignoreMissingFunction = function.isThunk();
|
||||
cmd.add(new DeleteFunctionCmd(symbol.getAddress(), ignoreMissingFunction));
|
||||
if (symbol.getSource() != SourceType.DEFAULT) {
|
||||
// remove label which gets created when non-default function is removed
|
||||
cmd.add(new DeleteLabelCmd(symbol.getAddress(), label,
|
||||
symbol.getParentNamespace()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
cmd.add(
|
||||
new DeleteLabelCmd(symbol.getAddress(), label, symbol.getParentNamespace()));
|
||||
}
|
||||
}
|
||||
if (cmd.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tool.execute(cmd, p)) {
|
||||
for (Symbol s : deleteList) {
|
||||
removeObject(s);
|
||||
}
|
||||
updateNow();
|
||||
}
|
||||
else {
|
||||
tool.setStatusInfo(cmd.getStatusMsg());
|
||||
reload();
|
||||
}
|
||||
}
|
||||
|
||||
public SymbolFilter getFilter() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
public Program getProgram() {
|
||||
return this.program;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getAddress(int row) {
|
||||
Symbol symbol = getRowObject(row);
|
||||
if (symbol == null) {
|
||||
return null;
|
||||
}
|
||||
return symbol.getAddress();
|
||||
}
|
||||
|
||||
private AddressBasedLocation getSymbolLocation(Symbol s) {
|
||||
if (s == null) {
|
||||
return new AddressBasedLocation();
|
||||
}
|
||||
if (program == null) {
|
||||
this.program = s.getProgram();
|
||||
}
|
||||
SymbolType type = s.getSymbolType();
|
||||
if (type == SymbolType.PARAMETER || type == SymbolType.LOCAL_VAR) {
|
||||
// Must use special location object for variables which renders variable storage
|
||||
// location since this can't be obtained from just a variable storage address
|
||||
Variable object = (Variable) s.getObject();
|
||||
if (object == null) {
|
||||
return null;
|
||||
}
|
||||
return new VariableSymbolLocation(object);
|
||||
}
|
||||
return new AddressBasedLocation(program, s.getAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Comparator<Symbol> createSortComparator(int columnIndex) {
|
||||
DynamicTableColumn<Symbol, ?, ?> column = getColumn(columnIndex);
|
||||
if (column instanceof NameTableColumn) {
|
||||
// note: we use our own name comparator to increase sorting speed for the name
|
||||
// column. This works because this comparator is called for each *row object*
|
||||
// allowing the comparator to compare the Symbols based on name instead of
|
||||
// having to use the table model's code for getting a column value for the
|
||||
// row object. The code for retrieving a column value is slower than just
|
||||
// working with the row object directly. See
|
||||
// ThreadedTableModel.getCachedColumnValueForRow for more info.
|
||||
return NAME_COL_COMPARATOR;
|
||||
}
|
||||
return super.createSortComparator(columnIndex);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Table Column Classes
|
||||
//==================================================================================================
|
||||
|
||||
private class NameTableColumn extends AbstractProgramBasedDynamicTableColumn<Symbol, Symbol> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Name";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Symbol getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
if (symbol.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
|
||||
private class PinnedTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<Symbol, Boolean> {
|
||||
|
||||
private PinnedRenderer renderer = new PinnedRenderer();
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Pinned";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
if (symbol.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
return symbol.isPinned();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GColumnRenderer<Boolean> getColumnRenderer() {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
private class PinnedRenderer extends GBooleanCellRenderer
|
||||
implements AbstractWrapperTypeColumnRenderer<Boolean> {
|
||||
// body is handled by parents
|
||||
}
|
||||
}
|
||||
|
||||
private class LocationTableColumn
|
||||
extends AbstractProgramLocationTableColumn<Symbol, AddressBasedLocation> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Location";
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressBasedLocation getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
return getSymbolLocation(symbol);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getProgramLocation(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) {
|
||||
|
||||
if (symbol.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
return symbol.getProgramLocation();
|
||||
}
|
||||
}
|
||||
|
||||
private class SymbolTypeTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<Symbol, String> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Type";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
if (symbol.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Note: this call is slow. If we decide that filtering/sorting on this value is
|
||||
// important, then this should be cached
|
||||
return SymbolUtilities.getSymbolTypeDisplayName(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
private class VariableSymbolLocation extends AddressBasedLocation {
|
||||
|
||||
VariableSymbolLocation(Variable variable) {
|
||||
super(variable.getSymbol().getAddress(), variable.getVariableStorage().toString());
|
||||
}
|
||||
}
|
||||
|
||||
private class DataTypeTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<Symbol, String> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Data Type";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
if (symbol.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
DataType dt = null;
|
||||
Object obj = symbol.getObject();
|
||||
if (obj instanceof Data) {
|
||||
dt = ((Data) obj).getDataType();
|
||||
}
|
||||
else if (obj instanceof Function) {
|
||||
dt = ((Function) obj).getReturnType();
|
||||
}
|
||||
else if (obj instanceof Variable) {
|
||||
dt = ((Variable) obj).getDataType();
|
||||
}
|
||||
else if (obj instanceof ExternalLocation) {
|
||||
dt = ((ExternalLocation) obj).getDataType();
|
||||
}
|
||||
if (dt != null) {
|
||||
return dt.getDisplayName();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private class NamespaceTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<Symbol, String> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Namespace";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
if (symbol.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
return symbol.getParentNamespace().getName(true);
|
||||
}
|
||||
}
|
||||
|
||||
private class SourceTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<Symbol, SourceType> {
|
||||
|
||||
private GColumnRenderer<SourceType> renderer = new AbstractGColumnRenderer<SourceType>() {
|
||||
@Override
|
||||
protected String getText(Object value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
return ((SourceType) value).getDisplayString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilterString(SourceType t, Settings settings) {
|
||||
return getText(t);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Source";
|
||||
}
|
||||
|
||||
@Override
|
||||
public GColumnRenderer<SourceType> getColumnRenderer() {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SourceType getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
if (symbol == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return symbol.getSource();
|
||||
}
|
||||
}
|
||||
|
||||
private class ReferenceCountTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<Symbol, Integer> {
|
||||
|
||||
private ReferenceCountRenderer renderer = new ReferenceCountRenderer();
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Reference Count";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
if (symbol.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
return Integer.valueOf(symbol.getReferenceCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public GColumnRenderer<Integer> getColumnRenderer() {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
// this renderer disables the default text filtering; this column is only filterable
|
||||
// via the column constraint filtering
|
||||
private class ReferenceCountRenderer extends GTableCellRenderer
|
||||
implements AbstractWrapperTypeColumnRenderer<Integer> {
|
||||
// body is handled by parents
|
||||
}
|
||||
}
|
||||
|
||||
private class OffcutReferenceCountTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<Symbol, Integer> {
|
||||
|
||||
private OffcutReferenceCountRenderer renderer = new OffcutReferenceCountRenderer();
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Offcut Ref Count";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
if (symbol.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Address address = symbol.getAddress();
|
||||
int count = 0;
|
||||
if (address.isMemoryAddress()) {
|
||||
CodeUnit codeUnit = p.getListing().getCodeUnitContaining(address);
|
||||
if (codeUnit != null) {
|
||||
AddressSet set =
|
||||
new AddressSet(codeUnit.getMinAddress(), codeUnit.getMaxAddress());
|
||||
set.deleteRange(address, address);
|
||||
ReferenceManager referenceManager = p.getReferenceManager();
|
||||
AddressIterator it =
|
||||
referenceManager.getReferenceDestinationIterator(set, true);
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Integer.valueOf(count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GColumnRenderer<Integer> getColumnRenderer() {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
// this renderer disables the default text filtering; this column is only filterable
|
||||
// via the column constraint filtering
|
||||
private class OffcutReferenceCountRenderer extends GTableCellRenderer
|
||||
implements AbstractWrapperTypeColumnRenderer<Integer> {
|
||||
// body is handled by parents
|
||||
}
|
||||
}
|
||||
|
||||
private class UserTableColumn extends AbstractProgramBasedDynamicTableColumn<Symbol, String> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "User";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnDescription() {
|
||||
return "The user that created or last edited this symbol.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
if (symbol.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
SourceType source = symbol.getSource();
|
||||
if (source != SourceType.USER_DEFINED) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Address address = symbol.getAddress();
|
||||
LabelHistory[] labelHistory = symbolTable.getLabelHistory(address);
|
||||
if (labelHistory.length > 0) {
|
||||
return labelHistory[0].getUserName();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class OriginalNameColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<Symbol, String> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Original Imported Name";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnDescription() {
|
||||
return "The original (pre-demangled) import name (External Symbols Only)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
if (symbol.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!symbol.isExternal()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
SymbolType symbolType = symbol.getSymbolType();
|
||||
if (symbolType != SymbolType.FUNCTION && symbolType != SymbolType.LABEL) {
|
||||
return null;
|
||||
}
|
||||
ExternalManager externalManager = p.getExternalManager();
|
||||
ExternalLocation externalLocation = externalManager.getExternalLocation(symbol);
|
||||
if (externalLocation != null) {
|
||||
return externalLocation.getOriginalImportedName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
Iterator<SymbolPanel> getPanels(){
|
||||
return provider.getSymPanels();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
The "src/resources/images" directory is intended to hold all image/icon files used by
|
||||
this module.
|
||||
2
codecut-gui/codecut-gui/src/test/java/README.test.txt
Normal file
2
codecut-gui/codecut-gui/src/test/java/README.test.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
The "test" directory is intended to hold unit test cases. The package structure within
|
||||
this folder should correspond to that found in the "src" folder.
|
||||
0
deepcut/deepcut-ghidra/Module.manifest
Normal file
0
deepcut/deepcut-ghidra/Module.manifest
Normal file
75
deepcut/deepcut-ghidra/README.md
Normal file
75
deepcut/deepcut-ghidra/README.md
Normal file
@@ -0,0 +1,75 @@
|
||||
Ghidra Deepcut Analyzer
|
||||
=======================
|
||||
|
||||
Implementation of the deepcut as a Ghidra one-shot analyzer.
|
||||
|
||||
## Building and Installation
|
||||
JDK 11 (or newer) and Ghidra 9.1.0 (or newer) are required.
|
||||
|
||||
Ghidra's standard Gradle build system is used. Set the
|
||||
`GHIDRA_INSTALL_DIR` environment variable before building, or set it as
|
||||
a Gradle property (useful for building in an IDE):
|
||||
|
||||
### Environment variable
|
||||
```bash
|
||||
$ export GHIDRA_INSTALL_DIR="/path/to/ghidra"
|
||||
$ ./gradlew
|
||||
```
|
||||
|
||||
### Gradle property
|
||||
```bash
|
||||
echo GHIDRA_INSTALL_DIR=/path/to/ghidra > gradle.properties
|
||||
```
|
||||
|
||||
The module ZIP will be output to `dist/`. Use **File > Install
|
||||
Extensions** and select the green plus to browse to the
|
||||
extension. Restart Ghidra when prompted.
|
||||
|
||||
For proper functionality, the plugin should be built with the same JRE
|
||||
used by your Ghidra installation. If you have multiple Java runtime
|
||||
environments installed, select the correct JRE by setting the
|
||||
`JAVA_HOME` environment variable before building.
|
||||
|
||||
### Python 3
|
||||
The deepcut graph based machine learning model needs Python 3 to
|
||||
execute. The analyzer calls and external python process to execute the
|
||||
model on a graph representation of the binary. There are no GPU
|
||||
requirements since the model converge quickly even running in CPU mode.
|
||||
|
||||
#### Python 3 Path
|
||||
By default the analyzer use the command `/usr/local/bin/python3` to
|
||||
execute the deepcut python script. This setting can be changed in the
|
||||
Analysis Options menu **Analysis -> Analyze All Open...** To change the
|
||||
setting you need to click the checkbox next to **Deepcut (Prototype)**
|
||||
first.
|
||||
|
||||
#### Dependencies
|
||||
Deepcut has the following Python 3 dependencies:
|
||||
|
||||
- torch 1.7.1
|
||||
- torch-geometric 1.6.3
|
||||
- torch-cluster 1.5.8
|
||||
- torch-sparse 0.6.8
|
||||
- torch-scatter 2.0.5
|
||||
- torch-spline-conv 1.2.0
|
||||
|
||||
To install the dependencies:
|
||||
|
||||
```bash
|
||||
pip install torch==1.7.1+cpu -f https://download.pytorch.org/whl/torch_stable.html
|
||||
pip install -r requirements-torch_geometric.txt
|
||||
```
|
||||
|
||||
The torch-cluster dependency can take a significant amount of time to
|
||||
build and install.
|
||||
|
||||
## Running the Analyzer
|
||||
The Deepcut analyzer will not run during auto-analysis. Once the binary
|
||||
is loaded and the auto-analyzer is finish use the menu item **Analysis
|
||||
-> One Shot -> Deepcut**
|
||||
|
||||
Once complete each function will include a `moduleX` value in the
|
||||
Namespace field.
|
||||
|
||||
If there are any errors please make sure you are using the proper path
|
||||
to Python 3 and the requirement dependencies installed.
|
||||
33
deepcut/deepcut-ghidra/build.gradle
Normal file
33
deepcut/deepcut-ghidra/build.gradle
Normal file
@@ -0,0 +1,33 @@
|
||||
// Builds a Ghidra Extension for a given Ghidra installation.
|
||||
//
|
||||
// An absolute path to the Ghidra installation directory must be supplied either by setting the
|
||||
// GHIDRA_INSTALL_DIR environment variable or Gradle project property:
|
||||
//
|
||||
// > export GHIDRA_INSTALL_DIR=<Absolute path to Ghidra>
|
||||
// > gradle
|
||||
//
|
||||
// or
|
||||
//
|
||||
// > gradle -PGHIDRA_INSTALL_DIR=<Absolute path to Ghidra>
|
||||
//
|
||||
// Gradle should be invoked from the directory of the project to build. Please see the
|
||||
// application.gradle.version property in <GHIDRA_INSTALL_DIR>/Ghidra/application.properties
|
||||
// for the correction version of Gradle to use for the Ghidra installation you specify.
|
||||
|
||||
//----------------------START "DO NOT MODIFY" SECTION------------------------------
|
||||
def ghidraInstallDir
|
||||
|
||||
if (System.env.GHIDRA_INSTALL_DIR) {
|
||||
ghidraInstallDir = System.env.GHIDRA_INSTALL_DIR
|
||||
}
|
||||
else if (project.hasProperty("GHIDRA_INSTALL_DIR")) {
|
||||
ghidraInstallDir = project.getProperty("GHIDRA_INSTALL_DIR")
|
||||
}
|
||||
|
||||
if (ghidraInstallDir) {
|
||||
apply from: new File(ghidraInstallDir).getCanonicalPath() + "/support/buildExtension.gradle"
|
||||
}
|
||||
else {
|
||||
throw new GradleException("GHIDRA_INSTALL_DIR is not defined!")
|
||||
}
|
||||
//----------------------END "DO NOT MODIFY" SECTION-------------------------------
|
||||
148
deepcut/deepcut-ghidra/data/GNN_Net.py
Normal file
148
deepcut/deepcut-ghidra/data/GNN_Net.py
Normal file
@@ -0,0 +1,148 @@
|
||||
# © 2021 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
# (JHU/APL). All Rights Reserved.
|
||||
#
|
||||
# This material may be only be used, modified, or reproduced by or for
|
||||
# the U.S. Government pursuant to the license rights granted under the
|
||||
# clauses at DFARS 252.227-7013/7014 or FAR 52.227-14. For any other
|
||||
# permission, please contact the Office of Technology Transfer at
|
||||
# JHU/APL.
|
||||
#
|
||||
# NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
# MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
# THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
# VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
# EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
# WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
# PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
# PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
# LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
# TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
# SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
# THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
# PROFITS.
|
||||
#
|
||||
# HAVE A NICE DAY.
|
||||
|
||||
# This material is based upon work supported by the Defense Advanced Research
|
||||
# Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
# under Contract Number N66001-20-C-4024.
|
||||
|
||||
import torch
|
||||
import torch.nn.functional as F
|
||||
from torch.nn import Sequential, Linear, ReLU
|
||||
from torch_geometric import nn
|
||||
|
||||
|
||||
class Net(torch.nn.Module):
|
||||
def __init__(self, num_features, num_edge_features, dim=32):
|
||||
super(Net, self).__init__()
|
||||
|
||||
self.init_mlp = Sequential(Linear(num_features, num_edge_features),
|
||||
ReLU(),
|
||||
Linear(num_edge_features, num_edge_features))
|
||||
self.init_bn = torch.nn.LayerNorm(num_edge_features)
|
||||
|
||||
self.init_emlp = Sequential(Linear(num_edge_features, num_edge_features),
|
||||
ReLU(),
|
||||
Linear(num_edge_features, num_edge_features))
|
||||
self.init_ebn = torch.nn.LayerNorm(num_edge_features)
|
||||
|
||||
mlp1 = Sequential(Linear(num_edge_features, dim),
|
||||
ReLU(),
|
||||
Linear(dim, dim), ReLU(), Linear(dim, dim))
|
||||
self.e_mlp1 = Sequential(Linear(num_edge_features, dim),
|
||||
ReLU(),
|
||||
Linear(dim, dim), ReLU(), Linear(dim, dim))
|
||||
self.e_bn1 = torch.nn.LayerNorm(dim)
|
||||
self.gin1 = nn.GINEConv(mlp1, train_eps=True).jittable()
|
||||
self.bn1 = nn.PairNorm() # nn.LayerNorm(dim) #torch.nn.BatchNorm1d(dim)
|
||||
|
||||
mlp2 = Sequential(Linear(dim, dim), ReLU(), Linear(dim, dim),
|
||||
ReLU(),
|
||||
Linear(dim, dim))
|
||||
self.gin2 = nn.GINEConv(mlp2, train_eps=True).jittable()
|
||||
self.bn2 = nn.PairNorm() # nn.LayerNorm(dim) #torch.nn.BatchNorm1d(dim)
|
||||
self.e_mlp2 = Sequential(Linear(3*dim, dim),
|
||||
ReLU(),
|
||||
Linear(dim, dim),
|
||||
ReLU(),
|
||||
Linear(dim, dim))
|
||||
self.ebn2 = torch.nn.LayerNorm(dim)
|
||||
|
||||
mlp3 = Sequential(Linear(dim, dim),
|
||||
ReLU(),
|
||||
Linear(dim, dim),
|
||||
ReLU(),
|
||||
Linear(dim, dim))
|
||||
self.gin3 = nn.GINEConv(mlp3, train_eps=True).jittable()
|
||||
self.bn3 = nn.PairNorm() # nn.LayerNorm(dim)
|
||||
self.e_mlp3 = Sequential(Linear(3*dim, dim),
|
||||
ReLU(),
|
||||
Linear(dim, dim),
|
||||
ReLU(),
|
||||
Linear(dim, dim))
|
||||
self.ebn3 = torch.nn.LayerNorm(dim)
|
||||
|
||||
self.out1 = torch.nn.Linear(3*dim, dim)
|
||||
self.out_bn = torch.nn.LayerNorm(dim)
|
||||
self.out2 = torch.nn.Linear(dim, 4)
|
||||
|
||||
def forward(self, x, edge_attr, edge_index, batch):
|
||||
# x, edge_attr, edge_index, batch = (
|
||||
# data.x,
|
||||
# data.edge_attr,
|
||||
# data.edge_index,
|
||||
# data.batch,
|
||||
# )
|
||||
|
||||
x = F.relu(self.init_mlp(x))
|
||||
x = self.init_bn(x)
|
||||
edge_attr = self.init_emlp(edge_attr)
|
||||
edge_attr = self.init_ebn(edge_attr)
|
||||
|
||||
x = F.relu(self.gin1(x, edge_index, edge_attr))
|
||||
x = self.bn1(x, batch)
|
||||
edge_attr = F.relu(self.e_mlp1(edge_attr))
|
||||
edge_attr = self.e_bn1(edge_attr)
|
||||
|
||||
x = F.relu(self.gin2(x, edge_index, edge_attr))
|
||||
x = self.bn2(x, batch)
|
||||
edge_attr = torch.cat([x[edge_index[0]], x[edge_index[1]], edge_attr],
|
||||
dim=1)
|
||||
edge_attr = self.e_mlp2(edge_attr)
|
||||
edge_attr = self.ebn2(edge_attr)
|
||||
|
||||
x = F.relu(self.gin3(x, edge_index, edge_attr))
|
||||
x = self.bn3(x, batch)
|
||||
edge_attr = torch.cat([x[edge_index[0]], x[edge_index[1]], edge_attr], dim=1)
|
||||
edge_attr = self.e_mlp3(edge_attr)
|
||||
edge_attr = self.ebn2(edge_attr) # oops typo this should be a 3
|
||||
|
||||
#x = x[edge_index[0]] + x[edge_index[1]] + edge_attr
|
||||
#x = F.softmax(x, dim=1)
|
||||
#edge_attr = F.softmax(edge_attr, dim=1)
|
||||
x = torch.cat([x[edge_index[0]], x[edge_index[1]], edge_attr], dim=1)
|
||||
|
||||
x = F.relu(self.out1(x))
|
||||
x = self.out_bn(x)
|
||||
x = self.out2(x)
|
||||
|
||||
#ret = torch.max(x, dim=1)[0]
|
||||
ret = torch.mean(x, dim=1)
|
||||
|
||||
#ret = torch.max(x[edge_index[0]] + x[edge_index[1]], dim=1)[0]
|
||||
#ret = torch.mean(x[edge_index[0]] + x[edge_index[1]], dim=1)
|
||||
return ret
|
||||
|
||||
def load_gnn(model_file):
|
||||
model = Net(
|
||||
num_features=2,
|
||||
num_edge_features=4,
|
||||
dim=64,
|
||||
)
|
||||
|
||||
loaded_weights = torch.load(model_file,
|
||||
map_location=torch.device('cpu'))
|
||||
model.load_state_dict(loaded_weights)
|
||||
|
||||
return model
|
||||
15
deepcut/deepcut-ghidra/data/README.txt
Normal file
15
deepcut/deepcut-ghidra/data/README.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
The "data" directory is intended to hold data files that will be used by this module and will
|
||||
not end up in the .jar file, but will be present in the zip or tar file. Typically, data
|
||||
files are placed here rather than in the resources directory if the user may need to edit them.
|
||||
|
||||
An optional data/languages directory can exist for the purpose of containing various Sleigh language
|
||||
specification files and importer opinion files.
|
||||
|
||||
The data/buildLanguage.xml is used for building the contents of the data/languages directory.
|
||||
|
||||
The skel language definition has been commented-out within the skel.ldefs file so that the
|
||||
skeleton language does not show-up within Ghidra.
|
||||
|
||||
See the Sleigh language documentation (docs/languages/index.html) for details Sleigh language
|
||||
specification syntax.
|
||||
|
||||
199
deepcut/deepcut-ghidra/data/deepcut.py
Normal file
199
deepcut/deepcut-ghidra/data/deepcut.py
Normal file
@@ -0,0 +1,199 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
# (JHU/APL). All Rights Reserved.
|
||||
#
|
||||
# This material may be only be used, modified, or reproduced by or for
|
||||
# the U.S. Government pursuant to the license rights granted under the
|
||||
# clauses at DFARS 252.227-7013/7014 or FAR 52.227-14. For any other
|
||||
# permission, please contact the Office of Technology Transfer at
|
||||
# JHU/APL.
|
||||
#
|
||||
# NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
# MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
# THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
# VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
# EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
# WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
# PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
# PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
# LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
# TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
# SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
# THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
# PROFITS.
|
||||
#
|
||||
# HAVE A NICE DAY.
|
||||
|
||||
# This material is based upon work supported by the Defense Advanced Research
|
||||
# Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
# under Contract Number N66001-20-C-4024.
|
||||
|
||||
|
||||
import json
|
||||
import sys
|
||||
import numpy as np
|
||||
|
||||
import torch
|
||||
|
||||
from math import log2, copysign
|
||||
from networkx import DiGraph
|
||||
from scipy.linalg import toeplitz
|
||||
|
||||
import GNN_Net
|
||||
|
||||
|
||||
class Deepcut:
|
||||
def __init__(self, fcg_data, model_file):
|
||||
self.fcg_data = fcg_data
|
||||
self.model_file = model_file
|
||||
|
||||
self.graph = DiGraph()
|
||||
self.functions = {}
|
||||
self.graph_connectivity = []
|
||||
self.node_features = []
|
||||
self.edge_features = []
|
||||
|
||||
self._generate_graph()
|
||||
self._generate_features()
|
||||
self._predicte_labels()
|
||||
|
||||
def _generate_graph(self):
|
||||
for f in self.fcg_data['functions']:
|
||||
self.graph.add_node(f['index'],
|
||||
num_inc=log2(2 + f['num_incoming_edges']),
|
||||
num_out=log2(2 + f['num_outgoing_edges']))
|
||||
self.functions[f['index']] = {
|
||||
'address': f['addr'],
|
||||
'name': f['name'],
|
||||
}
|
||||
|
||||
for e in self.fcg_data['edges']:
|
||||
index_dist_weight = copysign(log2(2 + abs(e['index_distance'])),
|
||||
e['index_distance'])
|
||||
address_dist_weight = copysign(log2(2 + abs(e['addr_distance'])),
|
||||
e['addr_distance']) / 4
|
||||
multiplicity_weight = log2(2 + abs(e['multiplicity']))
|
||||
|
||||
# The weight attribute is a 4-tuple. The multiplicity value
|
||||
# is 0.0 for the "opposite" direction
|
||||
self.graph.add_edge(e['src_index'], e['dst_index'],
|
||||
weights=(index_dist_weight,
|
||||
address_dist_weight,
|
||||
multiplicity_weight,
|
||||
0.0))
|
||||
|
||||
self.graph.add_edge(e['dst_index'], e['src_index'],
|
||||
weights=(-index_dist_weight,
|
||||
-address_dist_weight,
|
||||
0.0,
|
||||
multiplicity_weight))
|
||||
|
||||
def _generate_features(self):
|
||||
for n in sorted(list(self.graph.nodes)):
|
||||
self.node_features.append([self.graph.nodes[n]['num_out'],
|
||||
self.graph.nodes[n]['num_inc']])
|
||||
|
||||
for (n1, n2, d) in self.graph.edges(data=True):
|
||||
self.graph_connectivity.append([n1, n2])
|
||||
self.edge_features.append(list(d["weights"]))
|
||||
|
||||
def _predicte_labels(self):
|
||||
model = GNN_Net.load_gnn(self.model_file)
|
||||
m = model(x=torch.Tensor(self.node_features),
|
||||
edge_index=torch.LongTensor(self.graph_connectivity).t().contiguous(),
|
||||
edge_attr=torch.Tensor(self.edge_features),
|
||||
batch=torch.tensor([0] * len(self.graph)))
|
||||
|
||||
self.predicted_labels = torch.sigmoid(m).detach().numpy()
|
||||
|
||||
def _adjacency_matrix(self):
|
||||
num_funcs = len(self.graph.nodes)
|
||||
A = np.zeros((num_funcs, num_funcs))
|
||||
|
||||
for e, v in zip(self.graph_connectivity, self.predicted_labels):
|
||||
e0, e1 = e
|
||||
A[e0, e1] = v
|
||||
|
||||
A += A.T
|
||||
A *= 0.5
|
||||
|
||||
"""
|
||||
add a small connection between adjacent nodes,
|
||||
essentially to break ties in favor of merging communities
|
||||
"""
|
||||
x = np.zeros(num_funcs)
|
||||
x[1] = 0.05
|
||||
A += toeplitz(x)
|
||||
|
||||
return A
|
||||
|
||||
def _modularity(self):
|
||||
adj_matrix = self._adjacency_matrix()
|
||||
# node degrees
|
||||
k = np.sum(adj_matrix, axis=0)
|
||||
|
||||
k2 = np.array([k])
|
||||
B = k2.T @ k2
|
||||
B /= 2 * np.sum(k2)
|
||||
|
||||
Q = adj_matrix - B
|
||||
|
||||
def compute_partial_modularity(start, stop):
|
||||
return np.sum(Q[start:stop, start:stop])
|
||||
|
||||
scores = [0.0]
|
||||
scores = np.array(scores)
|
||||
cuts = [[0]]
|
||||
|
||||
# speedup so it runs in linear time
|
||||
max_cluster_size = 100
|
||||
|
||||
for index in range(1, len(self.graph.nodes)):
|
||||
update = [compute_partial_modularity(i, index) for i in
|
||||
range(max(0, index-max_cluster_size), index)]
|
||||
if index > max_cluster_size:
|
||||
update = [0]*(index-max_cluster_size) + update
|
||||
updated_scores = scores + update
|
||||
|
||||
i = np.argmax(updated_scores)
|
||||
|
||||
if index > max_cluster_size:
|
||||
i = np.argmax(updated_scores[index-max_cluster_size:])+ (index - max_cluster_size)
|
||||
|
||||
s = updated_scores[i]
|
||||
c = cuts[i] + [index]
|
||||
|
||||
scores = np.append(scores, s)
|
||||
cuts.append(c)
|
||||
|
||||
final_cut = cuts[-1]
|
||||
return final_cut
|
||||
|
||||
def module_list(self):
|
||||
cuts = self._modularity()
|
||||
return [self.functions[x] for x in cuts]
|
||||
|
||||
|
||||
def read_input():
|
||||
# Yes this expects the json to have no newlines
|
||||
inp = sys.stdin.readline()
|
||||
ret = json.loads(inp)
|
||||
return ret
|
||||
|
||||
|
||||
def main():
|
||||
fcg = read_input()
|
||||
|
||||
if len(sys.argv) == 2:
|
||||
model_file = sys.argv[1]
|
||||
else:
|
||||
model_file = "model_weights_1.p"
|
||||
|
||||
d = Deepcut(fcg, model_file)
|
||||
|
||||
print(json.dumps(d.module_list()))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
BIN
deepcut/deepcut-ghidra/data/model_weights_1.p
Normal file
BIN
deepcut/deepcut-ghidra/data/model_weights_1.p
Normal file
Binary file not shown.
5
deepcut/deepcut-ghidra/extension.properties
Normal file
5
deepcut/deepcut-ghidra/extension.properties
Normal file
@@ -0,0 +1,5 @@
|
||||
name=@extname@
|
||||
description=Use the deepcut algorithm to find module boundaries.
|
||||
author=JHU/APL
|
||||
createdOn=
|
||||
version=@extversion@
|
||||
1
deepcut/deepcut-ghidra/ghidra_scripts/README.txt
Normal file
1
deepcut/deepcut-ghidra/ghidra_scripts/README.txt
Normal file
@@ -0,0 +1 @@
|
||||
Java source directory to hold module-specific Ghidra scripts.
|
||||
5
deepcut/deepcut-ghidra/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
deepcut/deepcut-ghidra/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
185
deepcut/deepcut-ghidra/gradlew
vendored
Normal file
185
deepcut/deepcut-ghidra/gradlew
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
89
deepcut/deepcut-ghidra/gradlew.bat
vendored
Normal file
89
deepcut/deepcut-ghidra/gradlew.bat
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
3
deepcut/deepcut-ghidra/os/linux64/README.txt
Normal file
3
deepcut/deepcut-ghidra/os/linux64/README.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
The "os/linux64" directory is intended to hold Linux native binaries
|
||||
which this module is dependent upon. This directory may be eliminated for a specific
|
||||
module if native binaries are not provided for the corresponding platform.
|
||||
3
deepcut/deepcut-ghidra/os/osx64/README.txt
Normal file
3
deepcut/deepcut-ghidra/os/osx64/README.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
The "os/osx64" directory is intended to hold macOS (OS X) native binaries
|
||||
which this module is dependent upon. This directory may be eliminated for a specific
|
||||
module if native binaries are not provided for the corresponding platform.
|
||||
3
deepcut/deepcut-ghidra/os/win64/README.txt
Normal file
3
deepcut/deepcut-ghidra/os/win64/README.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
The "os/win64" directory is intended to hold MS Windows native binaries (.exe)
|
||||
which this module is dependent upon. This directory may be eliminated for a specific
|
||||
module if native binaries are not provided for the corresponding platform.
|
||||
7
deepcut/deepcut-ghidra/requirements-torch_geometric.txt
Normal file
7
deepcut/deepcut-ghidra/requirements-torch_geometric.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
torch-geometric==1.6.3
|
||||
|
||||
--find-links https://pytorch-geometric.com/whl/torch-1.7.0+cpu.html
|
||||
torch-sparse==0.6.8
|
||||
torch-scatter==2.0.5
|
||||
torch-cluster==1.5.8
|
||||
torch-spline-conv==1.2.0
|
||||
57
deepcut/deepcut-ghidra/src/main/help/help/TOC_Source.xml
Normal file
57
deepcut/deepcut-ghidra/src/main/help/help/TOC_Source.xml
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version='1.0' encoding='ISO-8859-1' ?>
|
||||
<!--
|
||||
|
||||
This is an XML file intended to be parsed by the Ghidra help system. It is loosely based
|
||||
upon the JavaHelp table of contents document format. The Ghidra help system uses a
|
||||
TOC_Source.xml file to allow a module with help to define how its contents appear in the
|
||||
Ghidra help viewer's table of contents. The main document (in the Base module)
|
||||
defines a basic structure for the
|
||||
Ghidra table of contents system. Other TOC_Source.xml files may use this structure to insert
|
||||
their files directly into this structure (and optionally define a substructure).
|
||||
|
||||
|
||||
In this document, a tag can be either a <tocdef> or a <tocref>. The former is a definition
|
||||
of an XML item that may have a link and may contain other <tocdef> and <tocref> children.
|
||||
<tocdef> items may be referred to in other documents by using a <tocref> tag with the
|
||||
appropriate id attribute value. Using these two tags allows any module to define a place
|
||||
in the table of contents system (<tocdef>), which also provides a place for
|
||||
other TOC_Source.xml files to insert content (<tocref>).
|
||||
|
||||
During the help build time, all TOC_Source.xml files will be parsed and validated to ensure
|
||||
that all <tocref> tags point to valid <tocdef> tags. From these files will be generated
|
||||
<module name>_TOC.xml files, which are table of contents files written in the format
|
||||
desired by the JavaHelp system. Additionally, the genated files will be merged together
|
||||
as they are loaded by the JavaHelp system. In the end, when displaying help in the Ghidra
|
||||
help GUI, there will be on table of contents that has been created from the definitions in
|
||||
all of the modules' TOC_Source.xml files.
|
||||
|
||||
|
||||
Tags and Attributes
|
||||
|
||||
<tocdef>
|
||||
-id - the name of the definition (this must be unique across all TOC_Source.xml files)
|
||||
-text - the display text of the node, as seen in the help GUI
|
||||
-target** - the file to display when the node is clicked in the GUI
|
||||
-sortgroup - this is a string that defines where a given node should appear under a given
|
||||
parent. The string values will be sorted by the JavaHelp system using
|
||||
a javax.text.RulesBasedCollator. If this attribute is not specified, then
|
||||
the text of attribute will be used.
|
||||
|
||||
<tocref>
|
||||
-id - The id of the <tocdef> that this reference points to
|
||||
|
||||
**The URL for the target is relative and should start with 'help/topics'. This text is
|
||||
used by the Ghidra help system to provide a universal starting point for all links so that
|
||||
they can be resolved at runtime, across modules.
|
||||
|
||||
|
||||
-->
|
||||
|
||||
|
||||
<tocroot>
|
||||
<!-- Uncomment and adjust fields to add help topic to help system's Table of Contents
|
||||
<tocref id="Ghidra Functionality">
|
||||
<tocdef id="HelpAnchor" text="My Feature" target="help/topics/my_topic/help.html" />
|
||||
</tocref>
|
||||
-->
|
||||
</tocroot>
|
||||
@@ -0,0 +1,64 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
WARNING!
|
||||
This file is copied to all help directories. If you change this file, you must copy it
|
||||
to each src/main/help/help/shared directory.
|
||||
|
||||
|
||||
Java Help Note: JavaHelp does not accept sizes (like in 'margin-top') in anything but
|
||||
px (pixel) or with no type marking.
|
||||
|
||||
*/
|
||||
|
||||
body { margin-bottom: 50px; margin-left: 10px; margin-right: 10px; margin-top: 10px; } /* some padding to improve readability */
|
||||
li { font-family:times new roman; font-size:14pt; }
|
||||
h1 { color:#000080; font-family:times new roman; font-size:36pt; font-style:italic; font-weight:bold; text-align:center; }
|
||||
h2 { margin: 10px; margin-top: 20px; color:#984c4c; font-family:times new roman; font-size:18pt; font-weight:bold; }
|
||||
h3 { margin-left: 10px; margin-top: 20px; color:#0000ff; font-family:times new roman; `font-size:14pt; font-weight:bold; }
|
||||
h4 { margin-left: 10px; margin-top: 20px; font-family:times new roman; font-size:14pt; font-style:italic; }
|
||||
|
||||
/*
|
||||
P tag code. Most of the help files nest P tags inside of blockquote tags (the was the
|
||||
way it had been done in the beginning). The net effect is that the text is indented. In
|
||||
modern HTML we would use CSS to do this. We need to support the Ghidra P tags, nested in
|
||||
blockquote tags, as well as naked P tags. The following two lines accomplish this. Note
|
||||
that the 'blockquote p' definition will inherit from the first 'p' definition.
|
||||
*/
|
||||
p { margin-left: 40px; font-family:times new roman; font-size:14pt; }
|
||||
blockquote p { margin-left: 10px; }
|
||||
|
||||
p.providedbyplugin { color:#7f7f7f; margin-left: 10px; font-size:14pt; margin-top:100px }
|
||||
p.ProvidedByPlugin { color:#7f7f7f; margin-left: 10px; font-size:14pt; margin-top:100px }
|
||||
p.relatedtopic { color:#800080; margin-left: 10px; font-size:14pt; }
|
||||
p.RelatedTopic { color:#800080; margin-left: 10px; font-size:14pt; }
|
||||
|
||||
/*
|
||||
We wish for a tables to have space between it and the preceding element, so that text
|
||||
is not too close to the top of the table. Also, nest the table a bit so that it is clear
|
||||
the table relates to the preceding text.
|
||||
*/
|
||||
table { margin-left: 20px; margin-top: 10px; width: 80%;}
|
||||
td { font-family:times new roman; font-size:14pt; vertical-align: top; }
|
||||
th { font-family:times new roman; font-size:14pt; font-weight:bold; background-color: #EDF3FE; }
|
||||
|
||||
/*
|
||||
Code-like formatting for things such as file system paths and proper names of classes,
|
||||
methods, etc. To apply this to a file path, use this syntax:
|
||||
<CODE CLASS="path">...</CODE>
|
||||
*/
|
||||
code { color: black; font-weight: bold; font-family: courier new, monospace; font-size: 14pt; white-space: nowrap; }
|
||||
code.path { color: #4682B4; font-weight: bold; font-family: courier new, monospace; font-size: 14pt; white-space: nowrap; }
|
||||
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<META name="generator" content=
|
||||
"HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net">
|
||||
<META http-equiv="Content-Language" content="en-us">
|
||||
<META http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
||||
<META name="GENERATOR" content="Microsoft FrontPage 4.0">
|
||||
<META name="ProgId" content="FrontPage.Editor.Document">
|
||||
|
||||
<TITLE>Skeleton Help File for a Module</TITLE>
|
||||
<LINK rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
|
||||
</HEAD>
|
||||
|
||||
<BODY>
|
||||
<H1><a name="HelpAnchor"></a>Skeleton Help File for a Module</H1>
|
||||
|
||||
<P>This is a simple skeleton help topic. For a better description of what should and should not
|
||||
go in here, see the "sample" Ghidra extension in the Extensions/Ghidra directory, or see your
|
||||
favorite help topic. In general, language modules do not have their own help topics.</P>
|
||||
</BODY>
|
||||
</HTML>
|
||||
43
deepcut/deepcut-ghidra/src/main/java/deepcut/Cut.java
Normal file
43
deepcut/deepcut-ghidra/src/main/java/deepcut/Cut.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package deepcut;
|
||||
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
class Cut {
|
||||
public String address;
|
||||
public String name;
|
||||
|
||||
public Cut(Program program, String address, String name) {
|
||||
this.address = address;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.address.toString() + ":" + this.name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package deepcut;
|
||||
|
||||
import ghidra.app.services.AbstractAnalyzer;
|
||||
import ghidra.app.services.AnalysisPriority;
|
||||
import ghidra.app.services.AnalyzerType;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.opinion.ElfLoader;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.CircularDependencyException;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.program.model.symbol.SymbolTable;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
/**
|
||||
* TODO: Provide class-level documentation that describes what this analyzer does.
|
||||
*/
|
||||
public class DeepCutAnalyzer extends AbstractAnalyzer {
|
||||
private final static String NAME = "Deepcut";
|
||||
private final static String DESCRIPTION = "Uses the deepcut algorithm to find module boundaries.";
|
||||
|
||||
private final static String OPTION_NAME_PYTHON_EXEC = "Python Executable";
|
||||
private final static String OPTION_DESCRIPTION_PYTHON_EXEC = "";
|
||||
private final static String OPTION_DEFAULT_PYTHON_EXEC = "/projects/venv/bin/python3";
|
||||
private String pythonExec = OPTION_DEFAULT_PYTHON_EXEC;
|
||||
|
||||
public DeepCutAnalyzer() {
|
||||
super(NAME, DESCRIPTION, AnalyzerType.FUNCTION_ANALYZER);
|
||||
setDefaultEnablement(false);
|
||||
setPriority(AnalysisPriority.REFERENCE_ANALYSIS.after());
|
||||
setPrototype();
|
||||
setSupportsOneTimeAnalysis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDefaultEnablement(Program program) {
|
||||
// Only supports one-time analysis.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAnalyze(Program program) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerOptions(Options options, Program program) {
|
||||
options.registerOption(OPTION_NAME_PYTHON_EXEC, pythonExec,
|
||||
null, OPTION_DESCRIPTION_PYTHON_EXEC);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void optionsChanged(Options options, Program program) {
|
||||
pythonExec = options.getString(OPTION_NAME_PYTHON_EXEC, pythonExec);
|
||||
}
|
||||
|
||||
private boolean checkError(DeepCutPython deepcut, MessageLog log)
|
||||
{
|
||||
String error = deepcut.readProcessError();
|
||||
if (!error.isEmpty()) {
|
||||
log.appendMsg(error);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
|
||||
throws CancelledException {
|
||||
|
||||
DeepCutPython deepcut = new DeepCutPython(pythonExec);
|
||||
FunctionCallGraph fcg = new FunctionCallGraph(program, monitor);
|
||||
|
||||
try {
|
||||
deepcut.startProcess();
|
||||
|
||||
if (checkError(deepcut, log)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
deepcut.writeProcess(fcg.toJson() + "\n");
|
||||
deepcut.waitFor();
|
||||
|
||||
if (checkError(deepcut, log)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String cuts_json = deepcut.readProcessOutput();
|
||||
|
||||
|
||||
Cut[] cuts = new GsonBuilder().create().fromJson(cuts_json, Cut[].class);
|
||||
|
||||
int i = 0;
|
||||
for (FunctionInfo fi : fcg.getFunctionInfos()) {
|
||||
AddressFactory af = program.getAddressFactory();
|
||||
Address cutAddress = af.getAddress(cuts[i].address);
|
||||
|
||||
if (fi.getAddress().compareTo(cutAddress) == -1) {
|
||||
addNamespace(program, "object" + i, fi.getFunction());
|
||||
} else {
|
||||
i++;
|
||||
addNamespace(program, "object" + i, fi.getFunction());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.appendException(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void addNamespace(Program program, String name, Function function)
|
||||
throws DuplicateNameException, InvalidInputException, CircularDependencyException {
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
Namespace namespace = null;
|
||||
|
||||
namespace = symbolTable.getNamespace(name, null);
|
||||
if(namespace == null) {
|
||||
namespace = symbolTable.createNameSpace(null, name,
|
||||
SourceType.USER_DEFINED);
|
||||
}
|
||||
|
||||
function.setParentNamespace(namespace);
|
||||
}
|
||||
|
||||
}
|
||||
102
deepcut/deepcut-ghidra/src/main/java/deepcut/DeepCutPython.java
Normal file
102
deepcut/deepcut-ghidra/src/main/java/deepcut/DeepCutPython.java
Normal file
@@ -0,0 +1,102 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package deepcut;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ghidra.framework.Application;
|
||||
|
||||
public class DeepCutPython {
|
||||
public Runtime runtime;
|
||||
public String pythonExec;
|
||||
|
||||
public Process process;
|
||||
public OutputStream stdin;
|
||||
public InputStream stdout;
|
||||
public InputStream stderr;
|
||||
|
||||
public DeepCutPython(String pythonExec) {
|
||||
this.pythonExec = pythonExec;
|
||||
this.runtime = Runtime.getRuntime();
|
||||
}
|
||||
|
||||
public void startProcess() throws IOException {
|
||||
String pythonFile = Application.getModuleDataFile("deepcut.py").toString();
|
||||
String modelFile = Application.getModuleDataFile("model_weights_1.p").toString();
|
||||
|
||||
//pythonFile = "/Users/desteaj1/Programs/AMP/deepcut/src/deepcut";
|
||||
String[] exec = {pythonExec, pythonFile, modelFile};
|
||||
|
||||
process = runtime.exec(exec);
|
||||
|
||||
// Yes this is confusing. stdin is a Java OutputStream, stdin is an InputStream
|
||||
stdin = process.getOutputStream();
|
||||
stdout = process.getInputStream();
|
||||
stderr = process.getErrorStream();
|
||||
}
|
||||
|
||||
public void waitFor() throws InterruptedException {
|
||||
process.waitFor();
|
||||
}
|
||||
|
||||
public void writeProcess(String data) throws IOException {
|
||||
writeProcess(data.getBytes());
|
||||
}
|
||||
|
||||
public void writeProcess(byte[] data) throws IOException {
|
||||
stdin.write(data);
|
||||
}
|
||||
|
||||
public String readProcessOutput() {
|
||||
return readProcess(stdout);
|
||||
|
||||
}
|
||||
|
||||
public String readProcessError() {
|
||||
return readProcess(stderr);
|
||||
|
||||
}
|
||||
|
||||
private String readProcess(InputStream stream) {
|
||||
String result = "";
|
||||
|
||||
try {
|
||||
if (stream.available() > 0 ) {
|
||||
result = new BufferedReader(new InputStreamReader(stream))
|
||||
.lines().collect(Collectors.joining("\n"));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
91
deepcut/deepcut-ghidra/src/main/java/deepcut/EdgeInfo.java
Normal file
91
deepcut/deepcut-ghidra/src/main/java/deepcut/EdgeInfo.java
Normal file
@@ -0,0 +1,91 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package deepcut;
|
||||
|
||||
class EdgeInfo {
|
||||
private FunctionInfo src;
|
||||
private FunctionInfo dst;
|
||||
|
||||
private int multiplicity;
|
||||
|
||||
/*
|
||||
distance between the two functions,
|
||||
either in terms of address
|
||||
or number of functions in between.
|
||||
*/
|
||||
private double addressDistance;
|
||||
private double indexDistance;
|
||||
|
||||
private boolean isSelfCall;
|
||||
|
||||
public EdgeInfo(FunctionInfo src, FunctionInfo dst, int multiplicity) {
|
||||
this.src = src;
|
||||
this.dst = dst;
|
||||
|
||||
this.multiplicity = multiplicity;
|
||||
|
||||
this.addressDistance = (double) dst.getAddress().subtract(src.getAddress());
|
||||
this.indexDistance = (double) dst.getAddressIndex() - src.getAddressIndex();
|
||||
}
|
||||
|
||||
public FunctionInfo getSrc() {
|
||||
return src;
|
||||
}
|
||||
|
||||
public FunctionInfo getDst() {
|
||||
return dst;
|
||||
}
|
||||
|
||||
public int getMultiplicity() {
|
||||
return multiplicity;
|
||||
}
|
||||
|
||||
public double getAddressDistance() {
|
||||
return addressDistance;
|
||||
}
|
||||
|
||||
public double getIndexDistance() {
|
||||
return indexDistance;
|
||||
}
|
||||
|
||||
public boolean getisSelfCall() {
|
||||
return isSelfCall;
|
||||
}
|
||||
|
||||
public long getSrcAddressIndex() {
|
||||
return src.getAddressIndex();
|
||||
}
|
||||
|
||||
public long getDstAddressIndex() {
|
||||
return dst.getAddressIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%-20s -> %20-s\t#%d", src.getName(),
|
||||
dst.getName(), multiplicity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package deepcut;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
class EdgeInfoSerializer implements JsonSerializer<EdgeInfo> {
|
||||
@Override
|
||||
public JsonElement serialize(EdgeInfo src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
JsonObject obj = new JsonObject();
|
||||
|
||||
obj.addProperty("src_index", src.getSrcAddressIndex());
|
||||
obj.addProperty("dst_index", src.getDstAddressIndex());
|
||||
obj.addProperty("multiplicity", src.getMultiplicity());
|
||||
obj.addProperty("addr_distance", src.getAddressDistance());
|
||||
obj.addProperty("index_distance", src.getIndexDistance());
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package deepcut;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.annotations.*;
|
||||
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.program.flatapi.FlatProgramAPI;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.FunctionIterator;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.RefType;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
|
||||
class FunctionCallGraph {
|
||||
private TaskMonitor monitor;
|
||||
private Program program;
|
||||
private FlatProgramAPI api;
|
||||
|
||||
// map from ghidra functions to my function class.
|
||||
public Map<Function, FunctionInfo> functionMap;
|
||||
|
||||
// list of functions, sorted by address
|
||||
@Expose(serialize = true)
|
||||
@SerializedName(value="functions")
|
||||
public List<FunctionInfo> functionList;
|
||||
|
||||
// Adjacency list of edges
|
||||
@Expose(serialize = true)
|
||||
@SerializedName(value="edges")
|
||||
public List<EdgeInfo> edgeList;
|
||||
|
||||
|
||||
public FunctionCallGraph(Program program, TaskMonitor monitor) {
|
||||
this.program = program;
|
||||
this.monitor = monitor;
|
||||
api = new FlatProgramAPI(program);
|
||||
|
||||
functionList = new ArrayList<FunctionInfo>();
|
||||
functionMap = new HashMap<Function, FunctionInfo>();
|
||||
edgeList = new ArrayList<EdgeInfo>();
|
||||
|
||||
createListOfFunctions();
|
||||
createListOfEdges();
|
||||
}
|
||||
|
||||
private void createListOfFunctions() {
|
||||
// Returns an iterator over all non-external functions in address (entry point) order.
|
||||
FunctionIterator iter = program.getFunctionManager().getFunctions(true);
|
||||
|
||||
int index = 0;
|
||||
while (iter.hasNext()) {
|
||||
Function function = iter.next();
|
||||
|
||||
FunctionInfo func_info = new FunctionInfo(function);
|
||||
func_info.setAddressIndex(index++);
|
||||
|
||||
functionList.add(func_info);
|
||||
functionMap.put(function, func_info);
|
||||
}
|
||||
}
|
||||
|
||||
private void createListOfEdges() {
|
||||
for (FunctionInfo func_info : functionList) {
|
||||
Function function = func_info.getFunction();
|
||||
|
||||
Map<Function, Integer> hm = getCountCallingFunctions(function);
|
||||
|
||||
for (Map.Entry<Function, Integer> val : hm.entrySet()) {
|
||||
Function src = val.getKey();
|
||||
int multiplicity = val.getValue();
|
||||
|
||||
// no idea why, but sometimes `src` is null.
|
||||
if (src == null) continue;
|
||||
|
||||
// set the `is_recursive` flag if the function calls itself
|
||||
if(function.equals(src)) {
|
||||
func_info.setIsRecursive(true);
|
||||
continue;
|
||||
}
|
||||
|
||||
// create the edge and add it to each list.
|
||||
FunctionInfo src_func_info = functionMap.get(src);
|
||||
EdgeInfo edge_info = new EdgeInfo(src_func_info, func_info, multiplicity);
|
||||
edgeList.add(edge_info);
|
||||
func_info.addIncomingEdge(edge_info);
|
||||
src_func_info.addOutgoingEdge(edge_info);
|
||||
}
|
||||
// remove the recursive call, if applicable.
|
||||
// Note: does nothing if `function` not in `hm`
|
||||
hm.remove(function);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
return a hashmap of the number of times each function calls this function.
|
||||
*/
|
||||
private Map<Function, Integer> getCountCallingFunctions(Function function) {
|
||||
|
||||
// hashmap to store the frequency of element
|
||||
Map<Function, Integer> hm = new HashMap<Function, Integer>();
|
||||
|
||||
/*
|
||||
first populate the hashmap with all the calling functions.
|
||||
this is needed b/c `getCallingFunctions` returns some functions which
|
||||
`getReferencesTo` doesn't pick up on.
|
||||
|
||||
I think this is b/c `getCallingFunctions` just tracks any xref.
|
||||
*/
|
||||
Set<Function> calling_funcs = function.getCallingFunctions(monitor);
|
||||
|
||||
for (Function f : calling_funcs) {
|
||||
hm.put(f, 0);
|
||||
}
|
||||
|
||||
// then populate the counts
|
||||
Address entryPoint = function.getEntryPoint();
|
||||
Reference[] references = api.getReferencesTo(entryPoint);
|
||||
|
||||
ArrayList<Function> func_list = new ArrayList<Function>();
|
||||
|
||||
for(Reference r : references) {
|
||||
RefType rt = r.getReferenceType();
|
||||
boolean xref_is_call = rt.isCall() || rt.isJump();
|
||||
if (xref_is_call) {
|
||||
Address toAddress = r.getFromAddress();
|
||||
Function func = api.getFunctionContaining(toAddress);
|
||||
func_list.add(func);
|
||||
}
|
||||
}
|
||||
|
||||
for (Function f : func_list) {
|
||||
Integer j = hm.get(f);
|
||||
hm.put(f, (j == null) ? 1 : j + 1);
|
||||
}
|
||||
|
||||
return hm;
|
||||
}
|
||||
|
||||
public List<FunctionInfo> getFunctionInfos() {
|
||||
return functionList;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
Gson gson = new GsonBuilder()
|
||||
.excludeFieldsWithoutExposeAnnotation()
|
||||
.registerTypeAdapter(FunctionInfo.class, new FunctionInfoSerializer())
|
||||
.registerTypeAdapter(EdgeInfo.class, new EdgeInfoSerializer())
|
||||
.create();
|
||||
|
||||
return gson.toJson(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder str = new StringBuilder();
|
||||
str.append("Function List:\n");
|
||||
|
||||
for(FunctionInfo fi : functionList) {
|
||||
str.append("{\"name\": \"" + fi.getName() +
|
||||
"\", \"addr\": \"0x" + fi.getAddress() +
|
||||
"\", \"idx\": " + fi.getAddressIndex() +
|
||||
", \"num_incoming_edges\": " + fi.getIncomingEdges().size() +
|
||||
", \"num_outgoing_edges\": " + fi.getOutgoingEdges().size() + "}\n");
|
||||
}
|
||||
|
||||
str.append("\nEdge List:\n");
|
||||
for(EdgeInfo ei : edgeList) {
|
||||
str.append("{\"src_idx\": " + ei.getSrc().getAddressIndex() +
|
||||
", \"dst_idx\": " + ei.getDst().getAddressIndex() +
|
||||
", \"multiplicity\": " + ei.getMultiplicity() +
|
||||
", \"addr_dst\": " + ei.getAddressDistance() +
|
||||
", \"idx_dst\": " + ei.getIndexDistance() + "}\n");
|
||||
}
|
||||
|
||||
return str.toString();
|
||||
}
|
||||
}
|
||||
129
deepcut/deepcut-ghidra/src/main/java/deepcut/FunctionInfo.java
Normal file
129
deepcut/deepcut-ghidra/src/main/java/deepcut/FunctionInfo.java
Normal file
@@ -0,0 +1,129 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package deepcut;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
|
||||
class FunctionInfo {
|
||||
private Function function;
|
||||
|
||||
private Address address;
|
||||
private long addressIndex;
|
||||
|
||||
private String name;
|
||||
|
||||
// `true` if the function is either external or a thunk function.
|
||||
private boolean isExternalThunk;
|
||||
|
||||
private List<EdgeInfo> incomingEdges;
|
||||
private List<EdgeInfo> outgoingEdges;
|
||||
|
||||
// `true` if the function ever calls itself
|
||||
private boolean isRecursive;
|
||||
|
||||
public FunctionInfo(Function function) {
|
||||
this.function = function;
|
||||
address = function.getEntryPoint();
|
||||
name = function.getName();
|
||||
|
||||
// will be set in a later pass
|
||||
addressIndex = -1;
|
||||
isRecursive = false;
|
||||
|
||||
isExternalThunk = function.isThunk() || function.isExternal() ||
|
||||
(!function.getParentNamespace().isGlobal());
|
||||
|
||||
incomingEdges = new ArrayList<EdgeInfo>();
|
||||
outgoingEdges = new ArrayList<EdgeInfo>();
|
||||
}
|
||||
|
||||
public void addIncomingEdge(EdgeInfo edge) {
|
||||
incomingEdges.add(edge);
|
||||
}
|
||||
|
||||
public List<EdgeInfo> getIncomingEdges() {
|
||||
return incomingEdges;
|
||||
}
|
||||
|
||||
public int getIncomingEdgeSize() {
|
||||
return incomingEdges.size();
|
||||
}
|
||||
|
||||
public void addOutgoingEdge(EdgeInfo edge) {
|
||||
outgoingEdges.add(edge);
|
||||
}
|
||||
|
||||
public List<EdgeInfo> getOutgoingEdges() {
|
||||
return outgoingEdges;
|
||||
}
|
||||
|
||||
public int getOutgoingEdgeSize() {
|
||||
return outgoingEdges.size();
|
||||
}
|
||||
|
||||
public Function getFunction() {
|
||||
return function;
|
||||
}
|
||||
|
||||
public void setIsRecursive(boolean val) {
|
||||
isRecursive = val;
|
||||
}
|
||||
|
||||
public boolean getIsRecursive() {
|
||||
return isRecursive;
|
||||
}
|
||||
|
||||
public void setAddress(Address address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public Address getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddressIndex(int index) {
|
||||
this.addressIndex = index;
|
||||
}
|
||||
|
||||
public long getAddressIndex() {
|
||||
return addressIndex;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getFunction().getName() + " " + address +
|
||||
" (" + addressIndex + ")";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/* ###
|
||||
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
* (JHU/APL).
|
||||
*
|
||||
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
|
||||
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
|
||||
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
|
||||
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
|
||||
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
|
||||
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
|
||||
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
|
||||
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
|
||||
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
|
||||
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
|
||||
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
|
||||
* PROFITS.
|
||||
*
|
||||
* This material is based upon work supported by the Defense Advanced Research
|
||||
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
|
||||
* under Contract Number N66001-20-C-4024.
|
||||
*
|
||||
* HAVE A NICE DAY.
|
||||
*/
|
||||
|
||||
package deepcut;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
class FunctionInfoSerializer implements JsonSerializer<FunctionInfo> {
|
||||
@Override
|
||||
public JsonElement serialize(FunctionInfo src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
JsonObject obj = new JsonObject();
|
||||
obj.addProperty("name", src.getName());
|
||||
obj.addProperty("addr", src.getAddress().toString());
|
||||
obj.addProperty("index", src.getAddressIndex());
|
||||
obj.addProperty("num_incoming_edges", src.getIncomingEdges().size());
|
||||
obj.addProperty("num_outgoing_edges", src.getOutgoingEdges().size());
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
The "src/resources/images" directory is intended to hold all image/icon files used by
|
||||
this module.
|
||||
2
deepcut/deepcut-ghidra/src/test/java/README.test.txt
Normal file
2
deepcut/deepcut-ghidra/src/test/java/README.test.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
The "test" directory is intended to hold unit test cases. The package structure within
|
||||
this folder should correspond to that found in the "src" folder.
|
||||
Reference in New Issue
Block a user