Skip to content

libdebug.debugger.debugger

Debugger

The Debugger class is the main class of libdebug. It contains all the methods needed to run and interact with the process.

Source code in libdebug/debugger/debugger.py
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
class Debugger:
    """The Debugger class is the main class of `libdebug`. It contains all the methods needed to run and interact with the process."""

    _sentinel: object = object()
    """A sentinel object."""

    _internal_debugger: InternalDebugger
    """The internal debugger object."""

    def __init__(self: Debugger) -> None:
        pass

    def post_init_(self: Debugger, internal_debugger: InternalDebugger) -> None:
        """Do not use this constructor directly. Use the `debugger` function instead."""
        self._internal_debugger = internal_debugger
        self._internal_debugger.start_up()

    def run(self: Debugger, redirect_pipes: bool = True) -> PipeManager | None:
        """Starts the process and waits for it to stop.

        Args:
            redirect_pipes (bool): Whether to hook and redirect the pipes of the process to a PipeManager.
        """
        return self._internal_debugger.run(redirect_pipes)

    def attach(self: Debugger, pid: int) -> None:
        """Attaches to an existing process."""
        self._internal_debugger.attach(pid)

    def detach(self: Debugger) -> None:
        """Detaches from the process."""
        self._internal_debugger.detach()

    def kill(self: Debugger) -> None:
        """Kills the process."""
        self._internal_debugger.kill()

    def terminate(self: Debugger) -> None:
        """Interrupts the process, kills it and then terminates the background thread.

        The debugger object will not be usable after this method is called.
        This method should only be called to free up resources when the debugger object is no longer needed.
        """
        self._internal_debugger.terminate()

    def cont(self: Debugger) -> None:
        """Continues the process."""
        self._internal_debugger.cont()

    def interrupt(self: Debugger) -> None:
        """Interrupts the process."""
        self._internal_debugger.interrupt()

    def wait(self: Debugger) -> None:
        """Waits for the process to stop."""
        self._internal_debugger.wait()

    def print_maps(self: Debugger) -> None:
        """Prints the memory maps of the process."""
        liblog.warning("The `print_maps` method is deprecated. Use `d.pprint_maps` instead.")
        self._internal_debugger.pprint_maps()

    def pprint_maps(self: Debugger) -> None:
        """Prints the memory maps of the process."""
        self._internal_debugger.pprint_maps()

    def resolve_symbol(self: Debugger, symbol: str, file: str = "binary") -> int:
        """Resolves the address of the specified symbol.

        Args:
            symbol (str): The symbol to resolve.
            file (str): The backing file to resolve the symbol in. Defaults to "binary"

        Returns:
            int: The address of the symbol.
        """
        return self._internal_debugger.resolve_symbol(symbol, file)

    @property
    def symbols(self: Debugger) -> SymbolList[Symbol]:
        """Get the symbols of the process."""
        return self._internal_debugger.symbols

    def breakpoint(
        self: Debugger,
        position: int | str,
        hardware: bool = False,
        condition: str = "x",
        length: int = 1,
        callback: None | bool | Callable[[ThreadContext, Breakpoint], None] = None,
        file: str = "hybrid",
    ) -> Breakpoint:
        """Sets a breakpoint at the specified location.

        Args:
            position (int | bytes): The location of the breakpoint.
            hardware (bool, optional): Whether the breakpoint should be hardware-assisted or purely software. Defaults to False.
            condition (str, optional): The trigger condition for the breakpoint. Defaults to None.
            length (int, optional): The length of the breakpoint. Only for watchpoints. Defaults to 1.
            callback (None | bool | Callable[[ThreadContext, Breakpoint], None], optional): A callback to be called when the breakpoint is hit. If True, an empty callback will be set. Defaults to None.
            file (str, optional): The user-defined backing file to resolve the address in. Defaults to "hybrid" (libdebug will first try to solve the address as an absolute address, then as a relative address w.r.t. the "binary" map file).
        """
        return self._internal_debugger.breakpoint(position, hardware, condition, length, callback, file)

    def watchpoint(
        self: Debugger,
        position: int | str,
        condition: str = "w",
        length: int = 1,
        callback: None | bool | Callable[[ThreadContext, Breakpoint], None] = None,
        file: str = "hybrid",
    ) -> Breakpoint:
        """Sets a watchpoint at the specified location. Internally, watchpoints are implemented as breakpoints.

        Args:
            position (int | bytes): The location of the breakpoint.
            condition (str, optional): The trigger condition for the watchpoint (either "w", "rw" or "x"). Defaults to "w".
            length (int, optional): The size of the word in being watched (1, 2, 4 or 8). Defaults to 1.
            callback (None | bool | Callable[[ThreadContext, Breakpoint], None], optional): A callback to be called when the watchpoint is hit. If True, an empty callback will be set. Defaults to None.
            file (str, optional): The user-defined backing file to resolve the address in. Defaults to "hybrid" (libdebug will first try to solve the address as an absolute address, then as a relative address w.r.t. the "binary" map file).
        """
        return self._internal_debugger.breakpoint(
            position,
            hardware=True,
            condition=condition,
            length=length,
            callback=callback,
            file=file,
        )

    def catch_signal(
        self: Debugger,
        signal: int | str,
        callback: None | bool | Callable[[ThreadContext, SignalCatcher], None] = None,
        recursive: bool = False,
    ) -> SignalCatcher:
        """Catch a signal in the target process.

        Args:
            signal (int | str): The signal to catch. If "*", "ALL", "all" or -1 is passed, all signals will be caught.
            callback (None | bool | Callable[[ThreadContext, SignalCatcher], None], optional): A callback to be called when the signal is caught. If True, an empty callback will be set. Defaults to None.
            recursive (bool, optional): Whether, when the signal is hijacked with another one, the signal catcher associated with the new signal should be considered as well. Defaults to False.

        Returns:
            SignalCatcher: The SignalCatcher object.
        """
        return self._internal_debugger.catch_signal(signal, callback, recursive)

    def hijack_signal(
        self: Debugger,
        original_signal: int | str,
        new_signal: int | str,
        recursive: bool = False,
    ) -> SyscallHandler:
        """Hijack a signal in the target process.

        Args:
            original_signal (int | str): The signal to hijack. If "*", "ALL", "all" or -1 is passed, all signals will be hijacked.
            new_signal (int | str): The signal to hijack the original signal with.
            recursive (bool, optional): Whether, when the signal is hijacked with another one, the signal catcher associated with the new signal should be considered as well. Defaults to False.

        Returns:
            SignalCatcher: The SignalCatcher object.
        """
        return self._internal_debugger.hijack_signal(original_signal, new_signal, recursive)

    def handle_syscall(
        self: Debugger,
        syscall: int | str,
        on_enter: None | bool | Callable[[ThreadContext, SyscallHandler], None] = None,
        on_exit: None | bool | Callable[[ThreadContext, SyscallHandler], None] = None,
        recursive: bool = False,
    ) -> SyscallHandler:
        """Handle a syscall in the target process.

        Args:
            syscall (int | str): The syscall name or number to handle. If "*", "ALL", "all" or -1 is passed, all syscalls will be handled.
            on_enter (None | bool |Callable[[ThreadContext, SyscallHandler], None], optional): The callback to execute when the syscall is entered. If True, an empty callback will be set. Defaults to None.
            on_exit (None | bool | Callable[[ThreadContext, SyscallHandler], None], optional): The callback to execute when the syscall is exited. If True, an empty callback will be set. Defaults to None.
            recursive (bool, optional): Whether, when the syscall is hijacked with another one, the syscall handler associated with the new syscall should be considered as well. Defaults to False.

        Returns:
            SyscallHandler: The SyscallHandler object.
        """
        return self._internal_debugger.handle_syscall(syscall, on_enter, on_exit, recursive)

    def hijack_syscall(
        self: Debugger,
        original_syscall: int | str,
        new_syscall: int | str,
        recursive: bool = False,
        **kwargs: int,
    ) -> SyscallHandler:
        """Hijacks a syscall in the target process.

        Args:
            original_syscall (int | str): The syscall name or number to hijack. If "*", "ALL", "all" or -1 is passed, all syscalls will be hijacked.
            new_syscall (int | str): The syscall name or number to hijack the original syscall with.
            recursive (bool, optional): Whether, when the syscall is hijacked with another one, the syscall handler associated with the new syscall should be considered as well. Defaults to False.
            **kwargs: (int, optional): The arguments to pass to the new syscall.

        Returns:
            SyscallHandler: The SyscallHandler object.
        """
        return self._internal_debugger.hijack_syscall(original_syscall, new_syscall, recursive, **kwargs)

    def gdb(
        self: Debugger,
        migrate_breakpoints: bool = True,
        open_in_new_process: bool = True,
        blocking: bool = True,
    ) -> GdbResumeEvent:
        """Migrates the current debugging session to GDB.

        Args:
            migrate_breakpoints (bool): Whether to migrate over the breakpoints set in libdebug to GDB.
            open_in_new_process (bool): Whether to attempt to open GDB in a new process instead of the current one.
            blocking (bool): Whether to block the script until GDB is closed.
        """
        return self._internal_debugger.gdb(migrate_breakpoints, open_in_new_process, blocking)

    def r(self: Debugger, redirect_pipes: bool = True) -> PipeManager | None:
        """Alias for the `run` method.

        Starts the process and waits for it to stop.

        Args:
            redirect_pipes (bool): Whether to hook and redirect the pipes of the process to a PipeManager.
        """
        return self._internal_debugger.run(redirect_pipes)

    def c(self: Debugger) -> None:
        """Alias for the `cont` method.

        Continues the process.
        """
        self._internal_debugger.cont()

    def int(self: Debugger) -> None:
        """Alias for the `interrupt` method.

        Interrupts the process.
        """
        self._internal_debugger.interrupt()

    def w(self: Debugger) -> None:
        """Alias for the `wait` method.

        Waits for the process to stop.
        """
        self._internal_debugger.wait()

    def bp(
        self: Debugger,
        position: int | str,
        hardware: bool = False,
        condition: str = "x",
        length: int = 1,
        callback: None | Callable[[ThreadContext, Breakpoint], None] = None,
        file: str = "hybrid",
    ) -> Breakpoint:
        """Alias for the `breakpoint` method.

        Args:
            position (int | bytes): The location of the breakpoint.
            hardware (bool, optional): Whether the breakpoint should be hardware-assisted or purely software. Defaults to False.
            condition (str, optional): The trigger condition for the breakpoint. Defaults to None.
            length (int, optional): The length of the breakpoint. Only for watchpoints. Defaults to 1.
            callback (Callable[[ThreadContext, Breakpoint], None], optional): A callback to be called when the breakpoint is hit. Defaults to None.
            file (str, optional): The user-defined backing file to resolve the address in. Defaults to "hybrid" (libdebug will first try to solve the address as an absolute address, then as a relative address w.r.t. the "binary" map file).
        """
        return self._internal_debugger.breakpoint(position, hardware, condition, length, callback, file)

    def wp(
        self: Debugger,
        position: int | str,
        condition: str = "w",
        length: int = 1,
        callback: None | Callable[[ThreadContext, Breakpoint], None] = None,
        file: str = "hybrid",
    ) -> Breakpoint:
        """Alias for the `watchpoint` method.

        Sets a watchpoint at the specified location. Internally, watchpoints are implemented as breakpoints.

        Args:
            position (int | bytes): The location of the breakpoint.
            condition (str, optional): The trigger condition for the watchpoint (either "w", "rw" or "x"). Defaults to "w".
            length (int, optional): The size of the word in being watched (1, 2, 4 or 8). Defaults to 1.
            callback (Callable[[ThreadContext, Breakpoint], None], optional): A callback to be called when the watchpoint is hit. Defaults to None.
            file (str, optional): The user-defined backing file to resolve the address in. Defaults to "hybrid" (libdebug will first try to solve the address as an absolute address, then as a relative address w.r.t. the "binary" map file).
        """
        return self._internal_debugger.breakpoint(
            position,
            hardware=True,
            condition=condition,
            length=length,
            callback=callback,
            file=file,
        )

    @property
    def arch(self: Debugger) -> str:
        """Get the architecture of the process."""
        return self._internal_debugger.arch

    @arch.setter
    def arch(self: Debugger, value: str) -> None:
        """Set the architecture of the process."""
        self._internal_debugger.arch = map_arch(value)

    @property
    def kill_on_exit(self: Debugger) -> bool:
        """Get whether the process will be killed when the debugger exits."""
        return self._internal_debugger.kill_on_exit

    @kill_on_exit.setter
    def kill_on_exit(self: Debugger, value: bool) -> None:
        if not isinstance(value, bool):
            raise TypeError("kill_on_exit must be a boolean")

        self._internal_debugger.kill_on_exit = value

    @property
    def threads(self: Debugger) -> list[ThreadContext]:
        """Get the list of threads in the process."""
        return self._internal_debugger.threads

    @property
    def breakpoints(self: Debugger) -> dict[int, Breakpoint]:
        """Get the breakpoints set on the process."""
        return self._internal_debugger.breakpoints

    @property
    def handled_syscalls(self: InternalDebugger) -> dict[int, SyscallHandler]:
        """Get the handled syscalls dictionary.

        Returns:
            dict[int, SyscallHandler]: the handled syscalls dictionary.
        """
        return self._internal_debugger.handled_syscalls

    @property
    def caught_signals(self: InternalDebugger) -> dict[int, SignalCatcher]:
        """Get the caught signals dictionary.

        Returns:
            dict[int, SignalCatcher]: the caught signals dictionary.
        """
        return self._internal_debugger.caught_signals

    @property
    def maps(self: Debugger) -> MemoryMapList[MemoryMap]:
        """Get the memory maps of the process."""
        return self._internal_debugger.maps

    @property
    def pprint_syscalls(self: Debugger) -> bool:
        """Get the state of the pprint_syscalls flag.

        Returns:
            bool: True if the debugger should pretty print syscalls, False otherwise.
        """
        return self._internal_debugger.pprint_syscalls

    @pprint_syscalls.setter
    def pprint_syscalls(self: Debugger, value: bool) -> None:
        """Set the state of the pprint_syscalls flag.

        Args:
            value (bool): the value to set.
        """
        if not isinstance(value, bool):
            raise TypeError("pprint_syscalls must be a boolean")
        if value:
            self._internal_debugger.enable_pretty_print()
        else:
            self._internal_debugger.disable_pretty_print()

        self._internal_debugger.pprint_syscalls = value

    @contextmanager
    def pprint_syscalls_context(self: Debugger, value: bool) -> ...:
        """A context manager to temporarily change the state of the pprint_syscalls flag.

        Args:
            value (bool): the value to set.
        """
        old_value = self.pprint_syscalls
        self.pprint_syscalls = value
        yield
        self.pprint_syscalls = old_value

    @property
    def syscalls_to_pprint(self: Debugger) -> list[str] | None:
        """Get the syscalls to pretty print.

        Returns:
            list[str]: The syscalls to pretty print.
        """
        if self._internal_debugger.syscalls_to_pprint is None:
            return None
        else:
            return [
                resolve_syscall_name(self._internal_debugger.arch, v)
                for v in self._internal_debugger.syscalls_to_pprint
            ]

    @syscalls_to_pprint.setter
    def syscalls_to_pprint(self: Debugger, value: list[int | str] | None) -> None:
        """Get the syscalls to pretty print.

        Args:
            value (list[int | str] | None): The syscalls to pretty print.
        """
        if value is None:
            self._internal_debugger.syscalls_to_pprint = None
        elif isinstance(value, list):
            self._internal_debugger.syscalls_to_pprint = [
                v if isinstance(v, int) else resolve_syscall_number(self._internal_debugger.arch, v) for v in value
            ]
        else:
            raise ValueError(
                "syscalls_to_pprint must be a list of integers or strings or None.",
            )
        if self._internal_debugger.pprint_syscalls:
            self._internal_debugger.enable_pretty_print()

    @property
    def syscalls_to_not_pprint(self: Debugger) -> list[str] | None:
        """Get the syscalls to not pretty print.

        Returns:
            list[str]: The syscalls to not pretty print.
        """
        if self._internal_debugger.syscalls_to_not_pprint is None:
            return None
        else:
            return [
                resolve_syscall_name(self._internal_debugger.arch, v)
                for v in self._internal_debugger.syscalls_to_not_pprint
            ]

    @syscalls_to_not_pprint.setter
    def syscalls_to_not_pprint(self: Debugger, value: list[int | str] | None) -> None:
        """Get the syscalls to not pretty print.

        Args:
            value (list[int | str] | None): The syscalls to not pretty print.
        """
        if value is None:
            self._internal_debugger.syscalls_to_not_pprint = None
        elif isinstance(value, list):
            self._internal_debugger.syscalls_to_not_pprint = [
                v if isinstance(v, int) else resolve_syscall_number(self._internal_debugger.arch, v) for v in value
            ]
        else:
            raise ValueError(
                "syscalls_to_not_pprint must be a list of integers or strings or None.",
            )
        if self._internal_debugger.pprint_syscalls:
            self._internal_debugger.enable_pretty_print()

    @property
    def signals_to_block(self: Debugger) -> list[str]:
        """Get the signals to not forward to the process.

        Returns:
            list[str]: The signals to block.
        """
        return [resolve_signal_name(v) for v in self._internal_debugger.signals_to_block]

    @signals_to_block.setter
    def signals_to_block(self: Debugger, signals: list[int | str]) -> None:
        """Set the signal to not forward to the process.

        Args:
            signals (list[int | str]): The signals to block.
        """
        if not isinstance(signals, list):
            raise TypeError("signals_to_block must be a list of integers or strings")

        signals = [v if isinstance(v, int) else resolve_signal_number(v) for v in signals]

        if not set(signals).issubset(get_all_signal_numbers()):
            raise ValueError("Invalid signal number.")

        self._internal_debugger.signals_to_block = signals

    @property
    def fast_memory(self: Debugger) -> bool:
        """Get the state of the fast_memory flag.

        It is used to determine if the debugger should use a faster memory access method.

        Returns:
            bool: True if the debugger should use a faster memory access method, False otherwise.
        """
        return self._internal_debugger.fast_memory

    @fast_memory.setter
    def fast_memory(self: Debugger, value: bool) -> None:
        """Set the state of the fast_memory flag.

        It is used to determine if the debugger should use a faster memory access method.

        Args:
            value (bool): the value to set.
        """
        if not isinstance(value, bool):
            raise TypeError("fast_memory must be a boolean")
        self._internal_debugger.fast_memory = value

    @property
    def instruction_pointer(self: Debugger) -> int:
        """Get the main thread's instruction pointer."""
        if not self.threads:
            raise ValueError("No threads available.")
        return self.threads[0].instruction_pointer

    @instruction_pointer.setter
    def instruction_pointer(self: Debugger, value: int) -> None:
        """Set the main thread's instruction pointer."""
        if not self.threads:
            raise ValueError("No threads available.")
        self.threads[0].instruction_pointer = value

    @property
    def syscall_arg0(self: Debugger) -> int:
        """Get the main thread's syscall argument 0."""
        if not self.threads:
            raise ValueError("No threads available.")
        return self.threads[0].syscall_arg0

    @syscall_arg0.setter
    def syscall_arg0(self: Debugger, value: int) -> None:
        """Set the main thread's syscall argument 0."""
        if not self.threads:
            raise ValueError("No threads available.")
        self.threads[0].syscall_arg0 = value

    @property
    def syscall_arg1(self: Debugger) -> int:
        """Get the main thread's syscall argument 1."""
        if not self.threads:
            raise ValueError("No threads available.")
        return self.threads[0].syscall_arg1

    @syscall_arg1.setter
    def syscall_arg1(self: Debugger, value: int) -> None:
        """Set the main thread's syscall argument 1."""
        if not self.threads:
            raise ValueError("No threads available.")
        self.threads[0].syscall_arg1 = value

    @property
    def syscall_arg2(self: Debugger) -> int:
        """Get the main thread's syscall argument 2."""
        if not self.threads:
            raise ValueError("No threads available.")
        return self.threads[0].syscall_arg2

    @syscall_arg2.setter
    def syscall_arg2(self: Debugger, value: int) -> None:
        """Set the main thread's syscall argument 2."""
        if not self.threads:
            raise ValueError("No threads available.")
        self.threads[0].syscall_arg2 = value

    @property
    def syscall_arg3(self: Debugger) -> int:
        """Get the main thread's syscall argument 3."""
        if not self.threads:
            raise ValueError("No threads available.")
        return self.threads[0].syscall_arg3

    @syscall_arg3.setter
    def syscall_arg3(self: Debugger, value: int) -> None:
        """Set the main thread's syscall argument 3."""
        if not self.threads:
            raise ValueError("No threads available.")
        self.threads[0].syscall_arg3 = value

    @property
    def syscall_arg4(self: Debugger) -> int:
        """Get the main thread's syscall argument 4."""
        if not self.threads:
            raise ValueError("No threads available.")
        return self.threads[0].syscall_arg4

    @syscall_arg4.setter
    def syscall_arg4(self: Debugger, value: int) -> None:
        """Set the main thread's syscall argument 4."""
        if not self.threads:
            raise ValueError("No threads available.")
        self.threads[0].syscall_arg4 = value

    @property
    def syscall_arg5(self: Debugger) -> int:
        """Get the main thread's syscall argument 5."""
        if not self.threads:
            raise ValueError("No threads available.")
        return self.threads[0].syscall_arg5

    @syscall_arg5.setter
    def syscall_arg5(self: Debugger, value: int) -> None:
        """Set the main thread's syscall argument 5."""
        if not self.threads:
            raise ValueError("No threads available.")
        self.threads[0].syscall_arg5 = value

    @property
    def syscall_number(self: Debugger) -> int:
        """Get the main thread's syscall number."""
        if not self.threads:
            raise ValueError("No threads available.")
        return self.threads[0].syscall_number

    @syscall_number.setter
    def syscall_number(self: Debugger, value: int) -> None:
        """Set the main thread's syscall number."""
        if not self.threads:
            raise ValueError("No threads available.")
        self.threads[0].syscall_number = value

    @property
    def syscall_return(self: Debugger) -> int:
        """Get the main thread's syscall return value."""
        if not self.threads:
            raise ValueError("No threads available.")
        return self.threads[0].syscall_return

    @syscall_return.setter
    def syscall_return(self: Debugger, value: int) -> None:
        """Set the main thread's syscall return value."""
        if not self.threads:
            raise ValueError("No threads available.")
        self.threads[0].syscall_return = value

    @property
    def regs(self: Debugger) -> Registers:
        """Get the main thread's registers."""
        if not self.threads:
            raise ValueError("No threads available.")
        return self.threads[0].regs

    @property
    def dead(self: Debugger) -> bool:
        """Whether the process is dead."""
        if not self.threads:
            raise ValueError("No threads available.")
        return self.threads[0].dead

    @property
    def memory(self: Debugger) -> AbstractMemoryView:
        """The memory view of the process."""
        return self._internal_debugger.memory

    @property
    def mem(self: Debugger) -> AbstractMemoryView:
        """Alias for the `memory` property.

        Get the memory view of the process.
        """
        return self._internal_debugger.memory

    @property
    def process_id(self: Debugger) -> int:
        """The process ID."""
        return self._internal_debugger.process_id

    @property
    def pid(self: Debugger) -> int:
        """Alias for `process_id` property.

        The process ID.
        """
        return self._internal_debugger.process_id

    @property
    def thread_id(self: Debugger) -> int:
        """The thread ID of the main thread."""
        if not self.threads:
            raise ValueError("No threads available.")
        return self.threads[0].tid

    @property
    def tid(self: Debugger) -> int:
        """Alias for `thread_id` property.

        The thread ID of the main thread.
        """
        return self._thread_id

    @property
    def running(self: Debugger) -> bool:
        """Whether the process is running."""
        return self._internal_debugger.running

    @property
    def saved_ip(self: Debugger) -> int:
        """Get the saved instruction pointer of the main thread."""
        if not self.threads:
            raise ValueError("No threads available.")
        return self.threads[0].saved_ip

    @property
    def exit_code(self: Debugger) -> int | None:
        """The main thread's exit code."""
        if not self.threads:
            raise ValueError("No threads available.")
        return self.threads[0].exit_code

    @property
    def exit_signal(self: Debugger) -> str | None:
        """The main thread's exit signal."""
        if not self.threads:
            raise ValueError("No threads available.")
        return self.threads[0].exit_signal

    @property
    def signal(self: Debugger) -> str | None:
        """The signal to be forwarded to the main thread."""
        if not self.threads:
            raise ValueError("No threads available.")
        return self.threads[0].signal

    @signal.setter
    def signal(self: Debugger, signal: str | int) -> None:
        """Set the signal to forward to the main thread."""
        if not self.threads:
            raise ValueError("No threads available.")
        self.threads[0].signal = signal

    @property
    def signal_number(self: Debugger) -> int | None:
        """The signal number to be forwarded to the main thread."""
        if not self.threads:
            raise ValueError("No threads available.")
        return self.threads[0].signal_number

    def backtrace(self: Debugger, as_symbols: bool = False) -> list:
        """Returns the current backtrace of the main thread.

        Args:
            as_symbols (bool, optional): Whether to return the backtrace as symbols
        """
        if not self.threads:
            raise ValueError("No threads available.")
        return self.threads[0].backtrace(as_symbols)

    def pprint_backtrace(self: Debugger) -> None:
        """Pretty pints the current backtrace of the main thread."""
        if not self.threads:
            raise ValueError("No threads available.")
        self.threads[0].pprint_backtrace()

    def pprint_registers(self: Debugger) -> None:
        """Pretty prints the main thread's registers."""
        if not self.threads:
            raise ValueError("No threads available.")
        self.threads[0].pprint_registers()

    def pprint_regs(self: Debugger) -> None:
        """Alias for the `pprint_registers` method.

        Pretty prints the main thread's registers.
        """
        self.pprint_registers()

    def pprint_registers_all(self: Debugger) -> None:
        """Pretty prints all the main thread's registers."""
        if not self.threads:
            raise ValueError("No threads available.")
        self.threads[0].pprint_registers_all()

    def pprint_regs_all(self: Debugger) -> None:
        """Alias for the `pprint_registers_all` method.

        Pretty prints all the main thread's registers.
        """
        self.pprint_registers_all()

    def step(self: Debugger) -> None:
        """Executes a single instruction of the process."""
        self._internal_debugger.step(self)

    def step_until(
        self: Debugger,
        position: int | str,
        max_steps: int = -1,
        file: str = "hybrid",
    ) -> None:
        """Executes instructions of the process until the specified location is reached.

        Args:
            position (int | bytes): The location to reach.
            max_steps (int, optional): The maximum number of steps to execute. Defaults to -1.
            file (str, optional): The user-defined backing file to resolve the address in. Defaults to "hybrid" (libdebug will first try to solve the address as an absolute address, then as a relative address w.r.t. the "binary" map file).
        """
        self._internal_debugger.step_until(self, position, max_steps, file)

    def finish(self: Debugger, heuristic: str = "backtrace") -> None:
        """Continues execution until the current function returns or the process stops.

        The command requires a heuristic to determine the end of the function. The available heuristics are:
        - `backtrace`: The debugger will place a breakpoint on the saved return address found on the stack and continue execution on all threads.
        - `step-mode`: The debugger will step on the specified thread until the current function returns. This will be slower.

        Args:
            heuristic (str, optional): The heuristic to use. Defaults to "backtrace".
        """
        self._internal_debugger.finish(self, heuristic=heuristic)

    def next(self: Debugger) -> None:
        """Executes the next instruction of the process. If the instruction is a call, the debugger will continue until the called function returns."""
        self._internal_debugger.next(self)

    def si(self: Debugger) -> None:
        """Alias for the `step` method.

        Executes a single instruction of the process.
        """
        self._internal_debugger.step(self)

    def su(
        self: Debugger,
        position: int | str,
        max_steps: int = -1,
    ) -> None:
        """Alias for the `step_until` method.

        Executes instructions of the process until the specified location is reached.

        Args:
            position (int | bytes): The location to reach.
            max_steps (int, optional): The maximum number of steps to execute. Defaults to -1.
        """
        self._internal_debugger.step_until(self, position, max_steps)

    def fin(self: Debugger, heuristic: str = "backtrace") -> None:
        """Alias for the `finish` method. Continues execution until the current function returns or the process stops.

        The command requires a heuristic to determine the end of the function. The available heuristics are:
        - `backtrace`: The debugger will place a breakpoint on the saved return address found on the stack and continue execution on all threads.
        - `step-mode`: The debugger will step on the specified thread until the current function returns. This will be slower.

        Args:
            heuristic (str, optional): The heuristic to use. Defaults to "backtrace".
        """
        self._internal_debugger.finish(self, heuristic)

    def ni(self: Debugger) -> None:
        """Alias for the `next` method. Executes the next instruction of the process. If the instruction is a call, the debugger will continue until the called function returns."""
        self._internal_debugger.next(self)

    def __repr__(self: Debugger) -> str:
        """Return the string representation of the `Debugger` object."""
        repr_str = "Debugger("
        repr_str += f"argv = {self._internal_debugger.argv}, "
        repr_str += f"aslr = {self._internal_debugger.aslr_enabled}, "
        repr_str += f"env = {self._internal_debugger.env}, "
        repr_str += f"escape_antidebug = {self._internal_debugger.escape_antidebug}, "
        repr_str += f"continue_to_binary_entrypoint = {self._internal_debugger.autoreach_entrypoint}, "
        repr_str += f"auto_interrupt_on_command = {self._internal_debugger.auto_interrupt_on_command}, "
        repr_str += f"fast_memory = {self._internal_debugger.fast_memory}, "
        repr_str += f"kill_on_exit = {self._internal_debugger.kill_on_exit})\n"
        repr_str += f"  Architecture: {self.arch}\n"
        repr_str += "  Threads:"
        for thread in self.threads:
            repr_str += f"\n    ({thread.tid}, {'dead' if thread.dead else 'alive'}) "
            repr_str += f"ip: {thread.instruction_pointer:#x}"
        return repr_str

arch: str property writable

Get the architecture of the process.

breakpoints: dict[int, Breakpoint] property

Get the breakpoints set on the process.

caught_signals: dict[int, SignalCatcher] property

Get the caught signals dictionary.

Returns:

Type Description
dict[int, SignalCatcher]

dict[int, SignalCatcher]: the caught signals dictionary.

dead: bool property

Whether the process is dead.

exit_code: int | None property

The main thread's exit code.

exit_signal: str | None property

The main thread's exit signal.

fast_memory: bool property writable

Get the state of the fast_memory flag.

It is used to determine if the debugger should use a faster memory access method.

Returns:

Name Type Description
bool bool

True if the debugger should use a faster memory access method, False otherwise.

handled_syscalls: dict[int, SyscallHandler] property

Get the handled syscalls dictionary.

Returns:

Type Description
dict[int, SyscallHandler]

dict[int, SyscallHandler]: the handled syscalls dictionary.

instruction_pointer: int property writable

Get the main thread's instruction pointer.

kill_on_exit: bool property writable

Get whether the process will be killed when the debugger exits.

maps: MemoryMapList[MemoryMap] property

Get the memory maps of the process.

mem: AbstractMemoryView property

Alias for the memory property.

Get the memory view of the process.

memory: AbstractMemoryView property

The memory view of the process.

pid: int property

Alias for process_id property.

The process ID.

pprint_syscalls: bool property writable

Get the state of the pprint_syscalls flag.

Returns:

Name Type Description
bool bool

True if the debugger should pretty print syscalls, False otherwise.

process_id: int property

The process ID.

regs: Registers property

Get the main thread's registers.

running: bool property

Whether the process is running.

saved_ip: int property

Get the saved instruction pointer of the main thread.

signal: str | None property writable

The signal to be forwarded to the main thread.

signal_number: int | None property

The signal number to be forwarded to the main thread.

signals_to_block: list[str] property writable

Get the signals to not forward to the process.

Returns:

Type Description
list[str]

list[str]: The signals to block.

symbols: SymbolList[Symbol] property

Get the symbols of the process.

syscall_arg0: int property writable

Get the main thread's syscall argument 0.

syscall_arg1: int property writable

Get the main thread's syscall argument 1.

syscall_arg2: int property writable

Get the main thread's syscall argument 2.

syscall_arg3: int property writable

Get the main thread's syscall argument 3.

syscall_arg4: int property writable

Get the main thread's syscall argument 4.

syscall_arg5: int property writable

Get the main thread's syscall argument 5.

syscall_number: int property writable

Get the main thread's syscall number.

syscall_return: int property writable

Get the main thread's syscall return value.

syscalls_to_not_pprint: list[str] | None property writable

Get the syscalls to not pretty print.

Returns:

Type Description
list[str] | None

list[str]: The syscalls to not pretty print.

syscalls_to_pprint: list[str] | None property writable

Get the syscalls to pretty print.

Returns:

Type Description
list[str] | None

list[str]: The syscalls to pretty print.

thread_id: int property

The thread ID of the main thread.

threads: list[ThreadContext] property

Get the list of threads in the process.

tid: int property

Alias for thread_id property.

The thread ID of the main thread.

__repr__()

Return the string representation of the Debugger object.

Source code in libdebug/debugger/debugger.py
def __repr__(self: Debugger) -> str:
    """Return the string representation of the `Debugger` object."""
    repr_str = "Debugger("
    repr_str += f"argv = {self._internal_debugger.argv}, "
    repr_str += f"aslr = {self._internal_debugger.aslr_enabled}, "
    repr_str += f"env = {self._internal_debugger.env}, "
    repr_str += f"escape_antidebug = {self._internal_debugger.escape_antidebug}, "
    repr_str += f"continue_to_binary_entrypoint = {self._internal_debugger.autoreach_entrypoint}, "
    repr_str += f"auto_interrupt_on_command = {self._internal_debugger.auto_interrupt_on_command}, "
    repr_str += f"fast_memory = {self._internal_debugger.fast_memory}, "
    repr_str += f"kill_on_exit = {self._internal_debugger.kill_on_exit})\n"
    repr_str += f"  Architecture: {self.arch}\n"
    repr_str += "  Threads:"
    for thread in self.threads:
        repr_str += f"\n    ({thread.tid}, {'dead' if thread.dead else 'alive'}) "
        repr_str += f"ip: {thread.instruction_pointer:#x}"
    return repr_str

attach(pid)

Attaches to an existing process.

Source code in libdebug/debugger/debugger.py
def attach(self: Debugger, pid: int) -> None:
    """Attaches to an existing process."""
    self._internal_debugger.attach(pid)

backtrace(as_symbols=False)

Returns the current backtrace of the main thread.

Parameters:

Name Type Description Default
as_symbols bool

Whether to return the backtrace as symbols

False
Source code in libdebug/debugger/debugger.py
def backtrace(self: Debugger, as_symbols: bool = False) -> list:
    """Returns the current backtrace of the main thread.

    Args:
        as_symbols (bool, optional): Whether to return the backtrace as symbols
    """
    if not self.threads:
        raise ValueError("No threads available.")
    return self.threads[0].backtrace(as_symbols)

bp(position, hardware=False, condition='x', length=1, callback=None, file='hybrid')

Alias for the breakpoint method.

Parameters:

Name Type Description Default
position int | bytes

The location of the breakpoint.

required
hardware bool

Whether the breakpoint should be hardware-assisted or purely software. Defaults to False.

False
condition str

The trigger condition for the breakpoint. Defaults to None.

'x'
length int

The length of the breakpoint. Only for watchpoints. Defaults to 1.

1
callback Callable[[ThreadContext, Breakpoint], None]

A callback to be called when the breakpoint is hit. Defaults to None.

None
file str

The user-defined backing file to resolve the address in. Defaults to "hybrid" (libdebug will first try to solve the address as an absolute address, then as a relative address w.r.t. the "binary" map file).

'hybrid'
Source code in libdebug/debugger/debugger.py
def bp(
    self: Debugger,
    position: int | str,
    hardware: bool = False,
    condition: str = "x",
    length: int = 1,
    callback: None | Callable[[ThreadContext, Breakpoint], None] = None,
    file: str = "hybrid",
) -> Breakpoint:
    """Alias for the `breakpoint` method.

    Args:
        position (int | bytes): The location of the breakpoint.
        hardware (bool, optional): Whether the breakpoint should be hardware-assisted or purely software. Defaults to False.
        condition (str, optional): The trigger condition for the breakpoint. Defaults to None.
        length (int, optional): The length of the breakpoint. Only for watchpoints. Defaults to 1.
        callback (Callable[[ThreadContext, Breakpoint], None], optional): A callback to be called when the breakpoint is hit. Defaults to None.
        file (str, optional): The user-defined backing file to resolve the address in. Defaults to "hybrid" (libdebug will first try to solve the address as an absolute address, then as a relative address w.r.t. the "binary" map file).
    """
    return self._internal_debugger.breakpoint(position, hardware, condition, length, callback, file)

breakpoint(position, hardware=False, condition='x', length=1, callback=None, file='hybrid')

Sets a breakpoint at the specified location.

Parameters:

Name Type Description Default
position int | bytes

The location of the breakpoint.

required
hardware bool

Whether the breakpoint should be hardware-assisted or purely software. Defaults to False.

False
condition str

The trigger condition for the breakpoint. Defaults to None.

'x'
length int

The length of the breakpoint. Only for watchpoints. Defaults to 1.

1
callback None | bool | Callable[[ThreadContext, Breakpoint], None]

A callback to be called when the breakpoint is hit. If True, an empty callback will be set. Defaults to None.

None
file str

The user-defined backing file to resolve the address in. Defaults to "hybrid" (libdebug will first try to solve the address as an absolute address, then as a relative address w.r.t. the "binary" map file).

'hybrid'
Source code in libdebug/debugger/debugger.py
def breakpoint(
    self: Debugger,
    position: int | str,
    hardware: bool = False,
    condition: str = "x",
    length: int = 1,
    callback: None | bool | Callable[[ThreadContext, Breakpoint], None] = None,
    file: str = "hybrid",
) -> Breakpoint:
    """Sets a breakpoint at the specified location.

    Args:
        position (int | bytes): The location of the breakpoint.
        hardware (bool, optional): Whether the breakpoint should be hardware-assisted or purely software. Defaults to False.
        condition (str, optional): The trigger condition for the breakpoint. Defaults to None.
        length (int, optional): The length of the breakpoint. Only for watchpoints. Defaults to 1.
        callback (None | bool | Callable[[ThreadContext, Breakpoint], None], optional): A callback to be called when the breakpoint is hit. If True, an empty callback will be set. Defaults to None.
        file (str, optional): The user-defined backing file to resolve the address in. Defaults to "hybrid" (libdebug will first try to solve the address as an absolute address, then as a relative address w.r.t. the "binary" map file).
    """
    return self._internal_debugger.breakpoint(position, hardware, condition, length, callback, file)

c()

Alias for the cont method.

Continues the process.

Source code in libdebug/debugger/debugger.py
def c(self: Debugger) -> None:
    """Alias for the `cont` method.

    Continues the process.
    """
    self._internal_debugger.cont()

catch_signal(signal, callback=None, recursive=False)

Catch a signal in the target process.

Parameters:

Name Type Description Default
signal int | str

The signal to catch. If "*", "ALL", "all" or -1 is passed, all signals will be caught.

required
callback None | bool | Callable[[ThreadContext, SignalCatcher], None]

A callback to be called when the signal is caught. If True, an empty callback will be set. Defaults to None.

None
recursive bool

Whether, when the signal is hijacked with another one, the signal catcher associated with the new signal should be considered as well. Defaults to False.

False

Returns:

Name Type Description
SignalCatcher SignalCatcher

The SignalCatcher object.

Source code in libdebug/debugger/debugger.py
def catch_signal(
    self: Debugger,
    signal: int | str,
    callback: None | bool | Callable[[ThreadContext, SignalCatcher], None] = None,
    recursive: bool = False,
) -> SignalCatcher:
    """Catch a signal in the target process.

    Args:
        signal (int | str): The signal to catch. If "*", "ALL", "all" or -1 is passed, all signals will be caught.
        callback (None | bool | Callable[[ThreadContext, SignalCatcher], None], optional): A callback to be called when the signal is caught. If True, an empty callback will be set. Defaults to None.
        recursive (bool, optional): Whether, when the signal is hijacked with another one, the signal catcher associated with the new signal should be considered as well. Defaults to False.

    Returns:
        SignalCatcher: The SignalCatcher object.
    """
    return self._internal_debugger.catch_signal(signal, callback, recursive)

cont()

Continues the process.

Source code in libdebug/debugger/debugger.py
def cont(self: Debugger) -> None:
    """Continues the process."""
    self._internal_debugger.cont()

detach()

Detaches from the process.

Source code in libdebug/debugger/debugger.py
def detach(self: Debugger) -> None:
    """Detaches from the process."""
    self._internal_debugger.detach()

fin(heuristic='backtrace')

Alias for the finish method. Continues execution until the current function returns or the process stops.

The command requires a heuristic to determine the end of the function. The available heuristics are: - backtrace: The debugger will place a breakpoint on the saved return address found on the stack and continue execution on all threads. - step-mode: The debugger will step on the specified thread until the current function returns. This will be slower.

Parameters:

Name Type Description Default
heuristic str

The heuristic to use. Defaults to "backtrace".

'backtrace'
Source code in libdebug/debugger/debugger.py
def fin(self: Debugger, heuristic: str = "backtrace") -> None:
    """Alias for the `finish` method. Continues execution until the current function returns or the process stops.

    The command requires a heuristic to determine the end of the function. The available heuristics are:
    - `backtrace`: The debugger will place a breakpoint on the saved return address found on the stack and continue execution on all threads.
    - `step-mode`: The debugger will step on the specified thread until the current function returns. This will be slower.

    Args:
        heuristic (str, optional): The heuristic to use. Defaults to "backtrace".
    """
    self._internal_debugger.finish(self, heuristic)

finish(heuristic='backtrace')

Continues execution until the current function returns or the process stops.

The command requires a heuristic to determine the end of the function. The available heuristics are: - backtrace: The debugger will place a breakpoint on the saved return address found on the stack and continue execution on all threads. - step-mode: The debugger will step on the specified thread until the current function returns. This will be slower.

Parameters:

Name Type Description Default
heuristic str

The heuristic to use. Defaults to "backtrace".

'backtrace'
Source code in libdebug/debugger/debugger.py
def finish(self: Debugger, heuristic: str = "backtrace") -> None:
    """Continues execution until the current function returns or the process stops.

    The command requires a heuristic to determine the end of the function. The available heuristics are:
    - `backtrace`: The debugger will place a breakpoint on the saved return address found on the stack and continue execution on all threads.
    - `step-mode`: The debugger will step on the specified thread until the current function returns. This will be slower.

    Args:
        heuristic (str, optional): The heuristic to use. Defaults to "backtrace".
    """
    self._internal_debugger.finish(self, heuristic=heuristic)

gdb(migrate_breakpoints=True, open_in_new_process=True, blocking=True)

Migrates the current debugging session to GDB.

Parameters:

Name Type Description Default
migrate_breakpoints bool

Whether to migrate over the breakpoints set in libdebug to GDB.

True
open_in_new_process bool

Whether to attempt to open GDB in a new process instead of the current one.

True
blocking bool

Whether to block the script until GDB is closed.

True
Source code in libdebug/debugger/debugger.py
def gdb(
    self: Debugger,
    migrate_breakpoints: bool = True,
    open_in_new_process: bool = True,
    blocking: bool = True,
) -> GdbResumeEvent:
    """Migrates the current debugging session to GDB.

    Args:
        migrate_breakpoints (bool): Whether to migrate over the breakpoints set in libdebug to GDB.
        open_in_new_process (bool): Whether to attempt to open GDB in a new process instead of the current one.
        blocking (bool): Whether to block the script until GDB is closed.
    """
    return self._internal_debugger.gdb(migrate_breakpoints, open_in_new_process, blocking)

handle_syscall(syscall, on_enter=None, on_exit=None, recursive=False)

Handle a syscall in the target process.

Parameters:

Name Type Description Default
syscall int | str

The syscall name or number to handle. If "*", "ALL", "all" or -1 is passed, all syscalls will be handled.

required
on_enter None | bool | Callable[[ThreadContext, SyscallHandler], None]

The callback to execute when the syscall is entered. If True, an empty callback will be set. Defaults to None.

None
on_exit None | bool | Callable[[ThreadContext, SyscallHandler], None]

The callback to execute when the syscall is exited. If True, an empty callback will be set. Defaults to None.

None
recursive bool

Whether, when the syscall is hijacked with another one, the syscall handler associated with the new syscall should be considered as well. Defaults to False.

False

Returns:

Name Type Description
SyscallHandler SyscallHandler

The SyscallHandler object.

Source code in libdebug/debugger/debugger.py
def handle_syscall(
    self: Debugger,
    syscall: int | str,
    on_enter: None | bool | Callable[[ThreadContext, SyscallHandler], None] = None,
    on_exit: None | bool | Callable[[ThreadContext, SyscallHandler], None] = None,
    recursive: bool = False,
) -> SyscallHandler:
    """Handle a syscall in the target process.

    Args:
        syscall (int | str): The syscall name or number to handle. If "*", "ALL", "all" or -1 is passed, all syscalls will be handled.
        on_enter (None | bool |Callable[[ThreadContext, SyscallHandler], None], optional): The callback to execute when the syscall is entered. If True, an empty callback will be set. Defaults to None.
        on_exit (None | bool | Callable[[ThreadContext, SyscallHandler], None], optional): The callback to execute when the syscall is exited. If True, an empty callback will be set. Defaults to None.
        recursive (bool, optional): Whether, when the syscall is hijacked with another one, the syscall handler associated with the new syscall should be considered as well. Defaults to False.

    Returns:
        SyscallHandler: The SyscallHandler object.
    """
    return self._internal_debugger.handle_syscall(syscall, on_enter, on_exit, recursive)

hijack_signal(original_signal, new_signal, recursive=False)

Hijack a signal in the target process.

Parameters:

Name Type Description Default
original_signal int | str

The signal to hijack. If "*", "ALL", "all" or -1 is passed, all signals will be hijacked.

required
new_signal int | str

The signal to hijack the original signal with.

required
recursive bool

Whether, when the signal is hijacked with another one, the signal catcher associated with the new signal should be considered as well. Defaults to False.

False

Returns:

Name Type Description
SignalCatcher SyscallHandler

The SignalCatcher object.

Source code in libdebug/debugger/debugger.py
def hijack_signal(
    self: Debugger,
    original_signal: int | str,
    new_signal: int | str,
    recursive: bool = False,
) -> SyscallHandler:
    """Hijack a signal in the target process.

    Args:
        original_signal (int | str): The signal to hijack. If "*", "ALL", "all" or -1 is passed, all signals will be hijacked.
        new_signal (int | str): The signal to hijack the original signal with.
        recursive (bool, optional): Whether, when the signal is hijacked with another one, the signal catcher associated with the new signal should be considered as well. Defaults to False.

    Returns:
        SignalCatcher: The SignalCatcher object.
    """
    return self._internal_debugger.hijack_signal(original_signal, new_signal, recursive)

hijack_syscall(original_syscall, new_syscall, recursive=False, **kwargs)

Hijacks a syscall in the target process.

Parameters:

Name Type Description Default
original_syscall int | str

The syscall name or number to hijack. If "*", "ALL", "all" or -1 is passed, all syscalls will be hijacked.

required
new_syscall int | str

The syscall name or number to hijack the original syscall with.

required
recursive bool

Whether, when the syscall is hijacked with another one, the syscall handler associated with the new syscall should be considered as well. Defaults to False.

False
**kwargs int

(int, optional): The arguments to pass to the new syscall.

{}

Returns:

Name Type Description
SyscallHandler SyscallHandler

The SyscallHandler object.

Source code in libdebug/debugger/debugger.py
def hijack_syscall(
    self: Debugger,
    original_syscall: int | str,
    new_syscall: int | str,
    recursive: bool = False,
    **kwargs: int,
) -> SyscallHandler:
    """Hijacks a syscall in the target process.

    Args:
        original_syscall (int | str): The syscall name or number to hijack. If "*", "ALL", "all" or -1 is passed, all syscalls will be hijacked.
        new_syscall (int | str): The syscall name or number to hijack the original syscall with.
        recursive (bool, optional): Whether, when the syscall is hijacked with another one, the syscall handler associated with the new syscall should be considered as well. Defaults to False.
        **kwargs: (int, optional): The arguments to pass to the new syscall.

    Returns:
        SyscallHandler: The SyscallHandler object.
    """
    return self._internal_debugger.hijack_syscall(original_syscall, new_syscall, recursive, **kwargs)

int()

Alias for the interrupt method.

Interrupts the process.

Source code in libdebug/debugger/debugger.py
def int(self: Debugger) -> None:
    """Alias for the `interrupt` method.

    Interrupts the process.
    """
    self._internal_debugger.interrupt()

interrupt()

Interrupts the process.

Source code in libdebug/debugger/debugger.py
def interrupt(self: Debugger) -> None:
    """Interrupts the process."""
    self._internal_debugger.interrupt()

kill()

Kills the process.

Source code in libdebug/debugger/debugger.py
def kill(self: Debugger) -> None:
    """Kills the process."""
    self._internal_debugger.kill()

next()

Executes the next instruction of the process. If the instruction is a call, the debugger will continue until the called function returns.

Source code in libdebug/debugger/debugger.py
def next(self: Debugger) -> None:
    """Executes the next instruction of the process. If the instruction is a call, the debugger will continue until the called function returns."""
    self._internal_debugger.next(self)

ni()

Alias for the next method. Executes the next instruction of the process. If the instruction is a call, the debugger will continue until the called function returns.

Source code in libdebug/debugger/debugger.py
def ni(self: Debugger) -> None:
    """Alias for the `next` method. Executes the next instruction of the process. If the instruction is a call, the debugger will continue until the called function returns."""
    self._internal_debugger.next(self)

post_init_(internal_debugger)

Do not use this constructor directly. Use the debugger function instead.

Source code in libdebug/debugger/debugger.py
def post_init_(self: Debugger, internal_debugger: InternalDebugger) -> None:
    """Do not use this constructor directly. Use the `debugger` function instead."""
    self._internal_debugger = internal_debugger
    self._internal_debugger.start_up()

pprint_backtrace()

Pretty pints the current backtrace of the main thread.

Source code in libdebug/debugger/debugger.py
def pprint_backtrace(self: Debugger) -> None:
    """Pretty pints the current backtrace of the main thread."""
    if not self.threads:
        raise ValueError("No threads available.")
    self.threads[0].pprint_backtrace()

pprint_maps()

Prints the memory maps of the process.

Source code in libdebug/debugger/debugger.py
def pprint_maps(self: Debugger) -> None:
    """Prints the memory maps of the process."""
    self._internal_debugger.pprint_maps()

pprint_registers()

Pretty prints the main thread's registers.

Source code in libdebug/debugger/debugger.py
def pprint_registers(self: Debugger) -> None:
    """Pretty prints the main thread's registers."""
    if not self.threads:
        raise ValueError("No threads available.")
    self.threads[0].pprint_registers()

pprint_registers_all()

Pretty prints all the main thread's registers.

Source code in libdebug/debugger/debugger.py
def pprint_registers_all(self: Debugger) -> None:
    """Pretty prints all the main thread's registers."""
    if not self.threads:
        raise ValueError("No threads available.")
    self.threads[0].pprint_registers_all()

pprint_regs()

Alias for the pprint_registers method.

Pretty prints the main thread's registers.

Source code in libdebug/debugger/debugger.py
def pprint_regs(self: Debugger) -> None:
    """Alias for the `pprint_registers` method.

    Pretty prints the main thread's registers.
    """
    self.pprint_registers()

pprint_regs_all()

Alias for the pprint_registers_all method.

Pretty prints all the main thread's registers.

Source code in libdebug/debugger/debugger.py
def pprint_regs_all(self: Debugger) -> None:
    """Alias for the `pprint_registers_all` method.

    Pretty prints all the main thread's registers.
    """
    self.pprint_registers_all()

pprint_syscalls_context(value)

A context manager to temporarily change the state of the pprint_syscalls flag.

Parameters:

Name Type Description Default
value bool

the value to set.

required
Source code in libdebug/debugger/debugger.py
@contextmanager
def pprint_syscalls_context(self: Debugger, value: bool) -> ...:
    """A context manager to temporarily change the state of the pprint_syscalls flag.

    Args:
        value (bool): the value to set.
    """
    old_value = self.pprint_syscalls
    self.pprint_syscalls = value
    yield
    self.pprint_syscalls = old_value

print_maps()

Prints the memory maps of the process.

Source code in libdebug/debugger/debugger.py
def print_maps(self: Debugger) -> None:
    """Prints the memory maps of the process."""
    liblog.warning("The `print_maps` method is deprecated. Use `d.pprint_maps` instead.")
    self._internal_debugger.pprint_maps()

r(redirect_pipes=True)

Alias for the run method.

Starts the process and waits for it to stop.

Parameters:

Name Type Description Default
redirect_pipes bool

Whether to hook and redirect the pipes of the process to a PipeManager.

True
Source code in libdebug/debugger/debugger.py
def r(self: Debugger, redirect_pipes: bool = True) -> PipeManager | None:
    """Alias for the `run` method.

    Starts the process and waits for it to stop.

    Args:
        redirect_pipes (bool): Whether to hook and redirect the pipes of the process to a PipeManager.
    """
    return self._internal_debugger.run(redirect_pipes)

resolve_symbol(symbol, file='binary')

Resolves the address of the specified symbol.

Parameters:

Name Type Description Default
symbol str

The symbol to resolve.

required
file str

The backing file to resolve the symbol in. Defaults to "binary"

'binary'

Returns:

Name Type Description
int int

The address of the symbol.

Source code in libdebug/debugger/debugger.py
def resolve_symbol(self: Debugger, symbol: str, file: str = "binary") -> int:
    """Resolves the address of the specified symbol.

    Args:
        symbol (str): The symbol to resolve.
        file (str): The backing file to resolve the symbol in. Defaults to "binary"

    Returns:
        int: The address of the symbol.
    """
    return self._internal_debugger.resolve_symbol(symbol, file)

run(redirect_pipes=True)

Starts the process and waits for it to stop.

Parameters:

Name Type Description Default
redirect_pipes bool

Whether to hook and redirect the pipes of the process to a PipeManager.

True
Source code in libdebug/debugger/debugger.py
def run(self: Debugger, redirect_pipes: bool = True) -> PipeManager | None:
    """Starts the process and waits for it to stop.

    Args:
        redirect_pipes (bool): Whether to hook and redirect the pipes of the process to a PipeManager.
    """
    return self._internal_debugger.run(redirect_pipes)

si()

Alias for the step method.

Executes a single instruction of the process.

Source code in libdebug/debugger/debugger.py
def si(self: Debugger) -> None:
    """Alias for the `step` method.

    Executes a single instruction of the process.
    """
    self._internal_debugger.step(self)

step()

Executes a single instruction of the process.

Source code in libdebug/debugger/debugger.py
def step(self: Debugger) -> None:
    """Executes a single instruction of the process."""
    self._internal_debugger.step(self)

step_until(position, max_steps=-1, file='hybrid')

Executes instructions of the process until the specified location is reached.

Parameters:

Name Type Description Default
position int | bytes

The location to reach.

required
max_steps int

The maximum number of steps to execute. Defaults to -1.

-1
file str

The user-defined backing file to resolve the address in. Defaults to "hybrid" (libdebug will first try to solve the address as an absolute address, then as a relative address w.r.t. the "binary" map file).

'hybrid'
Source code in libdebug/debugger/debugger.py
def step_until(
    self: Debugger,
    position: int | str,
    max_steps: int = -1,
    file: str = "hybrid",
) -> None:
    """Executes instructions of the process until the specified location is reached.

    Args:
        position (int | bytes): The location to reach.
        max_steps (int, optional): The maximum number of steps to execute. Defaults to -1.
        file (str, optional): The user-defined backing file to resolve the address in. Defaults to "hybrid" (libdebug will first try to solve the address as an absolute address, then as a relative address w.r.t. the "binary" map file).
    """
    self._internal_debugger.step_until(self, position, max_steps, file)

su(position, max_steps=-1)

Alias for the step_until method.

Executes instructions of the process until the specified location is reached.

Parameters:

Name Type Description Default
position int | bytes

The location to reach.

required
max_steps int

The maximum number of steps to execute. Defaults to -1.

-1
Source code in libdebug/debugger/debugger.py
def su(
    self: Debugger,
    position: int | str,
    max_steps: int = -1,
) -> None:
    """Alias for the `step_until` method.

    Executes instructions of the process until the specified location is reached.

    Args:
        position (int | bytes): The location to reach.
        max_steps (int, optional): The maximum number of steps to execute. Defaults to -1.
    """
    self._internal_debugger.step_until(self, position, max_steps)

terminate()

Interrupts the process, kills it and then terminates the background thread.

The debugger object will not be usable after this method is called. This method should only be called to free up resources when the debugger object is no longer needed.

Source code in libdebug/debugger/debugger.py
def terminate(self: Debugger) -> None:
    """Interrupts the process, kills it and then terminates the background thread.

    The debugger object will not be usable after this method is called.
    This method should only be called to free up resources when the debugger object is no longer needed.
    """
    self._internal_debugger.terminate()

w()

Alias for the wait method.

Waits for the process to stop.

Source code in libdebug/debugger/debugger.py
def w(self: Debugger) -> None:
    """Alias for the `wait` method.

    Waits for the process to stop.
    """
    self._internal_debugger.wait()

wait()

Waits for the process to stop.

Source code in libdebug/debugger/debugger.py
def wait(self: Debugger) -> None:
    """Waits for the process to stop."""
    self._internal_debugger.wait()

watchpoint(position, condition='w', length=1, callback=None, file='hybrid')

Sets a watchpoint at the specified location. Internally, watchpoints are implemented as breakpoints.

Parameters:

Name Type Description Default
position int | bytes

The location of the breakpoint.

required
condition str

The trigger condition for the watchpoint (either "w", "rw" or "x"). Defaults to "w".

'w'
length int

The size of the word in being watched (1, 2, 4 or 8). Defaults to 1.

1
callback None | bool | Callable[[ThreadContext, Breakpoint], None]

A callback to be called when the watchpoint is hit. If True, an empty callback will be set. Defaults to None.

None
file str

The user-defined backing file to resolve the address in. Defaults to "hybrid" (libdebug will first try to solve the address as an absolute address, then as a relative address w.r.t. the "binary" map file).

'hybrid'
Source code in libdebug/debugger/debugger.py
def watchpoint(
    self: Debugger,
    position: int | str,
    condition: str = "w",
    length: int = 1,
    callback: None | bool | Callable[[ThreadContext, Breakpoint], None] = None,
    file: str = "hybrid",
) -> Breakpoint:
    """Sets a watchpoint at the specified location. Internally, watchpoints are implemented as breakpoints.

    Args:
        position (int | bytes): The location of the breakpoint.
        condition (str, optional): The trigger condition for the watchpoint (either "w", "rw" or "x"). Defaults to "w".
        length (int, optional): The size of the word in being watched (1, 2, 4 or 8). Defaults to 1.
        callback (None | bool | Callable[[ThreadContext, Breakpoint], None], optional): A callback to be called when the watchpoint is hit. If True, an empty callback will be set. Defaults to None.
        file (str, optional): The user-defined backing file to resolve the address in. Defaults to "hybrid" (libdebug will first try to solve the address as an absolute address, then as a relative address w.r.t. the "binary" map file).
    """
    return self._internal_debugger.breakpoint(
        position,
        hardware=True,
        condition=condition,
        length=length,
        callback=callback,
        file=file,
    )

wp(position, condition='w', length=1, callback=None, file='hybrid')

Alias for the watchpoint method.

Sets a watchpoint at the specified location. Internally, watchpoints are implemented as breakpoints.

Parameters:

Name Type Description Default
position int | bytes

The location of the breakpoint.

required
condition str

The trigger condition for the watchpoint (either "w", "rw" or "x"). Defaults to "w".

'w'
length int

The size of the word in being watched (1, 2, 4 or 8). Defaults to 1.

1
callback Callable[[ThreadContext, Breakpoint], None]

A callback to be called when the watchpoint is hit. Defaults to None.

None
file str

The user-defined backing file to resolve the address in. Defaults to "hybrid" (libdebug will first try to solve the address as an absolute address, then as a relative address w.r.t. the "binary" map file).

'hybrid'
Source code in libdebug/debugger/debugger.py
def wp(
    self: Debugger,
    position: int | str,
    condition: str = "w",
    length: int = 1,
    callback: None | Callable[[ThreadContext, Breakpoint], None] = None,
    file: str = "hybrid",
) -> Breakpoint:
    """Alias for the `watchpoint` method.

    Sets a watchpoint at the specified location. Internally, watchpoints are implemented as breakpoints.

    Args:
        position (int | bytes): The location of the breakpoint.
        condition (str, optional): The trigger condition for the watchpoint (either "w", "rw" or "x"). Defaults to "w".
        length (int, optional): The size of the word in being watched (1, 2, 4 or 8). Defaults to 1.
        callback (Callable[[ThreadContext, Breakpoint], None], optional): A callback to be called when the watchpoint is hit. Defaults to None.
        file (str, optional): The user-defined backing file to resolve the address in. Defaults to "hybrid" (libdebug will first try to solve the address as an absolute address, then as a relative address w.r.t. the "binary" map file).
    """
    return self._internal_debugger.breakpoint(
        position,
        hardware=True,
        condition=condition,
        length=length,
        callback=callback,
        file=file,
    )