首页 > 其他分享 >记一次提取Shaft缓存

记一次提取Shaft缓存

时间:2023-04-12 17:55:47浏览次数:42  
标签:Shaft 缓存 提取 TEXT CREATE TABLE INTEGER NULL table

引子

aka,pixiv在境内没办法正常访问,于是有了一众第三方App能够实现直连pixiv的操作。通过绕过SNI审查的方式实现直连,其中我所提到的Shaft就是其中之一。
ref: https://github.com/CeuiLiSA/Pixiv-Shaft/issues/243
ref: https://github.com/CeuiLiSA/Pixiv-Shaft

背景介绍

有一台旧手机,里面有一些Shaft的缓存的图片和小说,解了bl锁但是没有root,没有办法直接获取缓存文件。

过程简介(仅包含原理) TL; DR

  1. Shaft是开源项目,可以直接阅读其源代码。
  2. 根据其源码,得知其使用了sqlite3来保存源网址和文件名的对照关系。缓存文件位于/data目录下、
  3. 数据库可以通过MIUI的备份和还原提取,但是缓存无法提取。
  4. 修改Shaft源代码,增加通过tar命令打包缓存到正常可访问目录的代码。
  5. 编译新版本apk,安装。
  6. 将第五步提取所得到的可读取tar文件导出。
    至此,缓存提取完成。

详解

1. 分析

分析其通过MIUI备份提取出的文件,可以得到多个数据库,如下:

  • google_app_measurement_local.db
  • roomDemo-database
    其中,roomDemo-database包含了文件名与网址的对应关系,较为重要。
    该数据库架构如下:
CREATE TABLE android_metadata (locale TEXT)
CREATE TABLE `feature_table` (`uuid` TEXT NOT NULL, `dateTime` INTEGER NOT NULL, `starType` TEXT NOT NULL, `userID` INTEGER NOT NULL, `illustID` INTEGER NOT NULL, `illustTitle` TEXT NOT NULL, `isShowToolbar` INTEGER NOT NULL, `name` TEXT NOT NULL, `dataType` TEXT NOT NULL, `illustJson` TEXT NOT NULL, `seriesId` INTEGER NOT NULL, PRIMARY KEY(`uuid`))
CREATE TABLE `illust_download_table` (`fileName` TEXT NOT NULL, `filePath` TEXT, `taskGson` TEXT, `illustGson` TEXT, `downloadTime` INTEGER NOT NULL, PRIMARY KEY(`fileName`))
CREATE TABLE `illust_downloading_table` (`fileName` TEXT NOT NULL, `uuid` TEXT, `taskGson` TEXT, PRIMARY KEY(`fileName`))
CREATE TABLE `illust_recmd_table` (`illustID` INTEGER NOT NULL, `illustJson` TEXT, `time` INTEGER NOT NULL, PRIMARY KEY(`illustID`))
CREATE TABLE `illust_table` (`illustID` INTEGER NOT NULL, `illustJson` TEXT, `time` INTEGER NOT NULL, `type` INTEGER NOT NULL, PRIMARY KEY(`illustID`))
CREATE TABLE room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)
CREATE TABLE `search_table` (`id` INTEGER NOT NULL, `keyword` TEXT, `searchTime` INTEGER NOT NULL, `searchType` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, PRIMARY KEY(`id`))
CREATE TABLE `tag_mute_table` (`id` INTEGER NOT NULL, `tagJson` TEXT, `searchTime` INTEGER NOT NULL, `type` INTEGER NOT NULL, PRIMARY KEY(`id`, `type`))
CREATE TABLE `upload_image_table` (`id` INTEGER NOT NULL, `fileName` TEXT, `filePath` TEXT, `uploadTime` INTEGER NOT NULL, PRIMARY KEY(`id`))
CREATE TABLE `user_table` (`userID` INTEGER NOT NULL, `userGson` TEXT, `loginTime` INTEGER NOT NULL, PRIMARY KEY(`userID`))
CREATE TABLE `uuid_list_table` (`uuid` TEXT NOT NULL, `listJson` TEXT, PRIMARY KEY(`uuid`))

对我们有用的是illust_recmd_table,里面包含了根据pixiv id向https://www.pixiv.net/ajax/illust/{id}发送请求所返回的JSON。(捂脸)我初三的时候写的代码质量好高 btw,我不确定这个URL现在还能不能使用。
水平有限,结合代码能从数据库里面得到的有效信息只有这些。

2. 提取

因为项目开源的时候包含了其签名文件,避免了很多不必要的麻烦。从build.gradle可以得知签名文件的信息。
签名文件自c227b1b3a3a494a1688a11f8070fc4b4f8d0d23d加入。

ref: https://github.com/CeuiLiSA/Pixiv-Shaft/blob/c227b1b3a3a494a1688a11f8070fc4b4f8d0d23d/keystore.jks
3.2.1

签名文件信息在e7e4ce0dd7e5c8a38506fc38077ba6ff9ce754ac加入。

...
    signingConfigs {
        release {
            storeFile file("../keystore.jks")
            storePassword "123456"
            keyAlias 'CeuiLiSA'
            keyPassword '123456'
        }
    }
...

ref: https://github.com/CeuiLiSA/Pixiv-Shaft/blob/e7e4ce0dd7e5c8a38506fc38077ba6ff9ce754ac/app/build.gradle
3.2.1

据此,我们可以利用这个签名文件对手上的包名相同的包签名进行直接安装。
另,有以下方法。须通过打开手机的USB调试选项并通过adb连接到手机。

pm uninstall -k <package name> # 移除软件包后保留数据和缓存目录。
pm install -r <apk file>       # 重新安装现有应用,并保留其数据。

另,pm install有以下选项值得注意。
-t:允许安装测试 APK。仅当您运行或调试了应用或者使用了 Android Studio 的 Build > Build APK 命令时,Gradle 才会生成测试 APK。如果是使用开发者预览版 SDK 构建的 APK,那么安装测试 APK 时必须在 install 命令中包含 -t 选项。
-d:允许版本代码降级。
ref: https://developer.android.google.cn/studio/command-line/adb?hl=zh-cn

因为缓存文件数量较大,所以我使用应用程序代码执行tar命令打包到可被用户直接读取的位置后复制到电脑上。
执行命令的kotlin方法如下。

fun shellExec(cmd: String?): Any {
    val mRuntime = Runtime.getRuntime() //执行命令的方法
    try {
        //Process中封装了返回的结果和执行错误的结果
        val mProcess = mRuntime.exec(cmd) //加入参数
        //使用BufferReader缓冲各个字符,实现高效读取
        //InputStreamReader将执行命令后得到的字节流数据转化为字符流
        //mProcess.getInputStream()获取命令执行后的的字节流结果
        val mReader = BufferedReader(InputStreamReader(mProcess.inputStream))
        //实例化一个字符缓冲区
        val mRespBuff = StringBuffer()
        //实例化并初始化一个大小为1024的字符缓冲区,char类型
        val buff = CharArray(1024)
        var ch = 0
        //read()方法读取内容到buff缓冲区中,大小为buff的大小,返回一个整型值,即内容的长度
        //如果长度不为null
        while (mReader.read(buff).also { ch = it } != -1) {
            //就将缓冲区buff的内容填进字符缓冲区
            mRespBuff.append(buff, 0, ch)
        }
        //结束缓冲
        mReader.close()
        Log.i("shell", "shellExec: $mRespBuff")
        //弹出结果
//            Log.i("shell", "执行命令: " + cmd + "执行成功");
        return mRespBuff
    } catch (e: IOException) {
        // 异常处理
        // TODO Auto-generated catch block
        e.printStackTrace()
        return "e:\n${e}"
    }
}

ref: https://github.com/char-46/PixShaftCacheSniffer/blob/master/app/src/main/java/ceui/lisa/pixiv/ShellExec.kt
需执行的命令如下。

tar cvvvf /path/to/target.tar ${PathUtils.getInternalAppCachePath()}

代码已上传至GitHub。
ref: https://github.com/char-46/PixShaftCacheSniffer

标签:Shaft,缓存,提取,TEXT,CREATE,TABLE,INTEGER,NULL,table
From: https://www.cnblogs.com/char46/p/17310645.html

相关文章

  • vue项目中发布新版本线上自动清缓存
    背景最近项目更新频繁,每次一更新客户都说还跟之前的一样。一查原因是因为客户没有清空浏览器的缓存。所以为了方便客户看到最新版本,开始调研再发布新版本后自动清理缓存。方案每次打包后的js和css都加上hash值后缀。当文件发生改变时,hash值也改变。这样就不会走缓存举个例子v......
  • web网站使用indexedDB缓存大数量案例
    前言及背景indexedDB是html5标准引入的web数据持久化方案之一,现代浏览器大多按照标准对其进行了实现,我在新的项目中用到它来作为持久化数据存储,由于最近在web端项目,每次web前端需要实时计算中间成果预计18G的中间过度数据,预计最终每次生成200M以上的结果数据,在此过程耗时30多......
  • 【Azure Redis 缓存】Azure Redis 4.0 被扫描到漏洞,如何修补呢?
    问题描述在安全级别要求高的公司中,任何系统都会进行安全扫描。比如Azure云上的Redis服务,也在扫描的范围中,最后发现Redis4.0存在以下漏洞:CVE-2019-10192:https://nvd.nist.gov/vuln/detail/CVE-2019-10192CVE-2019-10193:https://nvd.nist.gov/vuln/detail/CVE-2019-10193CVE-......
  • Redis之缓存穿透、缓存击穿、缓存雪崩及其解决方法
    原文地址:https://mp.weixin.qq.com/s?__biz=MzU2MDY0NDQwNQ==&mid=2247483949&idx=1&sn=6c643858d50cee4f9a2cac8ce838baff&chksm=fc05aa77cb722361dc3e3eb2b74f4d68308f7dc1d12e04dc4444a0d2595dc9e484ef318e93ce&scene=27 什么是缓存穿透缓存穿透是指查询一个缓存中和数据......
  • opencv-python 4.16. 基于GrabCut算法的交互式前景提取
    理论GrabCut算法由英国剑桥微软研究院的CarstenRother,VladimirKolmogorov和AndrewBlake设计。在他们的论文:"GrabCut":interactiveforegroundextractionusingiteratedgraphcuts中提出了一种基于最小用户交互的前景提取算法,其结果为GrabCut。从用户的角度来看,它是如何工......
  • nginx更新静态页面客户端缓存不刷新问题
    问题描述:频繁部署静态资源,nginx自带缓存未刷新通过ftp/sftp上传到nginx的静态页(尤其是打包好的单页应用),有可能遇到客户端缓存不刷新的问题,即使重启nginx都无效客户端浏览器也有缓存,一般关闭进程(手机清理,注意某些app光按返回键退回桌面是不会结束进程的),强制刷新网页等方法可以......
  • arcgis 提取高程到线
    提取高程到点:extractvaluestopoints提取高程到线呢?线->折点转点->extractvaluestopoints->点转线。for(){//遍历每一条线 //获取每条线的转折点 //提取转折点对应的高程 //点再转线,保存到新的shp中。}......
  • 【学习笔记】mybatis中的缓存介绍和使用
    文章目录介绍一级缓存和二级缓存让一级缓存失效的方法二级缓存的使用清空或者跳过二级缓存的3种方式介绍什么是缓存?缓存就是存储数据的一个地方(称作:Cache),当程序要读取数据时,会首先从缓存中获取,有则直接返回,否则从其他存储设备中获取,缓存最重要的一点就是从其内部获取数据的速度是......
  • python 提取字符中的数字
      一、isdigit()函数isdigit()函数是检测输入字符串是否只由数字组成。如果字符串只包含数字则返回True否则返回False。1234567891011dream="123456"print(dream.isdigit())#返回:True dream="123abc456"print(dream.isdigit())#返回:Fa......
  • 提取字符
    问题:提取汉字“线”和符号“_”之间的数字,及汉字“线”之前的字母(如果有) 函数公式解决:=MID(A1,FIND("线",A1)+1,FIND("_",A1)-FIND("线",A1)-1)&MIDB(A1,SEARCHB("?线",A1&"1线"),1)FIND("线",A1)找“线”在A1中的位置;FIND("_",A1)找“_”......