NationalSecurityAgency / ghidra

Ghidra is a software reverse engineering (SRE) framework
https://www.nsa.gov/ghidra
Apache License 2.0
51.35k stars 5.85k forks source link

IN-VM GDB local debugger crashes for ARM toolchain on Windows #3562

Closed staringatphones closed 1 year ago

staringatphones commented 2 years ago

Describe the bug The "IN-VM GNU gdb local debugger" option on Windows throws an uncaught exception and does not continue when supplied with the official GNU Arm Embedded Toolchain version of GDB.

To Reproduce Steps to reproduce the behavior:

  1. Install the GNU Arm Embedded Toolchain from the developer.arm.com website. The gcc-arm-none-eabi-10.3-2021.10-win32.exe installer was used. (Link to downloads page, Direct Download Link to installer).
  2. Launch Ghidra.
  3. Import an arm-compiled ELF file into the Ghidra project.
  4. Analyze the ELF using the Ghidra CodeBrowser tool.
  5. Open the analyzed ELF in the Ghidra Debugger tool.
  6. In the "Debugger Targets" pane, click the icon to "Create a new connection to a debugging agent."
  7. Select "IN-VM GNU gdb local debugger" in the main dropdown.
  8. In the "GDB launch command" entry, type "C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2021.10\bin\arm-none-eabi-gdb.exe" (with quotes)
  9. Leave the "Use existing session via new-ui" box unchecked.
  10. Click Connect.
  11. Ghidra throws "Uncaught Exception! UnsatisfiedLinkError - The specified procedure could not be found."

Expected behavior The ARM-specific GDB executable should launch within Ghidra, which allows the Debugger tool to debug the binary. (Note, a remote GDB server will be necessary to interface with an arm processor. The command target remote hostname:port would then be given to GDB. However, GDB fails to even launch in this scenario.)

Screenshots Screen Shot 2021-10-29 at 11 37 09 AM

The screenshot above shows the error message. A text file containing the entire contents of the error message is included in the Attachments section below.

Attachments error-output.txt This text file contains the complete text of the error given.

Environment (please complete the following information):

d-millar commented 2 years ago

Hi @staringatphones,

My apologies first off - you said "Windows Command Prompt" in your last post, and somehow I just didn't register the fact that you were running Windows. We are aware of this issue, and a fix (probably based on ConPty) is on our TODO list. May be awhile though.

I'd suggest checking out the previous issues #2908 and #3102 in Issues, and #2721 in Discussions. The basic workaround is to use the "GDB over SSH" agent rather than "IN-VM GDB" and either a WSL client or separate Linux VM running a gdbserver. Definitely, too complicated by half, but....let us know if neither of those will work for you or if you get stuck.

D

P.S. Thanks for closing out the other issue, by the by!

tommai78101 commented 1 year ago

Future readers from 2023 and onwards, for anyone who is looking into this issue about how to use the "IN-VM GNU gdb local debugger" in Ghidra 10.2.2 Stable on a Windows operating system:

Please make sure you use DOUBLE BACKSLASHES when you put in the absolute path in the "GDB launch command" text field:

image

Otherwise, you will get an error message: GetLastError() returned 2

The 2 here means ERROR_FILE_NOT_FOUND in Win32 API.

tommai78101 commented 1 year ago

Why double backslashes? You would ask. Well, well, well............

This is how Java interprets backslashes, when parsed into string values. The first backslash is to escape the proceeding second backslash.

d-millar commented 1 year ago

Yes, we should probably try to validate the input for this. As noted, double-backslashes (or forward slashes) will work.

tommai78101 commented 1 year ago

On the latest Ghidra 10.2.3, doing the same steps in OP now gives the following 2 errors, depending on whether you have "Use existing session via new-ui" checked or not.

Checked:

ghidra.dbg.error.DebuggerModelTerminatingException: Error while starting GDB: Pty implementation does not support null 
sessions. Try D:\devkitPro\devkitARM\bin\arm-none-eabi-gdb.exe i mi2
---------------------------------------------------
Build Date: 2023-Feb-08 1242 EST
Ghidra Version: 10.2.3
Java Home: D:\Java\jdk-17.35
JVM Version: Eclipse Adoptium 17
OS: Windows 10 10.0 amd64
Workstation: DESKTOP-CV0SBRD

Unchecked:

java.lang.UnsupportedOperationException: ConPTY does not have a name
---------------------------------------------------
Build Date: 2023-Feb-08 1242 EST
Ghidra Version: 10.2.3
Java Home: D:\Java\jdk-17.35
JVM Version: Eclipse Adoptium 17
OS: Windows 10 10.0 amd64
Workstation: DESKTOP-PC

If you used the GDB interpreter modes hinted by the error message -i mi2, you will get this:

ghidra.async.DisposedException: ghidra.dbg.error.DebuggerModelTerminatingException: GDB is terminating. Could not get target endian. Could not get target os. Could not get target architecture.
---------------------------------------------------
Build Date: 2023-Feb-08 1242 EST
Ghidra Version: 10.2.3
Java Home: D:\Java\jdk-17.35
JVM Version: Eclipse Adoptium 17
OS: Windows 10 10.0 amd64
Workstation: DESKTOP-CV0SBRD

Also, strangely enough, my operating system is actually Windows 11 Pro 22H2, not Windows 10 as printed in the text field.

image

Steps I did:

  1. Launch Ghidra v10.2.3 in Windows 11.
  2. Open the debugger.
  3. While waiting on the debugger, open and run the GDB server I have hosted on Windows 11.
  4. When GDB server is listening to a port, open a new debugger target in Ghidra Debugger.
  5. Set the option to IN-VM GNU gdb local debugger.
  6. Regardless of whether you check the checkbox or not for "Use existing session via new-ui", press the Connect button.

I have already verified the GDB server is able to communicate with GDB on a different Linux machine. So this is a Windows-specifc issue. This is more specific to the new changes made for 10.2.3:

* Debugger. Added GDB connector support for Windows (tested with GDB 11.1 on msys64). (GP-869, Issue #2908)

What is not really clear is, whether 7ab2f5d adds the Windows native implemention of ConPty to Ghidra, or Ghidra still has to go through MinGW to be able to connect to the GDB server.

d-millar commented 1 year ago

Hmmm, well, let's see if we can move the ball down the field a little....

You probably do not want "Use existing session via new-ui". This involves running gdb outside of Ghidra, issuing the command to spawn a new ui, and connectiing to that. For now, let's assume that's more than you need.

I think you were closest to succeeding with the "-i m2". For example, on my box, I am using "GDB launch command" == "C:/msys64/mingw64/bin/gdb -i mi2". The error message you are getting suggests that your version of arm-none-eabi-gdb does not support the commands: "show endian", "show os", and "show architecture". Maybe, try running arm-none-eabi-gdb on your client machine outside of Ghidra and executing those commands. If they are not defined, I think we can fake them out, but I have to remember how to load those definitions early enough in the process to make Ghidra happy.

d-millar commented 1 year ago

Another option if those commands are not supported is to use "gdb-multiarch".

tommai78101 commented 1 year ago

@d-millar I'm able to run arm-none-eabi-gdb in GDB/MI 2 interpreter mode, as output here:

D:\devkitPro\devkitARM\bin> .\arm-none-eabi-gdb.exe -i mi2
D:\devkitPro\devkitARM\bin\arm-none-eabi-gdb.exe: warning: Couldn't determine a path for the index cache directory.
=thread-group-added,id="i1"
~"GNU gdb (GDB) 10.2\n"
~"Copyright (C) 2021 Free Software Foundation, Inc.\n"
~"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law."
~"\nType \"show copying\" and \"show warranty\" for details.\n"
~"This GDB was configured as \"--host=x86_64-w64-mingw32 --target=arm-none-eabi\".\n"
~"Type \"show configuration\" for configuration details.\n"
~"For bug reporting instructions, please see:\n"
~"<https://www.gnu.org/software/gdb/bugs/>.\n"
~"Find the GDB manual and other documentation resources online at:\n    <http://www.gnu.org/software/gdb/documentation/>."
~"\n\n"
~"For help, type \"help\".\n"
~"Type \"apropos word\" to search for commands related to \"word\".\n"
(gdb)
echo "Hello world"
&"echo \"Hello world\"\n"
~"\"Hello world\""
^done
(gdb)

This is the same one with the latest compiled gdb-multiarch for Windows:

E:\gdb_multiarch\bin> ./gdb.exe -i mi2
=thread-group-added,id="i1"
~"GNU gdb (GDB) 12.1\n"
~"Copyright (C) 2022 Free Software Foundation, Inc.\n"
~"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law."
~"\nType \"show copying\" and \"show warranty\" for details.\n"
~"This GDB was configured as \"x86_64-w64-mingw32\".\n"
~"Type \"show configuration\" for configuration details.\n"
~"For bug reporting instructions, please see:\n"
~"<https://www.gnu.org/software/gdb/bugs/>.\n"
~"Find the GDB manual and other documentation resources online at:\n    <http://www.gnu.org/software/gdb/documentation/>."
~"\n\n"
~"For help, type \"help\".\n"
~"Type \"apropos word\" to search for commands related to \"word\".\n"
(gdb)
echo "hello world"
&"echo \"hello world\"\n"
~"\"hello world\""
^done
(gdb)

I believed all GDB version 6.0+ and above should have GDB/MI 2 by default.

tommai78101 commented 1 year ago

@d-millar Oh, just re-read your post. Here are the outputs for the show os, show endian, and show architecture on the arm-none-eabi-gdb:

show os
&"show os\n"
~"The current OS ABI is \"auto\" (currently \"none\").\n"
^done
(gdb)
show endian
&"show endian\n"
~"The target endianness is set automatically (currently little endian).\n"
^done
(gdb)
show architecutre
&"show architecutre\n"
&"Undefined show command: \"architecutre\".  Try \"help show\".\n"
^error,msg="Undefined show command: \"architecutre\".  Try \"help show\"."
(gdb)
show architecture
&"show architecture\n"
~"The target architecture is set to \"auto\" (currently \"arm\").\n"
^done
(gdb)
d-millar commented 1 year ago

Interesting - all of that is basically good news, i.e. the necessary commands are supported. So, the initial connection must be failing in some less-than-obvious way that is getting masked by the "show" errors. I will dig into the code some more tomorrow - see if I can re-create your issue.

d-millar commented 1 year ago

OK, this is totally a maybe, but try setting the environment variable HOME to %USERPROFILE%, re-launch Ghidra, and make sure space and slashes are escaped in the cmdline. I just tried this with "C:/Program\ Files\ (x86)/GNU\ Arm\ Embedded\ Toolchain/10\ 2021.10/bin/arm-none-eabi-gdb -i mi2".

tommai78101 commented 1 year ago

@d-millar I don't think it works. I still get the same errors Could not connect: GDB is terminating, even when I put the environment variable HOME in both the User Variables and in the System Variables. I even logged out and relogin just to make extra sure the environment variables are loaded up.

d-millar commented 1 year ago

OK, I thought that was possible but unlikely as I hit problems starting arm-none-eabi-gdb that I hadn't seen before, but they weren' terminating GDB. Are you running using ghidraRun or support/ghidraDebug? Any error messages you get either in the shell for ghidraDebug or the DebugConsole might be helpful - figuring out how it icould be dying is proving to be a bit of a challenge as I can't reproduce the error.

d-millar commented 1 year ago

One more idea, in launch.properties, try adding "VMARG=-Dagent.gdb.manger.log=true" - this will generate a GDB.log file in your home directory(?).

tommai78101 commented 1 year ago

@d-millar Noticed that the flag is spelled incorrectly, so I modified it a bit.

Here's the corrected flag:

VMARGS=-Dagent.gdb.manager.log=true

I tried this flag in the launch.properties file, and nothing happened. So I modified the launch.bat and added:

:: Set GDB logging enabled
set VMARG_LIST=%VMARG_LIST% -Dagent.gdb.manager.log=true

To force it to create the %USERPROFILE%\.ghidra\.ghidra_10.2.3_PUBLIC\GDB.log.

Here are the contents of the GDB.log:

<MI2: =thread-group-added,id="i1"
*CMD: class agent.gdb.manager.impl.cmd.GdbListInferiorsCommand
>MI2: -list-thread-groups

There's not much in here. The output contents are the same if I relaunch Ghidra with .\ghidraDebug.bat.

And here are the contents from the console terminal after running .\ghidraDebug.bat:

Listening for transport dt_socket at address: 18001
INFO  Using log config file: file:/D:/ghidra/ghidra_10.2.3_PUBLIC/support/debug.log4j.xml  (LoggingInitialization.java:51)
INFO  Using log file: C:\Users\fakeUser\.ghidra\.ghidra_10.2.3_PUBLIC\application.log  (LoggingInitialization.java:52)
INFO  Loading user preferences: C:\Users\fakeUser\.ghidra\.ghidra_10.2.3_PUBLIC\preferences  (Preferences.java:117)
INFO  Loading previous preferences: C:\Users\fakeUser\.ghidra\.ghidra_10.2.2_PUBLIC\preferences  (Preferences.java:170)
INFO  Class search complete (1703 ms)  (ClassSearcher.java:276)
INFO  Initializing SSL Context  (SSLContextInitializer.java:76)
INFO  Initializing Random Number Generator...  (SecureRandomFactory.java:37)
INFO  Random Number Generator initialization complete: SHA1PRNG  (SecureRandomFactory.java:41)
INFO  Trust manager disabled, cacerts have not been set  (ApplicationTrustManagerFactory.java:95)
INFO  User fakeUser started Ghidra.  (GhidraRun.java:80)
DEBUG Recovery snapshot timer set to 5 minute(s)  (RecoverySnapshotMgrPlugin.java:170)
INFO  Opening project: D:\Documents\ghidra\MyGhidraTestProject  (DefaultProject.java:134)
INFO  Packed database cache: C:\Users\fakeUser\AppData\Local\Ghidra\packed-db-cache  (PackedDatabaseCache.java:64)
DEBUG Using cached packed database: D:\ghidra\ghidra_10.2.3_PUBLIC\Ghidra\Features\Base\data\typeinfo\generic\generic_clib.gdt  (PackedDatabaseCache.java:364)
INFO  local Windows Pty session. PID = 1136  (LocalWindowsNativeProcessPtySession.java:40)
DEBUG Terminating agent.gdb.manager.impl.GdbManagerImpl@7503781a  (GdbManagerImpl.java:799)
DEBUG Terminating agent.gdb.manager.impl.GdbManagerImpl@7503781a  (GdbManagerImpl.java:799)
DEBUG STDOUT,MI2 reader exiting because java.lang.NumberFormatException: For input string: ""  (GdbManagerImpl.java:161)
ERROR Could not connect: ghidra.dbg.error.DebuggerModelTerminatingException: GDB is terminating  (ConsoleErrorDisplay.java:59)
WARN  Could not get target architecture: java.util.concurrent.CompletionException: ghidra.dbg.error.DebuggerModelTerminatingException: GDB is terminating  (DebuggerObjectModel.java:562)
WARN  Could not get target os: java.util.concurrent.CompletionException: ghidra.dbg.error.DebuggerModelTerminatingException: GDB is terminating  (DebuggerObjectModel.java:562)
WARN  Could not get target endian: java.util.concurrent.CompletionException: ghidra.dbg.error.DebuggerModelTerminatingException: GDB is terminating  (DebuggerObjectModel.java:562)

I did see a NumberFormatException, so perhaps there's something wrong with it?

I'm curious about your steps such that you're not able to reproduce the error. May I take reference on your steps?

d-millar commented 1 year ago

Well, I think your assessment may be correct - it might be the NumberFormatException. At a guess, this might be coming from "info proc mappings", but...again, not feeling like that's likely. So, my steps as exactly as I can describe them:

(1) From fresh download of ghidra_10.2.3_PUBLIC, execute ghidraRun.bat (double-clicked) (2) Set up a project in the usual way, and open the default Debugger tool (3) From Targets, hit "Create a new connection..." (4) From the pull-down menu, select "IN-VM GNU gdb local debugger" (5) For "GDB launch command", enter "C:/Program\ Files\ (x86)/GNU\ Arm\ Embedded\ Toolchain/10\ 2021.10/bin/arm-none-eabi-gdb -i mi2". This is the path I had for the default install from the download link you sent. (6) Leave "Use existing session via new-ui" unchecked, and hit "Connect" (7) At this point, in the Objects tree, I have Session->Inferiors->1 - \<null> (8) I can open this node and verify the Environment node, and I can run commands in the Interpreter

It sounds like you have done the same thing, but somewhere between 6 and 7 things went south.

Some things that are different:
I am running Windows Server 2019 (shouldn't make a difference?) I am using OpenJDK 17.0.1 2021-10-19 LTS (also shouldn't make a difference?)

What do you get if you run "info proc mappings" in arm-none-eabi-gdb? I am getting "Not supported on this target".

tommai78101 commented 1 year ago

@d-millar Same here.

(gdb)
info proc mappings
&"info proc mappings\n"
&"Not supported on this target.\n"
^error,msg="Not supported on this target."
(gdb)

However, we can trick GDB to give us info proc mappings:

  1. Put the following code in a text file stored in your desired location of your choice.
define info proc mappings
    echo 0x0 0xFFFFFFFF 0x100000000 0x0 mem \n
end
  1. Go into GDB interpreter mode.
  2. Type source path/to/text/file/containing/info proc mappings

This is what I did in GDB interpreter mode:

(gdb)
define info proc mappings
&"define info proc mappings\n"
~"Type commands for definition of \"info proc mappings\".\n"
~"End with a line saying just \"end\".\n"
~">"
echo 0x0 0xFFFFFFFF 0x100000000 0x0 mem \n
~">"
end
^done
(gdb)
info proc mappings
&"info proc mappings\n"
~"0x0 0xFFFFFFFF 0x100000000 0x0 mem \n"
^done
(gdb)

Would this be helpful to you in some way?

d-millar commented 1 year ago

LOL - that's my trick. (Glad it's catching on.) Just checking, though, you're not using that (say, from a .gdbinit file) in your current Ghidra tests, correct?

tommai78101 commented 1 year ago

@d-millar No. I'm not even aware that you can put this in a .gdbinit some where.

d-millar commented 1 year ago

Good, I guess - I'd hate to be burned by my own trick. Will drag some extra eyes on the log file tomorrow; see if anyone has a brilliant insight into the current problem.

tommai78101 commented 1 year ago

@d-millar Here's a better debug log messages I discovered as I finished setting up the Ghidra development environment and actually launching the debugger on my machine:

INFO  Using log config file: file:/E:/LargeGithubProjects/ghidra/Ghidra/Framework/Generic/bin/main/generic.log4jdev.xml   (LoggingInitialization.java:50) 
INFO  Using log file: C:\Users\fakeUser\.ghidra\.ghidra_10.3_DEV_location_LargeGithubProjects\application.log   (LoggingInitialization.java:51) 
INFO  Loading user preferences: C:\Users\fakeUser\.ghidra\.ghidra_10.3_DEV_location_LargeGithubProjects\preferences   (Preferences.java:122) 
INFO  Searching for classes...   (ClassSearcher.java:257) 
INFO  Class search complete (3579 ms)   (ClassSearcher.java:276) 
INFO  User fakeUser started Ghidra.   (GhidraRun.java:77) 
INFO  local Windows Pty session. PID = 24716   (LocalWindowsNativeProcessPtySession.java:40) 
ERROR Could not connect: ghidra.dbg.error.DebuggerModelTerminatingException: Error while starting GDB: Could not detect GDB's interpreter mode. 
Try D:\devkitPro\devkitARM\bin\arm-none-eabi-gdb.exe -i mi2   (ConsoleErrorDisplay.java:59) 
ERROR Could not get target architecture java.util.concurrent.CompletionException: java.lang.NullPointerException: Cannot invoke "java.io.PrintWriter.print(String)" because "wr" is null
    at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:315)
    at java.base/java.util.concurrent.CompletableFuture.uniAcceptNow(CompletableFuture.java:761)
    at java.base/java.util.concurrent.CompletableFuture.uniAcceptStage(CompletableFuture.java:735)
    at java.base/java.util.concurrent.CompletableFuture.thenAccept(CompletableFuture.java:2182)
    at agent.gdb.manager.impl.GdbManagerImpl.doExecute(GdbManagerImpl.java:853)
    at agent.gdb.manager.impl.GdbManagerImpl.execute(GdbManagerImpl.java:838)
    at agent.gdb.manager.impl.GdbManagerImpl.consoleCapture(GdbManagerImpl.java:1747)
    at agent.gdb.model.impl.GdbModelTargetEnvironment.lambda$1(GdbModelTargetEnvironment.java:87)
    at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1187)
    at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2309)
    at agent.gdb.model.impl.GdbModelTargetEnvironment.refreshArchitecture(GdbModelTargetEnvironment.java:86)
    at agent.gdb.model.impl.GdbModelTargetEnvironment.refreshInternal(GdbModelTargetEnvironment.java:168)
    at agent.gdb.model.impl.GdbModelTargetEnvironment.<init>(GdbModelTargetEnvironment.java:65)
    at agent.gdb.model.impl.GdbModelTargetInferior.<init>(GdbModelTargetInferior.java:100)
    at agent.gdb.model.impl.GdbModelTargetInferiorContainer.getTargetInferior(GdbModelTargetInferiorContainer.java:163)
    at agent.gdb.model.impl.GdbModelTargetInferiorContainer.inferiorAdded(GdbModelTargetInferiorContainer.java:58)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at ghidra.util.datastruct.ListenerMap$ListenerHandler.lambda$0(ListenerMap.java:134)
    at ghidra.util.datastruct.ListenerMap$1.execute(ListenerMap.java:57)
    at ghidra.util.datastruct.ListenerMap$ListenerHandler.invoke(ListenerMap.java:122)
    at jdk.proxy2/jdk.proxy2.$Proxy55.inferiorAdded(Unknown Source)
    at agent.gdb.manager.impl.GdbManagerImpl.lambda$58(GdbManagerImpl.java:470)
    at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.NullPointerException: Cannot invoke "java.io.PrintWriter.print(String)" because "wr" is null
    at agent.gdb.manager.impl.GdbManagerImpl.lambda$68(GdbManagerImpl.java:880)
    at java.base/java.util.concurrent.CompletableFuture.uniAcceptNow(CompletableFuture.java:757)
    ... 27 more
  (DebuggerObjectModel.java:571) 
ERROR Could not get target os java.util.concurrent.CompletionException: java.lang.NullPointerException: Cannot invoke "java.io.PrintWriter.print(String)" because "wr" is null
    at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:315)
    at java.base/java.util.concurrent.CompletableFuture.uniAcceptNow(CompletableFuture.java:761)
    at java.base/java.util.concurrent.CompletableFuture.uniAcceptStage(CompletableFuture.java:735)
    at java.base/java.util.concurrent.CompletableFuture.thenAccept(CompletableFuture.java:2182)
    at agent.gdb.manager.impl.GdbManagerImpl.doExecute(GdbManagerImpl.java:853)
    at agent.gdb.manager.impl.GdbManagerImpl.execute(GdbManagerImpl.java:838)
    at agent.gdb.manager.impl.GdbManagerImpl.consoleCapture(GdbManagerImpl.java:1747)
    at agent.gdb.model.impl.GdbModelTargetEnvironment.lambda$5(GdbModelTargetEnvironment.java:120)
    at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1187)
    at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2309)
    at agent.gdb.model.impl.GdbModelTargetEnvironment.refreshOS(GdbModelTargetEnvironment.java:119)
    at agent.gdb.model.impl.GdbModelTargetEnvironment.refreshInternal(GdbModelTargetEnvironment.java:169)
    at agent.gdb.model.impl.GdbModelTargetEnvironment.<init>(GdbModelTargetEnvironment.java:65)
    at agent.gdb.model.impl.GdbModelTargetInferior.<init>(GdbModelTargetInferior.java:100)
    at agent.gdb.model.impl.GdbModelTargetInferiorContainer.getTargetInferior(GdbModelTargetInferiorContainer.java:163)
    at agent.gdb.model.impl.GdbModelTargetInferiorContainer.inferiorAdded(GdbModelTargetInferiorContainer.java:58)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at ghidra.util.datastruct.ListenerMap$ListenerHandler.lambda$0(ListenerMap.java:134)
    at ghidra.util.datastruct.ListenerMap$1.execute(ListenerMap.java:57)
    at ghidra.util.datastruct.ListenerMap$ListenerHandler.invoke(ListenerMap.java:122)
    at jdk.proxy2/jdk.proxy2.$Proxy55.inferiorAdded(Unknown Source)
    at agent.gdb.manager.impl.GdbManagerImpl.lambda$58(GdbManagerImpl.java:470)
    at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.NullPointerException: Cannot invoke "java.io.PrintWriter.print(String)" because "wr" is null
    at agent.gdb.manager.impl.GdbManagerImpl.lambda$68(GdbManagerImpl.java:880)
    at java.base/java.util.concurrent.CompletableFuture.uniAcceptNow(CompletableFuture.java:757)
    ... 27 more
  (DebuggerObjectModel.java:571) 
ERROR Could not get target endian java.util.concurrent.CompletionException: java.lang.NullPointerException: Cannot invoke "java.io.PrintWriter.print(String)" because "wr" is null
    at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:315)
    at java.base/java.util.concurrent.CompletableFuture.uniAcceptNow(CompletableFuture.java:761)
    at java.base/java.util.concurrent.CompletableFuture.uniAcceptStage(CompletableFuture.java:735)
    at java.base/java.util.concurrent.CompletableFuture.thenAccept(CompletableFuture.java:2182)
    at agent.gdb.manager.impl.GdbManagerImpl.doExecute(GdbManagerImpl.java:853)
    at agent.gdb.manager.impl.GdbManagerImpl.execute(GdbManagerImpl.java:838)
    at agent.gdb.manager.impl.GdbManagerImpl.consoleCapture(GdbManagerImpl.java:1747)
    at agent.gdb.model.impl.GdbModelTargetEnvironment.lambda$9(GdbModelTargetEnvironment.java:149)
    at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1187)
    at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2309)
    at agent.gdb.model.impl.GdbModelTargetEnvironment.refreshEndian(GdbModelTargetEnvironment.java:148)
    at agent.gdb.model.impl.GdbModelTargetEnvironment.refreshInternal(GdbModelTargetEnvironment.java:170)
    at agent.gdb.model.impl.GdbModelTargetEnvironment.<init>(GdbModelTargetEnvironment.java:65)
    at agent.gdb.model.impl.GdbModelTargetInferior.<init>(GdbModelTargetInferior.java:100)
    at agent.gdb.model.impl.GdbModelTargetInferiorContainer.getTargetInferior(GdbModelTargetInferiorContainer.java:163)
    at agent.gdb.model.impl.GdbModelTargetInferiorContainer.inferiorAdded(GdbModelTargetInferiorContainer.java:58)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at ghidra.util.datastruct.ListenerMap$ListenerHandler.lambda$0(ListenerMap.java:134)
    at ghidra.util.datastruct.ListenerMap$1.execute(ListenerMap.java:57)
    at ghidra.util.datastruct.ListenerMap$ListenerHandler.invoke(ListenerMap.java:122)
    at jdk.proxy2/jdk.proxy2.$Proxy55.inferiorAdded(Unknown Source)
    at agent.gdb.manager.impl.GdbManagerImpl.lambda$58(GdbManagerImpl.java:470)
    at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.NullPointerException: Cannot invoke "java.io.PrintWriter.print(String)" because "wr" is null
    at agent.gdb.manager.impl.GdbManagerImpl.lambda$68(GdbManagerImpl.java:880)
    at java.base/java.util.concurrent.CompletableFuture.uniAcceptNow(CompletableFuture.java:757)
    ... 27 more
  (DebuggerObjectModel.java:571) 

It seems like in GdbManagerImpl.java:135, it first enters the while loop. It is then successful at reading the first line, which has the value:

=thread-group-added,id="i1"

And then on the second while loop iteration, just before it attempts to finish invoking reader.readLine(), it throws a NumberFormatException. Thus, I dug a bit even deeper. Here is the second line that has the value:

~"GNU gdb (GDB) 10.2\n"

Github won't show the control character ESC, so here it is in picture form:

image

After the K is parsed, the next character is what throws the NumberFormatException. It seems like GDB interpreter mode terminated its output string too soon here, as Ghidra is expecting a number here, either a 0, 1, or 2, but GDB sends back an empty character.

We all know that when logging into GDB interpreter mode, it outputs the following:

D:\devkitPro\devkitARM\bin> .\arm-none-eabi-gdb.exe -i mi2
=thread-group-added,id="i1"
~"GNU gdb (GDB) 10.2\n" <-----------------------------------------------------------------------
~"Copyright (C) 2021 Free Software Foundation, Inc.\n"
~"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law."
~"\nType \"show copying\" and \"show warranty\" for details.\n"
~"This GDB was configured as \"--host=x86_64-w64-mingw32 --target=arm-none-eabi\".\n"
~"Type \"show configuration\" for configuration details.\n"
~"For bug reporting instructions, please see:\n"
~"<https://www.gnu.org/software/gdb/bugs/>.\n"
~"Find the GDB manual and other documentation resources online at:\n    <http://www.gnu.org/software/gdb/documentation/>."
~"\n\n"
~"For help, type \"help\".\n"
~"Type \"apropos word\" to search for commands related to \"word\".\n"
(gdb)

It is the line marked with a <----- that showed where Ghidra couldn't finish parsing and exited too quickly.

Somehow, in the AnsiBufferedInputStream.java:368 in the Debugger-agent-gdb project, where in the parseNumericBuffer(), it is supposely expecting a numeric code when for whatever reason, on Windows operating systems, the GDB doesn't really give any numbers to execute an "Erase In Line" operation.

I'm not even sure whether if it's a bug on the GDB side, a bug on the Debugger-agent-gdb side, or it could be a minor difference due to different operating systems that's not taken into account yet. If anyone else have insights on this issue, it would be great to hear more about this.

nsadeveloper789 commented 1 year ago

It might just be a shorthand case we hadn't considered in the ANSI parsing: https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797

Looks like ESC[K is same as ESC[0K. Thanks for pointing it out. A lot of this ANSI stuff was introduced in order to handle Windows ConPTY. It inserts a lot of these control sequences when piping output from the child application to the parent console, so I suspect the difference in ANSI codes is due to a difference in Windows version. In any case, an ANSI decode problem like that shouldn't be throwing an exception :/ .

If you're willing to try a quick and dirty patch, where you pointed out in AnsiBufferedInputStream.java, patch the method:

    protected int parseNumericBuffer() {
        String numeric = readAndClearEscBuf();
        if (numeric.isEmpty()) {
            return 0;
        }
        int result = Integer.parseInt(numeric);
        return result;
    }

Other things call that where blank may not imply 0, so I'm not sure this is a solution, but it should at least move us forward troubleshooting.

tommai78101 commented 1 year ago

@nsadeveloper789 It worked. Applying the patch made Ghidra able to communicate with GDB successfully on the ARM device. I'll go ahead and create a pull request for this.

d-millar commented 1 year ago

Wow, score - nice call @nsadeveloper789

tommai78101 commented 1 year ago

@d-millar @nsadeveloper789 Made a pull request. I credited @nsadeveloper789 for the patch.

d-millar commented 1 year ago

Yep - thanks!

nsadeveloper789 commented 1 year ago

Glad to know it worked. As mentioned, I'm not certain it's The Right Solution (TM). On the other hand, if it works, then why not? Good news is this only applies to GDB on Windows, so I think it's relatively low risk to just take it. Thanks for submitting the patch!

StevensND commented 8 months ago

I don't have the IN-VM GNU gdb local debugger option.

I'm using Ghidra 11.0.1 btw.

Has the name changed or something?. Here's the screenshot

Sin título

In addition, when I modify the GDB launch command and indicate the following path:

C:\\Users\\Stevens\\Desktop\\gdb-multiarch-14.1\\bin\\gdb-multiarch.exe

I get the -i mi2 error shown in this screenshot. I'm using the 14.1 version of gdb-multiarch-windows

EDIT: If I run gdb-multiarch as administrator then I get this error:

com.sun.jna.LastErrorException: GetLastError() returned 740

Build Date: 2024-Jan-30 1212 EST Ghidra Version: 11.0.1 Java Home: C:\Program Files\Java\jdk-17 JVM Version: Oracle Corporation 17.0.10 OS: Windows 10 10.0 amd64 Workstation: DESKTOP-6N85SK3

nsadeveloper789 commented 8 months ago

A few things:

  1. Yes, the name is changed: "gdb" is the new name.
  2. There have been related issues with others using gdb-14.1 (See #6107)
  3. You'll need to append -i mi2 to the gdb path (launch command).
  4. Try with and without DOS line endings. This seems to be inconsistent with gdb on Windows, depending on whether it's based on Cygwin or MinGW.
nsadeveloper789 commented 8 months ago

I don't have any insight on the 740 error, as I've not tried running this as administrator on Windows.

StevensND commented 8 months ago

I don't have any insight on the 740 error, as I've not tried running this as administrator on Windows.

I tried the suggestions and I still get the 740 error.

And I need to run gdb-multiarch as administrator to bypass the -i mi2 error.

nsadeveloper789 commented 8 months ago

And I need to run gdb-multiarch as administrator to bypass the -i mi2 error.

I'm not sure I understand. Is trying to workaround the -i mi2 thing the only reason you need to run gdb as admin? I wouldn't ordinarily recommend running gdb as admin, unless you need to attach to a process of higher privilege. I suspect the 740 error (operation requires elevation) actually comes earlier in the launch process than whatever error related to -i mi2.

Please try the following and, if it doesn't work, please include all the details from the error message you can:

  1. Do not run gdb as admin. (I actually don't know what config options you're using to do this... In any case, have please have gdb run under the same account as Ghidra.)
  2. Select the "gdb" connector.
  3. Make sure the command line ends with -i mi2. No quotes. If gdb's path contains spaces, you can quote the path to gdb, but the -i mi2 part must be outside the quotes.
  4. Use DOS line endings.

If you indeed need to run gdb with higher privileges than Ghidra, you may try writing a batch script that wraps gdb -i mi2 with runas. In theory, you can then supply the path to that script instead of gdb when configuring the launch.