免ROOT调用Android系统隐藏接口教程
现在随着各个安卓手机厂商逐渐封闭Bootloader,致使很多手机用户都不能自行解锁BL进行刷机定制或者root删掉部分无用内置了,只能被迫用答辩。
在这种行情之下,有一部分软件在不通过root的方式下,依旧可以达到替代部分系统功能,比如应用静默安装、卸载,后台清理等功能。
1、分析Shizuku、黑域、AppManager软件工作原理
Shizuku工作原理:
可以看到这部分源码,它是需要通过引导用户在adb shell环境下执行一个脚本,来激活一个后台服务,从而实现调用系统服务的功能。
从这部分源码来看,它是通过调用app_process命令来执行该Java程序的,它里面也有main函数,这是Java程序入口。
public class ServiceStarter {
private static final String TAG = "ShizukuServiceStarter";
private static final String EXTRA_BINDER = "moe.shizuku.privileged.api.intent.extra.BINDER";
public static final String DEBUG_ARGS;
static {
int sdk = Build.VERSION.SDK_INT;
if (sdk >= 30) {
DEBUG_ARGS = "-Xcompiler-option" + " --debuggable" +
" -XjdwpProvider:adbconnection" +
" -XjdwpOptions:suspend=n,server=y";
} else if (sdk >= 28) {
DEBUG_ARGS = "-Xcompiler-option" + " --debuggable" +
" -XjdwpProvider:internal" +
" -XjdwpOptions:transport=dt_android_adb,suspend=n,server=y";
} else {
DEBUG_ARGS = "-Xcompiler-option" + " --debuggable" +
" -agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y";
}
}
private static final String USER_SERVICE_CMD_FORMAT = "(CLASSPATH='%s' %s%s /system/bin " +
"--nice-name='%s' moe.shizuku.starter.ServiceStarter " +
"--token='%s' --package='%s' --class='%s' --uid=%d%s)&";
// DeathRecipient will automatically be unlinked when all references to the
// binder is dropped, so we hold the reference here.
@SuppressWarnings("FieldCanBeLocal")
private static IBinder shizukuBinder;
public static String commandForUserService(String appProcess, String managerApkPath, String token, String packageName, String classname, String processNameSuffix, int callingUid, boolean debug) {
String processName = String.format("%s:%s", packageName, processNameSuffix);
return String.format(Locale.ENGLISH, USER_SERVICE_CMD_FORMAT,
managerApkPath, appProcess, debug ? (" " + DEBUG_ARGS) : "",
processName,
token, packageName, classname, callingUid, debug ? (" " + "--debug-name=" + processName) : "");
}
public static void main(String[] args) {
if (Looper.getMainLooper() == null) {
Looper.prepareMainLooper();
}
IBinder service;
String token;
UserService.setTag(TAG);
Pair<IBinder, String> result = UserService.create(args);
if (result == null) {
System.exit(1);
return;
}
service = result.first;
token = result.second;
if (!sendBinder(service, token)) {
System.exit(1);
}
Looper.loop();
System.exit(0);
Log.i(TAG, "service exited");
}
......
}
从这部分来看,可以更加清晰的了解到整个命令的参数拼接以及是如何执行的。
@Override
public String getUserServiceStartCmd(
UserServiceRecord record, String key, String token, String packageName,
String classname, String processNameSuffix, int callingUid, boolean use32Bits, boolean debug) {
String appProcess = "/system/bin/app_process";
if (use32Bits && new File("/system/bin/app_process32").exists()) {
appProcess = "/system/bin/app_process32";
}
return ServiceStarter.commandForUserService(
appProcess,
ShizukuService.getManagerApplicationInfo().sourceDir,
token, packageName, classname, processNameSuffix, callingUid, debug);
}
这部分是用来监听服务是否启动的,这部分简单看一下都能看懂。
package rikka.shizuku.server
import android.os.FileObserver
import android.util.Log
import java.io.File
import java.util.*
interface ApkChangedListener {
fun onApkChanged()
}
private val observers = Collections.synchronizedMap(HashMap<String, ApkChangedObserver>())
object ApkChangedObservers {
@JvmStatic
fun start(apkPath: String, listener: ApkChangedListener) {
// inotify watchs inode, if the there are still processes holds the file, DELTE_SELF will not be triggered
// so we need to watch the parent folder
val path = File(apkPath).parent!!
val observer = observers.getOrPut(path) {
ApkChangedObserver(path).apply {
startWatching()
}
}
observer.addListener(listener)
}
@JvmStatic
fun stop(listener: ApkChangedListener) {
val pathToRemove = mutableListOf<String>()
for ((path, observer) in observers) {
observer.removeListener(listener)
if (!observer.hasListeners()) {
pathToRemove.add(path)
}
}
for (path in pathToRemove) {
observers.remove(path)?.stopWatching()
}
}
}
class ApkChangedObserver(private val path: String) : FileObserver(path, DELETE) {
private val listeners = mutableSetOf<ApkChangedListener>()
fun addListener(listener: ApkChangedListener): Boolean {
return listeners.add(listener)
}
fun removeListener(listener: ApkChangedListener): Boolean {
return listeners.remove(listener)
}
fun hasListeners(): Boolean {
return listeners.isNotEmpty()
}
override fun onEvent(event: Int, path: String?) {
Log.d("ShizukuServer", "onEvent: ${eventToString(event)} $path")
if ((event and 0x00008000 /* IN_IGNORED */) != 0 || path == null) {
return
}
if (path == "base.apk") {
stopWatching()
ArrayList(listeners).forEach { it.onApkChanged() }
}
}
override fun startWatching() {
super.startWatching()
Log.d("ShizukuServer", "start watching $path")
}
override fun stopWatching() {
super.stopWatching()
Log.d("ShizukuServer", "stop watching $path")
}
}
private fun eventToString(event: Int): String {
val sb = StringBuilder()
if (event and FileObserver.ACCESS == FileObserver.ACCESS) {
sb.append("ACCESS").append(" | ")
}
if (event and FileObserver.MODIFY == FileObserver.MODIFY) {
sb.append("MODIFY").append(" | ")
}
if (event and FileObserver.ATTRIB == FileObserver.ATTRIB) {
sb.append("ATTRIB").append(" | ")
}
if (event and FileObserver.CLOSE_WRITE == FileObserver.CLOSE_WRITE) {
sb.append("CLOSE_WRITE").append(" | ")
}
if (event and FileObserver.CLOSE_NOWRITE == FileObserver.CLOSE_NOWRITE) {
sb.append("CLOSE_NOWRITE").append(" | ")
}
if (event and FileObserver.OPEN == FileObserver.OPEN) {
sb.append("OPEN").append(" | ")
}
if (event and FileObserver.MOVED_FROM == FileObserver.MOVED_FROM) {
sb.append("MOVED_FROM").append(" | ")
}
if (event and FileObserver.MOVED_TO == FileObserver.MOVED_TO) {
sb.append("MOVED_TO").append(" | ")
}
if (event and FileObserver.CREATE == FileObserver.CREATE) {
sb.append("CREATE").append(" | ")
}
if (event and FileObserver.DELETE == FileObserver.DELETE) {
sb.append("DELETE").append(" | ")
}
if (event and FileObserver.DELETE_SELF == FileObserver.DELETE_SELF) {
sb.append("DELETE_SELF").append(" | ")
}
if (event and FileObserver.MOVE_SELF == FileObserver.MOVE_SELF) {
sb.append("MOVE_SELF").append(" | ")
}
if (event and 0x00008000 == 0x00008000) {
sb.append("IN_IGNORED").append(" | ")
}
if (event and 0x40000000 == 0x40000000) {
sb.append("IN_ISDIR").append(" | ")
}
return if (sb.isNotEmpty()) {
sb.substring(0, sb.length - 3)
} else {
sb.toString()
}
}
黑阈工作原理:
由于黑域仅公开了早期版本源码,后面新版本,可能会有所不同。
直接看这部分源码实现,黑域是用c语言实现一个可执行文件,用来执行这个服务:me.piebridge.brevent.server.BreventServer
static int worker() {
FILE *file;
char *path;
char line[PATH_MAX];
char classpath[PATH_MAX];
char *arg[] = {"app_process", "/system/bin", "--nice-name=brevent_server",
"me.piebridge.brevent.server.BreventServer",
STR(SIGUSR1), BREVENT, start, NULL};
file = popen("pm path " BREVENT, "r");
if (file != NULL) {
memset(line, 0, PATH_MAX);
fgets(line, sizeof(line), file);
LOGD("line: %s", line);
rstrip(line);
pclose(file);
} else {
LOGE("can't exec pm path");
return -1;
}
path = strchr(line, ':');
if (path == NULL) {
LOGE("can't get path");
return -1;
}
path++;
LOGD("loader path: %s", path);
if (access(path, F_OK) == -1) {
LOGE("can't find loader: %s", path);
return -1;
}
pid_t pid = fork();
switch (pid) {
case -1:
LOGE("cannot fork");
return -1;
case 0:
break;
default:
return pid;
}
memset(classpath, 0, PATH_MAX);
sprintf(classpath, "CLASSPATH=%s", path);
putenv(classpath);
LOGD("start: %s", start);
return execvp(arg[0], arg);
}
通过激活这个服务进程,可以达到实现对手机上应用设置待机、打盹模式、强制终止等功能。
它通过引导用户使用adb shell执行该程序激活一个后台服务,然后调用系统隐藏接口来实现上述功能。
AppManager工作原理:
AppManager跟Rikka做的AppOps类似,这两个都是收费的,AppManager是开源的。
还是直接看源码,这个开源软件做的比较直接明了,没上面两个那么弯弯绕,通过shell脚本,可以更加直观的看到具体启动过程。
#!/system/bin/sh
# SPDX-License-Identifier: GPL-3.0-or-later
if [[ $# -lt 2 ]]; then
echo "USAGE: ./run_server.sh <path|port> <token>"
exit 1
fi
SERVER_NAME=
JAR_NAME=
JAR_PATH=
%ENV_VARS%
PORT="path:$1"
TOKEN=",token:$2"
ARGS="${PORT}${ARGS}${TOKEN}"
JAR_PACKAGE_NAME="io.github.muntashirakon.AppManager"
JAR_MAIN_CLASS="${JAR_PACKAGE_NAME}.server.ServerRunner"
TMP_PATH="/data/local/tmp"
EXEC_JAR_PATH=${TMP_PATH}/${JAR_NAME}
echo "Starting $SERVER_NAME..."
# Copy am.jar to executable directory
cp -f ${JAR_PATH} ${EXEC_JAR_PATH}
if [[ $? -ne 0 ]]; then
# Copy failed
echo "Error! Could not copy jar file to the executable directory."
exit 1
fi
# Fix permission
chmod 755 ${EXEC_JAR_PATH}
chown shell:shell ${EXEC_JAR_PATH}
# Debug log
echo "Jar path: $JAR_PATH"
echo "Args: $ARGS"
# Save jar path to environment variable
export CLASSPATH=${EXEC_JAR_PATH}
# Execute local server
exec app_process /system/bin --nice-name=${SERVER_NAME} ${JAR_MAIN_CLASS} "$ARGS" $@ &
if [[ $? -ne 0 ]]; then
# Start failed
echo "Error! Could not start local server."
exit 1
else
# Start success
echo "Local server has started."
echo "Use Ctrl+C to exit."
fi
# Print pid
ps | grep ${SERVER_NAME}
exit 0
核心部分:
# Execute local server
exec app_process /system/bin --nice-name=${SERVER_NAME} ${JAR_MAIN_CLASS} "$ARGS" $@ &
后边跟了个&符号,是后台执行的意思。
为什么要把这个也加入进来?因为它调用并封装了大量的隐藏接口,而且是支持Android4.x至Android14的,涵盖范围很广。
部分源码
ActivityManagerCompat.java#L159
@SuppressWarnings("deprecation")
public static IActivityManager getActivityManager() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
return IActivityManager.Stub.asInterface(ProxyBinder.getService(Context.ACTIVITY_SERVICE));
} else {
return ActivityManagerNative.asInterface(ProxyBinder.getService(Context.ACTIVITY_SERVICE));
}
}
@Nullable
public static IContentProvider getContentProviderExternal(String name, int userId, IBinder token, String tag)
throws RemoteException {
IActivityManager am = getActivityManager();
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
return am.getContentProviderExternal(name, userId, token, tag).provider;
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return ((android.app.ContentProviderHolder) am.getContentProviderExternal(name, userId, token)).provider;
} else {
return ((IActivityManager.ContentProviderHolder) am.getContentProviderExternal(name, userId, token)).provider;
}
} catch (NullPointerException e) {
return null;
}
}
2、分析Android Framework里面部分命令源码
通过上面对主流常用的免ROOT软件有了个简单的了解后,可以开始分析Android Framework源码了。
这里不对源码进行深度的分析,仅分析几个命令参数功能的源码以及各个Android版本的变动。
am命令
am force-stop pkgname
Android 7.0以下源码实现:
Am.java#L740
private void runForceStop() throws Exception {
int userId = UserHandle.USER_ALL;
String opt;
while ((opt=nextOption()) != null) {
if (opt.equals("--user")) {
userId = parseUserArg(nextArgRequired());
} else {
System.err.println("Error: Unknown option: " + opt);
return;
}
}
mAm.forceStopPackage(nextArgRequired(), userId);
}
Android8.0以上源码实现:
Am.java#L182
void runAmCmd(String[] args) throws AndroidException {
final MyShellCallback cb = new MyShellCallback();
try {
mAm.asBinder().shellCommand(FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
args, cb, new ResultReceiver(null) { });
} catch (RemoteException e) {
System.err.println(NO_SYSTEM_ERROR_CODE);
throw new AndroidException("Can't call activity manager; is the system running?");
} finally {
cb.mActive = false;
}
}
最终实现都是调用IActivityManager.forceStopPackage这个函数:
IActivityManager.aidl#L211
boolean clearApplicationUserData(in String packageName,
in IPackageDataObserver observer, int userId);
void forceStopPackage(in String packageName, int userId);//最终调用这个函数
boolean killPids(in int[] pids, in String reason, boolean secure);
IActivityManager对象获取
Android7.0以下源码:
ActivityManagerNative.java#L3047
/**
* Retrieve the system's default/global activity manager.
*/
static public IActivityManager getDefault() {
return gDefault.get();
}
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
Android8.0以上源码:
ActivityManager.java#L4216
/**
* @hide
*/
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
pm命令
pm uninstall packageName
Android4.4源码实现:
Pm.java#L1093
private void runUninstall() {
int unInstallFlags = PackageManager.DELETE_ALL_USERS;
String opt;
while ((opt=nextOption()) != null) {
if (opt.equals("-k")) {
unInstallFlags |= PackageManager.DELETE_KEEP_DATA;
} else {
System.err.println("Error: Unknown option: " + opt);
return;
}
}
String pkg = nextArg();
if (pkg == null) {
System.err.println("Error: no package specified");
showUsage();
return;
}
boolean result = deletePackage(pkg, unInstallFlags);
if (result) {
System.out.println("Success");
} else {
System.out.println("Failure");
}
}
private boolean deletePackage(String pkg, int unInstallFlags) {
PackageDeleteObserver obs = new PackageDeleteObserver();
try {
mPm.deletePackageAsUser(pkg, obs, UserHandle.USER_OWNER, unInstallFlags);
synchronized (obs) {
while (!obs.finished) {
try {
obs.wait();
} catch (InterruptedException e) {
}
}
}
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(PM_NOT_RUNNING_ERR);
}
return obs.result;
}
Android5.x源码实现:
Pm.java#L1322
private void runUninstall() throws RemoteException {
int flags = 0;
int userId = UserHandle.USER_ALL;
String opt;
while ((opt=nextOption()) != null) {
if (opt.equals("-k")) {
flags |= PackageManager.DELETE_KEEP_DATA;
} else if (opt.equals("--user")) {
String param = nextArg();
if (isNumber(param)) {
userId = Integer.parseInt(param);
} else {
showUsage();
System.err.println("Error: Invalid user: " + param);
return;
}
} else {
System.err.println("Error: Unknown option: " + opt);
return;
}
}
String pkg = nextArg();
if (pkg == null) {
System.err.println("Error: no package specified");
showUsage();
return;
}
if (userId == UserHandle.USER_ALL) {
userId = UserHandle.USER_OWNER;
flags |= PackageManager.DELETE_ALL_USERS;
} else {
PackageInfo info;
try {
info = mPm.getPackageInfo(pkg, 0, userId);
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(PM_NOT_RUNNING_ERR);
return;
}
if (info == null) {
System.err.println("Failure - not installed for " + userId);
return;
}
final boolean isSystem =
(info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
// If we are being asked to delete a system app for just one
// user set flag so it disables rather than reverting to system
// version of the app.
if (isSystem) {
flags |= PackageManager.DELETE_SYSTEM_APP;
}
}
final LocalIntentReceiver receiver = new LocalIntentReceiver();
mInstaller.uninstall(pkg, flags, receiver.getIntentSender(), userId);
final Intent result = receiver.getResult();
final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
PackageInstaller.STATUS_FAILURE);
if (status == PackageInstaller.STATUS_SUCCESS) {
System.out.println("Success");
} else {
Log.e(TAG, "Failure details: " + result.getExtras());
System.out.println("Failure ["
+ result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
}
}
Android6.x源码实现:
Pm.java#L1578
private int runUninstall() throws RemoteException {
int flags = 0;
int userId = UserHandle.USER_ALL;
String opt;
while ((opt=nextOption()) != null) {
if (opt.equals("-k")) {
flags |= PackageManager.DELETE_KEEP_DATA;
} else if (opt.equals("--user")) {
String param = nextArg();
if (isNumber(param)) {
userId = Integer.parseInt(param);
} else {
showUsage();
System.err.println("Error: Invalid user: " + param);
return 1;
}
} else {
System.err.println("Error: Unknown option: " + opt);
return 1;
}
}
String pkg = nextArg();
if (pkg == null) {
System.err.println("Error: no package specified");
showUsage();
return 1;
}
if (userId == UserHandle.USER_ALL) {
userId = UserHandle.USER_OWNER;
flags |= PackageManager.DELETE_ALL_USERS;
} else {
PackageInfo info;
try {
info = mPm.getPackageInfo(pkg, 0, userId);
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(PM_NOT_RUNNING_ERR);
return 1;
}
if (info == null) {
System.err.println("Failure - not installed for " + userId);
return 1;
}
final boolean isSystem =
(info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
// If we are being asked to delete a system app for just one
// user set flag so it disables rather than reverting to system
// version of the app.
if (isSystem) {
flags |= PackageManager.DELETE_SYSTEM_APP;
}
}
final LocalIntentReceiver receiver = new LocalIntentReceiver();
mInstaller.uninstall(pkg, null /* callerPackageName */, flags,
receiver.getIntentSender(), userId);
final Intent result = receiver.getResult();
final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
PackageInstaller.STATUS_FAILURE);
if (status == PackageInstaller.STATUS_SUCCESS) {
System.out.println("Success");
return 0;
} else {
Log.e(TAG, "Failure details: " + result.getExtras());
System.err.println("Failure ["
+ result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
return 1;
}
}
Android7以上源码实现:
Pm.java#L643
private int runUninstall() {
return runShellCommand("package", mArgs);
}
Android10以后,源码位置变更了。
PackageManagerShellCommand.java#L1701
private int runUninstall() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
int flags = 0;
int userId = UserHandle.USER_ALL;
long versionCode = PackageManager.VERSION_CODE_HIGHEST;
String opt;
while ((opt = getNextOption()) != null) {
switch (opt) {
case "-k":
flags |= PackageManager.DELETE_KEEP_DATA;
break;
case "--user":
userId = UserHandle.parseUserArg(getNextArgRequired());
break;
case "--versionCode":
versionCode = Long.parseLong(getNextArgRequired());
break;
default:
pw.println("Error: Unknown option: " + opt);
return 1;
}
}
final String packageName = getNextArg();
if (packageName == null) {
pw.println("Error: package name not specified");
return 1;
}
// if a split is specified, just remove it and not the whole package
final String splitName = getNextArg();
if (splitName != null) {
return runRemoveSplit(packageName, splitName);
}
userId = translateUserId(userId, true /*allowAll*/, "runUninstall");
final LocalIntentReceiver receiver = new LocalIntentReceiver();
PackageManagerInternal internal = LocalServices.getService(PackageManagerInternal.class);
if (internal.isApexPackage(packageName)) {
internal.uninstallApex(packageName, versionCode, userId, receiver.getIntentSender());
} else {
if (userId == UserHandle.USER_ALL) {
userId = UserHandle.USER_SYSTEM;
flags |= PackageManager.DELETE_ALL_USERS;
} else {
final PackageInfo info = mInterface.getPackageInfo(packageName,
PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId);
if (info == null) {
pw.println("Failure [not installed for " + userId + "]");
return 1;
}
final boolean isSystem =
(info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
// If we are being asked to delete a system app for just one
// user set flag so it disables rather than reverting to system
// version of the app.
if (isSystem) {
flags |= PackageManager.DELETE_SYSTEM_APP;
}
}
mInterface.getPackageInstaller().uninstall(new VersionedPackage(packageName,
versionCode), null /*callerPackageName*/, flags,
receiver.getIntentSender(), userId);
}
final Intent result = receiver.getResult();
final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
PackageInstaller.STATUS_FAILURE);
if (status == PackageInstaller.STATUS_SUCCESS) {
pw.println("Success");
return 0;
} else {
pw.println("Failure ["
+ result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
return 1;
}
}
最终实现:
Android4.4是调用IPackageManager.deletePackageAsUser函数实现
IPackageManager.aidl#L209
/**
* Delete a package for a specific user.
*
* @param packageName The fully qualified name of the package to delete.
* @param observer a callback to use to notify when the package deletion in finished.
* @param userId the id of the user for whom to delete the package
* @param flags - possible values: {@link #DONT_DELETE_DATA}
*/
void deletePackageAsUser(in String packageName, IPackageDeleteObserver observer,
int userId, int flags);
String getInstallerPackageName(in String packageName);
Android5以上是调用IPackageInstaller.uninstall函数实现
IPackageInstaller.aidl#L47
void unregisterCallback(IPackageInstallerCallback callback);
void uninstall(String packageName, int flags, in IntentSender statusReceiver, int userId);
这里就简单先分析了两个命令的功能,其它的就不一一分析了,由于篇幅的问题,只能交给你们自己去分析了。
3、Android跨进程通信简介
进程间通信简称IPC,英文为 Inter Process Communication ,在操作系统中,线程是CPU调度的基本单元,进程是操作系统调度的基本单元。在Android中一个进程一般指一个应用程序,可以包含多个线程,但最少有一个线程,在Android是主线程。
详情可以看别人写的
4、代码示例
通过上面对应用的分析与源码介绍,可以知道大概的流程了,如果我们要实现一个停止后台应用进程的功能,按照以往传统做法,我们除了在adb shell下调用
am force-stop pkgname
这个命令外,那么就只能通过获取root来调用这个命令实现了,而在以上几个项目成品源码的分析下,可以得知,通过adb shell调用app_process命令,启动一个Java后台进程,该Java进程即拥有adb shell一样的PID,它的级别与shell一样,也可调用am、pm、appops这些命令,且不再需要授权,只需要该服务在后台运行即可。
但是这样做的话,效率就太低下了,每一次运行一次命令就要重新创建一次shell对象,因为它即使是shell级别,但是调用命令,还是会调用一次app_process或者cmd命令,这跟上述的软件实现完全不一样,那么我们如果想要调用系统的api该怎么做?
通过以上软件的源码分析,我们可以知道,它们为了能够顺利调用系统api接口,在本地也创建了很多跟那些隐藏接口一样的文件跟对外接口函数,我们在分析Android Framework源码的时候,也发现了一个问题,里面那些接口,大部分都是@hide属性的,这部分我们是不可以调用的,但是adb shell环境下是可以使用的,这意味着,我们也仿照上述软件创建对应的文件跟编写对外接口函数名字,是否也可以达到同样效果?
实现思路:
- 编写一个Java应用程序入口
- 在这个Java应用程序里面,实现对应的功能
- 创建对应的系统接口文件
- 在Java应用程序里面编写一个socket服务端,用来接收提交过来的动态参数
- 编写一个客户端,用于提交对应的包名与其它动态参数
- 编写一个基于app_process的启动脚本,用于启动该Java程序
思路大概就这样,我这里已经写好了,就不给你们仔细分析了,你们直接看成品源码吧。
app_process启动的服务端源码:
package com.easymanager.mylife;
import android.os.Looper;
import android.os.Process;
import com.easymanager.core.entity.TransmissionEntity;
import com.easymanager.core.entity.easyManagerClientEntity;
import com.easymanager.core.server.easyManagerAPI;
import com.easymanager.core.utils.CMD;
import com.easymanager.enums.easyManagerEnums;
public class startAdbService {
private final static easyManagerAPI managerAPI = new easyManagerAPI();
public static void main(String[] args) {
// 利用looper让线程循环
Looper.prepareMainLooper();
new Thread(new Runnable() {
@Override
public void run() {
new adbService(new adbService.SocketListener() {
@Override
public CMD sendCMD(String cmdstr, boolean isRoot) {
if(cmdstr != null){
return new CMD(cmdstr,managerAPI.isRoot());
}
return null;
}
@Override
public Object doSomeThing(easyManagerClientEntity adbEntity2) {
if(adbEntity2 != null && adbEntity2.getCmdstr() == null){
TransmissionEntity entity = adbEntity2.getTransmissionEntity();
if(entity != null && entity.getRequestpkg() != null ){
if(managerAPI.getGrantUserState(entity.getRequestpkg()) == 0 || entity.getRequestpkg().equals("com.easymanager")){
switch (adbEntity2.getEasyManagerMode()){
case easyManagerEnums.IS_ROOT:
return managerAPI.isRoot();
case easyManagerEnums.IS_ADB:
return managerAPI.isADB();
case easyManagerEnums.GET_SYSTEM_SERVICE:
return managerAPI.getSystemService(entity.getPkgname());
case easyManagerEnums.KILL_PROCESS:
if(managerAPI.isRoot() || managerAPI.isADB()){
managerAPI.killpkg(entity.getPkgname());
}else {
return new String("not permisson use !! please active adb or root mode !!");
}
break;
case easyManagerEnums.SET_APPOPS:
if(managerAPI.isRoot() || managerAPI.isADB()){
managerAPI.setAppopsMode(entity);
}else {
return new String("not permisson use !! please active adb or root mode !!");
}
break;
case easyManagerEnums.INSTALL_APK:
if(managerAPI.isRoot() || managerAPI.isADB()){
managerAPI.installAPK(entity.getPkgname());
}else {
return new String("not permisson use !! please active adb or root mode !!");
}
break;
case easyManagerEnums.UNINSTALL_APK:
if(managerAPI.isRoot() || managerAPI.isADB()){
managerAPI.uninstallApp(entity.getPkgname());
}else {
return new String("not permisson use !! please active adb or root mode !!");
}
break;
case easyManagerEnums.SET_COMPONENT_OR_PACKAGE_ENABLE_STATE:
if(managerAPI.isRoot()){
managerAPI.setComponentOrPackageEnabledState(entity.getPkgname(),entity.getOpsmode());
}else {
return new String("not permisson use !! please active root mode !!");
}
break;
case easyManagerEnums.SET_PACKAGE_HIDE_STATE:
if(managerAPI.isRoot()){
managerAPI.setPackageHideState(entity.getPkgname(),entity.getOpsmode()==0);
}else {
return new String("not permisson use !! please active root mode !!");
}
break;
case easyManagerEnums.GET_SERVER_STATUS:
return true;
case easyManagerEnums.GET_GRANT_USERS:
return managerAPI.getGrantUsers();
}
}else {
if(adbEntity2.getEasyManagerMode() == easyManagerEnums.GRANT_USER){
managerAPI.requestGrantUserState(entity.getRequestpkg());
} else {
return new String("the " + entity.getRequestpkg() + " has not permission use the val .");
}
}
}else if(adbEntity2.getEasyManagerMode() == easyManagerEnums.IS_ADB){
return managerAPI.isADB();
}else if(adbEntity2.getEasyManagerMode() == easyManagerEnums.IS_ROOT){
return managerAPI.isRoot();
}else if(adbEntity2.getEasyManagerMode() == easyManagerEnums.GET_SERVER_STATUS){
return true;
}else {
return new String("please put requestpkg val .");
}
}
return new String(Process.myUid()+" --- " + Process.myUserHandle().hashCode());
}
});
}
}).start();
Looper.loop();
}
}
服务端源码:
package com.easymanager.mylife;
import com.easymanager.core.entity.easyManagerClientEntity;
import com.easymanager.core.entity.easyManagerServiceEntity;
import com.easymanager.core.utils.CMD;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class adbService {
public final static int PORT = 56997;
private SocketListener listener;
public adbService(SocketListener listener) {
this.listener = listener;
try {
// 利用ServerSocket类启动服务,然后指定一个端口
ServerSocket serverSocket = new ServerSocket(PORT);
System.out.println("easyManager adb server running " + PORT + " port");
ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(10);
// 新建一个线程池用来并发处理客户端的消息
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5,
10,
50000,
TimeUnit.MILLISECONDS,
queue
);
while (true) {
Socket socket = serverSocket.accept();
// 接收到新消息
executor.execute(new processMsg(socket));
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("SocketServer create Exception:" + e.toString());
}
}
class processMsg implements Runnable {
Socket socket;
CMD cmd;
Object o;
public processMsg(Socket s) {
socket = s;
}
public void run() {
try {
// 通过流读取内容
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
easyManagerClientEntity adbee2 = (easyManagerClientEntity) ois.readObject();
cmd = listener.sendCMD(adbee2.getCmdstr(),false);
o = listener.doSomeThing(adbee2);
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
easyManagerServiceEntity a = new easyManagerServiceEntity(cmd,o);
oos.writeObject(a);
oos.flush();
oos.close();
socket.close();
//需要实现appops的功能
} catch (Exception e) {
e.printStackTrace();
System.out.println("socket connection error:" + e.getStackTrace().toString());
}
}
}
public interface SocketListener{
CMD sendCMD(String cmdstr,boolean isRoot);
Object doSomeThing(easyManagerClientEntity adbEntity2);
}
}
客户端源码:
package com.easymanager.mylife;
import com.easymanager.core.entity.easyManagerClientEntity;
import com.easymanager.core.entity.easyManagerServiceEntity;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
public class adbClient {
private final int PORT = 56997;
private SocketListener listener;
private easyManagerServiceEntity adbEn;
public adbClient(easyManagerClientEntity adee2, SocketListener listener) {
this.listener = listener;
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Socket socket = new Socket();
ObjectOutputStream oos;
try {
socket.connect(new InetSocketAddress("127.0.0.1", PORT));
oos = new ObjectOutputStream(socket.getOutputStream());
// 发送指令
oos.writeObject(adee2);
oos.flush();
// 读取服务端返回
readServerData(socket,oos);
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private synchronized void readServerData(final Socket socket,ObjectOutputStream oos) {
try {
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
// cmd = (CMD)ois.readObject();
adbEn = (easyManagerServiceEntity) ois.readObject();
ois.close();
oos.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public easyManagerServiceEntity getAdbEntity(){
return adbEn;
}
public interface SocketListener {
easyManagerServiceEntity getAdbEntity(easyManagerServiceEntity adfb);
}
}
调用客户端源码的地方:
package com.easymanager.utils;
import android.content.Context;
import android.os.IBinder;
import com.easymanager.core.entity.TransmissionEntity;
import com.easymanager.core.entity.easyManagerClientEntity;
import com.easymanager.core.entity.easyManagerServiceEntity;
import com.easymanager.core.utils.CMD;
import com.easymanager.core.utils.FileUtils;
import com.easymanager.enums.easyManagerEnums;
import com.easymanager.mylife.adbClient;
import com.easymanager.mylife.startAdbService;
import java.util.HashMap;
public class easyManagerUtils {
public easyManagerServiceEntity putOptionOnServer(easyManagerClientEntity adben2){
adbClient ac = new adbClient(adben2, new adbClient.SocketListener() {
@Override
public easyManagerServiceEntity getAdbEntity(easyManagerServiceEntity adfb) {
return null;
}
});
return ac.getAdbEntity();
}
public void activeRoot(Context context) {
String cmdstr ="killall EAMADB\n" +
"exec app_process -Djava.class.path=\""+context.getApplicationInfo().sourceDir+"\" /system/bin --nice-name=EAMADB "+ startAdbService.class.getName()+" >>/dev/null 2>&1 &\n" +
"echo \"run EAMADB ok [root]\"";
CMD cmd = new CMD(cmdstr);
}
public void dead(boolean isRoot){
String cmdstr = "killall EAMADB;";
if(isROOT() || isADB()){
try {
runCMD(cmdstr);
}catch (Exception e){
}
}
if(isRoot){
new CMD(cmdstr);
}
}
public boolean isADB(){
easyManagerClientEntity adben2 = new easyManagerClientEntity(null,null,easyManagerEnums.IS_ADB);
return checkBool(adben2);
}
public boolean isROOT(){
easyManagerClientEntity adben2 = new easyManagerClientEntity(null,null,easyManagerEnums.IS_ROOT);
return checkBool(adben2);
}
public IBinder getSystemServer(String serverName,Context context){
easyManagerClientEntity adben2 = new easyManagerClientEntity(null,new TransmissionEntity(serverName,null,context.getPackageName(),0),easyManagerEnums.GET_SYSTEM_SERVICE);
return (IBinder) putOptionOnServer(adben2).getObject();
}
public boolean checkBool(easyManagerClientEntity adben2){
easyManagerServiceEntity serviceEntity = putOptionOnServer(adben2);
boolean a = false;
try{
a = (boolean) serviceEntity.getObject();
}catch (Exception e){
e.printStackTrace();
}
return a;
}
public CMD runCMD(String cmdstr){
easyManagerClientEntity adben2 = new easyManagerClientEntity(cmdstr,null,-1);
easyManagerServiceEntity eee = putOptionOnServer(adben2);
return eee.getCmd();
}
public void killpkg(TransmissionEntity entity){
easyManagerClientEntity adben2 = new easyManagerClientEntity(null,entity,easyManagerEnums.KILL_PROCESS);
easyManagerServiceEntity eee = putOptionOnServer(adben2);
}
public void setAppopsMode(TransmissionEntity entity){
easyManagerClientEntity adben2 = new easyManagerClientEntity(null,entity,easyManagerEnums.SET_APPOPS);
easyManagerServiceEntity eee = putOptionOnServer(adben2);
}
public void setAppopsModeCore(TransmissionEntity entity){
easyManagerClientEntity adben2 = new easyManagerClientEntity(null,entity,easyManagerEnums.SET_APPOPS_CORE);
easyManagerServiceEntity eee = putOptionOnServer(adben2);
}
public void installAPK(TransmissionEntity entity){
easyManagerClientEntity adben2 = new easyManagerClientEntity(null,entity,easyManagerEnums.INSTALL_APK);
easyManagerServiceEntity eee = putOptionOnServer(adben2);
}
public void uninstallAPK(TransmissionEntity entity){
easyManagerClientEntity adben2 = new easyManagerClientEntity(null,entity,easyManagerEnums.UNINSTALL_APK);
easyManagerServiceEntity eee = putOptionOnServer(adben2);
}
public void setComponentOrPackageEnabledState(TransmissionEntity entity){
easyManagerClientEntity adben2 = new easyManagerClientEntity(null,entity,easyManagerEnums.SET_COMPONENT_OR_PACKAGE_ENABLE_STATE);
easyManagerServiceEntity eee = putOptionOnServer(adben2);
}
public void setPackageHideState(TransmissionEntity entity){
easyManagerClientEntity adben2 = new easyManagerClientEntity(null,entity,easyManagerEnums.SET_PACKAGE_HIDE_STATE);
easyManagerServiceEntity eee = putOptionOnServer(adben2);
}
public void setFirewallState(int pkguid , boolean isEnable){
String disable_cmd = "iptables -I OUTPUT -m owner --uid-owner "+pkguid+" -j DROP";
String enable_cmd = "iptables -I OUTPUT -m owner --uid-owner "+pkguid+" -j ACCEPT";
CMD cmd = runCMD(isEnable ? enable_cmd : disable_cmd);
}
public void dumpAPK(String apksourcepath,String outpath){
FileUtils fu = new FileUtils();
fu.copyFile(apksourcepath,outpath);
}
public Boolean getServerStatus(){
TransmissionEntity entity = new TransmissionEntity(null,null,null,1);
easyManagerClientEntity adben2 = new easyManagerClientEntity(null,entity,easyManagerEnums.GET_SERVER_STATUS);
return checkBool(adben2);
}
}
所有实现的功能,都需要在服务端里面调用跟执行才能生效。
5、demo项目开源地址
完整项目开源地址
对应的开发工具包SDK
SDK成品下载地址
项目成品安装包下载地址