libdebug est une bibliothèque Python open source pour automatiser le débogage d'un exécutable binaire.
Avec libdebug, vous avez un contrôle total sur le flux de votre exécutable débogué. Avec lui, vous pouvez :
Lorsque vous exécutez plusieurs fois le même exécutable, le choix d’implémentations efficaces peut faire la différence. Pour cette raison, libdebug donne la priorité aux performances.
Page d'accueil : https://libdebug.org
Documentation : https://docs.libdebug.org
Ubuntu :
sudo apt install -y python3 python3-dev libdwarf-dev libelf-dev libiberty-dev linux-headers-generic libc6-dbg
Debian :
sudo apt install -y python3 python3-dev libdwarf-dev libelf-dev libiberty-dev linux-headers-generic libc6-dbg
Arch Linux :
sudo pacman -S python libelf libdwarf gcc make debuginfod
Feutre:
sudo dnf install -y python3 python3-devel kernel-devel binutils-devel libdwarf-devel
python3 -m pip install libdebug
PyPy3 est pris en charge mais n'est pas recommandé, car ses performances sont moins bonnes dans la plupart de nos tests.
Si vous souhaitez rester à jour avec les fonctionnalités les plus avancées (et que cela ne vous dérange pas d'être sur une branche instable), vous pouvez installer à partir d'une branche différente (par exemple, dev).
python3 -m pip install git+https://github.com/libdebug/libdebug.git@dev
Maintenant que libdebug est installé, vous pouvez commencer à l'utiliser dans vos scripts. Voici un exemple simple de la façon d'utiliser libdebug pour déboguer un binaire :
from libdebug import debugger
d = debugger ( "./test" )
# Start debugging from the entry point
d . run ()
my_breakpoint = d . breakpoint ( "function" )
# Continue the execution until the breakpoint is hit
d . cont ()
# Print RAX
print ( f"RAX is { hex ( d . regs . rax ) } " )
# Write to memory
d . memory [ 0x10ad , 8 , "binary" ] = b"Hello! x00 x00 "
# Continue the execution
d . cont ()
Le script ci-dessus exécutera le test
binaire dans le répertoire de travail et s'arrêtera à la fonction correspondant au symbole « fonction ». Il imprimera ensuite la valeur du registre RAX et tuera le processus.
Il y a tellement plus à faire avec libdebug. Veuillez lire la documentation pour en savoir plus.
libdebug offre de nombreuses fonctionnalités avancées. Jetez un œil à ce script faisant de la magie avec des signaux :
from libdebug import debugger , libcontext
libcontext . terminal = [ 'tmux' , 'splitw' , '-h' ]
# Define signal catchers
def catcher_SIGUSR1 ( t : ThreadContext , catcher : SignalCatcher ) -> None :
t . signal = 0x0
print ( f"SIGUSR1: Signal number { catcher } " )
def catcher_SIGINT ( t : ThreadContext , catcher : SignalCatcher ) -> None :
print ( f"SIGINT: Signal number { catcher } " )
def catcher_SIGPIPE ( t : ThreadContext , catcher : SignalCatcher ) -> None :
print ( f"SIGPIPE: Signal number { catcher } " )
def handler_geteuid ( t : ThreadContext , handler : SyscallHandler ) -> None :
t . regs . rax = 0x0
# Initialize the debugger
d = debugger ( '/path/to/executable' , continue_to_binary_entrypoint = False , aslr = False )
# Register signal catchers
catcher1 = d . catch_signal ( "SIGUSR1" , callback = catcher_SIGUSR1 )
catcher2 = d . catch_signal ( "SIGINT" , callback = catcher_SIGINT )
catcher3 = d . catch_signal ( "SIGPIPE" , callback = catcher_SIGPIPE )
# Register signal hijackings
d . hijack_signal ( "SIGQUIT" , "SIGTERM" )
d . hijack_signal ( "SIGINT" , "SIGPIPE" , recursive = True )
# Define which signals to block
d . signals_to_block = [ "SIGPOLL" , "SIGIO" , "SIGALRM" ]
d . handle_syscall ( "geteuid" , on_exit = handler_geteuid )
# Continue execution
d . cont ()
# Disable the catchers after execution
catcher1 . disable ()
catcher2 . disable ()
catcher3 . disable ()
bp = d . breakpoint ( 0xdeadc0de , hardware = True )
d . cont ()
d . wait ()
d . gdb ()
libdebug vous permet également d'exécuter toutes les commandes dès que possible, sans avoir à attendre un événement d'arrêt. Pour activer ce mode, vous pouvez utiliser le auto_interrupt_on_command=True
from libdebug import debugger
d = debugger ( "/path/to/executable" , auto_interrupt_on_command = True )
pipes = d . run ()
bp = d . breakpoint ( "function" )
d . cont ()
# Read shortly after the cont is issued
# The process is forcibly stopped to read the register
value = d . regs . rax
print ( f"RAX is { hex ( value ) } " )
system_offset = d . symbols . filter ( "system" )[ 0 ]. start
libc_base = d . maps . filter ( "libc" )[ 0 ]. base
system_address = libc_base + system_offset
d . memory [ 0x12ebe , 8 , "libc" ] = int . to_bytes ( system_address , 8 , "little" )
d . cont ()
d . wait ()
# Here we should be at the breakpoint
# This value is read while the process is stopped at the breakpoint
ip_value = d . regs . rip
print ( f"RIP is { hex ( ip_value ) } " )
d . kill ()
Si vous avez l'intention d'utiliser libdebug dans votre travail, veuillez citer ce référentiel en utilisant le biblatex suivant :
@software{libdebug_2024,
title = {libdebug: {Build} {Your} {Own} {Debugger}},
copyright = {MIT Licence},
url = {https://libdebug.org},
publisher = {libdebug.org},
author = {Digregorio, Gabriele and Bertolini, Roberto Alessandro and Panebianco, Francesco and Polino, Mario},
year = {2024},
doi = {10.5281/zenodo.13151549},
}