libdebug — это библиотека Python с открытым исходным кодом, предназначенная для автоматизации отладки двоичного исполняемого файла.
С помощью libdebug вы получаете полный контроль над потоком отлаживаемого исполняемого файла. С его помощью вы можете:
При многократном запуске одного и того же исполняемого файла выбор эффективных реализаций может иметь решающее значение. По этой причине libdebug отдает приоритет производительности.
Домашняя страница: https://libdebug.org.
Документация: https://docs.libdebug.org.
Убунту:
sudo apt install -y python3 python3-dev libdwarf-dev libelf-dev libiberty-dev linux-headers-generic libc6-dbg
Дебиан:
sudo apt install -y python3 python3-dev libdwarf-dev libelf-dev libiberty-dev linux-headers-generic libc6-dbg
Арч Линукс:
sudo pacman -S python libelf libdwarf gcc make debuginfod
Федора:
sudo dnf install -y python3 python3-devel kernel-devel binutils-devel libdwarf-devel
python3 -m pip install libdebug
PyPy3 поддерживается, но не рекомендуется, поскольку в большинстве наших тестов он работает хуже.
Если вы хотите быть в курсе самых передовых функций (и вы не против использовать нестабильную ветку), вы можете установить их из другой ветки (например, dev).
python3 -m pip install git+https://github.com/libdebug/libdebug.git@dev
Теперь, когда у вас установлена библиотека libdebug, вы можете начать использовать ее в своих сценариях. Вот простой пример использования libdebug для отладки двоичного файла:
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 ()
Приведенный выше скрипт запустит двоичный test
в рабочем каталоге и остановится на функции, соответствующей символу «функция». Затем он напечатает значение регистра RAX и завершит процесс.
С помощью libdebug можно сделать гораздо больше. Пожалуйста, прочитайте документацию, чтобы узнать больше.
libdebug предлагает множество расширенных функций. Взгляните на этот скрипт, творящий чудеса с сигналами:
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 также позволяет выполнять все команды как можно скорее, не дожидаясь события остановки. Чтобы включить этот режим, вы можете использовать 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 ()
Если вы собираетесь использовать libdebug в своей работе, укажите этот репозиторий, используя следующий библтекс:
@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},
}