赞
踩
原创文章,请勿转载!
本文内容仅限于安全研究,不公开具体源码。维护网络安全,人人有责。
本文关联文章超链接:
网页打开的图片都是裁剪后重新拼接的乱序图片,所以需要将乱序图片还原
这个chrome插件超级好用哦,网页加载资源文件时,可以将其替换成本地的资源文件,js代码调试时非常方便。
因为图片是由canvas标签绘制的,首先想到在浏览器的开发者工具中,监听Canvas事件,当浏览器使用canvas绘制图片时,触发Canvas事件。刷新时,有没有发现断点处的上下文很熟悉的感觉
下面是小编从源码(slide.7.8.4.js)中抠出来的背景图还原函数:
function $e(e, t) { var ylK = AaWgt.EeS()[28][34]; for (; ylK !== AaWgt.EeS()[28][32]; ) { switch (ylK) { case AaWgt.EeS()[4][34]: e = e[XxZM(150)]; t = t[XxZM(150)]; var r = e[YcZy(509)]; var n = e[YcZy(549)]; var i = h[XxZM(124)](YcZy(106)); i[YcZy(509)] = r; i[YcZy(549)] = n; var o = i[YcZy(167)](YcZy(187)); ylK = AaWgt.EeS()[8][33]; break; case AaWgt.EeS()[0][33]: o[XxZM(684)](e, 0, 0); var a = t[YcZy(167)](XxZM(187)); t[YcZy(549)] = n; t[XxZM(509)] = Ne; var s = n / 2; var u = 10; for (var c = 0; c < 52; c = c + 1) { var _ = Ge[c] % 26 * 12 + 1; var f = Ge[c] > 25 ? s : 0; var l = o[XxZM(697)](_, f, u, s); a[XxZM(621)](l, c % 26 * 10, c > 25 ? s : 0); } ylK = AaWgt.EeS()[0][32]; break; } } }
为了下面分析方便,将混淆的js还原下,配置chrome插件reres,将网页加载的js文件替换成对应的反混淆后的js文件,还原方式请看上一篇文章 极验滑块验证码破解与研究(一):AST还原混淆JS ,配置如下图:
下面是还原后的js代码,自己加上了一些注释,有错误的地方还望指出:
function $e(e, t) { // e为待还原的背景图对象 e = e["lKRC"]; // t为canvas图形容器对象 t = t["lKRC"]; // 将原图的宽度赋值给r,312 var r = e["width"]; // 将原图的高度赋值给n,160 var n = e["height"]; // h为document对象,i为canvas图形容器对象 var i = h["createElement"]("canvas"); // 定义canvas图形容器i的宽度为312 i["width"] = r; // 定义canvas图形容器i的高度为160 i["height"] = n; // o为图形容器i的二维绘图环境 var o = i["getContext"]("2d"); // 画布o上定位图像e o["drawImage"](e, 0, 0); // a为图形容器t的二维绘图环境 var a = t["getContext"]("2d"); // 定义canvas图形容器t的高度为160 t["height"] = n; // 定义canvas图形容器t的宽度为260 t["width"] = Ne; // s = 80 var s = n / 2; var u = 10; // Ge为数组对象,定值,其中为原图的还原顺序,共52个值,为0-51的整数。因此原图被切割成52块拼图,每块拼图大小为12*80。 // Ge = [39, 38, 48, 49, 41, 40, 46, 47, 35, 34, 50, 51, 33, 32, 28, 29, 27, 26, 36, 37, 31, 30, 44, 45, 43, 42, 12, 13, 23, 22, 14, 15, 21, 20, 8, 9, 25, 24, 6, 7, 3, 2, 0, 1, 11, 10, 4, 5, 19, 18, 16, 17] for (var c = 0; c < 52; c = c + 1) { // 每个小块在原图宽度上的相对位置, 注意每个小块的间隔是12 var _ = Ge[c] % 26 * 12 + 1; // 每个小块在原图高度上的相对位置 var f = Ge[c] > 25 ? s : 0; // 从原图中裁剪出对应的小块, 注意裁剪图片的宽度是10 var l = o["getImageData"](_, f, u, s); // 将裁剪的小块l, 画入二维绘图环境a a["putImageData"](l, c % 26 * 10, c > 25 ? s : 0); } // 注意原图为312*160的图片分割成52个大小为12*80的小块,最后由52个10*80的小块拼接成260*160完整图 }
这个js逻辑比较简单,所以改写成python代码啦
# -*- coding: utf-8 -*- import io from pathlib import Path from PIL import Image def parse_bg_captcha(img, im_show=False, save_path=None): """ 滑块乱序背景图还原 :param img: 图片路径str/图片路径Path对象/图片二进制 eg: 'assets/bg.webp' Path('assets/bg.webp') :param im_show: 是否显示还原结果, <type 'bool'>; default: False :param save_path: 保存路径, <type 'str'>/<type 'Path'>; default: None :return: 还原后背景图 RGB图片格式 """ if isinstance(img, (str, Path)): _img = Image.open(img) elif isinstance(img, bytes): _img = Image.open(io.BytesIO(img)) else: raise ValueError(f'输入图片类型错误, 必须是<type str>/<type Path>/<type bytes>: {type(img)}') # 图片还原顺序, 定值 _Ge = [39, 38, 48, 49, 41, 40, 46, 47, 35, 34, 50, 51, 33, 32, 28, 29, 27, 26, 36, 37, 31, 30, 44, 45, 43, 42, 12, 13, 23, 22, 14, 15, 21, 20, 8, 9, 25, 24, 6, 7, 3, 2, 0, 1, 11, 10, 4, 5, 19, 18, 16, 17] w_sep, h_sep = 10, 80 # 还原后的背景图 new_img = Image.new('RGB', (260, 160)) for idx in range(len(_Ge)): x = _Ge[idx] % 26 * 12 + 1 y = h_sep if _Ge[idx] > 25 else 0 # 从背景图中裁剪出对应位置的小块 img_cut = _img.crop((x, y, x + w_sep, y + h_sep)) # 将小块拼接到新图中 new_x = idx % 26 * 10 new_y = h_sep if idx > 25 else 0 new_img.paste(img_cut, (new_x, new_y)) if im_show: new_img.show() if save_path is not None: save_path = Path(save_path).resolve().__str__() new_img.save(save_path) return new_img if __name__ == '__main__': parse_bg_captcha("bg.webp", im_show=True, save_path='bg.jpg')
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。