js逆向爬虫1
目标:有道翻译
首先输入两个翻译,发现提交的参数请求中两个参数不同,一个是带time的,另一个是sign。很明显一个是时间生成器生成的数,另一个是做了加密的签名,很可能包含mysticTime的加密
在截包器中搜索关键词sign,发现有很多js包含,不好定位;因此转而去直接搜索mysticTime,定位到app.js
查看源文件,格式化后Ctrl+F搜索定位mysticTime,发现只有一个匹配结果,且很容易看出sign后的那个b函数就是签名函数
打断点调试,输入一个新的翻译,页面来到sign前,此时t已经在前一行的getTime函数中得到值;只需知道sign函数即可,单步进入函数
发现函数返回一个拼接的字符串(r="fanyideskweb",i="webfanyi"),并经过p函数加密处理,再次单步进入p函数
此时这个函数看起来很像Crypto库的哈希函数加密的,光标移到方法名前的c.a,可以看到其下有很多方法,基本验证猜想
在网上搜索到了示例代码如下,格式完全一样
最后只需确定最开始f函数传入的e值,再次运行发现传入e值不变,仍然为"fsdsogkndfokasodnaso"
编写js代码
const crypto = require('crypto');
const r = "fanyideskweb", i = "webfanyi";
function p(e) {
return crypto.createHash("md5").update(e.toString()).digest("hex")
}
function b(e, t) {
return p(`client=${r}&mysticTime=${e}&product=${i}&key=${t}`)
}
function f() {
const e = 'fsdsogkndfokasodnaso', s = 'client,mysticTime,product', u = 'fanyi.web', l = '1.0.0', d = 'web';
const t = (new Date).getTime();
return {
sign: b(t, e),
client: r,
product: i,
appVersion: l,
vendor: d,
pointParam: s,
mysticTime: t,
keyfrom: u
}
}
console.log(f())
输出如下,格式正确
编写python发送翻译请求
import requests
import execjs
ctx = execjs.compile(open('youdao.js', encoding='utf-8').read())
params = ctx.call('f')
data = {
'i': '你好',
'from': 'zh-CHS',
'to': 'en',
'domain': '0',
'dictResult': 'true',
'keyid': 'webfanyi'
}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54',
'Cookie': 'OUTFOX_SEARCH_USER_ID_NCOO=976405377.6815147; OUTFOX_SEARCH_USER_ID=-198948307@211.83.126.235; _ga=GA1.2.1162596953.1667349221; search-popup-show=12-2',
'Referer': 'https://fanyi.youdao.com/'
}
data.update(params)
results = requests.post('https://dict.youdao.com/webtranslate', data=data, headers=headers)
print(results.text)
发现返回结果被加密,说明前端做了解密处理,需要找到解密函数
从上一断点继续运行,很快就发现密文到明文的转换过程,可以看到下图蓝框中o接收了decode后的数据,并赋给a变量,而a变量正是解密后的数据,因此得出结论tn["a"].decodeData是关键解密函数
在这个函数上打断点后再次提交翻译输入,进入函数
复制该函数下来到js文件中测试
查询alloc函数发现属于Buffer下的方法,m函数也是c.a(Crypto)下的方法,直接替换为crypto
再次运行js,密文已经被解密
运行python时发现始终报错,说是gbk编码问题
解决方法:导入execjs前加入如下代码即可
import subprocess
from functools import partial
subprocess.Popen = partial(subprocess.Popen, encoding="utf-8")
最终效果图:
完整代码:
youdao.py
import requests
import subprocess
from functools import partial
subprocess.Popen = partial(subprocess.Popen, encoding="utf-8")
import execjs
translate_words = input('请输入要翻译的内容:')
ctx = execjs.compile(open('youdao.js', encoding='utf-8').read())
params = ctx.call('f')
data = {
'i': translate_words,
'from': 'AUTO',
'to': 'AUTO',
'domain': '0',
'dictResult': 'true',
'keyid': 'webfanyi'
}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54',
'Cookie': 'OUTFOX_SEARCH_USER_ID_NCOO=976405377.6815147; OUTFOX_SEARCH_USER_ID=-198948307@211.83.126.235; _ga=GA1.2.1162596953.1667349221; search-popup-show=12-2',
'Referer': 'https://fanyi.youdao.com/'
}
data.update(params)
results = requests.post('https://dict.youdao.com/webtranslate', data=data, headers=headers)
fanyi = eval(ctx.call('A', results.text))
print(fanyi['translateResult'][0][0]['tgt'])
youdao.js
const crypto = require('crypto');
const r = "fanyideskweb", i = "webfanyi";
function m(e) {
return crypto.createHash("md5").update(e).digest()
}
function p(e) {
return crypto.createHash("md5").update(e.toString()).digest("hex")
}
function b(e, t) {
return p(`client=${r}&mysticTime=${e}&product=${i}&key=${t}`)
}
function f() {
const e = 'fsdsogkndfokasodnaso', s = 'client,mysticTime,product', u = 'fanyi.web', l = '1.0.0', d = 'web';
const t = (new Date).getTime();
return {
sign: b(t, e),
client: r,
product: i,
appVersion: l,
vendor: d,
pointParam: s,
mysticTime: t,
keyfrom: u
}
}
A = (t,o,n)=>{
o = "ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl"
n = "ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4"
if (!t)
return null;
const a = Buffer.alloc(16, m(o))
, r = Buffer.alloc(16, m(n))
, i = crypto.createDecipheriv("aes-128-cbc", a, r);
let s = i.update(t, "base64", "utf-8");
return s += i.final("utf-8"),
s
}
2 条评论
博主你好,请问怎么知道的alloc函数属于Buffer下的方法, 我进入之后发现是其他的方法,一直补充所缺的方法,后面把e.alloc 改为Buffer.alloc 就可以了, 那Buffer这个是哪里来的呢
很抱歉,最近忙于科研没有及时看博客留言。这里js逆向其实不需要太多js相关知识,通常这些网站作者不会去写一大堆实现方法,而是调用函数库。如果接触过C语言的话可以知道alloc大概是申请地址空间的函数,去百度/谷歌搜下“js alloc”就可以看到很多结果显示Buffer.alloc(),就直接调用就好了