前提
本文介绍的方案是在有安卓底层SDK源码的方案下实行的,若不具备这个条件的就不用往下继续看了,以免耽误你的时间。
写作目的
公司是做工程设备,运行的是安卓系统,系统使用是的user,系统上层的app都是预安装的。正常情况下,这样的app的是没法做一些特权操作的,如重启系统、安装软件、读写一些特殊文件夹的文件。为了解决这些问题,特意做了一个方案,使第三方的app也拥有了root权限。
实现思路
我们知道系统启动后会运行init程序,init程序会解析init.rc脚本,并执行init.rc中的命令,而init程序是拥有root权限的,那么如果写一个服务程序,将此服务程序放入到init.rc中,使其开机启动,并常驻与系统中,这样这个服务程序也将拥有了root权限。而我们的第三方app想要获取一些特权操作时,只需要把把操作指令发送给我们的服务程序,由这个服务程序去执行即可。
最终效果
Runtime.getRuntime().exec(“god reboot”); // 第三方app执行了系统重启命令
不难看出,这其实是用java调用了一个exec方法执行了一条脚本命令,而god是我底层的一个发送程序,意思是将god后方的命令发送给我底层的一个服务程序。底层的服务程序收到命令后,就会去执行它。
下面简单说下代码
发送方部分代码(god)
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include "common/nativeNetServer.h"
#include "common/Utils.h"
int main(int argc,char *argv[]){
char cmdstr[1024] = {0};
int i;
if(argc < 2){
return -1;
}
pNativeNetServerOps netServer = createNativeNetServer();
if(netServer == NULL){
return -1;
}
char tempStr[32] = {0};
for(i = 1;i<argc;i++){
sprintf(cmdstr,"%s %s",cmdstr,argv[i]);
}
LOGD("[jerry] %s\n",cmdstr);
return netServer->runScript(netServer,cmdstr,4);
}
static int runScript(struct NativeNetServerOps * ops, const char * str,int timeout) {
pNativeNetServer pthis = (pNativeNetServer) ops;
int ret =-1;
if (pthis == NULL || pthis->udpServer == NULL) {
return -1;
}
char recvbuf[1024] = {0};
MsgBody msg = UdpBuildMsg(NOT_ACK, CMD_RUN_SCRIPT, str, strlen(str) + 1);
ret = pthis->udpServer->write(pthis->udpServer, msg.buf, msg.len,
SERVER_IP_ADDR, SERVER_PORT);
if(ret < 0){
LOGE(" fail to write!");
goto fail0;
}
ret = pthis->udpServer->read(pthis->udpServer,recvbuf,sizeof(recvbuf),timeout*1000);
if(ret < 0){
LOGE(" fail to read!");
goto fail0;
}
pT_Comm_Head ackComm = &recvbuf;
LOGE("ackComm.dataPack.dataStart : %d\n",ackComm->dataPack.dataStart);
return ackComm->dataPack.dataStart;
fail0:
LOGE(" fail to runScript!");
return -1;
}
这里的通讯使用的是socket,当然你也可以用管道或者binder或其他进程间通讯方案
服务端部分代码
static void cmdHandle(void* arg){
pCmdArg cmdArg = arg;
if(cmdArg == NULL){
return;
}
pT_Comm_Head commHead = (pT_Comm_Head )cmdArg->data;
commHead->dataPack.dtatLen = ntohl(commHead->dataPack.dtatLen);
switch(commHead->cmd)
{
case CMD_CONNECT_SCRIPT:{
LOGD("CMD_CONNECT_SCRIPT\n");
commHead->ackType = ACK_OK;
udpServer->ack(udpServer,commHead,sizeof(T_Comm_Head),cmdArg->remoteInfo);
}break;
case CMD_RUN_SCRIPT:{
char runScript[1024] = {0};
int ret;
memcpy(runScript,&commHead->dataPack.dataStart,commHead->dataPack.dtatLen);
// LOGD("system_run :[ %s ] start\n",runScript);
ret = system(runScript);
commHead->ackType = ACK_OK;
commHead->dataPack.dataStart = ret;
udpServer->ack(udpServer,commHead,sizeof(T_Comm_Head),cmdArg->remoteInfo);
LOGD("system_run :[ %s ] end\n",runScript);
}
break;
case CMD_HEARTBEAT:{
char heartbeatStr[1024] = {0};
memcpy(heartbeatStr,&commHead->dataPack.dataStart,commHead->dataPack.dtatLen);
daemonServer->keepHeartBeat(daemonServer,heartbeatStr);
}
break;
default:
break;
}
if(cmdArg){
if(cmdArg->data){
free(cmdArg->data);
}
free(cmdArg);
}
}
执行脚本时使用的是system,当然也可以使用popen并把代码的执行结果返回给app。
init.rc文件
service nativeNetserver /system/bin/nativeNetserver
class main
user root
group root
后续
当然这只是众多方案中的一种,本人技术有限,目前只能想到这个方案,若有同行朋友还有其他解法或补充之处,交流一二简直再好不过,由于代码文件比较多,这里就不全部上传,若有需要的朋友可以联系我,我将毫无保留的分享给你。