@@ -502,26 +502,80 @@ class DebuggerController:
502
502
503
503
>>> bv = load("test/binaries/helloworld")
504
504
>>> dbg = DebuggerController(bv)
505
- >>> dbg.launch()
505
+ >>> dbg.launch_and_wait()
506
+ <DebugStopReason.Breakpoint: 6>
507
+
508
+ When the ``launch_and_wait()`` returns ``DebugStopReason.Breakpoint``, it means the debugger has launched the target
509
+ successfully, and the target stopped at the entry point of the binary. Now we can perform other control operations
510
+ on it, e.g., resume the target by calling ``step_into_and_wait()``.
511
+
512
+ >>> dbg.step_into_and_wait()
513
+ <DebugStopReason.SingleStep: 4>
514
+
515
+ For all the API functions that resume the target, e.g., launch go, step into, etc, there are two variants of them.
516
+ One of them resumes the target and waits for it to stop again synchronously, ``step_into_and_wait`` will do a step
517
+ into, and it only returns AFTER the target stops again. This is more frequently used, and is suitable for automating
518
+ a sequence of actions. However, after calling such a synchronous API, if for any reason the target does not stop as
519
+ expected, Binary Ninja might be confused or hang.
520
+
521
+ The second set of API works asynchronously. For example, ``step into`` will only do a step into, but does NOT wait
522
+ for the target to stop again. Usually the asynchronous API is used with ``register_event_callback`` to listen for
523
+ the relevant events (e.g., target resumed, stopped, etc). The asynchronous API is harder to use and is only
524
+ recommended when the synchronous version does not suit your need. The Binary Ninja debugger UI uses the
525
+ asynchronous API.
526
+
527
+ To retrieve all the registers, run `regs`:
528
+
529
+ >>> dbg.regs
530
+ {'x0': <DebugRegister: x0, 0x1>, ...}
531
+
532
+ More often we only need get the value of one regisgter:
533
+
534
+ >>> dbg.regs['x1']
535
+ <DebugRegister: x1, 0x16fdffa70, &"/Users/xxxx//debugger/test/binaries/Darwin-arm64-signed/helloworld">
536
+
537
+ The result contains the register value as well as a string that can be dereferenced at its value.
538
+
539
+ ``ip`` returns the current intrudction pointer, and `stack_pointer`` returns the stack pointer:
540
+
541
+ >>> dbg.stack_pointer
542
+ 6171916256
543
+ >>> dbg.ip
544
+ 4294983364
545
+
546
+ To read/write memory value, use ``read_memory``/``write_memory``. Or you can directly read/write the binary view
547
+ object returned by `dbg.data`.
548
+
549
+ >>> dbg.read_memory(dbg.ip, 0x10)
550
+ <binaryninja.databuffer.DataBuffer object at 0x32ffa2f90>
551
+ >>> dbg.data.read(dbg.ip, 0x10)
552
+ b'\xfd {\x03 \xa9 \xfd \xc3 \x00 \x91 \xbf \xc3 \x1f \xb8 \xa0 \x83 \x1f \xb8 '
553
+
554
+ >>> dbg.write_memory(dbg.stack_pointer, b'a' * 0x10)
506
555
True
556
+ >>> dbg.data.write(dbg.stack_pointer, b'a' * 0x10)
557
+ 16
558
+
559
+ ``modules`` returns the list of modules, `threads` returns the list of threads.
507
560
508
- When the ``launch()`` returns True, it means the debugger has launched the target successfully. The target breaks at
509
- the entry point of the binary. Now we can perform other control operations on it, e.g., resume the target by calling
510
- ``go()``.
561
+ Breakpoints can be added via `add_breakpoint`:
511
562
512
- >>> dbg.go()
563
+ >>> dbg.add_breakpoint(0x100003ed0)
564
+
565
+ And it will be hit after we resume the target with ``go_and_wait``:
566
+
567
+ >>> dbg.go_and_wait()
568
+ <DebugStopReason.Breakpoint: 6>
569
+
570
+ We can resume it again:
571
+
572
+ >>> dbg.go_and_wait()
513
573
<DebugStopReason.ProcessExited: 2>
514
574
515
575
Since there are no other breakpoints in the target, the process executes and then exits.
516
576
517
- All target control functions, e.g., ``go()``, ``step_into()``, etc, are blocking. They will not return until the
518
- target breaks. In the future, we will switch to an asynchronous communication model where these functions return
519
- before the operation is performed.
520
-
521
- Starting from 4.1.5542-dev (0ad6b08b), the debugger no longer involves two binary views during debugging. Instead,
522
- it always uses the incoming binary view that is used to create the controller, and memory regions that are not
523
- present in the original binary view are represented using the new MemoryRegion API. The binary view can be accessed
524
- by the ``data`` property.
577
+ For more examples of using the debugger Python API, feel free to get some inspirations from our
578
+ [unit tests](https://github.com/Vector35/debugger/blob/dev/test/debugger_test.py)
525
579
526
580
"""
527
581
def __init__ (self , bv : binaryninja .BinaryView ):
0 commit comments