(编辑:jimmy 日期: 2024/10/30 浏览:2)
主页 :aHR0cHM6Ly93d3cuNXlpbmcuZnVuLw==
地址 : aHR0cHM6Ly93d3cuNWR5Ni52aXAvdm9kcGxheS85MTA5Ny02LTEuaHRtbA==
我这边因为分析过多次,发现每次再请求.m3u8之前,都会有一次webscoket的请求,那么断定websocket一定是非常重要的
其实在这里我们已经拿到了m3u8的视频地址,但是只拿到m3u8的地址不是咱们的目的。
当我们点击上图右边蓝色链接进入这个main.js的这个js文件中发现,他是加密的如下图
其实也可以侧面的反应出,我们的推断是没错的。 如果不是非常重要的东西,他基本不可能加密
我们将main.js的代码复制下来,然后开始进行还原,我是复制了两份一份是没有格式化的代码,另一分是格式化过的代码。
const parser = require("@babel/parser");const template = require("@babel/template").default;const traverse = require("@babel/traverse").default;const t = require("@babel/types");const generator = require("@babel/generator").default;const path = require("path");const fs = require("fs");const {decryptStr,decryptStrFunName } = require("E:\\desktop\\2\\decrypt.js");let filepath = "e:/desktop/main.js";fs.readFile(filepath,{"encoding":"utf-8"},function(err,data) { const ast = parser.parse(data); step(ast); let {code} = generator(ast); fs.writeFile("e:/desktop/main1.js",code,err => { if(err) throw err; console.log("保存成功"); });});function step(ast) { traverse(ast,{ CallExpression: ObjToStr, })}function ObjToStr(path) { let node = path.node; try{ if(node.callee.name == decryptStrFunName && node.arguments.length == 2) { let val = decryptStr(node.arguments[0].value,node.arguments[1].value); path.replaceWith(t.stringLiteral(val)); } }catch(e) { console.log(e); }}
这里我采用了一个简单的方案,就是我从网页分析这个执行流程,发现他应该走得下面得getcookie,而且我发现他得这个函数是个循环,如下图。那么我们已经知道他应该进入getCookie那么我们就不浪费时间了。
我们可以看到进入getCookie函数的条件是_0x163f4这个函数返回为true,那么我们就直接让他返回true,当我们再次运行的时候发现他已经可以进入getCookie了,此时这个暗桩被我门解决了
可以看到我们需要操作的这个地方正好就是MemberExpression的type
let filepath = "e:/desktop/2/main1.js";fs.readFile(filepath,{"encoding":"utf-8"},function(err,data) { const ast = parser.parse(data); step(ast); let {code} = generator(ast); fs.writeFile("e:/desktop/2/main2.js",code,err => { if(err) throw err; console.log("保存成功"); });});function step(ast) { traverse(ast,{ MemberExpression:HandleMemberExpression, })} function HandleMemberExpression(path) { if(path.get("object").isIdentifier() && t.isLiteral(path.node.property)) { let objname = path.node.object.name; let propertyValue = path.node.property.value; let bindPath = path.scope.getBinding(objname); if(bindPath === undefined) return; try{ if(!t.isObjectExpression( bindPath.path.node.init)) return; } catch(e) { console.log(e.message); return ; } let propertys = bindPath.path.node.init.properties; if(propertys.length === 0) return; try{ for (let i = 0;i<propertys.length;i++) { console.log(propertys[i].key.value); if(propertys[i].key.value === propertyValue) { if(!t.isStringLiteral( propertys[i].value)) return; path.replaceWith(propertys[i].value); break; } } }catch (e){ console.log(e.message); console.log(e.stack); } }}
我们可以看到右边的结果比左边又更清晰了不少,而且很多函数调用的参数上已经是明文字符串了
let filepath = "e:/desktop/2/main1.js";fs.readFile(filepath,{"encoding":"utf-8"},function(err,data) { const ast = parser.parse(data); step(ast); let {code} = generator(ast); fs.writeFile("e:/desktop/2/main2.js",code,err => { if(err) throw err; console.log("保存成功"); });});function step(ast) { traverse(ast,{ CallExpression: HandleCallExpression, })}function HandleCallExpression(path) { if(path.get("callee").isMemberExpression()) { let objname = path.node.callee.object.name; if(!t.isStringLiteral(path.node.callee.property)) return; let propertyValue = path.node.callee.property.value; let bindPath = path.scope.getBinding(objname); if(bindPath === undefined) return; if(!(t.isVariableDeclarator(bindPath.path.node) && t.isObjectExpression(bindPath.path.node.init))) return; let propertys = bindPath.path.node.init.properties; if(propertys.length === 0) return; let args = path.node.arguments; try { for (let i = 0; i < propertys.length; i++){ if(propertys[i].key.value === propertyValue) { if(!t.isFunctionExpression(propertys[i].value)) return; console.log(propertys[i].key.value); let retStmt = propertys[i].value.body.body[0]; if(t.isCallExpression(retStmt.argument) && t.isIdentifier(retStmt.argument.callee) ) { path.replaceWith(t.CallExpression(args[0],args.slice(1))); break; }else if (t.isBinaryExpression(retStmt.argument) && args.length === 2) { path.replaceWith(t.BinaryExpression(retStmt.argument.operator,args[0],args[1])); break; } } } }catch (e) { console.log(e.message); console.log(e.stack); } }}
from Crypto.Cipher import AESKEY= b"55ca5c48a943afdc"IV = b"d11424dcecfe16c0"def AesDecrypt(data,key,iv): cipher = AES.new(key,AES.MODE_CBC,iv) text_decrypted = cipher.decrypt(data) unpad = lambda s: s[0:-s[-1]] text_decrypted = unpad(text_decrypted) return text_decrypted.decode("utf-8")def main(): data = "05B1BEB5AF7E36DA88C6F05F66DE36B538D89E87EF822C966F1EBDDE39A4821C3EB3252E8CBFB7CC0959277CDEAF66E1967EFA09CC0C3CDDDBFC10ACBE5A0916E69C889648B4B0E948A294686803F72AB0082C24C0C70A43FA41DCDEF7C9B8E5593A450203F7A9824D04AEAFF85BF0888D28EA33BE8138B6BDF58FE1D0FBA21851C1880AA5A735D9E3E4947088C3BCFEA59AD9FB577E34080A6088BB749FE217637F07C86044B1B52565AA4E21C9DFF88B3294513DCF6B829EDC699555105BD3C6102B462A999638BA77280A0765C6A2" hexdata = bytes.fromhex(data) result = AesDecrypt(hexdata,KEY,IV) print(result)if __name__ == '__main__': main()
from Crypto.Cipher import AESKEY=b"55ca5c48a943afdc"IV = b"d11424dcecfe16c0"def AesDecrypt(data,key,iv): cipher = AES.new(key,AES.MODE_CBC,iv) text_decrypted = cipher.decrypt(data) unpad = lambda s: s[0:-s[-1]] text_decrypted = unpad(text_decrypted) return text_decrypted.decode("utf-8")def main(): data = "B41585343636DAF917D1FE95FDB299994966407EED69F70C9D63E68CE41CEEFBF158890F7FC3ABB31E71606B02C429599EB6552EA662086F99627682BA5B079297C072E4A63427CCAA26B4C3E94A04B2E5693F721F49F15FF12D269BB0D7343F20D8C7AE314B64AC5A9186D3B14A95D9F7675ED41C772A506AB6EF7F379ED21741CBBD55DBF408A940B751EB723CDE6AD795C18503A902FCE32A9D6880A15E196ECBAAE12B96BCB3C04383ED80791BD22CC0FD77744D00DEC1A9FB9B0DB09BCAE416069B399683199D61F9EF72EFF3022CB4320AF8C7BCAF9DC60796257449051F7713E2B1DDF078B35A28D32C5EF913" hexdata = bytes.fromhex(data) result = AesDecrypt(hexdata,KEY,IV) print(result)if __name__ == '__main__': main()
解密后的数据如下图
{"type":"getUrl","url":"HGbFux0Gub4gJ9LonhdVbo000orckSwQNMo000oiyo000oNH8PjeqHuSNsrkYvqFwpEd5YDZpUh352CUmhznDWlodcmfeA0mJz0qkbEAkychz2TBRLCGG1gO0O0O","sign":"7a4b804dc54d93acc5992f6f48221f9338cf896b00693a8c00a0e4d720ddb4fe"}
到此处我们来分析一下参数
查找url的来源,这里采用一种简单的方法, 我们直接在网页f12这里 ctrl+shift+f 搜索HGbFux0Gub4gJ9LonhdVbo000orckSwQNMo000oiyo000oNH8PjeqHuSNsrkYvqFwpEd5YDZpUh352CUmhznDWlodcmfeA0mJz0qkbEAkychz2TBRLCGG1gO0O0O
此时我们搜到到他其实就是在请求地址的主页上就有
查找sign的来源,如下图
我们发现他其实就是用url的值进行了sha,你可以看到_0x1ad6f6正好就是url的值 同时也传进了createSign这个函数内
验证sign
from hashlib import sha256import hmacdef HmacHash256(data): rawdata = data.encode("utf-8") key = b"55ca5c4d11424dcecfe16c08a943afdc" return hmac.new(key, rawdata, digestmod=sha256).digest().hex()def main(): data = "HGbFux0Gub4gJ9LonhdVbo000orckSwQNMo000oiyo000oNH8PjeqHuSNsrkYvqFwpEd5YDZpUh352CUmhznDWlodcmfeA0mJz0qkbEAkychz2TBRLCGG1gO0O0O" result = HmacHash256(data) print(result)if __name__ == '__main__': main()
至此发送数据的所有流程已经分析完成
我看到很多评论说,只要告诉我如何使用就行,ok,为了满足你们,我做一个二进制得命令行工具,打包到附件,附件里有说明