【问题标题】:How can I get dtrace to run the traced command with non-root priviledges?如何让 strace 以非 root 权限运行 trace 命令?
【发布时间】:2011-03-01 18:35:35
【问题描述】:

OS X 缺少 linux 的 strace,但它有 dtrace,这应该会更好。

但是,我想念对单个命令进行简单跟踪的能力。例如,在 linux 上,我可以编写 strace -f gcc hello.c 来捕获所有系统调用,这为我提供了所有编译器编译我的程序所需的文件名列表(构建了出色的 memoize 脚本根据这个技巧)

我想在 mac 上移植 memoize,所以我需要某种 strace。我真正需要的是gcc 读写的文件列表,所以我需要更多的是truss。果然我可以说dtruss -f gcc hello.c 并获得一些相同的功能,但是编译器以root 权限运行,这显然是不可取的(除了巨大的安全风险之外,一个问题是a.out 文件现在由根:-)

然后我尝试了dtruss -f sudo -u myusername gcc hello.c,但这感觉有点不对劲,而且无论如何都不起作用(我一直没有得到a.out 文件,不知道为什么)

所有这些长篇大论都试图激发我最初的问题:我如何让dtrace 以普通用户权限运行我的命令,就像strace 在 linux 中所做的那样?

编辑:似乎我不是唯一一个想知道如何做到这一点的人:问题#1204256 与我的几乎相同(并且具有相同的次优 sudo 答案:-)

【问题讨论】:

    标签: macos root dtrace strace


    【解决方案1】:

    我不知道如何以普通用户的身份运行您想要的东西,因为使用 dtrace 的 dtruss 似乎需要 su 权限。

    但是,我相信您正在寻找的命令而不是

    dtruss -f sudo -u myusername gcc hello.c

    sudo dtruss -f gcc hello.c

    输入密码后,dtruss 将运行 dtrace 将 sudo 权限,您将获得跟踪以及 a.out 文件。

    很抱歉,我无法提供进一步的帮助。

    【讨论】:

    • 实际以 root 身份运行命令本身的问题(除了明显的巨大安全风险)是 sudo 的运行时环境(PATH、env 变量、权限)与正常环境有很大不同,并且极大地影响被跟踪程序的行为。相反,Linux strace 在跟踪命令的功能行为方面几乎是透明的。
    【解决方案2】:

    我不知道你是否可以让 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 它正确地重新创建了环境,此时我们需要 cd 回到我们开始的地方。

    如果您希望环境成为该用户通常获得的环境,我认为“su -l user”比“sudo -u user”更好。那将是他们的登录环境;不知道有没有什么好办法让环境通过两次用户变更来继承。

    在您的问题中,除了丑陋之外,您对“sudo dtruss sudo”解决方法的另一个抱怨是“我一直没有得到 a.out 文件,不知道为什么”。我也不知道为什么,但是在我的小测试脚本中,“sudo dtruss sudo”变体也无法写入测试输出文件,而上面的“sudo dtruss su”变体确实创建了输出文件。

    【讨论】:

    • 确实,感谢您的尝试。不过,看起来我将不得不放弃这个。在 linux 上,我曾经使用来自 makefile-replacement 脚本的strace 命令来监视命令正在触及哪些文件。我想将这些脚本移植到 mac os x,但最终我意识到我不喜欢让我的所有构建都涉及多个级别的 sudo 只是为了监视我自己的命令。
    • 是的。出于您所描述的原因,直接尝试使 dtruss 充当非侵入性 strace 替代品似乎是错误的树。也许需要另一种方法。我可能发生的 2 件事:1) 获取 dtrace 源,修改用户级前端(需要以 root 身份运行),以便它只启动新进程并且只监视这些进程,添加一个选项来告诉它谁启动这些进程,然后将其设置为 root 拥有的 setuid。
    • 和#2(继续上一条评论):直接使用 dtrace 来回答您想要回答的问题,而不是尝试在您的 makefile 中将其用作 strace 的替代品观察子命令在做什么——你能在 make 进程之外编写 dtrace 探针来识别和注释 make 进程在做什么吗?
    • 非常感谢这些 cmets。至于#1,恐怕我没有足够的时间和足够的技能(到目前为止!)走这条路。选项 #2 对我来说听起来更现实,我一直在努力熟悉 DTrace 以尝试这样做。如果我可以编写一个作为守护进程运行的 DTrace 脚本并从外部监视我的所有构建,那么我确实可以获得我需要的信息。目前尚不清楚当有新进程要跟踪时如何通知该守护进程。如果我取得一些进展,我会发布更新。再次感谢。
    【解决方案3】:

    不是您的问题的答案,而是需要了解的内容。 OpenSolaris 通过“特权”(部分)解决了这个问题 - 请参阅 this page。即使在 OpenSolaris 中,也不可能允许用户在没有任何额外权限的情况下 dtruss 自己的进程。 原因是 dtrace 的工作方式 - 它启用了在内核中的探测 in。因此,允许非特权用户探测内核意味着用户可以做很多不需要的事情,例如通过在键盘驱动程序中启用探针来嗅探其他用户的密码!

    【讨论】:

    • 你可能是对的。即使在这里获得 root 权限不是问题,因为在我的笔记本电脑上我可以(并且已经这样做了)chmod a+s dtrace,但是,dtrace 并不是作为“unix 高级用户”工具,而是作为“unix 管理员”工具.这就是为什么试图从用户程序中使用它会导致这种人为的情况。非常感谢您的回答。
    • 难道它不能有一个“受限模式”,其中只有一些探测器(如系统调用探测器或用户空间探测器)会触发,并且只在某些进程(相关用户拥有的那些)中触发,并且只有一些功能可用:那些可以很容易地只检查用户自己的进程的功能,或者只提供对用户已经以其他方式可用的信息的访问?
    【解决方案4】:

    最简单的方法是使用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

    【讨论】:

    • 您是一位摇滚明星,因为您指出了 dtruss 的 -n 选项。这是 100% 的正确答案。
    【解决方案5】:

    dtruss-n 参数将导致 dtruss 等待并检查与 -n 的参数匹配的进程。 -f 选项仍可用于跟踪从-n 匹配的进程派生的进程。

    所有这一切意味着,如果您想以非特权用户身份运行一个进程(为了争论,假设它是 whoami),请按照以下步骤操作:

    1. 打开根外壳
    2. 运行dtruss -fn whoami
      • 这将等待名为“whoami”的进程存在
    3. 打开非特权 shell
    4. 运行whoami
      • 这将正常执行并退出
    5. 在 dtruss 窗口中观察系统调用跟踪
      • dtruss 不会自行退出——它会继续等待匹配的进程——所以当你完成后退出它

    这个答案重复了@kenorb 回复的后半部分,但它应该是一流的答案。

    【讨论】:

      【解决方案6】:

      似乎 OS X 不支持使用 dtrace 复制您需要的所有 strace 功能。但是,我建议尝试围绕合适的系统调用创建一个包装器。看起来DYLD_INSERT_LIBRARIES 是您想要破解的环境变量。这与 Linux 的 LD_PRELOAD 基本相同。

      进行库函数覆盖的一种更简单的方法是使用 DYLD_INSERT_LIBRARIES 环境变量(类似于 LD_PRELOAD on Linux)。这个概念很简单:在加载时动态链接器(dyld) 将加载 DYLD_INSERT_LIBRARIES 中指定的任何动态库 在可执行文件要加载的任何库之前。通过命名函数 与库函数中的函数相同,它将覆盖任何对 原件。

      原始函数也被加载,并且可以使用 dlsym(RTLD_NEXT,“函数名”);功能。这允许一个简单的 包装现有库函数的方法。

      根据Tom Robinsonexample,您可能还需要设置DYLD_FORCE_FLAT_NAMESPACE=1

      仅覆盖 fopen 的原始示例 (lib_overrides.c) 的副本:

      #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
      

      【讨论】:

        【解决方案7】:

        免责声明:这源自@kenorb 的answer。但它有一些优点: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 捕获到 $!,因此我们可以将其作为 arg 传递给 DTrace。

        kill $! 在我们关闭 DTrace 后运行。对于我们的cat 示例(进程自行关闭),这不是必需的,但它可以帮助我们结束长时间运行的后台进程,例如ping。将-p $! 传递给 DTrace 是执行此操作的首选方式,但在 macOS 上显然需要代码签名的可执行文件。


        您可以做的另一件事是在单独的 shell 中运行命令,然后窥探该 shell。见我的answer

        【讨论】:

          【解决方案8】:

          我在 bash 提示符下使用 Terminal.app 中的这种诊断和修复崩溃应用程序的通用方法修复了损坏的预览:

          $sudo dtruss -fn Preview 2&gt;&amp;1 | grep '/Users/'

          输入密码后,我启动了预览,它在打印 dtruss 时立即崩溃

          70256/0x19cd898: chdir("/Users/devon/Library/Containers/com.apple.Preview/Data\0", 0x0, 0x7FFF5EAC4BC8) = 0 0

          我没有删除这个可能的嫌疑人,而是将其重命名

          $(cd ~/Library/Containers &amp;&amp; mv -i com.apple.Preview com.apple.Preview.~NOT~)

          预览版现在可以正常工作,并创建了一个新的、无毒版本的目录。

          您可能更喜欢grep '"/',以防这是系统范围的问题 但是这会在/usr/System 等中向您发送数百个无辜的项目。

          从 MacOSX 11.6 开始,“El Capitan” dtrace/dtruss 默认已失效 - 请参阅fix it

          我使用这个 bash 函数,例如 $d /Applications/Preview.app/Contents/MacOS/Preview

          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 电脑来推送数据,使用 Levi's 牛仔裤来驱动拖拉机——两者都已沦为单纯的时尚公司。 SF distopia Idiocracy 似乎相关。

          【讨论】:

            猜你喜欢
            • 2020-10-02
            • 2011-12-22
            • 1970-01-01
            • 2010-09-28
            • 1970-01-01
            • 2022-11-25
            • 1970-01-01
            • 2019-10-15
            • 1970-01-01
            相关资源
            最近更新 更多