Как отследить программу с самого начала, не запуская ее от имени пользователя root
Я пишу инструмент, который вызывает DTrace для отслеживания программы, указанной пользователем.
Если мой инструмент использует dtrace -c для запуска программы как подпроцесса DTrace, я не только не могу передать никакие аргументы программе, но и программа запускается со всеми привилегиями DTrace, то есть с правами root (я нахожусь на Mac OS X). Это делает некоторые вещи, которые должны работать, и делает множество вещей, которые не должны работать, возможным.
Другое известное мне решение - запустить программу самостоятельно, приостановить ее, отправив SIGSTOP
передайте свой PID dtrace -p
затем продолжите, отправив SIGCONT
, Проблема в том, что либо программа выполняется в течение нескольких секунд без отслеживания, пока DTrace собирает информацию о символах, либо, если я сплю несколько секунд, прежде чем продолжить процесс, DTrace жалуется, что objc<pid>:<class>:<method>:entry
не соответствует никаким зондам.
Можно ли запустить программу под учетной записью пользователя, а не под учетной записью root, но при этом DTrace может отследить ее с самого начала?
8 ответов
Что-то вроде sudo dtruss -f sudo -u <original username> <command>
работал для меня, но я чувствовал себя плохо об этом потом.
Я подал об этом ошибку радара и закрыл ее как дубликат #5108629.
Этот скрипт принимает имя исполняемого файла (для приложения это CFBundleExecutable файла info.plist), который вы хотите отслеживать в DTrace в качестве параметра (вы можете запустить целевое приложение после запуска этого скрипта):
string gTarget; /* the name of the target executable */
dtrace:::BEGIN
{
gTarget = $$1; /* get the target execname from 1st DTrace parameter */
/*
* Note: DTrace's execname is limited to 15 characters so if $$1 has more
* than 15 characters the simple string comparison "($$1 == execname)"
* will fail. We work around this by copying the parameter passed in $$1
* to gTarget and truncating that to 15 characters.
*/
gTarget[15] = 0; /* truncate to 15 bytes */
gTargetPID = -1; /* invalidate target pid */
}
/*
* capture target launch (success)
*/
proc:::exec-success
/
gTarget == execname
/
{
gTargetPID = pid;
}
/*
* detect when our target exits
*/
syscall::*exit:entry
/
pid == gTargetPID
/
{
gTargetPID = -1; /* invalidate target pid */
}
/*
* capture open arguments
*/
syscall::open*:entry
/
((pid == gTargetPID) || progenyof(gTargetPID))
/
{
self->arg0 = arg0;
self->arg1 = arg1;
}
/*
* track opens
*/
syscall::open*:return
/
((pid == gTargetPID) || progenyof(gTargetPID))
/
{
this->op_kind = ((self->arg1 & O_ACCMODE) == O_RDONLY) ? "READ" : "WRITE";
this->path0 = self->arg0 ? copyinstr(self->arg0) : "<nil>";
printf("open for %s: <%s> #%d",
this->op_kind,
this->path0,
arg0);
}
Ну, это немного стар, но почему бы и нет:-)..
Я не думаю, что есть способ сделать это просто из командной строки, но, как предложено, простое приложение запуска, такое как следующее, сделало бы это. Конечно, ручное присоединение можно заменить несколькими вызовами libdtrace.
int main(int argc, char *argv[]) {
pid_t pid = fork();
if(pid == 0) {
setuid(123);
seteuid(123);
ptrace(PT_TRACE_ME, 0, NULL, 0);
execl("/bin/ls", "/bin/ls", NULL);
} else if(pid > 0) {
int status;
wait(&status);
printf("Process %d started. Attach now, and click enter.\n", pid);
getchar();
ptrace(PT_CONTINUE, pid, (caddr_t) 1, 0);
}
return 0;
}
Если другой ответ не работает для вас, можете ли вы запустить программу в gdb, взломать main (или даже раньше), получить pid и запустить скрипт? Я пробовал это в прошлом, и, похоже, это сработало.
Создайте программу запуска, которая будет ожидать какой-то сигнал (не обязательно буквальный сигнал, просто указание, что он готов), а затем exec() вашей цели. Теперь выполните dtrace -p программу запуска, и как только dtrace будет запущен, отпустите программу запуска.
Dtruss имеет -n
вариант, в котором вы можете указать имя процесса, который вы хотите отслеживать, не запуская его (кредит последней части ответа @kenorb на /questions/19706535/kak-ya-mogu-zastavit-dtrace-zapuskat-trassirovannuyu-komandu-s-privilegiyami-ne-root/19706545#19706545). Так что-то вроде следующего должно сделать это:
sudo dtruss -n "$program"
$program
Существует инструментdarwin-debug
который поставляется в CLT LLDB.framework от Apple, который будет запускать вашу программу и приостанавливать ее, прежде чем она что-либо сделает. Затем вы читаете pid из сокета unix, который вы передаете в качестве аргумента, и после подключения отладчика/dtrace вы продолжаете процесс.
darwin-debug will exec itself into a child process <PROGRAM> that is
halted for debugging. It does this by using posix_spawn() along with
darwin specific posix_spawn flags that allows exec only (no fork), and
stop at the program entry point. Any program arguments <PROGRAM-ARG> are
passed on to the exec as the arguments for the new process. The current
environment will be passed to the new process unless the "--no-env"
option is used. A unix socket must be supplied using the
--unix-socket=<SOCKET> option so the calling program can handshake with
this process and get its process id.
Смотрите мой ответ на связанный вопрос "Как заставить dtrace запускать трассированную команду с привилегиями не-root?" [Так в оригинале].
По сути, вы можете запустить (не-root) фоновый процесс, который ждет 1 с для запуска DTrace (извините за состояние гонки) и отслеживает PID этого процесса.
sudo true && \
(sleep 1; cat /etc/hosts) &; \
sudo dtrace -n 'syscall:::entry /pid == $1/ {@[probefunc] = count();}' $! \
&& kill $!
Полное объяснение в связанном ответе.