吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 22207|回复: 75
收起左侧

[原创] PC微信逆向:实现自动保存加密的聊天图片

  [复制链接]
鬼手56 发表于 2019-9-30 11:54
本帖最后由 鬼手56 于 2020-5-22 10:27 编辑

[TOC]

前言

本文基于anhkgg大佬的文章《微信PC端技术研究(2)-拿下语音》原文链接:https://bbs.pediy.com/thread-249274.htm

anhkgg大佬的这篇文章找到了保存语音消息的接口,这里直接给出相关特征码,方便定位(我使用的微信版本依旧是2.6.8.52)

偏移为0x30E326,下面的特征码

67E3E319    C745 FC 0100000>mov dword ptr ss:[ebp-0x4],0x1
67E3E320    FF77 34         push dword ptr ds:[edi+0x34]             ; 长度
67E3E323    FF77 30         push dword ptr ds:[edi+0x30]             ; 内容
67E3E326    E8 85F07300     call WeChatWi.6857D3B0
67E3E32B    8D85 58FFFFFF   lea eax,dword ptr ss:[ebp-0xA8]
67E3E331    50              push eax
67E3E332    E8 090E0000     call WeChatWi.67E3F140
C745 FC 01000000 FF77 ?? FF77 ?? E8 ???????? 8D85 ???????? 50 E8 ????????

基于保存语音的相关延伸

其实这个地方不单单有语音消息,还有图片消息,当我们发送一条图片消息时

1569046757602.png

[edi+0x30]的内容里面保存有这一次发送图片的相关数据,包括微信ID等一系列原始的数据。我们当然可以在这个地方写HOOK来保存图片,但是没有必要。因为这里的消息内容过多,处理起来相对会比较麻烦。

图片处理的相关流程

既然这个地方是最原始的消息内容,那么后面肯定会对消息进行相关处理。而且我们已经知道微信的接收的图片会用异或加密的方式保存到本地。那么我们不妨猜测一下图片相关的处理流程。

首先接收到原始的消息后,会对消息进行一系列的处理,其中就包括判断消息是否是图片。那么如果是图片则会取出图片数据,然后在内存中对图片进行加密。加密完成之后调用文件操作的API,写入加密后的图片到本地。

整个过程如图所示:

过程.png

自动保存图片相关思路

既然了解图片处理的流程,而且已经有了接收图片消息的call,那么我们就可以在接收到图片消息之后,在CreateFileW创建图片之前,找到对图片进行加密的算法和函数,将未加密前的图片保存出来。

实战保存聊天图片

1569047723267.png

在OD中找到保存语音的call,发送图片消息让程序断下的同时,对CreateFileW进行下断。之后F9运行

1569047797707.png

此时文件路径为xlog,这个明显不符合我们的要求,继续F9运行

1569048018955.png

一直找到图片路径带有Image关键字时,在创建图片
1569048058166.png

此时我们点击K显示堆栈,找到第一层返回地址,右键显示调用
1569048087833.png

当微信运行到这里的时候,图片加密已经完成,我们要在这个函数之前找到图片的加密算法,其实就在上面一点点的位置,鼠标稍微往上翻一下就能看到

1569050319138.png

找到这个地方之后清除剩下的所有的断点,只保留这一个

对保存图片call的相关分析

再次发送一张图片,程序断下。这段代码首先用循环的方式对图片进行加密,循环的次数即ecx的值,也就是图片的大小。其中有两个数据比较重要。

1569051562367.png

我们先在内存中查看[ebp-0x14]的内容,想要知道这段数据是什么其实很简单。先下CreateFileW断点

1569050500431.png

当CreateFileW断点断下后,执行到返回,查看打开的文件句柄

1569051713493.png

此时打开的图片句柄为0xF80,此时再下WriteFile断点
1569051751808.png

WriteFile断下后可以看到句柄为F80,写入的缓冲区地址为39FE820,而[ebp-0x14]的地址正好也是39FE820。也就是说[ebp-0x14]这个位置保存的是加密后的图片数据

回到之前的断点,再次发送一张图片,查看[ebp-0x4]的数据

1569051855952.png

此时[ebp-0x4]中保存了接收的图片,而ecx保存了图片的大小
1569050848908.png

1569050885428.png

这里借用PCHunter查看->进程内存,将这一段数据dump下来

1569050931136.png

问题就在于这里只是一张大小为4KB的缩略图,回到OD,再次按F9运行

1569051260987.png
断点断下,但是此时ecx的值变成0x5A140

1569051305995.png

用同样的方法dump下内存,大小为360KB,这个就是我们需要的原图了。

也就是说这个地方会端下来两次,第一次是缩略图,第二次才是我们要的原图。

代码实现保存聊天图片

示例代码如下:

void HookSaveImages()
{
        DWORD dwBaseAddress = (DWORD)GetModuleHandle(TEXT("WeChatWin.dll"));
        //需要hook的地址
        SaveImageAddress = dwBaseAddress + SaveImages;

        //跳回的地址
        SaveImageAddressBackAddress = SaveImageAddress + 5;

        //组装跳转数据
        BYTE jmpCode[5] = { 0 };
        jmpCode[0] = 0xE9;

        //计算偏移
        *(DWORD*)& jmpCode[1] = (DWORD)FnSaveImages - SaveImageAddress - 5;

        // 保存以前的属性用于还原
        DWORD OldProtext = 0;

        // 因为要往代码段写入数据,又因为代码段是不可写的,所以需要修改属性
        VirtualProtect((LPVOID)SaveImageAddress, 5, PAGE_EXECUTE_READWRITE, &OldProtext);

        //写入自己的代码
        memcpy((void*)SaveImageAddress, jmpCode, 5);

        // 执行完了操作之后需要进行还原
        VirtualProtect((LPVOID)SaveImageAddress, 5, OldProtext, &OldProtext);
}
__declspec(naked) void FnSaveImages()
{
        __asm
        {
                mov ebx, dword ptr ss : [ebp - 0x4];
                mov ImageData, ebx;
                mov ImageDataLen, ecx;
                pushad;
                pushfd;
        }

        //调用接收消息的函数
        FnSaveImagesCore();

        //恢复现场
        __asm
        {
                popfd
                popad
                //跳回被HOOK指令的下一条指令
                jmp SaveImageAddressBackAddress;
        }
}
void FnSaveImagesCore()
{
        //如果图片长度大于10KB则保存
        if (ImageDataLen >= 10240)
        {

                //获取临时文件夹目录
                char temppath[MAX_PATH] = { 0 };
                GetTempPathA(MAX_PATH, temppath);
                char imagedir[20] = { "WeChatRecordImages" };

                //拼接目录
                char WeChatExpressionsPath[MAX_PATH] = { 0 };
                sprintf_s(WeChatExpressionsPath, "%s%s\\", temppath, imagedir);
                //创建目录存放图片
                CreateDir(WeChatExpressionsPath);

                //保存图片
                CreateFileWithCurrentTime(WeChatExpressionsPath, (char*)".jpg", ImageData, ImageDataLen);
        }
}

实际效果

1.gif

免费评分

参与人数 25威望 +1 吾爱币 +35 热心值 +23 收起 理由
yosoul + 1 + 1 谢谢@Thanks!
monkey9981 + 1 + 1 谢谢@Thanks!
Hmily + 1 + 7 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
首席鉴婊师 + 1 这是高手
花慕兰 + 1 + 1 谢谢@Thanks!
lk19870906 + 1 我很赞同!
yjdh3344 + 1 热心回复!
心随梦动 + 1 我很赞同!
yi025 + 1 + 1 谢谢@Thanks!
Aug6thSml + 1 + 1 谢谢@Thanks!
回忆里的那个人 + 1 + 1 热心回复!
涛之雨 + 2 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
又红又专 + 1 + 1 很不错.... git上的人很多呀
wanfon + 1 + 1 感谢发布原创作品
Aries15123 + 1 + 1 我很赞同!
拿破仑骑乌龟 + 1 + 1 鼓励转贴优秀软件安全工具和文档!
hshcompass + 1 + 1 用心讨论,共获提升!
紫轩冰凌 + 2 + 1 谢谢@Thanks!
笙若 + 1 + 1 谢谢@Thanks!
gaosld + 1 + 1 用心讨论,共获提升!
liphily + 3 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
天空の幻像 + 1 + 1 防撤回带提示和多开会弄吗?
小糊涂虫 + 3 + 1 用心讨论,共获提升!
迷失自我 + 1 + 1 鼓励转贴优秀软件安全工具和文档!
无疆北月 + 1 + 1 热心回复!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

qwe956238 发表于 2021-6-28 23:42
为什么我那个点只断了一下,只能获取到缩略图,需要手动去点开微信的图片才会断第二下,什么原因呢?
yosoul 发表于 2020-8-3 10:09
zmb123 发表于 2020-7-30 09:59
你好,可以问下hook 地点是在什么地方呢?上面最后一张图片下面只说了到 [ebp-0x4] 是未加密的数据,怎样 ...

您好
我当时回复的时候 用的微信版本是 2.9.0.123  目前最新的版本 2.9.5.x 没有验证过有没有改动

按照 2.9.0.123版本 在OD里直接指导 wechatwin.dll 模块 然后直接 ctrl + b 然后搜索特征码
8b 75 ec 8b 5d fc
就能找到原文作者所说的 未加密的数据 hook点 然后直接下断 调试一次看看 应该就明白了吧
ShadowY 发表于 2019-9-30 12:00
tzxinqing 发表于 2019-9-30 12:18
逆向大佬,谢谢分享,虽然看不懂
一丝风 发表于 2019-9-30 12:22
厉害,虽然看不懂
airborne 发表于 2019-9-30 12:32
虽然看不懂,但还是谢谢分享
dywly 发表于 2019-9-30 12:44
看不懂什么
无疆北月 发表于 2019-9-30 12:45
虽然看不懂,但还是谢谢分享
L40vv4n 发表于 2019-9-30 12:47
虽然看不懂,但是可点赞!
飞鸟N 发表于 2019-9-30 12:49
谢谢分享
修行 发表于 2019-9-30 12:59
谢谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-16 00:29

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表