通过argv操作进程名称和参数
收藏

我有一个用c编写的程序,它只在linux上运行。我希望能够更改进程名,如ps命令所示。为此,我直接在argv[0]中更改字符串,并使用来自主线程的prctl(PR_SET_NAME, argv[0])调用。我还想从动态加载的共享库访问/proc/self/cmdline,将来甚至可以从其他程序访问。
我读到了,为了让它工作,我必须使用从argv[0]开始的原始内存空间。ELF标准指定此空间与\0空间分开。从postgres代码中查看ps_status.c,可以看到他们正在使用所有这些空间来处理argv字符串。实际上,当我将这个空间转换成environ时,我可以在memset中看到3000多个字符,并从'a'文件系统读取它们。当我试图使用这个空间动态(在运行时)在这个空间中创建新参数时,问题就开始了。(我读过,从基本测试中知道chrome/chromium做了类似的事情-通过命令行参数导出ps中的/proced进程的状态。)任何在空间中包含空分隔符并到达原始环境的内容都被视为结束。(我最初在命令行参数中有105个字符,我可以得到130个字符,但在这个3000个字符标记之前的其他参数不会被读取。)由此我推断,系统会记住原始大小,只允许我“读取”到字符串结束。(更改char**argv指针没有帮助。)
但Chrome不知怎么做到了。我看不到任何直接的方法。
这样做有可能吗?如果是,怎么做?告诉Linux内核argv内存和argc的大小发生了变化?
谢谢您。


最佳答案:

PR_SET_MM_ARG_STARTPR_SET_MM_ARG_END允许您这样做,如果您是根用户(更具体地说,如果流程具有CAP_SYS_RESOURCE功能)。
用法:

prctl(PR_SET_NAME, constructed_argv[0]);
prctl(PR_SET_MM, PR_SET_MM_ARG_START, constructed_argv, 0, 0);
prctl(PR_SET_MM, PR_SET_MM_ARG_END, end_of_constructed_argv, 0, 0);

下面是一个从systemd中充分记录使用它们的示例:
            /* Now, let's tell the kernel about this new memory */
            if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
                    /* HACK: prctl() API is kind of dumb on this point.  The existing end address may already be
                     * below the desired start address, in which case the kernel may have kicked this back due
                     * to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in
                     * action).  The proper solution would be to have a prctl() API that could set both start+end
                     * simultaneously, or at least let us query the existing address to anticipate this condition
                     * and respond accordingly.  For now, we can only guess at the cause of this failure and try
                     * a workaround--which will briefly expand the arg space to something potentially huge before
                     * resizing it to what we want. */
                    log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m");

                    if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) {
                            log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m");
                            (void) munmap(nn, nn_size);
                            goto use_saved_argv;
                    }

                    if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
                            log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m");
                            goto use_saved_argv;
                    }
            } else {
                    /* And update the end pointer to the new end, too. If this fails, we don't really know what
                     * to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure,
                     * and continue. */
                    if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0)
                            log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
            }

    公众号
    关注公众号订阅更多技术干货!