实战剖析:从微信小程序反编译到AES加解密爬虫的完整链路 1. 微信小程序反编译基础准备第一次接触微信小程序反编译时我像大多数技术爱好者一样既兴奋又忐忑。微信小程序的.wxapkg文件本质上是个经过加密的压缩包里面藏着小程序的前端源码和资源文件。要拿到这些资源我们需要一套完整的工具链。在Windows环境下我推荐使用以下工具组合Node.js环境这是运行反编译脚本的基础建议安装LTS版本如16.x微信开发者工具官方提供的IDE可以用来验证反编译结果解密工具pc_wxapkg_decrypt专门用于解密微信缓存中的.wxapkg文件反编译脚本wxappUnpacker将解密后的包还原为可读的源代码安装Node.js时有个小技巧一定要勾选Add to PATH选项否则后续命令行操作会遇到麻烦。验证安装是否成功可以运行node -v npm -v获取小程序包的过程很有意思。先在PC微信里打开目标小程序比如某个外卖小程序这时微信会在本地缓存目录生成加密包。具体路径通常是C:\Users\[用户名]\Documents\WeChat Files\Applet\[小程序ID]这里有个容易踩坑的地方不同微信版本可能缓存路径略有不同。如果找不到可以尝试在Everything等搜索工具中直接查找APP.wxapkg文件。2. 解密与反编译实战操作拿到加密的.wxapkg文件后真正的挑战才开始。我遇到过不少解密失败的情况大多数是因为路径中包含中文或空格。这里分享一个已验证可用的解密命令pc_wxapkg_decrypt.exe -wxid wxd418ee346d79d382 -in C:\path\to\__APP__.wxapkg成功解密后会生成dec.wxapkg文件。接下来用wxappUnpacker进行反编译node wuWxapkg.js ../decrypt/dec.wxapkg反编译过程中可能会遇到各种报错最常见的是模块缺失错误需要npm install安装依赖文件损坏错误可能解密不完整需要重新操作内存溢出错误大程序包需要增加Node内存限制反编译成功后你会看到完整的项目结构pages/ 页面组件目录utils/ 工具函数app.js 小程序入口文件app.json 全局配置用微信开发者工具导入项目时记得选择导入项目而不是新建项目并确保AppID填写正确可以随便填测试号。3. 逆向分析网络请求有了源码后我开始寻找关键API接口。推荐使用Fiddler或Charles抓包配合微信开发者工具的网络面板。这里有个实用技巧在源码中搜索关键词如request、wx.request可以快速定位网络请求代码。分析加密参数时我通常采用三板斧全局搜索找encrypt、decrypt、AES、CBC等关键词调用追踪从wx.request入手回溯参数生成过程断点调试在开发者工具中设置断点观察实时数据在某次分析中我发现加密逻辑藏在utils/crypto.js里。关键代码段如下function encryptData(data) { const key CryptoJS.enc.Utf8.parse(f13df6c54e8efdfe); const iv CryptoJS.enc.Utf8.parse(a3648c7c1ef3e9fe); return CryptoJS.AES.encrypt(data, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }).toString(); }特别注意padding方式这里是Pkcs7这个细节直接影响后续Python复现的成功率。headers中的常见加密字段包括sign参数签名timestamp时间戳nonce随机字符串encryptedData加密的业务数据4. Python复现AES加密逻辑将JavaScript的加密逻辑移植到Python需要特别注意数据格式处理。这是我的实现方案from Crypto.Cipher import AES from Crypto.Util.Padding import pad import base64 import json class WXEncryptor: def __init__(self): self.key f13df6c54e8efdfe.encode(utf-8) self.iv a3648c7c1ef3e9fe.encode(utf-8) def encrypt(self, data): if isinstance(data, dict): data json.dumps(data, ensure_asciiFalse) cipher AES.new(self.key, AES.MODE_CBC, self.iv) padded_data pad(data.encode(utf-8), AES.block_size, stylepkcs7) encrypted cipher.encrypt(padded_data) return base64.b64encode(encrypted).decode(utf-8) def decrypt(self, encrypted_data): cipher AES.new(self.key, AES.MODE_CBC, self.iv) decrypted cipher.decrypt(base64.b64decode(encrypted_data)) return decrypted[:-decrypted[-1]].decode(utf-8) # PKCS7 unpadding实际使用时可能会遇到以下问题编码问题确保所有字符串统一用UTF-8编码填充问题JavaScript和Python的PKCS7实现可能有细微差异字节对齐AES要求数据长度是16字节的倍数测试加密结果是否与小程序一致encryptor WXEncryptor() test_data {app_type:Wechat,version:3.0} encrypted encryptor.encrypt(test_data) print(f加密结果: {encrypted})5. 构建完整爬虫链路有了加密算法就可以构建完整的爬虫了。我的爬虫架构通常包含以下模块import requests class WXSpider: def __init__(self): self.session requests.Session() self.encryptor WXEncryptor() self.headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64), Content-Type: application/json } def build_payload(self, params): 构造加密请求体 timestamp int(time.time() * 1000) payload { **params, timestamp: timestamp, nonce: str(random.randint(1, 999999)) } return { data: self.encryptor.encrypt(payload), sign: self._generate_sign(payload) } def _generate_sign(self, params): 生成签名 param_str .join([f{k}{v} for k,v in sorted(params.items())]) return hashlib.md5((param_str your_salt).encode()).hexdigest() def fetch_data(self, api_url, params): 发送加密请求 encrypted self.build_payload(params) resp self.session.post(api_url, jsonencrypted, headersself.headers) if resp.status_code 200: return self.encryptor.decrypt(resp.json()[encryptedData]) return None实际使用时会遇到的反爬机制及应对策略IP限制使用代理池轮换IP行为验证控制请求频率模拟人工操作间隔参数校验确保timestamp、nonce等参数符合目标系统的预期6. 常见问题与调试技巧在这个项目中我踩过不少坑这里分享几个典型案例案例一加密结果不一致现象Python和JavaScript加密结果不同 排查步骤确认key和iv完全一致包括编码方式检查padding方式PKCS7在不同语言库中实现可能有差异验证输入数据是否完全相同特别是JSON字段顺序案例二解密后乱码解决方法# 正确的PKCS7 unpadding实现 def pkcs7_unpad(data): pad_len data[-1] return data[:-pad_len]案例三请求返回403可能原因headers缺少必要字段如Referer签名算法有细微差别请求频率过高调试时建议使用对比工具如Beyond Compare逐字节比对加密结果。对于复杂问题可以构造最小测试用例# 最小测试用例 plaintext test1234 js_encrypted 已知的JavaScript加密结果 py_encrypted encryptor.encrypt(plaintext) assert js_encrypted py_encrypted7. 进阶技巧与优化建议经过多个项目实践我总结出一些提升效率的方法代码混淆应对遇到混淆代码时可以使用AST解析工具分析代码结构变量名重命名如将_0x12ab3c改为更有意义的名称控制流平坦化还原性能优化当需要处理大量数据时# 使用连接池 adapter requests.adapters.HTTPAdapter( pool_connections100, pool_maxsize100 ) session.mount(http://, adapter) # 异步处理aiohttp示例 async def async_fetch(url, params): async with aiohttp.ClientSession() as session: async with session.post(url, jsonparams) as resp: return await resp.json()自动化监控对于长期运行的项目建议添加心跳检测定期验证加密是否仍然有效异常报警如响应结构变化自动降级机制当主算法失效时切换备用方案法律与道德提醒需要特别注意仅对自有或授权的小程序进行分析控制请求频率避免对目标服务器造成压力不获取、不存储用户隐私数据遵守robots.txt协议