雷速体育Canvas文字加密逆向总结
文章目录
- 前言
- 了解Canvas
- 寻找绘制方法
- 明确需要寻找什么
- 找寻方法,理清执行流程
- 找寻原始数据base64_
- **直接搜索**
- **hook方法**
- 复现js代码
- 编写`rot`函数
- 编写`roott`函数
- 编写`pushmsg`函数
- 使用Python如何解密
- 借助nodejs API服务
- 使用Python复现代码
前言
网址:https://live.leisu.com/wanchang
可以看到这个比分是使用canvas绘制上去的。
了解Canvas
首先了解下canvas
是一个可以使用脚本(通常为JavaScript)来绘制图形的 HTML 元素.例如,它可以用于绘制图表、制作图片构图或者制作简单的(以及不那么简单的)动画.
主要了解下 canvas绘制文本
https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial/Drawing_text
看了上面的简单的教程,姑且粗略地认为绘制文本前需要提供文本。
例如绘制hello world
则需要提供hello world
这个字符串。
在下方链接体验canvas文本绘制
https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial/Drawing_text#textbaseline%E4%BE%8B%E5%AD%90
寻找绘制方法
通过上面的了解,我们只需要找到数据在哪里就可以了。
F12查看network,浏览一遍下来没有发现明显的数据交互请求。全是js、css、png
等静态文件。
查看html源代码,也没有明显的数据。
html中的canvas的标签,js需要定位到canvas标签才继续操作,所以我们可以试着全局搜索canvas
关键字。
F12打开审查工具,Ctrl+Shift+F
全局搜索
一一查看搜索结果,查看到最后一个js
文件(match_layout_wc_xxx.js
),明显这个js
代码被混淆了。一般可以理解为,开发者只会对关键代码进行混淆。
说明这个文件的代码不想给其他人看。
继续往下看,可以看到这明显是Vue框架的写的。这个文件是一个Vue写的子组件。
当这个组件被挂载时,将执行this[_x116622[80]]()
,即调用this[_x11622[80]]
方法。
对这个位置打上断点,刷新网页,在此断点后查看_x11622[80]
所以,这个组件被挂载时,即执行this.drwaBase()
这个this.drwaBase
方法在哪里呢,Vue的方法统一定义在methods
内,或者在这个js文件里搜搜看。
我们把断点打在drwaBase
方法里的开头和结尾,让其跑完整个drwaBase
方法。
可以看到每跑一次drwaBase
方法,界面上就有一行数据显示出来。
在结合drwaBase
方法没有参数,所以在挂载这个组件前,数据就已经加载出来了。
所以,我需要找到数据何时被初始化。
跟着调用栈往上找。
在wanchang-xxxx.js
的文件中找到函数名为initData
方法,这个引起了我们的注意。可以看到有一个明显的JSON
, 在这一行上打断点。刷新开始调试。
所以,这句代码原本的样子可以还原为:
let _ = JSON.parse($.rot(base64_, STATIC_CONFIG.KST))
其中STATIC_CONFIG.KST
根据多次调试,这个值一直是整数6
let _ = JSON.parse($.rot(base64_, 6))
此时,我们接着看下_
是什么,展开其中一个数组,可以看到这就是我们所需的数据。
明确需要寻找什么
那么现在,我们的首要目的就是找到base64_
变量是什么时候赋值的以及$.rot
方法是什么。
找寻方法,理清执行流程
在Console
里输入$.rot
在Console
返回的代码是实际执行的。也就是:
$.rot = function (t, e) {const i = roott(t, e);return pushmsg(i)
}
点击返回内容可以直接跳转到此方法。
function(t) {try {let e = ["t", "ro"];if (!t || !window.LeisuJS)return;t[e[1] + e[0]] = function(t, e) {const i = roott(t, e);return pushmsg(i)}} catch (t) {"prod" != STATIC_CONFIG.NODE_ENV && console.error(t)}
}(jQuery),
简单的分析,传了一个Jquery
进去
然后对其设置了一个方法
t[e[1] + e[0]] = function(t, e) {const i = roott(t, e);return pushmsg(i)
}
替换一下就是:
t.rot = function(t, e) {const i = roott(t, e);return pushmsg(i)
}
可以看到这就是实际执行的代码。
那么到分析到现在整体流程有个大概的雏形了。
- 超长字符串传入
rot
方法; - 经过
roott
和pushmsg
方法的处理,最终得到有效数据
接着我们使用同样的方法找roott
与pushmsg
roott
e.roott = function(t, e) {for (var i = "", n = 0; n < t.length; n++) {var a = t.charCodeAt(n), o = a;a >= 65 && a <= 90 && (o = (a - 65 - 1 * e + 26) % 26 + 65),a >= 97 && a <= 122 && (o = (a - 97 - 1 * e + 26) % 26 + 97),i += String.fromCharCode(o)}return i
}
pushmsg
e.pushmsg = function(t) {let e = "";if ("undefined" == typeof window)try {e = nodeAtob(t)} catch (t) {console.log(t)}elsee = atob(t);const i = e.split("").map(function(t) {return t.charCodeAt(0)}), n = new Uint8Array(i), a = pako.inflate(n);return e = function(t) {let e, i, n, a, o = "";const r = t.length;for (e = 0; e < r; )switch ((i = t[e++]) >> 4) {case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:o += String.fromCharCode(i);break;case 12:case 13:n = t[e++],o += String.fromCharCode((31 & i) << 6 | 63 & n);break;case 14:n = t[e++],a = t[e++],o += String.fromCharCode((15 & i) << 12 | (63 & n) << 6 | (63 & a) << 0)}return o}(new Uint16Array(a)),unescape(e)
}
}(),
roott
可以很容易理解,pushmsg
刚开始也能看懂直到pako.inflate(n);
进去看了下这个inflate
,有点复杂不知道这一步是在干什么。
这看着有点像调用其他的工具包,于是我去搜索pake.inflate
一个解压库,将数据解压就可以用到这个pako
找寻原始数据base64_
直接搜索
base64_
已经是处于加密状态,是一个非常长的字符串,有理由相信实际返回的原始数据就是这个超长字符串。我们的拿出字符串的前几位去全局搜索。
看了下base64_被执行赋值操作的js文件是wc-xxxx.js
然后简单调试发现,请求这个js文件时,无需携带cookie,所以尝试直接请求这个js
文件,成功获取响应。
hook方法
base64_
是一个全局变量,如果我们能在base64_
被赋值的那一瞬间进入debug状态,然后插卡调用栈即可看到window.base64_
在何时被赋值的。
写一个hook函数
(
function (){'use strict';Object.defineProperty(window, 'base64_', {set: function(v) {console.log('window.base64_正在被赋值');debugger;return v;}})
}
)();
找一个在window.base64_
被赋值前就执行的js代码片段,越早越好。
进入此函数,随便打个断点
然后刷新界面,进入断点状态后,在console
粘贴hook代码
按F8
继续运行到下一个断点处
此时查看调用栈
复现js代码
打开webstorm
,新建一个nodejs
项目
pushmsg
中使用了两个包,分别是atob
与pako
,所以安装这两个包并导入。
npm install atob
npm install pako
const pako = require('pako')
const atob = require('atob')
编写rot
函数
function rot (t, e) {const i = roott(t, e);return pushmsg(i)
}
编写roott
函数
function roott(t, e) {for (var i = "", n = 0; n < t.length; n++) {var a = t.charCodeAt(n), o = a;a >= 65 && a <= 90 && (o = (a - 65 - 1 * e + 26) % 26 + 65),a >= 97 && a <= 122 && (o = (a - 97 - 1 * e + 26) % 26 + 97),i += String.fromCharCode(o)}return i
}
编写pushmsg
函数
我们导入的是atob
这个包所以,前面的一些判断直接删掉。
function pushmsg (t) {let e = "";e = atob(t);const i = e.split("").map(function(t) {return t.charCodeAt(0)}), n = new Uint8Array(i), a = pako.inflate(n);return e = function(t) {let e, i, n, a, o = "";const r = t.length;for (e = 0; e < r; )switch ((i = t[e++]) >> 4) {case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:o += String.fromCharCode(i);break;case 12:case 13:n = t[e++],o += String.fromCharCode((31 & i) << 6 | 63 & n);break;case 14:n = t[e++],a = t[e++],o += String.fromCharCode((15 & i) << 12 | (63 & n) << 6 | (63 & a) << 0)}return o}(new Uint16Array(a)),unescape(e)
}
复制一个base64_
进去调用rot
运行试试看,可以看到解密成功。
使用Python如何解密
我们的爬虫是用python写的,那么如何去使用这个nodejs解密的结果呢?
有两种方法,
- 在nodejs上起一个API服务,在Python中请求数据解密接口就行了。
- 使用纯Python代码复现解密代码。
借助nodejs API服务
nodejs有很多框架,目前我对nodejs不太熟悉,去搜了下,express
这个库比较流行。
用什么都可以,因为我们的接口就是在本子自己调用,不必考虑其他情况。
网上抄代码:
server.js
let express = require('express');
// 我们自己写的解密文件
const bb = require('./decode.js')
let app = express();
const bodyParser = require('body-parser');
app.use(bodyParser.json());//数据JSON类型
app.use(bodyParser.urlencoded({ extended: false }));//解析post请求数据app.all('*',function(req,res,next){let origin=req.headers.origin;res.setHeader('Access-Control-Allow-Origin',"*");res.setHeader('Access-Control-Allow-Headers','Content-Type');next();
})app.post('/data',function(req,res){console.log(req.body);var base = req.body.base// 调用解密方法var result = bb.rot(base)res.send(result)
})app.listen(8080)
启动服务
node server.js
效果演示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sgh0Qybm-1618038234596)(https://cdn.z2blog.com/picgo/20210410135116.gif)]
使用Python复现代码
atob
是将base64字符串解码
pako
是将zlib压缩文件
类似这些功能的包在Python中也用,还都是内置包
atob -> base64.b64decode
pako.inflate -> zlib.decompress
rot
def rot(t, e):i = roott(t, e)return pushmsg(i)
roott
# function roott(t, e) {# for (var i = "", n = 0; n < t.length; n++) {# var a = t.charCodeAt(n)
# , o = a;
# a >= 65 && a <= 90 && (o = (a - 65 - 1 * e + 26) % 26 + 65),
# a >= 97 && a <= 122 && (o = (a - 97 - 1 * e + 26) % 26 + 97),
# i += String.fromCharCode(o)
# }
# return i
# }
def roott(t, e):i = ""for n in range(len(t)):a = ord(n)o = aif 65 <= a <= 90:o = (a - 65 - 1 * e + 26) % 26 + 65if 97 <= a <= 122:o = (a - 97 - 1 * e + 26) % 26 + 97i += chr(o)return i
如果嫌麻烦,可以使用pyexecjs
,就像这样
import execjsjs = """function roott(t, e) {for (var i = "", n = 0; n < t.length; n++) {var a = t.charCodeAt(n), o = a;a >= 65 && a <= 90 && (o = (a - 65 - 1 * e + 26) % 26 + 65),a >= 97 && a <= 122 && (o = (a - 97 - 1 * e + 26) % 26 + 97),i += String.fromCharCode(o)}return i
}"""
s = execjs.compile(js)
...
# 调用roott
s.call('roott', t, e)
pushmsg
import base64
import zlib
from urllib import parsedef pushmsg(t):# 对应atobr = base64.b64decode(t.encode())# 解压b = zlib.decompress(r)p = parse.unquote(b.decode())return p.replace('%', '\\').encode().decode('unicode-escape')
完整代码
import base64
import zlib
from urllib import parsedef roott(t, e):i = ""for n in t:a = ord(n)o = aif 65 <= a <= 90:o = (a - 65 - 1 * e + 26) % 26 + 65if 97 <= a <= 122:o = (a - 97 - 1 * e + 26) % 26 + 97i += chr(o)return idef pushmsg(t):# 对应atobr = base64.b64decode(t.encode())# 解压b = zlib.decompress(r)p = parse.unquote(b.decode())print(p.replace('%', '\\').encode().decode('unicode-escape'))def rot(t, e):i = roott(t, e)return pushmsg(i)if __name__ == '__main__':base64_ = "kL7ylBr73JgC9w/XFj..."e = 6print(rot(base64_, e))
效果演示
雷速体育Canvas文字加密逆向总结相关推荐
- C语言文字加密程序的实现
前言:当今社会是一个信息社会,你的个人信息和聊天记录极有可能被别有用心的人时时刻刻监视着,那么你想不想实现专属于两个人或一个小圈子的人在社交软件上的交流不被任何其他人读懂呢?下面就给大家提供一个原理极 ...
- 针对文字加密的简单 JS 加密算法 --进制乱序法改良版
在上一篇文章<普通 http 网络下数据的安全传输(设计原理)>中,我曾经推荐浏览器和服务器之间的加密通讯宜采用<几个文字加密的 JS 简洁算法(续2)--进制乱序法>中提及的 ...
- canvas文字旋转功能
功能实现:使canvas文字围绕其中心点旋转 思路 平移画布,使画布中心点和文字的中心点重合(画布中心点默认在左上角点) 重新计算文字的起始点坐标 效果图 <!DOCTYPE html> ...
- Canvas 文字对齐方式
Canvas 文字对齐方式 常用的画文字的方法 public void drawText (String text, float x, float y, Paint paint) x,y 并不是指定文 ...
- ae制h5文字动画_7款超华丽的HTML5 Canvas文字动画特效
本文作者html5tricks,转载请注明出处 文字是网页中最为常见的元素之一,当然我们使用最多的就是调整文字的颜色.大小等基本属性.有时候我们在一些活动页面上需要展示特别样式的文字效果,这时候我们就 ...
- html5字体动画效果,7款超华丽的HTML5 Canvas文字动画特效
原标题:7款超华丽的HTML5 Canvas文字动画特效 文字是网页中最为常见的元素之一,当然我们使用最多的就是调整文字的颜色.大小等基本属性.有时候我们在一些活动页面上需要展示特别样式的文字效果,这 ...
- python写情书_Python程序员用文字加密的方式,给女程序员写情书,一周后牵手回家_TONOW...
谁说程序员不浪漫,尤其是Python程序员,对文字加密后,写情书只有特定的人才能看懂. image 在某公司,有这样一位Python程序员,我们叫他小福,由于性格比较内向,一直没有女朋友,直到今年6月 ...
- 2019年末逆向复习系列之百度指数Data加密逆向破解
郑重声明:本项目的所有代码和相关文章, 仅用于经验技术交流分享,禁止将相关技术应用到不正当途径,因为滥用技术产生的风险与本人无关. 这篇文章是公众号<云爬虫技术研究笔记>的<2019 ...
- canvas 文字时钟
canvas 文字时钟 先看看效果图 代码 <!DOCTYPE html> <html lang="en"> <head><meta ch ...
- 小工具分享----Java简单的文字加密解密
今天有空自己制作了一个jar包,用于实现java中简单的文字加密解密功能,保存可用于以后需要的项目中,再也不用耗时间去网上找资源了,值得收藏!戳这里---------https://download. ...
最新文章
- Oracle系统表查询
- Maven学习总结(一)——Maven入门
- ajax请求的五个步骤_监控整个页面,非AJAX,需要通知
- 波卡链Substrate (2)系统框架
- 【程序人生】不想一辈子做底层码农?快来看看这十条箴言
- C# 得到本机局域网IP地址
- Abstract Factory(抽象工厂)实践
- java loadlibrary_java – System.loadLibrary不起作用.链中的第二个lib的UnsatisfiedLinkError...
- 面向小姐姐的编程——JAVA面向对象之多态
- windows配置Hadoop开发环境
- xcode中c语言清屏函数,浅谈iMac
- Ember Model
- 帧同步(LockStep)该如何反外挂 及 优化
- Qt编写输入法V2019终极版
- Congfu Xu's HomePage
- vue项目运行出现66% buil 98% after emitting CopyPlugin
- 基于FPGA的电子计算器设计(下)
- 2020-10-18Go语言接口
- 对VC 一些常见问题的整理
- 明确生产计划,做好生产进度跟踪