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"); }
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories