Как я могу заставить dtrace запускать трассированную команду с привилегиями не-root?
OS X не хватает Linux strace
, но это имеет dtrace
что должно быть намного лучше.
Однако мне не хватает возможности выполнять простую трассировку по отдельным командам. Например, на Linux я могу написать strace -f gcc hello.c
чтобы закрыть все системные вызовы, что дает мне список всех имен файлов, необходимых компилятору для компиляции моей программы (на этом трюке построен отличный скрипт memoize)
Я хочу портировать памятку на Mac, поэтому мне нужно какое-то strace
, Что мне действительно нужно, так это список файлов gcc
читает и пишет, так что мне нужно больше truss
, Конечно же, могу ли я сказать, dtruss -f gcc hello.c
и получить примерно те же функциональные возможности, но затем компилятор запускается с привилегиями root, что, очевидно, нежелательно (кроме огромного риска для безопасности, одна проблема заключается в том, что a.out
файл теперь принадлежит пользователю root:-)
Я тогда попробовал dtruss -f sudo -u myusername gcc hello.c
, но это чувствует себя немного неправильно, и не работает в любом случае (я не получаю a.out
файл на все это время не уверен почему)
Вся эта длинная история пытается мотивировать мой оригинальный вопрос: как я могу получить dtrace
запустить мою команду с правами обычного пользователя, так же, как strace
делает в Linux?
Изменить: кажется, что я не единственный, кто задается вопросом, как это сделать: вопрос #1204256 почти такой же, как мой (и имеет тот же субоптимальный ответ sudo:-)
9 ответов
Не ответ на ваш вопрос, но что-то знать. OpenSolaris решил эту проблему (частично) с "привилегиями" - см. Эту страницу. Даже в OpenSolaris было бы невозможно позволить пользователю без каких-либо дополнительных привилегий управлять своим собственным процессом. Причина в том, как работает dtrace - он включает зонды в ядре. Таким образом, разрешение непривилегированному пользователю проверять ядро означает, что пользователь может делать много нежелательных вещей, например, вынюхивать пароль другого пользователя путем включения зондов в драйвере клавиатуры!
Самый простой способ - использовать sudo:
sudo dtruss -f sudo -u $USER whoami
Другим решением было бы сначала запустить отладчик и отслеживать новые специфические процессы. Например
sudo dtruss -fn whoami
Затем в другом терминале просто запустите:
whoami
Просто как тот.
Более сложные аргументы вы можете найти в руководстве: man dtruss
В качестве альтернативы вы можете присоединить dtruss к запущенному пользовательскому процессу, например, на Mac:
sudo dtruss -fp PID
или аналогичный в Linux/Unix с использованием strace:
sudo strace -fp PID
Другим хакерским приемом может быть выполнение команды и сразу после этого присоединение к процессу. Вот некоторые примеры:
sudo true; (./Pages &); sudo dtruss -fp `pgrep -n -x Pages`
sudo true; (sleep 1 &); sudo dtruss -fp `pgrep -n -x sleep`
sudo true; (tail -f /var/log/system.log &); sudo dtruss -fp `pgrep -n -x tail`
Замечания:
Первый sudo только для кэширования пароля при первом запуске,
этот трюк не работает для быстрых командных строк, таких как
ls, date
так как требуется некоторое время, пока отладчик не подключится к процессу,Вы должны ввести свою команду в двух местах,
ты можешь игнорировать
&
чтобы запустить процесс в фоновом режиме, если он уже делает это,после завершения отладки вам придется вручную убить фоновый процесс (например,
killall -v tail
)
-n
аргумент dtruss
заставит dtruss ждать и исследовать процессы, которые соответствуют аргументу -n
, -f
опция все еще будет работать, чтобы следовать процессам, разветвленным из процессов, соответствующих -n
,
Все это означает, что если вы хотите обсудить процесс (ради аргумента, скажем, это whoami
) работая от имени непривилегированного пользователя, выполните следующие действия:
- Откройте корневую оболочку
- Бежать
dtruss -fn whoami
- это будет сидеть и ждать, пока существует процесс с именем "whoami"
- Откройте непривилегированную оболочку
- Бежать
whoami
- это выполнится и выйдет нормально
- Наблюдайте за системным вызовом в окне dtruss
- dtruss не выйдет сам по себе - он продолжит ждать соответствующих процессов - так что прекратите его, когда вы закончите
Этот ответ дублирует последнюю часть ответа @kenorb, но он заслуживает того, чтобы быть ответом первого класса.
Я не знаю, сможете ли вы заставить dtruss быть неинвазивным, как strace.
Вариант "sudo [to root] dtruss sudo [back to nonroot] cmd", который, кажется, лучше работает в некоторых быстрых тестах для меня:
sudo dtruss -f su -l `whoami` cd `pwd` && cmd....
Внешний sudo, конечно, так что dtruss работает как root.
Внутренний su вернулся ко мне, и с -l он правильно воссоздает окружение, и в этот момент нам нужно вернуться туда, откуда мы начали.
Я думаю, что "su -l user" лучше, чем "sudo -u user", если вы хотите, чтобы окружение было тем, что обычно получает этот пользователь. Это будет их среда входа в систему, хотя; Я не знаю, есть ли хороший способ позволить среде наследовать через два пользовательских изменения вместо этого.
В вашем вопросе, еще одна жалоба, которую вы имели в отношении обходного пути "sudo dtruss sudo", кроме уродства, заключалась в том, что "я все время не получаю файл a.out, не знаю почему". Я тоже не знаю, почему, но в моем небольшом тестовом сценарии вариант "sudo dtruss sudo" также не смог записать в тестовый выходной файл, а вариант "sudo dtruss su" выше создал выходной файл.
Похоже, что OS X не поддерживает использование dtrace для репликации всех необходимых вам функций strace. Тем не менее, я бы предложил создать оболочку вокруг подходящих системных вызовов. Похоже, DYLD_INSERT_LIBRARIES - это переменная окружения, которую вы хотите немного взломать. Это в основном так же, как LD_PRELOAD
для Linux.
Гораздо более простой способ переопределения библиотечных функций - использование переменной среды DYLD_INSERT_LIBRARIES (аналог LD_PRELOAD в Linux). Концепция проста: во время загрузки динамический компоновщик (dyld) будет загружать любые динамические библиотеки, указанные в DYLD_INSERT_LIBRARIES, прежде чем любые библиотеки, которые хочет загрузить исполняемый файл. Называя функцию так же, как в библиотечной функции, она отменяет любые вызовы оригинала.
Исходная функция также загружается и может быть получена с помощью dlsym(RTLD_NEXT, "имя_функции"); функция. Это позволяет использовать простой метод упаковки существующих библиотечных функций.
Согласно примеру Тома Робинсона, вам может потребоваться установить DYLD_FORCE_FLAT_NAMESPACE=1
, тоже.
Копия оригинального примера (lib_overrides.c
) который переопределяет только fopen
:
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>
// for caching the original fopen implementation
FILE * (*original_fopen) (const char *, const char *) = NULL;
// our fopen override implmentation
FILE * fopen(const char * filename, const char * mode)
{
// if we haven’t already, retrieve the original fopen implementation
if (!original_fopen)
original_fopen = dlsym(RTLD_NEXT, "fopen");
// do our own processing; in this case just print the parameters
printf("== fopen: {%s,%s} ==\n", filename, mode);
// call the original fopen with the same arugments
FILE* f = original_fopen(filename, mode);
// return the result
return f;
}
Использование:
$ gcc -Wall -o lib_overrides.dylib -dynamiclib lib_overrides.c
$ DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=lib_overrides.dylib command-to-test
Отказ от ответственности: это получено из ответа @kenorb. Однако у него есть некоторые преимущества: PID более конкретен, чем execname. И мы можем заставить недолговечный процесс ждать DTrace, прежде чем он начнется.
Это немного условно, но…
Допустим, мы хотим отследить cat /etc/hosts
:
sudo true && \
(sleep 1; cat /etc/hosts) &; \
sudo dtrace -n 'syscall:::entry /pid == $1/ {@[probefunc] = count();}' $!; \
kill $!
Мы используем sudo true
чтобы убедиться, что мы очистили запрос пароля sudo, прежде чем запускать что-нибудь чувствительное ко времени.
Запускаем фоновый процесс ("подождите 1 секунду, затем сделайте что-нибудь интересное"). Тем временем мы запускаем DTrace. Мы захватили PID фонового процесса в $!
так что мы можем передать это DTrace как аргумент.
kill $!
запускается после закрытия DTrace. Это не обязательно для нашего cat
пример (процесс закрывается сам по себе), но это помогает нам завершить длительные фоновые процессы, такие как ping
, Переходя -p $!
DTrace - предпочтительный способ сделать это, но в macOS, очевидно, требуется исполняемый файл с подписью кода.
Другая вещь, которую вы можете сделать, это запустить команду в отдельной оболочке и отследить эту оболочку. Смотри мой ответ.
Я не знаю способа запустить то, что вы хотите, как обычный пользователь, так как кажется, что dtruss, который использует dtrace, требует привилегий su.
Тем не менее, я считаю, что команда, которую вы искали вместо
dtruss -f sudo -u myusername gcc hello.c
является
sudo dtruss -f gcc hello.c
После ввода пароля, dtruss запустит dtrace с привилегиями sudo, и вы получите трассировку, а также файл a.out.
Извините, я не смог помочь.
Я не могу комментировать ответ Кенорба из-за отсутствия репутации. Но просто хотел добавить, что параметр -W заставит dtruss ожидать запуска указанного процесса, чтобы вывод не содержал множество несвязанных системных вызовов. Так
sudo dtruss -fW whoami
позволил мне найти мою проблему, где все было похоронено с помощью параметров -fn.
Извините, я не могу помочь! Я бы набрал Bash$ d program under test
чтобы безопасно запустить dtruss в отдельном корневом окне, но, похоже, MacOSX SIP или что-то еще сейчас наносит вред dtruss -p
а также opensnoop -p
таким же тупым способом "все или ничего", как старый корень Un*x. Таким образом, этот bash-скрипт больше не работает:
d ()
{
case "$*" in
[0-9] | [0-9][0-9] | [0-9][0-9][0-9] | [0-9][0-9][0-9][0-9] | [0-9][0-9][0-9][0-9][0-9])
dtruss -f -p "$*"
;;
*)
bash -c 'echo -en "\t <<< press return in this window to run after launching trace in root window like this >>> \t # d $$" >/dev/tty; (read -u3 3</dev/tty); exec "$0" "$@"' "$@"
;;
esac
}
Несколько десятилетий назад у нас были компьютеры Apple для сбора данных и джинсы Леви для вождения тракторов - теперь царит идиократия (пух-лиз, дави меня…), обе являются просто модными компаниями.