Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
43f9cb5
feat: first prototype (based on nanobind). WIP - not yet working
Frank01001 Nov 1, 2024
4ce078c
debug: debugging setup, thread is dead by the time the nb function is…
Frank01001 Nov 2, 2024
68c41ed
Merge branch 'dev', remote-tracking branch 'origin' into arbitrary-sy…
Frank01001 Feb 15, 2025
5653dcb
feat: got the first correct arbitrary write syscall working. :D
Frank01001 Feb 15, 2025
603f960
chore: decluttering and refactoring
Frank01001 Feb 15, 2025
ae71c8d
Merge branch 'dev' into arbitrary-syscall
Frank01001 Mar 14, 2025
a3c01cc
fix: missing return
Frank01001 Mar 14, 2025
cf013b8
feat: created file for useful constants and parsing
Frank01001 Mar 14, 2025
0bec031
fix: Generalized error handling
Frank01001 Mar 23, 2025
9528512
Merge branch 'dev' into `arbitrary-syscall`
Frank01001 Mar 23, 2025
9840a79
feat: prototype of the python conversion of the arbitrary syscall code
Frank01001 Mar 23, 2025
ad6a7c3
fix: debugging prints to debug the addition of emulated syscall
Frank01001 Mar 26, 2025
75d7db6
Merge branch 'dev' into arbitrary-syscall
Frank01001 Mar 26, 2025
f720b51
feat: working prototype of syscall handling during invocation
Frank01001 Mar 26, 2025
e9b49d7
feat: prototype of error parsing for pprint_syscalls
Frank01001 Mar 26, 2025
cfbcc3e
Merge pull request #219 from libdebug/dev
MrIndeciso Mar 27, 2025
d08e30e
Merge remote-tracking branch 'origin' into arbitrary-syscall
Frank01001 Mar 27, 2025
16710c0
feat: further progress on parameter parsing for pprint_syscall
Frank01001 Mar 27, 2025
3e1486d
feat: partial implementation of arg parsing
Frank01001 Apr 2, 2025
a50765e
fix: finally the invoke syscall works (allegedly)
Frank01001 Apr 2, 2025
b828689
feat: overhaul of syscall invocation to handle fork and other cases
Frank01001 Apr 2, 2025
59cdf89
Merge branch 'dev' into arbitrary-syscall
Frank01001 Apr 2, 2025
8662cb4
test: many fixes and intensive testing (WIP)
Frank01001 Apr 3, 2025
c6811f3
fix: fork is still broken, but the rest should be fixed
Frank01001 Apr 7, 2025
72e4733
feat: more progress on the mapping of syscall arguments
Frank01001 Apr 7, 2025
dec1efd
feat: more syscall arg define parsing
Frank01001 Apr 9, 2025
0edf8f0
feat: implemented syscall parsing from value map
Frank01001 Apr 10, 2025
9c3c9d1
Merge remote-tracking branch 'origin/dev' into arbitrary-syscall
Frank01001 Apr 11, 2025
f4ced83
fix: ugly fix used at def con, not for production purposes
io-no Apr 15, 2025
33130f8
feat: added checks for clone syscall
Frank01001 Apr 16, 2025
e29cf1d
test: test for clone arbitrary invocation
Frank01001 Apr 16, 2025
666d1b5
fix: apparently the NOPs can fix everything
Frank01001 Apr 16, 2025
a43ece5
feat: more progress on AMD64 syscall parsing
Frank01001 Apr 16, 2025
8bd8f0f
feat: more syscalls
Frank01001 Apr 17, 2025
7a313b5
fix: wrong map structure
Frank01001 Apr 17, 2025
17fbd0b
feat: add check for callback
Frank01001 Apr 17, 2025
2339fc7
test: add tests for callbacks
Frank01001 Apr 17, 2025
b6ee97b
Merge remote-tracking branch 'origin/dev' into arbitrary-syscall
Frank01001 Apr 17, 2025
f296c85
fix: removed obsolete attribute
Frank01001 Apr 17, 2025
d9fc72d
feat: even more syscall constant values
Frank01001 Apr 18, 2025
3dea501
feat: more syscall args
Frank01001 Apr 19, 2025
0f93b13
fix: all tests now run correctly, including invocation in callback an…
Frank01001 Apr 19, 2025
bf741c0
docs: documentation of arbitrary syscall invocation
Frank01001 Apr 19, 2025
cd164d4
docs: minor changes to uniform some icons for API
Frank01001 Apr 19, 2025
0b41925
fix: added initial check to ensure we are not inside another syscall
Frank01001 Apr 19, 2025
bdc2fdc
fix: implemented function in status handler to check if the process i…
Frank01001 Apr 19, 2025
f180731
test: removed old TODO and added test for the new check
Frank01001 Apr 19, 2025
04d2fa8
docs: documented the behavior of callbacks and pprints while invoking…
Frank01001 Apr 19, 2025
1ff7e08
chore: removed syscall arg parsing feature, which has been moved to a…
Frank01001 Apr 19, 2025
0640841
fix: realigned leftovers of postponed feature
Frank01001 Apr 19, 2025
7b63efc
fix: extended sorrounding NOPs to other archs
Frank01001 Apr 19, 2025
270d4c6
fix: removed another trace of syscall arg parsing
Frank01001 Apr 22, 2025
32205cd
test: generalized tests to other archs
Frank01001 Apr 22, 2025
8837b4b
test: fix other skill issues in test
Frank01001 Apr 22, 2025
fec836c
test: this will fix the shellcode test being single arch
Frank01001 Apr 22, 2025
362e509
test: i386 fixes
Frank01001 Apr 22, 2025
017f454
test: this might finally work
Frank01001 Apr 22, 2025
38d240b
fix: changed wrong register for aarch64
Frank01001 Apr 23, 2025
417915d
fix: fixed wrong handling of clone syscall
Frank01001 Apr 23, 2025
cec9255
test: more fixes for tests on other archs
Frank01001 Apr 23, 2025
b9728f5
test: fixed test for i386 and hopefully also for aarch64
Frank01001 Apr 24, 2025
6bb29a0
fix: misc fixes (incomplete)
Frank01001 Apr 26, 2025
0067f45
Merge remote-tracking branch 'origin/dev' into arbitrary-syscall
Frank01001 Apr 26, 2025
ee5b920
fix: prettier but not working
Frank01001 Apr 28, 2025
e405b78
Merge remote-tracking branch 'origin/dev' into arbitrary-syscall
Frank01001 May 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docs/assets/ace_dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/ace_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/logging/liblog.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ The `debugger` option displays all logs related to the debugging operations perf
### :material-pipe: Pipe Logging
The `pipe` option, on the other hand, displays all logs related to interactions with the process pipe: bytes received and bytes sent.

<img src="/assets/pipe_logging.jpeg" alt="pipe argv option" />
<img src="../../assets/pipe_logging.jpeg" alt="pipe argv option" />

### :material-vector-union: The best of both worlds
The `dbg` option is the combination of the `pipe` and `debugger` options. It displays all logs related to the debugging operations performed on the process by libdebug, as well as interactions with the process pipe: bytes received and bytes sent.
Expand Down
2 changes: 1 addition & 1 deletion docs/multithreading/multithreading.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ stateDiagram-v2
</div>
<div style="text-align: center; font-size: 0.85rem;">All live threads are synchronized in their execution state.</div>

## **libdebug** API for Multithreading
## :octicons-code-24: API for Multithreading
To access the threads of a process, you can use the `threads` attribute of the [Debugger](../../from_pydoc/generated/debugger/debugger/) object. This attribute will return a list of [ThreadContext](../../from_pydoc/generated/state/thread_context/) objects, each representing a thread of the process.

If you're already familiar with the [Debugger](../../from_pydoc/generated/debugger/debugger/) object, you'll find the [ThreadContext](../../from_pydoc/generated/state/thread_context/) straightforward to use. The [Debugger](../../from_pydoc/generated/debugger/debugger/) has always acted as a facade for the main thread, enabling you to access registers, memory, and other thread state fields exactly as you would for the main thread. The difference you will notice is that the [ThreadContext](../../from_pydoc/generated/state/thread_context/) object is missing a couple of fields that just don't make sense in the context of a single thread (e.g. symbols, which belong to the binary, and memory maps, since they are shared for the whole process).
Expand Down
76 changes: 76 additions & 0 deletions docs/quality_of_life/arbitrary_code_execution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
icon: fontawesome/solid/wand-magic-sparkles
search:
boost: 4
---
# :fontawesome-solid-wand-magic-sparkles: Arbitrary Code Execution
Debugging is primarily a way to follow the execution of a program and understand its behavior. Stepping, stopping, continuing, reading registers...these are all tools that only serve to help humans understand the running code at their own pace. However, a debugger's intervention in a program execution can be much more than that. The debugger can virtually do anything to the program it is debugging, including changing its behavior, modifying its memory, and even executing arbitrary code.

<div style="text-align: center;">
<img src="../../assets/ace_light.png#only-light" loading="lazy" width="720rem" />
<img src="../../assets/ace_dark.png#only-dark" loading="lazy" width="720rem" />
</div>


Since **libdebug** 0.9.0 NAME_TO_BE_DETERMINED, **libdebug** offers some primitives to perform arbitrary code execution inside a process. Specifically, it allows for the arbitrary invocation of *syscalls*. This can be useful for a variety of purposes, such as:

- Playing with file descriptors
- Allocating new memory maps
- Forking the process to create checkpoints (feature coming soon)
- Opening sockets to turn offline interactions into a service

All of this is essentially live-patching the running code to perform arbitrary actions. This is a powerful feature that can be used to perform a variety of tasks, but it should be used with caution.

!!! WARNING "Altering The Control Flow"
The control flow of a binary is a delicate thing. When executing arbitrary code, **libdebug** cannot guarantee that the process won't crash or behave unexpectedly. Remember, you are changing things under the hood without the program's knowledge.

## :fontawesome-solid-terminal: Syscall Invocation
Invoking a syscall is as simple as calling the [`invoke_syscall()`](../../from_pydoc/generated/state/thread_context/#libdebug.state.thread_context.ThreadContext.invoke_syscall) method of the [`ThreadContext`](../../from_pydoc/generated/state/thread_context/) class or the [`Debugger`](../../from_pydoc/generated/debugger/debugger/) object. In the latter case, the syscall will be invoked in the context of the main thread of the process.

The invoked syscall will be treated just like any other syscall. This means that **libdebug** will show it when [pretty printing](../pretty_printing/#syscall-trace-pretty-printing) the syscall trace and callbacks will be triggered as expected.

What exactly happens when you call this primitive?

<div align="center">
```mermaid
graph TD
InvokeNode("invoke_syscall()") --> A[Backup Registers];
A --> B(Patch Code);
B --> CD("Syscall (Enter + Exit)");
CD --> E[Restore Code];
E --> F[Restore Registers];
F --> J(Return);

%% Styling the InvokeNode
style InvokeNode stroke:# blue,stroke-width:2px;
style J stroke:# blue,stroke-width:2px;
```
</div>
Additionally, when the syscall is a [`fork`](https://man7.org/linux/man-pages/man2/fork.2.html), [`vfork`](https://man7.org/linux/man-pages/man2/vfork.2.html) or [`clone`](https://man7.org/linux/man-pages/man2/clone.2.html), the function will also restore the state in the child process / thread. This is done by copying the registers from the parent process / thread to the child process / thread.

As you can see, registers values are restored after the syscall is executed to reduce the chances of the process crashing. However, be mindful that the syscall is indeed executed. Thus, the state of the process will have changed. Specifically, changes in the memory contents as a result of the syscall execution will not be reverted.

### :octicons-code-24: Syscall Invocation API

The following is the API of the arbitrary syscall invocation:
invoke
!!! ABSTRACT "Function Signature"
```python
d.invoke_syscall(syscall_identifier: str | int, ...) -> int:
```

**Parameters**:

| Argument | Type | Description |
| --- | --- | --- |
| `syscall_identifier` | `str` \| `int` | The syscall name or number. |
| `...` | `int` | The arguments to be passed to the syscall. Not fixed, since the number of arguments depends on the syscall. |

**Returns**:

| Return | Type | Description |
| --- | --- | --- |
| `int` | `int` | The return value from the syscall invocation. |

!!! QUESTION "Where can I call this?"
You can invoke syscalls whenever the process is stopped. The invocation can happen both synchronously and asynchronously (in a callback). The only exception to that is when the process is stopped on another syscall's enter or inside a syscall handling callback.
3 changes: 3 additions & 0 deletions docs/quality_of_life/quality_of_life.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@ Visualizing the state of the process you are debugging can be a daunting task. *
### [:octicons-stack-24: Stack Frame Utils](../stack_frame_utils/)
**libdebug** offers utilities to resolve the return addresses of a process.

### [:fontawesome-solid-wand-magic-sparkles: Arbitrary Code Execution](../arbitrary_code_execution/)
**libdebug** offers a few functions that will help you execute arbitrary code in the context of the process you are debugging. Beware though, this features can significantly change the intended behavior of the process and may cause unexpected behaviors.

### [:material-run-fast: Evasion of Anti-Debugging](../anti_debugging/)
**libdebug** offers a few functions that will help you evade simple anti-debugging techniques. These functions can be used to bypass checks for the presence of a debugger.
2 changes: 1 addition & 1 deletion docs/stopping_events/breakpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Hardware breakpoints are a more reliable way to set breakpoints. They are made p
Hardware breakpoints have to be aligned to 4 bytes (which is the size of an ARM instruction).


## **libdebug** API for Breakpoints
## :octicons-code-24: API for Breakpoints

The `breakpoint()` function in the [Debugger](../../from_pydoc/generated/debugger/debugger/) object sets a breakpoint at a specific address.

Expand Down
2 changes: 1 addition & 1 deletion docs/stopping_events/debugging_flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ When a **synchronous** event is hit, the process will stop, awaiting further com

Internally, hijacks are considered callbacks, so you cannot have a callback and hijack registered for the same event.

## Common APIs of Stopping Events
## :octicons-code-24: Common APIs of Stopping Events
All **libdebug** stopping events share some common attributes that can be employed in debugging scripts.

### :material-power: Enable/Disable
Expand Down
2 changes: 1 addition & 1 deletion docs/stopping_events/signals.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Signal catchers can be created to register [stopping events](../stopping_events/
!!! INFO "Multiple catchers for the same signal"
Please note that there can be at most **one** user-defined catcher or hijack for each signal. If a new catcher is defined for a signal that is already caught or hijacked, the new catcher will replace the old one, and a warning will be printed.

## **libdebug** API for Signal Catching
## :octicons-code-24: API for Signal Catching
The `catch_signal()` function in the [Debugger](../../from_pydoc/generated/debugger/debugger/) object registers a catcher for the specified signal.

!!! ABSTRACT "Function Signature"
Expand Down
4 changes: 3 additions & 1 deletion docs/stopping_events/syscalls.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Syscall handlers can be created to register [stopping events](../stopping_events
!!! QUESTION "Do I have to handle both on enter and on exit?"
When using [asynchronous](../debugging_flow) syscall handlers, you can choose to handle both or only one of the two events. However, when using synchronous handlers, both events will stop the process.

## **libdebug** API for Syscall Handlers
## :octicons-code-24: API for Syscall Handlers
The `handle_syscall()` function in the [Debugger](../../from_pydoc/generated/debugger/debugger/) object registers a handler for the specified syscall.

!!! ABSTRACT "Function Signature"
Expand Down Expand Up @@ -230,3 +230,5 @@ For your convenience, you can also easily provide the syscall parameters to be u

Again, the secret will be leaked to the standard output.

## :fontawesome-solid-wand-magic-sparkles: Arbitrary Syscall Invocation
Did you know? Since **libdebug** 0.9.0 NAME_TO_BE_DETERMINED you can also invoke an arbitrary syscall on a thread of your choice. Read more on this feature in the page dedicated to [Arbitrary Code Execution](../../quality_of_life/arbitrary_code_execution#arbitrary-syscall-invocation).
2 changes: 1 addition & 1 deletion docs/stopping_events/watchpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Watchpoints are a special type of [hardware breakpoint](../breakpoints#hardware-

Features of watchpoints are shared with breakpoints, so you can set [asynchronous](../debugging_flow) watchpoints and use properties in the same way.

## **libdebug** API for Watchpoints
## :octicons-code-24: API for Watchpoints
The `watchpoint()` function in the [Debugger](../../from_pydoc/generated/debugger/debugger/) object sets a watchpoint at a specific address. While you can also use the [breakpoint API](../breakpoints/#libdebug-api-for-breakpoints) to set up a watchpoint, a specific API is provided for your convenience:

!!! ABSTRACT "Function Signature"
Expand Down
4 changes: 4 additions & 0 deletions libdebug/architectures/aarch64/aarch64_call_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ def get_call_and_skip_amount(self: Aarch64CallUtilities, opcode_window: bytes) -
"""Check if the current instruction is a call instruction and compute the instruction size."""
skip = self.compute_call_skip(opcode_window)
return skip != 0, skip

def get_syscall_instruction(self: CallUtilitiesManager) -> bytes:
"""Return the bytes of the syscall instruction."""
return b"\x1f\x20\x03\xd5\x01\x00\x00\xD4\x1f\x20\x03\xd5" # SVC #0 + NOPs
4 changes: 4 additions & 0 deletions libdebug/architectures/amd64/amd64_call_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,7 @@ def get_call_and_skip_amount(self, opcode_window: bytes) -> tuple[bool, int]:
"""Check if the current instruction is a call instruction and compute the instruction size."""
skip = self.compute_call_skip(opcode_window)
return skip != 0, skip

def get_syscall_instruction(self: CallUtilitiesManager) -> bytes:
"""Return the bytes of the syscall instruction."""
return b"\x90\x0F\x05\x90\x90" # syscall + NOPs
6 changes: 5 additions & 1 deletion libdebug/architectures/call_utilities_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,9 @@ def compute_call_skip(self: CallUtilitiesManager, opcode_window: bytes) -> int:
"""Compute the address where to skip after the current call instruction."""

@abstractmethod
def get_call_and_skip_amount(self, opcode_window: bytes) -> tuple[bool, int]:
def get_call_and_skip_amount(self: CallUtilitiesManager, opcode_window: bytes) -> tuple[bool, int]:
"""Check if the current instruction is a call instruction and compute the instruction size."""

@abstractmethod
def get_syscall_instruction(self: CallUtilitiesManager) -> bytes:
"""Return the bytes of the syscall instruction."""
4 changes: 4 additions & 0 deletions libdebug/architectures/i386/i386_call_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,7 @@ def compute_call_skip(self: I386CallUtilities, opcode_window: bytes) -> int:
def get_call_and_skip_amount(self: I386CallUtilities, opcode_window: bytes) -> tuple[bool, int]:
skip = self.compute_call_skip(opcode_window)
return skip != 0, skip

def get_syscall_instruction(self: CallUtilitiesManager) -> bytes:
"""Return the bytes of the syscall instruction."""
return b"\x90\xCD\x80\x90" # int 0x80 + NOPs
14 changes: 13 additions & 1 deletion libdebug/builtin/pretty_print_syscall_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@
from libdebug.state.thread_context import ThreadContext


def negate_value(value: int, word_size: int) -> int:
"""Negate a value.

Args:
value (int): the value.
word_size (int): the word size.

Returns:
int: the negated value.
"""
return (1 << word_size * 8) - value

def pprint_on_enter(t: ThreadContext, syscall_number: int, **kwargs: int) -> None:
"""Function that will be called when a syscall is entered in pretty print mode.

Expand Down Expand Up @@ -92,4 +104,4 @@ def pprint_on_exit(syscall_return: int | tuple[int, int]) -> None:
f"{ANSIColors.YELLOW}{ANSIColors.STRIKE}0x{syscall_return[0]:x}{ANSIColors.RESET} {ANSIColors.YELLOW}0x{syscall_return[1]:x}{ANSIColors.RESET}",
)
else:
print(f"{ANSIColors.YELLOW}0x{syscall_return:x}{ANSIColors.RESET}")
print(f"{ANSIColors.YELLOW}0x{syscall_return:x}{ANSIColors.RESET}")
64 changes: 64 additions & 0 deletions libdebug/data/ace_fixup_kit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#
# This file is part of libdebug Python library (https://github.com/libdebug/libdebug).
# Copyright (c) 2025 Francesco Panebianco. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for details.
#

from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from libdebug.debugger import Debugger
from libdebug.state.thread_context import ThreadContext


@dataclass(frozen=True)
class ACEFixupKit:
"""A class that provides a fixup kit for new processes and threads after arbitrary code execution."""

register_dump: dict[str, int] = None
"""The register dump."""

patch_address: int = 0
"""The address of the applied patch."""

code_backup: bytes = b""
"""The code backup."""

@staticmethod
def create_fixup_kit(
thread: ThreadContext,
patch_address: int,
code_backup: bytes,
) -> ACEFixupKit:
"""Create a fixup kit from the template of the given thread."""
kit = ACEFixupKit(
register_dump={},
patch_address=patch_address,
code_backup=code_backup,
)
# Fill the register dump with the current register values
for reg_name in dir(thread.regs):
if isinstance(getattr(thread.regs, reg_name), int | float) and reg_name != "_thread_id":
kit.register_dump[reg_name] = getattr(thread.regs, reg_name)

def fixup_thread(
self: ACEFixupKit,
thread: ThreadContext,
) -> None:
"""Fixup the new thread from the template."""
# Restore the registers
for reg_name, reg_value in self.register_dump.items():
setattr(thread.regs, reg_name, reg_value)

def fixup_process(
self: ACEFixupKit,
debugger: Debugger,
) -> None:
"""Fixup the new process from the template."""
self.fixup_thread(debugger.threads[0])

# Restore the code
debugger.memory[self.patch_address : self.patch_address + len(self.code_backup)] = self.code_backup
4 changes: 3 additions & 1 deletion libdebug/data/syscall_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class SyscallHandler:
on_exit_user (Callable[[ThreadContext, SyscallHandler], None]): The callback defined by the user to execute when the syscall is exited.
on_enter_pprint (Callable[[ThreadContext, int, Any], None]): The callback defined by the pretty print to execute when the syscall is entered.
on_exit_pprint (Callable[[int | tuple[int, int]], None]): The callback defined by the pretty print to execute when the syscall is exited.
on_enter_invoked (Callable[[ThreadContext, SyscallHandler], None]): The internal callback to execute when the syscall is arbitrarily invoked.
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.
enabled (bool): Whether the syscall will be handled or not.
hit_count (int): The number of times the syscall has been handled.
Expand All @@ -35,8 +36,9 @@ class SyscallHandler:
syscall_number: int
on_enter_user: Callable[[ThreadContext, SyscallHandler], None]
on_exit_user: Callable[[ThreadContext, SyscallHandler], None]
on_enter_pprint: Callable[[ThreadContext, int, Any], None]
on_enter_pprint: Callable[[int, Any], None]
on_exit_pprint: Callable[[int | tuple[int, int]], None]
on_enter_invoked: Callable[[ThreadContext, SyscallHandler], None]
recursive: bool = False
hit_count: int = 0

Expand Down
9 changes: 9 additions & 0 deletions libdebug/debugger/debugger.py
Original file line number Diff line number Diff line change
Expand Up @@ -983,3 +983,12 @@ def load_snapshot(self: Debugger, file_path: str) -> Snapshot:
file_path (str): The path to the snapshot file.
"""
return self._internal_debugger.load_snapshot(file_path)

def invoke_syscall(self: Debugger, syscall_identifier: str | int, *args: int) -> int:
"""Invokes a syscall with the specified arguments on the main thread.

Args:
syscall_identifier (str | int): The syscall identifier.
*args (int): The syscall arguments.
"""
return self.threads[0].invoke_syscall(syscall_identifier, *args)
Loading
Loading