当前位置: 首页 > news >正文

pythonjs逆向 破解滑动验证码 - hello-*

现在的滑动验证码防盗等级都比较高,之前的是一张完整的图片带缺口,现在返回的图片是打乱顺序拼接而成的,所以现在破解不仅要识别滑块的缺口,同时还需要复原完整的图片

  

c54099cd75c88a72da72f835d472799a    fa7fee46

 一.伪造请求获取验证码图片

    image

    image

  可以看到请求中主要的两个参数ctxid和request,所以我们只需要找到这两个参数的生成逻辑并进行请求就可以获取到验证码图片

image

   根据谷歌浏览器关键字搜索便可以找到相关字段的生成逻辑,我们只需要仿照着他的逻辑进行生成即可,值得注意的是request参数进行了双重加密,base64Encode中嵌套一个encrypt。这块我们不需要关注他具体是怎么加密的,我们只需要找到他对应的js方法,通过python直接调用即可

function a() {for (var n, e = "6_11_7_10_4_12_3_1_0_5_2_9_8".split("_"), t = [], r = 0; r < 52; r++)n = 2 * parseInt(e[parseInt(r % 26 / 2)]) + r % 2,parseInt(r / 2) % 2 || (n += r % 2 ? -1 : 1),n += r < 26 ? 26 : 0,t.push(n);return t}
function base64Encode(n) {var e = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split("");return function(n) {var t, r, i, o, a, s, c;for (r = i = 0,o = n.length,s = (o -= a = o % 3) / 3 << 2,a > 0 && (s += 4),t = new Array(s); r < o; )c = n.charCodeAt(r++) << 16 | n.charCodeAt(r++) << 8 | n.charCodeAt(r++),t[i++] = e[c >> 18] + e[c >> 12 & 63] + e[c >> 6 & 63] + e[63 & c];return 1 == a ? (c = n.charCodeAt(r++),t[i++] = e[c >> 2] + e[(3 & c) << 4] + "==") : 2 == a && (c = n.charCodeAt(r++) << 8 | n.charCodeAt(r++),t[i++] = e[c >> 10] + e[c >> 4 & 63] + e[(15 & c) << 2] + "="),t.join("")}(n)}function encrypt(n) {var e, t = "e98ae8878c264a7e";function r(n) {if (/^[\x00-\x7f]*$/.test(n))return n;for (var e = [], t = n.length, r = 0, i = 0; r < t; ++r,++i) {var o = n.charCodeAt(r);if (o < 128)e[i] = n.charAt(r);else if (o < 2048)e[i] = String.fromCharCode(192 | o >> 6, 128 | 63 & o);else {if (!(o < 55296 || o > 57343)) {if (r + 1 < t) {var a = n.charCodeAt(r + 1);if (o < 56320 && 56320 <= a && a <= 57343) {var s = 65536 + ((1023 & o) << 10 | 1023 & a);e[i] = String.fromCharCode(240 | s >> 18 & 63, 128 | s >> 12 & 63, 128 | s >> 6 & 63, 128 | 63 & s),++r;continue}}throw new Error("Malformed string")}e[i] = String.fromCharCode(224 | o >> 12, 128 | o >> 6 & 63, 128 | 63 & o)}}return e.join("")}function i(n) {return 4294967295 & n}function o(n, e, t, r, i, o) {return (t >>> 5 ^ e << 2) + (e >>> 3 ^ t << 4) ^ (n ^ e) + (o[3 & r ^ i] ^ t)}function a(n, e) {var t, r = n.length, i = r >> 2;0 != (3 & r) && ++i,e ? (t = new Array(i + 1))[i] = r : t = new Array(i);for (var o = 0; o < r; ++o)t[o >> 2] |= n.charCodeAt(o) << ((3 & o) << 3);return t}return null == n || 0 === n.length ? n : (n = r(n),t = r(t),function(n, e) {var t = n.length, r = t << 2;if (e) {var i = n[t - 1];if (i < (r -= 4) - 3 || i > r)return null;r = i}for (var o = 0; o < t; o++)n[o] = String.fromCharCode(255 & n[o], n[o] >>> 8 & 255, n[o] >>> 16 & 255, n[o] >>> 24 & 255);var a = n.join("");return e ? a.substring(0, r) : a}(function(n, e) {var t, r, a, s, c, l, d = n.length, u = d - 1;for (r = n[u],a = 0,l = 0 | Math.floor(6 + 52 / d); l > 0; --l) {for (s = (a = i(a + 2654435769)) >>> 2 & 3,c = 0; c < u; ++c)t = n[c + 1],r = n[c] = i(n[c] + o(a, t, r, c, s, e));t = n[0],r = n[u] = i(n[u] + o(a, t, r, u, s, e))}return n}(a(n, !0), ((e = a(t, !1)).length < 4 && (e.length = 4),e)), !1))}
mainJs = execjs.compile(open(r"./Utils/main.js",encoding="utf-8").read())def getCaptcha(contextid):o = "appid=202503141611|ctxid=" + contextid+ "|a=quoteapi|p=|r=" + str(random.random())oEncrypt=mainJs.call('encrypt',o)# 对字符串进行URL编码reqStr=quote(mainJs.call('base64Encode',oEncrypt))print(reqStr)url='api/captcha/get?callback=&ctxid='+contextid+'&request='+reqStr+'&_='+str(int(time.time()*1000))print(url)initResponse = http.request('GET', url)print('initResponse:')print(initResponse.data)initResult = initResponse.data.decode('UTF-8')initData = json.loads(initResult)if initData["ReturnCode"]=="0" and initData["Data"]["CaptchaType"]=="init":slideResponse = http.request('GET', url)print('slideResponse:')print(slideResponse.data)slideResult = slideResponse.data.decode('UTF-8')slideData = json.loads(slideResult)print(slideData)if slideData["ReturnCode"]=="0" and slideData["Data"]["CaptchaType"]=="slide":print("getCaptcha slide success")captchaInfo={}captchaInfoObj=json.loads(slideData["Data"]['CaptchaInfo'])captchaInfo["bg"]="https://"+captchaInfoObj['static_servers'][0]+captchaInfoObj['bg']captchaInfo["fullbg"]="https://"+captchaInfoObj['static_servers'][0]+captchaInfoObj['fullbg']captchaInfo["slice"]="https://"+captchaInfoObj['static_servers'][0]+captchaInfoObj['slice']return captchaInfoelse:print("getCaptcha slide error")return Noneelse:print("getCaptcha init error")return None

 二.重新拼接验证码图片

image  image

 

通过上边的操作,我们就能获得一张顺序错乱的验证码,接下来我们就需要重新复原这张图片。接口返回一张错乱的验证码图片,但页面上为什么显示正常呢?那肯定是前端js逻辑把这块给处理了一下,页面就得到了一张正常的验证码图片,那么我们就需要找一下他前端js的处理逻辑了,只要按照他的逻辑处理,我们同样也能复原这张错乱验证码

image

 

def get_image(image_url):e = "6_11_7_10_4_12_3_1_0_5_2_9_8".split("_")location_list = []for r in range(52):location = {}# 计算索引index = int(r % 26 / 2)n = 2 * int(e[index]) + (r % 2)# 条件判断if (int(r / 2) % 2) == 0:n += -1 if (r % 2) else 1# 添加偏移量if r < 26:n += 26location['x'] = n % 26 * 12 + 1location['y'] = (0 - 160)/ 2 if n <= 25 else 0location_list.append(location)print('==================================')image_result = requests.get(image_url).contentimage_file = BytesIO(image_result) image = merge_image(image_file,location_list)return image

 三.获取缺口距离

def get_distance(url1,url2):'''拿到滑动验证码需要移动的距离:param image1:没有缺口的图片对象:param image2:带缺口的图片对象:return:需要移动的距离'''# print('size', image1.size)#print(type(image1))len=0threshold = 50image1=get_image(url1)image2=get_image(url2)# 创建绘图对象draw = ImageDraw.Draw(image2)for i in range(0,image1.size[0]):  # 260for j in range(0,image1.size[1]):  # 160pixel1 = image1.getpixel((i,j))pixel2 = image2.getpixel((i,j))res_R = abs(pixel1[0]-pixel2[0]) # 计算RGB差res_G = abs(pixel1[1] - pixel2[1])  # 计算RGB差res_B = abs(pixel1[2] - pixel2[2])  # 计算RGB差if res_R > threshold and res_G > threshold and res_B > threshold:len=i  #
                print(f'{len}-({res_R},{res_G},{res_B})')# 绘制一条线draw.line((130, 0, len, 20), fill="red", width=3)break image2.show()#image2.save('./Image/code.jpg')return len

 三.根据缺口距离生成滑动轨迹

def generate_sliding_track(x):# 初始化轨迹列表slide_track = [[random.randint(-50, -20), random.randint(-200, -100), 0],[0, 0, 0],]if x < 100:move_section = 1 #如果移动距离小于100 那么move次数为x加上 7到20之间的数else:move_section = 2 #如果移动距离小于100 那么move次数为x加上 2乘 7到20之间的数
up_down = random.randint(0, 1) #确定一个方向 x大于0或x小于0y = 0  #数组的y值time = random.randint(100,180)  #初始时间 即为第二个数组的时间  后续时间累加操作就可以了count = 0flag = 0repetition = int(x/4)  #重复x出现的个数frist_count = random.randint(6,10) #前面y为0的数组个数for i in range(x*random.randint(move_section*7,move_section*21)): #move_section 在这里起作用if i+1 > x: #如果i+1要等于x 或者小于x 但这里基本上都是等于xbreakif up_down == 0:  #up_down如果大于0 那么这个轨迹就是y增轨迹if i >frist_count:if count==0:y += random.randint(0, 1)count +=1if flag>random.randint(8,10):count = 0flag = 0if i + 1 > int(x / 5)*4:time += random.randint(20, 70)elif i+1 > x-3:time += random.randint(80, 180)else:time += random.randint(0,5)slide_track.append([i+1,y,time])flag+=1if random.randint(0,1):if repetition:slide_track.append([i + 1, y, time+random.randint(0,3)])flag += 1repetition -= 1else:  #前面几个数组y都为0time += random.randint(0, 5)slide_track.append([i + 1, y, time])if random.randint(0,1):if repetition:slide_track.append([i + 1, y, time+random.randint(0,3)])repetition -= 1if up_down == 1:  #up_down如果小于0 那么这个轨迹就是y减轨迹if i > frist_count:if count==0:y -= random.randint(0, 1)count +=1if flag>random.randint(8,10):count = 0flag = 0if i + 1 > int(x / 5)*4:time += random.randint(7, 40)elif i+1 > x-3:time += random.randint(80, 180)else:time += random.randint(0, 5)slide_track.append([i+1,y,time])flag +=1if random.randint(0,1):if repetition:slide_track.append([i + 1, y, time+random.randint(0,3)])flag += 1repetition -= 1else:time += random.randint(0, 5)slide_track.append([i + 1, y, time])if random.randint(0,1):if repetition:slide_track.append([i + 1, y, time+random.randint(0,3)])repetition -= 1return slide_track

 四.提交验证

image

 通过逆向网站js,我们可以查看到他提交时的逻辑,其中userresponse为缺口距离,data便是轨迹数据,我们只需要根据他的逻辑来实现我们的python逻辑即可

def validate(ctxid,l):try:tracks=generate_sliding_track(l)# # tn={"appid":"202503141611","ctxid":"","type":"slide","userresponse":0,"data":"","account":"quoteapi","password":"","t":0,"random":random.random()}n['ctxid']=ctxidn['userresponse']=ln['data']=format_tracks(tracks)n['t']=tracks[-1][2]o = "appid=" + n['appid'] + "|ctxid=" + n['ctxid'] + "|type=" + n['type'] + "|u=" + str(n['userresponse']) + "|d=" + n['data'] + "|a=" + n['account'] + "|p=" + n['password'] + "|t=" + str(n['t']) + "|r=" + str(n['random'])oEncrypt=mainJs.call('encrypt',o)# 对字符串进行URL编码reqStr=quote(mainJs.call('base64Encode',oEncrypt))print(reqStr)url='/api/captcha/Validate?callback=&ctxid='+ctxid+'&request='+reqStr+'&_=1756106181125'print(url)header['Referer']=''response = http.request('GET', url, headers=header)print(response.data)result = response.data.decode('UTF-8')jsonData = json.loads(result)print(jsonData)if jsonData["ReturnCode"]=="0":print("validate success")return json.loads(jsonData["Data"]['Result'])['validate']else:print("validate error")return Noneexcept Exception as e:print(e)return None

 

http://www.hskmm.com/?act=detail&tid=8565

相关文章:

  • 解决 pandas.to_csv 乱码、丢失行和自动换行问题 时间转换
  • JavaDay7
  • 前端场景题笔记
  • P3934 [Ynoi Easy Round 2016] 炸脖龙 I 做题记录
  • 【CompletableFuture 核心操作全解】详细注释版
  • 关于学术不端的一些思考
  • python基础-字典
  • pod 内nslookup请求时常异常
  • 单调队列优化DP
  • 4.5.11版本闪亮登场~快来看看有哪些新功能
  • 教你数分钟内创建并运行一个 DolphinScheduler Workflow!
  • AT_agc065_b [AGC065B] Erase and Insert
  • 《大模型时代——智能体的崛起与应用实践(微课视频版)》
  • 第三节:GoLangChain提示词(Prompts)处理详解
  • rhel8 中vdo 邏輯卷的邏輯擴容
  • Codeforces Round 1051 (Div. 2) 部分题解
  • kingbase金仓数据库的密码有效期和密码复杂度
  • HDF5文件
  • Error encountered when performing Introspect the Portion of idea Introspect using JDBC metadata在哪设置
  • 核桃 CSP-S 模拟
  • 正确输入连字号、连接号、破折号和负号
  • 9 月记录
  • python基础-元组
  • .net core中获得程序集以及注入框架的方法总结
  • python基础篇-list(列表)
  • vscode使用powershell中文乱码
  • 关于如何读懂 P11832 [省选联考 2025] 图排列?
  • Untitled
  • 敏感性分析
  • 完整教程:论园区电气安全管理系统的重要性