You are currently viewing 某道翻译Web端JS逆向分析

某道翻译Web端JS逆向分析

网站地址为:aHR0cHM6Ly9mYW55aS55b3VkYW8uY29tLyMv

如有问题请联系作者,将及时进行相应处理

一、分析过程

1.1 还原抓包过程

在使用该款翻译软件时,就想着自己能不能用代码的形式来实现翻译功能,说干就干。

在左边的框中输入需要翻译的英语单词”like”,在右边的框中就会出现翻译后的结果。

在进行翻译时,按键盘快捷键”F12″,可以打开Google 浏览器的DevTools工具(开发者工具),然后查看翻译时发起的网络请求,

依次选择”Network(网络)” –> “Fetch/XHR” –> 然后选择 “webtranslate”开头的网络请求

1.2 解释表单字段意义

通过仔细观察就可以知道。每个表单数据的意义如下所示:

表单字段意义
i即输入的词汇
from、to即输入的语言和输出的语言(通俗点就是什么语言翻译成什么)en–>zh-CHS
useTerm使用条款,默认为false
domain默认为0
dictResult字典结果,默认为true
keyidwebfanyi (固定字符串)
sign不固定。需要逆向分析
clientfanyideskweb
productwebfanyi
appVersion1.0.0
vendor /pointParamweb 、 client,mysticTime,product
mysticTime请求的时间戳
keyfromfanyi.web
midscreenmodelnetworkyduuid这些都是固定值,无需解释。

以上字段值,就只有i、sign、mysticTime这三个字段的值是变化的,由于i是输入的词汇,mysticTime是时间戳(毫秒级),所以,需要逆向分析的字段就只有sign字段。

1.3 js逆向分析sign字段值

分析时可以选择其中一个方法进行分析

1.3.1 直接搜索字符串

点击请求的链接处,然后点右上角三个”…“的地方,点击Search。

在下方的弹出的输入框中输入“sign”关键字

可以发现搜索出来的结果有点多,不太适合分析……那换下一种方式吧。

1.3.2 断点跟踪

选择翻译的请求,点击”initiator”,然后点击请求调用堆栈中的第一个。

点击进入后就是这样子的,然后点击左侧边框,即可下断点。

然后重新发起一个新的请求,即可将断点断下来

注意可以发现左上角多了一个类似暂停的符号,就是成功断下来了。

需要值得注意的是,该请求是否为自己所分析的请求。因为一般网络发送接口(send函数等)都是通用的,不会只用来发送一条翻译的请求!

技巧就是:断下来后,按一下快键键F8即可,端下来后就像这样,这便是我们需要找的

通过观察f参数,发现请求为”https://**/webtranslate/key” ,正是我们所需要的请求,接着往下分析。

为了更方便、更直观的知道处理的位置,会采用更直接的方法告知流程

断点断下来之后,点击十三次这个按钮

搜索”sign: S(“这个字符串,找到加密的处理sign字段值的位置:

可以很清楚的看见处理流程,sign –> S函数处理(拼接字符串)—>_函数(进行md5哈希算法)

其中,拼接字符串中的d、e、u、t参数分别是:

d:”fanyideskweb”

e:1722780312911 (当前时间戳)

u:”webfanyi”

t:”asdjnjfenknafdfsdfsd”

经过分析后发现,除了e为时间戳之外,其余参数都不会变,具体实现的Python代码如下:

def sign():
   # 该方法实现有道词典请求中sign字段的构成
   # 获取当前时间戳,以毫秒为单位
   timestamp_milliseconds = time.time() * 1000
   # 要拼接的字符串列表
   mysticTime=f"{'mysticTime=1722781084310'}"  # 实际使用中需要替换这个地方为f"{'mysticTime='}{str(int(timestamp_milliseconds))}"
   print("当前时间戳:",str(int(timestamp_milliseconds)))
   string_list = ["client=fanyideskweb&", mysticTime, "&product=webfanyi","&key=asdjnjfenknafdfsdfsd"]
   # 使用join方法将字符串列表拼接成一个字符串
   combined_string = ''.join(string_list)
   # 创建一个MD5哈希对象
   md5_hash = hashlib.md5()
   # 更新哈希对象的内容为拼接后的字符串
   md5_hash.update(combined_string.encode('utf-8'))
   # 获取MD5哈希值的十六进制表示
   md5_hex = md5_hash.hexdigest()
   return md5_hex

输出结果为:a7b495722dcb9840a806eec6ef683b75

和网页中的结果进行对比:

可以发现是一致的,到此,sign字段值的生成便找到了.

由于mysticTime的值并不是固定的,所以再使用以上代码时,需要进行一点点更改,参考#后的提示

分析完sign字段的值后,接着向下分析,

请求后,可以发现返回包中的数据是不可见的,具体如下所示:

接下来就要分析返回包中的数据了,

1.4 返回包解密

老规矩,还是断点调试更直接,断下来后,需要注意的是,因为之前说过,你要明白现在调试的请求,就是你需要分析的请求,所以需要先跳过一次请求

点击红色框中的“继续运行”按钮一次

为啥要跳过一次呢?等下后面再说

然后还是点击13次跳过当前函数按钮

来到了这个位置,可以发现e= {data:’一串加密’}

点击两次“继续执行”按钮,进入a(e.data)函数中

进入a函数后,可以发现已经到了解密的地方了,其中参数o就是我们需要解密的返回值字符串

xa.A.state.text.decodeKey:解密所需的key

xa.A.state.text.decodeIv:解密所需的iv

那么接下来就要进入到_a.A.decodeData函数中查看具体的算法和处理,

进入之后可以清楚的看见算法为aes,并且为128位,模式为cbc

其中a和n就是解密所需的key和iv,继续单步调试

可以发现T函数就是做了一次md5,而参数e则是一串字符串,

e= “ydsecret://query/key/BRGygVywfNBwpmBaZgWT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl”

一直调试完参数a的赋值函数后,会发现出现了一个类似“绷带”的图标

点击一下,出现了一串16字节的十六进制数值。这串值就是最后解密所需的key,就是md5(e)

进行验证一下

确实一致,那么就可以确定key = 08149da73c59ce62555b01e92f34e838,根据以上的操作,继续执行

可以知道iv = md5(“ydsecret://query/key/BRGygVywfNBwpmBaZgWT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl”)

即iv=43e84bfa8149ef67ac7453dc18218a2f

那么到这儿就已经很清楚了,解密返回包的值,就是base64 + aes_128_cbc

之前还留有一个问题,为什么要跳过一次请求???因为在进行翻译时,会向服务器申请解密所需的key和iv

如上图第一个请求,key?keyid=webfanyi

返回包中就是我们解密所需要的key和iv

好啦,到这儿,YD翻译的全部过程就已经分析完了,

二、总结

请遵守相关法律法规,不要做损害别人利益的事儿,