Running an Executable
You have created your first debugger object, and now you want to run the executable. Calling the run()
method will spawn a new child process and prepare it for the execution of your binary.
A few things to keep in mind
- Please remember that the process you are debugging (the tracee) and the debugger itself are running in different threads.
- Also note that breakpoints and other stopping events set by the user are not kept between different runs of the program. If you want to place them again, you should redo so after each call to
d.run()
. You cannot set breakpoints before callingd.run()
.
Process I/O
When execution is resumed, chances are that your process will need to take input and produce output. To interact with the standard input and output of the process, you can use the PipeManager returned by the run()
function.
from libdebug import debugger
d = debugger("program")
pipe = d.run()
d.cont()
print(pipe.recvline().decode())
d.wait()
All pipe receive-like methods have a timeout parameter that you can set. The default value, timeout_default
, can be set globally as a parameter of the PipeManager object. By default, this value is set to 2 seconds.
Changing the global timeout
- This sets the default timeout for all pipe receive-like methods to 10 seconds.
You can interact with the process's pipe manager using the following methods:
Method | Description |
---|---|
recv |
Receives at most numb bytes from the target's stdout.Parameters: - numb (int) [default = 4096]- timeout (int) [default = timeout_default] |
recverr |
Receives at most numb bytes from the target's stderr.Parameters: - numb (int) [default = 4096]- timeout (int) [default = timeout_default] |
recvuntil |
Receives data from stdout until a specified delimiter is encountered for a certain number of occurrences. Parameters: - delims (bytes)- occurrences (int) [default = 1]- drop (bool) [default = False]- timeout (int) [default = timeout_default]- optional (bool) [default = False] |
recverruntil |
Receives data from stderr until a specified delimiter is encountered for a certain number of occurrences. Parameters: - delims (bytes)- occurrences (int) [default = 1]- drop (bool) [default = False]- timeout (int) [default = timeout_default]- optional (bool) [default = False] |
recvline |
Receives numlines lines from the target's stdout.Parameters: - numlines (int) [default = 1]- drop (bool) [default = True]- timeout (int) [default = timeout_default]- optional (bool) [default = False] |
recverrline |
Receives numlines lines from the target's stderr.Parameters: - numlines (int) [default = 1]- drop (bool) [default = True]- timeout (int) [default = timeout_default]- optional (bool) [default = False] |
send |
Sends data to the target's stdin.Parameters: - data (bytes) |
sendafter |
Sends data after receiving a specified number of occurrences of a delimiter from stdout.Parameters: - delims (bytes)- data (bytes)- occurrences (int) [default = 1]- drop (bool) [default = False]- timeout (int) [default = timeout_default]- optional (bool) [default = False] |
sendline |
Sends data followed by a newline to the target's stdin.Parameters: - data (bytes) |
sendlineafter |
Sends a line of data after receiving a specified number of occurrences of a delimiter from stdout.Parameters: - delims (bytes)- data (bytes)- occurrences (int) [default = 1]- drop (bool) [default = False]- timeout (int) [default = timeout_default]- optional (bool) [default = False] |
close |
Closes the connection to the target. |
interactive |
Enters interactive mode, allowing manual send/receive operations with the target. Read more in the dedicated section. Parameters: - prompt (str) [default = "$ "]- auto_quit (bool) [default = False] |
When process is stopped
When the process is stopped, the PipeManager will not be able to receive new (unbuffered) data from the target. For this reason, the API includes a parameter called optional
.
When set to True
, libdebug will not necessarily expect to receive data from the process when it is stopped. When set to False
, any recv-like instruction (including sendafter
and sendlineafter
) will fail with an exception when the process is not running.
Operations on stdin like send
and sendline
are not affected by this limitation, since the kernel will buffer the data until the process is resumed.
Interactive I/O
The PipeManager contains a method called interactive()
that allows you to directly interact with the process's standard I/O. This method will print characters from standard output and error and read your inputs, letting you interact naturally with the process. The interactive()
method is blocking, so the execution of the script will wait for the user to terminate the interactive session. To quit an interactive session, you can press Ctrl+C
or Ctrl+D
.
The prompt
parameter sets the line prefix in the terminal (e.g. "$ "
and "> "
will produce $ cat flag
and > cat flag
respectively). By default, it is set to "$ "
. The auto_quit
parameter, when set to True
, will automatically quit the interactive session when the process is stopped.
If any of the file descriptors of standard input, output, or error are closed, a warning will be printed.
Attaching to a Running Process
If you want to attach to a running process instead of spawning a child, you can use the attach()
method in the Debugger object. This method will attach to the process with the specified PID.
The process will stop upon attachment, waiting for your commands.
Ptrace Scope
libdebug uses the ptrace
system call to interact with the process. For security reasons, this system call is limited by the kernel according to a ptrace_scope
parameter. Different systems have different default values for this parameter. If the ptrace
system call is not allowed, the attach()
method will raise an exception notifying you of this issue.