打造一款属于自己的翻译软件
你也可以拥有自己专属的翻译器
声明:
本人在此郑重声明,本文章中的所有内容仅供学习交流使用,已对抓包内容、敏感网址和数据接口做了脱敏处理。请勿将这些内容用于商业和非法用途,否则将承担所有法律后果。如果本文章内容侵犯了您的权益,请立即联系我,我将及时予以删除!
目标地址
aHR0cHM6Ly9mYW55aS55b3VkYW8uY29tL2luZGV4Lmh0bWwjLw==
对某道进行破解
开始分析:
- 首先打开浏览器控制台,输入输入一个词,点击翻译 可以看到请求头header中并无特殊的字段
- 在payload载荷中可以看到我们要翻译的词
协程
,因此说明点了翻译按钮之后,就是调用这个接口向后台发起请求,然后获取到翻译内容; - 确定了对应接口之后,可以看到payload中有一个特殊的参数
sign
,这个参数是我们需要去逆向出来的; - 点击响应,数据是一串我们看不懂的玩意 ,我们需要对response数据进行破解;
5. 分析完之后,我们主要任务就是header中的sign和response的数据解密;
准备开始js逆向
- 获取sign的值
- 第一网先撒下:先搜索
sign:
看看有没有出现,诶,出现了,只有3个地方 点击进入文件,格式化代码,我们都打上断点
2. 再次点击翻译
按钮,看看断点进入到哪里了
- 找到sign生成的位置,在
app.****.js
文件中,sign: b(t, e)
,
const t = (new Date).getTime();
可以看到t是个时间戳
e
的生成是f(e)
的参数,查看下调用堆栈
在Zo()
方法中e的取值 Wo["a"].state.text.secretKey
,我们可以测试多调用几次,发现这个值始终没有变化,是个固定,因此可以写死;
4. 然后b(t,e)函数进行处理,把鼠标光标放到b函数上,可以显示b函数的位置,点击进去,格式化代码
function b(e, t) {
return p(`client=${r}&mysticTime=${e}&product=${i}&key=${t}`)
}
函数b(e,t)中使用到的变量都是取自这块
const r = "fanyideskweb"
, i = "webfanyi"
, s = "client,mysticTime,product"
, l = "1.0.0"
, d = "web"
, u = "fanyi.web";
可以看到都是写死的,这就是很绝,我们直接替换掉里面的变量
5. 同样进入p()
方法中,明显看出这个就是传入一个参数e,然后经过md5
得到结果;
function p(e) {
return c.a.createHash("md5").update(e.toString()).digest("hex")
}
- 现在我们要测试下这个
md5
是不是正经的md5
,老套路,在控制台和随便一个在线加密的网站对参数'1'进行md5加密,结果对比是一致的,说明这里用的是正经的md5算法,作者并没有改写(md5这种一般很少有人改动),因为可以用通用包crypto-js里的md5进行替换;
7. 替换后的p()
就是下面这个
function p(e) {
//return c.a.createHash("md5").update(e.toString()).digest("hex")
return CryptoJS.MD5(e.toString()).toString()
/**
* c.a.createHash("md5").update('1').digest("hex")
* 正儿八经的
* 'c4ca4238a0b923820dcc509a6f75849b'
* 因此可用标准算法crypto-js替换
*/
}
- 替换之后,就可以得到
sign
的值
- 解密返回后的数据
- 先撒网用关键字
json.parse(
去撒,看看有没有结果,有7个,不多,都打上断点试下
这里为什么我们要用这个关键字去搜索呢?(因为大多数情况下,后端如果返回明文数据,一般也是json格式,前端拿到数据会有json.parse()转换成json对象,这样方便取值,如果想我们这种情况是密文的话,前端拿到密文,解密之后,接下来的动作一般也是进行json.parse()转换,所有我们搜索这个关键字,基本也就是找解密的位置)
2. 重新点击翻译按钮,断点进入到textTranslate.81983bbc.js
文件中
3. o是后天返回的密文数据,通过下面这段代码进行对o的解密操作
const n = Jo["a"].decodeData(o, Wo["a"].state.text.decodeKey, Wo["a"]
4. 在控制输出,这两个对象是固定的值(可以多次几次,其实这两个值,decodeKey就是解密算法的密钥,decodeIv就是偏移量)
Wo["a"].state.text.decodeKey
Wo["a"].state.text.decodeIv
Jo["a"].decodeData()
这个就是我们的解密方法,鼠标光标放到这里,点击进入函数- 解密函数如下,可以看出,用的是aes算法
O = (t, o, n) => {
if (!t)
return null;
const a = e.alloc(16, m(o))
, r = e.alloc(16, m(n))
, i = c.a.createDecipheriv("aes-128-cbc", a, r);
let s = i.update(t, "base64", "utf-8");
return s += i.final("utf-8"),
s
}
-
点击进入
c.a.createDecipheriv("aes-128-cbc", a, r)
中,这里是文件名chunk-vendors.641ed4ef.js
中 -
function c(t, e, n) 中用到了模块
o[t]
,s[t]
,这块模块又各种引用了其他模块,链路挺深的,所以这里我们选择全扣,(这里我已经帮你们试过了,一点一点扣太痛苦了)
o = n("bac2")
, s = n("0be8")
9. 把整个chunk-vendors.641ed4ef.js
文件拷贝下来,放入到我们的本地的pycharm中,总共4w多行代码,还好,不多
10. 把这个文件拉到第一行位置,看到关键字webpackJsonp
,知道我们是由多个文件webpack打包成的,因此下一步我们要定位到webpack加载器loader
11. 随便在加载器调用的位置打一个断点,刷新页面,即可进入断点位置,点击引用出进入
- 这里便是我们的加载器了
function s(t) {
if (n[t])
return n[t].exports;
var o = n[t] = {
i: t,
l: !1,
exports: {}
};
return e[t].call(o.exports, o, o.exports, s),
o.l = !0,
o.exports
}
13. 找到加载器后,就基本完成了一大半了,把整个加载器文件都拷贝到本地,然后把chunk-vendors.641ed4ef.js
里面的模块也粘贴到加载器里面的模块中
- 开始补环境,缺啥补啥,window对象补上去
15. 导出加载器对象,定义一个全局变量,名字随便取,我们这里定义var _ps
_ps=s
- 点击进入解密函数,这个模块的话是956a模块,
function c(t, e, n) {
if (t = t.toLowerCase(),
o[t])
return i.createDecipheriv(t, e, n);
if (s[t])
return new r({
key: e,
iv: n,
mode: t,
decrypt: !0
});
throw new TypeError("invalid suite type")
}
17.打印下_ps('956a')
模块内容,有我们需要的方法 createDecipheriv: [Function: c]
18. createDecipheriv()方法搞定了,剩下两个参数a和r的值,其实也就是上面提到的解密key和偏移iv值,这两个值也是固定的,因此我们可以在控制台把这个值打印出来,拷贝下来,不用扣这段代码了;
const a = e.alloc(16, m(o))
, r = e.alloc(16, m(n))
, i = c.a.createDecipheriv("aes-128-cbc", a, r);
19. 输入密文,获取到明文结果
到这里我们就可以结合python打造我们自己的翻译器了
文章持续更新,可以微信搜一搜「 python君 」第一时间阅读