JS逆向——某道翻译
文章中所有内容仅供学习交流,不可用于任何商业用途和非法用途,如有侵权,请联系作者立即删除!
- 目标网站:aHR0cHM6Ly9mYW55aS55b3VkYW8uY29tL2luZGV4Lmh0bWwjLw==
一、定位接口
- 使用Chrome浏览器,打开Network进行抓包
- 输入要翻译的字符串:Hello World !
页面没有重新加载,初步判断是Ajax请求,我们看 Fetch/XHR请求,有两个请求
- 分析接口
经过查看分析,key的请求返回内容中不是我们需要的信息,只剩下webtranslate返回的内容,但是返回的内容是一长串字符串,我们先看一下请求参数,然后再换个要翻译的内容,看看参数有哪些变化。
- 参数对比
两次请求参数对比,发现,只有i、sign、mysticTime三个参数发生了变化
i:要翻译的字符串
sign:加密字符串
mysticTime:1701264377572,13位数字字符串,去掉后三位放入时间戳转换工具,发现就是时间戳
- 寻找sign加密位置
全局搜索sign:(小技巧),搜索的位置打上断点,逐个排除
再次请求,返现断点这个位置暂停,发现mysticTime就是时间戳
进入k(o, e)函数,看一下函数构造
执行会发现,k(o, e)函数右进入了j(e)函数,进行了一些列跟MD5有关的操作,先把这些函数抠出来
在看k(o, e)函数的传参,o是时间戳,e呢?
通过堆栈不难发现t就是e,那这个值怎么生成的(我这里查看了很久都没找到),但是发现不管翻译的内容如何,这t的值是固定,我们可以写死。
所以抠出的JS代码如下图,我们调用输出一下:
1 2 3 4 5 6 7 8 9 10 | var t= 'fsdsogkndfokasodnaso' ; var e= new Date().getTime() function j(e) { return c.a.createHash( "md5" ).update(e.toString()).digest( "hex" ) } function k(e, t) { return j(`client=${u}&mysticTime=${e}&product=${d}&key=${t}`) } console.log(k(e,t)) |
ReferenceError: u is not defined u未定义
在继续打上断点看一下
在JavaScript中``反引号为模板字符串,反引号包括的字符串中出现${}是占位符的意思,相当于Python的f'{}'
观察u的值,通过测试发现u的值和d的值也是固定,我们也可以写死
再次运行代码,提示ReferenceError: c is not defined,c未定义,我们看一下内容,明显是MD5加密,使用Python MD5或者Node.js的都行,这里我用Node.js的的,crypto代替c.a
1 2 3 4 5 6 7 8 9 10 11 12 13 | var crypto = require( 'crypto' ); var t= 'fsdsogkndfokasodnaso' ; var u= "fanyideskweb" var d= "webfanyi" var e= new Date().getTime() function j(e) { return crypto.createHash( "md5" ).update(e.toString()).digest( "hex" ) } function k(e, t) { return j(`client=${u}&mysticTime=${e}&product=${d}&key=${t}`) } console.log(k(e,t)) |
再次运行,发现运行成功
对比页面上的加密字符串(防止魔改MD5),发现跟页面上的加密字符串不一致
在进行分析,组成加密字符串的时间戳一直再变动,所以生成的加密字符串不一致,那我们传入跟页面上一样的字符串在本地JS代码中测试一下
这次生成的加密字符串一致了
开始编写程序,请求接口看看返回的内容
JS代码:
1 2 3 4 5 6 7 8 9 10 | var crypto = require( 'crypto' ); var u= "fanyideskweb" var d= "webfanyi" function j(e) { return crypto.createHash( "md5" ).update(e.toString()).digest( "hex" ) } function return_sign(e, t) { return j(`client=${u}&mysticTime=${e}&product=${d}&key=${t}`) } |
Python代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | import time import execjs import requests def get_sign(t): with open ( "1.js" , "r" , encoding = 'utf-8' ) as f: read_js = f.read() e = 'fsdsogkndfokasodnaso' sign = execjs. compile (read_js).call( 'return_sign' , t,e) return sign def get_web(str1, t): url = 'https://dict.youdao.com/webtranslate' header = { "Content-Type" : "application/x-www-form-urlencoded" , "Referer" : "https://fanyi.youdao.com/index.html" , "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36" , } cookie = { "OUTFOX_SEARCH_USER_ID_NCOO" : "1880472979.5706484" , "OUTFOX_SEARCH_USER_ID" : "-320001268@240e:3b8:2e6b:8db0:7dde:588a:9908:d4bb" } sign = get_sign(t) data = { "i" : str1, "from" : "auto" , "to" : "", "dictResult" : "true" , "keyid" : "webfanyi" , "sign" : sign, "client" : "fanyideskweb" , "product" : "webfanyi" , "appVersion" : "1.0.0" , "vendor" : "web" , "pointParam" : "client,mysticTime,product" , "mysticTime" : t, "keyfrom" : "fanyi.web" , "mid" : "1" , "screen" : "1" , "model" : "1" , "network" : "wifi" , "abtest" : "0" , "yduuid" : "abcdefg" } resp = requests.post(url, headers = header,cookies = cookie, data = data) print (resp.status_code) print (resp.text) if __name__ = = '__main__' : str1 = 'Hello Wodeld !' t = time.time() get_web(str1, t) |
运行之后发现返回的加密的字符串
在Ajax请求位置返回内容,也就是得到sign值之后,继续跟踪,寻找解密函数,会发执行完这里右侧a中就会出现翻译内容,确定解密函数位置
进入解密函数,看一下执行逻辑,抠出代码
对e.alloc跟踪,你会发现,这里出现Uint8Arry函数,还有一个图标
点击这个图标,然后会弹出以下内容
怎么看着像是内存呀,我们查查Uint8Arry的用法
Uint8Arry:数组类型表示一个 8 位无符号整型数组,创建时内容被初始化为 0。创建完后,可以以对象的方式或使用数组下标索引的方式引用数组中的元素。
我们用Node.js中Buffer.alloc代替
进入y(o)函数,发现就是MD5函数,我们引入crypto包,代替c.a
运行代码,
运行后报错: 'gbk' codec can't decode byte 0xb7 in position 68: illegal multibyte sequenc
execjs包编码问题在引入前修改一下编码:
1 2 3 | import subprocess from functools import partial subprocess.Popen = partial(subprocess.Popen, encoding = 'utf-8' ) |
再次运行,得到正确结果
完整1.js代码
1 2 3 4 5 6 7 8 9 10 | var crypto = require( 'crypto' ); var u = "fanyideskweb" var d = "webfanyi" function j(e) { return crypto.createHash( "md5" ).update(e.toString()).digest( "hex" ) } function return_sign(e, t) { return j(`client = ${u}&mysticTime = ${e}&product = ${d}&key = ${t}`) } |
完整2.js代码
1 2 3 4 5 6 7 8 9 10 11 12 | var crypto = require( 'crypto' ); function return_text(t, o, n) { if (!t) return null ; const a = Buffer.alloc(16, crypto.createHash( "md5" ).update(o).digest()) , i = Buffer.alloc(16, crypto.createHash( "md5" ).update(n).digest()) , r = crypto.createDecipheriv( "aes-128-cbc" , a, i); let s = r.update(t, "base64" , "utf-8" ); return s += r.final( "utf-8" ), s } |
完整Python代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | import subprocess from functools import partial subprocess.Popen = partial(subprocess.Popen, encoding = 'utf-8' ) import time import execjs import requests def get_sign(t): with open ( "1.js" , "r" , encoding = 'utf-8' ) as f: read_js = f.read() e = 'fsdsogkndfokasodnaso' sign = execjs. compile (read_js).call( 'return_sign' , t, e) return sign def get_text(str1): with open ( "2.js" , "r" , encoding = 'utf-8' ) as f: read_js = f.read() o = "ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl" n = "ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4" str2 = execjs. compile (read_js).call( 'return_text' , str1,o,n) return str2 def get_web(str1, t): url = 'https://dict.youdao.com/webtranslate' header = { "Content-Type" : "application/x-www-form-urlencoded" , "Referer" : "https://fanyi.youdao.com/index.html" , "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36" , } cookie = { "OUTFOX_SEARCH_USER_ID_NCOO" : "1880472979.5706484" , "OUTFOX_SEARCH_USER_ID" : "-320001268@240e:3b8:2e6b:8db0:7dde:588a:9908:d4bb" } sign = get_sign(t) data = { "i" : str1, "from" : "auto" , "to" : "", "dictResult" : "true" , "keyid" : "webfanyi" , "sign" : sign, "client" : "fanyideskweb" , "product" : "webfanyi" , "appVersion" : "1.0.0" , "vendor" : "web" , "pointParam" : "client,mysticTime,product" , "mysticTime" : t, "keyfrom" : "fanyi.web" , "mid" : "1" , "screen" : "1" , "model" : "1" , "network" : "wifi" , "abtest" : "0" , "yduuid" : "abcdefg" } resp = requests.post(url, headers = header, cookies = cookie, data = data) if resp.status_code = = 200 : str3 = get_text(resp.text) print (str3) if __name__ = = '__main__' : str1 = 'Hello World !' t = time.time() get_web(str1, t) |
End !!!
本文来自博客园,作者:小二哥呀,转载请注明原文链接:https://www.cnblogs.com/toddywang/p/17869565.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)