su命令改写

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 activity 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");
}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值