图形图像您现在的位置是:首页 > 博客日志 > 图形图像

解码解密微信电脑版image文件夹下缓存的用户图片 dat文件解码解密查看方法

<a href='mailto:'>微wx笑</a>的头像微wx笑 2021-07-28图形图像 2 0关键字: 解码  解密  微信  image  图片  dat  java  

在使用微信Mac版的时候,接收到的图片可以直接右键》打开文件所在位置就能查看图片,但是Windows版本的就不行了,虽然文件已经接收保存到本地了,但是却以“.dat”格式进行保存并且进行了加密处理,用图片浏览器打开也提示无法查看。这里就介绍一下使用Java语言对其进行批量解密的方法。

在使用微信Mac版的时候,接收到的图片可以直接右键》打开文件所在位置就能查看图片,但是Windows版本的就不行了,虽然文件已经接收保存到本地了,但是却以“.dat”格式进行保存并且进行了加密处理,用图片浏览器打开也提示无法查看。这里就介绍一下使用Java语言对其进行批量解密的方法。zus无知人生


zus无知人生

一、找到图片

Windows版本想到找到微信聊天记录中的图片,需要以下步骤:zus无知人生

1、设置

image.pngzus无知人生

2、文件管理,打开文件夹

image.pngzus无知人生

3、打开 FileStorage》Image 文件夹

image.pngzus无知人生

二、解密原理

1. 原理描述


zus无知人生

原理很简单,就是按字节对接收到的图片文件进行了异或处理保存为dat文件,查看时再解码,并且使用的加密代码几乎是一样的,只要我们弄到了加密的字节码,使用其对dat文件进行异或操作保存为png文件便可以查看了。zus无知人生


zus无知人生

2. 获取加密字节码

16进制编辑器 HEdit下载zus无知人生

链接: https://pan.baidu.com/s/1lcaXiWyL31oYhxMAMGAOiA 提取码: bqx6 复制这段内容后打开百度网盘手机App,操作更方便哦zus无知人生

image.pngzus无知人生


zus无知人生

将其中一个文件夹的dat文件通过16进制编辑器打开,记录其开头两个16进制的值,随机挑选一部分dat文件打开,查看开头两个16进制的值并对比,一般来说是一样的,这两个值是解密的关键。zus无知人生

3、文件头对比

加密的文件头部zus无知人生

image.pngzus无知人生

解密后的文件头部zus无知人生

image.pngzus无知人生

我这里开头的两个值几乎都是8C AB,同时我们了解到网络中传输的图片多为jpeg格式,而jpeg格式的图片开头两个16进制的值通常为FF D8,我们打开计算器,将这两个值异或一下,得到两个16进制的值,通常来说应该是一样的,那么这个16进制的值就是解码的关键了。zus无知人生

4、使用计算器计算加密字节码

打开计算器,查看菜单选中程序员、基本zus无知人生

image.pngzus无知人生

鼠标点击输入 8CAB,点击Xorzus无知人生

image.pngzus无知人生

再输入FFD8,点击=号,就可以等到结果了zus无知人生

image.pngzus无知人生

本机的结果是73,那么我们只需要将dat文件的所有数据都与73异或便可以得到解密数据了,同时把数据保存为png格式便可以使用看图软件直接查看了。zus无知人生

注意:每个人得到的值可能不一样。zus无知人生

注意:这里只是教大家怎么手动计算,后面代码里面已经可以通过程序自动计算了。zus无知人生


zus无知人生

三、批量执行解密

处理逻辑

1、 获取指定路径下的所有dat文件,进入for循环处理zus无知人生

2、按次序读入dat文件,获取加密字节码及扩展名,然后按byte对其数据与加密字节码进行异或zus无知人生

3、将异或后的数据保存下来,后缀改为对应的扩展名,输出到指定文件夹zus无知人生

解密代码:decode.java

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.util.Date;
/**
 * 微信电脑版image文件夹下缓存的用户图片dat文件解码解密类
 * @author lipw
 * @email admin@ivu4e.com
 * @site https://ivu4e.com/
 */
class decode
{
    public static void main(String[] args) {
        System.out.println("hello");
        // 使用时修改这里的路径就可以了
        BatchDecodeFileContent("D:/Documents/WeChat Files/wxid_/FileStorage/Image/2021-07", "D:/Documents/WeChat Files/wxid_/FileStorage/Image/decodeimg5");
    }

    /**
    * 批量对文件进行解密处理
    * @param    inputFileDir 要解密的文件夹
    * @param    outputFileDir 解密后保存在哪个文件夹
    */
    public static void BatchDecodeFileContent(String inputFileDir, String outputFileDir){
        File f = new File(inputFileDir);
        if (!f.exists() || !f.isDirectory()){
            System.out.println("要解密的文件夹不存在!");
        }
        
        if (!outputFileDir.endsWith("/")){
            outputFileDir = outputFileDir.concat("/");
        }
        File o = new File(outputFileDir);
        if (!o.exists()){
            try {
                System.out.println("解密后保存的文件夹不存在,尝试创建...");
                o.mkdirs();
            } catch (Exception e) {
                System.out.println("创建解密后保存的文件夹失败,程序终止执行");
                return;
            }
        }
        
        File[] fl = f.listFiles(new FilenameFilter(){
            @Override
            public boolean accept(File dir, String name) {
                if (name.endsWith(".dat")){
                    return true;
                }
                return false;
            }
        });
        System.out.println("共找到 " + fl.length + " 个 dat 文件");

        if (fl.length == 0){
            System.out.println("没有需要解码的文件");
        }
        long begin = new Date().getTime();
        System.out.println("====开始解码====time:" + begin);

        Bom bom;
        FileInputStream fis;
        File of;
        FileOutputStream fos;
        byte[] bs = new byte[1024 * 1024];
        int rl;
        for (File file : fl){
            System.out.println(file.getAbsolutePath());
            try {
                fis = new FileInputStream(file);
                rl = fis.read(bs);
                bom = getFileBom(bs);
                if (bom.getXorVal() == 0x00 || bom.getExtn() == null){
                    System.out.println("获取加密的字节码失败");
                    continue;
                }
                of = new File(outputFileDir + file.getName() + bom.getExtn());
                fos = new FileOutputStream(of);
                while(rl > 0){
                    for (int i = 0; i < rl; i++){
                        bs[i] = (byte)(bs[i] ^ bom.getXorVal());
                    }
                    fos.write(bs, 0, rl);
                    rl = fis.read(bs);
                }

                fis.close();
                fos.close();
            } catch (Exception e) {
                
            }
        }
        
        long end = new Date().getTime();
        System.out.println("decode2====" + fl.length + " 个dat文件解码完成====用时:" + (end - begin));
    }

    /**
     * 获取加密的字节码
     * @param buff 读取的文件的第一块,包含文件头的部分
     * @return
     */
    public static Bom getFileBom(byte[] buff){
        Bom bom = new Bom();
        if (buff.length < 2){
            return bom;
        }
        // jpeg
        if ((byte)(buff[0] ^ 0xFF) == (byte)(buff[1] ^ 0xD8)){
            bom.setXorVal((byte)(buff[0] ^ 0xFF));
            bom.setExtn(".jpeg");
             return bom;
        }
        // png
        if ((byte)(buff[0] ^ 0x89) == (byte)(buff[1] ^ 0x50)){ // Xor计算之后需要加上强制类型转换(byte),否则比较的时候会出现不相等的情况
             bom.setXorVal((byte)(buff[0] ^ 0x89));
             bom.setExtn(".png");
             return bom;
        }
        // bmp
        if ((byte)(buff[0] ^ 0x42) == (byte)(buff[1] ^ 0x4D)){
            bom.setXorVal((byte)(buff[0] ^ 0x42));
            bom.setExtn(".bmp");
             return bom;
        }
        // gif
        if ((byte)(buff[0] ^ 0x47) == (byte)(buff[1] ^ 0x49)){
            bom.setXorVal((byte)(buff[0] ^ 0x47));
            bom.setExtn(".gif");
             return bom;
        }
        // tif
        if ((byte)(buff[0] ^ 0x49) == (byte)(buff[1] ^ 0x49)){
            bom.setXorVal((byte)(buff[0] ^ 0x49));
            bom.setExtn(".tif");
             return bom;
        }
        System.out.printf("%x%x==%x%x", buff[0], buff[1],(buff[0] ^ 0x89),(buff[1] ^ 0x50));
        return bom;
    }

    // 文件头
    static class Bom {
        // 对文件加密解密使用的字节码
        byte xorVal = 0x00;
        // 文件扩展名
        String extn = null;
        
        public Bom() {

        }
        public byte getXorVal() {
            return xorVal;
        }
        public void setXorVal(byte xorVal) {
            this.xorVal = xorVal;
        }
        public String getExtn() {
            return extn;
        }
        public void setExtn(String extn) {
            this.extn = extn;
        }
        
    }
}

使用时注意:
zus无知人生

1、输入输出目录的替换zus无知人生

2、0x73是对 dat 文件中的字节与 FF、D8 进行异或运算得到的数字,具体计算方法参考下面的链接。zus无知人生

其它语言版本:

参考:https://blog.csdn.net/a386115360/article/details/103215560/ zus无知人生


zus无知人生

本文由 微wx笑 创作,采用 CC BY-NC 4.0 许可协议。 非商业性使用可自由转载、引用、甚至修改,但需署名作者且注明出处。

很赞哦! () 有话说 ()