From 3be53dc05ed04516c6d2e03c1389d1c39a015a40 Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Fri, 3 Mar 2023 13:54:07 -0500 Subject: [PATCH] GP-1007: Fix GADP agent nodepJar issues --- .../Debug/Debugger-agent-dbgeng/build.gradle | 47 +----------- .../agent/dbgeng/gadp/DbgEngGadpServer.java | 60 ++++++--------- .../Debugger-agent-dbgmodel/build.gradle | 44 +---------- .../dbgmodel/gadp/DbgModelGadpServer.java | 44 ++++------- .../Debug/Debugger-agent-frida/build.gradle | 60 +-------------- .../agent/frida/gadp/FridaGadpServer.java | 22 +++--- .../src/main/sh/execjar.sh | 22 ++++++ Ghidra/Debug/Debugger-agent-gdb/build.gradle | 66 +---------------- .../java/agent/gdb/gadp/GdbGadpServer.java | 74 +++++++++---------- .../Debugger-agent-gdb/src/main/sh/execjar.sh | 2 +- Ghidra/Debug/Debugger-agent-lldb/build.gradle | 60 +-------------- .../java/agent/lldb/gadp/LldbGadpServer.java | 22 +++--- .../src/main/sh/execjar.sh | 22 ++++++ gradle/debugger/hasExecutableJar.gradle | 41 ++++++++++ gradle/debugger/hasNodepJar.gradle | 61 +++++++++++++++ 15 files changed, 262 insertions(+), 385 deletions(-) create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/sh/execjar.sh create mode 100644 Ghidra/Debug/Debugger-agent-lldb/src/main/sh/execjar.sh create mode 100644 gradle/debugger/hasExecutableJar.gradle create mode 100644 gradle/debugger/hasNodepJar.gradle diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/build.gradle b/Ghidra/Debug/Debugger-agent-dbgeng/build.gradle index 26301609aa..6f73a8fd56 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/build.gradle +++ b/Ghidra/Debug/Debugger-agent-dbgeng/build.gradle @@ -18,8 +18,10 @@ apply from: "$rootProject.projectDir/gradle/jacocoProject.gradle" apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle" apply from: "$rootProject.projectDir/gradle/nativeProject.gradle" apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle" -apply plugin: 'eclipse' +apply from: "$rootProject.projectDir/gradle/debugger/hasNodepJar.gradle" + +apply plugin: 'eclipse' eclipse.project.name = 'Debug Debugger-agent-dbgeng' dependencies { @@ -33,53 +35,12 @@ dependencies { testImplementation project(path: ":Debugger-gadp", configuration: 'testArtifacts') } -def boolean filterJar(File jarfile) { - if (jarfile.name.contains("gradle-api")) { - return false - } else if (jarfile.name.contains("groovy-all")) { - return false - } else if (jarfile.name.contains("gradle-installation-beacon")) { - return false - } - return true -} - -jar { +tasks.nodepJar { manifest { attributes['Main-Class'] = 'agent.dbgeng.gadp.DbgEngGadpServer' } } -task configureNodepJar { - dependsOn(configurations.default) - - doLast { - configurations.default.files.forEach { - if (filterJar(it)) { - nodepJar.from(zipTree(it)) - } - } - } -} - -task nodepJar(type: Jar) { - inputs.file(file(jar.archivePath)) - dependsOn(configureNodepJar) - dependsOn(jar) - - archiveAppendix = 'nodep' - manifest { - attributes['Main-Class'] = 'agent.dbgeng.gadp.DbgEngGadpServer' - } - - from(zipTree(jar.archivePath)) - // TODO: This kind of stinks. I could probably apply some judicious excludes - // images I don't care. - // I probably must include duplicate LICENSE files, so that all are included - // IDK why the duplicate OSGi framework classes, but I probably don't care. - duplicatesStrategy = 'include' -} - test { jvmArgs('-Xrs') // TODO: Is this needed, or left over from trial-and-error if ("win_x86_64".equals(getCurrentPlatformName())) { diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/DbgEngGadpServer.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/DbgEngGadpServer.java index 1552853e32..ed4c51c613 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/DbgEngGadpServer.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/gadp/DbgEngGadpServer.java @@ -30,10 +30,28 @@ import ghidra.dbg.agent.AgentWindow; import ghidra.util.Msg; public interface DbgEngGadpServer extends AutoCloseable { + public static final String USAGE = + """ + This is the GADP server for Windows dbgeng.dll. Usage: + + gadp-agent-gdbeng [-H HOST/ADDR] [-p PORT] [-i ID] [-t TRANSPORT] + [-r REMOTE] + + Options: + + --host/-H The address of the interface on which to listen. + --port/-p The TCP port on which to listen for GADP. Default is 12345 + --transport/-t The transport specification for the Process Server. Default + is tcp:port=11200 + --remote/-r The transport specification for a remote server. + + Starts a dbgeng.dll-based GADP server "agent". Once the server has started, it + will print the interface IP and port. + """; public static final String DEFAULT_DBGSRV_TRANSPORT = "tcp:port=11200"; /** - * The entry point for the SCTL-DBGENG server in stand-alone mode + * The entry point for the GADP-DBGENG server in stand-alone mode * * Run it to see help. * @@ -55,7 +73,7 @@ public interface DbgEngGadpServer extends AutoCloseable { /** * Create a new instance of the server * - * @param addr the address to bind the SCTL server to + * @param addr the address to bind the GADP server to * @param busId the client ID the server should use on the bus for synthesized commands * @param dbgSrvTransport the transport specification for the {@code dbgeng.dll} server * @return the server instance @@ -71,7 +89,6 @@ public interface DbgEngGadpServer extends AutoCloseable { public class DbgEngRunner { protected InetSocketAddress bindTo; protected List dbgengArgs = new ArrayList<>(); - protected byte busId = 1; protected String dbgSrvTransport = DEFAULT_DBGSRV_TRANSPORT; protected String remote = null; @@ -134,23 +151,6 @@ public interface DbgEngGadpServer extends AutoCloseable { } iface = ait.next(); } - else if ("-i".equals(a) || "--bus-id".equals(a)) { - if (!ait.hasNext()) { - System.err.println("Expected ID"); - printUsage(); - System.exit(-1); - } - String busIdStr = ait.next(); - try { - busId = Byte.parseByte(busIdStr); - //dbgengArgs.add(busIdStr); - } - catch (NumberFormatException e) { - System.err.println("Byte required. Got " + busIdStr); - printUsage(); - System.exit(-1); - } - } else if ("-t".equals(a) || "--transport".equals(a)) { if (!ait.hasNext()) { System.err.println("Expected TRANSPORT"); @@ -181,23 +181,7 @@ public interface DbgEngGadpServer extends AutoCloseable { } protected void printUsage() { - System.out.println("This is the GADP server for Windows dbgeng.dll. Usage:"); - System.out.println(); - System.out.println(" [-H HOST/ADDR] [-p PORT] [-i ID] [-t TRANSPORT] [-r REMOTE]"); - System.out.println(); - System.out.println("Options:"); - System.out.println( - " --host/-H The address of the interface on which to listen."); - System.out.println(" Default is localhost"); - System.out.println( - " --port/-p The TCP port on which to listen for GADP. Default is 12345"); - System.out.println( - " --bus-id/-i The numeric client id for synthetic requests. Default is 1"); - System.out.println( - " --transport/-t The transport specification for the Process Server."); - System.out.println(" Default is tcp:port=11200"); - System.out.println( - " --remote/-r The transport specification for a remote server."); + System.out.println(USAGE); } } @@ -209,7 +193,7 @@ public interface DbgEngGadpServer extends AutoCloseable { CompletableFuture startDbgEng(String[] args); /** - * Get the local address to which the SCTL server is bound. + * Get the local address to which the GADP server is bound. * * @return the local socket address */ diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/build.gradle b/Ghidra/Debug/Debugger-agent-dbgmodel/build.gradle index 37a701146d..485bc8cf8a 100644 --- a/Ghidra/Debug/Debugger-agent-dbgmodel/build.gradle +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/build.gradle @@ -19,6 +19,8 @@ apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle" apply from: "$rootProject.projectDir/gradle/nativeProject.gradle" apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle" +apply from: "$rootProject.projectDir/gradle/debugger/hasNodepJar.gradle" + apply plugin: 'eclipse' eclipse.project.name = 'Debug Debugger-agent-dbgmodel' @@ -31,52 +33,12 @@ dependencies { testImplementation project(path: ":Debugger-gadp", configuration: 'testArtifacts') } -def boolean filterJar(File jarfile) { - if (jarfile.name.contains("gradle-api")) { - return false - } else if (jarfile.name.contains("groovy-all")) { - return false - } else if (jarfile.name.contains("gradle-installation-beacon")) { - return false - } - return true -} - -jar { +tasks.nodepJar { manifest { attributes['Main-Class'] = 'agent.dbgmodel.gadp.DbgModelGadpServer' } } -task configureNodepJar { - dependsOn(configurations.default) - doLast { - configurations.default.files.forEach { - if (filterJar(it)) { - nodepJar.from(zipTree(it)) - } - } - } -} - -task nodepJar(type: Jar) { - inputs.file(file(jar.archivePath)) - dependsOn(configureNodepJar) - dependsOn(jar) - - archiveAppendix = 'nodep' - manifest { - attributes['Main-Class'] = 'agent.dbgmodel.gadp.DbgModelGadpServer' - } - - from(zipTree(jar.archivePath)) - // TODO: This kind of stinks. I could probably apply some judicious excludes - // images I don't care. - // I probably must include duplicate LICENSE files, so that all are included - // IDK why the duplicate OSGi framework classes, but I probably don't care. - duplicatesStrategy = 'include' -} - test { jvmArgs('-Xrs') // TODO: Is this needed, or left over from trial-and-error if ("win_x86_64".equals(getCurrentPlatformName())) { diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/DbgModelGadpServer.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/DbgModelGadpServer.java index c8b60bd911..a6e9d39a8b 100644 --- a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/DbgModelGadpServer.java +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/DbgModelGadpServer.java @@ -28,47 +28,35 @@ import ghidra.dbg.agent.AgentWindow; import ghidra.util.Msg; /** - * The interface for the SCTL-{@code dbgeng.dll} server + * The interface for the GADP-{@code dbgeng.dll} server * + *

* This is just an interface to specify the truly public methods. This is also a convenient place to * put all the command-line parsing logic. * - * This server implements the SCTL commands necessary to have a smooth debugging experience in + *

+ * This server implements the GADP commands necessary to have a smooth debugging experience in * Ghidra. It implements almost every command that has use on a binary without debugging * information. It operates as a standalone debugging server based on {@code dbgeng.dll}, which can - * accept other {@code dbgeng.dll}-based clients as well as SCTL clients. + * accept other {@code dbgeng.dll}-based clients as well as GADP clients. * + *

* Without limitation, the caveats are listed here: * - * 1) The {@code Tnames} request in not implemented. The only namespaces available are those given - * in the {@code Rstat} response. - * - * 2) For binaries without a debugging database (pdb file), the symbol commands only search the - * exported symbols. - * - * 3) The type commands are not implemented. Ghidra can read most PDB files directly. - * - * 4) While SCTL presents thread-specific control, {@code dbgeng.dll} does not. Continue ("g" in - * {@code dbgeng.dll}) affects all debugged targets, except those with higher suspect counts and + *

    + *
  1. For binaries without a debugging database (pdb file), the symbol commands only search the + * exported symbols.
  2. + *
  3. The type commands are not implemented. Ghidra can read most PDB files directly.
  4. + *
  5. While GADP presents thread-specific control, {@code dbgeng.dll} does not. Continue ("g" in + * {@code dbgeng.dll}) affects all debugged targets, except those with higher suspend counts and * those that are frozen. The API makes it impossible to perfectly track which threads are actually - * executed by "g". The server thus assumes that all threads run when any thread runs, and it will - * synthesize the commands to reflect that in the connected clients. - * - * 5) The {@code Ttrace} command is not supported. The user can configure filters in the host - * debugger; however, some events will always be trapped by the SCTL server. Future versions may - * adjust this. - * - * 6) Snapshots are not supported. {@code dbgeng.dll} as no equivalent. - * - * 7) System calls are no yet reported. Windows programs do not use {@code fork} and {@code exec}. - * Instead, calls to {@code CreateProcess} cause the server to synthesize {@code Tattach} commands. - * - * 8) The {@code Tunwind1} command is not supported. Ghidra should unwind instead. + * executed by "g". The server thus assumes that all threads run when any thread runs.
  6. + *
*/ public interface DbgModelGadpServer extends DbgEngGadpServer { /** - * The entry point for the SCTL-DBGENG server in stand-alone mode + * The entry point for the GADP-DBGMODEL server in stand-alone mode * * Run it to see help. * @@ -90,7 +78,7 @@ public interface DbgModelGadpServer extends DbgEngGadpServer { /** * Create a new instance of the server * - * @param addr the address to bind the SCTL server to + * @param addr the address to bind the GADP server to * @param busId the client ID the server should use on the bus for synthesized commands * @param dbgSrvTransport the transport specification for the {@code dbgeng.dll} server * @return the server instance diff --git a/Ghidra/Debug/Debugger-agent-frida/build.gradle b/Ghidra/Debug/Debugger-agent-frida/build.gradle index d4a95dca62..2537a86cd6 100644 --- a/Ghidra/Debug/Debugger-agent-frida/build.gradle +++ b/Ghidra/Debug/Debugger-agent-frida/build.gradle @@ -19,6 +19,8 @@ apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle" apply from: "$rootProject.projectDir/gradle/nativeProject.gradle" apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle" +apply from: "$rootProject.projectDir/gradle/debugger/hasExecutableJar.gradle" + apply plugin: 'eclipse' eclipse.project.name = 'Debug Debugger-agent-frida' @@ -33,68 +35,14 @@ dependencies { testImplementation project(path: ':Debugger-gadp', configuration: 'testArtifacts') } -def boolean filterJar(File jarfile) { - if (jarfile.name.contains("gradle-api")) { - return false - } else if (jarfile.name.contains("groovy-all")) { - return false - } else if (jarfile.name.contains("gradle-installation-beacon")) { - return false - } - return true -} - -jar { +tasks.nodepJar { manifest { attributes['Main-Class'] = 'agent.lldb.gadp.FridaGadpServer' } } -task configureNodepJar { - doLast { - configurations.default.files.forEach { - if (filterJar(it)) { - nodepJar.from(zipTree(it)) - } - } - } -} - -task nodepJar(type: Jar) { - inputs.file(file(jar.archivePath)) - dependsOn(configureNodepJar) - dependsOn(jar) - - archiveAppendix = 'nodep' - manifest { - attributes['Main-Class'] = 'agent.lldb.gadp.FridaGadpServer' - } - - from(zipTree(jar.archivePath)) -} - -task executableJar { - ext.execsh = file("src/main/sh/execjar.sh") - ext.jarfile = file(nodepJar.archivePath) +tasks.executableJar { ext.outjar = file("${buildDir}/bin/gadp-agent-frida") - dependsOn(nodepJar) - inputs.file(execsh) - inputs.file(jarfile) - outputs.file(outjar) - doLast { - outjar.parentFile.mkdirs() - outjar.withOutputStream { output -> - execsh.withInputStream { input -> - output << input - } - jarfile.withInputStream { input -> - output << input - } - } - exec { - commandLine("chmod", "+x", outjar) - } - } } test { diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/FridaGadpServer.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/FridaGadpServer.java index 3e8248cd46..04445b0a0a 100644 --- a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/FridaGadpServer.java +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/FridaGadpServer.java @@ -28,6 +28,17 @@ import ghidra.dbg.agent.AgentWindow; import ghidra.util.Msg; public interface FridaGadpServer extends AutoCloseable { + public static final String USAGE = + """ + This is the GADP server for Frida. Usage: + + gadp-agent-frida [-H HOST/ADDR] [-p PORT] + + Options: + --host/-H The address of the interface on which to listen. Default is + localhost + --port/-p The TCP port on which to listen for GADP. Default is 12345 + """; /** * The entry point for the Frida server in stand-alone mode @@ -133,16 +144,7 @@ public interface FridaGadpServer extends AutoCloseable { } protected void printUsage() { - System.out.println("This is the GADP server for Frida. Usage:"); - System.out.println(); - System.out.println(" [-H HOST/ADDR] [-p PORT] [-i ID] [-t TRANSPORT] [-r REMOTE]"); - System.out.println(); - System.out.println("Options:"); - System.out.println( - " --host/-H The address of the interface on which to listen."); - System.out.println(" Default is localhost"); - System.out.println( - " --port/-p The TCP port on which to listen for GADP. Default is 12345"); + System.out.println(USAGE); } } diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/sh/execjar.sh b/Ghidra/Debug/Debugger-agent-frida/src/main/sh/execjar.sh new file mode 100644 index 0000000000..26738bd11d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/sh/execjar.sh @@ -0,0 +1,22 @@ +#!/usr/bin/bash +## ### +# 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. +## +# This clever bit can be prepended to a JAR to make it self-executable + +set -e + +java -jar "$0" ${@:1} +exit diff --git a/Ghidra/Debug/Debugger-agent-gdb/build.gradle b/Ghidra/Debug/Debugger-agent-gdb/build.gradle index c686052160..9f143b25f9 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/build.gradle +++ b/Ghidra/Debug/Debugger-agent-gdb/build.gradle @@ -19,6 +19,8 @@ apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle" apply from: "$rootProject.projectDir/gradle/nativeProject.gradle" apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle" +apply from: "$rootProject.projectDir/gradle/debugger/hasExecutableJar.gradle" + apply plugin: 'eclipse' eclipse.project.name = 'Debug Debugger-agent-gdb' @@ -33,74 +35,14 @@ dependencies { testImplementation project(path: ':Debugger-gadp', configuration: 'testArtifacts') } -def boolean filterJar(File jarfile) { - if (jarfile.name.contains("gradle-api")) { - return false - } else if (jarfile.name.contains("groovy-all")) { - return false - } else if (jarfile.name.contains("gradle-installation-beacon")) { - return false - } - return true -} - -jar { +tasks.nodepJar { manifest { attributes['Main-Class'] = 'agent.gdb.gadp.GdbGadpServer' } } -task configureNodepJar { - dependsOn(configurations.default) - doLast { - configurations.default.files.forEach { - if (filterJar(it)) { - nodepJar.from(zipTree(it)) - } - } - } -} - -task nodepJar(type: Jar) { - inputs.file(file(jar.archivePath)) - dependsOn(configureNodepJar) - dependsOn(jar) - - archiveAppendix = 'nodep' - manifest { - attributes['Main-Class'] = 'agent.gdb.gadp.GdbGadpServer' - } - - from(zipTree(jar.archivePath)) - // TODO: This kind of stinks. I could probably apply some judicious excludes - // images I don't care. - // I probably must include duplicate LICENSE files, so that all are included - // IDK why the duplicate OSGi framework classes, but I probably don't care. - duplicatesStrategy = 'include' -} - -task executableJar { - ext.execsh = file("src/main/sh/execjar.sh") - ext.jarfile = file(nodepJar.archivePath) +tasks.executableJar { ext.outjar = file("${buildDir}/bin/gadp-agent-gdb") - dependsOn(nodepJar) - inputs.file(execsh) - inputs.file(jarfile) - outputs.file(outjar) - doLast { - outjar.parentFile.mkdirs() - outjar.withOutputStream { output -> - execsh.withInputStream { input -> - output << input - } - jarfile.withInputStream { input -> - output << input - } - } - exec { - commandLine("chmod", "+x", outjar) - } - } } test { diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/gadp/GdbGadpServer.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/gadp/GdbGadpServer.java index 45e46322cf..70200ff652 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/gadp/GdbGadpServer.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/gadp/GdbGadpServer.java @@ -30,6 +30,38 @@ import ghidra.dbg.agent.AgentWindow; import ghidra.util.Msg; public interface GdbGadpServer extends AutoCloseable { + public static final String USAGE = + """ + This is the GADP wrapper for GDB. Usage: + + gadp-agent-gdb [GDB options] [--agent-args [-H HOST/ADDR] [-p PORT] + [-g CMD] [-x]] + + Options: + + Use gdb -h for suitable [GDB options] + + THE FOLLOWING OPTIONS MUST BE PRECEDED BY --agent-args + --host/-H The address of the interface on which to listen. Default is + localhost + --port/-p The TCP port on which to listen. Default is 12345. 0 for + automatic. + --gdb-cmd/-g The command to launch gdb. Default is 'gdb' + --existing/-x Do not launch gdb. Instead just open a pty + + Starts a GDB-based GADP server "agent". In general, it can be invoked in the + same manner as standard gdb. Arguments to control the GADP server and GDB + invocation are given after the --gadp-args flag. Once the server has started, it + will print the interface IP and port. The -g and -x flags are mutually + exclusive. The one appearing last get preference. The -x flags causes the agent + to refrain from launching its own gdb process. Instead, it prints the file name + of a pseudo terminal (pty) where it expects a GDB/MI v2 interpreter from an + existing gdb process. Use the new-ui command (available since GDB version 7.12) + to join the agent to the existing session: + + (gdb) new-ui mi2 /dev/ptyXX + """; + public static void main(String[] args) throws Exception { try { new Runner().run(args); @@ -73,7 +105,7 @@ public interface GdbGadpServer extends AutoCloseable { Iterator ait = Arrays.asList(args).iterator(); while (ait.hasNext()) { String a = ait.next(); - if ("--gadp-args".equals(a)) { + if ("--agent-args".equals(a)) { break; } else if ("-h".equals(a) || "--help".equals(a)) { @@ -131,45 +163,7 @@ public interface GdbGadpServer extends AutoCloseable { } private void printUsage() { - System.out.println("This is the GADP wrapper for GDB. Usage:"); - System.out.println(); - System.out.println( - " gadpgdb [GDB options] [--gadp-args [-H HOST/ADDR] [-p PORT] [-g CMD] [-x]]"); - System.out.println(); - System.out.println("Options:"); - System.out.println(); - System.out.println("Use gdb -h for suitable [GDB options]"); - System.out.println(); - System.out.println( - " --host/-H The address of the interface on which to listen. Default is localhost"); - System.out.println( - " --port/-p The TCP port on which to listen. Default is 12345. 0 for automatic."); - System.out.println( - " --gdb-cmd/-g The command to launch gdb. Default is 'gdb'"); - System.out.println( - " --existing/-x Do not launch gdb. Instead just open a pty"); - System.out.println(); - System.out.println( - "Starts a GDB-based GADP server \"agent\". In general, it can be invoked in"); - System.out.println( - "the same manner as standard gdb. Arguments to control the GADP server and"); - System.out.println( - "GDB invocation are given after the --gadp-args flag. Once the server has"); - System.out.println( - "started, it will print the interface IP and port. The -g and -x flags are"); - System.out.println( - "mutually exclusive. The one appearing last get preference. The -x flags"); - System.out.println( - "causes the agent to refrain from launching its own gdb process. Instead,"); - System.out.println( - "it prints the file name of a private terminate (pty) where it expects a"); - System.out.println( - "GDB/MI v2 interpreter from an existing gdb process. Use the new-ui command"); - System.out.println( - "(available since GDB version 7.12) to join the agent to the existing"); - System.out.println("session:"); - System.out.println(); - System.out.println("(gdb) new-ui mi2 /dev/ptyXX"); + System.out.println(USAGE); } } diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/sh/execjar.sh b/Ghidra/Debug/Debugger-agent-gdb/src/main/sh/execjar.sh index c5300670ff..26738bd11d 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/sh/execjar.sh +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/sh/execjar.sh @@ -18,5 +18,5 @@ set -e -java -jar "$0" +java -jar "$0" ${@:1} exit diff --git a/Ghidra/Debug/Debugger-agent-lldb/build.gradle b/Ghidra/Debug/Debugger-agent-lldb/build.gradle index 57338fd0ca..8b05a63bd7 100644 --- a/Ghidra/Debug/Debugger-agent-lldb/build.gradle +++ b/Ghidra/Debug/Debugger-agent-lldb/build.gradle @@ -19,6 +19,8 @@ apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle" apply from: "$rootProject.projectDir/gradle/nativeProject.gradle" apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle" +apply from: "$rootProject.projectDir/gradle/debugger/hasExecutableJar.gradle" + apply plugin: 'eclipse' eclipse.project.name = 'Debug Debugger-agent-lldb' @@ -33,68 +35,14 @@ dependencies { testImplementation project(path: ':Debugger-gadp', configuration: 'testArtifacts') } -def boolean filterJar(File jarfile) { - if (jarfile.name.contains("gradle-api")) { - return false - } else if (jarfile.name.contains("groovy-all")) { - return false - } else if (jarfile.name.contains("gradle-installation-beacon")) { - return false - } - return true -} - -jar { +tasks.nodepJar { manifest { attributes['Main-Class'] = 'agent.lldb.gadp.LldbGadpServer' } } -task configureNodepJar { - doLast { - configurations.default.files.forEach { - if (filterJar(it)) { - nodepJar.from(zipTree(it)) - } - } - } -} - -task nodepJar(type: Jar) { - inputs.file(file(jar.archivePath)) - dependsOn(configureNodepJar) - dependsOn(jar) - - archiveAppendix = 'nodep' - manifest { - attributes['Main-Class'] = 'agent.lldb.gadp.LldbGadpServer' - } - - from(zipTree(jar.archivePath)) -} - -task executableJar { - ext.execsh = file("src/main/sh/execjar.sh") - ext.jarfile = file(nodepJar.archivePath) +tasks.executableJar { ext.outjar = file("${buildDir}/bin/gadp-agent-lldb") - dependsOn(nodepJar) - inputs.file(execsh) - inputs.file(jarfile) - outputs.file(outjar) - doLast { - outjar.parentFile.mkdirs() - outjar.withOutputStream { output -> - execsh.withInputStream { input -> - output << input - } - jarfile.withInputStream { input -> - output << input - } - } - exec { - commandLine("chmod", "+x", outjar) - } - } } test { diff --git a/Ghidra/Debug/Debugger-agent-lldb/src/main/java/agent/lldb/gadp/LldbGadpServer.java b/Ghidra/Debug/Debugger-agent-lldb/src/main/java/agent/lldb/gadp/LldbGadpServer.java index 31a6b3631a..b1337318c0 100644 --- a/Ghidra/Debug/Debugger-agent-lldb/src/main/java/agent/lldb/gadp/LldbGadpServer.java +++ b/Ghidra/Debug/Debugger-agent-lldb/src/main/java/agent/lldb/gadp/LldbGadpServer.java @@ -28,6 +28,17 @@ import ghidra.dbg.agent.AgentWindow; import ghidra.util.Msg; public interface LldbGadpServer extends AutoCloseable { + public static final String USAGE = + """ + This is the GADP server for Frida. Usage: + + gadp-agent-lldb [-H HOST/ADDR] [-p PORT] + + Options: + --host/-H The address of the interface on which to listen. Default is + localhost + --port/-p The TCP port on which to listen for GADP. Default is 12345 + """; /** * The entry point for the LLDB server in stand-alone mode @@ -133,16 +144,7 @@ public interface LldbGadpServer extends AutoCloseable { } protected void printUsage() { - System.out.println("This is the GADP server for LLVM's lldb. Usage:"); - System.out.println(); - System.out.println(" [-H HOST/ADDR] [-p PORT] [-i ID] [-t TRANSPORT] [-r REMOTE]"); - System.out.println(); - System.out.println("Options:"); - System.out.println( - " --host/-H The address of the interface on which to listen."); - System.out.println(" Default is localhost"); - System.out.println( - " --port/-p The TCP port on which to listen for GADP. Default is 12345"); + System.out.println(USAGE); } } diff --git a/Ghidra/Debug/Debugger-agent-lldb/src/main/sh/execjar.sh b/Ghidra/Debug/Debugger-agent-lldb/src/main/sh/execjar.sh new file mode 100644 index 0000000000..26738bd11d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-lldb/src/main/sh/execjar.sh @@ -0,0 +1,22 @@ +#!/usr/bin/bash +## ### +# 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. +## +# This clever bit can be prepended to a JAR to make it self-executable + +set -e + +java -jar "$0" ${@:1} +exit diff --git a/gradle/debugger/hasExecutableJar.gradle b/gradle/debugger/hasExecutableJar.gradle new file mode 100644 index 0000000000..bca8f24fde --- /dev/null +++ b/gradle/debugger/hasExecutableJar.gradle @@ -0,0 +1,41 @@ +/* ### + * 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. + */ +apply from: "$rootProject.projectDir/gradle/debugger/hasNodepJar.gradle" + + +task executableJar { + ext.execsh = file("src/main/sh/execjar.sh") + ext.jarfile = file(nodepJar.archivePath) + ext.outjar = file("${buildDir}/bin/run") + dependsOn(nodepJar) + inputs.file { execsh } + inputs.file { jarfile } + outputs.file { outjar } + doLast { + outjar.parentFile.mkdirs() + outjar.withOutputStream { output -> + execsh.withInputStream { input -> + output << input + } + jarfile.withInputStream { input -> + output << input + } + } + exec { + commandLine("chmod", "+x", outjar) + } + } +} diff --git a/gradle/debugger/hasNodepJar.gradle b/gradle/debugger/hasNodepJar.gradle new file mode 100644 index 0000000000..a309d000d2 --- /dev/null +++ b/gradle/debugger/hasNodepJar.gradle @@ -0,0 +1,61 @@ +/* ### + * 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. + */ +def boolean filterJar(File jarfile) { + if (jarfile.name.contains("gradle-api")) { + return false + } else if (jarfile.name.contains("groovy-all")) { + return false + } else if (jarfile.name.contains("gradle-installation-beacon")) { + return false + } + return true +} + +task configureNodepJar { + dependsOn(configurations.default) + doLast { + configurations.default.files.forEach { jar -> + if (filterJar(jar)) { + nodepJar.from(zipTree(jar)) { + // The real solution here is probably to sort out the dependency graph + // Still, I imagine some of the excludes will be necessary + exclude "help/**" + exclude "images/**" + exclude "OSGI-OPT/**" + exclude "org/osgi/**" + exclude "aQute/**" + // Duplicate. And signature breaks nodep jar + exclude "META-INF/*.SF" + exclude "META-INF/*.DSA" + exclude "META-INF/*.RSA" + // Ensure all LICENSES are included, by renaming to avoid collisions + rename("((LICENSE)|(AL2\\.0)|(LGPL2\\.1)|(NOTICE)|(NOTICE.txt)|(DEPENDENCIES))", "${jar.name}-\$1") + } + } + } + } +} + +task nodepJar(type: Jar) { + inputs.file(file(jar.archivePath)) + dependsOn(configureNodepJar) + dependsOn(jar) + + archiveAppendix = 'nodep' + + from(zipTree(jar.archivePath)) + duplicatesStrategy = 'exclude' +}