主要内容:
frida-inject脚本持久化的方案
App开关文件目录以及脚本存储文件目录selinux配置
封装和超级Root权限后台进程通信的java Api接口以及封装访问脚本相关目录文件的Java接口
系统源码中添加App启动的时候执行frida-inject命令的逻辑代码
开发控制App实现对第三方App 持久化的配置
方案原理:
在系统源码App启动入口的地方,找一个合适的时机。将frida-inject组装执行附加的命令通过Socket连接发送到超级Root权限后台进程中执行,达到实现App启动就加载js脚本的功能。App启动入口加载时机我们选择ActivityThread.java中的handleBindApplication方法。以下是该方案的一个图示:
在该方案中,设计以下的文件目录用来判断App是否需要开启frida inject持久化以及加载的js文件路径。设计如下:
# 配置某一个App是否开启持久化功能,$PKG_NAME表示App的包名
#比如一个参考:/data/system/xsettings/frdinject/persist/com.android.jnidemo01/persist_fridainject
/data/system/xsettings/frdinject/persist/$PKG_NAME/persist_fridainject
#配置某一个App加载的js文件路径,$PKG_NAME表示App的包名
# /data/system/xsettings/frdinject/jscfg/com.android.jnidemo01/config.js
/data/system/xsettings/frdinject/jscfg/$PKG_NAME/config.js
在涉及以上的文件或者目录中,管控端App具有system权限,需要配置系统权限的App对以上文件或者目录读写的selinux权限。普通App需要对以上文件有读的权限,所以需要配置第三方App具有读以上文件的selinux权限。
(1).创建文件类型selinux标签
在如下文件中创建文件标签frdinject_data_file,文件列表:
system\sepolicy\public\file.te
system\sepolicy\prebuilts\api\29.0\public\file.te
在以上文件中添加如下内容,需要保证两个文件添加的内容一致。不然要编译错误。
# /data/system/xsettings/
type frdinject_data_file, file_type, data_file_type, core_data_file_type,mlstrustedobject;
(2).为文件目录关联frdinject_data_file标签
在如下文件中添加关联/data/system/xsettings目录selinux标签,文件列表:
system\sepolicy\prebuilts\api\29.0\private\file_contexts
system\sepolicy\private\file_contexts
在以上文件中添加如下内容,需要保证两个文件添加的内容一致。不然要编译错误。
/data/system/xsettings(/.*)? u:object_r:frdinject_data_file:s0
(3).配置App访问frdinject_data_file标签的权限
这个地方主要配置两种App。一种是system权限的配置端App。第二种是第三方App。
具有系统权限的配置端App selinux配置如下:
在如下文件中:
system\sepolicy\private\system_app.te
system\sepolicy\prebuilts\api\29.0\private\system_app.te
添加如下访问权限,添加之后请保持两个文件内容一致:
# add for accessing frdinject_data_file
allow system_app frdinject_data_file:dir { getattr setattr open read write remove_name create add_name search rmdir };
allow system_app frdinject_data_file:file { getattr setattr open read write create unlink };
第三方App配置selinux如下:
在以下文件中添加app访问文件夹的selinux策略。确保相对应的文件内容一致。
在以下untrusted_app_all.te文件:
system\sepolicy\private\untrusted_app_all.te
system\sepolicy\prebuilts\api\29.0\private\untrusted_app_all.te
添加内容如下:
# add for accessing frdinject_data_file
allow untrusted_app_all frdinject_data_file:dir { getattr open read search };
allow untrusted_app_all frdinject_data_file:file { getattr open read };
在以下untrusted_app_27.te文件:
system\sepolicy\private\untrusted_app_27.te
system\sepolicy\prebuilts\api\29.0\private\untrusted_app_27.te
在以下文件:
# add for accessing frdinject_data_file
allow untrusted_app_27 frdinject_data_file:dir { getattr open read search };
allow untrusted_app_27 frdinject_data_file:file { getattr open read };
在以下untrusted_app_25.te文件:
system\sepolicy\private\untrusted_app_25.te
system\sepolicy\prebuilts\api\29.0\private\untrusted_app_25.te
# add for accessing frdinject_data_file
allow untrusted_app_25 frdinject_data_file:dir { getattr open read search };
allow untrusted_app_25 frdinject_data_file:file { getattr open read };
在以下untrusted_app.te文件:
system\sepolicy\private\untrusted_app.te
system\sepolicy\prebuilts\api\29.0\private\untrusted_app.te
# add for accessing frdinject_data_file
allow untrusted_app frdinject_data_file:dir { getattr open read search };
allow untrusted_app frdinject_data_file:file { getattr open read };
该持久化实现中,使用了如下目录。
/data/system/xsettings/frdinject/persist
/data/system/xsettings/frdinject/jscfg
所以可以将该目录放在init进程启动的时候进行创建。由于init进程会解析init.rc文件执行配置的命令,所以可以根据init.rc创建文件夹的规则加入创建以上两个目录的操作。在system\core\rootdir\init.rc文件中添加内容如下:
# /data/system/xsettings/frdinject/persist
mkdir /data/system/xsettings 0775 system system
mkdir /data/system/xsettings/frdinject 0775 system system
mkdir /data/system/xsettings/frdinject/persist 0775 system system
mkdir /data/system/xsettings/frdinject/jscfg 0775 system system
在源码根目录中创建如下对应的目录:
frameworks\base\core\java\android\xfrd
在目录xfrd中将存放MgskSu模块的接口实现。具体实现如下:
package android.xfrd;import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
public class MgskSu {
public static String magiskSu(String cmd)
{
String retString="";
try {
//创建socket
String myCmd="do_cmd|"+cmd;
Socket mSocket = new Socket();
InetSocketAddress inetSocketAddress=new InetSocketAddress("127.0.0.1",11111);
mSocket.connect(inetSocketAddress);
BufferedWriter bufferedWriter=new BufferedWriter(new OutputStreamWriter(mSocket.getOutputStream()));
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
bufferedWriter.write(myCmd+"\r\n");
bufferedWriter.flush();
retString=bufferedReader.readLine();
bufferedReader.close();
bufferedWriter.close();
}catch (Exception eeeee)
{
eeeee.printStackTrace();
}
return retString;
}
}
在该方案中通过对应App的包名组装文件路径来识别是否存在来判断App是否需要启动就加载js文件。在MagiskSu.java同目录中创建FrdSettings.java模块文件。核心代码如下:
package android.xfrd;import android.system.Os;
import java.io.File;
public class FrdSettings {
static final String TAG=FrdSettings.class.getSimpleName();
private static final String SETTINGS_DIR="/data/system/xsettings/frdinject/persist";
private static final String CONFIG_JS_DIR="/data/system/xsettings/frdinject/jscfg";
public static final String PERSIST_FRIDA_INJECT="persist_fridainject";
public static final String PERSIST_FRIDA_GADGET="persist_fridagadget";
public static final String PERSIST_FRIDA_GUMJS="persist_fridagumjs";
//
public static boolean isAppJsPathExists(String pkgName){
File file=new File(CONFIG_JS_DIR+"/"+pkgName+"/config.js");
return file.exists();
}
public static String getAppJsPath(String pkgName)
{
File file=new File(CONFIG_JS_DIR+"/"+pkgName+"/config.js");
return file.getAbsolutePath();
}
/**********************判断是否开启持久化***************************/
//设置对应的App是否开启持久化
public static void setEnablePersistFridaInject(String pkgName,String methodType, boolean isEnable) {
File file = new File(SETTINGS_DIR);
if (!file.exists()) {
file.mkdirs();
try {
Os.chmod(file.getAbsolutePath(), 0775);
} catch (Exception eeee) {
}
}
File pkgFile = new File(file, pkgName);
if (!pkgFile.exists()) {
pkgFile.mkdirs();
try {
Os.chmod(pkgFile.getAbsolutePath(), 0775);
} catch (Exception eeee) {
eeee.printStackTrace();
}
}
File enableFile = new File(pkgFile, methodType);
if (isEnable) {
if (!enableFile.exists()) {
try {
enableFile.createNewFile();
//MyLog.d(TAG, "create file success");
Os.chmod(enableFile.getAbsolutePath(), 0775);
} catch (Exception eeee) {
//MyLog.d(TAG, "file create errror:" + enableFile.getAbsolutePath());
eeee.printStackTrace();
}
} else {
//MyLog.d(TAG, "file exists:" + enableFile.getAbsolutePath());
}
} else {
if (enableFile.exists()) {
enableFile.delete();
}
}
}
//判断app是否打开自动注入脚本功能
public static boolean isEnablePersistFrida(String pkgName,String methodType) {
File enableFile = new File(SETTINGS_DIR, pkgName + "/"+methodType);
return enableFile.exists();
}
}
在之前方案分析中已经找到了在frameworks\base\core\java\android\app\ActivityThread.java中的handleBindApplication方法中加入加载js的逻辑是一个比较不错的选择。在handleBindApplication中加入如下关键调用代码,实现App启动的时候就去加载执行js。核心关键代码如下:
String curPkgName = data.appInfo.packageName;
int mypid=Process.myPid();
int mytempUid=Process.myUid(); if(mytempUid>10000)
{
Boolean isB=FrdSettings.isEnablePersistFrida(curPkgName,FrdSettings.PERSIST_FRIDA_INJECT);
Log.d("FridaInject","curPkgName isEnableFridaInject:"+isB);
if(isB)
{
String jsPath=FrdSettings.getAppJsPath(curPkgName);
if(jsPath!=null)
{
//这个地方比较重要,不然App运行会奔溃。
//App中主线程中调用网络socket操作配置
android.os.StrictMode.ThreadPolicy policy = new android.os.StrictMode.ThreadPolicy.Builder().permitAll().build();
android.os.StrictMode.setThreadPolicy(policy);
String cmdString="myfridainjectarm64 -p "+mypid+" -s "+jsPath+" -e";
String retString=MgskSu.magiskSu(cmdString);
try{
Thread.sleep(50);
}catch(Exception eee)
{
}
Log.d("FridaInject","retString is "+retString);
}else{
Log.d("FridaInject","jsPath is null");
}
}else{
}
}
控制端App中对App是否持久化开关配置以及js路径配置使用的封装接口和以上系统源码中封装的FrdSettings.java是一样的封装。这个地方就不讲接口的封装了。只说一下几个关键的地方使用。
配置界面参考:
主要涉及的几个接口调用如下:
(1).打开/关闭第三方App加载js功能
调用了封装的接口:setEnablePersistFridaInject
(2).将选择的js文件复制到对应App的配置js目录
调用了封装的接口:copyJsFileToAppJsPath
(3).判断App当前是否配置了加载js功能
调用了封装的接口:isEnablePersistFrida
(4).获取App配置的js文件路径
置以及js路径配置使用的封装接口和以上系统源码中封装的FrdSettings.java是一样的封装。这个地方就不讲接口的封装了。只说一下几个关键的地方使用。
配置界面参考:
[外链图片转存中…(img-L6IhssD7-1667868558625)]
主要涉及的几个接口调用如下:
(1).打开/关闭第三方App加载js功能
调用了封装的接口:setEnablePersistFridaInject
(2).将选择的js文件复制到对应App的配置js目录
调用了封装的接口:copyJsFileToAppJsPath
(3).判断App当前是否配置了加载js功能
调用了封装的接口:isEnablePersistFrida
(4).获取App配置的js文件路径
调用了封装接口:getAppJsPath