Loading

【Go】彩云小译翻译接口js逆向解密返回值

一、前言

彩云小译网页版进行抓包分析,将js算法代码转换成go代码,使用go发送http请求编写一个翻译小工具。

主要实现:

  • 翻译(解密翻译结果)
  • 单词字典查询
  • 生成JWT(保持有效期)

二、抓包

打开网页按F12调出开发人员工具,再切换到网络选项卡,在输入框里输入内容开始抓包,会抓到两个接口,translator为翻译接口,dict为查询字典的接口。

1684307031333

translator接口的响应里面,发现并没有找到翻译的结果??其实 target就是翻译结果,只是进行了加密,需要对其进行解密才能得到翻译结果。

{
    "isdict": 1,
    "confidence": 1.21429,
    "target": "5Y2t5nJ9",
    "rc": 0
}

三、分析

我们先对 translator响应的 target进行逆向分析

我们接着通过上一步抓包抓到的接口,在发起程序选项卡中找到对应的调用点

1684307494480

这个很明显就是调用翻译接口的地方,我们点击右边蓝色的js文件,查看源代码

1684307726440

这里我们很容易就看到了我们要找到target,这里应该是已经请求完接口的返回数据了,dh为请求函数,获取到target并且赋值给了变量o,接着是一个三元表达式,通过typeof来判断o的类型是否为string,我们知道其实target就是一串字符串,所以只需要看问号后面的表达式了,问号后面也是一个赋值操作,我们直接下一个断点,再到页面中进行翻译操作

1684308047050

已经断下来了,我们看到o确实是target密文,我们把鼠标悬浮到Zs函数上,点击蓝色的链接跳转到对应的代码

1684308176983

可以看到Zs首先是调用了一下vh函数,并且把target传入进去,进行一些字符串操作后返回给了变量t,我们直接把vh函数代码抠下来,这段代码是可以直接运行的

function vh(e) {
    const t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
      , i = "NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm"
      , a = n=>t.indexOf(n)
      , o = n=>a(n) > -1 ? i[a(n)] : n;
    return e.split("").map(o).join("")
}

再调用Ah函数来解密,我们看到在Ah函数中调用了oh.decode()函数,decode很明显是解密了,我们跟之前的操作一样,进入到decode函数中查看代码

1684308378623

decode就是N函数,我们发现N函数中又调用了几个函数嵌套,我们按照上一步的方法,把使用到的函数全部抠下来

const vh = (e)=>{
    const t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
      , i = "NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm"
      , a = n=>t.indexOf(n)
      , o = n=>a(n) > -1 ? i[a(n)] : n;
    return e.split("").map(o).join("")
}

const b = function(I) {
    switch (I.length) {
    case 4:
        var U = (7 & I.charCodeAt(0)) << 18 | (63 & I.charCodeAt(1)) << 12 | (63 & I.charCodeAt(2)) << 6 | 63 & I.charCodeAt(3)
          , F = U - 65536;
        return String.fromCharCode((F >>> 10) + 55296) + String.fromCharCode((F & 1023) + 56320);
    case 3:
        return String.fromCharCode((15 & I.charCodeAt(0)) << 12 | (63 & I.charCodeAt(1)) << 6 | 63 & I.charCodeAt(2));
    default:
        return String.fromCharCode((31 & I.charCodeAt(0)) << 6 | 63 & I.charCodeAt(1))
    }
}

const w = function(I) {
    const y = /[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g
    return I.replace(y, b)
}

const E = function(I) {
    return window.atob(I)
}

const C = function(I) {
    return w(E(I))
}
const k = function(I) {
    return String(I).replace(/[-_]/g, function(U) {
        return U == "-" ? "+" : "/"
    }).replace(/[^A-Za-z0-9\+\/]/g, "")
}
const N = function(I) {
    return C(k(I))
}

抠下来之后运行发现报错,找不到o,我们通过下断点,然后在控制台输入出o,发现o就是内置函数 fromCharCode,因此我们只需要把o替换成 String.fromCharCode就行

1684308790390

我们尝试运行一下代码,发现可以成功解密,那就代表我们找的解密算法是对的,那么我们如何在go中使用呢?go中应该也有像python中的execjs那样直接调用js脚本的,但是我们这次不是用那种方法,我们下面把js代码转换成go代码

四、转换

将js算法转换成go代码,并且进行简化代码

package main

import (
	"encoding/base64"
	"fmt"
)

// 将字母表中的字母替换为另一个字母表中的字母
func substituteAlphabet(input string) string {
	alphabet := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	substitution := "NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm"
	findIndex := func(n byte) int {
		for index, b := range alphabet {
			if byte(b) == n {
				return index
			}
		}
		return -1
	}
	substitute := func(n byte) byte {
		if findIndex(n) > -1 {
			return substitution[findIndex(n)]
		}
		return n
	}
	result := ""
	for _, c := range input {
		result += string(substitute(byte(c)))
	}
	return result
}

// 从字符串中删除双字节字符
func removeDoubleByte(input string) string {
	bytes := []byte(input)
	for i := 0; i < len(bytes); i++ {
		if bytes[i] == 194 && i+1 < len(bytes) && bytes[i+1] >= 128 && bytes[i+1] <= 191 {
			bytes[i] = bytes[i+1]
			bytes[i+1] = 0
		}
	}
	return string(bytes)
}

// 对输入的字符串进行base64解码
func decodeBase64(input string) string {
	decoded, err := base64.StdEncoding.DecodeString(input)
	if err != nil {
		panic(err)
	}
	return removeDoubleByte(string(decoded))
}

func main() {
	code := substituteAlphabet("5Y2t5nJ977lZ5YvJ55JZ77lO")
	text := decodeBase64(code)
	fmt.Println(text)
}

1684309125035

五、请求

我们再次对网页进行抓包,然后在开发者工具中右键对应接口,复制curl

1684309464458

再到https://curlconverter.com/#go中将curl转换成对应的go代码,生成的代码放到go里面是可以直接运行的

我们需要先请求接口,通过返回的数据拿到target,再使用解密对target进行解密,拿到最终的翻译结果

1684310061224

1684309876269

六、总结

我写完之后发现已经写了220多行了,go的代码量确实比python的要多

加了一个dict字典查询接口,还有生成jwt的接口(可以防止jwt过期)

完整代码已经上传到Github

以上内容仅用于学习研究

posted @ 2023-05-17 16:01  流星Studio  阅读(164)  评论(0编辑  收藏  举报