源码
github地址:https://github.com/cctyl/decrypt-wechat-dat-img
https://www.123pan.com/s/ERbyVv-7dJJ3.html 提取码:vUcM
起因
翻看微信聊天记录的备份时,找到 WeChat Files\微信id\FileStorage\Image\日期
,本以为能看一下过去发的照片,结果打开看到是一些.dat
结尾的文件。
显然被加密。
一顿百度,发现了这个 吾爱论坛,大神们早就把加密算法解出来了。
软件界面
简单描述加密原理
微信会从0x00 到 0xff 这1byte 字节的取值范围中,找到一个值,然后使用这个值作为密码,对图片数据的每一个字节,使用这个秘钥进行异或操作,最后生成.dat 文件。
解码也很简单,只需要找到这个秘钥,然后对.dat 文件再异或一次,输出出去,就得到原本的文件了
文件后缀获取
因为文件全部转换为.dat后缀,现在想恢复原本后缀,可以根据文件头来进行解码。吾爱文章也已经给出常用文件头和后缀的对应关系,直接抄。
图形界面
解码代码很简单,但是命令行用起来很不方便,对普通用户来说非常不友好。
还是需要一个界面来进行操作。
最熟悉的就是vue+springboot啦。但是小小一个工具,居然还要启动一个http服务,还要启动一个浏览器,非常的丑陋。
最终还是选择了javaFx。
本身没什么功能,就是一个文件选择器,和一个日志输出窗口。摆放一下就可以了。
VBox vBox = new VBox();
HBox hBox = new HBox();
Scene scene = new Scene(vBox);
Button btOpen = new Button("选择微信存储文件夹");
btOpen.setPrefWidth(200);
btOpen.setPrefHeight(100);
Button stop = new Button("暂停");
stop.setPrefWidth(100);
stop.setPrefHeight(100);
HBox.setMargin(stop,new Insets(0,0,0,20));
hBox.getChildren().addAll(btOpen,stop);
hBox.setAlignment(Pos.CENTER);
TextArea console = new TextArea();
vBox.getChildren().addAll(hBox, console);
vBox.setAlignment(Pos.CENTER);
点击按钮触发文件夹选择事件,事件的回调会返回一个文件路径,这个拿着这个路径去扫描文件即可。
DirectoryChooser chooser = new DirectoryChooser();
chooser.setTitle("打开微信存储文件夹");
btOpen.setOnMouseClicked(e -> {
try {
File dir = chooser.showDialog(stage);
thread = DatUtil.start(dir.getAbsolutePath());
} catch (NullPointerException ex) {
LogTool.log("打开文件夹错误");
ex.printStackTrace();
}
});
异步操作问题
因为图片可能上千个,不能用ui线程去更新,所以启动了一个新的线程。而又要求这个线程可以随时被用户停止。
所以我们每解码一个文件,就判断一下标记,看是否为false。根据这个标记来退出。
注意共享数据的可见性问题。
示例如下:
private Thread innerThread;
/**
* 停止标记
*/
private volatile boolean stop = false;
public void start() {
innerThread.start();
}
private void run(List<T> list, Consumer<T> task,Consumer<Integer> onComplete) {
int i = 0;
for (T t : list) {
if (stop) {
LogTool.log("线程退出");
break;
}
try {
task.accept(t);
i++;
} catch (Exception e) {
e.printStackTrace();
//出现异常就终止
stop = true;
}
}
onComplete.accept(i);
}
/**
* 停止线程
*/
public void stop() {
stop = true;
innerThread.interrupt();//如果线程在休眠,就打断
LogTool.log(innerThread.getName()+"线程终止...." );
}
界面刷新过快问题
这个javaFx居然还不支持快速刷新,当日志快速输出时,会出现 瞎报错问题。只报一个nullPointer。
此时需要使用 Platform.runLater(() -> );
来进行ui的更新
可执行文件的打包
java做一个可执行文件还是挺麻烦的。毕竟面向普通用户,给一个jar,让人摸不着头脑。
想的两个方案:
- 打包为exe
- 给一个start.bat 去启动
第二个方案,使用bat启动,会弹一个黑窗口,丑陋。即使用vbs关闭,那最开始启动时还是要弹出。不好。
那就得打包为exe。市面上常见方案有:exe4j
, lanuch4j
。 其中exe4j 免费版,启动前要弹一个窗口,启动很缓慢,不采用。
使用launch4j,配置时,只需要配置一下启动类,jdk版本,jre路径即可,点击顶部小齿轮开始生成。
我在配置的时候发现,配了icon会报错,不知道什么原因。
jre问题
jdk并不是所有用户都会安装,本身也不像.net
,visual c++
一样方便安装和下载。我们使用时需要给用户提供一个java的运行时。
jdk8以上已经不会默认包含jre,需要自己生成,命令:bin\jlink.exe --module-path jmods --add-modules java.desktop --output jre
, 执行后会生成jre。
问题来了,jre 太大了!! 本身项目打成jar包,才10兆,jre200m! 这合理吗。。。
必须要精简一下,但是非常容易出错,我也就没试。
解决方案:直接拷现有的jre。比如as的,或者dbeaver的。尝试之后发现,dbeaver的jre才40m,且运行正常。
于是直接采用,方法exe文件的旁边,打包上传,最后执行即可。