提取微信小程序资源
2024-03-17 17:33:41

解密小程序

PC微信的小程序默认存放在%USERPROFILE%\Documents\WeChat Files\Applet文件夹下,每个以wx开头的文件夹对应一个小程序。
进去后会发现有若干以.wxapkg结尾的文件,这都是小程序的包,是经过加密的。
已经有网友制作了解密工具 PC微信小程序包解密工具C#版无需root或模拟器
解密后就会得到一个原始的wxapkg文件。

wxapkg结构

类似压缩包的文件,里面存放的是小程序的代码和资源。文件结构也很简单,就是Header + Index + Data

我编写了一个 010 Editor 的模板文件来查看文件结构,这样更直观

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
//------------------------------------------------
//--- 010 Editor v12.0.1 Binary Template
//
// File:
// Authors:
// Version:
// Purpose:
// Category: WeChat
// File Mask: *.wxapkg
// ID Bytes: BE, 56 31 4D 4D 57 58
// History:
//------------------------------------------------

typedef struct {
uchar firstMark <format=hex>;
uint unknown;
uint infoListLength;
uint dataLength;
uchar lastMark <format=hex>;
uint fileCount;
} HEADER;

typedef struct {
uint length;
char fileName[length];
} FILE_NAME;

typedef struct {
FILE_NAME fileName;
uint offset;
uint size;

if (size > 0) {
local int64 pos = FTell();
FSeek(offset);
uchar data[size];
FSeek(pos);
}
} FILE <comment=fileName.fileName>;

local string magic = ReadString(0, 6);
if (magic == "V1MMWX") {
Printf("无法解析加密文件");
return;
}

BigEndian();
HEADER header;

local int i;
for (i = 0; i < header.fileCount; ++i) {
FILE file;
}

效果如图

可以看到,wxapkg也没什么神秘的,就是一个压缩包。
提取文件也有很多工具软件可用,见文章尾部。这里我贴一个 python3 版本的

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
# coding: utf-8
# py2 origin author lrdcq
# usage python3 unwxapkg.py filename

__author__ = 'Integ: https://github.com./integ'

import sys, os
import struct

class WxapkgFile(object):
nameLen = 0
name = ""
offset = 0
size = 0

if len(sys.argv) < 2:
print('usage: unwxapkg.py filename [output_dir]')
exit()

with open(sys.argv[1], "rb") as f:
root = os.path.dirname(os.path.realpath(f.name))
name = os.path.basename(f.name) + '_dir'
if len(sys.argv) > 2:
name = sys.argv[2]

#read header
firstMark = struct.unpack('B', f.read(1))[0]
print('first header mark = {}'.format(firstMark))

info1 = struct.unpack('>L', f.read(4))[0]
print('info1 = {}'.format(info1))

indexInfoLength = struct.unpack('>L', f.read(4))[0]
print('indexInfoLength = {}'.format(indexInfoLength))

bodyInfoLength = struct.unpack('>L', f.read(4))[0]
print('bodyInfoLength = {}'.format(bodyInfoLength))

lastMark = struct.unpack('B', f.read(1))[0]
print('last header mark = {}'.format(lastMark))

if firstMark != 0xBE or lastMark != 0xED:
print('its not a wxapkg file!!!!!')
f.close()
exit()

fileCount = struct.unpack('>L', f.read(4))[0]
print('fileCount = {}'.format(fileCount))

#read index
fileList = []
for i in range(fileCount):
data = WxapkgFile()
data.nameLen = struct.unpack('>L', f.read(4))[0]
data.name = f.read(data.nameLen)
data.offset = struct.unpack('>L', f.read(4))[0]
data.size = struct.unpack('>L', f.read(4))[0]
print('readFile = {} at Offset = {}'.format(str(data.name, encoding = "utf-8"), data.offset))

fileList.append(data)

#save files
for d in fileList:
d.name = '/' + name + str(d.name, encoding = "utf-8")
path = root + os.path.dirname(d.name)

if not os.path.exists(path):
os.makedirs(path)

w = open(root + d.name, 'wb')
f.seek(d.offset)
w.write(f.read(d.size))
w.close()

print('writeFile = {}{}'.format(root, d.name))

f.close()

运行命令python unwxapkg.py xxx.wxapkg即可提取文件到磁盘。

相关工具

PC微信小程序包解密工具C#版无需root或模拟器
pc_wxapkg_decrypt
SS.UnWxapkg

相关阅读

微信小程序源码阅读笔记1
微信小程序“反编译”实战(一):解包
微信小程序“反编译”实战(二):源码还原
搜索编程的艺术之C#实现小程序包解密算法
反编译PC微信小程序