bugku的奇怪的二维码

题目链接:https://ctf.bugku.com/challenges/detail/id/2971.html

初步使用binwalk查看,无明显的文件隐藏。

加密方式:在其中的5*5的局部黑白格子中,藏有信息。目测两个黑为摩斯密码的长,一个黑为摩斯密码的短。1个白色是摩斯密码的间隔,2个白色是字母的分割。题目提示3个字母,

呃呃呃,本来暴力应该也可以过的,也就17576种可能。

这里按照常规方式解题吧。

编写python,大部分代码是AI写的,我提供思路给AI的。如果觉得某些写法有点反人类,属于正常的。

思路如下:

1.解析二维码的行和列数。并转为2维数组

2.根据黑白的二维数组,进行遍历所有的5*5的局部信息,每个信息包含25个位信息。

3.根据25个位的信息,进行摩斯密码解密。输出内容包含局部区域的左上角位置,解析出来的字母,25个位信息。

代码如下:

python 复制代码
from PIL import Image
import numpy as np

# 读取图像
image_path = r"file.jpg"
img = Image.open(image_path).convert('L')  # 转换为灰度图
data = np.array(img)

# 获取图像尺寸
height, width = data.shape

# 将二维码转换为二值化二维数组
def convert_to_binary_array(gray_data, threshold=128):
    """
    将灰度图像数据转换为二值化二维数组
    1 表示黑色像素,0 表示白色像素
    """
    binary_array = (gray_data < threshold).astype(int)
    return binary_array

# 使用更精确的算法计算二维码模块数量
def calculate_qr_module_count(binary_array):
    """
    使用边缘检测和网格模式识别计算二维码的模块数量
    """
    # 检测图像中的黑白变化,找出模块边界
    row_changes = []
    for row in range(height):
        changes = 0
        for col in range(1, width):
            if binary_array[row, col] != binary_array[row, col-1]:
                changes += 1
        row_changes.append(changes)
    
    # 找到变化次数最多的行,这通常对应二维码的有效区域
    max_changes = max(row_changes)
    
    # 二维码模块数量通常是变化次数的一半(因为每次黑白变化算两次)
    # 加上3是因为二维码边缘有空白区域
    module_count = (max_changes // 2) + 3
    
    # 确保模块数量是合理的
    if module_count < 21 or module_count > 100:
        # 使用备用方法:通过寻找模式重复来确定模块大小
        # 在实际二维码中,模块大小通常是一致的
        module_sizes = []
        
        # 检查前10行中连续相同颜色的像素长度
        for row in range(min(10, height)):
            current_color = binary_array[row, 0]
            current_length = 1
            for col in range(1, width):
                if binary_array[row, col] == current_color:
                    current_length += 1
                else:
                    if current_length > 1:
                        module_sizes.append(current_length)
                    current_color = binary_array[row, col]
                    current_length = 1
        
        if module_sizes:
            # 找到最常见的长度作为模块大小
            from collections import Counter
            most_common_size = Counter(module_sizes).most_common(1)[0][0]
            module_count = max(width, height) // most_common_size
    
    return module_count

# 生成二维码的25*25二维数组(使用拟合算法)
def generate_qr_array(binary_array, module_count):
    """
    使用拟合算法生成module_count x module_count大小的二维码黑白情况二维数组
    1表示黑色,0表示白色
    使用占比超过90%的判断标准
    """
    # 计算精确的模块大小(考虑到像素可能不是完美整除)
    module_size_height = height / module_count
    module_size_width = width / module_count
    
    module_array = []
    
    # 遍历每个模块
    for module_row in range(module_count):
        current_row = []
        for module_col in range(module_count):
            # 计算这个模块在图像中的精确位置
            start_row = int(module_row * module_size_height)
            end_row = int((module_row + 1) * module_size_height)
            start_col = int(module_col * module_size_width)
            end_col = int((module_col + 1) * module_size_width)
            
            # 确保不会越界
            end_row = min(end_row, height)
            end_col = min(end_col, width)
            
            # 获取这个模块区域内的所有像素
            block = binary_array[start_row:end_row, start_col:end_col]
            
            # 计算黑色像素(值为1)的数量
            total_pixels = block.size
            black_pixels = np.sum(block)
            white_pixels = total_pixels - black_pixels
            
            # 计算占比
            black_ratio = black_pixels / total_pixels if total_pixels > 0 else 0
            white_ratio = white_pixels / total_pixels if total_pixels > 0 else 0
            
            # 判断颜色:占比超过90%才确定为该颜色
            if black_ratio >= 0.9:
                current_row.append(1)  # 黑色
            elif white_ratio >= 0.9:
                current_row.append(0)  # 白色
            else:
                # 如果两种颜色占比都不超过90%,则使用平均值判断
                # 这里可以根据实际情况调整策略
                if black_ratio > white_ratio:
                    current_row.append(1)
                else:
                    current_row.append(0)
        
        module_array.append(current_row)
    
    return module_array

# 遍历并解析所有5*5区域的函数
def parse_5x5_regions(qr_array):
    """
    遍历二维码25*25中的所有5*5区域
    按行展开为25长度信息
    """
    # 存储所有5*5区域的展开结果
    region_results = {}
    
    # 遍历所有可能的5*5区域
    for start_row in range(len(qr_array) - 4):
        for start_col in range(len(qr_array[0]) - 4):
            # 提取5*5区域
            region = []
            for i in range(5):
                row = qr_array[start_row + i][start_col:start_col + 5]
                region.append(row)
            
            # 按行展开为25长度的一维数组
            flattened = [cell for row in region for cell in row]
            
            # 存储结果
            region_results[(start_row, start_col)] = {
                'region': region,
                'flattened': flattened,
                'flattened_str': ''.join(map(str, flattened))
            }
    
    # 特别处理左上角定位符右下角的5*5区域(定位符通常从(0,0)开始,大小为7*7)
    # 所以右下角5*5区域应该是从(2,2)开始
    top_left_corner_region = []
    for i in range(2, 7):
        row = qr_array[i][2:7]
        top_left_corner_region.append(row)
    
    # 按行展开为25长度的一维数组
    corner_flattened = [cell for row in top_left_corner_region for cell in row]
    
    return {
        'all_regions': region_results,
        'top_left_corner_region': {
            'region': top_left_corner_region,
            'flattened': corner_flattened,
            'flattened_str': ''.join(map(str, corner_flattened))
        }
    }


# 解析5*5区域的摩斯密码
def parse_5x5_morse(flattened_array):
    """
    解析5x5区域的摩斯码
    功能:
    1. 将输入的二进制字符串转换为摩斯电码
    2. 按连续1的数量解析:1个1='.', 2个1='-',≥3个1视为无效
    3. 使用00作为分隔符分割不同的摩斯符号
    4. 将解析出的摩斯码转换为对应的字母
    """
    # 确保输入是字符串类型
    if not isinstance(flattened_array, str):
        flattened_array = ''.join(map(str, flattened_array))
    
    # 完整的摩斯电码字典
    morse_to_char = {
        '.-': 'A', '-...': 'B', '-.-.': 'C', '-..': 'D', '.': 'E',
        '..-.': 'F', '--.': 'G', '....': 'H', '..': 'I', '.---': 'J',
        '-.-': 'K', '.-..': 'L', '--': 'M', '-.': 'N', '---': 'O',
        '.--.': 'P', '--.-': 'Q', '.-.': 'R', '...': 'S', '-': 'T',
        '..-': 'U', '...-': 'V', '.--': 'W', '-..-': 'X', '-.--': 'Y',
        '--..': 'Z'
    }
    
    # 使用00作为分隔符分割字符串
    segments = flattened_array.split('00')
    
    # 存储解析结果
    result = []

    # 解析每个段
    for segment in segments:        
        if len(segment) == 0:
            break

        # 解析连续的1
        morse_code = ''
        i = 0
        while i < len(segment):
            # 处理连续的1
            if segment[i] == '1':
                start = i
                while i < len(segment) and segment[i] == '1':
                    i += 1
                count = i - start
                
                # 根据连续1的数量确定摩斯符号
                if count == 1:
                    morse_code += '.'
                elif count == 2:
                    morse_code += '-'
                else:  # 连续3个或以上的1,视为无效
                    morse_code = ''
                    break
            # 处理单个0(不影响解析)
            elif segment[i] == '0':
                start = i
                while i < len(segment) and segment[i] == '0':
                    i += 1
                count = i - start
                if count >= 2:
                    morse_code = ''
                    break
                if len(morse_code) == 0:
                    morse_code = ''
                    break
        
        # 如果解析出有效的摩斯码,则查找对应的字母
        if morse_code in morse_to_char:
            result.append(morse_to_char[morse_code])
        else:
            break
    
    return result

# 主函数
def main():
    # 声明使用全局变量
    global data, height, width
    
    # 输出像素的行列数量
    print(f"像素:{height}*{width}")
    
    # 将图像转换为二值化二维数组
    binary_array = convert_to_binary_array(data)
    
    # 使用改进的算法计算模块数量
    module_count = calculate_qr_module_count(binary_array)
    
    # 输出二维码的行列数量
    print(f"二维码:{module_count}*{module_count}")
    
    # 生成25*25的二维码黑白情况二维数组
    qr_array = generate_qr_array(binary_array, module_count)
    
    # 输出25*25的二维码黑白信息
    print("\n二维码25*25黑白信息:")
    for row in qr_array:
        # 将当前行转换为字符串(1表示黑块,0表示白块)
        row_str = ''.join(map(str, row))
        print(row_str)
    
    # 解析所有5*5区域
    print("\n开始解析5*5区域...")
    print()
    region_results = parse_5x5_regions(qr_array)
    
    # 处理所有5*5区域
    for pos, region in region_results['all_regions'].items():
        # 使用新的摩斯密码解析函数解析该区域
        region_words = parse_5x5_morse(region['flattened'])
        
        # 获取25个二维码的黑白信息
        info_str = region['flattened_str']
        
        # 输出所有字母结果,不再限制长度
        if len(region_words) == 3:
            print(f"区域: {pos} 字母:{region_words} 信息:{info_str}")
    
    # 特殊处理左上角定位符右下角5*5区域(确保也被处理)
    corner_region = region_results['top_left_corner_region']
    corner_words = parse_5x5_morse(corner_region['flattened'])
    
    # 获取角落区域的25个二维码的黑白信息
    corner_info_str = corner_region['flattened_str']
    
    for word in corner_words:
        if word.isalpha():
            print(f"区域: (2,2) 字母:{word} 信息:{corner_info_str}")



if __name__ == "__main__":
    main()

运行结果如下:

python 复制代码
像素:500*499
二维码:25*25

二维码25*25黑白信息:
1111111011110011101111111
1000001000011101101000001
1011101001010100001011101
1011101000110010101011101
1011101000111111001011101
1000001001010111001000001
1111111101001010101111111
0000001001001010000000000
1101101100101110101000001
0111101010100011000101111
1000100000001010101111010
0000010100000010101111001
1100001111100110010111000
1011110000101000011101000
1101101100100010010101000
1011000010101111001000010
1010011101011000111111101
0000000011010001100010110
1111111001001110101010100
1000001000010100100010100
1011101011010100111111101
1011101011101100100100011
1011101000010101110001011
1000001011111100010001011
1111111010011100011011001

开始解析5*5区域...

区域: (2, 4) 字母:['I', 'I', 'I'] 信息:1010010100101000010011110
区域: (3, 16) 字母:['S', 'I', 'E'] 信息:1010100101001001011100000
区域: (5, 15) 字母:['E', 'E', 'A'] 信息:1001001011000000101010001
区域: (6, 6) 字母:['D', 'A', 'D'] 信息:1101010010110011010100000
区域: (7, 6) 字母:['E', 'A', 'D'] 信息:1001011001101010000001000
区域: (7, 9) 字母:['E', 'E', 'R'] 信息:1001001011010000001000000
区域: (8, 7) 字母:['E', 'E', 'I'] 信息:1001001010000001000011110
区域: (10, 12) 字母:['S', 'U', 'E'] 信息:1010100101011001000000100
区域: (12, 9) 字母:['T', 'S', 'E'] 信息:1100101010010000101110110
区域: (14, 14) 字母:['E', 'A', 'E'] 信息:1001011001001110110010101
区域: (17, 8) 字母:['N', 'E', 'E'] 信息:1101001001000101101011101
区域: (17, 9) 字母:['I', 'E', 'T'] 信息:1010010011001011010111011

可以看到几个3字母,一个个尝试即可。

相关推荐
天才测试猿1 小时前
树控件、下拉框、文本框常用测试用例
自动化测试·软件测试·python·功能测试·测试工具·职场和发展·测试用例
将心ONE1 小时前
pip导出项目依赖
开发语言·python·pip
shx66661 小时前
2.2.1 ROS2 在功能包中编写 Python 节点
开发语言·python·ros2
beijingliushao1 小时前
100-Spark Local模式部署
大数据·python·ajax·spark
BoBoZz191 小时前
TessellatedBoxSource 创建并渲染一个细分的、可移动的箱体模型
python·vtk·图形渲染·图形处理
weixin_457340212 小时前
旋转OBB数据集标注查看器
图像处理·人工智能·python·yolo·目标检测·数据集·旋转
nvd112 小时前
LLM 对话记忆功能实现深度解析
python
电饭叔2 小时前
Luhn算法初介绍
python