Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
fmagin committed Dec 15, 2021
1 parent 65a9c27 commit dcc069e
Show file tree
Hide file tree
Showing 13 changed files with 443 additions and 0 deletions.
29 changes: 29 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Main

on:
push:
tags:
- 'v*.*.*'

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 1.11
uses: actions/setup-java@v1
with:
java-version: 1.11
- uses: er28-0652/setup-ghidra@master
with:
version: "10.1"

- name: Build with Gradle
run: gradle buildExtension

- name: Release
uses: softprops/action-gh-release@v1
with:
files: ./dist/*zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
40 changes: 40 additions & 0 deletions .github/workflows/test_extension.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# This workflow will build a Java project with Gradle
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle

name: Test

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up JDK 1.11
uses: actions/setup-java@v1
with:
java-version: 1.11
- uses: er28-0652/setup-ghidra@master
with:
version: "10.1"

- name: Build Extension
run: gradle buildExtension

- name: Upload built extension as artifact for debugging
uses: actions/upload-artifact@v2
with:
path: ./dist/*zip
retention-days: 1

- name: Install Extension
run: unzip ./dist/*zip -d $GHIDRA_INSTALL_DIR/Ghidra/Extensions

- name: Run Tests
run: echo "Execute your tests here!"
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
build/
.gradle/
gradlew
gradlew.bat
lib/*jar
16 changes: 16 additions & 0 deletions .idea/fileTemplates/GhidraKotlinScript.kt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions .idea/runConfigurations/Run_Ghidra.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Ghidra Extension in Kotlin using IntelliJ IDEA

> Write a Ghidra Extension without using Java or Eclipse!
## Setup

* Hit `Use this template` at the top of the repo on GitHub
* Clone the new repo to your development machine
* Add the line `GHIDRA_INSTALL_DIR=/path/to/your/ghidra_10.1_PUBLIC/` to `$HOME/.gradle/gradle.properties`
* Open IntelliJ, create a new `Project from Existing Sources...` and select the `build.gradle`
* If you are using the [Kotlin Jupyter Plugin](https://github.com/GhidraJupyter/ghidra-jupyter-kotlin) uncomment the line in the `dependencies` block in the `build.gradle`
* Wait for IntelliJ to finish indexing and fetching dependencies, hit the build button, and then run Ghidra


## Features

* Gradle Config that works out of the box with IntelliJ
* IntelliJ IDEA Run Configuration for debugging of the extension
* If you have are using the [Kotlin Jupyter Plugin](https://github.com/GhidraJupyter/ghidra-jupyter-kotlin) you can also set breakpoints in the script file!
* GitHub CI files that
* make sure the extension at least builds for each PR
* will automatically build a release and publish it on GitHub if a commit is tagged with a version matching `vX.X.X`, e.g. `v1.2.3`/`v1.2.0` (`v1.2` doesn't work!)


## Additional Development Tips

These aspects can not be included in the repo files itself, but make development smoother.

### Thread Breakpoints

Make sure that you use breakpoints that only suspend the thread, and not everything.
This means that the breakpoint will only suspend the thread that is currently running the analysis or the script,
and the GUI will keep working.
* Set a breakpoint, right-click the icon, and in the `Suspend` line select `Thread` instead of `All`
* IntelliJ IDEA will suggest making this the default, click this too


### Use Scripts and the Jupyter Kernel to prototype ideas

With the [Kotlin Jupyter Plugin](https://github.com/GhidraJupyter/ghidra-jupyter-kotlin) you can test your new ideas first.
IntelliJ IDEA can do hot reloading of classes, but this has limits and then requires an IDE restart,
which take an annoying amount of time. The QT Console only is fairly basic, but the Jupyter Notebook uses nearly the same
code analysis engine as IntelliJ itself.

### Automatic conversion to Kotlin

* pasting Java code into a Kotlin file you will get the suggestion for this to be converted and then pasted
* right-click `.java` file in the Project Tree there is an action at the very bottom to convert the entire file


## Issues

If any step in this process doesn't work as described in the README, please open an issue on GitHub.
I have only tested this on Linux so there might be some aspects that work differently on macOS or Windows, though these
should be minor.
67 changes: 67 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Builds a Ghidra Extension for a given Ghidra installation.
//
// An absolute path to the Ghidra installation directory must be supplied
// To make this work well with IntelliJ this should be automatically resolve able and NOT an environmental variable
// You can either:
// * use the `gradle.properties` file in the extension folder to set it for this project
// * add the line `GHIDRA_INSTALL_DIR=/path/to/ghidras/ghidra_10.1_PUBLIC/` to your global config (`.gradle/gradle.properties` in your home folder)


plugins {
id 'org.jetbrains.kotlin.jvm' version "1.6.0"
id 'idea'
}

repositories {
mavenCentral()
}

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

// Set the JVM target to 11, as described in https://stackoverflow.com/a/44297713/13220684
// Ghidra requires 11 and the buildExtension.gradle sets this for Java
// IntelliJ will complain about the discrepancy between the Java and the Kotlin target otherwise
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
kotlinOptions {
jvmTarget = "11"
}
}

dependencies {
// If you are using the Ghidra Jupyter Plugin for Kotlin, add the following line to declare it as a dependency
// This allows using the extension methods and makes sure that the required Kotlin libraries are present and not
// conflicting
// api fileTree(dir: ghidraInstallDir + 'Ghidra/Extensions/GhidraJupyterKotlin/', include: "**/*.jar")
}

// Make it explicit that the compilation depends on some libraries in the `lib` folder,
// otherwise gradle will issue warnings
compileKotlin.dependsOn(copyDependencies)

// Add the ghidra_scripts directory as an additional source set, so IntelliJ IDEA treats the scripts there as part of
// the module
// this means that your plugin code is available for code completion and analysis and that all dependencies of the module
// are inherited
sourceSets {
main {
kotlin {
srcDirs 'ghidra_scripts'
}
}
}
5 changes: 5 additions & 0 deletions extension.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name=@extname@
description=Kotlin Example Extension.
author=Florian Magin
createdOn=2021-12-15
version=@extversion@
13 changes: 13 additions & 0 deletions ghidra_scripts/KotlinExtensionExampleScript.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SCRIPT DESCRIPTION
//@category Examples
//@toolbar world.png

import ghidra.app.script.GhidraScript

@Suppress("unused")
class KotlinExtensionExampleScript : GhidraScript() {
@Throws(Exception::class)
override fun run() {
TODO("Script code goes here")
}
}
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#GHIDRA_INSTALL_DIR=/path/to/ghidras/ghidra_10.1_PUBLIC/
Empty file added lib/.gitkeep
Empty file.
24 changes: 24 additions & 0 deletions src/main/kotlin/ghidra/examples/HelloWorldService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* ###
* 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 ghidra.examples

import ghidra.examples.KitchenSinkPlugin
import ghidra.framework.plugintool.ServiceInfo

@ServiceInfo(defaultProvider = [KitchenSinkPlugin::class])
interface HelloWorldService {
fun sayHello()
}
Loading

0 comments on commit dcc069e

Please sign in to comment.