NationalSecurityAgency / ghidra

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

Debugger and WinDbg - KeyError on map["Attributes"] #6825

Closed stellarpower closed 4 weeks ago

stellarpower commented 1 month ago

Describe the bug Trying to debug a Windows application analysed with Ghidra. I've set up Python (3.12; my application is 32-bit but capstone is a dependency of pybag and was not happy using 32-bit python, which I initially guessed might cause less friction given this is Windows) and installed pybag and protocol buffers as per the F1 help.

To Reproduce I've spent all afternoon trying to get the debugger to start - so I don't know enough about ghidra or the full setup to design an MRE.

I start the debugger and I get one of python's famously uneasy-on-the-eyes backtraces to the terminal:

Connected to Ghidra 11.1.2 at 127.0.0.1:56017

Microsoft (R) Windows Debugger Version 10.0.17763.132 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.  

CommandLine: C:\Inferior.exe
Symbol search path is: srv*
Executable search path is:
ModLoad: 00000000`00400000 00000000`00acd000   Inferior.exe
ModLoad: 00007ffa`784f0000 00007ffa`786e8000   ntdll.dll
ModLoad: 00000000`77510000 00000000`776b4000   ntdll.dll
ModLoad: 00007ffa`76f90000 00007ffa`76fe9000   C:\WINDOWS\System32\wow64.dll   
ModLoad: 00007ffa`77950000 00007ffa`779d3000   C:\WINDOWS\System32\wow64win.dll
(3254.6bc8): Break instruction exception - code 80000003 (first chance)        
dict_keys(['LocalVariables', 'Parameters', 'SwitchTo', 'ToDisplayString'])
Traceback (most recent call last):
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\hooks.py", line 147, in _func
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\hooks.py", line 181, in on_state_changed
    return on_stop(args)
           ^^^^^^^^^^^^^
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\hooks.py", line 377, in on_stop
    state.record("Stopped")
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\hooks.py", line 81, in record
    commands.put_frames()
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\util.py", line 289, in _func
    return self.run(func, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\util.py", line 258, in run
    return future.result(0.5)
           ^^^^^^^^^^^^^^^^^^
  File "C:\Python312-64\Lib\concurrent\futures\_base.py", line 456, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "C:\Python312-64\Lib\concurrent\futures\_base.py", line 401, in __get_result
    raise self._exception
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\util.py", line 118, in run
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\commands.py", line 1254, in put_frames
    (values, keys) = create_generic(path)
                     ^^^^^^^^^^^^^^^^^^^^
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\commands.py", line 1352, in create_generic
    result = put_generic(obj)
             ^^^^^^^^^^^^^^^^
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\commands.py", line 1402, in put_generic
    update_by_container(node.path, index, lobj)
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\commands.py", line 1322, in update_by_container
    attr = map["Attributes"]
           ~~~^^^^^^^^^^^^^^
KeyError: 'Attributes'
Traceback (most recent call last):
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\data\support\local-dbgeng.py", line 71, in <module>
    main()
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\data\support\local-dbgeng.py", line 66, in main
    on_state_changed(DbgEng.DEBUG_CES_EXECUTION_STATUS, DbgEng.DEBUG_STATUS_BREAK)
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\hooks.py", line 147, in _func
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\hooks.py", line 181, in on_state_changed
    return on_stop(args)
           ^^^^^^^^^^^^^
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\hooks.py", line 377, in on_stop
    state.record("Stopped")
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\hooks.py", line 81, in record
    commands.put_frames()
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\util.py", line 289, in _func
    return self.run(func, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\util.py", line 258, in run
    return future.result(0.5)
           ^^^^^^^^^^^^^^^^^^
  File "C:\Python312-64\Lib\concurrent\futures\_base.py", line 456, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "C:\Python312-64\Lib\concurrent\futures\_base.py", line 401, in __get_result
    raise self._exception
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\util.py", line 118, in run
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\commands.py", line 1254, in put_frames
    (values, keys) = create_generic(path)
                     ^^^^^^^^^^^^^^^^^^^^
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\commands.py", line 1352, in create_generic
    result = put_generic(obj)
             ^^^^^^^^^^^^^^^^
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\commands.py", line 1402, in put_generic
    update_by_container(node.path, index, lobj)
  File "C:\...Ghidra\Debug\Debugger-agent-dbgeng\pypkg\src\ghidradbg\commands.py", line 1322, in update_by_container
    attr = map["Attributes"]
           ~~~^^^^^^^^^^^^^^
KeyError: 'Attributes'

Based on the contents of that file, I simply wrapped the line in an exception handler and return if not present. IDK if the check for None was meant to handle a bad access into the hashmap or conversely if the key is always expected but nillable and thus we have a problem (i.e. in ruby speak, if [] should really have been []? but the original author didn't hit the case when this was missing). I am one step further along for now so using as a workaround.

Environment (please complete the following information):

d-millar commented 1 month ago

@stellarpower Hmmm, just as a test case, maybe try unselecting “dbgmodel” when you start the target.

stellarpower commented 1 month ago

Thanks, I think I did. The little checkbox next to the path to the engine?

d-millar commented 1 month ago

Yes, correct - the stack suggests you’re using generic containers, which, if I remember correctly, only happens if you’re using “dbgmodel” vs. straight “dbgeng”. Related: do you have the newest version of windbg installed, i.e. what was called Windbg Preview, and/or the current WDK or are you running from a base Windows 10 install?

d-millar commented 1 month ago

Verified the error and put in a fix. Even with the fix, however, I would not recommend using "dbgmodel" with the default Windows 10 install as a good bit of the functionality in the default C:\Windows\System32\dbgmodel.dll is missing. If you can, install the latest windbg or WDK and point the dbgeng directory parameter at their versions. If not, maybe stick to "dbgmodel" off.