您的位置:首页 > 移动开发 > Android开发

su命令改写

2016-06-03 09:11 681 查看

su命令改写

    在android的系统定制过程中,会遇到有些客户要求他们的apk能获得root权限,在此,我们需要改写su命令来满足客户的需求,并且做到其他apk不能获取root权限。

    su命令的源码在android中的\system\extras\su目录下面,研究代码我们发现,su命令最后其实调用的就是execvp函数,只不过他自己的权限要求是6755,有s位,所以在调用的时候会变成root,顺带着execvp函数执行的命令也变成root了,原理简单,那么接下来我们就要思考如何才能使客户的apk获得root权限,其他未经授权的不允许呢,答案也简单,那就是通过包名,每个apk的包名是不一样的。我们允许客户的apk能够执行su命令,而其他的则不行。 那么我们怎么才能在C代码中获取包名呢,我们研究Linux发现,在su命令中,可以通过getppid函数获取父进程的pid,也就是apk的进程pid,如果你用adb进入android系统,在proc目录下,你会发现这么一堆东西,



这就是每个进程的信息,然后我们进去看看,以pid为733举例:



在里面的cmdline文件中,我们发现了包名,哈哈,那么问题就解决了,我们可以在su命令中获取到包名了。
原理就是这样,下面是完整的代码,供参考:
/*
**
** Copyright 2008, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0 **
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#define LOG_TAG "su"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>

#include <unistd.h>
#include <time.h>

#include <pwd.h>

// cxj add
#include <fcntl.h>

#include <private/android_filesystem_config.h>

/*
* SU can be given a specific command to exec. UID _must_ be
* specified for this (ie argc => 3).
*
* Usage:
* su 1000
* su 1000 ls -l
*/
int main(int argc, char **argv)
{
struct passwd *pw;
int uid, gid, myuid;

// cxj modify
#if 0
/* Until we have something better, only root and the shell can use su. */
myuid = getuid();
if (myuid != AID_ROOT && myuid != AID_SHELL) {
fprintf(stderr,"su: uid %d not allowed to su\n", myuid);
return 1;
}
#else
char *delim = " ";
char *p = NULL;
char cmdline[512];
int myfd;

sprintf(cmdline, "/proc/%d/cmdline", getppid());

myfd = open(cmdline, O_RDONLY);
if (myfd >= 0)
{
memset(cmdline, 0x00, 512);
read(myfd, cmdline, 512);
close(myfd);
p = strtok(cmdline, delim);
if (p != NULL)
{
if (0 == strcmp(p, "com.redphx.markethelper"))
{
// su allow, now to exe ...
}
else
{
return 1;
}
}
}
#endif

if(argc < 2) {
uid = gid = 0;
} else {
pw = getpwnam(argv[1]);

if(pw == 0) {
uid = gid = atoi(argv[1]);
} else {
uid = pw->pw_uid;
gid = pw->pw_gid;
}
}

if(setgid(gid) || setuid(uid)) {
fprintf(stderr,"su: permission denied\n");
return 1;
}

/* User specified command for exec. */
if (argc == 3 ) {
if (execlp(argv[2], argv[2], NULL) < 0) {
fprintf(stderr, "su: exec failed for %s Error:%s\n", argv[2],
strerror(errno));
return -errno;
}
} else if (argc > 3) {
/* Copy the rest of the args from main. */
char *exec_args[argc - 1];
memset(exec_args, 0, sizeof(exec_args));
memcpy(exec_args, &argv[2], sizeof(exec_args));
if (execvp(argv[2], exec_args) < 0) {
fprintf(stderr, "su: exec failed for %s Error:%s\n", argv[2],
strerror(errno));
return -errno;
}
}

/* Default exec shell. */
execlp("/system/bin/sh", "sh", NULL);

fprintf(stderr, "su: exec failed\n");
return 1;
}


接下来我们聊聊带有su权限管理的写法,市场有很多像superuser类似的apk,用来进行root权限管理,那么他们的原理又是什么呢,其实也很简单。
在上面的基础上,su命令去访问superuser的数据库,看看里面有没有调用者的包名,如果存在了,那么通过,获得root权限,没有,启动superuser,询问客户是否授权,
su命令中启动android activity的方法为调用shell命令:
am start -a android.intent.action.MAIN -n com.koushikdutta.superuser/com.koushikdutta.superuser.SuperuserRequestActivity
如果superuser上用户同意apk获取root权限,那么superuser就将包名写回数据库,su接着去读取,这样就完成授权了,是不是很简单啊,还是看完整的源码吧:
/*
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0 **
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>

#include <unistd.h>
#include <time.h>

#include <pwd.h>

#include <sqlite3.h>

#define DBPATH "/data/data/com.koushikdutta.superuser/databases/superuser.sqlite"

static int g_puid;

static void printRow(int argc, char** argv, char** azColName)
{
int i;
for (i = 0; i < argc; i++)
{
printf("%s: %s\n", azColName[i], argv[i]);
}
}

typedef struct whitelistCallInfo whitelistCallInfo;
struct whitelistCallInfo
{
sqlite3* db;
int count;
};

static int whitelistCallback(void *data, int argc, char **argv, char **azColName)
{
whitelistCallInfo* callInfo = (whitelistCallInfo*)data;
// note the count
int count = atoi(argv[2]);
callInfo->count = count;
// remove whitelist entries that are expired
if (count - 1 <= 0)
{
char remove[1024];
sprintf(remove, "delete from whitelist where _id='%s';", argv[0]);
sqlite3_exec(callInfo->db, remove, NULL, NULL, NULL);
return 0;
}

char update[1024];
sprintf(update, "update whitelist set count=%d where _id='%s';", count, argv[0]);
sqlite3_exec(callInfo->db, update, NULL, NULL, NULL);
return 0;
}

static int checkWhitelist()
{
sqlite3 *db;
int rc = sqlite3_open_v2(DBPATH, &db, SQLITE_OPEN_READWRITE, NULL);
if (!rc)
{
char *errorMessage;
char query[1024];
sprintf(query, "select * from whitelist where _id=%d limit 1;", g_puid);
struct whitelistCallInfo callInfo;
callInfo.count = 0;
callInfo.db = db;
rc = sqlite3_exec(db, query, whitelistCallback, &callInfo, &errorMessage);
if (rc != SQLITE_OK)
{
sqlite3_close(db);
return 0;
}
sqlite3_close(db);
return callInfo.count;
}
sqlite3_close(db);
return 0;
}

static int executionFailure(char *context)
{
fprintf(stderr, "su: %s. Error:%s\n", context, strerror(errno));
return -errno;
}

static int permissionDenied()
{
// the superuser activit
9288
y couldn't be started
printf("su: permission denied\n");
return 1;
}

int main(int argc, char **argv)
{
struct stat stats;
struct passwd *pw;
int uid = 0;
int gid = 0;

int ppid = getppid();
char szppid[256];
sprintf(szppid, "/proc/%d", ppid);
stat(szppid, &stats);
g_puid = stats.st_uid;

// lets make sure the caller is allowed to execute this
if (!checkWhitelist())
{
char sysCmd[1024];
sprintf(sysCmd, "am start -a android.intent.action.MAIN -n com.koushikdutta.superuser/com.koushikdutta.superuser.SuperuserRequestActivity --ei uid %d --ei pid %d > /dev/null", g_puid, ppid);
if (system(sysCmd))
return executionFailure("am.");

int found = 0;
int i;
for (i = 0; i < 10; i++)
{
sleep(1);
// 0 means waiting for user input
// > 0 means yes/always
// < 0 means no
int checkResult = checkWhitelist();
if (checkResult > 0)
{
found = 1;
break;
}
else if (checkResult < 0)
{
// user hit no
return permissionDenied();
}
}

if (!found)
return permissionDenied();
}

if(setgid(gid) || setuid(uid))
return permissionDenied();

char *exec_args[argc + 1];
exec_args[argc] = NULL;
exec_args[0] = "sh";
int i;
for (i = 1; i < argc; i++)
{
exec_args[i] = argv[i];
}
execv("/system/bin/sh", exec_args);
return executionFailure("sh");
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android su