声明
本文章中所有内容仅供进修交流使用,不消于其他任何目的,不供应完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于贸易用途和不法用途,不然由此发生的一切后果均与作者无关!
逆向方针
方针:xx80 邮箱多种类验证码逆向剖析
网址:aHR0cHM6Ly93d3cuMjk4MC5jb20vbG9naW4v
这个网站的验证码,会络续变换,非常有意思,堪比一个验证码产物。今朝碰到的种类有:滑块、点选、扭转、拼图乱序、钟表
,不知道还有没有其余,不外分歧类其余验证码加解密把持一般,首要就是明文参数组织的分歧。我们就来解决一下它的滑块、点选、扭转验证码,因为这几个对照常见:
流程剖析
我们就以滑块验证码来剖析加解密把持,先抓包剖析,发现首页加载,验证码加载两处处所都有 debugger:
发现这两处 debugger 的组织都是一般的,不外在分歧的 js 文件中,能够发现它是经由函数的 constructor 来执行 debugger 把持,解决的方式好多,首要讲两种:
(()=>{
Function.prototype.__constructor = Function.prototype.constructor;
Function.prototype.constructor = function(){
if(arguments && typeof arguments[0]==='string'){
if("debugger"===arguments[0]){
return
}
return Function.prototype.__constructor.apply(this,arguments);
}
}
})()
相关常识,能够阅读K哥往期文章:JS 逆向之 Hook,吃着暖锅唱着歌,倏忽就被麻匪劫了!
function _0x49fb64(_0x4e04ef) {
function _0x36c27c(_0x53f377) {
var _0xbd8c62 = _0x2d52;
if (typeof _0x53f377 === 'string')
return function(_0x18f1bb) {}
[_0xbd8c62(0x2d1)]('while\x20(true)\x20{}')[_0xbd8c62(0x353)](_0xbd8c62(0x222));
else
('' + _0x53f377 / _0x53f377)['length'] !== 0x1 || _0x53f377 % 0x14 === 0x0 ? function() {
return !![];
}
['constructor'](_0xbd8c62(0x1f8) + 'gger')[_0xbd8c62(0x31a)](_0xbd8c62(0x526)) : function() {
return ![];
}
['constructor'](_0xbd8c62(0x1f8) + _0xbd8c62(0x25d))[_0xbd8c62(0x353)]('stateObject');
_0x36c27c(++_0x53f377);
}
try {
if (_0x4e04ef)
return _0x36c27c;
//else
// _0x36c27c(0x0);
} catch (_0x5b38c0) {}
}
我们看滑块验证码的图片恳求接口:
发现返回的数据 mes 密文,相关 type 类型如下:
我们在来看看滑块图片的恳求参数:
发现有 token 、appid、k 需要解决,我们先搜刮,发现 token 、appid 是经由 getBehaviorRegister 接口返回:
小结一下
今朝我们需要逆向剖析的方针有:
我们一步步来解决。
sign 值
经由启动器查找,或许 xhr 断点很快就能找到如下生成位置:
更进 i 函数后,就会很显着的看出其加密体式为 SHA256,明文 i 的组织对照简洁,就不剖析了:
k 值
同样的方式很快就能找到如下生成位置:
跟进去:
很显着是 AES 加密,加密模式为 ECB:
后背传入的参数离别是明文以及 key 值。明文部门为 fingerPrinterList[0x2] + ',' + fingerPrinterSon
,都是指纹信息加密生成,就不具体剖析,生成位置如下:
key 值 :
(_0x37cb01 + _0x134810)['substring'](8, 24)
mes 值解密
这个我们有很多方式定位,好比打 xhr 断点跟值或许 HOOK 等等。其实上面剖析 k 值的生成时 AES 加密的下面就是 AES 解密算法的位置,我们其实都能够猜到。这里选择跟值,找到 'success' : 里面的把持,很快就能定位到解密函数:
第一个参数就是 mes 值,第二个参数 key 跟上面 k 值加密的 key 连结一致。
解密后的数据雷同如下:
'{"dif":"0","answer":["9","未","雨"],"bg":"https://csmoss.duoyi.com/19/124bf7b8f82292","num":3,"sn":"9a363bc74a8a","type":23,"list":[]}'
滑块的图片时乱序的,需要还原,其他的不需要,附上还原图片测试代码:
def split_and_reorder_image(image_path, reorder_array, split_ratios=(10, 1)):
"""
:param image_path: 乱序的图片路径
:param reorder_array: 滑块图片接口返回的 mes 解密后的 list
:param split_ratios:
"""
# 打开并读取原始图片
original_image = Image.open(image_path)
width, height = original_image.size
# 按照指定的比例朋分图片
split_width = width // split_ratios[0]
split_height = height // split_ratios[1]
images = [(i * split_width, j * split_height, (i + 1) * split_width, (j + 1) * split_height)
for i in range(split_ratios[0]) for j in range(split_ratios[1])]
images = [original_image.crop(box) for box in images]
reordered_images = ["" for _ in range(len(images))]
# 遍历reorder_array,凭据个中的索引将images列表中的元素添加到reordered_images列表的响应位置
for i, new_index in enumerate(reorder_array):
reordered_images[new_index] = images[i]
# 将从新排序的子图片拼接回原始图片
new_width = split_width * split_ratios[0]
new_height = split_height * split_ratios[1]
new_image = Image.new('RGB', (new_width, new_height))
for i, img in enumerate(reordered_images):
x = (i % split_ratios[0]) * split_width
y = (i // split_ratios[0]) * split_height
new_image.paste(img, (x, y))
new_image.save(image_path)
验证恳求
成功:{"message":{"pass":"cd5201bdb47748fe8b9114769d7122cb"},"code":1};
失败:{"message":"not match","code":0}。
恳求参数:
剖析一下:
直接从启动器下手,点击进去就能够发现生成位置:
portion = Math['random']()['toFixed'](2);
cn = md5('num' + parseInt(10000000 + Math['random']() * 1000000) + 'time' + new Date()['getTime']());
timestamp = new Date()['getTime']();
// token 、sn 上面接口返回; :5b350044ac092f7bf3c2bc791638ca2f 文件写死
signature = md5(md5(portion + ':' + timestamp + ':' + token) + ':' + sn + ':1:' + cn + ':auth' + ":5b350044ac092f7bf3c2bc791638ca2f")
我们来剖析一下分歧验证码的明文有什么差别,是若何生成的:
点选要害参数:
slide:点选轨迹;
click_behavior:[点选坐标 + 时间 + 顺序];
portion,较量后点选坐标,较量如下,就是和图片宽高的一个比例:
# center_points 点选坐标
portion = []
for i in range(len(center_points)):
portion.append([str(format(center_points[i][0]/320, '.5f')), str(round((center_points[i][1]+0.83332824707031)/200, 5))])
滑块要害参数:
/**
*
* @param x : ddddocr / cv2 识别距离
* @param startposition : 这是获取图片接口返回的startPosition参数
* @returns {{portion: number, value: number}} : value: 滑动条滑动的距离, portion: 较量后的距离
*/
function get_slide_distance(x, startposition) {
var _0x3c1ee0 = 72;
var _0x3d74b9 = 1.11;
var _0x4b4e13 = 0.74;
var _0x1fda62 = startposition; // 这是获取图片接口返回的startPosition参数
var _0x101145 = 1;
var _0x3d3596;
for (var i = 0; i < 999; i++) {
_0x101145++;
var _0x3d3596 = Math["pow"](_0x3c1ee0 * _0x101145, 0.4 * Math['abs'](Math['sin'](0.025 * _0x101145))) + Math["pow"](_0x101145, _0x3d74b9) * _0x4b4e13 + _0x1fda62;
// _0x3d3596 :较量出来的识别距离
if (Math.abs(_0x3d3596 - x) <= 1) {
break;
}
}
var portion = (Math["pow"](_0x3c1ee0 * _0x101145, 0.4 * Math['abs'](Math['sin'](0.025 * _0x101145))) + Math["pow"](_0x101145, _0x3d74b9) * _0x4b4e13) / 320;
var res={"portion":portion,"value":_0x101145}
return res;
}
扭转要害参数:
portion:识别角度;
slide,扭转滑动轨迹,是凭据滑动条滑动的距离来较量轨迹,滑动条滑动的距离较量如下:
int(portion * 0.6)
轨迹参考代码:
# 轨迹
import random
def __ease_out_expo(sep):
"""
缓动函数 easeOutExpo
参考:https://easings.net/zh-cn#easeOutExpo
"""
if sep == 1:
return 1
else:
return 1 - pow(2, -10 * sep)
def get_slide_track(distance):
"""
凭据滑动距离生成滑动轨迹
:param distance: 需要滑动的距离
:return: 滑动轨迹<type 'list'>: [[x,y,t], ...]
x: 已滑动的横向距离
y: 已滑动的纵向距离, 除起点外, 均为0
t: 滑动过程消费的时间, 单元单子: 毫秒
"""
if not isinstance(distance, int) or distance < 0:
raise ValueError(f"distance类型必需是大于等于0的整数: distance: {distance}, type: {type(distance)}")
# 初始化轨迹列表
slide_track = [
[0, 0, -(distance * 20)]
]
# 共记录count次滑块位置信息
count = 50 + int(distance / 2)
# 初始化滑动时间
t = slide_track[0][2]
# 记录上一次滑动的距离
_x = 0
_y = slide_track[0][1]
for i in range(count):
_y -= 1 if i % 9 == 0 else 0
# 已滑动的横向距离
x = slide_track[0][0] + round(__ease_out_expo(i / count) * distance)
# 滑动过程消费的时间
t -= random.randint(10, 20)
if x == _x:
continue
slide_track.append([x, _y, t])
_x = x
slide_track.append(slide_track[-1])
return slide_track
究竟验证
滑块:
感激北大出书社友情赞助5本《游戏力:竞技游戏设计实战教程》.
1.从选题立意出发,具体讲解“好游戏”的设计前提:选题决意了游戏的主题和基调,进而影响游戏的整体设计和玩家体验,一个好的选题,能够让游戏赢在起跑线。
2.讲解游戏焦点机制设计,是游戏“好玩”的要害:游戏的焦点机制是游戏的魂魄,直接决意玩家的留存率,经由合理设计焦点机制,游戏可以吸引更多的玩家,提高游戏的市场占有率与盈利能力。
3.分门别类分化游戏设计过程:具体介绍游戏的地图设计、地图设计、含社交系统、成家机制、排行榜段位、付费与成就系统等,让游戏的每一个细节都布满吸引力。
照样老例子,在2024年4月13日之前存眷本公家号,而且在非送书文章下面有过留言的同窗都能够列入。在本文留言即可。我会选出5位同窗送出本书。