共计 5946 个字符,预计需要花费 15 分钟才能阅读完成。
申明
本文章中所有内容仅供学习交换应用,不用于其余任何目标,不提供残缺代码,抓包内容、敏感网址、数据接口等均已做脱敏解决,严禁用于商业用途和非法用处,否则由此产生的所有结果均与作者无关!
本文章未经许可禁止转载,禁止任何批改后二次流传,擅自应用本文解说的技术而导致的任何意外,作者均不负责,若有侵权,请在公众号【K 哥爬虫】分割作者立刻删除!
指标
- 指标:数美全家桶,包含:滑块、文字点选、图标点选、语序点选、空间推理、无感验证
- 地址:
// 官网体验地址
aHR0cHM6Ly93d3cuaXNodW1laS5jb20vdHJpYWwvY2FwdGNoYS5odG1s
// 官网暗藏地址
aHR0cHM6Ly9jYXN0YXRpYy5mZW5na29uZ2Nsb3VkLmNuL3ByL3YxLjAuNC9kZW1vLmh0bWw=
// 某红书验证页面
aHR0cHM6Ly93d3cueGlhb2hvbmdzaHUuY29tL3dlYi1sb2dpbi9jYXB0Y2hh
数美不同类型验证码外围的 JS 都是一样的,只是个别参数有渺小差异,次要以滑块为例来剖析,通过 JS 代码以及官网文档能够看出数美是有无感验证的,然而官网体验地址里并没有放进去,官网有一个暗藏地址,外面的 demo 是最全的,包含无感,能够去下面给出的第二个地址里查看;数美的加密参数蕴含了 DES 加密算法,参数名以及 DES Key 不定时会变动,本文也会剖析如何利用 AST 来获取动静的参数。
抓包剖析
conf
接口,获取配置,次要是获取外围的 captcha-sdk.min.js
的地址,申请参数解释:
参数 | 含意 |
---|---|
organization |
数美调配的公司标识,个别是每个网站惟一,写死即可 |
appId |
利用标识,辨别不同利用,数美后盾能够治理 |
callback |
回调参数 |
lang |
语言,zh-cn 简体中文、zh-tw 繁体中文、en 英文 |
model |
模式,slide 滑块、auto_slide 无感验证、select 文字点选、icon_select 图标点选、seq_select 语序点选、spatial_select 空间推理 |
sdkver |
这个 sdk 版本是 captcha-sdk.min.js 外部写死的 |
channel |
推广渠道,数美后盾能够治理 |
captchaUuid |
32 位随机字符串,与业务方本身埋点数据配合,便于后续定位问题或进行数据统计 |
rversion |
captcha-sdk.min.js 版本号 |
返回后果重点看 captcha-sdk.min.js
文件地址,如下图所示有个 v1.0.4-171
,本文中咱们称 v1.0.4
为大版本,171
为小版本,小版本不定时会更新,版本号一直升高。
而后就是 register
接口,不同类型,返回的数据都大同小异,其中 bg
是背景图片,fg
是滑块,文字点选、空间推理中 order
是提示信息,k
、l
、rid
三个参数后续会用到。
最初就是 fverify
验证接口,有相似下图红框中的 12 个参数,都是通过 JS 生成的,其参数名会依据 captcha-sdk.min.js
的变动而变动,其中有个最长的相似于下图的 ep
值,蕴含了轨迹加密。返回值里参数解释:
参数 | 含意 |
---|---|
code |
1100 :胜利;1901 :QPS 超限;1902 :参数不非法;1903 :服务失败;9101 :无权限操作 |
riskLevel |
处理倡议,PASS :失常,倡议间接放行;REJECT :违规,倡议间接拦挡 |
逆向剖析
跟栈会发现外围逻辑在 captcha-sdk.min.js
里,这个 JS 相似于 OB 混同(以前的文章介绍过,此处不再细说):
这里能够本人写 AST 还原一下,为了不便咱们间接应用 v_jstools 解混同:
而后替换掉原来的 captcha-sdk.min.js
,如果你测试的是官网的体验页面,应用 Fiddler 替换时要留神可能有跨域问题,须要利用 Filters 性能,设置响应头 Access-Control-Allow-Origin
字段值为以后域名:
如果你没留神到这个跨域问题,可能会替换之后发现没替换胜利,起因是数美的资源有四个域名,其中一个宕了便会启用另一个,你替换其中一个报错了就会主动跳转另一个,所以看起来你并没有替换胜利:
PS:若替换的 JS 格式化了,那么你在网页上滑动也是校验失败的,因为 JS 里检测了格式化,将 JS 压缩成一行再替换即可,具体检测的地位后文会讲到。
captchaUuid
间接搜寻关键词下断点,通过屡次调试会发现第一个呈现 captchaUuid
的中央是在 smcp.min.js
,如下图所示:
这里的栈并不多,来回跟栈也没发现是哪里生成的,此时能够从初始地位也就是 embed.html
初始化验证码的中央开始单步跟:
单步跟进去会发现一个 getCaptchaUuid()
的办法,将此办法扣进去即可。
function generateTimeFormat() {var e = new Date()
, t = function(n) {return +n < 10 ? "0" + n : n.toString();
};
return ((e.getFullYear().toString() + t(e.getMonth() + 1)) + t(e.getDate()) + t(e.getHours()) + t(e.getMinutes())) + t(e.getSeconds());
}
function getCaptchaUuid() {
var c = "";
var o = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678";
var s = o.length;
for (var a = 0; a < 18; a++) {c += o.charAt(Math.floor(Math.random() * s));
}
return generateTimeFormat() + c;}
12 个加密参数
间接跟栈就很容易找到,如下图所示的地位,D 就是生成的所有参数,此外,也能够通过搜寻关键字 getEncryptContent
或者间接搜寻参数名称来定位。
能够发现上图里就有四个加密参数,都用到了 getEncryptContent
这个加密办法,加密办法传入两个参数,一个是待加密参数,一个是 DES Key,这四个待加密参数别离为 appId
值、channel
值、lang
值和一个 getSafeParams
办法。
重点跟进 getEncryptContent
办法看看,一个控制流,挑几个重点的讲一下,第一步是获取一个 key
,这个 key
是在后面设置的,后续会讲到,实际上这个 key
没啥用。
而后会有一个 isJsFormat
的格式化检测函数,失常应该是 false 的,如果你格式化了就为 true,也就会导致 f 的值为工夫戳加数美的域名,这个 f 值后续是 DES 的 Key,不对的话天然怎么滑都不会通过。
而后就是 DES 加密了,这个 DES 是规范的加密算法,下图中传入的 1 和 0 示意的是加密,0 和 0 则示意解密,解密的状况也有,后续会遇到,mode
为 ECB
,padding
为 ZeroPadding
,不须要 iv
,能够间接扣代码,或者间接引库即可。
var CryptoJS = require("crypto-js")
function DESEncrypt(key, word) {var key_ = CryptoJS.enc.Utf8.parse(key);
var srcs = CryptoJS.enc.Utf8.parse(word);
var encrypted = CryptoJS.DES.encrypt(srcs, key_, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.ZeroPadding
});
return encrypted.toString();}
function DESDecrypt(key, word) {var key_ = CryptoJS.enc.Utf8.parse(key);
var decrypt = CryptoJS.DES.decrypt(word, key_, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.ZeroPadding
});
return decrypt.toString(CryptoJS.enc.Utf8);
}
这里的四个值就剖析完了,还有八个值是在后面生成的,如下图所示 x 的值即为其余八个值,往前看是一个函数生成的,往里面跟即可。
跟进来是一个 getMouseAction
办法,外面先是挨个取值,后续会对这些值进行 DES 加密,下图中的 a、c 参数就是 register
接口返回的 k、l 值,s 参数是对 register
接口返回的 k 值进行解密操作:
上图中 u = this._data
外面的值,依据滑块、点选、无感模式的不同,也有所差别,以下代码中,以 baseData
来示意 this._data
的值,依据模式的不同,可分为三类,大抵形成如下:
滑块(slide
):
/*
track:滑动轨迹(x, y, t),distance:滑动间隔,randomNum:生成两数之间的随机值,示例:var track = [[0, -2, 0], [62, 1, 98], [73, 4, 205], [91, 3, 303], [123, -3, 397], [136, 8, 502], [160, 0, 599], [184, 0, 697], [169, 0, 797]]
var distance = 169
*/
var baseData = {}
baseData.mouseData = track
baseData.startTime = 0
baseData.endTime = track[track.length - 1][2] + randomNum(100, 500)
baseData.mouseEndX = distance
baseData.trueWidth = 300
baseData.trueHeight = 150
baseData.selectData = []
baseData.blockWidth = 40
滑块轨迹生成代码:
def get_sm_track(distance):
track_length = random.randint(4, 10)
track = [[0, -2, 0]]
m = distance % track_length
e = int(distance / track_length)
for i in range(track_length):
x = (i + 1) * e + m + random.randint(20, 40)
y = -2 + (random.randint(-1, 10))
t = (i + 1) * 100 + random.randint(-3, 5)
if i == track_length - 1:
x = distance
track.append([x, y, t])
else:
track.append([x, y, t])
logger.info("track: %s" % track)
return track
点选类(文字点选 select
、图标点选 icon_select
、语序点选 seq_select
、空间推理 spatial_select
):
/*
coordinate:点选坐标(x, y),randomNum:生成两数之间的随机值,示例:var coordinate = [[171, 101], [88, 102], [138, 109], [225, 100]]
*/
var baseData = {}
var time_ = new Date().getTime()
coordinate.forEach(function(co) {co[0] = co[0] / 300
co[1] = co[1] / 150
co[2] = time_
time_ += randomNum(100, 500)
})
baseData.mouseData = coordinate
baseData.startTime = time_ - randomNum(800, 20000)
baseData.endTime = coordinate[coordinate.length - 1][2]
baseData.mouseEndX = 0
baseData.trueWidth = 300
baseData.trueHeight = 150
baseData.selectData = coordinate
baseData.blockWidth = undefined
无感(auto_slide
):
/*
randomNum:生成两数之间的随机值
*/
var baseData = {}
baseData.mouseData = [[0, 0, 0]]
baseData.startTime = 0
baseData.endTime = randomNum(100, 500)
baseData.mouseEndX = 260
baseData.trueWidth = 300
baseData.trueHeight = 150
baseData.selectData = []
baseData.blockWidth = 40
这些值生成完了之后,就是挨个通过 getEncryptContent
进行加密,后面曾经剖析过,实际上就是 DES 加密,能够看到分为点选、滑块和无感三类,其中 DES Key 也是会每隔一段时间变动的:
再往下走还有三个加密参数,待加密值是定值,而后将 s 的值(也就是后面 register
接口返回的 k 通过 DES 解密后的值赋值给了 this._data.__key
)。
至此所有加密参数就搞完了。
后果验证
AST 获取动静参数
后面说了,/v1.0.4-171/captcha-sdk.min.js
文件地址,咱们称 v1.0.4
为大版本,171
为小版本,小版本每隔一段时间会更新,版本号会一直升高,具体更新周期是多少?这里举荐一个办法 document.lastModified
,该办法记录的是物理网页的最初批改工夫,咱们间接拜访 JS 地址,就能够间接查看不同版本的 JS 是啥时候更新的了,多比照几个版本,发现更新间隔时间并没有太显著的法则,如下图所示:
不同版本外面的 12 个加密参数的名称和 DES 加密的 Key 都不一样,咱们能够利用 AST 来动静获取这 12 个参数,通过测试,以下版本均可失常提取:
v1.0.4-148
~v1.0.4-171
v1.0.3-147
~v1.0.3-171
v1.0.1-147
~v1.0.1-171
截止本文公布,小版本 171
为最新,v1.0.4
小版本从 148
开始,v1.0.3
、v1.0.1
在 147
以前没有混同,可自行正则匹配,暂未发现其余大版本,如有遇到不能适配的,可分割我瞅瞅,残缺的代码在公众号 k 哥爬虫
中,有须要的能够点击下方链接。
【验证码逆向专栏】数美验证码全家桶逆向剖析以及 AST 获取动静参数
PS:此 AST 代码仅实现对动静参数的提取,并非还原所有的混同,提取进去的后果是有序、未去重的,后续按索引取就行。