Android: 从应用程序执行二进制文件
Android: Execute binary from application
如标题所示,我想从我的 android 应用程序中执行一个二进制文件。我使用 NDK 创建了一个二进制文件,并且可以使用 adb shell 运行 它。然后我尝试使用 Runtime exec 运行 它,但权限被拒绝。我尝试了不同的目录并更改了文件权限。
我调查了这个问题,发现很多堆栈溢出 questions/answers 大部分都已经过时了。 Android 安全性似乎在不同版本中发生了很大变化。
我发现了什么:
OS 阻止应用程序执行自己编写的代码。这称为 Write xor execute 并且是有道理的。 .apk 和 .dex 文件除外。这应该意味着我想要的是不可能的。
我还发现了一些能够执行此类二进制文件的应用程序,例如 Android offline C compiler, which compiles and executes C code. Another well-known application that can execute binaries is the terminal emulator Termux。此外,有趣的是,在执行二进制文件时,它们都具有与 adb shell 相同的性能。因此,我假设他们运行 shell.
上的二进制文件
我最终想知道的是这些应用程序如何执行这些二进制文件,以及我是否可以实现相同的目标。
我进一步研究了这两个应用程序的代码,发现它们使用本机代码创建了一个执行 shell 命令的进程。相反,我总是使用运行时 object,可以从 Java 访问它。此外,他们使用本机代码来更改文件权限。这让我想知道使用本机代码是否可以让开发人员在这些限制方面有更多的自由。
我的问题: 如果在尝试执行二进制文件时使用 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;
}
}
如果我遗漏了一些东西并且还有其他一些执行二进制文件的技巧,我也想听听。
非常感谢任何帮助 and/or 见解!
P.S.: 可能也很重要,我不想在 .apk 中包含二进制文件,而是在 运行 申请期间下载或写入它。此外,二进制文件并不复杂。我目前只是尝试执行“Hello world”-binary。
我发现了区别,它与本机代码无关...
这些应用程序使用 targetSdkVersion 28
或更低版本,其中仍然允许执行二进制文件。这意味着可以使用 Runtime
对象以及此 SDK 版本中的本机代码执行二进制文件。
应用程序仍然可以安装在较新的 Android 版本上。
所以我通过将 targetSdkVersion
设置为 28 解决了我的问题。
如标题所示,我想从我的 android 应用程序中执行一个二进制文件。我使用 NDK 创建了一个二进制文件,并且可以使用 adb shell 运行 它。然后我尝试使用 Runtime exec 运行 它,但权限被拒绝。我尝试了不同的目录并更改了文件权限。
我调查了这个问题,发现很多堆栈溢出 questions/answers 大部分都已经过时了。 Android 安全性似乎在不同版本中发生了很大变化。
我发现了什么: OS 阻止应用程序执行自己编写的代码。这称为 Write xor execute 并且是有道理的。 .apk 和 .dex 文件除外。这应该意味着我想要的是不可能的。
我还发现了一些能够执行此类二进制文件的应用程序,例如 Android offline C compiler, which compiles and executes C code. Another well-known application that can execute binaries is the terminal emulator Termux。此外,有趣的是,在执行二进制文件时,它们都具有与 adb shell 相同的性能。因此,我假设他们运行 shell.
上的二进制文件我最终想知道的是这些应用程序如何执行这些二进制文件,以及我是否可以实现相同的目标。
我进一步研究了这两个应用程序的代码,发现它们使用本机代码创建了一个执行 shell 命令的进程。相反,我总是使用运行时 object,可以从 Java 访问它。此外,他们使用本机代码来更改文件权限。这让我想知道使用本机代码是否可以让开发人员在这些限制方面有更多的自由。
我的问题: 如果在尝试执行二进制文件时使用 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;
}
}
如果我遗漏了一些东西并且还有其他一些执行二进制文件的技巧,我也想听听。
非常感谢任何帮助 and/or 见解!
P.S.: 可能也很重要,我不想在 .apk 中包含二进制文件,而是在 运行 申请期间下载或写入它。此外,二进制文件并不复杂。我目前只是尝试执行“Hello world”-binary。
我发现了区别,它与本机代码无关...
这些应用程序使用 targetSdkVersion 28
或更低版本,其中仍然允许执行二进制文件。这意味着可以使用 Runtime
对象以及此 SDK 版本中的本机代码执行二进制文件。
应用程序仍然可以安装在较新的 Android 版本上。
所以我通过将 targetSdkVersion
设置为 28 解决了我的问题。