Android:从应用程序执行二进制文件

时间:2021-06-14 17:49:03

标签: java android android-ndk android-shell

正如标题所示,我想从我的 android 应用程序中执行一个二进制文件。我使用 NDK 创建了一个二进制文件,并且可以使用 adb shell 运行它。然后我尝试使用 Runtime exec 运行它,但权限被拒绝。我尝试了不同的目录并更改了文件权限。

我调查了这个问题,发现了很多堆栈溢出问题/答案,这些问题/答案大多已经过时。不同版本的 Android 安全性似乎发生了很大变化。

我的发现: 操作系统阻止应用程序执行自己编写的代码。这被称为 Write xor execute 并且是有道理的。 .apk 和 .dex 文件除外。这应该意味着我想要的是不可能的。

我还发现了一些能够执行此类二进制文件的应用程序,例如 Android offline C compiler,它编译并执行 C 代码。另一个可以执行二进制文件的著名应用程序是终端模拟器 Termux。此外,有趣的是,在执行二进制文件时,它们都具有与 adb shell 相同的性能。因此,我假设他们通过 shell 运行二进制文件。

我最终想知道的是这些应用程序如何执行这些二进制文件,以及我是否可以实现相同的目标。

我进一步研究了这两个应用程序的代码,它们使用本机代码创建一个执行 shell 命令的进程。相反,我总是使用可以从 Java 访问的 Runtime 对象。此外,他们使用本机代码来更改文件权限。这让我想知道使用本机代码是否能让开发人员在这些限制方面有更多的自由。

我的问题:在尝试执行二进制文件时使用 Java 还是本机代码有区别吗?如果不是,他们可以执行二进制文件的原因是什么,这可以用 Java 实现吗?

以下是来自 Termux 的原生代码,它创建了一个执行 shell 命令的子进程:


static int create_subprocess(JNIEnv *env, const char *cmd, char *const argv[], char *const envp[], int masterFd)
{
    // same size as Android 1.6 libc/unistd/ptsname_r.c
    char devname[64];
    pid_t pid;

    fcntl(masterFd, F_SETFD, FD_CLOEXEC);

    // grantpt is unnecessary, because we already assume devpts by using /dev/ptmx
    if(unlockpt(masterFd)){
        throwIOException(env, errno, "trouble with /dev/ptmx");
        return -1;
    }
    memset(devname, 0, sizeof(devname));
    // Early (Android 1.6) bionic versions of ptsname_r had a bug where they returned the buffer
    // instead of 0 on success.  A compatible way of telling whether ptsname_r
    // succeeded is to zero out errno and check it after the call
    errno = 0;
    int ptsResult = ptsname_r(masterFd, devname, sizeof(devname));
    if (ptsResult && errno) {
        throwIOException(env, errno, "ptsname_r returned error");
        return -1;
    }

    pid = fork();
    if(pid < 0) {
        throwIOException(env, errno, "fork failed");
        return -1;
    }

    if(pid == 0){
        int pts;

        setsid();

        pts = open(devname, O_RDWR);
        if(pts < 0) exit(-1);

        ioctl(pts, TIOCSCTTY, 0);

        dup2(pts, 0);
        dup2(pts, 1);
        dup2(pts, 2);

        closeNonstandardFileDescriptors();

        if (envp) {
            for (; *envp; ++envp) {
                putenv(*envp);
            }
        }

        execv(cmd, argv);
        exit(-1);
    } else {
        return (int) pid;
    }
}

如果我遗漏了一些东西,并且还有其他一些执行二进制文件的技巧,我也想听听。

非常感谢任何帮助和/或见解!

PS:可能也很重要,我不想将二进制文件包含在 .apk 中,而是在我的应用程序运行时下载或编写它。此外,二进制文件并不复杂。我目前只是尝试执行“Hello world”二进制文件。

1 个答案:

答案 0 :(得分:1)

我发现了不同之处,它与本机代码无关...

这些应用程序使用 targetSdkVersion 28 或更低版本,但仍允许执行二进制文件。这意味着可以使用 Runtime 对象以及此 SDK 版本中的本机代码执行二进制文件。

这些应用程序仍然可以安装在较新的 Android 版本上。

所以我通过将 targetSdkVersion 设置为 28 解决了我的问题。