小程序前端代码提取
准备
- 提取Android端小程序(微信、支付宝):
a. 一部已经root的Android手机, 打开开发者模式USB调试
b. 安装adb (官方下载链接: https://developer.android.com/studio/releases/platform-tools)
c. python运行环境 (本人使用WSL)
d. 安装微信或支付宝 - 提取PC端(微信):
a. python、python3(含pip3)运行环境 (本人使用WSL)
b. 安装PC端微信
微信小程序 (Android端)
- 将Android手机通过USB连接PC, 通过adb.exe devices能看到该设备
- 执行adb.exe shell
- 执行su切换到root模式下, 如果是第一次操作, 可能需要Android端 授权获取root
"$“变为”#"即切换到root用户成功
- 进入小程序缓存路径, 根据时间找到你需要的小程序缓存(wxapkg)
路径: /data/data/com.tencent.mm/MicroMsg/<32位id>/appbrand/pkg/
- 把文件cp到非root权限可以访问的路径下, 例如: /storage/emulated/0/Android/
- 退出Android root和shell, 需要两次exit, 通过adb.exe pull拉取到本地
- 用以下代码进行解包
#!/usr/bin/env python2
# usage python2 unwxapkg.py filename
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'
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 = ' + str(firstMark)
info1 = struct.unpack('>L', f.read(4))[0]
print 'info1 = ' + str(info1)
indexInfoLength = struct.unpack('>L', f.read(4))[0]
print 'indexInfoLength = ' + str(indexInfoLength)
bodyInfoLength = struct.unpack('>L', f.read(4))[0]
print 'bodyInfoLength = ' + str(bodyInfoLength)
lastMark = struct.unpack('B', f.read(1))[0]
print 'last header mark = ' + str(lastMark)
if firstMark != 0xBE or lastMark != 0xED:
print 'its not a wxapkg file!!!!!'
exit()
fileCount = struct.unpack('>L', f.read(4))[0]
print 'fileCount = ' + str(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 = ' + data.name + ' at Offset = ' + str(data.offset)
fileList.append(data)
#save files
for d in fileList:
d.name = '/' + name + d.name
path = root + os.path.dirname(d.name)
if not os.path.exists(path):
os.makedirs(path)
w = open(root + d.name, 'w')
f.seek(d.offset)
w.write(f.read(d.size))
w.close()
print 'writeFile = ' + root + d.name
f.close()
支付宝小程序 (Android端)
支付宝的小程序路径为: /data/data/com.eg.android.AlipayGphone/files/nebulaInstallApps/<支付宝appid>/<32位id>/xxx.tar
直接tar -xf <file>解压即可
微信小程序 (PC端)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import argparse
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Hash import SHA1
from Crypto.Cipher import AES
# 微信小程序包 自定义标识
WXAPKG_FLAG = 'V1MMWX'
WXAPKG_FLAG_LEN = len(WXAPKG_FLAG)
def main():
parser = argparse.ArgumentParser(description='PC微信小程序wxapkg包解密工具')
parser.add_argument('--wxid', metavar='微信小程序ID', required=True)
parser.add_argument('--iv', metavar='iv', required=False, default='the iv: 16 bytes')
parser.add_argument('--salt', metavar='salt', required=False, default='saltiest')
parser.add_argument('-f', '--file', metavar='加密的小程序包文件路径', required=True)
parser.add_argument('-o', '--output', metavar='解密后的小程序包文件路径', required=True)
args = parser.parse_args()
key = PBKDF2(args.wxid.encode('utf-8'), args.salt.encode('utf-8'), 32, count=1000, hmac_hash_module=SHA1)
# 读取加密的内容
if not os.path.exists(args.file):
raise Exception('文件不存在')
with open(args.file, mode='rb') as f:
dataByte = f.read()
if dataByte[0:WXAPKG_FLAG_LEN].decode() != WXAPKG_FLAG:
raise Exception('该文件无需解密, 或者不是微信小程序wxapkg加密包')
# 初始化密钥
cipher = AES.new(key, AES.MODE_CBC, args.iv.encode('utf-8'))
# 解密头部1024个字节
originData = cipher.decrypt(dataByte[WXAPKG_FLAG_LEN: 1024 + WXAPKG_FLAG_LEN])
# 初始化xor密钥, 解密剩余字节
xorKey = 0x66
if len(args.wxid) >= 2:
xorKey = ord(args.wxid[len(args.wxid) - 2])
afData = dataByte[1024 + WXAPKG_FLAG_LEN:]
out = bytearray()
for i in range(len(afData)):
out.append(afData[i] ^ xorKey)
originData = originData[0:1023] + out
# 保存解密后的数据
with open(args.output, mode='wb') as f:
f.write(originData)
print('解密成功', args.output)
if __name__ == "__main__":
main()