├── extension ├── Module.manifest ├── extension.properties ├── src │ └── main │ │ └── java │ │ └── deeplinks │ │ ├── DeepLinksPlugin.java │ │ ├── DeepLinksToolPlugin.java │ │ └── SchemeHandlerServer.java └── build.gradle ├── img ├── install.png ├── configure.png ├── context-menu.png ├── drawio-object.png └── ghidra-deep-links-logo.png ├── os ├── mac │ ├── ghidra-deep-links-logo.icns │ ├── GhidraCommunicator.h │ ├── AppDelegate.h │ ├── main.m │ ├── AppDelegate.m │ ├── Info.plist │ └── GhidraCommunicator.m ├── linux │ ├── ghidra-opener.desktop │ └── push_to_socket └── win │ └── push_to_socket.ps1 ├── .gitignore ├── Dockerfile ├── docker-compose.yml ├── .github ├── ISSUE_TEMPLATE │ ├── FEATURE REQUEST.md │ └── BUG REPORT.md ├── workflows │ ├── main.yml │ └── mac_app.yml ├── CODE OF CONDUCT.md └── CONTRIBUTING.md ├── docker_build.py ├── install-offline.sh ├── install-offline.ps1 ├── BUILDING.md ├── install.ps1 ├── install.sh ├── README.md └── LICENSE /extension/Module.manifest: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foundryzero/ghidra-deep-links/HEAD/img/install.png -------------------------------------------------------------------------------- /img/configure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foundryzero/ghidra-deep-links/HEAD/img/configure.png -------------------------------------------------------------------------------- /img/context-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foundryzero/ghidra-deep-links/HEAD/img/context-menu.png -------------------------------------------------------------------------------- /img/drawio-object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foundryzero/ghidra-deep-links/HEAD/img/drawio-object.png -------------------------------------------------------------------------------- /img/ghidra-deep-links-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foundryzero/ghidra-deep-links/HEAD/img/ghidra-deep-links-logo.png -------------------------------------------------------------------------------- /os/mac/ghidra-deep-links-logo.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foundryzero/ghidra-deep-links/HEAD/os/mac/ghidra-deep-links-logo.icns -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | bin/ 3 | .gradle/ 4 | build/ 5 | .settings/ 6 | .project 7 | dist/ 8 | .pydevproject 9 | .classpath 10 | 11 | .DS_Store 12 | -------------------------------------------------------------------------------- /extension/extension.properties: -------------------------------------------------------------------------------- 1 | name=@extname@ 2 | description=Provides deep linking support for ghidra through the disas:// scheme 3 | author=Foundry Zero 4 | createdOn= 5 | version=@extversion@ 6 | -------------------------------------------------------------------------------- /os/linux/ghidra-opener.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Name=Disas Scheme Handler for Ghidra 4 | Exec=INSTALL_LOCATION/push_to_socket %u 5 | StartupNotify=false 6 | Hidden=true 7 | MimeType=x-scheme-handler/disas; 8 | -------------------------------------------------------------------------------- /os/win/push_to_socket.ps1: -------------------------------------------------------------------------------- 1 | $tcpconn = New-Object System.Net.Sockets.TcpClient("127.0.0.1", "5740") 2 | $tcpstream = $tcpconn.GetStream() 3 | 4 | $writer = New-Object System.IO.StreamWriter($tcpstream) 5 | $writer.AutoFlush = $true 6 | 7 | $writer.WriteLine($args[0]) 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gradle:jdk21 2 | 3 | RUN apt-get update && apt-get install python3-pip -y && rm -rf /var/lib/apt/lists/* 4 | RUN bash -c "AIOHTTP_NO_EXTENSIONS=1 pip3 install pygithub --break-system-packages" 5 | 6 | COPY docker_build.py /docker_build.py 7 | 8 | CMD [ "python3", "-u", "/docker_build.py" ] 9 | -------------------------------------------------------------------------------- /os/mac/GhidraCommunicator.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface GhidraCommunicator : NSObject 4 | 5 | - (void)open; 6 | - (void)close; 7 | - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)streamEvent; 8 | - (void)sendToGhidra:(NSString *)url; 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /os/mac/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface AppDelegate : NSObject 4 | 5 | - (void)handleURLEvent:(NSAppleEventDescriptor *)event 6 | withReplyEvent:(NSAppleEventDescriptor *)replyEvent; 7 | - (void)applicationWillFinishLaunching:(NSNotification *)aNotification; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | build.service: 3 | build: "." 4 | volumes: 5 | - "./extension:/home/gradle/ghidra-deep-links" 6 | - "./LICENSE:/home/gradle/ghidra-deep-links/LICENSE" 7 | environment: 8 | GHIDRA_VERSION: "${GHIDRA_VERSION}" 9 | working_dir: "/home/gradle/ghidra-deep-links" 10 | -------------------------------------------------------------------------------- /os/mac/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "AppDelegate.h" 3 | 4 | int main(int argc, const char *argv[]) { 5 | @autoreleasepool { 6 | AppDelegate *delegate = [[AppDelegate alloc] init]; 7 | NSApplication *app = [NSApplication sharedApplication]; 8 | app.delegate = delegate; 9 | NSLog(@"Starting GhidraDeepLinksHandler"); 10 | NSApplicationMain(argc, argv); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/FEATURE REQUEST.md: -------------------------------------------------------------------------------- 1 | ## Summary 2 | 3 | 4 | 5 | ## Motivation 6 | 7 | 8 | 9 | ## Describe alternatives you've considered 10 | 11 | 12 | 13 | ## Additional context 14 | 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/BUG REPORT.md: -------------------------------------------------------------------------------- 1 | ### Environment 2 | 3 | Please list the OS, version of ghidra and version of ghidra-deep-links under which this bug was experienced. 4 | 5 | ### Description 6 | 7 | 8 | 9 | ### Steps to Reproduce 10 | 11 | 1. 12 | 2. 13 | 3. 14 | 15 | **Expected behavior:** 16 | 17 | What you expect to happen 18 | 19 | **Actual behavior:** 20 | 21 | What actually happens 22 | 23 | ### Additional Information 24 | 25 | Any additional information, configuration or data that might be necessary to reproduce the issue. -------------------------------------------------------------------------------- /os/linux/push_to_socket: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | # Attempt to start ghidra if it's not already running 5 | if ! pgrep -f ghidra > /dev/null ; then 6 | $GHIDRA_HOME/ghidraRun 7 | fi 8 | 9 | # Wait for the socket to be created and for at least one line to be written to it 10 | # If ghidra doesn't start or our extension isn't loaded then the socket will never be created, so we set a timeout here. 11 | # The timeout must be long because the socket will only have the ready signal written once a project is loaded 12 | # which could depend on a user inputting credentials 13 | timeout 5m bash -c 'while ! head -n 1 < /dev/tcp/127.0.0.1/5740 ; do sleep 0.1; done' > /dev/null 14 | 15 | # Send the url 16 | echo $1 > /dev/tcp/127.0.0.1/5740 17 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | on: workflow_dispatch 2 | name: build 3 | jobs: 4 | build-matrix: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | matrix: 8 | ghidra_version: ['11.3.1', '11.3', '11.2.1', '11.2', '11.1.2', '11.1.1', '11.1', '11.0.3', '11.0.2', '11.0.1', '11.0', '10.4', '10.3.3', '10.3.2', '10.3.1', '10.3'] 9 | steps: 10 | - name: checkout repo 11 | uses: actions/checkout@v3 12 | 13 | - name: build 14 | env: 15 | GHIDRA_VERSION: ${{ matrix.ghidra_version }} 16 | run: docker compose up --exit-code-from build.service 17 | 18 | - name: export artifacts 19 | uses: actions/upload-artifact@v4 20 | with: 21 | name: dist-${{ matrix.ghidra_version }} 22 | path: extension/dist/*.zip 23 | -------------------------------------------------------------------------------- /os/mac/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | #import "GhidraCommunicator.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (void)handleURLEvent:(NSAppleEventDescriptor *)event 7 | withReplyEvent:(NSAppleEventDescriptor *)replyEvent { 8 | NSString *url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; 9 | NSLog(@"Got URL: %@", url); 10 | 11 | GhidraCommunicator *communicator = [[GhidraCommunicator alloc] init]; 12 | [communicator sendToGhidra:url]; 13 | 14 | exit(0); 15 | } 16 | 17 | - (void)applicationWillFinishLaunching:(NSNotification *)aNotification { 18 | [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self 19 | andSelector:@selector(handleURLEvent:withReplyEvent:) 20 | forEventClass:kInternetEventClass 21 | andEventID:kAEGetURL]; 22 | NSLog(@"GhidraDeepLinksHandler Started"); 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /docker_build.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import sys 4 | import urllib.request 5 | import zipfile 6 | 7 | from github import Github 8 | 9 | os.umask(0o000) 10 | g = Github() 11 | 12 | requested_version = os.environ["GHIDRA_VERSION"] 13 | requested_version_name = f"Ghidra {requested_version}" 14 | 15 | repo = g.get_repo("NationalSecurityAgency/ghidra") 16 | releases = repo.get_releases() 17 | 18 | print(f"Building for {requested_version_name}") 19 | 20 | for release in releases: 21 | if release.title == requested_version_name: 22 | print(f"Downloading {release.title}...") 23 | urllib.request.urlretrieve(release.assets[0].browser_download_url, "ghidra.zip") 24 | break 25 | 26 | print("Extracting ghidra.zip...") 27 | with zipfile.ZipFile("ghidra.zip", "r") as zf: 28 | zf.extractall("../ghidra/") 29 | 30 | os.remove("ghidra.zip") 31 | 32 | print("Building...") 33 | ret = subprocess.call( 34 | [ 35 | "gradle", 36 | f"-PGHIDRA_INSTALL_DIR={os.path.dirname(os.getcwd())}/ghidra/ghidra_{requested_version}_PUBLIC", 37 | ] 38 | ) 39 | 40 | sys.exit(ret) 41 | -------------------------------------------------------------------------------- /install-offline.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Installing Ghidra Deep Links..." 4 | 5 | if pgrep snapd > /dev/null ; then 6 | echo "Warning: Ghidra Deep Links does not work from programs using snaps." 7 | fi 8 | 9 | read -p ".desktop install location [~/.local/share/applications]:" INSTALL_LOCATION 10 | 11 | if [ -z "$INSTALL_LOCATION" ] ; then 12 | INSTALL_LOCATION="$HOME/.local/share/applications" 13 | fi 14 | 15 | echo "Install handler..." 16 | 17 | cp os/linux/ghidra-opener.desktop $INSTALL_LOCATION 18 | cp os/linux/push_to_socket $INSTALL_LOCATION 19 | sed -i -e "s|INSTALL_LOCATION|$INSTALL_LOCATION|g" $INSTALL_LOCATION/ghidra-opener.desktop 20 | chmod +x $INSTALL_LOCATION/push_to_socket 21 | 22 | echo "Setting handler as default for disas:// links..." 23 | 24 | xdg-mime default ghidra-opener.desktop x-scheme-handler/disas 25 | 26 | echo "Done." 27 | 28 | echo "Set GHIDRA_HOME to your ghidra installation to enable cold start link handling." 29 | 30 | echo "The next time you open ghidra, install the extension and enable the plugin in both the main Ghidra window and any tools (eg Code Browser) you use." 31 | -------------------------------------------------------------------------------- /install-offline.ps1: -------------------------------------------------------------------------------- 1 | Write-Output "Installing Ghidra Deep Links" 2 | Write-Output "This will install a handler for the disas:// uri scheme for the current user." 3 | $confirm = Read-Host "Are you sure you wish to continue? [y/N]" 4 | if (-not ($confirm -eq 'y')) { 5 | Return "Exiting..." 6 | } 7 | 8 | Copy-Item -Path ".\os\win\push_to_socket.ps1" -Destination "~\.ghidra\push_to_socket.ps1" 9 | 10 | Write-Output "Installing scheme handler..." 11 | 12 | Push-Location 13 | Set-Location -Path Registry::HKEY_CURRENT_USER\Software\Classes\ 14 | New-Item -Force -Path disas 15 | Set-ItemProperty -Path disas -Name "(default)" -Value "URL:Ghidra Protocol" 16 | New-ItemProperty -Force -Path disas -Name "URL Protocol" -PropertyType String 17 | New-Item -Force -Path disas\DefaultIcon 18 | New-Item -Force -Path disas\shell 19 | New-Item -Force -Path disas\shell\open 20 | New-Item -Force -Path disas\shell\open\command 21 | Set-ItemProperty -Path disas\shell\open\command -Name "(default)" -Value "powershell.exe -NoProfile -WindowStyle Minimized -ExecutionPolicy Bypass -File $HOME\.ghidra\push_to_socket.ps1 %1" 22 | Pop-Location 23 | 24 | Return "Done." 25 | -------------------------------------------------------------------------------- /BUILDING.md: -------------------------------------------------------------------------------- 1 | ### Requirements 2 | 3 | - Docker, Docker Compose (only if building with docker, see below) 4 | - Java JDK 17+ 5 | - Gradle 6 | 7 | # Docker 8 | 9 | ### Requirements 10 | 11 | - Docker 12 | - Docker Compose v2 13 | - A network connection on the build machine 14 | 15 | ``` 16 | git clone https://github.com/foundryzero/ghidra-deep-links.git 17 | cd ghidra-deep-links 18 | GHIDRA_VERSION=10.3.3 docker compose up --exit-code-from build.service 19 | ``` 20 | 21 | Prior to ghidra 10.3.2, ghidra plugins needed to be built against each new release of ghidra in order to be compatible. As of 10.3.2 it is possible to bypass the compatibility check, but it is still advisable to build the plugin for each specific version. To build for a different ghidra version, edit the `GHIDRA_VERSION` environment variable. 22 | 23 | # Manual 24 | 25 | ### Requirements 26 | 27 | - Java JDK 17+ 28 | - Gradle 29 | - Ghidra (of the version you're building against) 30 | 31 | ``` 32 | git clone https://github.com/foundryzero/ghidra-deep-links.git 33 | cd ghidra-deep-links/extension 34 | gradle -PGHIDRA_INSTALL_DIR=/path/to/ghidra/install 35 | ``` 36 | 37 | However you build, the built extension will be placed in `dist/` in the source directory. 38 | 39 | -------------------------------------------------------------------------------- /install.ps1: -------------------------------------------------------------------------------- 1 | Write-Output "Installing Ghidra Deep Links" 2 | Write-Output "This will install a handler for the disas:// uri scheme for the current user and download the latest version of the ghidra plugin." 3 | $confirm = Read-Host "Are you sure you wish to continue? [y/N]" 4 | if (-not ($confirm -eq 'y')) { 5 | Return "Exiting..." 6 | } 7 | 8 | Invoke-WebRequest -URI https://raw.githubusercontent.com/foundryzero/ghidra-deep-links/main/os/win/push_to_socket.ps1 -OutFile "~\.ghidra\push_to_socket.ps1" 9 | 10 | Write-Output "Installing scheme handler..." 11 | 12 | Push-Location 13 | Set-Location -Path Registry::HKEY_CURRENT_USER\Software\Classes\ 14 | New-Item -Force -Path disas 15 | Set-ItemProperty -Path disas -Name "(default)" -Value "URL:Ghidra Protocol" 16 | New-ItemProperty -Force -Path disas -Name "URL Protocol" -PropertyType String 17 | New-Item -Force -Path disas\DefaultIcon 18 | New-Item -Force -Path disas\shell 19 | New-Item -Force -Path disas\shell\open 20 | New-Item -Force -Path disas\shell\open\command 21 | Set-ItemProperty -Path disas\shell\open\command -Name "(default)" -Value "powershell.exe -NoProfile -WindowStyle Minimized -ExecutionPolicy Bypass -File $HOME\.ghidra\push_to_socket.ps1 %1" 22 | Pop-Location 23 | 24 | Return "Done." 25 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Installing Ghidra Deep Links..." 4 | 5 | if pgrep snapd > /dev/null; then 6 | echo "Warning: Ghidra Deep Links does not work from programs using snaps." 7 | fi 8 | 9 | read -p ".desktop install location [~/.local/share/applications]:" INSTALL_LOCATION 10 | 11 | if [ -z "$INSTALL_LOCATION" ] ; then 12 | INSTALL_LOCATION="$HOME/.local/share/applications" 13 | fi 14 | 15 | cd $INSTALL_LOCATION 16 | 17 | echo "Downloading handler..." 18 | 19 | curl -f -O "https://raw.githubusercontent.com/foundryzero/ghidra-deep-links/main/os/linux/ghidra-opener.desktop" 20 | sed -i -e "s|INSTALL_LOCATION|$INSTALL_LOCATION|g" ghidra-opener.desktop 21 | 22 | curl -f -O "https://raw.githubusercontent.com/foundryzero/ghidra-deep-links/main/os/linux/push_to_socket" 23 | chmod +x push_to_socket 24 | 25 | echo "Setting handler as default for disas:// links..." 26 | 27 | xdg-mime default ghidra-opener.desktop x-scheme-handler/disas 28 | 29 | cd - > /dev/null 30 | 31 | echo "Done." 32 | 33 | echo "Set GHIDRA_HOME to your ghidra installation to enable cold start link handling." 34 | 35 | echo "The next time you open ghidra, install the extension and enable the plugin in both the main Ghidra window and any tools (eg Code Browser) you use." 36 | -------------------------------------------------------------------------------- /os/mac/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | main 9 | CFBundleIconFile 10 | ghidra-deep-links-logo.icns 11 | CFBundleIdentifier 12 | com.ghidra-deep-links-handler 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | GhidraDeepLinksHandler 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 20 23 | CFBundleURLTypes 24 | 25 | 26 | CFBundleURLName 27 | Ghidra Handler 28 | CFBundleURLSchemes 29 | 30 | disas 31 | 32 | 33 | 34 | LSUIElement 35 | 36 | 37 | -------------------------------------------------------------------------------- /os/mac/GhidraCommunicator.m: -------------------------------------------------------------------------------- 1 | #import "GhidraCommunicator.h" 2 | 3 | CFReadStreamRef readStream; 4 | CFWriteStreamRef writeStream; 5 | NSOutputStream *outputStream; 6 | 7 | @implementation GhidraCommunicator 8 | 9 | - (void)open { 10 | CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (CFStringRef)@"127.0.0.1", 5740, &readStream, &writeStream); 11 | 12 | if(!CFWriteStreamOpen(writeStream)) { 13 | NSLog(@"Failed to open writeStream"); 14 | return; 15 | } 16 | 17 | outputStream = (NSOutputStream *)writeStream; 18 | [outputStream retain]; 19 | [outputStream setDelegate:self]; 20 | [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 21 | [outputStream open]; 22 | } 23 | 24 | - (void)close { 25 | [outputStream close]; 26 | [outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 27 | [outputStream release]; 28 | outputStream = nil; 29 | } 30 | 31 | - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)streamEvent { 32 | switch(streamEvent) { 33 | case NSStreamEventErrorOccurred: 34 | NSLog(@"Cannot connect to Ghidra"); 35 | case NSStreamEventEndEncountered: 36 | [self close]; 37 | default: 38 | return; 39 | } 40 | } 41 | 42 | - (void)sendToGhidra:(NSString *)url { 43 | [self open]; 44 | 45 | uint8_t *buf = (uint8_t *)[[url stringByAppendingString:@"\n"] UTF8String]; 46 | NSLog(@"Attempt to send to Ghidra"); 47 | [outputStream write:buf maxLength:strlen((char *)buf)]; 48 | 49 | [self close]; 50 | } 51 | 52 | @end -------------------------------------------------------------------------------- /.github/workflows/mac_app.yml: -------------------------------------------------------------------------------- 1 | name: build mac middleware 2 | on: workflow_dispatch 3 | jobs: 4 | build: 5 | runs-on: macos-latest 6 | steps: 7 | - name: Checkout 8 | uses: actions/checkout@v3 9 | 10 | - name: Create output directories 11 | run: mkdir -p bundle/GhidraDeepLinksHandler.app/Contents/MacOS && mkdir -p bundle/GhidraDeepLinksHandler.app/Contents/Resources 12 | working-directory: os/mac 13 | 14 | - name: Copy Info.plist 15 | run: cp Info.plist bundle/GhidraDeepLinksHandler.app/Contents/Info.plist 16 | working-directory: os/mac 17 | 18 | - name: Copy icon 19 | run: cp ghidra-deep-links-logo.icns bundle/GhidraDeepLinksHandler.app/Contents/Resources/ghidra-deep-links-logo.icns 20 | working-directory: os/mac 21 | 22 | - name: Build x86_64 23 | run: clang -target x86_64-apple-macos10.12 -framework Cocoa -lobjc *.m -o main-x86_64 24 | working-directory: os/mac 25 | 26 | - name: Build ARM64 27 | run: clang -target arm64-apple-macos11 -framework Cocoa -lobjc *.m -o main-arm64 28 | working-directory: os/mac 29 | 30 | - name: Package universal binary 31 | run: lipo -create -output bundle/GhidraDeepLinksHandler.app/Contents/MacOS/main main-x86_64 main-arm64 32 | working-directory: os/mac 33 | 34 | - name: Create dmg 35 | run: hdiutil create /tmp/tmp.dmg -ov -volname "DeeplinksHandlerInstall" -fs HFS+ -srcfolder os/mac/bundle 36 | 37 | - name: Finalise dmg 38 | run: hdiutil convert /tmp/tmp.dmg -format UDZO -o GhidraDeepLinksHandler.dmg 39 | 40 | - name: Export artifacts 2 41 | uses: actions/upload-artifact@v4 42 | with: 43 | name: GhidraDeepLinksHandler 44 | path: GhidraDeepLinksHandler.dmg 45 | -------------------------------------------------------------------------------- /extension/src/main/java/deeplinks/DeepLinksPlugin.java: -------------------------------------------------------------------------------- 1 | package deeplinks; 2 | 3 | import ghidra.app.plugin.PluginCategoryNames; 4 | import ghidra.app.services.ProgramManager; 5 | import ghidra.framework.main.ApplicationLevelOnlyPlugin; 6 | import ghidra.framework.main.UtilityPluginPackage; 7 | import ghidra.framework.plugintool.Plugin; 8 | import ghidra.framework.plugintool.PluginInfo; 9 | import ghidra.framework.plugintool.PluginTool; 10 | import ghidra.framework.plugintool.util.PluginStatus; 11 | 12 | /** 13 | * Provides a plugin to catch disas:// deep links sent across a socket and 14 | * attempt to find open the files they point to. 15 | */ 16 | //@formatter:off 17 | @PluginInfo( 18 | status = PluginStatus.RELEASED, 19 | packageName = UtilityPluginPackage.NAME, 20 | category = PluginCategoryNames.NAVIGATION, 21 | shortDescription = "Handles incoming disas:// deep links.", 22 | description = "Handles incoming disas:// deep links.", 23 | // The services required actually are inside the subtools, not the main ghidra window itself, so we can't declare them as required here. 24 | servicesRequired = { }, 25 | eventsProduced = { } 26 | ) 27 | //@formatter:on 28 | public class DeepLinksPlugin extends Plugin implements ApplicationLevelOnlyPlugin { 29 | 30 | static final int PORT = 5740; // IANA unassigned as of 2023-07-28 31 | 32 | ProgramManager programManager; 33 | SchemeHandlerServer serverThread; 34 | 35 | /** 36 | * Constructs a new instance of the plugin. Called very early on in the 37 | * initialisation process. 38 | * 39 | * @param tool The tool that the plugin is being loaded into. 40 | */ 41 | public DeepLinksPlugin(PluginTool tool) { 42 | super(tool); 43 | 44 | // Create a thread to run our socket server on 45 | serverThread = new SchemeHandlerServer(this, PORT, tool); 46 | 47 | } 48 | 49 | /** 50 | * Start the server. 51 | * 52 | * Called when the plugin loader decides it's time to load our plugin. 53 | */ 54 | @Override 55 | public void init() { 56 | super.init(); 57 | 58 | serverThread.start(); 59 | } 60 | 61 | /** 62 | * Stop and cleanup the server. 63 | * 64 | * Called when the plugin gets unloaded. 65 | */ 66 | @Override 67 | public void dispose() { 68 | serverThread.cleanup(); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /extension/build.gradle: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | // Builds a Ghidra Extension for a given Ghidra installation. 17 | // 18 | // An absolute path to the Ghidra installation directory must be supplied either by setting the 19 | // GHIDRA_INSTALL_DIR environment variable or Gradle project property: 20 | // 21 | // > export GHIDRA_INSTALL_DIR= 22 | // > gradle 23 | // 24 | // or 25 | // 26 | // > gradle -PGHIDRA_INSTALL_DIR= 27 | // 28 | // Gradle should be invoked from the directory of the project to build. Please see the 29 | // application.gradle.version property in /Ghidra/application.properties 30 | // for the correction version of Gradle to use for the Ghidra installation you specify. 31 | 32 | //----------------------START "DO NOT MODIFY" SECTION------------------------------ 33 | def ghidraInstallDir 34 | 35 | if (System.env.GHIDRA_INSTALL_DIR) { 36 | ghidraInstallDir = System.env.GHIDRA_INSTALL_DIR 37 | } 38 | else if (project.hasProperty("GHIDRA_INSTALL_DIR")) { 39 | ghidraInstallDir = project.getProperty("GHIDRA_INSTALL_DIR") 40 | } 41 | 42 | if (ghidraInstallDir) { 43 | apply from: new File(ghidraInstallDir).getCanonicalPath() + "/support/buildExtension.gradle" 44 | } 45 | else { 46 | throw new GradleException("GHIDRA_INSTALL_DIR is not defined!") 47 | } 48 | //----------------------END "DO NOT MODIFY" SECTION------------------------------- 49 | 50 | repositories { 51 | // Declare dependency repositories here. This is not needed if dependencies are manually 52 | // dropped into the lib/ directory. 53 | // See https://docs.gradle.org/current/userguide/declaring_repositories.html for more info. 54 | // Ex: mavenCentral() 55 | } 56 | 57 | dependencies { 58 | // Any external dependencies added here will automatically be copied to the lib/ directory when 59 | // this extension is built. 60 | } 61 | 62 | // Exclude additional files from the built extension 63 | // Ex: buildExtension.exclude '.idea/**' 64 | -------------------------------------------------------------------------------- /.github/CODE OF CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | In the interest of fostering an open and welcoming environment, we as 7 | contributors and maintainers pledge to make participation in our project and 8 | our community a harassment-free experience for everyone, regardless of age, body 9 | size, disability, ethnicity, sex characteristics, gender identity and expression, 10 | level of experience, education, socio-economic status, nationality, personal 11 | appearance, race, religion, or sexual identity and orientation. 12 | 13 | ## Our Standards 14 | 15 | Examples of behavior that contributes to creating a positive environment 16 | include: 17 | 18 | * Using welcoming and inclusive language 19 | * Being respectful of differing viewpoints and experiences 20 | * Gracefully accepting constructive criticism 21 | * Focusing on what is best for the community 22 | * Showing empathy towards other community members 23 | 24 | Examples of unacceptable behavior by participants include: 25 | 26 | * The use of sexualized language or imagery and unwelcome sexual attention or 27 | advances 28 | * Trolling, insulting/derogatory comments, and personal or political attacks 29 | * Public or private harassment 30 | * Publishing others' private information, such as a physical or electronic 31 | address, without explicit permission 32 | * Other conduct which could reasonably be considered inappropriate in a 33 | professional setting 34 | 35 | ## Our Responsibilities 36 | 37 | Project maintainers are responsible for clarifying the standards of acceptable 38 | behavior and are expected to take appropriate and fair corrective action in 39 | response to any instances of unacceptable behavior. 40 | 41 | Project maintainers have the right and responsibility to remove, edit, or 42 | reject comments, commits, code, wiki edits, issues, and other contributions 43 | that are not aligned to this Code of Conduct, or to ban temporarily or 44 | permanently any contributor for other behaviors that they deem inappropriate, 45 | threatening, offensive, or harmful. 46 | 47 | ## Scope 48 | 49 | This Code of Conduct applies within all project spaces, and it also applies when 50 | an individual is representing the project or its community in public spaces. 51 | Examples of representing a project or community include using an official 52 | project e-mail address, posting via an official social media account, or acting 53 | as an appointed representative at an online or offline event. Representation of 54 | a project may be further defined and clarified by project maintainers. 55 | 56 | ## Enforcement 57 | 58 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 59 | reported by contacting the project team at info@foundryzero.co.uk . All 60 | complaints will be reviewed and investigated and will result in a response that 61 | is deemed necessary and appropriate to the circumstances. The project team is 62 | obligated to maintain confidentiality with regard to the reporter of an incident. 63 | Further details of specific enforcement policies may be posted separately. 64 | 65 | Project maintainers who do not follow or enforce the Code of Conduct in good 66 | faith may face temporary or permanent repercussions as determined by other 67 | members of the project's leadership. 68 | 69 | ## Attribution 70 | 71 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 72 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 73 | 74 | [homepage]: https://www.contributor-covenant.org 75 | 76 | For answers to common questions about this code of conduct, see 77 | https://www.contributor-covenant.org/faq 78 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Foundry Zero Open Source Project Contributing Guide 2 | 3 | 👍🎉 First off, thanks for taking the time to contribute! 🎉👍 4 | 5 | 6 | The following is a set of guidelines for contributing to ghidra-deep-links which is hosted in the Foundry Zero organisation on GitHub. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. 7 | 8 | ## Code of Conduct 9 | This project and everyone participating in it is governed by the Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to info@foundryzero.co.uk. 10 | 11 | ## What should I know before I get started? 12 | 13 | Take a look at the [README.md](https://github.com/foundryzero/ghidra-deep-links/blob/main/README.md) for more information about ghidra-deep-links. 14 | 15 | ## How Can I Contribute? 16 | 17 | ### Your First Code Contribution 18 | 19 | Unsure where to begin contributing to ghidra-deep-links? You can start by looking through these `beginner` and `help-wanted` issues: 20 | 21 | * [Beginner issues](https://github.com/foundryzero/ghidra-deep-links/labels/good%20first%20issue) - issues which should only require a few lines of code, and a test or two. 22 | * [Help wanted issues](https://github.com/foundryzero/ghidra-deep-links/labels/help-wanted) - issues which should be a bit more involved than `beginner` issues. 23 | 24 | #### Pull Requests 25 | 26 | The process described here has several goals: 27 | 28 | - Maintain ghidra-deep-links's quality 29 | - Fix problems that are important to users 30 | - Engage the community in working toward the best possible ghidra-deep-links 31 | 32 | While the prerequisites above must be satisfied prior to having your pull request reviewed, the reviewer(s) may ask you to complete additional design work, tests, or other changes before your pull request can be ultimately accepted. 33 | 34 | 35 | ### Reporting Bugs 36 | 37 | #### Before Submitting A Bug Report 38 | 39 | Before submitting a bug report, please check to see if the bug has already been raised as an issue by searching [our github issues](https://github.com/foundryzero/ghidra-deep-links/labels/bug) 40 | 41 | #### How Do I Submit A (Good) Bug Report? 42 | 43 | Bugs are tracked as GitHub issues. Please raise your bug as a GitHub issue using our [enhancement template](https://github.com/foundryzero/ghidra-deep-links/blob/main/.github/ISSUE_TEMPLATE/BUG%20REPORT.md). 44 | 45 | Explain the problem and include additional details to help maintainers reproduce the problem: 46 | 47 | * Use a clear and descriptive title for the issue to identify the problem. 48 | * Describe the exact steps which reproduce the problem in as many details as possible. 49 | * Provide specific examples to demonstrate the steps. Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets in the issue, use Markdown code blocks. 50 | * Describe the behavior you observed after following the steps and point out what exactly is the problem with that behavior. 51 | * Explain which behavior you expected to see instead and why. 52 | 53 | ### Suggesting Enhancements 54 | 55 | This section guides you through submitting an enhancement suggestion for ghidra-deep-links, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion 📝 and find related suggestions 🔎. 56 | 57 | #### Before Submitting A Feature Request 58 | 59 | Before submitting a feature request, please check to see if the bug has already been raised as an issue by searching [our github issues](https://github.com/foundryzero/ghidra-deep-links/labels/enhancement) 60 | 61 | #### How Do I Submit A (Good) Enhancement Suggestion? 62 | 63 | Features are tracked as GitHub issues. Please raise your bug as a GitHub issue using our [bug issue template](https://github.com/foundryzero/ghidra-deep-links/blob/main/.github/ISSUE_TEMPLATE/FEATURE%20REQUEST.md). 64 | 65 | * **Use a clear and descriptive title** for the issue to identify the suggestion. 66 | * **Provide a step-by-step description of the suggested enhancement** in as many details as possible. 67 | * **Provide specific examples to demonstrate the steps**. Include copy/pasteable snippets which you use in those examples, as [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines). 68 | * **Describe the current behavior** and **explain which behavior you would like to see instead** and why. 69 | * **Explain why this enhancement would be useful** 70 | * **Specify the name and version of the OS you're using.** 71 | 72 | 73 | ## Attribution 74 | 75 | This contributor guide is based on the [guide](https://github.com/atom/atom/blob/master/.CONTRIBUTING/CONTRIBUTING.md) developed by the Atom project -------------------------------------------------------------------------------- /extension/src/main/java/deeplinks/DeepLinksToolPlugin.java: -------------------------------------------------------------------------------- 1 | package deeplinks; 2 | 3 | import java.awt.datatransfer.Clipboard; 4 | import java.awt.datatransfer.StringSelection; 5 | import java.io.UnsupportedEncodingException; 6 | import java.net.URLEncoder; 7 | import org.apache.commons.text.StringEscapeUtils; 8 | 9 | import docking.ActionContext; 10 | import docking.action.DockingAction; 11 | import docking.action.MenuData; 12 | import docking.dnd.GClipboard; 13 | import ghidra.app.context.ListingActionContext; 14 | import ghidra.app.context.NavigatableActionContext; 15 | import ghidra.app.plugin.PluginCategoryNames; 16 | import ghidra.app.plugin.ProgramPlugin; 17 | import ghidra.app.plugin.core.decompile.DecompilerActionContext; 18 | import ghidra.framework.main.UtilityPluginPackage; 19 | import ghidra.framework.plugintool.PluginInfo; 20 | import ghidra.framework.plugintool.PluginTool; 21 | import ghidra.framework.plugintool.util.PluginStatus; 22 | import ghidra.program.model.address.Address; 23 | import ghidra.program.model.listing.Program; 24 | import ghidra.program.model.symbol.Symbol; 25 | import ghidra.program.model.symbol.SymbolTable; 26 | 27 | /** 28 | * A plugin that provides actions for copying disas :// deep links to the 29 | * current address or symbol in a code or decompiler listing. 30 | */ 31 | //@formatter:off 32 | @PluginInfo( 33 | status = PluginStatus.RELEASED, 34 | packageName = UtilityPluginPackage.NAME, 35 | category = PluginCategoryNames.NAVIGATION, 36 | shortDescription = "Provides actions for creating deep links in code listings.", 37 | description = "Provides actions for creating deep links in code listings.", 38 | servicesRequired = { }, 39 | eventsProduced = { } 40 | ) 41 | //@formatter:on 42 | public class DeepLinksToolPlugin extends ProgramPlugin { 43 | abstract class LinkCreateAction extends DockingAction { 44 | /* 45 | * Encapsulation of context menu actions for copying a link to the current 46 | * location to the clipboard Subclasses of this should override the 47 | * makeClipboardString abstract method to specify the exact text 48 | */ 49 | 50 | public LinkCreateAction(String name, String owner) { 51 | /* 52 | * name will be the title of this action in the context menu. 53 | */ 54 | super(name, owner); 55 | } 56 | 57 | @Override 58 | public boolean isAddToPopup(ActionContext context) { 59 | // This context menu only makes sense inside a Listing or Decompiler window. 60 | return (context instanceof ListingActionContext || context instanceof DecompilerActionContext); 61 | } 62 | 63 | @Override 64 | public void actionPerformed(ActionContext context) { 65 | /* 66 | * Copy a link to the clipboard, using makeClipboardString to get the exact link 67 | * text. 68 | */ 69 | if (!(context instanceof ListingActionContext || context instanceof DecompilerActionContext)) { 70 | return; 71 | } 72 | 73 | NavigatableActionContext naviContext = (NavigatableActionContext) context; 74 | 75 | String clipboardData = makeClipboardString(naviContext.getLocation().getAddress(), 76 | naviContext.getProgram()); 77 | copyToSystemClipboard(clipboardData); 78 | } 79 | 80 | public void addToTool(PluginTool targetTool) { 81 | /* 82 | * Add this action to the context menu for the given PluginTool 83 | */ 84 | setPopupMenuData(new MenuData(new String[] { this.getName() })); 85 | setEnabled(true); 86 | targetTool.addAction(this); 87 | } 88 | 89 | /* 90 | * Subclasses should override this method to specify the text to copy to the 91 | * clipboard when this action is performed on the given address. 92 | */ 93 | protected abstract String makeClipboardString(Address address, Program prog); 94 | 95 | protected String getAddressText(Address address) { 96 | return "0x" + address.toString(); 97 | } 98 | 99 | protected String getSymbolText(Address address, Program prog) { 100 | SymbolTable symbols = prog.getSymbolTable(); 101 | Symbol symbol = symbols.getPrimarySymbol(address); 102 | if (symbol != null) { 103 | return symbol.getName(); 104 | } 105 | return getAddressText(address); 106 | } 107 | 108 | protected String buildURL(Address address, Program prog) { 109 | SymbolTable symbols = prog.getSymbolTable(); 110 | Symbol symbol = symbols.getPrimarySymbol(address); 111 | String loc = getAddressText(address); 112 | try { 113 | // Don't encode slashes inside the query string 114 | // This is explicitly allowed in RFC-3986 115 | // see https://datatracker.ietf.org/doc/html/rfc3986#section-3.4 116 | String encodedPath = URLEncoder.encode(prog.getDomainFile().getPathname(), "utf-8"); 117 | encodedPath = encodedPath.replace("%2F", "/"); 118 | 119 | if (symbol != null) { 120 | final String symbolName = symbol.getName(true); 121 | final String TEMPLATE = "disas://%s/?ghidra_path=%s&offset=%s&label=%s"; 122 | return TEMPLATE.formatted(prog.getExecutableMD5(), encodedPath, URLEncoder.encode(loc, "utf-8"), 123 | URLEncoder.encode(symbolName, "utf-8")); 124 | } else { 125 | final String TEMPLATE = "disas://%s/?ghidra_path=%s&offset=%s"; 126 | return TEMPLATE.formatted(prog.getExecutableMD5(), encodedPath, URLEncoder.encode(loc, "utf-8")); 127 | } 128 | } catch (UnsupportedEncodingException e) { 129 | e.printStackTrace(); 130 | } 131 | return ""; 132 | } 133 | 134 | private static void copyToSystemClipboard(String data) { 135 | Clipboard systemClip = GClipboard.getSystemClipboard(); 136 | systemClip.setContents(new StringSelection(data), null); 137 | } 138 | } 139 | 140 | public DeepLinksToolPlugin(PluginTool tool) { 141 | super(tool); 142 | registerActions(); 143 | } 144 | 145 | private void registerActions() { 146 | /* 147 | * Create and register the different type of link copying actions. 148 | */ 149 | new LinkCreateAction("Copy Deep Link", getName()) { 150 | // Just the URL 151 | 152 | @Override 153 | protected String makeClipboardString(Address address, Program prog) { 154 | return buildURL(address, prog); 155 | } 156 | 157 | }.addToTool(tool); 158 | 159 | new LinkCreateAction("Copy Markdown Deep Link", getName()) { 160 | // Markdown formatted link 161 | 162 | @Override 163 | protected String makeClipboardString(Address address, Program prog) { 164 | String url = buildURL(address, prog); 165 | String linkTitle = getSymbolText(address, prog); 166 | return String.format("[`%s`](%s)", linkTitle, url); 167 | } 168 | 169 | }.addToTool(tool); 170 | 171 | new LinkCreateAction("Copy draw.io Deep Link", getName()) { 172 | private final static String template = ""; 173 | 174 | @Override 175 | protected String makeClipboardString(Address address, Program prog) { 176 | String url = StringEscapeUtils.escapeHtml4(buildURL(address, prog)); 177 | String linkTitle = StringEscapeUtils.escapeHtml4(getSymbolText(address, prog)); 178 | return String.format(template, url, linkTitle, url); 179 | } 180 | }.addToTool(tool); 181 | 182 | } 183 | 184 | } 185 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | ghidra deep links logo 3 |

4 | 5 | # Ghidra Deep Links 6 | 7 | A cross-platform plugin for Ghidra that provides deep linking support. This enables the generation of clickable `disas://` links that can be included in 3rd party applications. Example use cases include: 8 | 9 | * Direct linking from research notes or reports to relevant binary locations. 10 | * Sharing an interesting section address with peers over Slack, Discord, Teams etc. 11 | * Including links in vulnerability write-ups or tutorials to direct readers to the exact address of an issue. 12 | * Creating draw.io diagrams for documenting program structure with direct links to corresponding locations. 13 | 14 | The linking mechanism will work across different project structures and with both shared and non-shared Ghidra projects. 15 | 16 | ## IDA support 17 | 18 | Don't use Ghidra? Not a problem... 19 | 20 | We have collaborated with the team behind Heimdallr, a plugin that provides deep linking support for IDA, on a new platform-agnostic URI schema so that links generated in Ghidra Deep Links can be opened by Heimdallr and vice versa. This assumes the exact same binary is loaded on both platforms. This is great as now teams can use their prefered disassembler while being able to use the same external notes and reference links. Check out Heimdallr over at: 21 | 22 | We invite other developers to adopt the `disas://` schema outlined in [URL format](#-url-format) to enable more cross-application compatibility. 23 | 24 | ## ▶️ Usage 25 | 26 | With a CodeBrowser tool open, right click on a line in the listing view which will present new context menu items: 27 | 28 | ![The context menu in a Ghidra code listing containing the new Deep Link items](img/context-menu.png) 29 | 30 | Click on the *Copy Deep Link* item and a `disas://` link will be added to the clipboard. This can be shared by pasting like any normal link. 31 | 32 | When you (or somebody else) clicks on the link the referenced binary will open in a CodeBrowser session and the memory address from the link will be jumped to. 33 | 34 | > ⚠️ Currently the link handler does not distinguish between projects, and (on non-linux platforms) it cannot open Ghidra by itself. Therefore you will need to have Ghidra open and the project containing the binary referenced in the link open. 35 | 36 | You can use the *Copy Markdown Deep Link* context menu item to copy a fully formatted Markdown link, where the symbol name or address is the title of the link and the `disas://` URL is the target. 37 | 38 | ### Creating draw.io objects 39 | 40 | The *Copy draw.io Deep Link* context menu item populates your clipboard with a [draw.io](https://draw.io) object. Pasting this into draw.io will add an object to your diagram, labelled with the symbol name or address of the location where you right-clicked. This object will have both the text and the object as a whole linked with the `disas://` URL to the original binary location. Here's what it will look like when pasted into your draw.io diagram: 41 | 42 | ![Deep link pasted into draw.io](img/drawio-object.png) 43 | 44 | If you save your diagram as an SVG image and open it in a browser, you will now be able to click on this object and Ghidra will navigate to the original location. This makes it easy to create diagrams visually documenting the relationship between important locations in your binary, and allowing easy navigation around them. 45 | 46 | Top tip: use draw.io's [Editable SVG image](https://www.drawio.com/blog/xml-in-png) functionality to create an SVG file that you can load directly in draw.io. 47 | 48 | ## ⚙️ Installation 49 | 50 | ### Linux 51 | 52 | 1. Download and install the latest release of the ghidra-deep-links extension from 53 | 54 | 2. Install the `disas://` handler by executing the following: 55 | 56 | (Before curling and executing random scripts from the internet it is a good idea to validate they don't do anything malicious. Please review the contents of this script before execution at `install.sh`) 57 | 58 | ```bash 59 | bash -c "$(curl https://raw.githubusercontent.com/foundryzero/ghidra-deep-links/main/install.sh)" 60 | ``` 61 | 62 | 3. Alternatively, clone this repo and run `install-offline.sh`. 63 | 64 | 4. Follow the instructions in [Plugin Activation](#plugin-activation) to complete the install. 65 | 66 | ### Windows 67 | 68 | 1. Download and install the latest release of the ghidra-deep-links extension from 69 | 70 | 2. Install the `disas://` handler by executing the following in a PowerShell window: 71 | 72 | (Before executing random PowerShell scripts from the internet it is a good idea to validate they don't do anything malicious. Please review the contents of this script before execution at `install.ps1`) 73 | 74 | ```ps 75 | Invoke-Expression (Invoke-WebRequest https://raw.githubusercontent.com/foundryzero/ghidra-deep-links/main/install.ps1).Content 76 | ``` 77 | 78 | 3. Alternatively, clone this repo and run `install-offline.ps1`. 79 | 80 | 4. Follow the instructions in [Plugin Activation](#plugin-activation) to complete the install. 81 | 82 | ### Mac 83 | 84 | 1. Download and install the latest release of the ghidra-deep-links extension from 85 | 86 | 2. Additionally download `GhidraDeepLinksHandler.dmg` from the above releases page, mount the dmg and install the handler app as normal (drag to Applications) 87 | 88 | 3. Run the following to disable Gatekeeper on the handler app. 89 | 90 | (This is required as we do not code sign our releases. Please review the code at `os/mac`. This can be compiled from source by following the steps in `.github/workflows/mac_app.yml`) 91 | 92 | ```bash 93 | xattr -d com.apple.quarantine /Applications/GhidraDeepLinksHandler.app 94 | ``` 95 | 96 | 4. Follow the instructions in Plugin Activation below. 97 | 98 | ## Plugin Activation 99 | 100 | 1. From the Ghidra project browser click `File -> Install Extensions`. Click the green `+` button and select the extension downloaded from the releases page (Don't extract the zip archive). 101 | 102 | ![Extension installation in Ghidra](img/install.png) 103 | 104 | 2. In the main ghidra window (the one that shows your project files), go to `File -> Configure -> Utility` and enable `ghidra-deep-links`. 105 | 106 | ![Extension configuration in the Project window](img/configure.png) 107 | 108 | 3. In a CodeBrowser window, go to `File -> Configure -> Utility` and enable `DeepLinksToolPlugin`. 109 | 110 | 4. Verify the extension is correctly installed by loading a project then visiting [disas://?ghidra_verify=true](disas://?ghidra_verify=true). This should open a dialog box in Ghidra. 111 | 112 | # 🔨 Building 113 | 114 | See [BUILDING.md](./BUILDING.md) 115 | 116 | # 🔗 URL format 117 | 118 | The URLs take the format 119 | 120 | ```raw 121 | disas:///[?ghidra_path=]&offset=[&label=