逆向的思路
https://www.aynakeya.com/articles/ctf/wechat-video-encryption-reverse-engineer/
逆向的实战
https://github.com/xuncv/WechatVideoSniffer
尝试仿照的实现:
import os
import random
import string
import shutil
import threading
import time
import sys
import signal
import asyncio
from mitmproxy import proxy, options
from mitmproxy.tools.dump import DumpMaster
from mitmproxy.addons import core
import requests
from pathlib import Path
import re
import subprocess
# 全局变量和状态管理
class AppState:
def __init__(self):
self.video_url = None
self.download_running = False
self.decrypt_table = {} # 存储视频URL和解密数组的对应关系
self.download_queue = []
self.lock = threading.Lock()
app_state = AppState()
# 创建必要的目录
def init_dirs():
os.makedirs("cache", exist_ok=True)
os.makedirs("视频", exist_ok=True)
# 生成随机字符串
def random_string(length=6):
return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(length))
# mitmproxy的HTTP请求拦截处理
class VideoInterceptor:
def request(self, flow):
# 只处理HTTPS请求
if not flow.request.scheme == "https":
return
# 拦截worker_release.js请求
if flow.request.method == "GET" and flow.request.url.endswith("/worker_release.js"):
# 移除压缩,确保我们可以修改响应内容
if "Accept-Encoding" in flow.request.headers:
del flow.request.headers["Accept-Encoding"]
# 接收从注入脚本发来的解密数组
if flow.request.method == "POST" and "httpbin.org" in flow.request.host:
if app_state.video_url:
print(f"[+] 拦截到视频下载链接和解密数组: {app_state.video_url}")
dec_array = flow.request.content
# 保存解密数组到文件
save_path = os.path.join("cache", random_string(16) + ".bin")
with open(save_path, "wb") as f:
f.write(dec_array)
with app_state.lock:
# 存储URL和解密数组的对应关系
app_state.decrypt_table[app_state.video_url] = save_path
# 添加到下载队列
app_state.download_queue.append(app_state.video_url)
app_state.video_url = None
# 设置响应头以避免CORS问题
flow.response = requests.Response()
flow.response.status_code = 200
flow.response.headers = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET",
"Content-Type": "text/html"
}
flow.response.content = b"<html><body>success!</body></html>"
def response(self, flow):
if not flow.request.scheme == "https":
return
# 修改worker_release.js响应
if flow.request.method == "GET" and flow.request.url.endswith("/worker_release.js"):
# 注入代码获取解密数组
inject_script = "var rr=\\2.reverse();fetch('https://www.httpbin.org/post',{method:'POST',headers:{'Content-Type':'application/octet-stream',},body:rr,}).then(response=>{console.log(response.ok,response.body)});\\1.decryptor_array.set(rr);"
content = flow.response.text
modified_content = re.sub(r'(\w)\.decryptor_array\.set\((\w)\.reverse\(\)\)', inject_script, content)
flow.response.text = modified_content
# 拦截视频URL
if "finder.video.qq.com/251/20302" in flow.request.url:
if flow.request.method == "HEAD":
app_state.video_url = flow.request.url
# 添加CORS相关头
flow.response.headers["Access-Control-Allow-Origin"] = "*"
flow.response.headers["Access-Control-Allow-Headers"] = "*"
flow.response.headers["Access-Control-Allow-Methods"] = "OPTIONS,POST,GET"
# 下载和解密视频
def download_and_decrypt_video(url):
try:
# 从存储中获取对应的解密数组路径
with app_state.lock:
dec_array_path = app_state.decrypt_table.get(url)
if not dec_array_path:
print(f"[-] 获取链接解密数组失败: {url}")
return
if not os.path.exists(dec_array_path):
print("[-] 解密数组路径获取失败!")
return
# 读取解密数组
with open(dec_array_path, "rb") as f:
dec_array = f.read()
# 删除临时文件
os.remove(dec_array_path)
# 生成随机文件名
name = random_string()
filename = os.path.join("cache", f"{name}_raw.dec")
# 处理可能的URL参数
clean_url = re.sub(r"\&X-snsvideoflag=(\w+)", "", url)
print(f"[+] 开始通过链接下载视频, 链接: {clean_url}")
# 下载视频
response = requests.get(clean_url, stream=True, timeout=30)
response.raise_for_status()
with open(filename, "wb") as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
print("[+] 视频下载完成。")
# 解密视频
dec_size = len(dec_array) # 通常是131072
with open(filename, "rb+") as fw:
enc_video_bin = fw.read(dec_size)
print(f"[+] 开始解密视频,加密块大小: {len(enc_video_bin)}, {dec_size}")
# 进行XOR解密
dec_video_array = bytearray()
for i in range(min(len(enc_video_bin), dec_size)):
dec_video_array.append(enc_video_bin[i] ^ dec_array[i])
# 写入解密后的数据
fw.seek(0)
fw.write(dec_video_array)
# 保留原数据的其余部分
if len(enc_video_bin) > dec_size:
fw.write(enc_video_bin[dec_size:])
# 移动到视频文件夹
video_filename = os.path.join("视频", f"{name}.mp4")
shutil.move(filename, video_filename)
print(f"[+] 解密完成,已保存视频到路径: {video_filename}")
# 清理记录
with app_state.lock:
if url in app_state.decrypt_table:
del app_state.decrypt_table[url]
except Exception as e:
print(f"[-] 下载或解密过程出错: {e}")
# 下载管理线程
def download_manager():
while app_state.download_running:
try:
url_to_download = None
with app_state.lock:
if app_state.download_queue:
url_to_download = app_state.download_queue.pop(0)
if url_to_download:
print(f"[*] 正在处理下载队列中的视频: {url_to_download}")
download_and_decrypt_video(url_to_download)
else:
time.sleep(1)
except Exception as e:
print(f"[-] 下载管理器出错: {e}")
time.sleep(1)
# 安装证书
def install_certificate():
try:
# 生成证书
cert_dir = os.path.expanduser("~/.mitmproxy")
os.makedirs(cert_dir, exist_ok=True)
print("[*] 正在安装证书...")
if sys.platform == "win32":
# Windows平台
subprocess.run(["mitmdump", "--set", "confdir=~/.mitmproxy", "--set", "ssl_insecure=true"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=10)
print("[*] 将在浏览器中打开证书安装页面...")
subprocess.run(["start", "http://mitm.it"], shell=True)
else:
# Linux/macOS平台
subprocess.run(["mitmdump", "--set", "confdir=~/.mitmproxy", "--set", "ssl_insecure=true"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1)
print("[*] 请访问 http://mitm.it 安装证书")
print("[+] 证书安装完成后,请按回车继续...")
input()
except Exception as e:
print(f"[-] 证书安装失败: {e}")
sys.exit(1)
def main():
print("=" * 50)
print("微信视频号下载助手 Python版")
print("=" * 50)
# 初始化目录
init_dirs()
print("[+] 初始化完成")
# 安装证书
install_certificate()
try:
# 启动mitmproxy
print("[*] 正在启动代理服务器...")
opts = options.Options(
listen_host="127.0.0.1",
listen_port=8080,
ssl_insecure=True,
confdir="~/.mitmproxy"
)
from mitmproxy import master
dump_master = master.Master(opts=opts, event_loop=asyncio.get_event_loop())
# 初始化代理服务器 - 旧版本兼容
master = DumpMaster(dump_master)
master.addons.add(VideoInterceptor())
# 启动下载管理器
app_state.download_running = True
download_thread = threading.Thread(target=download_manager)
download_thread.daemon = True
download_thread.start()
print("[+] 代理服务器启动成功,监听端口: 8080")
print("[*] 请设置微信代理为 127.0.0.1:8080")
print("[*] 按 Ctrl+C 停止程序")
# 运行代理服务器
master.run()
except KeyboardInterrupt:
print("\n[*] 正在关闭程序...")
finally:
# 清理资源
app_state.download_running = False
if 'master' in locals():
master.shutdown()
print("[+] 程序已关闭")
if __name__ == "__main__":
main()

被折叠的 条评论
为什么被折叠?



