文章资料-情感.机器.认知-电子AI 游客
获取、导出微信所有表情
【80914】by1 2021-05-13 最后编辑2021-05-13 14:32:08 浏览1300

获取、导出微信所有表情


置顶 Mr_Hock 2019-08-25 15:45:00  7663  收藏 25

分类专栏: 业余

版权

前言

适用: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;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

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;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140


点赞

14


评论

12


分享


收藏

25


打赏

关注

一键三连


立即提问


————————————————

版权声明:本文为CSDN博主「Mr_Hock」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/qq_43572067/article/details/100062493