diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/data/debugger-launchers/local-dbgeng.ps1 b/Ghidra/Debug/Debugger-agent-dbgeng/data/debugger-launchers/local-dbgeng.ps1 index 691cb22b75..df1110b240 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/data/debugger-launchers/local-dbgeng.ps1 +++ b/Ghidra/Debug/Debugger-agent-dbgeng/data/debugger-launchers/local-dbgeng.ps1 @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. ## -#@title dbgeng (.ps1) +#@title dbgeng #@image-opt env:OPT_TARGET_IMG #@desc #@desc

Launch with dbgeng

@@ -26,7 +26,6 @@ #@icon icon.debugger #@help dbgeng#local #@depends Debugger-rmi-trace -#@arg :file "Image" "The target binary executable image" #@env OPT_TARGET_IMG:file="" "Image" "The target binary executable image" #@env OPT_TARGET_ARGS:str="" "Arguments" "Command-line arguments to pass to the target" #@env OPT_PYTHON_EXE:file!="python" "Python command" "The path to the Python 3 interpreter. Omit the full path to resolve using the system PATH." diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/data/debugger-launchers/ssh-dbgeng-attach.ps1 b/Ghidra/Debug/Debugger-agent-dbgeng/data/debugger-launchers/ssh-dbgeng-attach.ps1 index 966ed0ab2f..849d353aed 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/data/debugger-launchers/ssh-dbgeng-attach.ps1 +++ b/Ghidra/Debug/Debugger-agent-dbgeng/data/debugger-launchers/ssh-dbgeng-attach.ps1 @@ -69,7 +69,7 @@ $answer = Check-Result-And-Prompt-Mitigation $sshproc @" It appears ghidradbg is missing from the remote system. This can happen if you forgot to install the required package. This can also happen if you installed the packages to a different Python environment than is being used by the -remote's gdb. +remote's dbgeng. This script is about to offer automatic resolution. If you'd like to resolve this manually, answer no to the next question and then see Ghidra's help by diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/data/debugger-launchers/ssh-dbgeng.ps1 b/Ghidra/Debug/Debugger-agent-dbgeng/data/debugger-launchers/ssh-dbgeng.ps1 index 96575eb840..2208baa218 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/data/debugger-launchers/ssh-dbgeng.ps1 +++ b/Ghidra/Debug/Debugger-agent-dbgeng/data/debugger-launchers/ssh-dbgeng.ps1 @@ -72,7 +72,7 @@ $answer = Check-Result-And-Prompt-Mitigation $sshproc @" It appears ghidradbg is missing from the remote system. This can happen if you forgot to install the required package. This can also happen if you installed the packages to a different Python environment than is being used by the -remote's gdb. +remote's dbgeng. This script is about to offer automatic resolution. If you'd like to resolve this manually, answer no to the next question and then see Ghidra's help by diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/data/support/dbgsetuputils.ps1 b/Ghidra/Debug/Debugger-agent-dbgeng/data/support/dbgsetuputils.ps1 index a9181ae5e8..f7cf589b51 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/data/support/dbgsetuputils.ps1 +++ b/Ghidra/Debug/Debugger-agent-dbgeng/data/support/dbgsetuputils.ps1 @@ -17,11 +17,6 @@ function Compute-Dbg-PipInstall-Args { $argvpart = $args -join ", " - $arglist = @("$Env:OPT_PYTHON_EXE -c `"") - $arglist+=("import os, sys, runpy") - $arglist+=("sys.argv=['pip', 'install', '--force-reinstall', $argvpart]") - $arglist+=("os.environ['PIP_BREAK_SYSTEM_PACKAGE']='1'") - $arglist+=("runpy.run_module('pip', run_name='__main__')") - + $arglist = @("$Env:OPT_PYTHON_EXE -c `"import os, sys, runpy; sys.argv=['pip', 'install', '--force-reinstall', $argvpart]; os.environ['PIP_BREAK_SYSTEM_PACKAGE']='1'; runpy.run_module('pip', run_name='__main__')`"") return $arglist } diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/data/support/local-dbgeng-attach.py b/Ghidra/Debug/Debugger-agent-dbgeng/data/support/local-dbgeng-attach.py index ff0222ff8b..3132436c84 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/data/support/local-dbgeng-attach.py +++ b/Ghidra/Debug/Debugger-agent-dbgeng/data/support/local-dbgeng-attach.py @@ -17,6 +17,7 @@ import os import sys + cxn = os.getenv('GHIDRA_TRACE_RMI_ADDR') target = os.getenv('OPT_TARGET_PID') args = os.getenv('OPT_ATTACH_FLAGS') @@ -52,6 +53,11 @@ def append_paths(): def main(): append_paths() # Delay these imports until sys.path is patched + try: + import ghidradbg + except Exception as e: + print(e) + exit(253) from ghidradbg import commands as cmd from pybag.dbgeng import core as DbgEng from ghidradbg.hooks import on_state_changed @@ -82,5 +88,7 @@ if __name__ == '__main__': try: main() except SystemExit as x: + if x.code == 253: + exit(253) if x.code != 0: print(f"Exited with code {x.code}") diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/data/support/local-dbgeng.py b/Ghidra/Debug/Debugger-agent-dbgeng/data/support/local-dbgeng.py index 7ee9c43442..320ca19c50 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/data/support/local-dbgeng.py +++ b/Ghidra/Debug/Debugger-agent-dbgeng/data/support/local-dbgeng.py @@ -17,6 +17,7 @@ import os import sys + cxn = os.getenv('GHIDRA_TRACE_RMI_ADDR') target = os.getenv('OPT_TARGET_IMG') args = os.getenv('OPT_TARGET_ARGS') @@ -55,6 +56,11 @@ def main(): return # Delay these imports until sys.path is patched + try: + import ghidradbg + except: + print(e) + exit(253) from ghidradbg import commands as cmd from pybag.dbgeng import core as DbgEng from ghidradbg.hooks import on_state_changed @@ -92,5 +98,7 @@ if __name__ == '__main__': try: main() except SystemExit as x: + if x.code == 253: + exit(253) if x.code != 0: print(f"Exited with code {x.code}") diff --git a/Ghidra/Debug/Debugger-agent-gdb/data/debugger-launchers/ssh-gdb-win.ps1 b/Ghidra/Debug/Debugger-agent-gdb/data/debugger-launchers/ssh-gdb-win.ps1 index 4551fa9430..9642a9af00 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/data/debugger-launchers/ssh-gdb-win.ps1 +++ b/Ghidra/Debug/Debugger-agent-gdb/data/debugger-launchers/ssh-gdb-win.ps1 @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. ## -#@title gdb via ssh ((remote=cmd shell) +#@title gdb via ssh (cmd shell) #@image-opt arg:1 #@desc #@desc

Launch with gdb via ssh to a remote Windows machine

@@ -30,7 +30,7 @@ #@enum Endian:str auto big little #@arg :str "Image" "The target binary executable image on the remote system" #@env OPT_TARGET_ARGS:str="" "Arguments" "Command-line arguments to pass to the target" -##@env OPT_SSH_PATH:file="ssh" "ssh command" "The path to ssh on the local system. Omit the full path to resolve using the system PATH." +#@env OPT_SSH_PATH:file="ssh" "ssh command" "The path to ssh on the local system. Omit the full path to resolve using the system PATH." #@env OPT_HOST:str="localhost" "[User@]Host" "The hostname or user@host" #@env OPT_REMOTE_PORT:int=12345 "Remote Trace RMI Port" "A free port on the remote end to receive and forward the Trace RMI connection." #@env OPT_EXTRA_SSH_ARGS:str="" "Extra ssh arguments" "Extra arguments to pass to ssh. Use with care." @@ -44,7 +44,7 @@ $Env:OPT_OS_WINDOWS = $true $arglist = @("") -$arglist += ("cat > .\.gdbinit") +$arglist += ("cat > .\ghidra.gdbinit") $sshargs = Compute-Ssh-Args $arglist True $image = ($args[0]).Replace("\", "\\") @@ -60,7 +60,7 @@ Replace('$OPT_START_CMD', $Env:OPT_START_CMD)` $arglist = @("") $arglist += ("$Env:OPT_GDB_PATH") -$arglist += ("-q") +$arglist += ("-q", "-x", ".\ghidra.gdbinit") $sshargs = Compute-Ssh-Args $arglist True $sshproc = Start-Process -FilePath ssh -ArgumentList $sshargs[1..$sshargs.Count] -NoNewWindow -Wait -PassThru diff --git a/Ghidra/Debug/Debugger-agent-gdb/data/debugger-launchers/ssh-gdb-win.sh b/Ghidra/Debug/Debugger-agent-gdb/data/debugger-launchers/ssh-gdb-win.sh index b2ded6d66a..f0b0cab724 100755 --- a/Ghidra/Debug/Debugger-agent-gdb/data/debugger-launchers/ssh-gdb-win.sh +++ b/Ghidra/Debug/Debugger-agent-gdb/data/debugger-launchers/ssh-gdb-win.sh @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. ## -#@title gdb via ssh (to cmd) +#@title gdb via ssh (cmd shell) #@image-opt arg:1 #@desc #@desc

Launch with gdb via ssh

@@ -64,7 +64,7 @@ function rewrite-gdbinit() { function launch-gdb-ssh-init() { local -a sshargs - compute-ssh-args true "cat > .\.gdbinit" + compute-ssh-args true "cat > .\ghidra.gdbinit" "${sshargs[@]}" } @@ -73,7 +73,7 @@ rewrite-gdbinit | launch-gdb-ssh-init function launch-gdb-ssh() { local -a sshargs - compute-ssh-args true "gdb -q" + compute-ssh-args true "gdb -q -x .\ghidra.gdbinit" "${sshargs[@]}" } diff --git a/Ghidra/Debug/Debugger-agent-gdb/data/support/gdbinit_template b/Ghidra/Debug/Debugger-agent-gdb/data/support/gdbinit_template index ab55be9810..1434dbe6ab 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/data/support/gdbinit_template +++ b/Ghidra/Debug/Debugger-agent-gdb/data/support/gdbinit_template @@ -1,8 +1,16 @@ +python +def try_import(): + try: + import ghidragdb + except Exception as e: + gdb.execute("exit(253)") +end + + set pagination off set confirm off show version -python import ghidragdb -python if not 'ghidragdb' in locals(): exit(253) +python try_import() set architecture $OPT_ARCH set endian $OPT_ENDIAN file '$OPT_TARGET_IMG' diff --git a/Ghidra/Debug/Debugger-agent-lldb/data/debugger-launchers/ssh-lldb-win.ps1 b/Ghidra/Debug/Debugger-agent-lldb/data/debugger-launchers/ssh-lldb-win.ps1 index 760998b2de..3fd12b7a1c 100644 --- a/Ghidra/Debug/Debugger-agent-lldb/data/debugger-launchers/ssh-lldb-win.ps1 +++ b/Ghidra/Debug/Debugger-agent-lldb/data/debugger-launchers/ssh-lldb-win.ps1 @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. ## -#@title lldb via ssh (remote=cmd shell) +#@title lldb via ssh (cmd shell) #@image-opt arg:1 #@desc #@desc

Launch with lldb via ssh to a remote Windows machine

diff --git a/Ghidra/Debug/Debugger-agent-lldb/data/debugger-launchers/ssh-lldb-win.sh b/Ghidra/Debug/Debugger-agent-lldb/data/debugger-launchers/ssh-lldb-win.sh index ea0cbab20f..20e783283a 100755 --- a/Ghidra/Debug/Debugger-agent-lldb/data/debugger-launchers/ssh-lldb-win.sh +++ b/Ghidra/Debug/Debugger-agent-lldb/data/debugger-launchers/ssh-lldb-win.sh @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. ## -#@title lldb via ssh (to cmd) +#@title lldb via ssh (cmd shell) #@image-opt arg:1 #@desc #@desc

Launch with lldb via ssh

diff --git a/Ghidra/Debug/Debugger-agent-x64dbg/data/debugger-launchers/local-x64dbg.ps1 b/Ghidra/Debug/Debugger-agent-x64dbg/data/debugger-launchers/local-x64dbg.ps1 index 89ee5794bb..e2099a571e 100644 --- a/Ghidra/Debug/Debugger-agent-x64dbg/data/debugger-launchers/local-x64dbg.ps1 +++ b/Ghidra/Debug/Debugger-agent-x64dbg/data/debugger-launchers/local-x64dbg.ps1 @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. ## -#@title x64dbg (.ps1) +#@title x64dbg #@image-opt env:OPT_TARGET_IMG #@desc #@desc

Launch with x64dbg

diff --git a/Ghidra/Debug/Debugger-agent-x64dbg/data/debugger-launchers/ssh-x64dbg-attach.ps1 b/Ghidra/Debug/Debugger-agent-x64dbg/data/debugger-launchers/ssh-x64dbg-attach.ps1 index a70cff8a90..1b55f480c1 100644 --- a/Ghidra/Debug/Debugger-agent-x64dbg/data/debugger-launchers/ssh-x64dbg-attach.ps1 +++ b/Ghidra/Debug/Debugger-agent-x64dbg/data/debugger-launchers/ssh-x64dbg-attach.ps1 @@ -53,6 +53,7 @@ function Compute-Python-Args { } $arglist+=($Env:OPT_TARGET_PID) + $arglist+=($Env:OPT_X64DBG_EXE) return $arglist } @@ -69,10 +70,10 @@ $sshproc = Start-Process -FilePath $sshargs[0] -ArgumentList $sshargs[1..$ssharg $version = Get-Ghidra-Version $answer = Check-Result-And-Prompt-Mitigation $sshproc @" -It appears ghidradbg is missing from the remote system. This can happen if you +It appears ghidraxdbg is missing from the remote system. This can happen if you forgot to install the required package. This can also happen if you installed the packages to a different Python environment than is being used by the -remote's gdb. +remote's x64dbg. This script is about to offer automatic resolution. If you'd like to resolve this manually, answer no to the next question and then see Ghidra's help by @@ -93,14 +94,14 @@ are copied and installed. NOTE: Automatic resolution will cause this session to terminate. When it has finished, try launching again. -"@ "Would you like to install 'ghidradbg>=$version'?" +"@ "Would you like to install 'ghidraxdbg>=$version'?" if ($answer) { Write-Host "Copying Wheels to $Env:OPT_HOST" Mitigate-Scp-PyModules "Debugger-rmi-trace" "" Write-Host "Installing Wheels into python" - $arglist = Compute-Dbg-PipInstall-Args "'-f'" "os.environ['HOME']" "'ghidradbg>=$version'" + $arglist = Compute-X64dbg-PipInstall-Args "'-f'" "os.environ['HOME']" "'ghidraxdbg>=$version'" $sshargs = Compute-Ssh-Args $arglist False Start-Process -FilePath $sshargs[0] -ArgumentList $sshargs[1..$sshargs.Count] -NoNewWindow -Wait } diff --git a/Ghidra/Debug/Debugger-agent-x64dbg/data/debugger-launchers/ssh-x64dbg.ps1 b/Ghidra/Debug/Debugger-agent-x64dbg/data/debugger-launchers/ssh-x64dbg.ps1 index a9d5baa51d..8da0081fd7 100644 --- a/Ghidra/Debug/Debugger-agent-x64dbg/data/debugger-launchers/ssh-x64dbg.ps1 +++ b/Ghidra/Debug/Debugger-agent-x64dbg/data/debugger-launchers/ssh-x64dbg.ps1 @@ -57,6 +57,9 @@ function Compute-Python-Args { if ("$Env:OPT_TARGET_IMG" -ne "") { $arglist+=($Env:OPT_TARGET_IMG) } + if ("$Env:OPT_X64DBG_EXE" -ne "") { + $arglist+=($Env:OPT_X64DBG_EXE) + } if ("$Env:OPT_TARGET_DIR" -ne "") { $arglist+=($Env:OPT_TARGET_DIR) } @@ -78,10 +81,10 @@ $sshproc = Start-Process -FilePath $sshargs[0] -ArgumentList $sshargs[1..$ssharg $version = Get-Ghidra-Version $answer = Check-Result-And-Prompt-Mitigation $sshproc @" -It appears ghidradbg is missing from the remote system. This can happen if you +It appears ghidraxdbg is missing from the remote system. This can happen if you forgot to install the required package. This can also happen if you installed the packages to a different Python environment than is being used by the -remote's gdb. +remote's x64dbg. This script is about to offer automatic resolution. If you'd like to resolve this manually, answer no to the next question and then see Ghidra's help by @@ -102,14 +105,14 @@ are copied and installed. NOTE: Automatic resolution will cause this session to terminate. When it has finished, try launching again. -"@ "Would you like to install 'ghidradbg>=$version'?" +"@ "Would you like to install 'ghidraxdbg>=$version'?" if ($answer) { Write-Host "Copying Wheels to $Env:OPT_HOST" Mitigate-Scp-PyModules "Debugger-rmi-trace" "" Write-Host "Installing Wheels into python" - $arglist = Compute-Dbg-PipInstall-Args "'-f'" "os.environ['HOME']" "'ghidradbg>=$version'" + $arglist = Compute-X64dbg-PipInstall-Args "'-f'" "os.environ['HOME']" "'ghidraxdbg>=$version'" $sshargs = Compute-Ssh-Args $arglist False Start-Process -FilePath $sshargs[0] -ArgumentList $sshargs[1..$sshargs.Count] -NoNewWindow -Wait } diff --git a/Ghidra/Debug/Debugger-agent-x64dbg/data/support/local-x64dbg-attach.py b/Ghidra/Debug/Debugger-agent-x64dbg/data/support/local-x64dbg-attach.py index b5845cfb89..6b6eda33c0 100644 --- a/Ghidra/Debug/Debugger-agent-x64dbg/data/support/local-x64dbg-attach.py +++ b/Ghidra/Debug/Debugger-agent-x64dbg/data/support/local-x64dbg-attach.py @@ -29,6 +29,7 @@ def parse_parameters(): if argc >= 3: cxn = sys.argv[1] target = sys.argv[2] + os.environ['OPT_X64DBG_EXE'] = sys.argv[3] return True print("Error: expected (cxn, target, initdir, ...)") return False @@ -48,6 +49,11 @@ def append_paths(): def main(): append_paths() # Delay these imports until sys.path is patched + try: + import ghidraxdbg + except: + print(e) + exit(253) from ghidraxdbg import commands as cmd from ghidraxdbg.hooks import on_state_changed from ghidraxdbg.util import dbg @@ -77,5 +83,7 @@ if __name__ == '__main__': try: main() except SystemExit as x: + if x.code == 253: + exit(253) if x.code != 0: print(f"Exited with code {x.code}") diff --git a/Ghidra/Debug/Debugger-agent-x64dbg/data/support/local-x64dbg.py b/Ghidra/Debug/Debugger-agent-x64dbg/data/support/local-x64dbg.py index 5d550299a6..5ca1447afa 100644 --- a/Ghidra/Debug/Debugger-agent-x64dbg/data/support/local-x64dbg.py +++ b/Ghidra/Debug/Debugger-agent-x64dbg/data/support/local-x64dbg.py @@ -17,7 +17,6 @@ import os import sys - cxn = os.getenv('GHIDRA_TRACE_RMI_ADDR') target = os.getenv('OPT_TARGET_IMG') args = os.getenv('OPT_TARGET_ARGS') @@ -32,12 +31,13 @@ def parse_parameters(): if argc >= 3: cxn = sys.argv[1] target = sys.argv[2] + os.environ['OPT_X64DBG_EXE'] = sys.argv[3] if argc > 4: - initdir = sys.argv[3] + initdir = sys.argv[4] else: initdir = "." - if argc > 4: - args = sys.argv[4] + if argc > 5: + args = sys.argv[5] else: args = "" return True @@ -63,6 +63,11 @@ def main(): return # Delay these imports until sys.path is patched + try: + import ghidraxdbg + except Exception as e: + print(e) + exit(253) from ghidraxdbg import commands as cmd from ghidraxdbg.hooks import on_state_changed from ghidraxdbg.util import dbg @@ -96,5 +101,7 @@ if __name__ == '__main__': try: main() except SystemExit as x: + if x.code == 253: + exit(253) if x.code != 0: print(f"Exited with code {x.code}") diff --git a/Ghidra/Debug/Debugger-agent-x64dbg/data/support/x64dbgsetuputils.ps1 b/Ghidra/Debug/Debugger-agent-x64dbg/data/support/x64dbgsetuputils.ps1 index 25d7388d65..35445ee68a 100644 --- a/Ghidra/Debug/Debugger-agent-x64dbg/data/support/x64dbgsetuputils.ps1 +++ b/Ghidra/Debug/Debugger-agent-x64dbg/data/support/x64dbgsetuputils.ps1 @@ -17,11 +17,6 @@ function Compute-X64dbg-PipInstall-Args { $argvpart = $args -join ", " - $arglist = @("$Env:OPT_PYTHON_EXE -c `"") - $arglist+=("import os, sys, runpy") - $arglist+=("sys.argv=['pip', 'install', '--force-reinstall', $argvpart]") - $arglist+=("os.environ['PIP_BREAK_SYSTEM_PACKAGE']='1'") - $arglist+=("runpy.run_module('pip', run_name='__main__')") - + $arglist = @("$Env:OPT_PYTHON_EXE -c `"import os, sys, runpy; sys.argv=['pip', 'install', '--force-reinstall', $argvpart]; os.environ['PIP_BREAK_SYSTEM_PACKAGE']='1'; runpy.run_module('pip', run_name='__main__')`"") return $arglist } diff --git a/Ghidra/Debug/Debugger-rmi-trace/data/support/setuputils.ps1 b/Ghidra/Debug/Debugger-rmi-trace/data/support/setuputils.ps1 index e068525c28..e59229cd59 100644 --- a/Ghidra/Debug/Debugger-rmi-trace/data/support/setuputils.ps1 +++ b/Ghidra/Debug/Debugger-rmi-trace/data/support/setuputils.ps1 @@ -71,7 +71,8 @@ function Compute-Ssh-Args { $cmdline = $arglist -join " " -replace "`"", "\`"" $sshargs = @("`"$Env:OPT_SSH_PATH`"") - $sshargs+=("-t") +# NB: Is this needed? It break target==Windows. +# $sshargs+=("-t") if ($forward) { $sshargs+=("-R$Env:OPT_REMOTE_PORT`:$Env:GHIDRA_TRACE_RMI_ADDR") } diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/AbstractRmiConnectorsTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/AbstractRmiConnectorsTest.java index b4d41f4bcc..ab5b0d04b7 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/AbstractRmiConnectorsTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/AbstractRmiConnectorsTest.java @@ -15,9 +15,8 @@ */ package agent; -import static org.junit.Assert.assertEquals; -import static org.junit.Assume.assumeFalse; -import static org.junit.Assume.assumeTrue; +import static org.junit.Assert.*; +import static org.junit.Assume.*; import java.io.FileOutputStream; import java.io.OutputStream; @@ -91,16 +90,24 @@ public abstract class AbstractRmiConnectorsTest assertEquals("pip failed", 0, result); } + protected boolean isWindows() { + return OperatingSystem.CURRENT_OPERATING_SYSTEM == OperatingSystem.WINDOWS; + } + protected PathIsFile chooseImage() { - if (OperatingSystem.CURRENT_OPERATING_SYSTEM == OperatingSystem.WINDOWS) { + if (isWindows()) { return new PathIsFile(Path.of("C:\\Windows\\notepad.exe")); } return new PathIsFile(Path.of("/bin/ls")); } + protected String chooseImageToString() { + return chooseImage().path().toString(); + } + protected PathIsFile findQemu(String bin) { - if (OperatingSystem.CURRENT_OPERATING_SYSTEM == OperatingSystem.WINDOWS) { - return new PathIsFile(Path.of("C:\\msys64\\ucrt64\bin\\").resolve(bin)); + if (isWindows()) { + return new PathIsFile(Path.of("C:\\msys64\\ucrt64\\bin\\").resolve(bin)); } return new PathIsFile(Path.of(bin)); } diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/DbgEngConnectorsTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/DbgEngConnectorsTest.java index 26e1712eec..c7229cc2a2 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/DbgEngConnectorsTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/dbgeng/rmi/DbgEngConnectorsTest.java @@ -16,13 +16,14 @@ package agent.dbgeng.rmi; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; import java.nio.file.Path; import java.util.List; import java.util.Map; +import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; @@ -52,7 +53,27 @@ public class DbgEngConnectorsTest extends AbstractRmiConnectorsTest { // Make sure system doesn't cause path failures to pass unpip("ghidradbg", "ghidratrace"); // Ensure a compatible version of protobuf - pip("protobuf==6.31.0"); + pip("protobuf>=6.31.0"); + } + + @Test + public void testLocalDbgWithImage() throws Exception { + try (LaunchResult result = doLaunch("dbgeng", Map.ofEntries( + Map.entry("env:OPT_TARGET_IMG", chooseImage()), + Map.entry("env:OPT_PYTHON_EXE", + new PathIsFile(Path.of(getPythonCmd())))))) { + checkResult(result); + } + } + + @Test + public void testLocalDbgWithImageBat() throws Exception { + try (LaunchResult result = doLaunch("dbgeng (.bat)", Map.ofEntries( + Map.entry("env:OPT_TARGET_IMG", chooseImage()), + Map.entry("env:OPT_PYTHON_EXE", + new PathIsFile(Path.of(getPythonCmd())))))) { + checkResult(result); + } } @Test @@ -61,7 +82,7 @@ public class DbgEngConnectorsTest extends AbstractRmiConnectorsTest { try (LaunchResult result = doLaunch("dbgeng", Map.ofEntries( Map.entry("env:OPT_TARGET_IMG", chooseImage()), Map.entry("env:OPT_PYTHON_EXE", - new PathIsFile(Path.of("C:\\Python313\\python.exe")))))) { + new PathIsFile(Path.of(getPythonCmd())))))) { assertThat(result.exception(), instanceOf(EarlyTerminationException.class)); assertThat(result.sessions().get("Shell").content(), containsString("Would you like to install")); @@ -69,17 +90,77 @@ public class DbgEngConnectorsTest extends AbstractRmiConnectorsTest { try (LaunchResult result = doLaunch("dbgeng", Map.ofEntries( Map.entry("env:OPT_TARGET_IMG", chooseImage()), Map.entry("env:OPT_PYTHON_EXE", - new PathIsFile(Path.of("C:\\Python313\\python.exe")))))) { + new PathIsFile(Path.of(getPythonCmd())))))) { checkResult(result); } } @Test - public void testLocalDbgWithImage() throws Exception { - try (LaunchResult result = doLaunch("dbgeng", Map.ofEntries( + public void testLocalDbgSetupBat() throws Exception { + pipOob("protobuf==3.19.0"); + try (LaunchResult result = doLaunch("dbgeng (.bat)", Map.ofEntries( Map.entry("env:OPT_TARGET_IMG", chooseImage()), Map.entry("env:OPT_PYTHON_EXE", - new PathIsFile(Path.of("C:\\Python313\\python.exe")))))) { + new PathIsFile(Path.of(getPythonCmd())))))) { + assertThat(result.exception(), instanceOf(EarlyTerminationException.class)); + assertThat(result.sessions().get("Shell").content(), + containsString("Would you like to install")); + } + try (LaunchResult result = doLaunch("dbgeng (.bat)", Map.ofEntries( + Map.entry("env:OPT_TARGET_IMG", chooseImage()), + Map.entry("env:OPT_PYTHON_EXE", + new PathIsFile(Path.of(getPythonCmd())))))) { + checkResult(result); + } + } + + // NB: The next three tests tend to leave residual python processes running + // which will cause permissions problems when subsequent tests attempt + // to access python's site-packages + + @Test + public void testDbgViaSsh() throws Exception { + pip("ghidradbg==%s".formatted(Application.getApplicationVersion())); + try (LaunchResult result = doLaunch("dbgeng via ssh", Map.ofEntries( + Map.entry("env:OPT_TARGET_IMG", chooseImage()), + Map.entry("OPT_HOST", "localhost")))) { + checkResult(result); + } + } + + @Test + public void testDbgViaSshSetupProtobuf() throws Exception { + // NB: If you fail on the next line, delete everything (ghidradbg, + // ghidratrace, google, protobuf) from site-packages + pip("ghidradbg==%s".formatted(Application.getApplicationVersion())); + // Overwrite with an incompatible version we don't include + pipOob("protobuf==3.19.0"); + try (LaunchResult result = doLaunch("dbgeng via ssh", Map.ofEntries( + Map.entry("env:OPT_TARGET_IMG", chooseImage()), + Map.entry("OPT_HOST", "localhost")))) { + assertTrue(result.exception() instanceof EarlyTerminationException); + assertThat(result.sessions().get("Shell").content(), + Matchers.containsString("Would you like to install")); + } + try (LaunchResult result = doLaunch("dbgeng via ssh", Map.ofEntries( + Map.entry("env:OPT_TARGET_IMG", chooseImage()), + Map.entry("OPT_HOST", "localhost")))) { + checkResult(result); + } + } + + @Test + public void testDbgViaSshSetupGhidraDbg() throws Exception { + try (LaunchResult result = doLaunch("dbgeng via ssh", Map.ofEntries( + Map.entry("env:OPT_TARGET_IMG", chooseImage()), + Map.entry("OPT_HOST", "localhost")))) { + assertTrue(result.exception() instanceof EarlyTerminationException); + assertThat(result.sessions().get("Shell").content(), + Matchers.containsString("Would you like to install")); + } + try (LaunchResult result = doLaunch("dbgeng via ssh", Map.ofEntries( + Map.entry("env:OPT_TARGET_IMG", chooseImage()), + Map.entry("OPT_HOST", "localhost")))) { checkResult(result); } } diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/gdb/rmi/GdbConnectorsTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/gdb/rmi/GdbConnectorsTest.java index 63b5a32967..9335e4427d 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/gdb/rmi/GdbConnectorsTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/gdb/rmi/GdbConnectorsTest.java @@ -16,7 +16,7 @@ package agent.gdb.rmi; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.nio.file.Path; import java.util.List; @@ -53,7 +53,11 @@ public class GdbConnectorsTest extends AbstractRmiConnectorsTest { // Make sure system doesn't cause path failures to pass unpip("ghidragdb", "ghidratrace"); // Ensure a compatible version of protobuf - pip("protobuf==6.31.0"); + pip("protobuf>=6.31.0"); + } + + private String sshLauncherTitle() { + return isWindows() ? "gdb via ssh (cmd shell)" : "gdb via ssh"; } @Test @@ -102,7 +106,7 @@ public class GdbConnectorsTest extends AbstractRmiConnectorsTest { } programManager.openProgram(program); try (LaunchResult result = doLaunch("gdb + qemu-system", Map.ofEntries( - Map.entry("arg:1", dummy), + Map.entry(isWindows() ? "env:OPT_TARGET_IMG" : "arg:1", dummy), Map.entry("env:OPT_GDB_PATH", new PathIsFile(Path.of("gdb"))), Map.entry("env:GHIDRA_LANG_EXTTOOL_qemu_system", findQemu("qemu-system-aarch64")), Map.entry("env:OPT_EXTRA_QEMU_ARGS", "-machine virt")))) { @@ -127,8 +131,8 @@ public class GdbConnectorsTest extends AbstractRmiConnectorsTest { @Test public void testGdbViaSsh() throws Exception { pip("ghidragdb==%s".formatted(Application.getApplicationVersion())); - try (LaunchResult result = doLaunch("gdb via ssh", Map.ofEntries( - Map.entry("arg:1", "/bin/ls"), + try (LaunchResult result = doLaunch(sshLauncherTitle(), Map.ofEntries( + Map.entry("arg:1", chooseImage().path().toFile().getAbsolutePath()), Map.entry("OPT_HOST", "localhost")))) { checkResult(result); } @@ -136,15 +140,15 @@ public class GdbConnectorsTest extends AbstractRmiConnectorsTest { @Test public void testGdbViaSshSetupGhidraGdb() throws Exception { - try (LaunchResult result = doLaunch("gdb via ssh", Map.ofEntries( - Map.entry("arg:1", "/bin/ls"), + try (LaunchResult result = doLaunch(sshLauncherTitle(), Map.ofEntries( + Map.entry("arg:1", chooseImageToString()), Map.entry("OPT_HOST", "localhost")))) { assertTrue(result.exception() instanceof EarlyTerminationException); assertThat(result.sessions().get("Shell").content(), Matchers.containsString("Would you like to install")); } - try (LaunchResult result = doLaunch("gdb via ssh", Map.ofEntries( - Map.entry("arg:1", "/bin/ls"), + try (LaunchResult result = doLaunch(sshLauncherTitle(), Map.ofEntries( + Map.entry("arg:1", chooseImageToString()), Map.entry("OPT_HOST", "localhost")))) { checkResult(result); } @@ -152,18 +156,20 @@ public class GdbConnectorsTest extends AbstractRmiConnectorsTest { @Test public void testGdbViaSshSetupProtobuf() throws Exception { + // NB: If you fail on the next line, delete everything (ghidragdb, + // ghidratrace, google, protobuf) from site-packages pip("ghidragdb==%s".formatted(Application.getApplicationVersion())); // Overwrite with an incompatible version we don't include pipOob("protobuf==3.19.0"); - try (LaunchResult result = doLaunch("gdb via ssh", Map.ofEntries( - Map.entry("arg:1", "/bin/ls"), + try (LaunchResult result = doLaunch(sshLauncherTitle(), Map.ofEntries( + Map.entry("arg:1", chooseImageToString()), Map.entry("OPT_HOST", "localhost")))) { assertTrue(result.exception() instanceof EarlyTerminationException); assertThat(result.sessions().get("Shell").content(), Matchers.containsString("Would you like to install")); } - try (LaunchResult result = doLaunch("gdb via ssh", Map.ofEntries( - Map.entry("arg:1", "/bin/ls"), + try (LaunchResult result = doLaunch(sshLauncherTitle(), Map.ofEntries( + Map.entry("arg:1", chooseImageToString()), Map.entry("OPT_HOST", "localhost")))) { checkResult(result); } diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/LldbConnectorsTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/LldbConnectorsTest.java index 7193d83f29..84e961bc6a 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/LldbConnectorsTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/lldb/rmi/LldbConnectorsTest.java @@ -16,8 +16,8 @@ package agent.lldb.rmi; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeFalse; +import static org.junit.Assert.*; +import static org.junit.Assume.*; import java.nio.file.Path; import java.util.List; @@ -36,7 +36,6 @@ import ghidra.app.util.importer.MessageLog; import ghidra.debug.api.action.AutoMapSpec; import ghidra.debug.api.tracermi.TraceRmiLaunchOffer.LaunchResult; import ghidra.framework.Application; -import ghidra.framework.OperatingSystem; import ghidra.framework.plugintool.AutoConfigState.PathIsFile; import ghidra.pty.testutil.DummyProc; @@ -60,7 +59,11 @@ public class LldbConnectorsTest extends AbstractRmiConnectorsTest { // Make sure system doesn't cause path failures to pass unpip("ghidralldb", "ghidratrace"); // Ensure a compatible version of protobuf - pip("protobuf==6.31.0"); + pip("protobuf>=6.31.0"); + } + + private String sshLauncherTitle() { + return isWindows() ? "lldb via ssh (cmd shell)" : "lldb via ssh"; } /** @@ -72,6 +75,7 @@ public class LldbConnectorsTest extends AbstractRmiConnectorsTest { * @throws Exception because */ @Test + @Ignore("TODO") public void testLocalLldbSetup() throws Exception { pipOob("protobuf==3.19.0"); try (LaunchResult result = doLaunch("lldb", Map.of("arg:1", chooseImage()))) { @@ -94,7 +98,7 @@ public class LldbConnectorsTest extends AbstractRmiConnectorsTest { @Test @Ignore("TODO") public void testLldbQemuUser() throws Exception { - assumeFalse(OperatingSystem.WINDOWS == OperatingSystem.CURRENT_OPERATING_SYSTEM); + assumeFalse(isWindows()); PathIsFile image = createArmElfImage(); program = AutoImporter.importByUsingBestGuess(image.path().toFile(), null, "/", this, new MessageLog(), monitor).getPrimaryDomainObject(); @@ -151,43 +155,45 @@ public class LldbConnectorsTest extends AbstractRmiConnectorsTest { @Test public void testLldbViaSsh() throws Exception { pip("ghidralldb==%s".formatted(Application.getApplicationVersion())); - try (LaunchResult result = doLaunch("lldb via ssh", Map.ofEntries( - Map.entry("arg:1", "/bin/ls"), + try (LaunchResult result = doLaunch(sshLauncherTitle(), Map.ofEntries( + Map.entry("arg:1", chooseImageToString()), Map.entry("OPT_HOST", "localhost")))) { checkResult(result); } } @Test + @Ignore("TODO") public void testLldbViaSshSetupGhidraLldb() throws Exception { - try (LaunchResult result = doLaunch("lldb via ssh", Map.ofEntries( - Map.entry("arg:1", "/bin/ls"), + try (LaunchResult result = doLaunch(sshLauncherTitle(), Map.ofEntries( + Map.entry("arg:1", chooseImageToString()), Map.entry("OPT_HOST", "localhost")))) { assertTrue(result.exception() instanceof EarlyTerminationException); assertThat(result.sessions().get("Shell").content(), Matchers.containsString("Would you like to install")); } - try (LaunchResult result = doLaunch("lldb via ssh", Map.ofEntries( - Map.entry("arg:1", "/bin/ls"), + try (LaunchResult result = doLaunch(sshLauncherTitle(), Map.ofEntries( + Map.entry("arg:1", chooseImageToString()), Map.entry("OPT_HOST", "localhost")))) { checkResult(result); } } @Test + @Ignore("TODO") public void testLldbViaSshSetupProtobuf() throws Exception { pip("ghidralldb==%s".formatted(Application.getApplicationVersion())); // Overwrite with an incompatible version we don't include pipOob("protobuf==3.19.0"); - try (LaunchResult result = doLaunch("lldb via ssh", Map.ofEntries( - Map.entry("arg:1", "/bin/ls"), + try (LaunchResult result = doLaunch(sshLauncherTitle(), Map.ofEntries( + Map.entry("arg:1", chooseImageToString()), Map.entry("OPT_HOST", "localhost")))) { assertTrue(result.exception() instanceof EarlyTerminationException); assertThat(result.sessions().get("Shell").content(), Matchers.containsString("Would you like to install")); } - try (LaunchResult result = doLaunch("lldb via ssh", Map.ofEntries( - Map.entry("arg:1", "/bin/ls"), + try (LaunchResult result = doLaunch(sshLauncherTitle(), Map.ofEntries( + Map.entry("arg:1", chooseImageToString()), Map.entry("OPT_HOST", "localhost")))) { checkResult(result); } diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/X64dbgConnectorsTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/X64dbgConnectorsTest.java new file mode 100644 index 0000000000..6eb0bb9b27 --- /dev/null +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/X64dbgConnectorsTest.java @@ -0,0 +1,177 @@ +/* ### + * 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 agent.x64dbg.rmi; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.nio.file.Path; +import java.util.List; +import java.util.Map; + +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; + +import agent.AbstractRmiConnectorsTest; +import generic.jar.ResourceFile; +import ghidra.app.plugin.core.debug.gui.tracermi.launcher.AbstractTraceRmiLaunchOffer.EarlyTerminationException; +import ghidra.debug.api.tracermi.TraceRmiLaunchOffer.LaunchResult; +import ghidra.framework.Application; +import ghidra.framework.plugintool.AutoConfigState.PathIsFile; + +public class X64dbgConnectorsTest extends AbstractRmiConnectorsTest { + + @Override + protected String getPythonCmd() { + return "C:\\Python313\\python"; + } + + @Override + protected List getPipLinkModules() { + return List.of( + Application.getModuleRootDir("Debugger-rmi-trace"), + Application.getModuleRootDir("Debugger-agent-x64dbg")); + } + + @Before + public void setUpX64Dbg() throws Exception { + // Make sure system doesn't cause path failures to pass + unpip("ghidraxdbg", "ghidratrace"); + // Ensure a compatible version of protobuf + pip("protobuf>=6.31.0"); + } + + @Test + public void testLocalX64dbgWithImage() throws Exception { + try (LaunchResult result = doLaunch("x64dbg", Map.ofEntries( + Map.entry("env:OPT_TARGET_IMG", chooseImage()), + Map.entry("env:OPT_X64DBG_EXE", new PathIsFile(Path.of("x64dbg.exe"))), + Map.entry("env:OPT_PYTHON_EXE", + new PathIsFile(Path.of(getPythonCmd())))))) { + checkResult(result); + } + } + + @Test + public void testLocalX64dbgWithImageBat() throws Exception { + try (LaunchResult result = doLaunch("x64dbg (.bat)", Map.ofEntries( + Map.entry("env:OPT_TARGET_IMG", chooseImage()), + Map.entry("env:OPT_PYTHON_EXE", + new PathIsFile(Path.of(getPythonCmd())))))) { + checkResult(result); + } + } + + @Test + public void testLocalX64dbgSetup() throws Exception { + pipOob("protobuf==3.19.0"); + try (LaunchResult result = doLaunch("x64dbg", Map.ofEntries( + Map.entry("env:OPT_TARGET_IMG", chooseImage()), + Map.entry("env:OPT_X64DBG_EXE", new PathIsFile(Path.of("x64dbg.exe"))), + Map.entry("env:OPT_PYTHON_EXE", + new PathIsFile(Path.of(getPythonCmd())))))) { + assertThat(result.exception(), instanceOf(EarlyTerminationException.class)); + assertThat(result.sessions().get("Shell").content(), + containsString("Would you like to install")); + } + try (LaunchResult result = doLaunch("x64dbg", Map.ofEntries( + Map.entry("env:OPT_TARGET_IMG", chooseImage()), + Map.entry("env:OPT_X64DBG_EXE", new PathIsFile(Path.of("x64dbg.exe"))), + Map.entry("env:OPT_PYTHON_EXE", + new PathIsFile(Path.of(getPythonCmd())))))) { + checkResult(result); + } + } + + @Test + public void testLocalX64dbgSetupBat() throws Exception { + pipOob("protobuf==3.19.0"); + try (LaunchResult result = doLaunch("x64dbg (.bat)", Map.ofEntries( + Map.entry("env:OPT_TARGET_IMG", chooseImage()), + Map.entry("env:OPT_PYTHON_EXE", + new PathIsFile(Path.of(getPythonCmd())))))) { + assertThat(result.exception(), instanceOf(EarlyTerminationException.class)); + assertThat(result.sessions().get("Shell").content(), + containsString("Would you like to install")); + } + try (LaunchResult result = doLaunch("x64dbg (.bat)", Map.ofEntries( + Map.entry("env:OPT_TARGET_IMG", chooseImage()), + Map.entry("env:OPT_PYTHON_EXE", + new PathIsFile(Path.of(getPythonCmd())))))) { + checkResult(result); + } + } + + // NB: The next three tests tend to leave residual python processes running + // which will cause permissions problems when subsequent tests attempt + // to access python's site-packages + + @Test + public void testX64dbgViaSsh() throws Exception { + pip("ghidratrace==%s".formatted(Application.getApplicationVersion())); + pip("ghidraxdbg==%s".formatted(Application.getApplicationVersion())); + try (LaunchResult result = doLaunch("x64dbg via ssh", Map.ofEntries( + Map.entry("env:OPT_TARGET_IMG", chooseImage()), + Map.entry("env:OPT_X64DBG_EXE", new PathIsFile(Path.of("x64dbg.exe"))), + Map.entry("OPT_HOST", "localhost")))) { + checkResult(result); + } + } + + @Test + public void testX64dbgViaSshSetupProtobuf() throws Exception { + // NB: If you fail on the next line, delete everything (ghidradbg, + // ghidratrace, google, protobuf) from site-packages + pip("ghidraxdbg==%s".formatted(Application.getApplicationVersion())); + // Overwrite with an incompatible version we don't include + pipOob("protobuf==3.19.0"); + try (LaunchResult result = doLaunch("x64dbg via ssh", Map.ofEntries( + Map.entry("env:OPT_TARGET_IMG", chooseImage()), + Map.entry("env:OPT_X64DBG_EXE", new PathIsFile(Path.of("x64dbg.exe"))), + Map.entry("OPT_HOST", "localhost")))) { + assertTrue(result.exception() instanceof EarlyTerminationException); + assertThat(result.sessions().get("Shell").content(), + Matchers.containsString("Would you like to install")); + } + try (LaunchResult result = doLaunch("x64dbg via ssh", Map.ofEntries( + Map.entry("env:OPT_TARGET_IMG", chooseImage()), + Map.entry("env:OPT_X64DBG_EXE", new PathIsFile(Path.of("x64dbg.exe"))), + Map.entry("OPT_HOST", "localhost")))) { + checkResult(result); + } + } + + @Test + public void testX64dbgViaSshSetupGhidraDbg() throws Exception { + try (LaunchResult result = doLaunch("x64dbg via ssh", Map.ofEntries( + Map.entry("env:OPT_TARGET_IMG", chooseImage()), + Map.entry("env:OPT_X64DBG_EXE", new PathIsFile(Path.of("x64dbg.exe"))), + Map.entry("OPT_HOST", "localhost")))) { + assertTrue(result.exception() instanceof EarlyTerminationException); + assertThat(result.sessions().get("Shell").content(), + Matchers.containsString("Would you like to install")); + } + try (LaunchResult result = doLaunch("x64dbg via ssh", Map.ofEntries( + Map.entry("env:OPT_TARGET_IMG", chooseImage()), + Map.entry("env:OPT_X64DBG_EXE", new PathIsFile(Path.of("x64dbg.exe"))), + Map.entry("OPT_HOST", "localhost")))) { + checkResult(result); + } + } + +}