首页 > 其他分享 >爬虫案例之有道翻译execjs改写(JS代码改良版)

爬虫案例之有道翻译execjs改写(JS代码改良版)

时间:2023-05-14 09:12:13浏览次数:43  
标签:return 函数 改良版 crypto JS 参数 sign execjs 我们

网易有道翻译之逆向破解[execjs代码改写]

***用到的知识点:

(1)requests模块和session模块,发起请求

  • 什么是session对象?
    • 该对象和requests模块用法几乎一致.
    • 对于在请求过程中产生了cookie的请求
    • 如果该请求是使用session发起的,则cookie会被自动存储到session中.
session = requests.Session()

详见博客:https://www.cnblogs.com/dream-ze/p/17176494.html

(2)headers头部伪装,随机UA伪装

# UA伪装之随机headers模块
from fake_useragent import UserAgent

headers = {'User-Agent': UserAgent().random,} # 这里的random方法不需要random模块支持

(3)execjs的使用

# 在cmd中调起外部程序, 阻塞的方式
os.system(command="python test.py")

# 在cmd中调起外部程序, 非阻塞的方式
import subprocess
process = subprocess.Popen(command="python test.py", stderr=subprocess.PIPE)
# 终止进程
# process.kill()	

# communicate: 当前线程阻塞,直到子进程执行结束
stdout, stderr = process.communicate(timeout=172800)
# 简单理解就是为了防止 execjs 执行代码时报错
import subprocess
from functools import partial
subprocess.Popen = partial(subprocess.Popen, encoding='utf-8')
# 导入Python中执行 js 代码的模块
import execjs

# 打开JS 代码文件
    # 注意这里可能会报一个错误:文中出现了连‘gb18030’也无法编码的字符,可以使用‘ignore’属性进行忽略
    # UnicodeDecodeError: 'gbk' codec can't decode byte 0xbc in position 405: illegal multibyte sequence
    with open("js文件路径", encoding='gb18030', errors="ignore") as f:
        # 将JS 代码读出来
        jsCode = f.read()
    # 将读出来的JS代码交给 execjs 进行编译
    JS_sign_t = execjs.compile(jsCode)
    # call() 运行代码中的xxx函数. 后续的参数是xxx的参数 # 后面的参数可留空
    result = JS_sign_t.call("需要调用的函数名")

详见博客:https://www.cnblogs.com/dream-ze/p/17362503.html

(4)json模块,序列化与反序列化

# 在Python中将文本数据类型转换为字典类型
dataDict = json.loads(decrypt_data)

详见博客:https://www.cnblogs.com/dream-ze/p/17337544.html

(5)逆向改写之引入外部包

  • 我们在逆向补写 JS 代码时,时常会遇到需要外部包的情况,在Python里面我们可以直接 import 但是在 JS 里面是这样引入外部包的
// const 自己定义的变量名 = require('需要导入的包名');

// 导入crypto 加密算法的包
const crypto = require('crypto');

(6)逆向改写知识补充之Node.js Buffer.alloc()方法

  • Buffer.alloc()方法用于创建指定大小的新缓冲区对象。
  • 此方法比Buffer . alloconsafe()方法慢,但它确保新创建的 Buffer 实例永远不会包含可能敏感的旧信息或数据。
  • 语法
Buffer.alloc(size, fill, encoding)
  • 参数:

    • 该方法接受三个参数,如上所述,如下所述:
      • 大小:指定缓冲区的大小。
      • 填充:为可选参数,指定填充缓冲区的值。其默认值为 0。
      • 编码:如果缓冲区值是字符串,它是指定值的可选参数。其默认值为‘utf8’
  • 返回值:

    • 这个方法返回一个新的指定大小的初始化缓冲区。
    • 如果给定的大小不是数字,将引发类型错误。
  • 例一

    // Node.js program to demonstrate the   
    // Buffer.alloc() Method
    
    // Allocate buffer of given size
    // using buffer.alloc() method
    var buf = Buffer.alloc(6);
    
    // Prints: <Buffer 00 00 00 00 00 00>
    console.log(buf);
    
    • 结果为

      <Buffer 00 00 00 00 00 00>
      
  • 例二

    // Node.js program to demonstrate the   
    // Buffer.alloc() Method
    
    // Allocate buffer of given size
    // using buffer.alloc() method
    var buf = Buffer.alloc(6, 'a');
    
    // Prints <Buffer 61 61 61 61 61>
    console.log(buf);
    
    • 结果为

      T2<缓冲区 61 61 61 61 61 >
      

【一】分析网站

  • 网站主页

  • 打开开发者模式进行抓包分析

  • 一共抓到了三个包,逐个分析抓到的包里,我们可以发现
    • 第一个包的请求方式为post请求
    • 且载荷中带着一系列的数据

  • post请求所携带的数据data

i: apple
from: auto
to: 
domain: 0
dictResult: true
keyid: webfanyi
sign: c56f096d38dc6fbbf217842ddcd092ce
client: fanyideskweb
product: webfanyi
appVersion: 1.0.0
vendor: web
pointParam: client,mysticTime,product
mysticTime: 1683975605075
keyfrom: fanyi.web
  • 从上述分析以及再次抓包可以发现:

    • i : 是我们输入的值
    • sign:每次都会发生变化
      • 可能是有加密算法进行加密返回
    • mysticTime:每次都会发生变化
      • 根据经验和验证知道这是时间错
  • 再观察返回的response数据可以发现是一堆字符

    • 通过观察可以发现,是base64编码返回的结果
    • 并且可以发现,其不是标准的base64编码的数据,而是进行了变种
Z21kD9ZK1ke6ugku2ccWu-MeDWh3z252xRTQv-wZ6jddVo3tJLe7gIXz4PyxGl73nSfLAADyElSjjvrYdCvEP4pfohVVEX1DxoI0yhm36ytQNvu-WLU94qULZQ72aml6JKK7ArS9fJXAcsG7ufBIE0gd6fbnhFcsGmdXspZe-8 whVFbRB_8Fc9JlMHh8DDXnskDhGfEscN_rfi-A-AHB3F9Vets82vIYpkGNaJOft_JA-m5cGEjo-UNRDDpkTz_NIAvo5PbATpkh7PSna2tHcE6Hou9GBtPLB67vjScwplB96-zqZKXJJEzU5HGF0oPDY_weAkXArzXyGLBPXFCnn_IWJDkGD4vqBQQAh2n52f48GD_cb-PSCT_8b-ESsKUI9NJa11XsdaUZxAc8TzrYnXwdcQbtl_kZGKhS6_rCtuNEBouA_lvM2CbS7TTtV2U4zVmJKpp-c6nt3yZePK3Av01GWn1pH_3sZbaPEx8DUjSbdp4i4iK-Mj4p2HPoph67DR7B9MFETYku_28SgP9xsKRRvFH4aHBHESWX4FDbwaU=

【二】逆向处理一

【1】包的分析处理

  • 首先,我们知道这个请求的网站是:

    https://dict.youdao.com/webtranslate

  • 返回的数据也是从这个网站返回的

  • 那么,所有的请求和加密操作一定会经过这个网站的调用

  • 以此为关键点,我们进行关键字搜索

    即在开发者模式搜索 webtranslate 关键字

  • 得到搜索结果,该结果就是调用此网址的一个函数位置
  • 我们进入到这个函数里面

  • 我们可以清楚地看到,在函数的内部存在我们请求的网址

  • 在该位置打上断点,重新发起请求

    • 如果被断住,则是我们想要的数据位置
    • 如果没被断住,则二次请求,查看其他位置

  • 在该位置被断住
  • 在控制台,输入各个函数或参数即可查看这个参数的结果
  • 通过分析这行代码
y = (e,t)=>Object(a["d"])("https://dict.youdao.com/webtranslate", Object(n["a"])(Object(n["a"])({}, e), O(t)), {
                headers: {
                    "Content-Type": "application/x-www-form-urlencoded"
                }
            })
  • 我们可以发现

    • "https://dict.youdao.com/webtranslate"

      • 这个参数是我们请求的网址
    • Object(n["a"])(Object(n["a"])({}, e), O(t))

      • 这个参数是一个函数调用,我们在控制台将这行代码进行打印查看结果
    • 我们发现其返回的结果正是我们刚才data数据的一部分

      {i: 'apple', from: 'auto', to: '', domain: '0', dictResult: true, …}
      appVersion: "1.0.0"
      client: "fanyideskweb"
      dictResult: true
      domain: "0"
      from: "auto"
      i: "apple"
      keyfrom: "fanyi.web"
      keyid: "webfanyi"
      mysticTime: 1683977202745
      pointParam: "client,mysticTime,product"
      product: "webfanyi"
      sign: "77ca196b587d32de00b9b249153ed901"
      to: ""
      vendor: "web"
      

【2】sign 值 和 t 值的逆向

  • 事实证明,我们没有选错位置

    • 我们也可以在控制台查看其他参数的结果
  • 通过分析发现 O 函数正是我们想要找的函数

  • 我们在 O 函数处打上断点,再次进行请求

  • 将鼠标悬浮在 O 函数上,进入到其调用函数里面

  • 进入到函数调用处

  • 我们可以看到,这里有一个t变量的声明

    • 且这个赋值函数正是时间函数,也就是时间戳

    • 我们将其扣出来放到 JS 文件里

      // 声明 t 变量:时间戳
      const t = (new Date).getTime();
      
  • 我们向下看,则可以发现,sign 值正是由 h 函数生成

  • 同样的方法,我们进入到函数 h 中,分析其代码

  • 进入到 h 函数

  • 观察发现,需要两个参数,一个参数是 t ,另一个参数是 e

  • 这里我们进入到这个断点的函数,观察其生成值

  • 我们可以看到传入的值

    e : 1683977894302
    t : fsdsogkndfokasodnaso
    
  • 再次发起请求,观察这两个值是否会发生变化

    • 发起请求后发现,e发生了变化正是时间戳
    • t没有发生变化,是定值
  • 分析这个js代码,h 函数的方法是什么

    v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`)
    
    # 这段函数其实是ES6的语法,类似于Python中的
    name = 'dream'
    age = 22
    ptint(f'你好,我叫{name},我今年{}')
    
    #而在ES6语法中相当于
    consolo.log(`你好,我叫${name},我今年${name}`)
    
  • 由此我们知道,其返回的正式一段文本字符串,我们在控制台打印这段函数调用,查看其结果

`client=${d}&mysticTime=${e}&product=${u}&key=${t}`

'client=fanyideskweb&mysticTime=1683977894302&product=webfanyi&key=fsdsogkndfokasodnaso'
  • 由此,我们也将其扣出来,并补上所有的参数

    // 声明变量,h 函数有用
    var e = "fsdsogkndfokasodnaso";
    var d = "fanyideskweb";
    var u = "webfanyi";
    
    function h(e, t) {
        return (`client=${d}&mysticTime=${e}&product=${u}&key=${t}`)
    }
    
  • 有上面分析的 js 代码可知

  • h 函数的最终返回值为

    return (`client=${d}&mysticTime=${e}&product=${u}&key=${t}`)
    
  • 那下一步,我们就要查看这个参数传进去的 v 函数的调用关系及返回结果

  • 同样的方法,我们悬浮进入 v 函数

  • 可以看到我们的 v 函数正是,其上面的代码部分

    function v(e) {
                    return r.a.createHash("md5").update(e.toString()).digest("hex")
                }
    
  • 其中的参数e就是我们刚才生成的一串字符串

  • 分析这段代码,我们不难看出,这正是一个md5加密,并且返回的是其16进制的字符串

  • 我们也将其扣出来,注意这里有一个细节

    • 我们将源代码扣出来运行后,他会告诉我们 r 没有定义
    • 这里我们采用替换法,用 crypto 模块替代r.a
    // 导入crypto 加密算法的包
    const crypto = require('crypto');
    // 这里要验证sign值生成的是否相等
    
    function v(e) {
        // 返回 md5 加密后的 sign 值
        // 这里有一个傻逼的设定,你传参进来的参数名要和下面的这个参数名一样,否则生成的就是错的 sign值
        return crypto.createHash("md5").update(e.toString()).digest("hex")
    }
    
  • 至此我们的 sign 值和 t 值 就已经完全逆向出来了

  • 值得注意的是,我们要结合我们已经生成的sign值和t值,把参数写死,然后查看其sign值生成是否相等

  • 总结:

    • 首先要根据关键字,找到根源
    • 接着要根据这个关键字所在的函数找到其参数生成的位置以及参数的调用关系
    • 通过值得比较和调用关系的确定,进入到相应的调用函数中
    • 分析其代码生成的方式
    • 最后根据我们的知识对其进行逆向
    • 最后得到我们想要的结果

【3】sign 值 和 t 值的获取

  • 01.sign.js 文件改写
// 导入crypto 加密算法的包
const crypto = require('crypto');

// 声明 t 变量:时间戳
const t = (new Date).getTime();
// 声明变量,h 函数有用
var e = "fsdsogkndfokasodnaso";
var d = "fanyideskweb";
var u = "webfanyi";

function h(e, t) {
    return (`client=${d}&mysticTime=${e}&product=${u}&key=${t}`)
}
// 这里要验证sign值生成的是否相等
function v(e) {
    // 返回 md5 加密后的 sign 值
    // 这里有一个傻逼的设定,你传参进来的参数名要和下面的这个参数名一样,否则生成的就是错的 sign值
    return crypto.createHash("md5").update(e.toString()).digest("hex")
}

// 定义 程序入口
function main() {
    sign = v(h(t, e))
    // JS 返回一组数据要用字典,数组或者两个单值不行
    return [sign, t]
}

【二】逆向处理二

【1】承上启下

  • 从上面我们进行逆向,返回的结果就是我们响应中的base64变种编码数据
  • 接下来我们需要进行的操作就是将base64编码数据进行解码
  • 我们知道,我们的base64编码解码后的数据也不是明文数据,一定还存在加密算法
  • 所以我们接下来的就是进行解码和解密

【2】js 代码分析

  • 再次回到我们这个网址调用的地方,我们知道这个函数就是将我们的数据发送到这网站,并携带一定的请求头

  • 那我们只需要找到这个参数到底是被传入到那个函数进行调用,我们就知道了解密的位置

  • 通过观察这行js代码,我们分析得到

    y = (e,t)=>Object(a["d"])("https://dict.youdao.com/webtranslate", Object(n["a"])(Object(n["a"])({}, e), O(t)), {
                    headers: {
                        "Content-Type": "application/x-www-form-urlencoded"
                    }
                })
    
  • 最后调用的函数是

    Object(a["d"])
    
  • 同样的方法我们进入到这个函数

  • 分析这段 js 代码

    function l(e, t, o) {
                return new Promise((n,c)=>{
                    a["a"].post(e, t, o).then(e=>{
                        n(e.data)
                    }
                    ).catch(e=>{
                        c(e)
                    }
                    )
                }
                )
            }
    
  • 我们可以知道

    • Promise 函数是 JS 函数中的一个回调函数

  • 通过断点分析这段函数中传入的参数 e t o 正是我们前面对应的,请求网址,data参数 ,和请求头
  • 我们再次分析我们请求网址所在的哪行代码
  • 我们需要明白的思路是
    • 我们需要调用一个函数,如果调用的这个函数的参数是下一个函数所生成的,那我们就要先执行第二个函数,将第二个函数的返回值,给第一个函数进行调用。
  • 由此,我们可以利用调用堆栈,调用堆栈就是函数之间逐级的调用关系,其中越靠上表示越被后调用
  • 那我们想知道是哪个函数想获得这个函数的调用值进行二次解析
  • 那我们只需要向上返回一层

  • 我们现在执行的是 y 函数 ,他的上一级调用函数就是 ln 函数 ,ln函数需要对 y 函数的结果进行运算。而我们 y 函数的调用结果我们已经解析出来了,正是我们得到的 base64编码数据

【3】逆向算法分析

  • 我们进入到 ln 函数的调用方法中

    an["a"].getTextTranslateResult(Object(nn["a"])(Object(nn["a"])({
                    i: e.data.keyword,
                    from: e.data.from,
                    to: e.data.to
                }, n), {}, {
                    dictResult: !0,
                    keyid: "webfanyi"
                }), o).then(o=>{
                    an["a"].cancelLastGpt();
                    const n = an["a"].decodeData(o, sn["a"].state.text.decodeKey, sn["a"].state.text.decodeIv)
                      , a = n ? JSON.parse(n) : {};
                    console.log("解密后的接口数据:", a),
                    0 === a.code ? e.success && t(e.success)(a) : e.fail && t(e.fail)(a)
                }
    
  • 从 这个函数的名字我们不难理解,正是得到文本翻译结果的意思

  • 这也表明我们的思路是正确的

  • 再往下进行分析代码

  • 我们看到了很眼熟的代码

    const n = an["a"].decodeData(o, sn["a"].state.text.decodeKey, sn["a"].state.text.decodeIv)
    
  • 从这段代码,我们不难看出,正是进行了解密动作,同时从函数中的结构我们也不难发现 , 非常 符合我们 AES 中的 CBC 加密模式。参数分别对应 (加密数据,key值,iv偏移量)

  • 接下来我们在该处进行断点

  • 从这个位置我们可以很明显的看到,传入进来的 O 值正是我们解密后的base64编码数据,
  • 这也更加印证了我们的思路是正确的,并没有跑偏

【4】逆向算法解密

  • 接下来我们的思路也很明确
  • 首先,我们要分析代码,找到我们加密所用到的key和iv
  • 然后进行base64编码的解码
  • 最后进行算法解密
  • 在断点位置,我们查看这两个值

  • 通过反复断点,重新进入,分析发现这两个值是定值,并不是随机变化的
decodeKey:"ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl"
decodeIv:"ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4"
  • 我们知道,我们AES算法中传入进去的是二进制数据,这是字符串,显然不是,那我们就需要知道这个数据是如何编程二进制的(要考虑到其他的加密方式,而不是简单的就将字符串变成二进制就完事了)

  • 我们进入到decode函数中查看其内部代码

  • 在这里我们看到了他完整的AES对象生成过程

  • 在这里我们要注意,在逆向生成js代码时
S = (t,o,n)=>{
                if (!t)
                    return null;
                const a = e.alloc(16, g(o))
                  , c = e.alloc(16, g(n))
                  , i = r.a.createDecipheriv("aes-128-cbc", a, c);
                let s = i.update(t, "base64", "utf-8");
                return s += i.final("utf-8"),
                s
            }
  • 在这段代码里,这是一个箭头函数,我们要先将其改成有名函数

  • 将r.a替换成加密算法

  • 补全必要的参数

  • 但是运行JS代码时,会报错,说 e 没有被定义

    • 这里我们采用替换法,同时也可以百度 搜一下 e.alloc
    • 这是node.js 里面的一个方法叫 Node.js Buffer.alloc()方法
  • 所以我们可以将代码改写如下

    const crypto = require('crypto');
    function decrypt(t) {
    
        if (!t)
            return null;
        const a = Buffer.alloc(16, g(o))
            , c = Buffer.alloc(16, g(n))
            , i = crypto.createDecipheriv("aes-128-cbc", a, c);
        let s = i.update(t, "base64", "utf-8");
        return s += i.final("utf-8")
    }
    
  • 进入到该断点位置

  • 从这里我们可以看到,我们的 key 和 iv 传给了我们的 g 函数进行了加密
  • 我们进入到 g 函数

  • 在这里我们很明显的可以看到,他也是一个md5加密,但是这次取的是2进制的字符串
function g(e) {
                return r.a.createHash("md5").update(e).digest()
            }
  • 我们将这段代码进行改写

    function g(e) {
        return crypto.createHash("md5").update(e).digest()
    }
    
  • 至此,我们的所有参数就都逆向生成完了

  • 小结:

    • 首先我们要明白各个函数之间的调用关系
    • 处理好各个函数之间的调用关系至关重要
    • 知道了各个函数之间的调用关系后,我们再逐层进行分析
    • 同时注意每一步出现的每个参数,每一个函数的意义,明白这些才能让我们的解密过程变得更快
    • 最后要有耐心
  • 02 decript.js

const crypto = require('crypto');

// 这个是加密 key 和 iv 的函数
function g(e) {
    return crypto.createHash("md5").update(e).digest()
}

function decrypt(t) {

    let o = "ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl"
    let n = "ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4"
    if (!t)
        return null;
    const a = Buffer.alloc(16, g(o))
        , c = Buffer.alloc(16, g(n))
        , i = crypto.createDecipheriv("aes-128-cbc", a, c);
    let s = i.update(t, "base64", "utf-8");
    return s += i.final("utf-8")
}

// console.log(decrypt(text))

【三】代码完善

【1】逆向部分一 “01 sign.js”

// 导入crypto 加密算法的包
const crypto = require('crypto');

// 声明 t 变量:时间戳
const t = (new Date).getTime();
// 声明变量,h 函数有用
var e = "fsdsogkndfokasodnaso";
var d = "fanyideskweb";
var u = "webfanyi";

function h(e, t) {
    return (`client=${d}&mysticTime=${e}&product=${u}&key=${t}`)
}
// 这里要验证sign值生成的是否相等
function v(e) {
    // 返回 md5 加密后的 sign 值
    // 这里有一个傻逼的设定,你传参进来的参数名要和下面的这个参数名一样,否则生成的就是错的 sign值
    return crypto.createHash("md5").update(e.toString()).digest("hex")
}

// 定义 程序入口
function main() {
    sign = v(h(t, e))
    // JS 返回一组数据要用字典,数组或者两个单值不行
    return [sign, t]
}

【2】逆向部分二 “02 decript.js”

// 导入crypto 加密算法的包
const crypto = require('crypto');

// 声明 t 变量:时间戳
const t = (new Date).getTime();
// 声明变量,h 函数有用
var e = "fsdsogkndfokasodnaso";
var d = "fanyideskweb";
var u = "webfanyi";

function h(e, t) {
    return (`client=${d}&mysticTime=${e}&product=${u}&key=${t}`)
}
// 这里要验证sign值生成的是否相等
function v(e) {
    // 返回 md5 加密后的 sign 值
    // 这里有一个傻逼的设定,你传参进来的参数名要和下面的这个参数名一样,否则生成的就是错的 sign值
    return crypto.createHash("md5").update(e.toString()).digest("hex")
}

// 定义 程序入口
function main() {
    sign = v(h(t, e))
    // JS 返回一组数据要用字典,数组或者两个单值不行
    return [sign, t]
}

【3】逆向部分三 “main.py”

# requests:发送请求模块
import requests
# UA伪装之随机headers模块
from fake_useragent import UserAgent
# json 序列化模块
import json
import subprocess
from functools import partial
subprocess.Popen = partial(subprocess.Popen, encoding='utf-8')
# 导入Python中执行 js 代码的模块
import execjs


def get_sign():
    '''
    word:需要传入的单词/句子
    # 逆向获取到时间戳和sign值
    :return: 返回获取到md5加密值
    '''
    # 构建session对象:携带cookie,防止cookie过期
    session = requests.Session()
    # 有道翻译对应的主网址
    main_url = 'https://dict.youdao.com/webtranslate'
    # headers 请求头
    headers = {
        'Accept': 'application/json, text/plain, */*',
        'Accept-Encoding': 'gzip, deflate, br',
        'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive',
        'Content-Length': '239',
        'Content-Type': 'application/x-www-form-urlencoded',
        'Host': 'dict.youdao.com',
        'Origin': 'https://fanyi.youdao.com',
        'Pragma': 'no-cache',
        'Referer': 'https://fanyi.youdao.com/',
        'Sec-Fetch-Dest': 'empty',
        'Sec-Fetch-Mode': 'cors',
        'Sec-Fetch-Site': 'same-site',
        'Cookie': '[email protected]; OUTFOX_SEARCH_USER_ID_NCOO=1723343714.3489342; YOUDAO_MOBILE_ACCESS_TYPE=0',
        'User-Agent': UserAgent().random,

    }
    # 构建sign值和时间戳
    # 打开JS 代码文件
    # 注意这里可能会报一个错误:文中出现了连‘gb18030’也无法编码的字符,可以使用‘ignore’属性进行忽略
    # UnicodeDecodeError: 'gbk' codec can't decode byte 0xbc in position 405: illegal multibyte sequence
    with open("01 sign.js", encoding='gb18030', errors="ignore") as f:
        # 将JS 代码读出来
        jsCode = f.read()
    # 将读出来的JS代码交给 execjs 进行编译
    JS_sign_t = execjs.compile(jsCode)
    # call() 运行代码中的xxx函数. 后续的参数是xxx的参数
    result = JS_sign_t.call("main")
    sign = result[0]
    mysticTime = result[1]
    # 构建 post 请求所需要的data参数
    word = input('请输入内容:')
    data = {
        'i': word,  # 传入的参数(单词/句子)
        'from': 'auto',
        'to': '',
        'dictResult': 'true',
        'keyid': 'webfanyi',
        'sign': sign,  # sign值 -- md5加密
        'client': 'fanyideskweb',
        'product': 'webfanyi',
        'appVersion': '1.0.0',
        'vendor': 'web',
        'pointParam': 'client,mysticTime,product',
        'mysticTime': mysticTime,  # --- 时间戳生成
        'keyfrom': 'fanyi.web',
    }
    # 发送请求,获取到加密的base64编码返回结果
    response = session.post(url=main_url, headers=headers, data=data)
    ret = response.text
    # 返回数据,等待下一步调用
    return ret


def decript_data(ret):
    # 打开JS 代码文件
    # 注意这里可能会报一个错误:文中出现了连‘gb18030’也无法编码的字符,可以使用‘ignore’属性进行忽略
    # UnicodeDecodeError: 'gbk' codec can't decode byte 0xbc in position 405: illegal multibyte sequence
    with open("02 decript.js", encoding='gb18030', errors="ignore") as f:
        # 将JS 代码读出来
        jsCode = f.read()
    # 将读出来的JS代码交给 execjs 进行编译
    JS_sign_t = execjs.compile(jsCode)
    # call() 运行代码中的xxx函数. 后续的参数是xxx的参数
    decrypt_data = JS_sign_t.call("decrypt", ret)
    # 将逆向破解出来的文本进行json序列化转成字典,方便下面做数据提取
    dataDict = json.loads(decrypt_data)
    # 提取翻译后的结果
    translate_result = [lineDict.get("tgt") for lineDict in dataDict.get("translateResult")[0]]
    # 如果是长句子,考虑到每一句话的拼接
    translate_result = "\n".join(translate_result)
    print(translate_result)


def choose_y_n(choose_result):
    if choose_result == 'y':
        use()
    else:
        print('感谢您的使用!祝您生活愉快!')


def use():
    ret = get_sign()
    decript_data(ret)
    choose_result = input('继续使用请输入y:::')
    choose_y_n(choose_result)


if __name__ == '__main__':
    use()

标签:return,函数,改良版,crypto,JS,参数,sign,execjs,我们
From: https://www.cnblogs.com/dream-ze/p/17398723.html

相关文章

  • JS async/await 的理解和用法
    1.asyncasync是一个加在函数前的修饰符,被async定义的函数会默认返回一个Promise对象resolve的值。因此对async函数可以直接then,返回值就是then方法传入的函数。2.awaitawait也是一个修饰符,只能放在async定义的函数内。可以理解为等待。async一般用在获取res修饰,await一般......
  • 【nodejs基础】Expres编写接口详解05
    一、使用Express写接口浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域接口的跨域问题刚才编写的GET和POST接口,存在一个很严重的问题:不支持跨域请求,解决接口跨域问题的方案主要有两种CORS(主流解决方案,推荐)JSONP(有缺陷:只支持GET请......
  • 【Azure 应用服务】Azure JS Function 异步方法中执行SQL查询后,Callback函数中日志无
    Warning:Unexpectedcallto'log'onthecontextobjectafterfunctionexecutionhascompleted.Pleasecheckforasynchronouscallsthatarenotawaitedorcallsto'done'madebeforefunctionexecutioncompletes.Th......
  • 爬虫案例之网易有道翻译JS代码复杂版
    网易有道翻译逆向案例本次案例逆向的是网易有道云翻译https://fanyi.youdao.com/index.html#/用到的知识包括requests模块及方法md5加密js代码环境的补全【一】分析网站(1)网站页面如图(2)抓包(3)分析抓到的包逐个查看每个包的标头和载荷在webtranslate这个......
  • js计算一个矩形内部,有一个等比缩放的矩形,如何判断宽和高那个先溢出外层的矩形
    最近在做jscanvas绘图需求时,遇到一个矩形图形重叠逻辑判断问题。一个任意矩形内部,有一个任意等比缩放的矩形,如何判断宽和高那个先溢出外层的矩形?宽和高那个先贴到边上?可以根据两个矩形的比例关系来判断宽和高那个先溢出。首先计算出两个矩形的宽高比,然后比较它们的大小关系。......
  • js计算一个矩形内部,有一个等比缩放的矩形,如何判断宽和高那个先溢出外层的矩形
    最近在做jscanvas绘图需求时,遇到一个矩形图形重叠逻辑判断问题。一个任意矩形内部,有一个任意等比缩放的矩形,如何判断宽和高那个先溢出外层的矩形?宽和高那个先贴到边上?可以根据两个矩形的比例关系来判断宽和高那个先溢出。首先计算出两个矩形的宽高比,然后比较它们的大小关系。......
  • 常用模块,time,random,json,os
    模块底层都是c语言写的模块的分类内置模块,不需要自己安装,直接拿过来用扩展模块,第三方模块,需要自己安装本地编辑器安装小白教程(forchange.cn)random随机数.random()不入参,求(0,1)之间的随机数,开区间.randint(a,b)求随机整数,闭区间[a,b].randrange(start,stop,step......
  • mockjs
    开始mock一个用于拦截ajax请求,并返回模拟数据的库。主要让前端独立于后端进行开发,通过pnpmaddmockjs来进行安装基础初窥门径vardata=Mock.mock({//属性list的值是一个数组,其中含有1到10个元素'list|1-10':[{//属性id是一个自增数,起始值为1......
  • JSP_5.11_课堂笔记
    insert.jsp<%@pageimport="java.sql.Statement"%><%@pageimport="java.sql.Connection"%><%@pageimport="java.sql.DriverManager"%><%@pagelanguage="java"contentType="text/html;charset=UT......
  • 解决vue.js出现Vue.js not detected错误
    VUEvue-devtools安装成功,但是图标为灰色,且控制台无vue选项的解决办法今天在学习VUE的过程中,安装了vue-devtools工具,但是发现图标一直是灰色,解决后,记录一下解决办法:1.查看拓展程序打开开发者模式和插件,如图所示两个开关,具体操作为:点击浏览器右上角三个点→更多工具→扩展工具......