Size: a a a

DCG#7812 DEFCON-RUSSIA

2019 October 15

r

rzrzrz in DCG#7812 DEFCON-RUSSIA
slinkin
Неа...
Причем даже на обычных бинарниках не срабатывает. Видимо я что-то базовое упускаю.
Например
ltrace -e strcpy wget --help >/dev/null
+++ exited (status 0) +++
В бинаре в обще есть символы?
источник

r

rzrzrz in DCG#7812 DEFCON-RUSSIA
Бинарь в обще использует либы?
cat /proc/pid/mem(или как-то так)
источник

a

alxchk in DCG#7812 DEFCON-RUSSIA
продуктивнее код ltrace позырить. у меня он никогда толком не работал
источник

🧐

🧐 in DCG#7812 DEFCON-RUSSIA
rzrzrz
Бинарь в обще использует либы?
cat /proc/pid/mem(или как-то так)
cat /proc/pid/maps
источник

s

slinkin in DCG#7812 DEFCON-RUSSIA
rzrzrz
В бинаре в обще есть символы?
Да.
objdump -T /usr/bin/wget
Множество символов из GLIBC_2.2.5
источник

r

rzrzrz in DCG#7812 DEFCON-RUSSIA
slinkin
Всё что вы описали - верно.
-fno-plt для ltrace и без PLT для python.

Можете пояснить следующие моменты:
За что отвечает флаг PLT?
Если скомпилировать пакет с этим флагом, это даст результат на трассировку?
Есть-ли способ добиться трассировки библиотек подключенных через libdl?
Ещё пишут что на бинврях собранных с флагом PIE, ltrace не будет работать
источник

s

slinkin in DCG#7812 DEFCON-RUSSIA
rzrzrz
Ещё пишут что на бинврях собранных с флагом PIE, ltrace не будет работать
Где написано? И wget и python являются PIE
источник

r

rzrzrz in DCG#7812 DEFCON-RUSSIA
Я нагуглил
источник
2019 October 16

s

slinkin in DCG#7812 DEFCON-RUSSIA
Может кому-то будет интересно:

Если я всё правильно понял, основная проблема в том, что ltrace во время трассировки использует PLT (Procedure Linkage Table). Все программы, которые ставятся посредством пакетного менеджера в arch собираются с конфигами из
/etc/makepkg.conf
, как верно заметил @dura_lex. (Это касается только пакетов, которые именно собираются на тачке). По дефолту в
makepkg.conf
установлен флаг
-fno-plt
, т.е. все приложения установливаемые в систему будут без PLT. Как итог, ltrace не будет выполнять трассировку.

Провёл тест - убрал флаг из
makepkg.conf
, установил aircrack-ng-git через yay и запустил его через ltrace - получил трассировку.

Спасибо всем за помощь!
источник

S

Slava in DCG#7812 DEFCON-RUSSIA
источник

AX

Abc Xyz in DCG#7812 DEFCON-RUSSIA
источник

AX

Abc Xyz in DCG#7812 DEFCON-RUSSIA
> За что отвечает флаг PLT?

За включение таблиц PLT (Procedure Linkage Table) в ELF файл. Если начинать с самых азов, то существуют shared library, которые сейчас распространены повсеместно (хотя местами static library снова становятся популярными). Эти самые shared library должны быть универсальными, работать для каждой программы одинаково хорошо и правильно, поэтому есть понятие PIC (position-independent code), а также PIE (position-independent executable) — исполняемые файлы как раз таки состоящие из PIC. PIC нужен для того, чтобы ELF файл (точнее часть его данных из секций) загружался по любому желаемому адресу, ведь если адрес у всех библиотек будет фиксирован, то рано или поздно возникнет конфликт адресов. Также полезно read-only библиотеку расшарить между несколькими процессами, чтобы в памяти не было 100500 копий одной и той же, без PIC это также было бы затруднительно. Таким образом, можно взять каждую из shared library и загрузить по произвольному адресу, этот процесс называется prelinking. В ходе prelinking процесса происходит парсинг ELF файла, чтение GOT (Global Offset Table), которая содержит адреса глобальных переменных, которые потом вкупе с таблицей релокаций, например, .rela.dyn, дают реальный адрес переменной (характер и способ «выдачи» адреса регулируется типами релокаций). Это про данные в PIC, что же касается функций, то их адреса обычно вычисляются как раз таки с помощью PLT. PLT обычно содержит функции-трамплины, которые уже вызывают необходимую функцию. Я, конечно, могу ошибаться, но вкратце, без нюансов, вроде, всё так работает.

-fno-pltman 1 gcc: не использовать PLT для PIC, вместо трамплинов и прочего загружать адреса вызываемых функций сразу же на места их непосредственного вызова, это позволяет проводить оптимизации и бла-бла-бла, можете почитать сами.

По всему этому советуют читать цикл статей от разработчика GOLD линкера, я не читал, годно или нет — не знаю.

Теперь что касается ltrace. Его функционал не особо мудрёный: он использует ptrace (man 2 ptrace) для модификации памяти целевых процессов, считывает PLT, перезаписывает трамплины бряками (int $3), запускает, ждёт SIGTRAP, подменяет инструкции и т. д. К сожалению, по GOT ltrace не работает, хотя, наверное, мог бы.

> Если скомпилировать пакет с этим флагом, это даст результат на трассировку?

Скорее без этого флага. Да, трассировка заработает, но до тех пор, пока на пути не появится очередной бинарный файл с -fnoplt, например, флаг -f позволяет трассировать дочерние процессы (правда, я не представляю, как это всё будет работать в многопоточном режиме, неудобно должно быть).

В случае с Python (конечная цель) я бы посоветовал сделать так:

1. написать кастомный makepkg.conf,
2. установить либо python36, либо младше, либо python-git с нужной версией.

1. cat /etc/makepkg.conf | grep "^CFLAGS" | sed 's/ -fno-plt//' > ~/.makepkg.conf, CFLAGS, думаю, будет достаточно;
2. склонить нужный PKGBUILD с AUR, либо воспользоваться AUR helpers.

Всё должно работать.

Я заметил возможность трейсить уже запущенные процессы,  поэтому, стоит сказать, что по умолчанию в Manjaro, наверное, запрещён ptrace из-за Yama Linux Security Module. Как отключить-включить см. /proc/sys/kernel/yama/ptrace_scope в man 2 ptrace.

> Есть-ли способ добиться трассировки библиотек подключенных через libdl?

Думаю, что есть несколько.

1. Тем же ltrace или чем угодно ещё обнаружить все вызовы dlsym, dlopen и подобных, написать для данных функций обёртки с нужным кодом, по окончанию работы которого передавать управление на нужную функцию, передать путь к кастомной библиотеке при вызове целевого файла (LD_PRELOAD и иже с ними).

2. GDB. Можно заскриптовать всё те же функции.
источник

AX

Abc Xyz in DCG#7812 DEFCON-RUSSIA
3. Модифицировать ltrace, таким образом чтобы он при обнаружении новой загруженной библиотеки распространял свои бряки дальше. Отследить и выявить можно опять же по вышеназванным функциям, дополнительную информацию можно получить из структуры r_debug. Я нашёл исходники уже сделанного чего-то подобного. Я запушил на AUR версию `ltrace` с официального гита, она, вроде, собирается и работает, так что можно без проблем накатить модифицированную версию в PKBUILD, но, честно говоря, я не уверен, что оно будет работать, логика run-time линкеров за эти годы таки поменялась. К слову, версия с гита хоть и давно не обновлялась, но всё равно гораздо новее той, что в некоторых репозиториях.

4. Есть утилита latrace, логика работы которой базируется на LD_AUDIT. Всё бы хорошо, но она не работает на современном glibc, нужно допиливать, т. к. поддержка давно прекращена. Жаль, хороший потенциал был.

5. Использовать perf_events из ядра. Да, может показывать не всё, терять данные т. к. это всё-таки профилировщик, но, как по мне, для «просто посмотреть» вариант идеальный, использую его для всего. Настроек и фильтров куча и маленькая тележка. В нашем случае будет достаточно:

# perf trace -F min --call-graph=dwarf -- python -c "print('HW')"

В том числе будет показывать и dlsym, dlopen, и даже имена (если будут) вызываемых функций таким методом.
источник

AG

Andrew Grigorev in DCG#7812 DEFCON-RUSSIA
Раз уж такая пьянка... А eBPF uretprobe нельзя глобально повесить и фильтровать по pid'у? Я не настоящий сварщик, а в примерах bpftrace и bcc оно только на конкретные символы вешается (можно по wildcard, но суть не меняется). Для получения такого полного трейса видимо не получится eBPF использовать?..
источник

s

slinkin in DCG#7812 DEFCON-RUSSIA
Abc Xyz
3. Модифицировать ltrace, таким образом чтобы он при обнаружении новой загруженной библиотеки распространял свои бряки дальше. Отследить и выявить можно опять же по вышеназванным функциям, дополнительную информацию можно получить из структуры r_debug. Я нашёл исходники уже сделанного чего-то подобного. Я запушил на AUR версию `ltrace` с официального гита, она, вроде, собирается и работает, так что можно без проблем накатить модифицированную версию в PKBUILD, но, честно говоря, я не уверен, что оно будет работать, логика run-time линкеров за эти годы таки поменялась. К слову, версия с гита хоть и давно не обновлялась, но всё равно гораздо новее той, что в некоторых репозиториях.

4. Есть утилита latrace, логика работы которой базируется на LD_AUDIT. Всё бы хорошо, но она не работает на современном glibc, нужно допиливать, т. к. поддержка давно прекращена. Жаль, хороший потенциал был.

5. Использовать perf_events из ядра. Да, может показывать не всё, терять данные т. к. это всё-таки профилировщик, но, как по мне, для «просто посмотреть» вариант идеальный, использую его для всего. Настроек и фильтров куча и маленькая тележка. В нашем случае будет достаточно:

# perf trace -F min --call-graph=dwarf -- python -c "print('HW')"

В том числе будет показывать и dlsym, dlopen, и даже имена (если будут) вызываемых функций таким методом.
@dura_lex
Премного благодарен за развернутое объяснение!
С latrace-м - да, буквально вчера поставил его как альтернативу и столкнулся несовместимостью текущего glibc.
По PLT - так всё и понял=)
Думаю, если немного работы, постараюсь разобраться с latrace-ом, в противном случае соберу отдельно python.
источник

AX

Abc Xyz in DCG#7812 DEFCON-RUSSIA
Andrew Grigorev
Раз уж такая пьянка... А eBPF uretprobe нельзя глобально повесить и фильтровать по pid'у? Я не настоящий сварщик, а в примерах bpftrace и bcc оно только на конкретные символы вешается (можно по wildcard, но суть не меняется). Для получения такого полного трейса видимо не получится eBPF использовать?..
Да, вполне себе можно, это уже next level как по мне, т. к. там можно написать трассировщик чего угодно и как угодно, но ключевое слово — написать. Если использовать wildcard и подключать probes для всего, то в итоге можно ничего и не дождаться, поэтому нужно тонко настраивать.

Вот, например, а-ля ltrace на rust от Джулии Иванс, к сожалению сейчас не работает, сигфолтится, но код наипростейший, можно самому написать/исправить.
Вот прекрасная статья на эту же тему. А вот презентация от красношляпников, где они расписывают все возможные методы трассировки.
источник

JG

JeisonWi Garrison in DCG#7812 DEFCON-RUSSIA
Abc Xyz
Да, вполне себе можно, это уже next level как по мне, т. к. там можно написать трассировщик чего угодно и как угодно, но ключевое слово — написать. Если использовать wildcard и подключать probes для всего, то в итоге можно ничего и не дождаться, поэтому нужно тонко настраивать.

Вот, например, а-ля ltrace на rust от Джулии Иванс, к сожалению сейчас не работает, сигфолтится, но код наипростейший, можно самому написать/исправить.
Вот прекрасная статья на эту же тему. А вот презентация от красношляпников, где они расписывают все возможные методы трассировки.
Next gen же!
источник

AX

Abc Xyz in DCG#7812 DEFCON-RUSSIA
slinkin
@dura_lex
Премного благодарен за развернутое объяснение!
С latrace-м - да, буквально вчера поставил его как альтернативу и столкнулся несовместимостью текущего glibc.
По PLT - так всё и понял=)
Думаю, если немного работы, постараюсь разобраться с latrace-ом, в противном случае соберу отдельно python.
Ещё на Rust есть тула для профайлинга python программ, тоже использует ptrace на сколько я знаю, но уже получше, чем ltrace, правда, вывод не очень подходит для задачи, но можно же самому подогнать, как надо. Ещё советую частоту увеличить, чтобы ничего не пропускал. Например так:
py-spy record -r 1000000 -n -f raw -o request.txt -- python3 -c 'import requests; requests.get("https://www.python.org")'
источник

AX

Abc Xyz in DCG#7812 DEFCON-RUSSIA
JeisonWi Garrison
Next gen же!
Ну да, ну да.
источник

S

Slava in DCG#7812 DEFCON-RUSSIA
А вот и на ведро
источник