├── .build ├── default.init.aardio ├── default.main.aardio └── default.Manifest.xml ├── dist ├── download │ └── bm8zbu.mp4.dow!oad └── WechatVideoSniffer.exe ├── res ├── BCMakeCert.dll ├── CertMaker.dll ├── FiddlerCore4.dll └── BasicFormatsForCore.dll ├── README.assets └── screenshots.gif ├── README.md ├── lib └── config.aardio ├── default.aproj └── main.aardio /.build/default.init.aardio: -------------------------------------------------------------------------------- 1 | //发布前触发 2 | import ide; -------------------------------------------------------------------------------- /dist/download/bm8zbu.mp4.dow!oad: -------------------------------------------------------------------------------- 1 | { 2 | acceptRanges="bytes"; 3 | contentLength=26295001 4 | } -------------------------------------------------------------------------------- /res/BCMakeCert.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuncv/WechatVideoSniffer/HEAD/res/BCMakeCert.dll -------------------------------------------------------------------------------- /res/CertMaker.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuncv/WechatVideoSniffer/HEAD/res/CertMaker.dll -------------------------------------------------------------------------------- /res/FiddlerCore4.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuncv/WechatVideoSniffer/HEAD/res/FiddlerCore4.dll -------------------------------------------------------------------------------- /README.assets/screenshots.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuncv/WechatVideoSniffer/HEAD/README.assets/screenshots.gif -------------------------------------------------------------------------------- /dist/WechatVideoSniffer.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuncv/WechatVideoSniffer/HEAD/dist/WechatVideoSniffer.exe -------------------------------------------------------------------------------- /res/BasicFormatsForCore.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuncv/WechatVideoSniffer/HEAD/res/BasicFormatsForCore.dll -------------------------------------------------------------------------------- /.build/default.main.aardio: -------------------------------------------------------------------------------- 1 | //此触发器在生成EXE以后执行 2 | import ide; 3 | import fsys; 4 | 5 | //获取生成的EXE文件路径 6 | var publishFile = ide.getPublishPath(); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WechatVideoSniffer 2 | 微信视频号PC版视频地址嗅探器 3 | 4 | 本项目采用[aardio](https://www.aardio.com/)编程语言开发 5 | 6 | ### 重要提醒 7 | 目前初步分析微信视频对流媒体进行加密,尚无逆向方法,可采用如下方法绕过: 8 | ~~1. 视频转发至`文件传输助手`,然后关闭微信视频窗口~~ 9 | ~~2. 从`文件传输助手`打开视频,即可抓到未加密的视频地址~~ 10 | pc端微信,搜一搜,在搜索列表播放的视频可以监听到进行下载 11 | 12 | ## 原理 13 | 14 | 使用FiddlerCore .Net组件,注册系统代理,监听主机的http/https请求,从而匹配出微信视频号视频的地址 15 | 16 | ## 使用方法 17 | 18 | 1. 打开软件,点击`监听`按钮 19 | 20 | > 如果第一次运行,会安装一个证书,这个证书的作用是建立中间人捕获https协议 21 | 22 | 2. 在`PC微信`上打开视频 23 | 24 | 3. 此时软件列表会更新视频链接,点击`下载选中` 按钮就可以下载视频,视频存放在`download`目录下。 25 | 26 | ![screenshots](README.assets/screenshots.gif) 27 | 28 | ## 软件获取 29 | 30 | 自行编译或工程`dist`目录下载 31 | -------------------------------------------------------------------------------- /lib/config.aardio: -------------------------------------------------------------------------------- 1 | //config 配置文件 2 | import fsys.config; 3 | config = fsys.config("/config/"); 4 | //config = fsys.config( io.appData("/软件作者/应用程序名/") ); 5 | 6 | //不需要序列化的配置名字前请添加下划线 7 | namespace config { 8 | __appName = "应用程序名"; 9 | __website = "http://www.aardio.com/"; 10 | } 11 | 12 | /**intellisense(config) 13 | __appName = 应用程序名 14 | __website = 官方网站 15 | saveAll() = 写入所有配置到文件 16 | ? = 获取值时指定不以下划线开始的配置表名称,\n返回一个可自动序列化到同名配置文件的表对象。\n如果此对象名以下划线开始,则可以正常读写值不会序列化为配置文件。\n否则不能对此对象直接赋值,只能对配置表对象的成员赋值。\n\n配置表可自动自文件加载,退出线程前自动序列化并存入文件。\n仅序列化以字符串、数值为键的元素,\n仅序列化值为字符串、数值、buffer 以及定义了 _serialize 元方法的成员。\n循环引用的值转换为 null,序列化时忽略成员函数\n!fsys_table. 17 | end intellisense**/ -------------------------------------------------------------------------------- /default.aproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.build/default.Manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 10 | WechatVideoSniffer 11 | 12 | 13 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 52 | 53 | 54 | 55 | 56 | 57 | True/PM 58 | PerMonitorV2, PerMonitor 59 | 60 | 61 | -------------------------------------------------------------------------------- /main.aardio: -------------------------------------------------------------------------------- 1 | import win.ui; 2 | /*DSG{{*/ 3 | mainForm = win.form(text="WechatVideoSniffer";right=959;bottom=591) 4 | mainForm.add( 5 | btnClear={cls="button";text="清空列表";left=797;top=540;right=889;bottom=580;z=5}; 6 | btnCopyUrl={cls="button";text="复制链接";left=797;top=177;right=889;bottom=211;z=6}; 7 | btnDown={cls="button";text="下载选中";left=794;top=120;right=885;bottom=157;z=4}; 8 | btnSniffer={cls="button";text="监听";left=794;top=14;right=885;bottom=51;z=1}; 9 | btnStop={cls="button";text="停止";left=794;top=70;right=885;bottom=107;z=2}; 10 | listview={cls="listview";left=17;top=15;right=787;bottom=584;edge=1;hscroll=1;z=3} 11 | ) 12 | /*}}*/ 13 | 14 | import dotNet 15 | import win.clip 16 | mainForm.listview.insertColumn( "URL",-1 ) 17 | dotNet.reference({ 18 | "FiddlerCore4" = $"res\FiddlerCore4.dll"; 19 | "CertMaker" = $"res\CertMaker.dll"; 20 | "BCMakeCert" = $"res\BCMakeCert.dll"; 21 | "BasicFormatsForCore" = $"res\BasicFormatsForCore.dll" 22 | }) 23 | 24 | var FiddlerCore4 = dotNet.load("FiddlerCore4") 25 | Fiddler = FiddlerCore4.import("Fiddler") 26 | 27 | InstallCertificate = function(){ 28 | if(!Fiddler.CertMaker.rootCertExists()){ 29 | if(!Fiddler.CertMaker.createRootCert()){ 30 | return false; 31 | } 32 | if(Fiddler.CertMaker.trustRootCert()){ 33 | return false; 34 | } 35 | } 36 | return true; 37 | } 38 | 39 | UninstallCertificate = function(){ 40 | if(Fiddler.CertMaker.rootCertExists()){ 41 | if(!Fiddler.CertMaker.removeFiddlerGeneratedCerts(true)){ 42 | return false; 43 | } 44 | } 45 | return true; 46 | } 47 | 48 | // 视频链接过滤规则 49 | var urls = {} 50 | filterUrl = function(url){ 51 | if(string.find(url,"^https\:\/\/finder*\.video\.qq\.com\/251\/20302\/") or string.find(url,"mpvideo\.qpic\.cn") ){ 52 | // 去除无用链接格式 53 | if(!!string.match(url,"token=\w+")){ 54 | if(!urls[url]){ 55 | urls[url] = true 56 | return true; 57 | } 58 | } 59 | } 60 | return false; 61 | } 62 | 63 | 64 | AttachListening = function(){ 65 | Fiddler.FiddlerApplication.BeforeRequest = function(s){ 66 | s.bBufferResponse = false; 67 | if(filterUrl(s.fullUrl)){ 68 | mainForm.listview.addItem( {s.fullUrl} ) 69 | } 70 | } 71 | /* 72 | Fiddler.FiddlerApplication.BeforeResponse = function(s){ 73 | if(string.startWith(s.fullUrl,"https://finder.video.qq.com")){ 74 | mainForm.listview.addItem( {s.fullUrl} ) 75 | } 76 | } 77 | */ 78 | } 79 | 80 | StartupFiddlerCore = function(){ 81 | Fiddler.FiddlerApplication.Startup(9898,Fiddler.FiddlerCoreStartupFlags.Default | Fiddler.FiddlerCoreStartupFlags.RegisterAsSystemProxy | Fiddler.FiddlerCoreStartupFlags.AllowRemoteClients) 82 | } 83 | 84 | UninstallFiddler = function(){ 85 | if(Fiddler.FiddlerApplication.IsStarted()){ 86 | Fiddler.FiddlerApplication.Shutdown(); 87 | } 88 | } 89 | 90 | mainForm.btnSniffer.oncommand = function(id,event){ 91 | InstallCertificate() 92 | AttachListening() 93 | StartupFiddlerCore() 94 | mainForm.btnSniffer.disabled = true 95 | } 96 | 97 | mainForm.btnStop.oncommand = function(id,event){ 98 | UninstallCertificate() 99 | UninstallFiddler() 100 | mainForm.btnSniffer.disabled = false 101 | } 102 | 103 | mainForm.btnClear.oncommand = function(id,event){ 104 | mainForm.listview.clear() 105 | urls = {} 106 | } 107 | 108 | mainForm.btnDown.oncommand = function(id,event){ 109 | var index = mainForm.listview.selIndex 110 | if(index>0){ 111 | var url = mainForm.listview.getItemText(index,1,10240) 112 | mainForm.btnDown.text = "下载中" 113 | mainForm.btnDown.disabled = true 114 | thread.invokeAndWait( 115 | function(url){ 116 | import inet.httpFile 117 | math.randomize() 118 | var remoteFile = inet.httpFile( url,"/download/" + string.random(6) + ".mp4" ) 119 | remoteFile.test() 120 | remoteFile.download() 121 | remoteFile.close() 122 | },url 123 | ) 124 | mainForm.msgbox("下载完成") 125 | mainForm.btnDown.text = "下载选中" 126 | mainForm.btnDown.disabled = false 127 | } 128 | } 129 | 130 | mainForm.onClose = function(hwnd,message,wParam,lParam){ 131 | //UninstallCertificate() 132 | UninstallFiddler() 133 | } 134 | 135 | mainForm.btnCopyUrl.oncommand = function(id,event){ 136 | var index = mainForm.listview.selIndex 137 | if(index>0){ 138 | var url = mainForm.listview.getItemText(index,1,10240) 139 | win.clip.write(url) 140 | } 141 | } 142 | 143 | mainForm.show(); 144 | return win.loopMessage(); --------------------------------------------------------------------------------