Как отследить программу с самого начала, не запуская ее от имени пользователя root - PullRequest
11 голосов
/ 30 июля 2009

Я пишу инструмент, который вызывает DTrace для отслеживания программы, указанной пользователем.

Если мой инструмент использует dtrace -c для запуска программы в качестве подпроцесса DTrace, я не только не могу передать никакие аргументы программе, но и программа запускается со всеми привилегиями DTrace, то есть с правами root ( нахожусь на Mac OS X). Это делает некоторые вещи, которые должны работать, и делает множество вещей, которые не должны работать, возможными.

Другое известное мне решение - это запустить программу самостоятельно, поставить ее на паузу, отправив SIGSTOP, передать ее PID на dtrace -p, а затем продолжить, отправив SIGCONT. Проблема в том, что либо программа выполняется в течение нескольких секунд без отслеживания, пока DTrace собирает информацию о символах, либо, если я сплю несколько секунд, прежде чем продолжить процесс, DTrace жалуется, что objc<pid>:<class>:<method>:entry не соответствует ни одному из зондов.

Есть ли способ, с помощью которого я могу запустить программу под учетной записью пользователя, а не под учетной записью root, но при этом DTrace может отследить ее с самого начала?

Ответы [ 7 ]

6 голосов
/ 05 декабря 2009

Что-то вроде sudo dtruss -f sudo -u <original username> <command> сработало для меня, но потом мне стало плохо.

Я подал об этом ошибку радара и закрыл ее как дубликат # 5108629.

3 голосов
/ 11 июля 2012

Этот скрипт принимает имя исполняемого файла (для приложения это 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);
}
3 голосов
/ 22 апреля 2011

Ну, это немного устарело, но почему бы и нет: -) ..

Я не думаю, что есть способ сделать это просто из командной строки, но, как предложено, простое приложение запуска, такое как следующее, сделало бы это. Конечно, ручное присоединение также можно заменить несколькими вызовами 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;
}
3 голосов
/ 30 июля 2009

Если другой ответ не работает для вас, можете ли вы запустить программу в gdb, взломать main (или даже раньше), получить pid и запустить скрипт? Я пробовал это в прошлом, и, похоже, это сработало.

2 голосов
/ 30 июля 2009

Создайте программу запуска, которая будет ожидать какой-либо сигнал (не обязательно буквальный сигнал, просто указание, что он готов), а затем exec () вашей цели. Теперь выполните dtrace -p программу запуска, и после запуска dtrace запустите программу запуска.

1 голос
/ 27 декабря 2013

dtruss имеет опцию -n, в которой вы можете указать имя процесса, который вы хотите отслеживать, не запуская его.

sudo dtruss -n "$program"
$program
0 голосов
/ 25 июня 2017

См. мой ответ на связанный вопрос"Как заставить dtrace запустить трассированную команду с привилегиями не-root?" [Так в оригинале].

По сути, вы можете запустить (не-root) фоновый процесс, который ждет 1 секунда для запуска DTrace (извините за состояние гонки) и отслеживает PID этого процесса.

sudo true && \
(sleep 1; cat /etc/hosts) &; \
sudo dtrace -n 'syscall:::entry /pid == $1/ {@[probefunc] = count();}' $! \
&& kill $!

Полное объяснение в связанном ответе.

...