Как лучше понять и реверс-инжиниринг системных вызовов внутри процессов на конкретном примере

Вопрос или проблема

Я совсем новичок в Linux и поэтому буду признателен за любые советы по пониманию системных вызовов и возможности, знания и инструменты для обратной разработки их происхождения или их процесса выполнения.

Как следует из названия, я представляю пример, являющийся моим анализом процесса Xorg, который я отслеживал в моем окружении рабочего стола Linux. Таким образом, я пытаюсь понять процесс выполнения вызовов DRM_IOCTL, в данном случае специфического системного вызова DRM_IOCTL_CURSOR2, который происходит в этом процессе. Моя цель – понять, что вызывает этот вызов в этом окружении рабочего стола, или скорее, какие шаги я могу предпринять в общем, чтобы исследовать подобные вопросы.

Из моего ограниченного понимания я знаю, что Xorg запускается как подпроцесс SDDM, но помимо инициации сервера Xorg, у меня нет представления о том, как пройти через процесс и определить триггеры для некоторых процессных вызовов или, возможно, использовать инструменты для этого. Таким образом, это концептуальный вопрос о том, как подойти к анализу таких вопросов в общем. Потребуются ли здесь какие-то специфические знания о текущем процессе и его архитектуре. Могу ли я воспользоваться какими-либо общими подходами на моей системе для отслеживания системных вызовов, подобно выявлению ppids процессов для моего собственного интереса.

На данный момент я слабо знаком с использованием таких инструментов, как strace, bpftrace и общие командные инструменты, такие как ps и lsof. Извиняюсь, если это общий вопрос, если так, я буду рад сузить его еще больше.

Да, это звучит хорошо: bpftrace, чтобы определить, как часто делается системный вызов и кем, а также strace, чтобы выяснить, какие системные вызовы делает конкретный процесс. Значительная часть достигнута!

Следующая часть – это выяснить, какие функции в вашем процессе выполняют системные вызовы – и единственный способ сделать это – подключиться к отладчику.

По умолчанию отладчик в Linux – это gdb. Давайте проведем вас через это.

Предварительное условие (пока): Наличие отладочных символов для вашего процесса. Это легко, если вы что-то создали сами и включены отладочные символы (но тогда, что же вы декомпилируете); это легко, если вы можете использовать возможности вашей системы для загрузки для вас отладочной информации. Мне нравится, что Fedora (и другие основные дистрибутивы Linux, такие как Debian или Ubuntu) предлагают символы debuginfod, это делает данный процесс значительно проще.

  1. Установите gdb.
  2. Установите переменную среды, указывающую gdb, где найти данные debuginfod (необязательно, но делает вещи намного читабельнее для начала; отрегулируйте URL для вашего дистрибутива):
    export DEBUGINFOD_URLS=https://ubuntu.com/server/docs/about-debuginfod
    
  3. найдите ID процесса (pid) процесса, с которым вы хотите работать (например, с помощью bpftrace)
  4. подключите gdb к этому PID: gdb --pid the_pid_you_care_about. На этом этапе, возможно, потребуется загрузить много отладочной информации.
  5. скажите gdb поймать все системные вызовы, которые являются ioctl, а затем продолжите выполнение:
    (gdb) catch syscall ioctl
    (gdb) continue
    
  6. вас вернут к интерфейсу gdb каждый раз, когда вызывается ioctl; в этот момент вы можете сделать, например, backtrace, чтобы увидеть стек вызовов, включая имена функций, и print переменные из текущего кадра стека (если они не были оптимизированы компилятором)
  7. вы можете detach от процесса или quit (и убить его)

В любом случае, то, как работает пункт 5, немного неудобно: вы на самом деле не хотите останавливаться на каждом ioctl, только на ioctl с конкретными аргументами, и вы не очень хотите останавливаться, вы хотите сделать обратный трассировку и записать это в файл, возможно. Так что, вместо 4-го пункта вы могли бы сделать:

(gdb) set logging on
(gdb) set logging file ~/mylog.txt
(gdb) catch syscall ioctl
(gdb) commands
backtrace
continue
end
(gdb) continue

Конечно, ситуация, когда у вас есть отладочные символы, очень роскошна, но для начала давайте не усложнять себе жизнь больше, чем необходимо ^^.

Если вы столкнетесь с программой, для которой вы не можете получить отладочные символы, вы, вероятно, будете использовать сочетание живой отладки, дизассемблирования, пошагового выполнения и присвоения имен адресам памяти, например, в достаточно мощном инструменте ghidra.

.

Ответ или решение

Конечно, для глубокого понимания и обратного проектирования системных вызовов, например, в окружении Linux, необходим подход, сочетающий использование инструментов, таких как strace, bpftrace, gdb, и понимание архитектуры процесса. Рассмотрим шаги, которые помогут вам разобраться с проблемой изучения системных вызовов, на примере аналитики DRM_IOCTL_CURSOR2 вызова в процессе Xorg вашего десктопного окружения.

Шаги для Анализа

1. Подготовка рабочей среды
Убедитесь, что у вас установлены все необходимые инструменты для анализа: gdb, bpftrace, strace. Эти инструменты предоставят возможность отслеживать и анализировать системные вызовы.

2. Определение идентификатора процесса
Используйте командные инструменты, такие как ps или bpftrace, чтобы определить PID процесса Xorg. Это можно сделать, выполнив команду ps aux | grep Xorg.

3. Работа с strace
Запустите strace, чтобы получить информацию о системных вызовах, выполняемых процессом. Например:

strace -p [PID]

Так вы сможете увидеть, когда вызывается DRM_IOCTL_CURSOR2, и какие параметры ему передаются.

4. Использование bpftrace для анализа паттернов
При помощи bpftrace вы можете наблюдать за частотой вызова конкретных системных вызовов и их источниками:

bpftrace -e 'tracepoint:syscalls:sys_enter_ioctl /pid == [PID]/ { @[comm] = count(); }'

Это позволит вам увидеть, какие команды чаще всего инициируют вызов ioctl.

5. Глубокий анализ с gdb
Теперь, для детального исследования, используйте gdb:

  • Подключитесь к процессу:

    gdb --pid [PID]
  • Установите отлов вызова ioctl:

    (gdb) catch syscall ioctl
  • Продолжите выполнение программы:

    (gdb) continue

    Каждый раз при срабатывании ioctl вы будете возвращаться в интерфейс gdb, где сможете использовать команду backtrace, чтобы увидеть стек вызовов.

  • Настройте логирование, чтобы автоматизировать процесс:

    (gdb) set logging on
    (gdb) set logging file ~/mylog.txt
    (gdb) catch syscall ioctl
    (gdb) commands
    (gdb) backtrace
    (gdb) continue
    (gdb) end
    (gdb) continue

Альтернативные подходы

Если отладочные символы недоступны, вам может понадобиться погрузиться в дизассемблирование и отладку с использованием инструментов, таких как ghidra. Это усложнит задачу, так как придётся вручную исследовать машинный код и проводить обратное проектирование для определения логики.

Заключение

Процесс понимания и обратного проектирования системных вызовов требует сочетания нескольких техник и инструментов. На первом этапе можно использовать высокоуровневые утилиты для получения общего представления, и затем перейти к более низкоуровневым инструментам для детального анализа. Это комплексный подход позволит вам глубже понять взаимодействие процессов и системных вызовов в среде Linux.

Такой детальный анализ поможет вам, как специалисту, точно определить триггеры вызовов и построить понимание внутренней логики работы процесса, особенно в контексте таких сложных экосистем, как десктопные окружения на базе Linux.

Оцените материал
Добавить комментарий

Капча загружается...