这篇文章上次修改于 591 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

无聊水

前言

在MIUI系统设置中,我们可以对第三方APP进行备份和还原,并且在新一点的系统中,备份的是完整数据,而不仅仅是应用

于是就想,是否有可能向备份压缩包添加文件,并且进行还原,以此实现向APP私有目录下添加文件

更进一步可以修改一些动态库或者配置,以此实现代码植入以及hook

结论在前:完全没有校验,除了apk本体,其他随便改了还原

不对,有一个恢复数据账号验证的设置,开启了会有账号验证,不过还是不影响修改数据

记录

先确定下备份功能在那个APP里面,虽然前面提到备份是在系统设置操作,但是有的时候设置里面也会通过调用其他APP,内嵌界面

所以需要确定具体代码在哪个APP里面,首先查找立即备份四个字

使用MT管理器提取设置,即com.android.settings,使用MT管理器查看提取出的APK,点击resources.arsc并使用Arsc++编辑器打开

搜索字符串立即备份

点击name=后面的部分,点击跳转,选择type-info

复制记录下id=后面的内容,这里是0x7f12040a

然后打开全部dex,去搜索这个id,有的时候使用name=后面的内容查找不一定找得到,可能会被一些优化手段去除

优先建议使用id查找,然后发现名字都是同一个,那么现在查找名字

演示.mp4

发现是com.android.settings.MiuiMasterClear,到这里也可以确定了,是在系统设置里面做的

现在把apk拿出来,用JEB分析

这里有一个遍历显示私有目录下文件情况的APP

然后停止APP,备份这个软件


备份文件夹下面有一个descript.xml,内容如下,看得出来没有什么签名信息在里面

{
    "jsonMsg":"",
    "bakVersion":"2",
    "brState":"3",
    "autoBackup":"false",
    "device":"xaga",
    "miuiVersion":"V14.0.3.0.TLOCNXM",
    "date":"1682838265747",
    "size":"12956734",
    "storageLeft":"77613150208",
    "supportReconnect":"true",
    "autoRetransferCnt":"0",
    "transRealCompletedSize":"0",
    "packages":{
        "package":{
            "packageName":"com.test.fgum",
            "feature":"102",
            "bakFile":"FGum(com.test.fgum).bak",
            "bakType":"2",
            "pkgSize":"14047744",
            "sdSize":"0",
            "state":"1",
            "completedSize":"12956734",
            "error":"0",
            "progType":"0",
            "bakFileSize":"12956734",
            "transingCompletedSize":"0",
            "transingTotalSize":"12955648",
            "transingSdCompletedSize":"0",
            "sectionSize":"0",
            "sendingIndex":"0"
        }
    },
    "filesModifyTime":""
}

另外就是FGum(com.test.fgum).bak文件,这个文件使用7z可以正常打开,但是我们查看文件头,发现并不是标准的压缩包

有一个_manifest文件,里面有很长一串内容,看起来这个很关键

直接修改了bak文件中的js文件内容,文件大小没有改变,然后替换回去,尝试进行还原

然后测试下,确实改变了,但是没有什么错误提示,也就是可以直接改文件,至于_manifest那个是啥,没有影响

不过还是想看看,结果发现设置里面并没有_manifest这个字符串的踪迹,是的备份实际上是另一个APP,即com.miui.backup

原来是就是tar打包?

很快就能定位到_manifest的代码了,如下,可以看到很长那一串是被备份APP的签名信息

所以完全没有什么校验嘛,只要不改APP其他的都可以动

private void writeAppManifest(PackageInfo pkg, File manifestFile) throws IOException {
    StringBuilder builder = new StringBuilder(0x1000);
    StringBuilderPrinter printer = new StringBuilderPrinter(builder);
    printer.println(Integer.toString(1));
    printer.println(pkg.packageName);
    printer.println(Integer.toString(pkg.versionCode));
    printer.println(Integer.toString(Build.VERSION.SDK_INT));
    String s = this.mContext.getPackageManager().getInstallerPackageName(pkg.packageName);
    printer.println((s == null ? "" : s));
    printer.println("0");
    if(pkg.signatures == null) {
        printer.println("0");
    }
    else {
        printer.println(Integer.toString(pkg.signatures.length));
        Signature[] arr_signature = pkg.signatures;
        for(int v = 0; v < arr_signature.length; ++v) {
            printer.println(arr_signature[v].toCharsString());
        }
    }

    FileOutputStream outstream = null;
    try {
        outstream = new FileOutputStream(manifestFile);
        outstream.write(builder.toString().getBytes());
    }
    catch(Throwable throwable0) {
        if(outstream != null) {
            outstream.close();
        }

        throw throwable0;
    }

    outstream.close();
}

实际的压缩打包代码,也就是把文件头去掉,后面就是正常的tar文件了:

private void buildFormattedFileSysApp(String pkg, int feature, File baseDir, File dst, File manifest, File store, BackupMeta meta) throws IOException, InterruptedException {
    ParcelFileDescriptor[] arr_parcelFileDescriptor1;
    DeflaterOutputStream deflaterOutputStream0;
    FileOutputStream out = null;
    ParcelFileDescriptor[] pipes = null;
    AtomicBoolean latch = new AtomicBoolean(false);
    try {
        out = new FileOutputStream(dst);
        out.write(("MIUI BACKUP\n2\n" + (pkg + "\n") + (feature + "\n") + "null\n" + "ANDROID BACKUP\n" + 2 + "\n1\n" + "none\n").getBytes("UTF-8"));
        deflaterOutputStream0 = new DeflaterOutputStream(out, new Deflater(9), true);
    }
    catch(Throwable throwable0) {
        goto label_44;
    }

如果文件头不去掉,将文件名后缀改为.tar,那么使用7z打开会出现错误提示:

还有一个问题是,这些文件夹之间的关系是什么,之间看可以指定a里面是apk本身,r里面是APP私有目录下的文件

备份了下QQ,发现更多,sp基本上是shared_prefs的东西,db则是数据库文件

不过这个也很好定位:

com.miui.backup.restore.FullBackup

部分关系如下:

TOKEN备份命名文件夹原始文件夹
CACHE_TREE_TOKENccache
DATABASE_TREE_TOKENdbdatabases
DEVICE_CACHE_TREE_TOKENd_cuser_de/cache
DEVICE_DATABASE_TREE_TOKENd_dbuser_de/databases
DEVICE_FILES_TREE_TOKENd_fuser_de/files
DEVICE_NO_BACKUP_TREE_TOKENd_nbuser_de/no_backup
DEVICE_ROOT_TREE_TOKENd_ruser_de
DEVICE_SHAREDPREFS_TREE_TOKENd_spuser_de/shared_prefs
FILES_TREE_TOKENffiles
NO_BACKUP_TREE_TOKENnbno_backup
ROOT_TREE_TOKENr./
SHAREDPREFS_TREE_TOKENspshared_prefs

所以我们通常只管向r这个文件夹修改替换文件就好了

总结

MIUI的备份文件没有做校验,完全可以修改或添加内容后还原

这样可以用来修改一些APP的东西,或者配置,以及修改so/jar等,最终实现hook