通过全志SystemMix可扩展接口为app提供root权限

一、SystemMix接口说明

在android/frameworks/base/swextend/systemmix目录下,全志提供了一套用于访问底层高权限信息的接口,用户可以参照里面的做法来扩展SystemMix类的功能。

1. systemmix目录结构:

2. 该机制使用了Android上使用广泛的 客户端<--->服务端 机制去实现,调度流程如下:

3. 该SystemMix.java文件为用户提供了如下几个示例接口:

	/**
	* mount device or other
	*/
	public static int Mount(String source, String mountPoint, String fs, int flags, String options){
		return nativeMount(source, mountPoint, fs, flags, options);	
	}
	
	/**
	* umount device or other
	*/
	public static int Umount(String mountPoint){
		return nativeUmount(mountPoint);	
	}
	
    /**
    * get a property's value
    */
	public static String getProperty(String key){
	    return nativeGetProperty(key);
	}

    /**
    * set a property's value
    */
	public static void setProperty(String key, String value){
		if(key != null && value != null){
			nativeSetProperty(key,value);
		}
	}
	
	/**
	* get command para in /proc/cmdline
	*/
	public static String getCmdPara(String name){
	    HashMap<String, String> paraMap = mapPara();
	    return paraMap.get(name);    
	}
	
	@SuppressWarnings("null")
	private static HashMap<String, String> mapPara(){
	    HashMap<String, String> paraMap = new HashMap<String, String>();
		String cmdline = getCmdLine();
		Log.d(TAG,"getCmdLine = " + cmdline);
		if(cmdline != null){
			String[] list = cmdline.split(" ");
			if(list != null){
				for(int i = 0; i < list.length; i++){
					String[] map = list[i].split("=");
					if(map == null || map.length != 2){
						continue;
					}
					paraMap.put(map[0], map[1]);
				}
			}
		}
		return paraMap;
	}
	
	private static String getCmdLine(){
	    byte[] desData = new byte[256];
	    int ret = nativeGetFileData(desData, desData.length, "/proc/cmdline");
	    String str = null;
	    if(ret > 0){
	        str = new String(desData);    
	    }
	    return str;
	}

用户在扩展该SystemMix接口的时候,可以参考getCmdPara()方法的调度流程,同时对java端、jni端,client端,server端做出相应的扩展。

二、扩展SystemMix接口,让app执行shell命令

在SystemMix中添加runShell操作接口,为app提供root权限来执行shell命令。


1. SystemMix接口扩展

我们按照java端(SystemMix.java)、jni端(com_softwinner_SystemMix.cpp),client端(ISystemMixService.h, ISystemMixService.cpp),server端(SystemMixService.h, SystemMixService.cpp)的顺序修改android/frameworks/base/swextend/systemmix目录下的文件,增加runShell操作接口。

关键代码如下:

SystemMix.java

	private static native String nativeRunShell(String cmd);
	
	/**
	* run shell command
	*/	
	public static String runShell(String cmd){
		return nativeRunShell(cmd);	  	
	}

com_softwinner_SystemMix.cpp

static jstring runShell_native(JNIEnv *env, jobject clazz, jstring jcmd) {
	jstring value = NULL;
	if (systemmixService == NULL || jcmd == NULL) {
		throw_NullPointerException(env, "systemmix service has not start, or shell cmd is null!");
	}
	const char *cmd = env->GetStringUTFChars(jcmd, NULL);
	char *cvalue = new char[MAX_BUFFER_SIZE];
	if (cvalue == NULL) {
		env->ReleaseStringUTFChars(jcmd, cmd);
		throw_NullPointerException(env, "runshell_native() fail to allocate memory for value");
	}

	int ret = systemmixService->runShell(cmd, cvalue);
	value = env->NewStringUTF(cvalue);
	if (value == NULL) {
		ALOGE("Fail in creating java string with %s", cvalue);
	}
	delete[] cvalue;
	env->ReleaseStringUTFChars(jcmd, cmd);
	return value;
}

static JNINativeMethod method_table[] = {
    { "nativeInit", "()V", (void*)init_native},
    { "nativeSetProperty", "(Ljava/lang/String;Ljava/lang/String;)I", (void*)setProperty_native },
    { "nativeGetProperty", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getProperty_native },
    { "nativeGetFileData", "([BILjava/lang/String;)I", (void*)getFileData_native },
    { "nativeMount", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;)I", (void*)mount_native },
    { "nativeUmount", "(Ljava/lang/String;)I", (void*)umount_native },
    { "nativeRunShell", "(Ljava/lang/String;)Ljava/lang/String;", (void*)runShell_native },
};

ISystemMixService.h

// defind max buffer size for runshell
#define MAX_BUFFER_SIZE 1024

ISystemMixService.cpp

enum {
	GET_PROPERTY = IBinder::FIRST_CALL_TRANSACTION,
	GET_FILEDATA,
	MOUNT,
	UMOUNT,
	RUN_SHELL,
	SET_PROPERTY = IBinder::LAST_CALL_TRANSACTION
};

	int runShell(const char *cmd, char *result) {
		if (DEBUG) {
			ALOGV("ISystemMixService::runShell()  cmd = %s", cmd);
		}
		Parcel data, reply;
		data.writeInterfaceToken(ISystemMixService::getInterfaceDescriptor());
		data.writeCString(cmd);

		remote()->transact(RUN_SHELL, data, &reply);
		int ret = reply.readInt32();
		ALOGD("ISystemMixService::runShell() ret = %d", ret);
		const char* rpy = reply.readCString();
		int len = (int)strlen(rpy);
		if(MAX_BUFFER_SIZE > len){
			strcpy(result, rpy);
		}else{
			strncpy(result, rpy, MAX_BUFFER_SIZE);
			result[MAX_BUFFER_SIZE - 1] = '\0';
		}
		//ALOGD("ISystemMixService::runShell() result=\n%s  rpy=\n%s", result, rpy);
		return ret;
	}

IMPLEMENT_META_INTERFACE(SystemMixService, "com.softwinner.ISystemMixService");

status_t BnSystemMixService::onTransact(
	uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags){

	switch(code){
	case SET_PROPERTY:{
		CHECK_INTERFACE(ISystemMixService, data, reply);
		const char *key = data.readCString();
		const char *value = data.readCString();
		reply->writeInt32(setProperty(key, value));
		return NO_ERROR;
	}break;
	case GET_PROPERTY:{
		CHECK_INTERFACE(ISystemMixService, data, reply);
		const char *key = data.readCString();
		char *value = new char[PROPERTY_VALUE_MAX];
		ALOGD("BnSystemMixService::getProperty() key=%s", key);
		int ret = getProperty(key, value);
		ALOGD("BnSystemMixService::getProperty() value=%s, ret=%d", value, ret);
		reply->writeInt32(ret);
		if(ret > 0){
			reply->writeCString(value);
		}
		delete[] value;
		return NO_ERROR;
	}break;
	case GET_FILEDATA:{
		CHECK_INTERFACE(ISystemMixService, data, reply);
		int count = data.readInt32();
		const char *filePath = data.readCString();
		int8_t *d = new int8_t[count];
		memset(d, 0, count * sizeof(int8_t));
		int ret = getFileData(d, count, filePath);
		ALOGD("BnSystemMixService::getFileData() read data is %s", d);
		reply->writeInt32(ret);
		reply->write(d, count * sizeof(int8_t));
		delete[] d;
		return NO_ERROR;
	}break;
	case MOUNT:{
		CHECK_INTERFACE(ISystemMixService, data, reply);
		const char *src = data.readCString();
		const char *mountpoint = data.readCString();
		const char *fs = data.readCString();
		unsigned int flag = data.readInt32();
		const char *options = data.readCString();
		reply->writeInt32(mountDev(src, mountpoint, fs, flag, options));
		return NO_ERROR;
	}break;
	case UMOUNT:{
		CHECK_INTERFACE(ISystemMixService, data, reply);
		const char *umountpoint = data.readCString();
		reply->writeInt32(umountDev(umountpoint));
		return NO_ERROR;
	}break;
	case RUN_SHELL: {
		CHECK_INTERFACE(ISystemMixService, data, reply);
		const char *cmd = data.readCString();
		char *result = new char[MAX_BUFFER_SIZE];
		memset(result, 0, MAX_BUFFER_SIZE);
		reply->writeInt32(runShell(cmd, result));
		//ALOGD("BnSystemMixService::runShell() result is %s", result);
		reply->writeCString(result);
		delete[] result;
		return NO_ERROR;
	}break;
	default:
		return BBinder::onTransact(code, data, reply, flags);
	}
}

SystemMixService.h

class SystemMixService:public BnSystemMixService{

public:
	static void instantiate();

	virtual int getProperty(const char *key, char *value);
	virtual int setProperty(const char *key, const char *value);
	virtual int getFileData(int8_t *data, int count, const char *filePath);
	virtual int mountDev(const char *src, const char *mountPoint, const char *fs, 
		unsigned int flag, const char *options);
	virtual int umountDev(const char *mountPoint);
	virtual int runShell(const char *cmd, char *result);

private:
	SystemMixService();
	virtual ~SystemMixService();
};

SystemMixService.cpp

int SystemMixService::runShell(const char *cmd, char *result) {
	if (DEBUG) {
		ALOGD("SystemMixService::runshell() cmd = %s", cmd);
	}

	char buffer[MAX_BUFFER_SIZE] = {0};
	FILE *fp;
	int ret = 0;

	// write command to buffer, %s 2>&1
	snprintf(buffer, sizeof(buffer), "%s\n", cmd);
	fp = popen(buffer, "r");
	if (fp != NULL){
		int remainder = MAX_BUFFER_SIZE;
		int len = 0;
		memset(buffer, 0, MAX_BUFFER_SIZE);
		while (fgets(buffer, MAX_BUFFER_SIZE, fp) != NULL){
			len = strlen(buffer);
			if (remainder > len){
				strcat(result, buffer);
				remainder -= len;
			}else{
				if (remainder > 1){
					strncat(result, buffer, remainder - 1);
					remainder = 0;
					break;
				}
			}
			//ALOGD("%s", buffer);
		}

		ret = pclose(fp);
		if (ret == -1){
			ALOGD("SystemMixService::runshell() pclose() failed\n");
			return -1;
		}

		if (WIFEXITED(ret)){
			ALOGD("SystemMixService::runshell() subprocess exited, exit code: %d\n", WEXITSTATUS(ret));
			if (0 != WEXITSTATUS(ret)){
				ALOGD("SystemMixService::runshell() command failed: %s\n", strerror(WEXITSTATUS(ret)));
				return WEXITSTATUS(ret);
			}
		}else{
			ALOGD("SystemMixService::runshell() subprocess exit failed\n");
			return -1;
		}
	}else{
		ALOGD("SystemMixService::runshell() popen() %s error\n", cmd);
		return -1;
	}

	//ALOGD("SystemMixService::runshell()  result = %s", result);
	return 0;
}

2. 启动文件说明

SystemMix接口实现的关键在于系统启动的时候以root身份加载了systemmix服务,详见android/device/softwinner/wing-common/init.rc文件:

# start system mix service
service property /system/bin/systemmixservice
	class main
	user root
	group root audio camera graphics inet net_bt net_bt_admin net_raw
	ioprio rt 4
	oneshot

3. app项目中的使用方法

runShell操作接口扩展完毕后重新编译Android系统,会在android/out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/目录下生成classes.jar,classes-full-debug.jar(带调试信息)文件。

拷贝classes.jar库文件到你AndroidStudio工程的app\libs目录下(如果没有libs文件夹,请新建一个),然后点击Project Structure,

在弹出的界面上选择Dependencies选项卡,

点击右侧+按钮,在弹出的菜单中选择 2 Jar dependency,

弹出Select Path界面,选择libs目录下的classes.jar文件,点击OK,将库文件加入到工程中。

如果提示库文件冲突,导入失败,请打开classes.jar包,删除有冲突的目录。可以只保留META-INF和com目录,删除com下除softwinner以外的其它目录。

在你的代码中引用SystemMix:import com.softwinner.SystemMix,在需要执行shell命令的地方使用SystemMix.runShell("shell command")即可,shell command为Android所支持的shell命令。

提示:可以使用am start启动app,使用pm install静默安装apk。

代码示例(显示根目录下的文件结构):

String cmd, str;
cmd = "ls -l /";
str = SystemMix.runShell(cmd);
Log.d(TAG, String.format("Run shell result:\n %s", str));

源码下载地址:https://download.csdn.net/download/kezunhb/10381901

免积分下载地址:https://www.etdev.net/thread-49-1-1.html

猜你喜欢

转载自blog.csdn.net/kezunhb/article/details/80138236
今日推荐