vector35 / debugger Goto Github PK
View Code? Open in Web Editor NEWBinary Ninja debugger
License: Other
Binary Ninja debugger
License: Other
Sometimes you want to debug a library instead of the executable binary, so it would be nice to have the option to attach to an existing process and debug a module loaded in that process
Once Vector35/binaryninja-api#1532 lands, the current highlights should be migrated to ephemeral
We need a test binary that loads a few modules, and the test should pause in between each load, do a modules list, and ensure we're reading accurate results as each is loaded.
Currently eax, ebx, ecx, edx, and edi are showing in the registers list. We should either show all 32-bit registers (i.e. include esi, esp, ebp, r8d, r9d, etc) or show none of them.
The same goes for the 16-bit and 8-bit registers, including the high 8-bit registers (ah, bh, ...)
Environment: macOS + lldb
Is there any way to make self-modifying code work with the debugger plugin? Thank you!
When debugging a binary that calls library functions, we can load a new BinaryView from the module path and get analysis for those functions as well. This should definitely be optional as some processes (especially on macOS) load a lot of large libraries.
Inter-BV analysis is an entirely different (and much harder) problem for BN core, and if it ever is implemented it would be great to include. Not the focus of this issue though.
Currently the stack frame is just a structure of void*
s, but if we are in the loaded binary we can have the analysis fill in the variables. Additionally (at least on x86_64), we can walk the stack frames and fill out the previous stack frames/structures
Platform Information:
MacOS 11.6 (20G165) (M1)
Application Version:
2.4.2846 Personal
I ran into an issue while attempting to use the debugger plugin with a cross-compiled binary run through qemu-user and an exposed gdbserver port.
In a VM I ran the following command
qemu-i386 -L /usr/i686-linux-gnu -g 1234 ./dragon
In Binja I connected to the host using the REMOTE_GDB protocol and get a prompt to enter the path for the binary. After entering the path I receive the following error.
I subsequently attempted to use gdb-multiarch
and was able to connect to the qemu-user run binary and debug as normal.
Detected remote process running at different path: /home/parallels/projects/ctfs/dragon/dragon
Traceback (most recent call last):
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/binjaplug.py", line 387, in apply
remote_address = self.state.modules.relative_addr_to_absolute(bp)
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/binjaplug.py", line 262, in relative_addr_to_absolute
if self[module]:
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/binjaplug.py", line 230, in __getitem__
for (modpath, modaddr) in self:
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/binjaplug.py", line 222, in __iter__
self.update()
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/binjaplug.py", line 218, in update
self.module_cache = self.state.adapter.mem_modules().items()
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/QueuedAdapter.py", line 186, in mem_modules
return self.submit(lambda: self.adapter.mem_modules(cache_ok))
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/QueuedAdapter.py", line 84, in submit
raise result
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/QueuedAdapter.py", line 48, in worker
self.results[index] = (True, job())
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/QueuedAdapter.py", line 186, in <lambda>
return self.submit(lambda: self.adapter.mem_modules(cache_ok))
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/gdb.py", line 96, in mem_modules
data = self.get_remote_file(fpath)
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/gdblike.py", line 457, in get_remote_file
(result, errno, attachment) = self.rspConn.tx_rx('vFile:setfs:0', 'host_io')
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/rsp.py", line 109, in tx_rx
raise RspGeneralError('host i/o packet did not start with F: ' + str(reply))
Vector35_debugger.rsp.RspGeneralError: host i/o packet did not start with F: b''
Exception ignored on calling ctypes callback function: <function _ThreadActionContext.__init__.<locals>.<lambda> at 0x1830c1af0>
Traceback (most recent call last):
File "/Applications/Binary Ninja.app/Contents/MacOS/plugins/../../Resources/python/binaryninja/scriptingprovider.py", line 57, in <lambda>
self.callback = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(lambda ctxt: self.execute())
File "/Applications/Binary Ninja.app/Contents/MacOS/plugins/../../Resources/python/binaryninja/scriptingprovider.py", line 65, in execute
self.func()
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/dockwidgets/ControlsWidget.py", line 324, in perform_attach_after
self.debug_state.ui.on_step()
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/ui.py", line 175, in on_step
self.detect_new_code()
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/ui.py", line 253, in detect_new_code
local_rip = self.state.local_ip
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/binjaplug.py", line 489, in local_ip
return self.memory_view.remote_addr_to_local(self.ip)
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/ProcessView.py", line 101, in remote_addr_to_local
remote_base = self.get_remote_base(relative_view)
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/ProcessView.py", line 62, in get_remote_base
base = debug_state.modules[module]
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/binjaplug.py", line 230, in __getitem__
for (modpath, modaddr) in self:
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/binjaplug.py", line 222, in __iter__
self.update()
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/binjaplug.py", line 218, in update
self.module_cache = self.state.adapter.mem_modules().items()
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/QueuedAdapter.py", line 186, in mem_modules
return self.submit(lambda: self.adapter.mem_modules(cache_ok))
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/QueuedAdapter.py", line 84, in submit
raise result
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/QueuedAdapter.py", line 48, in worker
self.results[index] = (True, job())
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/QueuedAdapter.py", line 186, in <lambda>
return self.submit(lambda: self.adapter.mem_modules(cache_ok))
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/gdb.py", line 96, in mem_modules
data = self.get_remote_file(fpath)
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/gdblike.py", line 457, in get_remote_file
(result, errno, attachment) = self.rspConn.tx_rx('vFile:setfs:0', 'host_io')
File "/Users/adi/Library/Application Support/Binary Ninja/repositories/official/plugins/Vector35_debugger/rsp.py", line 109, in tx_rx
raise RspGeneralError('host i/o packet did not start with F: ' + str(reply))
Vector35_debugger.rsp.RspGeneralError: host i/o packet did not start with F: b''
Right click address action:
As you single step the header bar flashes and elements move around too much, it'd probably be better if the elements were just disabled and not hidden.
The most straightforward use case is for debugging the main executable + the .dll
s it depends on.
So that you're able to ie. step inside a win32 API call ([1]) and see it all labeled properly instead of the 'raw' fallback view, which is currently extremely limited ([2]).
Not sure about scope, would Binja's core would need major changes for this too?
[1]: Or just any .dll
of course, many ie. games are spread out in multiple .dll
s.
[2]: For example: 1) it doesn't disassemble instructions 2) can't scroll it 3) can't increase the number of lines it displays.
Hey -
Really like the debugger so far but I think I've broken it in a way I can't immediately see how to fix. I fired up an executable and originally it would display the disassembly view next to the debugger memory output. But I cannot get it to return even if I turn on reflections. Attached is an image:
Unfortunately there seems to very little documentation with the debugger so far so I am not sure where to go with this. Is there a way to get my disassembly view to return so I can get back to debugging?
I'm trying to debug a program in a router. I'm using gdbserver for this.
When I connect with Binja everything seams to work normally but when the target program receive a request
the debugger in Binary ninja stops working, and It print this on the log.
Traceback (most recent call last):
File "_ctypes/callbacks.c", line 237, in 'calling callback function'
File "C:\Users\someUser\AppData\Local\Vector35\BinaryNinja\plugins\..\python\binaryninja\scriptingprovider.py", line 51, in <lambda>
self.callback = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(lambda ctxt: self.execute())
File "C:\Users\someUser\AppData\Local\Vector35\BinaryNinja\plugins\..\python\binaryninja\scriptingprovider.py", line 59, in execute
self.func()
File "C:\Users\someUser\AppData\Roaming\Binary Ninja\repositories\official\plugins\Vector35_debugger\dockwidgets\ControlsWidget.py", line 355, in <lambda>
execute_on_main_thread_and_wait(lambda: perform_resume_after(reason, data))
File "C:\Users\someUser\AppData\Roaming\Binary Ninja\repositories\official\plugins\Vector35_debugger\dockwidgets\ControlsWidget.py", line 359, in perform_resume_after
self.debug_state.ui.on_step()
File "C:\Users\someUser\AppData\Roaming\Binary Ninja\repositories\official\plugins\Vector35_debugger\ui.py", line 169, in on_step
self.context_display()
File "C:\Users\someUser\AppData\Roaming\Binary Ninja\repositories\official\plugins\Vector35_debugger\ui.py", line 97, in context_display
threads = list(self.state.threads)
File "C:\Users\someUser\AppData\Roaming\Binary Ninja\repositories\official\plugins\Vector35_debugger\binjaplug.py", line 166, in __iter__
self.update()
File "C:\Users\someUser\AppData\Roaming\Binary Ninja\repositories\official\plugins\Vector35_debugger\binjaplug.py", line 144, in update
self.state.adapter.thread_select(tid)
File "C:\Users\someUser\AppData\Roaming\Binary Ninja\repositories\official\plugins\Vector35_debugger\QueuedAdapter.py", line 153, in thread_select
return self.submit(lambda: self.adapter.thread_select(tidx))
File "C:\Users\someUser\AppData\Roaming\Binary Ninja\repositories\official\plugins\Vector35_debugger\QueuedAdapter.py", line 84, in submit
raise result
File "C:\Users\someUser\AppData\Roaming\Binary Ninja\repositories\official\plugins\Vector35_debugger\QueuedAdapter.py", line 48, in worker
self.results[index] = (True, job())
File "C:\Users\someUser\AppData\Roaming\Binary Ninja\repositories\official\plugins\Vector35_debugger\QueuedAdapter.py", line 153, in <lambda>
return self.submit(lambda: self.adapter.thread_select(tidx))
File "C:\Users\someUser\AppData\Roaming\Binary Ninja\repositories\official\plugins\Vector35_debugger\gdblike.py", line 230, in thread_select
raise DebugAdapter.GeneralError('setting tid 0x%X for step and continue' % tid)
Vector35_debugger.DebugAdapter.GeneralError: setting tid 0x534E for step and continue
https://i.imgur.com/85dOFii.png
The 'name' should be a short name which displays without the path, that's what long name is for.
In a recursive function (eg helloworld_recursion), step return will not honor the current stack frame and just steps until any recursive call returns from the function.
To fix this requires more than a trivial breakpoint+go implementation (what we have currently) because we also have to check that the stack pointer has not moved.
Once #105 is resolved, the next step would be to add proper support.
The cat testbin, for example, can be started, continued until it's waiting on input, then paused and continued again, which will end its execution.
It should return to waiting for input.
If the user patches the binary, we can send those changes to gdb immediately after attaching.
Currently, when you switch to the debugger, none of the widgets appear and the user has to manually show them all. We should automatically show the relevant debugging widgets when the user switches to the debug view, and hide them when they switch away.
Occasionally, it appears the memory view can call mem_read() before the adapter has rspConn set.
Platform macOS 10.15 / BN Latest dev / Debugger master branch
File "/Users/glennsmith/Documents/binaryninja/ui/binaryninja.app/Contents/MacOS/plugins/../../Resources/python/binaryninja/binaryview.py", line 2001, in _read
data = self.perform_read(offset, length)
File "/Users/glennsmith/Library/Application Support/Binary Ninja/plugins/debugger/ProcessView.py", line 169, in perform_read
batch = adapter.mem_read(block, cache_len)
File "/Users/glennsmith/Library/Application Support/Binary Ninja/plugins/debugger/QueuedAdapter.py", line 186, in mem_read
return self.submit(lambda: self.adapter.mem_read(address, length))
File "/Users/glennsmith/Library/Application Support/Binary Ninja/plugins/debugger/QueuedAdapter.py", line 84, in submit
raise result
File "/Users/glennsmith/Library/Application Support/Binary Ninja/plugins/debugger/QueuedAdapter.py", line 41, in perform_task
self.results[index] = (True, job())
File "/Users/glennsmith/Library/Application Support/Binary Ninja/plugins/debugger/QueuedAdapter.py", line 186, in <lambda>
return self.submit(lambda: self.adapter.mem_read(address, length))
File "/Users/glennsmith/Library/Application Support/Binary Ninja/plugins/debugger/gdblike.py", line 309, in mem_read
reply = self.rspConn.tx_rx('m%x,%x' % (address, sz))
AttributeError: 'DebugAdapterLLDB' object has no attribute 'rspConn'
In the live memory view, if right-click on byte and select "Change Type..." or use hotkey "Y", I can change to primitive types like int
or uint64_t
, but not to something like struct foo
.
conversation from lexsek:
Just a TAB to send GDB commands (could be usefull for forks -> follow-fork-mode [child|parent] and so on
Or just the ability to choose between the processes created by forks
That sould be also great to follow spawned processes by the main debugged process
2:30
Maybe a "Process TAB" just like the thread one
New
2:32
But yeah basically beeing able to choose easily which process to follow (inside binja impletemented stuff, or just a tab to send gdb commands to start like follow-fork-mode) would be awesome
Turns out when your view reports itself as 0xffffffffffffffff bytes long, some things stop working
List of known things broken:
bv.__len__
The debugger is working nicely for me (at least after patching a file). Unfortunately its functionality is a bit limited and I'd love to be able to send GDB commands directly to the debugger.
Is such functionality available? If not, where would I go about adding it?
Since we add tag types to bndbs, we should have a Setting for the default icon.
Currently, the widgets in the debugger sidebar can only be expanded/collapsed, and we should support dragging the boundary of the widgets to shrink its size
With the release of HLIL adding linear IL views for all the ILs, we can support showing a linear view of the live code in addition to the graph view. Should use the same UIActions for switching to linear/graph as the normal views.
If an address is also the value of a register, the address gets labelled with that register (GOOD!) but repeated with a colon (BAD!). For instance if $rsp is 0x7ffeedad7c98, it gets labelled $rsp:$rsp
. That colon has meant bit concatenation or segment:offset in my experience.
Here's a screenshot exhibiting the problem with $rsp:$rsp
, $rsi:$rsi
, $rdx:$rdx
:
This could allow some optimization at least in the /proc/pid/maps reading, and maybe later too if we can make use of pulling the target from the remote side.
Have the ability to add conditions to breakpoints.
What do we want for conditions? Could use either python or the expression parser.
See DIE
Currently, these are handled correctly, but no UI indicator is provided. We should let the user know the process has exited more explicitly
When remote debugging, lldb's debugserver sends no registers in the general registers packet. 5a33172c is a workaround for this but it takes multiple seconds to load all the registers.
Currently the only information we have on segments is their binary path and start address. Because of this there is no way to determine their lengths and as a result, we don't know a lot of metadata about addresses (eg rwx). Also will fix #27 if we can create segments for only the mapped memory
Hi! I was testing out the debugger plugin with some CTF binaries and I found that it didn't work for most of them. I did some investigation to get a minimal case and the cause seems to be related to binaries which have had their interpreter modified using patchelf. I've noticed that the gdbserver process is defunct which makes me suspect the issue has something to do with how gdbserver is invoked, but I tested and gdbserver does work with these binaries.
Traceback (most recent call last):
File "/opt/binaryninja/plugins/../python/binaryninja/binaryview.py", line 2142, in _read
data = self.perform_read(offset, length)
File "/home/sky/.binaryninja/repositories/official/plugins/Vector35_debugger/ProcessView.py", line 167, in perform_read
batch = adapter.mem_read(block, cache_len)
File "/home/sky/.binaryninja/repositories/official/plugins/Vector35_debugger/QueuedAdapter.py", line 180, in mem_read
return self.submit(lambda: self.adapter.mem_read(address, length))
File "/home/sky/.binaryninja/repositories/official/plugins/Vector35_debugger/QueuedAdapter.py", line 84, in submit
raise result
File "/home/sky/.binaryninja/repositories/official/plugins/Vector35_debugger/QueuedAdapter.py", line 48, in worker
self.results[index] = (True, job())
File "/home/sky/.binaryninja/repositories/official/plugins/Vector35_debugger/QueuedAdapter.py", line 180, in <lambda>
return self.submit(lambda: self.adapter.mem_read(address, length))
File "/home/sky/.binaryninja/repositories/official/plugins/Vector35_debugger/gdblike.py", line 345, in mem_read
reply = self.rspConn.tx_rx('m%x,%x' % (address, sz))
AttributeError: 'NoneType' object has no attribute 'tx_rx'
I have a minimal case (stub binary, libc, ld-linux) here.
bug.zip
Currently the debugger allows one to step through the program one HLIL instruction at a time, but there is no way that I can tell to view the HLIL variable names within the debugger, which makes this method significantly less useful. Adding support to view HLIL variable names from the debugger and see their values over time would allow for more useful insights and, when combined with API access, could significantly simplify the automation of common work.
see subject
If I want to debug a library or a plugin on macOS I can use Hopper which gives you the chance to set the path of the main program that will load the plugin into memory. Then they manage to map the addresses once is loaded and I can debug the plugin. I don't know how they made it happen but it works very well.
If Binja is able to do this with its debugger it would be awesome. I have been doing some research about it and I think we can make it work. Keep in mind I am doing the testing on Win but the goal is to make it work on all architectures.
First we have to update the UI to let the user set the path of the executable (I had to go to the code a do it manually to test it out)
I had taken a couple of days to learn how to use Windbg and I find out that we can do this:
Break once the module loads into memory
sxe ld:moduleName.dll
Get the address of the module
x moduleName*!
start end module name
00000000901f0000
000000009329d000
moduleName C (export symbols) C:\Program Files\Company\CompanyFX2020\Support Files\Plug-ins\moduleName.aex
Now I have:
0x901f0000
0x180000000
0x1819165cf
hex(0x1819165cf-0x180000000+0x901f0000)
With this a can map/translate the breakpoints on Binja to the addresses of the loaded module into memory.
We should have exception handling to better tell the user what they need to do to get backends working. IE, on ubuntu: sudo apt install gdbserver
MacOS: xcode-select --install
I think is all that's required?
Windows: ?
Additionally -- that information should be in the installation instructions available both in the readme and in the debugger installation instructions json field so it's shown in the plugin information.
See Josh's stream ~52m in: https://www.twitch.tv/syrillian
See deREferencing
Zain and I explored several ways to implement the step return, but none of them seem always reliable. We might need to support stepping return using different strategies
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.