【解密实战】简单的图片加密分析

查看 18|回复 1
作者:JiGuro   
最近在开发新模块,开发模块的过程中,也遇到了一些有意思的事情,拿出来和大家分享。
前提声明:本文没有任何破解方法,只探讨加解密技术。仅供学习交流,不提供成品,仅分享思路,严禁用于商业用途。本文内容有亿点长,请耐心观看


Screenshot_2025-10-02-09-23-02-510_com.kuaiduizuoye.scan.jpg (193.28 KB, 下载次数: 1)
下载附件
2025-10-3 18:21 上传

众所周知,某对一直有一个很恶心的功能,即查看解析时只能看图片,还不停的有各种广告冒出来。如果你想截屏或录屏?对不起,“涉及隐私内容无法截图”。想保存图片到相册?对不起,免费用户不给保存,VIP用户一天也只能保存5次


Screenshot_2025-10-02-09-22-06-803_com.kuaiduizuoye.scan.jpg (153.67 KB, 下载次数: 1)
下载附件
2025-10-3 18:25 上传

不知道是谁想出来这么恶心的功能的,VIP也只能保存5次,这未免也太坑了吧。虽然我不怎么使用这个软件,但鉴于广大学生群体的需求,我对某对展开了一系列研究。
今天就让我们梳理一下脉络,一步一步带你找到图片
既然会弹出弹窗,那我们无妨用老方法试一试。


Screenshot_2025-10-02-09-38-44-196_com.android.shell.jpg (164.83 KB, 下载次数: 0)
下载附件
2025-10-3 18:21 上传

打开算法助手,勾选模块作用域,对某对进行hook,设置如下:


68ddfebadd9a0.png (60.04 KB, 下载次数: 0)
下载附件
2025-10-3 18:26 上传

我们执行一遍之前保存图片的操作,再回到算法助手,发现算法助手并未抓到弹出的弹窗,只抓到了保存图片按钮的 onClick 。


Screenshot_2025-10-02-09-50-51-194_com.junge.algorithmAidePro.jpg (110.26 KB, 下载次数: 1)
下载附件
2025-10-3 18:20 上传

我们进入,看到回调类为:com.kuaiduizuoye.scan.activity.scan.activity.BookCompleteDetailsPictureBrowseActivity$2


Screenshot_2025-10-02-09-51-01-967_com.junge.algorithmAidePro.jpg (177.34 KB, 下载次数: 1)
下载附件
2025-10-3 18:20 上传

我们打开 MT管理器 ,定位到该类。


Screenshot_2025-10-02-10-01-36-179_bin.mt.plus.jpg (266.95 KB, 下载次数: 1)
下载附件
2025-10-3 18:20 上传

我们将类中 Smali 转成 Java 以便分析。
[Java] 纯文本查看 复制代码// 前面的导入省略...
// 可以发现这玩意是个匿名内部类,给查看图片页面的所有按钮统一收尸
public class BookCompleteDetailsPictureBrowseActivity$2 extends ad {
    // 宿主 Activity 的引用,方便回头调它的公共/私有方法
    final BookCompleteDetailsPictureBrowseActivity a;
    BookCompleteDetailsPictureBrowseActivity$2(BookCompleteDetailsPictureBrowseActivity host) {
        this.a = host;
    }
    // 所有按钮的点击事件都扔这儿,按 id 分流
    public void a(View view) {
            //其他按钮逻辑省略
            case 2131299884:      // 保存到相册,这次的关键
                StatisticsBase.onNlogStatEvent("GPU_014", "sourcely", "3");
                // 拉起家长模式校验,校验成功通过后回调真正保存
                new a(a).a(3, success -> {
                    if (success == 1) BookCompleteDetailsPictureBrowseActivity.c(a);
                });
                break;
    }
    // 下面的方法无关,省略...
从这里可以发现,我们并不能看见保存按钮回调的真正机制,反而倒是发现了其他功能的逻辑,但这与我们想要保存图片的逻辑无关。
就算我们赋值 VIP ,你会发现点击“保存”按钮后,变成了最开始展示的“会员每日可保存5张”的弹窗,且点击保存后,会提示你不是会员,无法保存。
分析到这里,很多人就应该要放弃了
但我总是说,一条路不通,要换条路走。
首先我们要知道,一个软件的图片加载方式只有两种:1、本地加载;2、网络加载。某对的加载方式显然是第二种。
我们完全可以通过网络抓包的方式,对某对进行抓包,从而获取该图片。
这里先为小白简要解释一下抓包的原理:
第一步,设置代理:将手机的网络流量(Wi-Fi或移动数据)导向一个中间节点,即手机本地的VPN服务,这也是抓包软件为啥要和你申请权限的原因。
第二步,拦截流量:抓包软件作为“中间人”,读取所有进出的网络数据包(HTTP/HTTPS/TCP/UDP等),并将其拦截,等待下一步操作。
第三步,解析内容:对数据包进行解码,记录并展示请求地址、头信息、参数、响应内容等。
第四步,HTTPS解密:通过安装自定义证书,抓包软件可解密HTTPS流量,查看加密内容(需用户手动信任证书)。
第五步,记录与展示:将上述信息以列表或详情形式呈现,供用户分析调试。若用户在之前设置了覆写,此时抓包软件就会替换数据包为之前已经设置好的。
第六步,返回软件:将原数据包或修改过的数据包进行封装伪装,最后返回给软件。
简而言之,抓包软件让手机流量“绕道”经过它,从而读取并记录网络通信内容。
了解这些后,我们就可以正确使用抓包软件了。
首先,我们使用 JustTrustMe++ 模块,对某对的 ssl 证书检查机制进行 hook ,使其能够信任我们的证书。


Screenshot_2025-10-02-10-11-20-833_com.android.shell.jpg (169.53 KB, 下载次数: 1)
下载附件
2025-10-3 18:20 上传

在做完这步之后,我们就可以开始抓包了。
这里使用大家熟知的 小黄鸟(HttpCancry) 进行抓包,打开小黄鸟,并设置目标软件为某对,如图:


Screenshot_2025-10-02-10-21-14-458_com.guoshi.httpcanary.jpg (66.46 KB, 下载次数: 1)
下载附件
2025-10-3 18:20 上传

我们点击右下角按钮,使小黄鸟进入小窗模式,并打开某对,回到之前的解析页面。


IMG_20251002_123207.jpg (326.66 KB, 下载次数: 0)
下载附件
2025-10-3 18:21 上传

在排除很多无关数据包后,我们可以很清楚的看到三张图片。经过比对,这就是前三张图片,且图片未被加密,可以通过链接直接下载。这样,我们确实可以得到想要的图片。


Screenshot_2025-10-02-10-27-16-544_com.guoshi.httpcanary.jpg (171.64 KB, 下载次数: 0)
下载附件
2025-10-3 18:20 上传



Screenshot_2025-10-02-10-27-23-758_com.guoshi.httpcanary.jpg (201.67 KB, 下载次数: 1)
下载附件
2025-10-3 18:20 上传

但是,我不仅仅满足于此。每次要使用模块,并用小黄鸟进行抓包,抓包完了之后还要寻找,非常麻烦。
于是,我准备寻找新思路。
我们先分析抓包得来的数据,能够发现:当我们点击解析的某一张图时,某对会先向 https://kd-book.cdnjtzy.com/scanimg/scan GET 这张图片和这张图片前后的两张图,总共是三张图。然后,当我们向右滑时,每滑动一下,某对都会 GET 下一张图之后再展示。然而我们可以通过实践得知,已经向数据包请求过的图像,即使返回之前的图像页,某对也不会向服务器二次请求,因此滑动时没有加载圆圈,也并无卡顿。
那么我们可以合理推想,当我们已经查看过一张图片时,某对一定会将其缓存在某目录,如果用户返回查看,就可以直接使用缓存图片,而不是重复请求浪费服务器资源。这是很多软件非常正常的做法。
所以现在有了思路,最大的问题是找出缓存文件夹在何处。由于这是软件的基础功能,所以不可能向用户要文件读写权限,缓存在公用目录。所以只有一种可能,某对将图片缓存到了 /data/user/0/com.kuaiduizuoye.scan//storage/emulated/0/Android/data/com.kuaiduizuoye.scan/ 两个私有目录下。
我们当然可以进入私有目录,直接查找后缀为 .jpg 的图片(由抓包数据可知)。我们在 /storage/emulated/0/Android/data/com.kuaiduizuoye.scan/ 目录中,搜索到了20多个结果,可以发现几张置灰的图片,非常可疑,且数量刚好是3。


Screenshot_2025-10-02-10-46-37-129_bin.mt.plus.jpg (345.11 KB, 下载次数: 0)
下载附件
2025-10-3 18:20 上传

我们定位到目录 /storage/emulated/0/Android/data/com.kuaiduizuoye.scan/files/image/ 目录里除了刚刚的3张图,还存在某对本身的许多图标,这就更能佐证缓存目录的猜想了。
我们打开图片查看,发现管理器提示“图片损坏”。


Screenshot_2025-10-02-10-48-43-848_bin.mt.plus.jpg (66.07 KB, 下载次数: 1)
下载附件
2025-10-3 18:20 上传

这又是怎么回事?
让我们再次测试,这是否确实是缓存目录?我们回到某对,从刚刚点击的图片开始逐个向左滑。发现,每向左滑一张图,目录里就多一张灰图,且每个灰图文件的大小和抓包得出的真实图片大小相差无几。
那么看来,这确实就是缓存目录了,这些损坏的图片也确实就是解析图片。那为什么他们会损坏呢?
有经验的同学到这里一眼就能看出来,这些图片肯定经过了什么特殊的处理,比如加密或是转译。


Screenshot_2025-10-02-11-27-30-930_bin.mt.plus.jpg (452.73 KB, 下载次数: 1)
下载附件
2025-10-3 18:21 上传

我们可以用 MT 的 16进制编辑器 对损坏图片文件的16进制进行查看,发现 图片文件头 为 FF D8 FF DB,这是常见的 jpeg 文件头,图片文件尾 为 FF D9 这也没什么问题,那理论来说这张图片就是可以被解析的。但问题是显示损坏,那么看来中间的数据信息被修改了,这需要进一步分析。


Screenshot_2025-10-02-11-14-39-911_bin.mt.plus.jpg (195.71 KB, 下载次数: 0)
下载附件
2025-10-3 18:21 上传

我们注意到,这些损坏图片的命名存在规律。图片以 xxxxxxxx_TRANSITION2.jpg 命名。我们可以到 dex 中搜索字符串 TRANSITION2 ,尝试找出加密函数。


Screenshot_2025-10-02-11-17-40-883_bin.mt.plus.jpg (291.84 KB, 下载次数: 1)
下载附件
2025-10-3 18:21 上传

只有一个结果,我们转成 Java 看看:
[Java] 纯文本查看 复制代码// 导入包省略...
public class v {
    public static File b(String str) {
        PatchProxyResult proxy = PatchProxy.proxy(new Object[]{str}, (Object) null, changeQuickRedirect, true, 15154, new Class[]{String.class}, File.class);
        if (proxy.isSupported) {
            return (File) proxy.result;
        }
        return new File(DirectoryManager.getDirectory(a), "" + TextUtil.md5(str) + "_TRANSITION2.jpg");
    }
}
代码大致意思是,根据输入的字符串 str ,生成一个缓存图片文件对象( .jpg ),命名方式是图像的 MD5 值,再以固定后缀 _TRANSITION2.jpg 结尾,用于进行图像过渡或加密。但方法中并没有提到具体的加密算法。
那么我们如何逆向算法从而恢复文件呢?没错,我们可以把抓包得到的图片和这些损坏的图片进行对比,最后得出图片间的差异。
我们选取成对的图片进行比对,先用 MT管理器 的文本对比功能,发现损坏的文件比原始文件在数据开始部分新增了一段未知数据,其余的数据都是完全一致的,这与我们之前的推断重合。


Screenshot_2025-10-02-10-57-11-414_bin.mt.plus.jpg (459.77 KB, 下载次数: 1)
下载附件
2025-10-3 18:21 上传

那么究竟新增了哪些数据呢?直接比对文本全是乱码,我们无法直观清晰的看出。
这时我们还是要用到 16进制 。MT 虽然有16进制编辑器,但还未添加双文件16进制的比较功能。
(如果 Bin 能读到,希望能添加这个功能,目前安卓端好像还没有出现 16进制 比较软件,连在线网页好像都没有undefined)
那我们只能自己手搓脚本进行比对了。这里我们使用简单方便的 Python (实际上是不会写别的undefined) 来进行比对:代码传送门 (https://share.weiyun.com/aPuC1ZY2)
关于环境配置问题,这里稍微提几句。
安卓端可以下载 TermuxZeroTermux,设置好了权限之后,安装 Python3 环境即可。
如果你不知道怎么设置和配置环境,这里强烈推荐 国光 大佬的 Termux 教程,当时我就是这样学会的,是目前我看到最系统最完整的 Termux 教程,而且完全免费,大佬牛逼!国光大佬教程链接:Termux 高级终端安装使用配置教程 (https://www.sqlsec.com/2018/05/termux.html),为方便各位学习,我也会将珍藏多年的 Pdf 离线版本放在文末,各位可以自由下载。
总之,配置好环境后,运行脚本,输入前后文件名,脚本就会自动比对16进制并输出结果。


Screenshot_2025-10-02-11-21-22-950_bin.mt.plus.jpg (265.95 KB, 下载次数: 1)
下载附件
2025-10-3 18:21 上传

照这样把三组文件比对完,我们就可以分析添加的数据了。


Screenshot_2025-10-02-11-13-00-288_com.termux.jpg (356 KB, 下载次数: 1)
下载附件
2025-10-3 18:21 上传

我们拿一份来举例:
这是损坏图片的部分16进制:
---------- 文件一(损坏图片) ----------
16 进制:
00000000:  FF D8 FF DB 00 84 00 08 06 06 07 06 05 08 07 07  |................|
00000010:  07 09 09 08 0A 0C 14 0D 0C 0B 0B 0C 19 12 13 0F  |................|
00000020:  14 1D 1A 1F 1E 1D 1A 1C 1C 20 24 2E 27 20 22 2C  |......... $.' ",|
00000030:  23 1C 1C 28 37 29 2C 30 31 34 34 34 1F 27 39 3D  |#..(7),01444.'9=|
00000040:  38 32 3C 2E 33 34 32 01 09 09 09 0C 0B 0C 18 0D  |82
以下是损坏图片和正常图片的对比结果:
---------- 对比结果 ----------
文件一 vs 文件二 块级差异:
  偏移 0x00000096-0x00000121 (文件一)  0x00000096-0x00000096 (文件二,损坏图片)  -> reduce
    文件一减少部分:
    00000096:  FF D8 FF DB 00 84 00 08 06 06 07 06 05 08 07 07  |................|
    000000A6:  07 09 09 08 0A 0C 14 0D 0C 0B 0B 0C 19 12 13 0F  |................|
    000000B6:  14 1D 1A 1F 1E 1D 1A 1C 1C 20 24 2E 27 20 22 2C  |......... $.' ",|
    000000C6:  23 1C 1C 28 37 29 2C 30 31 34 34 34 1F 27 39 3D  |#..(7),01444.'9=|
    000000D6:  38 32 3C 2E 33 34 32 01 09 09 09 0C 0B 0C 18 0D  |82 reduce
    文件一减少部分:
    00000122:  08 03 7B 02 D0 03 01 22 00 02 11 01 03 11 01 FF  |..{...."........|
  偏移 0x00000129-0x0000012E (文件一)  0x00000098-0x00000098 (文件二)  -> reduce
    文件一减少部分:
    00000129:  22 00 02 11 01 03 11 01 FF C4 01 A2 00 00 01 05  |"...............|

由于代码写的较烂,连续修改的地方输出时会中断,请各位谅解。
为了防止看不懂输出信息,这里也有必要普及一下 JPEG 编码协议的概念。
实际上,JPEG 文件就是许多“小盒子”摞起来
想象 JPEG 是一摞快递盒,每个盒子上都贴着标签(段标记,Marker),告诉你里面装的是啥:  
有的装“照片基本信息”(宽、高、颜色分量)、有的装“缩略图”、有的装“真正的压缩图像流”、有的装“注释”。  每个标签都是两字节,十六进制表示,固定以 `FF` 开头。


Screenshot_2025-10-02-11-45-20-599_com.moonshot.kimichat.png (135.54 KB, 下载次数: 0)
下载附件
2025-10-3 18:21 上传

假设有一张 JPEG 图片,你能看到这样的文件头:
"

1. `FF D8` —— 这是 SOI,永远打头,相当于“身份证照片页”。  
2. `FF E0` —— 这是 APP0 段标记。  
3. `00 10` —— 本段长度 16 字节(含长度本身占的 2 字节)。  
4. `4A 46 49 46 00` —— ASCII 就是 “JFIF\0”,说明它符合 JFIF 标准(最普遍的 JPEG 子格式)。  
5. 后面几个字节告诉你版本号、密度单位、缩略图尺寸等,不重要先跳过。
继续往下,你大概率还会见到:
`FF DB`(量化表)、`FF C0`(SOF0,里面藏着宽高)、`FF C4`(哈夫曼表)、`FF DA`(SOS,紧跟着就是真正的压缩流,直到文件尾巴)。最后两个字节一定是 `FF D9`(EOI),表示“照片到此结束”。
那么现在你懂了吗?通过分析图片比对信息,我们可以发现,这张图片存在两个 SOI。
偏移 0x0000: FF D8  ← 第一个 SOI,正常偏移 0x0090: FF D8  ← 又一个 SOI,绝对不应该出现。损坏图片前面(0x8C-0x8F)刚好是上一个 SOF0 段的末尾。也就是说,第一个帧的语法还没结束就被强行截断,导致结构错位。从 0x0090 开始,某对把同一张图的 DQT、SOF0、DHT、SOS 又原样复制了一份,最后导致图片解析错误。
所以,0x0090 处的第二个  FF D8  是非法的,应把它连同后面那份完全重复的 DQT/SOF/DHT/SOS 全部删掉,只保留 0x0000–0x008B 那一段,再在后面接上原来真正的 SOS 数据(0x02D8 开始),文件才能合法。
后续分析其他的损坏图片文件,其实它们都是这样的。知道了解决方法,那现在就可以来解决问题了。
我们在 16进制编辑器 中,尝试将多余的文件头删去,发现图片复原。


IMG_20251002_124207.jpg (316.87 KB, 下载次数: 1)
下载附件
2025-10-3 18:21 上传

在电脑上看,其实更加明显,这里使用的工具是 Beyond Compare 5


屏幕截图 2025-10-02 120625.png (377.54 KB, 下载次数: 0)
下载附件
2025-10-3 18:21 上传



屏幕截图 2025-10-02 120752.png (227.6 KB, 下载次数: 0)
下载附件
2025-10-3 18:21 上传

至此,整个逆向分析过程结束。
我们完全可以通过编写脚本或插件的方法,来还原某对加密的图片文件,从而达到保存图片至相册的效果。我自己写了一个 Python 小工具,名叫 kuai_jpg_recover ,支持命令行参数和互动,无需安装任何额外库即可使用。目前准备开源,待开源后我也会把仓库链接贴到本文。代码链接: https://share.weiyun.com/ioNt9OKR,大家也可以在文末下载。
这个功能也会集成在即将发布的 某对模块 中,请各位敬请期待
脚本演示:


Screenshot_2025-10-02-12-20-22-960_com.termux.jpg (250.52 KB, 下载次数: 1)
下载附件
2025-10-3 18:21 上传



Screenshot_2025-10-02-12-21-25-196_com.termux.jpg (485.62 KB, 下载次数: 1)
下载附件
2025-10-3 18:21 上传

解密前:


Screenshot_2025-10-02-12-18-47-839_bin.mt.plus.jpg (286.78 KB, 下载次数: 1)
下载附件
2025-10-3 18:21 上传

解密后:


Screenshot_2025-10-02-12-21-38-855_bin.mt.plus.jpg (483.18 KB, 下载次数: 1)
下载附件
2025-10-3 18:21 上传

可以看到,所有的图片均被解密成功。
感谢大家的阅读!虽然这种图片加密非常简单,但也是一次宝贵的经验。
我的模块预计再过几天才能完工,闲暇时先写个帖子分享一下自己的体验。
码字不易,点赞可有?
帖子内所有工具及原包都已放在下面,各位自取。
最后,恭祝各位国庆快乐!
Python工具 (kuai_jpg_recover和hexdiff):
https://jiguro.lanzouw.com/ivxzB37hc9od
Termux入门指南:
https://jiguro.lanzouw.com/iV55d37hca3i
原包和其他工具:
https://jiguro.lanzouw.com/iILwS37hc9mb

下载次数, 图片

buluo533   

感谢国庆更新的大佬,大佬国庆节快乐,又学习了
您需要登录后才可以回帖 登录 | 立即注册

返回顶部