You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android异常su进程处理:窃取父进程文件描述符方案咨询

解决Android中su进程stdin/stdout异常的方案

问题根源分析

你遇到的核心问题是:su进程作为中间层接管了终端的stdin/stdout,且未正确处理进程组与终端控制关系。当按下ctrl + c发送SIGINT信号时,信号会被传递给终端的前台进程组——而su及其所有子进程都属于该组,因此会被全部终止。绕开su的输入中转,让新shell直接绑定终端,是完全可行的解决思路。

你的方案可行性验证

你的思路方向完全正确:通过fork独立的shell进程,让它直接关联终端设备、脱离su的进程组控制,这样信号只会传递给新shell,不会影响其他进程。下面我把具体实现细节拆解清楚:

C程序实现步骤详解

1. 定位父su进程ID

如果你的C程序在su启动的环境中运行,直接调用getppid()就能拿到su的PID。若需要更严谨验证,可读取/proc/<pid>/comm文件确认进程名称为"su":

#include <unistd.h>
#include <stdio.h>
#include <string.h>

pid_t get_su_parent_pid() {
    pid_t ppid = getppid();
    char comm_path[64];
    snprintf(comm_path, sizeof(comm_path), "/proc/%d/comm", ppid);
    
    FILE* fp = fopen(comm_path, "r");
    if (!fp) return -1;
    
    char comm[32];
    fgets(comm, sizeof(comm), fp);
    fclose(fp);
    
    // 去掉换行符
    comm[strcspn(comm, "\n")] = '\0';
    if (strcmp(comm, "su") == 0) {
        return ppid;
    }
    return -1;
}

2. 获取终端设备文件描述符

用ttyname()获取当前终端路径(比如/dev/pts/1),再打开它获得可读写的文件描述符:

#include <fcntl.h>

int get_tty_fd() {
    const char* tty_path = ttyname(STDIN_FILENO);
    if (!tty_path) {
        perror("Failed to get terminal path");
        return -1;
    }
    int fd = open(tty_path, O_RDWR);
    if (fd == -1) {
        perror("Failed to open terminal");
    }
    return fd;
}

3. Fork并启动独立的Shell进程

这一步的核心是让新Shell成为终端的前台进程组,脱离su的进程组控制。代码如下:

#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>

int main() {
    // 获取su父进程ID(可选,如需后续处理su进程)
    pid_t su_pid = get_su_parent_pid();
    if (su_pid == -1) {
        fprintf(stderr, "Could not find parent su process\n");
        // 即使找不到,仍可尝试启动新shell
    }

    // 获取终端文件描述符
    int tty_fd = get_tty_fd();
    if (tty_fd == -1) {
        return 1;
    }

    // Fork新进程
    pid_t child_pid = fork();
    if (child_pid == 0) {
        // 子进程:创建新进程组并成为前台进程组
        setpgid(0, 0); // 将自身设为新进程组组长
        if (tcsetpgrp(tty_fd, getpid()) == -1) {
            perror("Failed to set terminal foreground process group");
            _exit(1);
        }

        // 重定向标准输入输出到终端
        dup2(tty_fd, STDIN_FILENO);
        dup2(tty_fd, STDOUT_FILENO);
        dup2(tty_fd, STDERR_FILENO);
        close(tty_fd);

        // 替换为Shell(若设备有bash可替换为/system/bin/bash)
        execl("/system/bin/sh", "sh", NULL);
        // 若execl执行失败,退出子进程
        perror("Failed to execute shell");
        _exit(1);
    } else if (child_pid > 0) {
        // 父进程:可选择等待子进程结束,或直接退出让子进程独立运行
        // 如需终止su进程,可在此发送信号:kill(su_pid, SIGTERM);
        waitpid(child_pid, NULL, 0);
    } else {
        perror("Failed to fork process");
        close(tty_fd);
        return 1;
    }

    close(tty_fd);
    return 0;
}

关键注意事项

  • 编译适配:需用Android NDK编译程序,确保目标架构与设备匹配(比如arm64-v8a),编译命令示例:
    $NDK_PATH/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android29-clang su_fix.c -o su_fix
    
  • 进程组设置:setpgid和tcsetpgrp是核心,缺少这两步,新Shell仍会属于su的进程组,ctrl+c仍会终止所有进程。
  • su进程处理:若不需要su继续运行,可在父进程中向su_pid发送SIGTERM信号;若不主动终止,su进程会成为孤儿进程被init收养,失去终端控制权后通常会自行退出。

内容的提问来源于stack exchange,提问作者twosixonetwo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:09:13