js逆向爬虫1

目标:有道翻译

首先输入两个翻译,发现提交的参数请求中两个参数不同,一个是带time的,另一个是sign。很明显一个是时间生成器生成的数,另一个是做了加密的签名,很可能包含mysticTime的加密

image-20221224231117551

在截包器中搜索关键词sign,发现有很多js包含,不好定位;因此转而去直接搜索mysticTime,定位到app.js

image-20221224231430496.png

查看源文件,格式化后Ctrl+F搜索定位mysticTime,发现只有一个匹配结果,且很容易看出sign后的那个b函数就是签名函数

image-20221224231548222.png

打断点调试,输入一个新的翻译,页面来到sign前,此时t已经在前一行的getTime函数中得到值;只需知道sign函数即可,单步进入函数

image-20221224232029705.png

发现函数返回一个拼接的字符串(r="fanyideskweb",i="webfanyi"),并经过p函数加密处理,再次单步进入p函数

image-20221224232239587.png

此时这个函数看起来很像Crypto库的哈希函数加密的,光标移到方法名前的c.a,可以看到其下有很多方法,基本验证猜想

image-20221224232354743.png

在网上搜索到了示例代码如下,格式完全一样

image-20221224232648330.png

最后只需确定最开始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())

输出如下,格式正确

image-20221224234137615

编写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)

发现返回结果被加密,说明前端做了解密处理,需要找到解密函数

image-20221225110003120.png

从上一断点继续运行,很快就发现密文到明文的转换过程,可以看到下图蓝框中o接收了decode后的数据,并赋给a变量,而a变量正是解密后的数据,因此得出结论tn["a"].decodeData是关键解密函数

image-20221225110325162.png

在这个函数上打断点后再次提交翻译输入,进入函数

image-20221225110843997.png

复制该函数下来到js文件中测试

image-20221225111301360.png

image-20221225111237743.png

查询alloc函数发现属于Buffer下的方法,m函数也是c.a(Crypto)下的方法,直接替换为crypto

再次运行js,密文已经被解密

image-20221225111611881.png

运行python时发现始终报错,说是gbk编码问题

image-20221225112812183.png

解决方法:导入execjs前加入如下代码即可

import subprocess
from functools import partial

subprocess.Popen = partial(subprocess.Popen, encoding="utf-8")

最终效果图:

image-20221225114102436.png

完整代码:

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
}
最后修改:2022 年 12 月 29 日
如果觉得我的文章对你有用,请随意赞赏