I started messing around with Ghidra for some research I am currently doing
which I will talk about in a future post. I needed to develop a script for
Ghidra but the editor in it is too basic (a simple text box). I am really used
to developing using vim (I switched to neovim a couple weeks ago) and
decided I’d prefer to use it instead of developing inside of Ghidra.
My requirements for this were:
I had just setup CoC - Conquer of Completion
and figured I’d use its coc-java plugin to do this.
Without further ado, here is how I got it working.
Easiest way I found so far to install plugins on neovim is using vim-plug.
Just download
plug.vim
and place it in $HOME/.local/share/nvim/site/autoload/plug.vim.
Edit your $HOME/.config/nvim/init.vim file and add to the start of it:
if has('nvim')
call plug#begin('~/.config/nvim/site/autoload/plug.vim')
" Plugins will be added here
call plug#end()
endif
You should create the $HOME/.config/nvim/site/autoload/plug.vim directory if
it doesn’t already exist.
Open neovim and try the :PlugStatus command. It should open a window with
the status of the plugins that are installed.
Installing CoC now is pretty simple. Just a matter of adding it to the list
of desired plugins in your init.vim file.
if has('nvim')
call plug#begin('~/.config/nvim/site/autoload/plug.vim')
" CoC for code completion with language servers
Plug 'neoclide/coc.nvim', {'branch': 'release'}
call plug#end()
endif
Just reload the init.vim file now (either by re-opening neovim or by
running :source /path/to/your/init.vim). After that just use the command
:PlugInstall to get CoC installed.
In order for CoC to work, you will need to jave both nodejs and yarn
installed on your machine. On Ubuntu just use the following apt command to
install those:
sudo apt install nodejs yarnpkg
CoC has many plugins for different languages. I am developing my Ghidra
scripts using Java so for that I need to install the
coc-java plugin.
After you have CoC installed, just run the following command on neovim to
install coc-java:
:CocInstall coc-java
That is it! This should be enough to get coc-java installed.
The coc-java plugin uses either a maven or a gradle configuration file
in order to know how to build the java file being edited. The classpath
configuration is what is necessary to find the dependencies for the source
being edited so that it can be built and so that code completion and the
linter work. I found it easier to use gradle for this as there was already a
gradle file in the Ghidra directory that I was able to use as a base.
For gradle I just downloaded the binary from
here. I downloaded the binary-only version
of it. At the time of this writing, the latest version was v6.3.
After downloading it, just unzip the file and place it somewhere you like on
your machine. In my case I put it on $HOME/software/gradle-6.3. All you have
to do then is add the $HOME/software/gradle-6.3/bin directory to your
$PATH variable.
export PATH=$PATH:$HOME/software/gradle-6.3/bin
I have Ghidra installed in the folder $HOME/software/ghidra_9.1.2.
By default Ghidra will look for its scripts in various directories. The
first one it looks at is $HOME/ghidra_scripts. This is where I am doing all
my script development.
In that directory, create a build.gradle file with the following contents:
apply plugin: 'java'
def ghidraDir = "/path/to/your/ghidra_9.1.2/Ghidra"
def ghidraProps = new Properties()
configurations {
helpPath
}
sourceSets {
main.java.srcDirs = ['./']
main.resources.srcDirs = ['./']
}
file(ghidraDir + "/application.properties").withReader { reader ->
ghidraProps.load(reader)
project.ext.ghidra_version = ghidraProps.getProperty('application.version')
project.ext.RELEASE_NAME = ghidraProps.getProperty('application.release.name')
project.ext.DISTRO_PREFIX = "ghidra_${ghidra_version}"
}
dependencies {
compile fileTree(dir: 'lib', include: "*.jar")
compile fileTree(dir: ghidraDir + '/Framework', include: "**/*.jar")
compile fileTree(dir: ghidraDir + '/Features', include: "**/*.jar")
helpPath fileTree(dir: ghidraDir + '/Features/Base', include: "**/Base.jar")
}
Don’t forget to change the ghidraDir definition in the build.gradle file
with the appropriate path where you have Ghidra installed.
In the same directory, create a .classpath file with the following contents:
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="bin/main" path="">
<attributes>
<attribute name="gradle_scope" value="main"/>
<attribute name="gradle_used_by_scope" value="main,test"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11/"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry kind="output" path="bin/default"/>
</classpath>
And now, in this same directory create a Test.java file with the following
contents:
//@category Gilgalab
import ghidra.app.script.GhidraScript;
import ghidra.util.Msg;
public class Test extends GhidraScript {
@Override
public void run() throws Exception {
try {
int int1 = askInt("integer 1", "enter integer 1");
int int2 = askInt("integer 2", "enter integer 2");
println("int1 + int2 = " + (int1 + int2));
}
catch (IllegalArgumentException iae) {
Msg.warn(this, "Error during headless processing: " + iae.toString());
}
}
}
This is how it looks like as you type your code:
Now open Ghidra and open the Script Manager (menu Window -> Script
Manager). In the new window displayed, click the Reload button and notice
that a folder called Gilgalab appeared in it.
Ghidra Script Manager
Inside that folder you can see the Test.java script. Click the Run button
and it should ask you for two numbers and should show you the result of the
sum of those numbers in the Console windows (menu Window -> Console).
Running the test script
After entering the value 10 for the first input and 20 for the second, the
result can be seen in the Console window (menu Window -> Console).
Console windows showing the result of the script execution