libdebug é uma biblioteca Python de código aberto para automatizar a depuração de um executável binário.
Com libdebug você tem controle total do fluxo do seu executável depurado. Com ele você pode:
Ao executar o mesmo executável várias vezes, a escolha de implementações eficientes pode fazer a diferença. Por esse motivo, a libdebug prioriza o desempenho.
Página inicial: https://libdebug.org
Documentação: 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
Arco Linux:
sudo pacman -S python libelf libdwarf gcc make debuginfod
Fedor:
sudo dnf install -y python3 python3-devel kernel-devel binutils-devel libdwarf-devel
python3 -m pip install libdebug
PyPy3 é suportado, mas não recomendado, pois apresenta pior desempenho na maioria dos nossos testes.
Se você quiser se manter atualizado com os recursos mais avançados (e não se importa em estar em um branch instável), você pode instalar a partir de um branch diferente (por exemplo, dev).
python3 -m pip install git+https://github.com/libdebug/libdebug.git@dev
Agora que você instalou o libdebug, pode começar a usá-lo em seus scripts. Aqui está um exemplo simples de como usar libdebug para depurar um binário:
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 ()
O script acima irá executar o test
binário no diretório de trabalho e parar na função correspondente ao símbolo "função". Em seguida, ele imprimirá o valor do registro RAX e encerrará o processo.
Há muito mais que pode ser feito com libdebug. Por favor, leia a documentação para saber mais.
libdebug oferece muitos recursos avançados. Dê uma olhada neste script fazendo mágica com sinais:
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 também permite que você execute todos os comandos o mais rápido possível, sem ter que esperar por um evento de parada. Para ativar este modo, você pode usar o 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 ()
Se você pretende usar libdebug em seu trabalho, cite este repositório usando o seguinte biblatex:
@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},
}