获取、导出微信所有表情

前言

适用:PC端微信 2.6.8.51版本
发布于2019年8月25日
本文仅作技术研究

一直在想微信收藏的表情包为什么不能右键另存为到本地
像QQ一样多好,有时候想把微信的表情导入到QQ里面用也不行,多不方便

百度查了一下,都是找CustomEmotions目录下的文件,将后缀名改成gif,貌似已经被和谐了,现在都不管用了

于是就开始研究了一下,发现有个捷径可以将微信里面的收藏所有表情导出,甚至可以把聊天中对方发的表情存下来

开始步入正题

首先就是OD看一波,看看进程都加载了什么模块,能不能从中发现一些信息
在这里插入图片描述

看看加载模块
在这里插入图片描述
首先筛选出与表情相关,最有可能用到模块
WeChatResource.dll 应该是关于资源的,表情好像也跟资源有关吧
WeChatWin.dll 似乎是一个主模块!20多M!比exe还大,估计整个程序的框架主逻辑都在这里面
WXAMDecoder.dll 看名字是跟解密有关的,表情的缓存文件似乎是被加密过了,应该会跟这个有关

接下来开个监控软件,监控一下发表情的时候都有什么文件读写操作
在这里插入图片描述

你会发现微信会打开这些文件名为哈希值的文件,且没有后缀名
随便找一个文件,拖到十六进制里面看看
在这里插入图片描述
你会发现这些文件前面几个字节都是这个标识符:V1MMWX

很明显这些文件的数据都是经过加密的,应该是微信开发人员自定义的文件格式
这时候就可以想到,我们每发一个表情,微信就在本地生成对应的缓存文件,这个缓存文件可能是使用表情加密后生成

那么回到我们刚刚筛选出的那几个模块,其中有一个似乎跟加解密有关的模块:WXAMDecoder.dll

然后我们来看看这个模块都有写什么导出函数
在这里插入图片描述
看见这些导出函数名,是不是心里突然兴奋了一下?似乎还有几个跟图片有关的!
我们来筛选一下
WxAMFrame_delete 框架删除?沾不上关系
WxAMFrame_new 跟框架一对的似乎
wxam_dec_decode_buffer_4 解密buffer,这个有嫌疑!
wxam_dec_getWXGFInfo_4 取用微信gif信息,emmm,是有点远方亲戚关系
wxam_dec_get_option_4 这个应该不是了
wxam_dec_init_4 解密初始化,可以忽略
wxam_dec_isWXGF_4 是否微信的gif,有点远方亲戚关系
wxam_dec_rewind_buffer_4 这个看名字不像
wxam_dec_uninit_4 这个也不像
wxam_dec_wxam2picSeq_4 am转到pic?
wxam_dec_wxam2pic_4 am转到pic?
wxam_dec_wxpc2jpg_4 pc转到jpg?

好吧,有些看着像又不像,先不管了,全部打上断点试一下都会调哪些接口
然后发个表情,马上击中要害
在这里插入图片描述
经验告诉我,首先看堆栈里面传的都是什么
果然不出所料,根据前面几个字节判断,GIF89似乎是gif图片文件的标识符!
这么说第一个是储存表情buff的指针,第二个是size!
为了验证自己的猜想是否正确,来写个小程序把对应的buff dump出来看看是不是图片!
在这里插入图片描述
简单的用易语言写了2行代码,没错,就2行足矣,将目标进程的指定的内存地址数据dump出来并生成一个1.gif的文件
在这里插入图片描述

似乎是一张图片!打开看看

在这里插入图片描述

没错,就是我在微信里发的表情

在这里插入图片描述
瞬间明白,原来发表情会走这个接口:wxam_dec_isWXGF_4
看接口名字,似乎是判断是不是微信的gif?
于是我换了一个静态的表情发出去,也会调用这个接口,看来非gif也会调这个接口

后来发现一个问题,就是发过的表情,再发就不会调用这个接口了,估计是调用之前会检查一下缓存文件是否存在,如果存在就不调了

解决方法:

用OD调一下吧,估计也就几个判断和跳转的问题

WeChatWin.dll + 0x7DB79
在这里插入图片描述
没错就这里了,如果跳过的话就不会调用接口了,所以只要把它nop即可!

彩蛋

后面发现原来打开收藏表情的那个窗口,也会调用wxam_dec_isWXGF_4
就是这个窗口
在这里插入图片描述
然后可以把wxam_dec_isWXGF_4 接口勾上,全部都写出文件,就能把自己收藏的全部表情转存到本地了!

如果写出过的表情,也不会再调用wxam_dec_isWXGF_4 接口,还是用OD调一下,改一个跳转就可以了

WeChatWin.dll + 0x2841D6
在这里插入图片描述
改成imp就好了,每次打开收藏表情的窗口,都能调用wxam_dec_isWXGF_4 接口

成果

最后撸起袖子敲一把代码,写成了一个dll,注入到微信进程内即可获取、导出微信所有表情了
成品下载:https://pan.baidu.com/s/1fDVs_n01jGuXF9-QNC1a9Q
提取码: cb4d

运行微信,随便打开一个人的聊天框,然后运行 RemoteInject.exe
在这里插入图片描述
注入成功后弹出提示框,转存的表情会放入微信运行目录下的GetWeChatPic文件夹

在这里插入图片描述

图片的名称使用了哈希值命名
在这里插入图片描述

源代码:

使用VS2015以上编译

RemoteInject.exe
// RemoteInject.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "windows.h"
#include "atlstr.h"
#include <TlHelp32.h>


char* GetProgramDll()
{
	static char exeFullPath[MAX_PATH] = { 0 }; // Full path
	char *nWeak;

	GetModuleFileNameA(NULL, exeFullPath, MAX_PATH);
	nWeak = strrchr(exeFullPath, '\\');

	memcpy(nWeak + 1, "GetWeChatPic.dll", strlen("GetWeChatPic.dll"));

	return exeFullPath;
}


DWORD GetProcessPid(CString nProcessName)
{
	PROCESSENTRY32 nPT;
	nPT.dwSize = sizeof(nPT);
	HANDLE nSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

	BOOL nRet = Process32First(nSnapShot, &nPT);
	while (nRet)
	{
		if (nProcessName.MakeLower() == CString(nPT.szExeFile).MakeLower())
		{
			return nPT.th32ProcessID;
		}
		nRet = Process32Next(nSnapShot, &nPT);
	}
	return 0;
}


int main()
{	
	printf("适用:PC端微信 2.6.8.51版本\r\n更新与2019年8月25日\r\n");

	DWORD nPid = GetProcessPid("wechat.exe");
	HANDLE nHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, nPid);

	printf("进程ID:%d  -  进程句柄:%d\r\n", nPid, nHandle);


	CHAR *DllPath = GetProgramDll();
	int nLen = strlen(DllPath)+1;
	LPVOID pBuf = VirtualAllocEx(nHandle, NULL, nLen, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
	if (!pBuf)
	{
		printf("申请内存失败!\r\n");		
		getchar();
		return 0;
	}

	if (!WriteProcessMemory(nHandle, pBuf, DllPath, nLen, 0))
	{
		printf("写入内存失败!\r\n");
		getchar();
		return 0;

	}

	HANDLE hRemoteThread = CreateRemoteThread(nHandle, NULL, NULL,(LPTHREAD_START_ROUTINE)LoadLibraryA, pBuf, 0, 0);

	WaitForSingleObject(hRemoteThread, -1);

	CloseHandle(hRemoteThread);

	VirtualFreeEx(nHandle, pBuf, 0, MEM_FREE);

	printf("注入完成!\r\n");
	getchar();
    return 0;
}
GetWeChatPic.dll
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include "stdio.h"
#include "windows.h"
#include <shellapi.h>

DWORD FileBuff;
DWORD FileSize;

CHAR FileName[MAX_PATH];

FILE *pFile;

DWORD Old_wxam_dec_isWXGF_4;


void MyHook(LPVOID HookAddress, LPVOID NewAddress, DWORD *OldAddress,DWORD HookBytesNum)
{

	BYTE JumpByte[6] = { 0x68,0x00,0x00,0x00,0x00,0xc3 };

	*(DWORD*)(JumpByte + 1) = (DWORD)HookAddress + HookBytesNum;

	*OldAddress = (DWORD)VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

	memcpy((LPVOID)*OldAddress, HookAddress, HookBytesNum);

	memcpy((BYTE*)*OldAddress + HookBytesNum, JumpByte,6);

	*(DWORD*)(JumpByte + 1) = (DWORD)NewAddress;

	WriteProcessMemory((HANDLE)-1, HookAddress, JumpByte, 6, 0);

}

DWORD GetHash(char *nBuff,int nBuffSize)
{
	DWORD nHash = 0;

	for (int i = 0; i < nBuffSize; i++)
	{
		nHash = ((nHash << 25) | (nHash >> 7));
		nHash = nHash + nBuff[i];
	}
	return nHash;
}

char* GetProgramDir()
{
	static char exeFullPath[MAX_PATH] = { 0 }; // Full path
	char *nWeak;

	GetModuleFileNameA(NULL, exeFullPath, MAX_PATH);
	nWeak = strrchr(exeFullPath, '\\');

	memcpy(nWeak + 1, "GetWeChatPic", strlen("GetWeChatPic"));

	return exeFullPath;
}

__declspec(naked) void Hook()
{
	__asm
	{
		pushad;
		mov eax, [esp + 36];
		mov FileBuff, eax;
		mov eax, [esp + 40];
		mov FileSize, eax;
	}

	sprintf_s(FileName, 256, "GetWeChatPic\\%08X.gif", GetHash((char*)FileBuff, FileSize));

	fopen_s(&pFile, FileName, "wb+");

	fwrite((LPVOID)FileBuff, FileSize, 1, pFile);

	fclose(pFile);

	__asm
	{
		popad;
		jmp Old_wxam_dec_isWXGF_4;
	}
}



BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	{

		{
			HMODULE nHmodule = GetModuleHandleA("WeChatWin.dll");

			BYTE HookByte2[2] = { 0x90,0x90 };
			DWORD pFunAddress = (DWORD)nHmodule + 0x7DB79;//聊天收发的表情全部保存下来
			WriteProcessMemory((HANDLE)-1, (LPVOID)pFunAddress, HookByte2, 2, 0);

			BYTE HookByte5[5] = { 0xe9,0x82,0,0,0 };
			pFunAddress = (DWORD)nHmodule + 0x2841DC;//打开收藏的表情全部保存下来
			WriteProcessMemory((HANDLE)-1, (LPVOID)pFunAddress, HookByte5, 5, 0);
		}



		HMODULE nHmodule = GetModuleHandleA("WXAMDecoder.dll");
		LPVOID pFunAddress = GetProcAddress(nHmodule, "wxam_dec_isWXGF_4");

		if (pFunAddress)
		{
			MyHook(pFunAddress, Hook, &Old_wxam_dec_isWXGF_4, 9);

			SECURITY_ATTRIBUTES SecurityAttributes;
			SecurityAttributes.lpSecurityDescriptor = 0;
			SecurityAttributes.bInheritHandle = false;
			SecurityAttributes.nLength = sizeof(SecurityAttributes);
			CreateDirectoryA("GetWeChatPic", &SecurityAttributes);


			if (MessageBoxA(0, "注入成功!\r\n是否打开储存的表情文件夹?", "Tips", MB_ICONINFORMATION | MB_YESNO)==IDYES)
				ShellExecuteA(NULL, ("open"), ("explorer"), GetProgramDir(), NULL, SW_SHOW);

		}
		else
		{
			MessageBoxA(0, "注入失败!请重启微信进入到聊天框内再注入!", "Tips", MB_ICONERROR);
		}

	}
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}
  • 31
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值