Initial commit of codecut-gui and deepcut from JHU/APL & DARPA open source review

This commit is contained in:
evm
2022-12-20 16:57:33 -05:00
parent 0373f7a84b
commit 34317e9d72
70 changed files with 9831 additions and 0 deletions

View File

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

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

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

View 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()

View File

@@ -0,0 +1,5 @@
name=@extname@
description=CodeCut / DeepCut GUI
author=JHU/APL
createdOn=
version=@extversion@

View File

@@ -0,0 +1 @@
Java source directory to hold module-specific Ghidra scripts.

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

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

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

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

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

View File

@@ -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; }

View File

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

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}

View File

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

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View 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 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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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 + "]";
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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) + "]";
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,2 @@
The "src/resources/images" directory is intended to hold all image/icon files used by
this module.

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

View File

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

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

View 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

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

View 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()

Binary file not shown.

View File

@@ -0,0 +1,5 @@
name=@extname@
description=Use the deepcut algorithm to find module boundaries.
author=JHU/APL
createdOn=
version=@extversion@

View File

@@ -0,0 +1 @@
Java source directory to hold module-specific Ghidra scripts.

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

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

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

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

View 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

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

View File

@@ -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; }

View File

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

View 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;
}
}

View File

@@ -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);
}
}

View 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;
}
}

View 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);
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View 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 + ")";
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,2 @@
The "src/resources/images" directory is intended to hold all image/icon files used by
this module.

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