背景 想做frida持久化hook,通过搜索资料发现gadget可以。
这是官方的资料:https://frida.re/docs/gadget/
这里有详细的使用方法,比如反编译apk插入so文件,如果这个app没有so库,就需要自己在application自己加代码load so,但是这些都不是我现在需要的操作。
我既然修改的是AOSP,在app加载的时候加载so就行了。
经过搜索资料发现了这个博客:http://zhuoyue360.com/crack/78.html
这里面应该学习了某个大佬的课程然后分享出来的,经过分析和实践验证了博客中的一部分内容是可行的,有一些重要的部分是没有的,但是主干思路有了之后剩下的部分有盼头了。
实践与验证 环境:
AOSP 10 r41
pixel 3
Ubuntu 18.04
修改app的启动流程,在启动期间去加载so。 在frameworks/base/core/java/android/app/ActivityThread.java
中的handleBindApplication
方法中,找到Application
创建之前的位置,经过我的测试onCreate
之后加载so也可以的!加入如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 String curPkgName = data.appInfo.packageName;int curUid = Process.myUid();if (curUid > 10000 ) { Persist.LOGD("curPkgName: " + curPkgName + " curUid: " + curUid); Boolean isPersist = Persist.isEnablePersist(curPkgName); Persist.LOGD("isPersist: " + isPersist); if (isPersist) { if (Persist.doXiaojianbangPersist(appContext, curPkgName)){ Persist.LOGD("doXiaojianbangPersist is ok" ); }else { Persist.LOGD("doXiaojianbangPersist failed" ); }; } }
这阶段主要是判断app是否需要持久化hook,不是所有的app都需要这样,正常流程。
下面是Persist
这个类的加入了,其实这个类加入到系统编译不需要这么麻烦,直接在ActivityThread.java
的同级目录下创建这个类就行了,整个流程这个类其实就是给ActivityThread.java
使用的,如果按照上面博客的操作还得给这个类配置白名单。
在编译阶段复制so到system/lib下 在/frameworks/base/cmds/
目录下创建一个自己的目录,把GitHub上面下载的gadget的so文件放到目录中。
下载地址:https://github.com/frida/frida/releases随便下载一个版本,我习惯用12.x版本或者14.x版本。高版本有bug不考虑了。
下载好之后放进去Ubuntu解压xz文件
这样就得到了so文件了,arm和arm64都下载回来。
添加复制so的脚本
1 2 3 4 5 6 7 //源码根目录下,在打开这个文件 /build/make/target/product/handheld_system.mk //找到PRODUCT_COPY_FILES地方 //myfrida这个目录就是你创建的,你可以随便写自己的。 PRODUCT_COPY_FILES += \ frameworks/base/cmds/myfrida/frida-gadget-14.2.18-android-arm.so:$(TARGET_COPY_OUT_SYSTEM)/lib/myfrida.so \ frameworks/base/cmds/myfrida/frida-gadget-14.2.18-android-arm64.so:$(TARGET_COPY_OUT_SYSTEM)/lib64/myfrida.so
加入这个之后,保存文件,编译刷机之后怎么验证是否成功了呢。
adb shell进入system/lib和system/lib64目录下查看是否存在myfrida.so,这个命名不要和里面的so重名,不然就挂了。
如果你已经做到这一步了,你会发现没有什么用,app启动的时候其实没有注入so的,上面的判断是否持久化没有成立的。
1 Boolean isPersist = Persist.isEnablePersist(curPkgName);
这里是没有成立的。分析这个方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 import android.content.Context;import android.util.Log;import android.os.Process;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import org.json.JSONObject;public class Persist { public static final String SO_NAME = "libxiaojianbang.so" ; public static final String SO_CONFIG_NAME = "libxiaojianbang.config.so" ; public static final String LIB32_DIR = "/system/lib" ; public static final String LIB64_DIR = "/system/lib64" ; public static final String SETTINGS_DIR = "/data/system/xsettings/xiaojianbang/persist" ; public static final String ENABLE_PERSIST_FILE_NAME = "xiaojianbang_persist" ; public static final String CONFIG_JS_DIR = "/data/system/xsettings/xiaojianbang/jscfg" ; public static final String CONFIG_JS_FILE_NAME = "config.js" ; public static final String TAG_NAME = "xiaojianbang_persist" ; public static void LOGD (String msg) { Log.d(TAG_NAME, msg); } private static boolean saveFile (String filePath, String textMsg) { try { FileOutputStream fileOutputStream = new FileOutputStream (filePath); fileOutputStream.write(textMsg.getBytes("utf-8" )); fileOutputStream.flush(); fileOutputStream.close(); return true ; } catch (Exception e) { e.printStackTrace(); } return false ; } private static boolean copyFile (File srcFile, File dstFile) { try { FileInputStream fileInputStream = new FileInputStream (srcFile); FileOutputStream fileOutputStream = new FileOutputStream (dstFile); byte [] data = new byte [16 * 1024 ]; int len = -1 ; while ((len = fileInputStream.read(data)) != -1 ) { fileOutputStream.write(data,0 , len); fileOutputStream.flush(); } fileInputStream.close(); fileOutputStream.close(); return true ; } catch (Exception e) { e.printStackTrace(); } return false ; } public static boolean isEnablePersist (String pkgName) { File enableFile = new File (SETTINGS_DIR, pkgName + File.separator + ENABLE_PERSIST_FILE_NAME); return enableFile.exists(); } private static File getConfigJSPath (String pkgName) { return new File (CONFIG_JS_DIR, pkgName + File.separator + CONFIG_JS_FILE_NAME); } private static File copyJSFile (Context context, String pkgName) { File srcJSFile = getConfigJSPath(pkgName); if (!srcJSFile.exists()) { LOGD("srcJSFile not exists" ); return null ; } File dstJSFile = new File (context.getFilesDir(), CONFIG_JS_FILE_NAME); boolean isCopyJSOk = copyFile(srcJSFile, dstJSFile); if (!isCopyJSOk){ LOGD("copyJSFile fail: " + srcJSFile + " -> " + dstJSFile); return null ; } return dstJSFile; } private static boolean genGadgetConfig (Context context, File dstJSFile) { JSONObject jsonObject = new JSONObject (); JSONObject childObj = new JSONObject (); try { childObj.put("type" , "script" ); childObj.put("path" , dstJSFile.toString()); jsonObject.put("interaction" , childObj); }catch (Exception e){ e.printStackTrace(); return false ; } String configFilePath = context.getFilesDir() + File.separator + SO_CONFIG_NAME; boolean isSaveOk = saveFile(configFilePath, jsonObject.toString()); if (!isSaveOk){ LOGD("saveFile fail: " + configFilePath); return false ; } return true ; } private static File copySoFile (Context context) { File srcSoFile = new File (LIB32_DIR, SO_NAME); if (Process.is64Bit()) { srcSoFile = new File (LIB64_DIR, SO_NAME); } if (!srcSoFile.exists()) { LOGD("srcSoFile not exists" ); return null ; } File dstSoFile = new File (context.getFilesDir(), SO_NAME); if (srcSoFile.length() != dstSoFile.length()) { boolean isCopyFileOk = copyFile(srcSoFile, dstSoFile); if (!isCopyFileOk){ LOGD("copySoFile fail: " + srcSoFile + " -> " + dstSoFile); return null ; } } return dstSoFile; } public static boolean doXiaojianbangPersist (Context context, String pkgName) { File dstJSFile = copyJSFile(context, pkgName); if (null == dstJSFile) return false ; if (!genGadgetConfig(context, dstJSFile)) return false ; File dstSoFile = copySoFile(context); if (null == dstSoFile) return false ; System.load(dstSoFile.toString()); return true ; } }
是否激活持久化是这样判断的:判断文件是否存在
1 /data/system/xsettings/xiaojianbang/persist/com.xiaojianbang.app/xiaojianbang_persist
这个文件是否存在。仔细发现我们的系统中并没有xsettings
这个目录,需要我们创建。
持久化相关的目录 /system/core/rootdir/init.rc
在手机启动的时候处理我们需要的目录。
在chown root radio /proc/cmdline
下面添加代码
1 2 3 4 mkdir /data/system/xsettings 0775 system system mkdir /data/system/xsettings/xiaojianbang 0775 system system mkdir /data/system/xsettings/xiaojianbang/persist 0775 system system mkdir /data/system/xsettings/xiaojianbang/jscfg 0775 system system
其实不一定要在chown root radio /proc/cmdline
下面添加代码,这里的操作无非是创建目录给权限。
只要mkdir /data/system
目录创建之后去执行上面的代码就行了,也就是先有父目录,后面就可以创建子目录了。
OK,事情都好像很顺利,开心的编译刷机,然后进入shell中创建目录,持久化文件,app启动的时候注入so成功了。
1 /data/system/xsettings/xiaojianbang/jscfg/pkgName/config.js
在这里写入hook脚本写成功了。
注意,这里是通过shell去做的,博客也没有说,我是这样先做注入测试的。
我们需要针对某个app进行持久化hook每次都这样手动进入shell去设置是有点麻烦的,能不能开发一个app来做这件事:
1:查看当前用户安装的app列表。
2:选择某个app配置js脚本。js脚本也可能多个,做列表来显示多个脚本,可以选择其中一个来hook。
3:是否激活持久化hook。
写app嘛,老本行了,说干就干。
编写管理脚本的app 需求已经明确了,开始撸码。
根据上面Persist
这个类的规则,我们想激活某个app进行持久化hook就需要在/data/system/xsettings/xiaojianbang/persist/pkgName/xiaojianbang_persist
这里有一个文件,举个例子:
包名是com.demo。就需要在/data/system/xsettings/xiaojianbang/persist
下创建目录com.demo
切在目录中创建一个文件,空文件就行。
最后是这样: /data/system/xsettings/xiaojianbang/persist/com.demo/xiaojianbang_persist
创建目录和创建文件都不难,这里会出问题,我们直接写app创建是没有权限的,普通用户的app无法操作系统目录。
那如果我们也是系统级app呢?这就到了AOSP内置apk的问题了。
我的哔站有视频:https://www.bilibili.com/video/BV1S14y1K7Ew
我的公众号:https://mp.weixin.qq.com/s/c9TT25UR5JCmTHFOXRVnvQ
这个管理的app是无so的,直接复制脚本就行了。签名使用platform
。
编译apk的时候需要在配置文件AndroidMainfest.xml
中加入android:sharedUserId="android.uid.system"
这样app有系统权限了。
一切都很美好~,创建目录,创建文件。。。Permission Denial
还是没有权限。。。
配置新增目录的SEPolicy 这里需要配置的内容就比较多了,下一篇给出全部配置代码。
欢迎关注公众号:黄大官AOSP