吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1544|回复: 1
收起左侧

[Web逆向] spiderdemo中等题目简单分析

[复制链接]
ljl9090 发表于 2025-10-22 20:56

T2

主要是代码混淆,需要处理,处理后发现:典型MD5,但是验证不通过,魔改版本。


function getSign(input) {
    // 关键改动:输入加盐
    input = input + "\xA3\xAC\xA1\xA3fdjf,jkgfkl";
    return md5(input);
  }

  function md5(str) {
    return hex(md5_cycle(str2binl(str), str.length * 8));
  }

  function md5_cycle(x, len) {
    let a = 1732584193;
    let b = -271733879;
    let c = -1732584194;
    let d = 271733878;
    // 填充
    x[len >> 5] |= 0x80 << (len % 32);
    x[(((len + 64) >>> 9) << 4) + 14] = len;
    for (let i = 0; i < x.length; i += 16) {
      let olda = a, oldb = b, oldc = c, oldd = d;
      a = ff(a, b, c, d, x[i + 0], 7, -680876936);
      d = ff(d, a, b, c, x[i + 1], 12, -389564586);
      c = ff(c, d, a, b, x[i + 2], 17, 606105819);
      b = ff(b, c, d, a, x[i + 3], 22, -1044525330);
      a = ff(a, b, c, d, x[i + 4], 7, -176418897);
      d = ff(d, a, b, c, x[i + 5], 12, 1200080426);
      c = ff(c, d, a, b, x[i + 6], 17, -1473231341);
      b = ff(b, c, d, a, x[i + 7], 22, -45705983);
      a = ff(a, b, c, d, x[i + 8], 7, 1770035416);
      d = ff(d, a, b, c, x[i + 9], 12, -1958414417);
      c = ff(c, d, a, b, x[i + 10], 17, -42063);
      b = ff(b, c, d, a, x[i + 11], 22, -1990404162);
      a = ff(a, b, c, d, x[i + 12], 7, 1804603682);
      d = ff(d, a, b, c, x[i + 13], 12, -40341101);
      c = ff(c, d, a, b, x[i + 14], 17, -1502002290);
      b = ff(b, c, d, a, x[i + 15], 22, 1236535329);

    ...  

      a = add(a, olda);
      b = add(b, oldb);
      c = add(c, oldc);
      d = add(d, oldd);
    }
    return [a, b, c, d];
  }

T3

(1)
https://www.spiderdemo.cn/static/protos/challenge.proto 生成一个 .proto 文件
转换为 python 调用 (authentication_pb2.py)

计算 signature

function get_sign(){
    const timestamp = Date['now']()
    const timestampStr = timestamp["toString"]();
    const signature = md_sign['OooO'](timestampStr);
    return [timestamp,signature]
}

其中 加密函数md_sign 一看四个初始值,很像md5,但是肯定是魔改了。 
然后就是 protobuf 请求: 大概这样 类似 (所有人代码肯定存在差异,大胆修改)
request_msg = authentication_pb2.ChallengeRequest()
request_msg.page = page
request_msg.challengetype = "surwrexibfkdoohqjh"
request_msg.timestamp = timestamp  # 确保是 int 类型
request_msg.signature = signature  # 确保是 string 类型
# 1. 本地序列化
data = request_msg.SerializeToString()

try:
    test_msg = authentication_pb2.ChallengeRequest()
    test_msg.ParseFromString(data)
except Exception as e:
    print(f"Protobuf 格式错误: {e}")

结果:

T7 CSS1_challenge

"""
<style>:root { --offset-0-4244: calc(max((2 3 5px), (2 3 5px))); --offset-1-4629: calc(min((3 5px), (3 5px))); --neg-2-2731: calc(-1 var(--offset-1-4629)); --offset-3-6224: calc((3 5px) / 1 1); }</style><span data-type="char" data-calc="二 五" style="position:relative;left:var(--offset-0-4244)">7</span><span title="position data" data-calc="c e" style="position:relative;left:var(--neg-2-2731)">6</span><span title="position data" data-calc="三 * 五" style="position:relative;right:var(--offset-3-6224)">4</span>9
"""

(1) 计算style 中的函数 (有的没有style)
  ① 没有style
   if '</style>' not in html_code:
        html_code = re.sub(r'<span.*?>', '', html_code)
        style = re.sub(r'</span>', '', html_code).strip()
        digits_with_offset = [(digit, 0) for digit in re.findall(r'[0-9]', html_code)]
        return  move_by_offset(digits_with_offset)
move_by_offset 是根据当前位置进行移动操作

② 处理style
类似下面的一些处理方法:
def eval_calc(self, expr):
    expr = expr.strip()
    expr = self.replace_chinese_numerals(expr)
    var_pattern = r'var\(--[^)]+\)'
    while re.search(var_pattern, expr):
        var_match = re.search(var_pattern, expr)
        var_name = var_match.group(0)[4:-1]  # 提取变量名
        var_value = self.variables.get(var_name, 0)
        expr = expr.replace(var_match.group(0), str(var_value))
    try:
        # 替换运算符号周围的空格
        expr = re.sub(r'\s*([+\-*/()])\s*', r'\1', expr)
        expr = expr.replace('px','')

        return self.parse_number(str(eval(expr)))
    except Exception as e:
        print(f"计算表达式错误: {expr}, 错误: {e}")
        return 0

def parse_number(self, s):
        s = s.strip().replace('px', '')  # 移除单位
        if s.startswith('0x'):
            return int(s, 16)
        elif s.startswith('0b'):
            return int(s, 2)
        else:
            return float(s) if '.' in s else int(s)

    def extract_digits(html_code):
    soup = BeautifulSoup(html_code, 'html.parser')
    digits_with_offset = []
    evaluator = CSSEvaluator()
    if '</style>' not in html_code:
        # style = re.sub(r'<span.*?>', '', html_code,flags=re.DOTALL)
        html_code_no = re.sub(r'<span.*?>', '', html_code)
        style = re.sub(r'</span>', '', html_code_no).strip()
        digits_with_offset = [(digit, 0) for digit in re.findall(r'[0-9]', style)]
        return  move_by_offset(digits_with_offset)

    # 解析CSS变量
    style_tag = soup.find('style')
    if style_tag and style_tag.string:
        evaluator.parse_css_variables(style_tag.string)
    # print(evaluator.variables)
    html_code1 = re.sub(r'<style.*?</style>', '', html_code)
    # print(html_code1)
    result = html_code1.split('</span>')

    start_num,end_num,digit,style = '','','',''
    for item in result:
        # print('item',item.strip())
        if item == '':
            continue
        if item.strip().isdigit():
            # print('end',item)
            end_num = item.strip()
        else:
            if item.strip()[0].isdigit():
                start_num = item.split('<span')[0]
                # print('start',item)
            if item.strip()[-1].isdigit():
                digit = item.strip()[-1]
                style = item.strip()
                # print('digit',digit,style)
        if start_num and start_num.isdigit():
            for s1 in start_num:
                digits_with_offset.append((s1, 0))
            start_num = ''
            # digits_with_offset.append((start_num, 0))
        if style:
            if '"position:relative' not in style and end_num.strip() == '':
                digits_with_offset.append((digit, 0))
                continue
            # 提取偏移量变量
            if end_num.strip().isdigit():
                pass
            else:
                offset_match = re.search(r'left:var\(([^)]+)\)', style)
                if not offset_match:
                    offset_match = re.search(r'right:var\(([^)]+)\)', style)
                    offset_var = offset_match.group(1)
                    offset = evaluator.variables.get(offset_var, 0)
                    offset = -offset
                else:
                    offset_var = offset_match.group(1)
                    offset = evaluator.variables.get(offset_var, 0)
                # 按原始需求进行15px单位转换
                offset = int(offset / 15) if offset != 0 else 0
                digits_with_offset.append((digit, offset))
                digit,style = '',''
        if end_num and end_num.strip().isdigit():  # 添加结束数字
            for s2 in end_num:  # 添加结束数字
                # print('s2',s2)
                digits_with_offset.append((s2, 0))
            end_num = ''
    print(digits_with_offset)
    return move_by_offset(digits_with_offset)

...

(2) 处理文本的数据
① 获取数字和位置信息
这里 处理需要注意很多细节,比如 很多数据没有 在span标签中,如何处理,
"position:relative' 是否存在 ,left: 还是right: 等

3.结果

T9 font_sprites_challenge  (一种思路)

  1. 获取图片 分隔
  2. 计算相似度

    
    def calculate_hash(image_path):
    """计算图片的感知哈希值"""
    img = cv2.imread(image_path)
    if img is None:
        raise ValueError(f"无法读取图片: {image_path}")
    img = cv2.resize(img, (8, 8), interpolation=cv2.INTER_AREA)
    
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    avg = gray.mean()
    hash_str = ""
    for i in range(8):
        for j in range(8):
            # 如果像素值大于平均值则为1,否则为0
            hash_str += "1" if gray[i, j] > avg else "0"
    return hash_str
    def hamming_distance(hash1, hash2):
    if len(hash1) != len(hash2):
        raise ValueError("两个哈希值长度必须相同")
    return sum(c1 != c2 for c1, c2 in zip(hash1, hash2))

def image_similarity(image1_path, image2_path):

计算哈希值

hash1 = calculate_hash(image1_path)
hash2 = calculate_hash(image2_path)
distance = hamming_distance(hash1, hash2)
similarity = 1 - distance / 64
return similarity
```
  1. 细节处理

T10 font_svg_challenge

  1. 通过获取的数据,整理一份字典
  2. 计算相似度
    python
    def parse_data(data):
    paths = re.findall('<path d="(.*?)"', data['svg_content'], re.S)
    result = ""
    for path in paths:
        min_dist = float("inf")
        matched_digit = ""
        for digit, ref_path in ref_paths.items():
            dist = Levenshtein.distance(path, ref_path)
            if dist < min_dist:
                min_dist = dist
                matched_digit = digit
        result += matched_digit
    result = [int(result[i:i + 4]) for i in range(0, len(result), 4)]
    print("识别结果:", result)
    return result
  3. 细节处理

T16 slide_scratch_challenge

  1. 获取图片
    2 通过提示词,和物体信息 计算移动距离
    由于下载的图片可以看见信息 这里首先对提示词整理,大概十几个类别,同时对物体信息检测分隔(yolo)。
    计算这里 可以训练分类模型 ,或者相似度模型
    ResNet50 残差神经网络可以进行分类训练
    3.验证token
  2. 处理细节 请求结果

T17 slide_puzzle_challenge

  1. 获取图片
    2  计算距离
    这里分隔图片 上下,使用边缘算子,计算拼接相似度,每次移动一个像素。选择最合理的距离

    def calculate_distance(image_path, split_y=99):
    img = cv2.imread(image_path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    upper = gray[:split_y, :]  # 上半部分(需要移动的区域)
    lower = gray[split_y:, :]  # 下半部分(固定区域)
    upper_edges = cv2.Canny(upper, xxxx, xxxxxx) #自己参数调整
    
    contours, _ = cv2.findContours(upper_edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    if not contours:
        raise ValueError("未检测到上半部分轮廓,请调整边缘检测参数")
    cnt = max(contours, key=lambda c: cv2.contourArea(c))
    x, y, w, h = cv2.boundingRect(cnt)  # 上半部分轮廓的边界框
    print(x, y, w, h )
    result = cv2.matchTemplate(lower, upper_edges, cv2.TM_CCOEFF_NORMED)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
    match_x, _ = max_loc  # 匹配到的水平坐标
    distance = match_x - x
    return distance
  2. 验证token

  3. 处理细节 获取结果

T18 cap6_challenge

  1. 获取图片

  2. 通过cv2 或者dddd 计算两次距离

  3. 计算结果

“”“个人觉得 这个比较简单了。 ”“”

T19 slide_cylinder_challenge

上一篇已经写了。
补充点:
(1)计算轨迹(x,y,t) tracks = track[:-50]

def get_distince():
    # 读取图像
    image = cv2.imread('img.jpg')
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # Canny边缘检测
    edges = cv2.Canny(gray, 50, 150)
    # 寻找轮廓(用于确定人物区域)
    contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    person_x = 100
    # 假设最大的轮廓是人物(实际需根据图像情况调整)
    if contours:
        largest_contour = max(contours, key=cv2.contourArea)
        x, y, w, h = cv2.boundingRect(largest_contour)
        person_x = x
    else:
        print("未检测到轮廓")
    return person_x
def on_mouse_move(currentX, clientY,timestamp):
    max_container = 400
    minX, maxX = 0x118, max_container - 0x46  # 280, 334
    currentX = max(0, min(maxX, currentX))  #
    # slideDistance = currentX
    # targetAngle = currentX / maxX * 360  # 0x168 = 360°
    posX = currentX / maxX * 350  # 0x15e = 350
    record = {
        'x': posX,
        'y': clientY,
        'timestamp': timestamp
    }
    return record

(2) 计算的轨迹 的编码不是标准的
编码可以直接根据js 改写即可。

T20 cap8_challenge

  1. 获取图片
  2. 目标检测 分隔图片
    图片标注
    进行相似度模型训练
    可以借鉴这个帖子
    计算获取结果
    3  请求 注意细节

“”“ 这道题不难,但可能是最麻烦的”“”

T26 click_stitch_challenge

  1. 获取图片
  2. 计算旋转角度
    def solve_puzzle_fixed_layout(img_path, inner_gap=8, outer_gap=8, strip=5):
    """
    逻辑:
    • 固定 [1,2,3,4] 位置,只尝试每块的旋转角度。
    • 先拼接 (1,2),计算水平边缘匹配;
    • 拼接 (3,4),计算水平边缘匹配;
    • 拼接 (上半, 下半),计算垂直边缘匹配;
    • 得分 = 上下+左右总相关系数。
      """
  3. 验证token
  4. 处理细节,多优化, 获取结果

总结:
大多数不难,但是部分处理起来费时间。

免费评分

参与人数 3威望 +1 吾爱币 +22 热心值 +3 收起 理由
涛之雨 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Akihi6 + 1 + 1 用心讨论,共获提升!
buluo533 + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

我真不是大饼 发表于 2025-11-7 20:55
大佬牛逼
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-5-15 00:34

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表