GP-6966: fix for tests

GP-6966: post-review
GP-6966: in review
GP-6966: not all tests pass, but...
GP-6966: better messaging
GP-6966: mods for x64dbg
GP-6966: docs (GP-6972)
GP-6966: fixes for linux->windows
GP-6966: fix for exit; fix for WINDBG_DIR
This commit is contained in:
d-millar
2026-06-24 11:29:29 -04:00
parent 09f298a51c
commit bd72532653
28 changed files with 256 additions and 99 deletions

View File

@@ -31,7 +31,7 @@
#@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."
#@env OPT_PYTHON_ARGS:str="" "python cmd args" "Arguments passed to python (versus the target)"
#@env OPT_USE_DBGMODEL:bool=true "Use dbgmodel" "Load and use dbgmodel.dll if it is available."
#@env WINDBG_DIR:dir="" "Path to dbgeng.dll directory" "Path containing dbgeng and associated DLLS (if not Windows Kits)."
#@env WINDBG_DIR:dir="C:\Windows\System32" "Path to dbgeng.dll directory" "Path containing dbgeng and associated DLLS (if not Windows Kits)."
. ..\support\dbgsetuputils.ps1
@@ -47,6 +47,7 @@ function Compute-Python-Args {
$arglist+=($Env:GHIDRA_TRACE_RMI_ADDR)
$arglist+=($Env:OPT_USE_DBGMODEL)
$arglist+=($Env:WINDBG_DIR)
$arglist+=($Env:OPT_TARGET_IMG)
if ("$Env:OPT_TARGET_ARGS" -ne "") {

View File

@@ -34,6 +34,7 @@
#@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."
#@env OPT_PYTHON_ARGS:str="" "python cmd args" "Arguments passed to python (versus the target)"
#@env OPT_USE_DBGMODEL:bool=true "Use dbgmodel" "Load and use dbgmodel.dll if it is available."
#@env WINDBG_DIR:dir="C:\Windows\System32" "Path to dbgeng.dll directory" "Path containing dbgeng and associated DLLS (if not Windows Kits)."
. ..\support\dbgsetuputils.ps1
@@ -49,6 +50,7 @@ function Compute-Python-Args {
$arglist+=("localhost:$Env:OPT_REMOTE_PORT")
$arglist+=($Env:OPT_USE_DBGMODEL)
$arglist+=($Env:WINDBG_DIR)
$arglist+=($Env:OPT_TARGET_PID)
$arglist+=($Env:OPT_ATTACH_FLAGS)

View File

@@ -15,7 +15,6 @@
# limitations under the License.
##
#@title dbgeng attach via ssh (shell)
#@image-opt arg:1
#@desc <html><body width="300px">
#@desc <h3>Launch with <tt>dbgeng</tt> via <tt>ssh</tt></h3>
#@desc <p>
@@ -36,11 +35,10 @@
#@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."
#@env OPT_PYTHON_ARGS:str="" "python cmd args" "Arguments passed to python (versus the target)"
#@env OPT_USE_DBGMODEL:bool=true "Use dbgmodel" "Load and use dbgmodel.dll if it is available."
#@env WINDBG_DIR:dir="C:\\Windows\\System32" "Path to dbgeng.dll directory" "Path containing dbgeng and associated DLLS (if not Windows Kits)."
. ../support/dbgsetuputils.sh
target_image=$(echo $OPT_TARGET_IMG | sed 's/\\/\\\\/g')
OPT_OS_WINDOWS=true
function launch-dbg-scp() {
@@ -52,7 +50,7 @@ function launch-dbg-scp() {
function launch-dbg-ssh() {
local -a sshargs
compute-ssh-args true "$OPT_PYTHON_EXE -i $OPT_PYTHON_ARGS .\\local-dbgeng-attach.py localhost:$OPT_REMOTE_PORT $OPT_USE_DBGMODEL $OPT_TARGET_PID $OPT_ATTACH_FLAGS"
compute-ssh-args true "$OPT_PYTHON_EXE -i $OPT_PYTHON_ARGS .\\local-dbgeng-attach.py localhost:$OPT_REMOTE_PORT $OPT_USE_DBGMODEL $WINDBG_DIR $OPT_TARGET_PID $OPT_ATTACH_FLAGS"
"${sshargs[@]}"
}
@@ -98,7 +96,9 @@ finished, try launching again.
" "Would you like to install 'ghidradbg>=$version'?"; then
echo "Copying Wheels to $OPT_HOST"
mitigate-scp-pymodules "Debugger-rmi-trace" "<SELF>"
if ! mitigate-scp-pymodules "Debugger-rmi-trace" "<SELF>"; then
exit 1
fi
echo "Installing Wheels into Python"
do-installation

View File

@@ -26,7 +26,7 @@
#@icon icon.debugger
#@help dbgeng#ssh
#@depends Debugger-rmi-trace
#@env OPT_TARGET_IMG:file="" "Image" "The target binary executable image"
#@env OPT_TARGET_IMG:str="" "Image" "The target binary executable image"
#@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_HOST:str="localhost" "[User@]Host" "The hostname or user@host"
@@ -35,6 +35,7 @@
#@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."
#@env OPT_PYTHON_ARGS:str="" "python cmd args" "Arguments passed to python (versus the target)"
#@env OPT_USE_DBGMODEL:bool=true "Use dbgmodel" "Load and use dbgmodel.dll if it is available."
#@env WINDBG_DIR:dir="C:\Windows\System32" "Path to dbgeng.dll directory" "Path containing dbgeng and associated DLLS (if not Windows Kits)."
. ..\support\dbgsetuputils.ps1
@@ -50,6 +51,7 @@ function Compute-Python-Args {
$arglist+=("localhost:$Env:OPT_REMOTE_PORT")
$arglist+=($Env:OPT_USE_DBGMODEL)
$arglist+=($Env:WINDBG_DIR)
$arglist+=($Env:OPT_TARGET_IMG)
if ("$Env:OPT_TARGET_ARGS" -ne "") {

View File

@@ -15,7 +15,7 @@
# limitations under the License.
##
#@title dbgeng via ssh (shell)
#@image-opt arg:1
#@image-opt env:OPT_TARGET_IMG
#@desc <html><body width="300px">
#@desc <h3>Launch with <tt>dbgeng</tt> via <tt>ssh</tt></h3>
#@desc <p>
@@ -27,7 +27,7 @@
#@icon icon.debugger
#@help dbgeng#ssh
#@depends Debugger-rmi-trace
#@env OPT_TARGET_IMG:file="" "Image" "The target binary executable image"
#@env OPT_TARGET_IMG:str="" "Image" "The target binary executable image"
#@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_HOST:str="localhost" "[User@]Host" "The hostname or user@host"
@@ -36,6 +36,7 @@
#@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."
#@env OPT_PYTHON_ARGS:str="" "python cmd args" "Arguments passed to python (versus the target)"
#@env OPT_USE_DBGMODEL:bool=true "Use dbgmodel" "Load and use dbgmodel.dll if it is available."
#@env WINDBG_DIR:dir="C:\\Windows\\System32" "Path to dbgeng.dll directory" "Path containing dbgeng and associated DLLS (if not Windows Kits)."
. ../support/dbgsetuputils.sh
@@ -52,7 +53,7 @@ function launch-dbg-scp() {
function launch-dbg-ssh() {
local -a sshargs
compute-ssh-args true "$OPT_PYTHON_EXE -i $OPT_PYTHON_ARGS .\\local-dbgeng.py localhost:$OPT_REMOTE_PORT $OPT_USE_DBGMODEL $target_image $OPT_TARGET_ARGS"
compute-ssh-args true "$OPT_PYTHON_EXE -i $OPT_PYTHON_ARGS .\\local-dbgeng.py localhost:$OPT_REMOTE_PORT $OPT_USE_DBGMODEL $WINDBG_DIR $target_image $OPT_TARGET_ARGS"
"${sshargs[@]}"
}
@@ -98,7 +99,9 @@ finished, try launching again.
" "Would you like to install 'ghidradbg>=$version'?"; then
echo "Copying Wheels to $OPT_HOST"
mitigate-scp-pymodules "Debugger-rmi-trace" "<SELF>"
if ! mitigate-scp-pymodules "Debugger-rmi-trace" "<SELF>"; then
exit 1
fi
echo "Installing Wheels into Python"
do-installation

View File

@@ -20,5 +20,5 @@ compute-dbg-pipinstall-args() {
printf -v argvpart ", %s" "$@"
pipargs=("$OPT_PYTHON_EXE")
pipargs+=(-c)
pipargs+=("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__')")
pipargs+=("\"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__')\"")
}

View File

@@ -28,7 +28,13 @@ def append_paths():
def main():
append_paths()
# Delay these imports until sys.path is patched
try:
import ghidradbg
except ModuleNotFoundError:
os._exit(253)
from ghidradbg import commands as cmd
from ghidradbg import util
from pybag.dbgeng import core as DbgEng

View File

@@ -27,14 +27,17 @@ def parse_parameters():
global cxn, target, args
os.environ['OPT_OS_WINDOWS'] = "true"
argc = len(sys.argv)
# This is the .bat case:
if argc == 1:
return True
if argc >= 4:
# Local and remote .ps1 cases
if argc >= 5:
cxn = sys.argv[1]
os.environ['OPT_USE_DBGMODEL'] = sys.argv[2]
target = sys.argv[3]
if argc > 4:
args = sys.argv[4]
os.environ['WINDBG_DIR'] = sys.argv[3]
target = sys.argv[4]
if argc > 5:
args = sys.argv[5]
return True
print("Error: expected (cxn, use_dbgmodel, target, ...)")
return False
@@ -59,8 +62,9 @@ def main():
# Delay these imports until sys.path is patched
try:
import ghidradbg
except Exception as e:
exit(253)
except ModuleNotFoundError:
os._exit(253)
from ghidradbg import commands as cmd
from pybag.dbgeng import core as DbgEng
from ghidradbg.hooks import on_state_changed
@@ -91,7 +95,5 @@ 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}")

View File

@@ -28,7 +28,13 @@ def append_paths():
def main():
append_paths()
# Delay these imports until sys.path is patched
try:
import ghidradbg
except ModuleNotFoundError:
os._exit(253)
from ghidradbg import commands as cmd
from pybag.dbgeng import core as DbgEng
from ghidradbg.hooks import on_state_changed

View File

@@ -28,7 +28,13 @@ def append_paths():
def main():
append_paths()
# Delay these imports until sys.path is patched
try:
import ghidradbg
except ModuleNotFoundError:
os._exit(253)
from ghidradbg import commands as cmd
from pybag.dbgeng import core as DbgEng
from ghidradbg.hooks import on_state_changed

View File

@@ -26,16 +26,19 @@ args = os.getenv('OPT_TARGET_ARGS')
def parse_parameters():
global cxn, target, args
argc = len(sys.argv)
# This is the .bat case:
if argc == 1:
return True
if argc >= 4:
# Local and remote .ps1 cases
if argc >= 5:
cxn = sys.argv[1]
os.environ['OPT_USE_DBGMODEL'] = sys.argv[2]
target = sys.argv[3]
if argc > 4:
args = sys.argv[4]
os.environ['WINDBG_DIR'] = sys.argv[3]
target = sys.argv[4]
if argc > 5:
args = sys.argv[5]
return True
print("Error: expected (cxn, use_dbgmodel, target, ...)")
print("Error: expected (cxn, use_dbgmodel, windbg_dir, target, ...)")
return False
def append_paths():
@@ -52,14 +55,16 @@ def append_paths():
def main():
global cxn, target, args
append_paths()
if parse_parameters() is False:
return
# Delay these imports until sys.path is patched
try:
import ghidradbg
except:
exit(253)
except ModuleNotFoundError:
os._exit(253)
from ghidradbg import commands as cmd
from pybag.dbgeng import core as DbgEng
from ghidradbg.hooks import on_state_changed
@@ -97,7 +102,5 @@ 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}")

View File

@@ -28,7 +28,13 @@ def append_paths():
def main():
append_paths()
# Delay these imports until sys.path is patched
try:
import ghidradbg
except ModuleNotFoundError:
os._exit(253)
from ghidradbg import commands as cmd
from pybag.dbgeng import core as DbgEng
from ghidradbg.hooks import on_state_changed

View File

@@ -28,7 +28,13 @@ def append_paths():
def main():
append_paths()
# Delay these imports until sys.path is patched
try:
import ghidradbg
except ModuleNotFoundError:
os._exit(253)
from ghidradbg import commands as cmd
from pybag.dbgeng import core as DbgEng
from ghidradbg.hooks import on_state_changed

View File

@@ -35,6 +35,26 @@
might work around this by copying the affected DLLs from your WinDbg installation into your
Python installation.</P>
<P>Also worth noting, there are now two local launchers, the original .bat-based launcher and a
newer .ps1-based launcher. The powershell launcher is not signed. You must therefore set the
current user's execution policy or permissions on the script:</P>
<UL style="list-style-type: none">
<LI>
<PRE>
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
</PRE>
</LI>
<LI style="list-style: none">or</LI>
<LI>
<PRE>
Unblock-File "Ghidra\Debug\Debugger-agent-dbgeng\data\debugger-launchers\local-dbgeng.ps1"
</PRE>
</LI>
</UL>
<H3><A name="setup"></A>Setup</H3>
<P>Installing WinDbg is highly recommended. If you wish to forego installing WinDbg, you can
@@ -48,7 +68,7 @@
<UL style="list-style-type: none">
<LI>
<PRE>
python3 -m pip install pybag protobuf>=3.20.3
python3 -m pip install pybag protobuf&gt;=3.20.3
</PRE>
</LI>
</UL>
@@ -123,7 +143,7 @@ python3 -m pip install --no-index -f Debugger-rmi-trace\pypkg\dist -f Debugger-a
<LI><B>Env</B>: This is a composite string containg Environment Variable entries delineated
by '/0' separators. For example, you could redefine USERNAME and USERPROFILE with the entry
'USERNAME=SomeUser/0USERPROFILE=C:\Users\SomeUser'.</LI>
"<TT>USERNAME=SomeUser/0USERPROFILE=C:\Users\SomeUser</TT>".</LI>
<LI><B>CreateFlags</B>: Flags used when creating the process, typically either
DEBUG_PROCESS(1) or DEBUG_ONLY_THIS_PROCESS(2) if you do not wish to follow spawned
@@ -163,42 +183,45 @@ python3 -m pip install --no-index -f Debugger-rmi-trace\pypkg\dist -f Debugger-a
<LI>
<B>Connection</B>: This is the connection string specifying the transport options for
communicating with the remote debugger. A typical example might be
'tcp:port=12345,server=192.168.0.2' for a debugger that has issued the command
"<TT>tcp:port=12345,server=192.168.0.2</TT>" for a debugger that has issued the command
<PRE>
.server tcp:port=12345
</PRE>
</LI>
</UL>
<H2><A name="ssh"></A>Remote via SSH</H2>
<H2><A name="ssh"></A>Remote via SSH</H2>
<P>"<TT>ssh-dbgeng</TT>" is the remote equivalent to "<TT>dbgeng</TT>";
"<TT>ssh-dbgeng-attach</TT>" is the remote equivalent to "<TT>dbgeng-attach</TT>".
</P>
<P>"<TT>ssh-dbgeng</TT>" is the remote equivalent to "<TT>dbgeng</TT>";
"<TT>ssh-dbgeng-attach</TT>" is the remote equivalent to "<TT>dbgeng-attach</TT>".</P>
<H3><A name="remote_setup"></A>Setup</H3>
<H3><A name="remote_setup"></A>Setup</H3>
<P>Instructions are indentical to those above but executed on the remote machine.</P>
<P>Instructions are identical to those above but executed on the remote machine. Both of these
commands are now implemented via powershell, so the same caveats listed above for the local
.ps1 launcher apply here, i.e. you will need to set the current user's execution policy or
permissions on the script. Separate launchers have also been added for starting the debugger on
a remote Windows box from a local Linux machine.</P>
<H3>Additional Options</H3>
<H3>Additional Options</H3>
<UL>
<LI><B>ssh command</B>: The ssh command to execute, optionaly with full path.</LI>
<LI><B>[User@]Host</B>: This is the host name of the target system, optionally including a
user name. This is passed as is to <TT>ssh</TT>, which may interpret it according to local
configuration.</LI>
<UL>
<LI><B>ssh command</B>: The ssh command to execute, optionaly with full path.</LI>
<LI><B>Remote Trace RMI Port</B>: An available TCP port on the target system, which will
listen for dbgeng's Trace RMI connection and forward it back to Ghidra.</LI>
<LI><B>[User@]Host</B>: This is the host name of the target system, optionally including a
user name. This is passed as is to <TT>ssh</TT>, which may interpret it according to local
configuration.</LI>
<LI><B>Extra <TT>ssh</TT> arguments</B>: These are extra arguments to pass to <TT>ssh</TT>.
They are inserted immediately after the <TT>ssh</TT> command but before the host name. Beware
that syntax errors may cause strange behavior, and that not all features may be compatible
with this launcher.</LI>
</UL>
<LI><B>Remote Trace RMI Port</B>: An available TCP port on the target system, which will
listen for dbgeng's Trace RMI connection and forward it back to Ghidra.</LI>
<H2><A name="svrcx"></A>Process Server</H2>
<LI><B>Extra <TT>ssh</TT> arguments</B>: These are extra arguments to pass to <TT>ssh</TT>.
They are inserted immediately after the <TT>ssh</TT> command but before the host name. Beware
that syntax errors may cause strange behavior, and that not all features may be compatible
with this launcher.</LI>
</UL>
<H2><A name="svrcx"></A>Process Server</H2>
<P>The "<TT>dbgeng-svrcx</TT>" launcher extends the base dbgeng launcher adding an option for
connecting through a remote process server.</P>
@@ -209,11 +232,10 @@ python3 -m pip install --no-index -f Debugger-rmi-trace\pypkg\dist -f Debugger-a
<LI>
<B>Connection</B>: This is the connection string specifying the transport options for
communicating with the remote server. A typical example might be
'tcp:port=12345,server=192.168.0.2' for a process server launched on the machine at
192.168.0.2 using:
"<TT>tcp:port=12345,server=192.168.0.2</TT>" for a process server launched on the machine
at 192.168.0.2 using:
<PRE>
dbgsrv -t tcp:port=12345
dbgsrv -t tcp:port=12345
</PRE>
</LI>
</UL>

View File

@@ -15,6 +15,7 @@
# limitations under the License.
##
#@title drgn core
#@image-opt env:OPT_TARGET_IMG
#@desc <html><body width="300px">
#@desc <h3>Launch with <tt>drgn-core</tt></h3>
#@desc <p>

View File

@@ -118,7 +118,9 @@ finished, try launching again.
" "Would you like to install 'ghidragdb>=$version'?"; then
echo "Copying Wheels to $OPT_HOST"
mitigate-scp-pymodules "Debugger-rmi-trace" "<SELF>"
if ! mitigate-scp-pymodules "Debugger-rmi-trace" "<SELF>"; then
exit 1
fi
echo "Installing Wheels into GDB's embedded Python"
do-installation

View File

@@ -94,7 +94,9 @@ finished, try launching again.
" "Would you like to install 'ghidragdb>=$version'?"; then
echo "Copying Wheels to $OPT_HOST"
mitigate-scp-pymodules "Debugger-rmi-trace" "<SELF>"
if ! mitigate-scp-pymodules "Debugger-rmi-trace" "<SELF>"; then
exit 1
fi
echo "Installing Wheels into GDB's embedded Python"
do-installation

View File

@@ -116,7 +116,9 @@ finished, try launching again.
" "Would you like to install 'ghidralldb>=$version'?"; then
echo "Copying Wheels to $OPT_HOST"
mitigate-scp-pymodules "Debugger-rmi-trace" "<SELF>"
if ! mitigate-scp-pymodules "Debugger-rmi-trace" "<SELF>"; then
exit 1
fi
echo "Installing Wheels into LLDB's embedded Python"
do-installation

View File

@@ -93,7 +93,9 @@ finished, try launching again.
" "Would you like to install 'ghidralldb>=$version'?"; then
echo "Copying Wheels to $OPT_HOST"
mitigate-scp-pymodules "Debugger-rmi-trace" "<SELF>"
if ! mitigate-scp-pymodules "Debugger-rmi-trace" "<SELF>"; then
exit 1
fi
echo "Installing Wheels into LLDB's embedded Python"
do-installation

View File

@@ -15,7 +15,6 @@
# limitations under the License.
##
#@title x64dbg attach via ssh (shell)
#@image-opt arg:1
#@desc <html><body width="300px">
#@desc <h3>Launch with <tt>x64dbg</tt> via <tt>ssh</tt></h3>
#@desc <p>
@@ -39,7 +38,6 @@
. ../support/x64dbgsetuputils.sh
target_image=$(echo $OPT_TARGET_IMG | sed 's/\\/\\\\/g')
x64dbg_exe=$(echo $OPT_X64DBG_EXE | sed 's/\\/\\\\/g')
OPT_OS_WINDOWS=true
@@ -62,7 +60,7 @@ version=$(get-ghidra-version)
function do-installation() {
local -a pipargs
compute-dbg-pipinstall-args "'-f'" "os.environ['HOME']" "'ghidraxdbg>=$version'"
compute-x64dbg-pipinstall-args "'-f'" "os.environ['HOME']" "'ghidraxdbg>=$version'"
local -a sshargs
compute-ssh-args false "${pipargs[@]}"
@@ -96,10 +94,12 @@ 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'?"; then
" "Would you like to install 'ghidraxdbg>=$version'?"; then
echo "Copying Wheels to $OPT_HOST"
mitigate-scp-pymodules "Debugger-rmi-trace" "<SELF>"
if ! mitigate-scp-pymodules "Debugger-rmi-trace" "<SELF>"; then
exit 1
fi
echo "Installing Wheels into python"
do-installation

View File

@@ -26,7 +26,7 @@
#@icon icon.debugger
#@help x64dbg#ssh
#@depends Debugger-rmi-trace
#@env OPT_TARGET_IMG:file="" "Image" "The target binary executable image"
#@env OPT_TARGET_IMG:str="" "Image" "The target binary executable image"
#@env OPT_TARGET_ARGS:str="" "Arguments" "Command-line arguments to pass to the target"
#@env OPT_TARGET_DIR:str="" "Dir" "Initial directory"
#@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."

View File

@@ -15,7 +15,7 @@
# limitations under the License.
##
#@title x64dbg via ssh (shell)
#@image-opt arg:1
#@image-opt env:OPT_TARGET_IMG
#@desc <html><body width="300px">
#@desc <h3>Launch with <tt>x64dbg</tt> via <tt>ssh</tt></h3>
#@desc <p>
@@ -27,7 +27,7 @@
#@icon icon.debugger
#@help x64dbg#ssh
#@depends Debugger-rmi-trace
#@env OPT_TARGET_IMG:file="" "Image" "The target binary executable image"
#@env OPT_TARGET_IMG:str="" "Image" "The target binary executable image"
#@env OPT_TARGET_ARGS:str="" "Arguments" "Command-line arguments to pass to the target"
#@env OPT_TARGET_DIR:str="" "Dir" "Initial directory"
#@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."
@@ -97,10 +97,12 @@ 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'?"; then
" "Would you like to install 'ghidraxdbg>=$version'?"; then
echo "Copying Wheels to $OPT_HOST"
mitigate-scp-pymodules "Debugger-rmi-trace" "<SELF>"
if ! mitigate-scp-pymodules "Debugger-rmi-trace" "<SELF>"; then
exit 1
fi
echo "Installing Wheels into python"
do-installation

View File

@@ -55,8 +55,9 @@ def main():
# Delay these imports until sys.path is patched
try:
import ghidraxdbg
except:
exit(253)
except ModuleNotFoundError:
os._exit(253)
from ghidraxdbg import commands as cmd
from ghidraxdbg.hooks import on_state_changed
from ghidraxdbg.util import dbg
@@ -86,7 +87,5 @@ 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}")

View File

@@ -65,8 +65,9 @@ def main():
# Delay these imports until sys.path is patched
try:
import ghidraxdbg
except Exception as e:
exit(253)
except ModuleNotFoundError:
os._exit(253)
from ghidraxdbg import commands as cmd
from ghidraxdbg.hooks import on_state_changed
from ghidraxdbg.util import dbg
@@ -100,7 +101,5 @@ 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}")

View File

@@ -20,5 +20,5 @@ compute-x64dbg-pipinstall-args() {
printf -v argvpart ", %s" "$@"
pipargs=("$OPT_PYTHON_EXE")
pipargs+=(-c)
pipargs+=("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__')")
pipargs+=("\"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__')\"")
}

View File

@@ -90,9 +90,6 @@ compute-ssh-args() {
printf -v qargs '%q ' "$@"
sshargs+=("$OPT_SSH_PATH")
if [ -n "$OPT_OS_WINDOWS" ]; then
sshargs+=(-t)
fi
if [ "$forward" == "true" ]; then
sshargs+=("-R$OPT_REMOTE_PORT:$GHIDRA_TRACE_RMI_ADDR")
fi
@@ -142,6 +139,9 @@ mitigate-scp-pymodules() {
local -a scpargs
for mod in "$@"; do
dist=$(ghidra-module-pydist "$mod")
if [ "$?" -ne "0" ]; then
return 1
fi
scpargs+=("$dist"/*)
done
scp "${scpargs[@]}" "$OPT_HOST:~/"

View File

@@ -15,11 +15,14 @@
*/
package agent;
import static org.junit.Assert.*;
import static org.junit.Assume.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.*;
import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
@@ -33,14 +36,14 @@ import ghidra.app.plugin.core.debug.gui.modules.DebuggerModulesPlugin;
import ghidra.app.plugin.core.debug.gui.tracermi.launcher.AbstractTraceRmiLaunchOffer.NoStaticMappingException;
import ghidra.app.plugin.core.debug.gui.tracermi.launcher.TraceRmiLauncherServicePlugin;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
import ghidra.app.services.DebuggerAutoMappingService;
import ghidra.app.services.TraceRmiLauncherService;
import ghidra.app.services.*;
import ghidra.debug.api.ValStr;
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer;
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer.*;
import ghidra.framework.Application;
import ghidra.framework.OperatingSystem;
import ghidra.framework.plugintool.AutoConfigState.PathIsFile;
import ghidra.pty.*;
import ghidra.util.SystemUtilities;
public abstract class AbstractRmiConnectorsTest
@@ -52,9 +55,29 @@ public abstract class AbstractRmiConnectorsTest
return "python";
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NoLocalSetup {
}
boolean localSetup() throws Exception {
Method method = null;
try {
method = getClass().getMethod(name.getMethodName());
}
catch (NoSuchMethodException e) {
return true;
}
NoLocalSetup annotation = method.getAnnotation(NoLocalSetup.class);
return annotation == null;
}
protected abstract List<ResourceFile> getPipLinkModules();
protected void unpip(String... specs) throws Exception {
if (!localSetup()) {
return;
}
List<String> args = new ArrayList<>();
args.addAll(List.of(getPythonCmd(), "-m", "pip", "uninstall", "-y"));
args.addAll(List.of(specs));
@@ -63,6 +86,9 @@ public abstract class AbstractRmiConnectorsTest
}
protected void pipOob(String... specs) throws Exception {
if (!localSetup()) {
return;
}
List<String> args = new ArrayList<>();
args.addAll(List.of(getPythonCmd(), "-m", "pip", "install"));
args.addAll(List.of(specs));
@@ -71,6 +97,9 @@ public abstract class AbstractRmiConnectorsTest
}
protected void pip(String... specs) throws Exception {
if (!localSetup()) {
return;
}
List<String> args = new ArrayList<>();
args.addAll(List.of(getPythonCmd(), "-m", "pip", "install", "--no-index"));
for (ResourceFile root : Application.getApplicationRootDirectories()) {
@@ -90,6 +119,20 @@ public abstract class AbstractRmiConnectorsTest
assertEquals("pip failed", 0, result);
}
protected int runInTerminal(List<String> command, Map<String, String> env)
throws IOException, InterruptedException {
TerminalService terminalService = tool.getService(TerminalService.class);
if (terminalService == null) {
throw new NullPointerException("Terminal Service is not installed in test tool");
}
try (Pty pty = PtyFactory.local().openpty();
Terminal term = terminalService.createWithStreams(Charset.forName("utf8"),
pty.getParent().getInputStream(), pty.getParent().getOutputStream())) {
PtySession session = pty.getChild().session(command.toArray(String[]::new), env);
return session.waitExited();
}
}
protected boolean isWindows() {
return OperatingSystem.CURRENT_OPERATING_SYSTEM == OperatingSystem.WINDOWS;
}

View File

@@ -18,11 +18,14 @@ package agent.dbgeng.rmi;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.junit.Assume.*;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import javax.swing.JOptionPane;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
@@ -30,9 +33,11 @@ import org.junit.Test;
import agent.AbstractRmiConnectorsTest;
import generic.jar.ResourceFile;
import ghidra.app.plugin.core.debug.gui.tracermi.launcher.AbstractTraceRmiLaunchOffer.EarlyTerminationException;
import ghidra.app.plugin.core.debug.gui.tracermi.launcher.AbstractTraceRmiLaunchOffer.NoStaticMappingException;
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer.LaunchResult;
import ghidra.framework.Application;
import ghidra.framework.plugintool.AutoConfigState.PathIsFile;
import ghidra.pty.testutil.DummyProc;
public class DbgEngConnectorsTest extends AbstractRmiConnectorsTest {
@@ -122,8 +127,8 @@ public class DbgEngConnectorsTest extends AbstractRmiConnectorsTest {
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")))) {
Map.entry("env:OPT_TARGET_IMG", chooseImage().toString()),
Map.entry("env:OPT_HOST", "localhost")))) {
checkResult(result);
}
}
@@ -136,15 +141,16 @@ public class DbgEngConnectorsTest extends AbstractRmiConnectorsTest {
// 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")))) {
Map.entry("env:OPT_TARGET_IMG", chooseImage().toString()),
Map.entry("env:OPT_HOST", "localhost")))) {
// If the next line fails, make sure "python" on your box is 3.13
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")))) {
Map.entry("env:OPT_TARGET_IMG", chooseImage().toString()),
Map.entry("env:OPT_HOST", "localhost")))) {
checkResult(result);
}
}
@@ -152,19 +158,53 @@ public class DbgEngConnectorsTest extends AbstractRmiConnectorsTest {
@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")))) {
Map.entry("env:OPT_TARGET_IMG", chooseImage().toString()),
Map.entry("env:OPT_HOST", "localhost")))) {
// If the next line fails, make sure "python" on your box is 3.13
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")))) {
Map.entry("env:OPT_TARGET_IMG", chooseImage().toString()),
Map.entry("env:OPT_HOST", "localhost")))) {
checkResult(result);
}
}
@Test
@NoLocalSetup
public void testLinuxToDbgViaSshSetupGhidraDbg() throws Exception {
assumeFalse(isWindows());
String hostname = JOptionPane.showInputDialog("Windows Hostname");
assertEquals(0, runInTerminal(
List.of(DummyProc.which("ssh"), hostname, "pip uninstall ghidradbg"), null));
try (LaunchResult result = doLaunch("dbgeng via ssh (shell)", Map.ofEntries(
Map.entry("env:OPT_TARGET_IMG", chooseImage().toString()),
Map.entry("env:OPT_HOST", hostname)))) {
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 (shell)", Map.ofEntries(
Map.entry("env:OPT_TARGET_IMG", chooseImage().toString()),
Map.entry("env:OPT_HOST", hostname)))) {
checkResult(result);
}
}
@Override
protected void checkResult(LaunchResult result) {
// Exits to python shell, requires quit which generates early term exc.
if (result.exception() != null &&
!(result.exception() instanceof EarlyTerminationException) &&
!(result.exception() instanceof NoStaticMappingException)) {
throw new AssertionError(result);
}
}
// LATER?: kernel
// LATER?: attach (usermode by PID)
// LATER?: ext