找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1757

积分

0

好友

257

主题
发表于 3 天前 | 查看: 10| 回复: 0

题目背景

本文是对强网杯2025题目“PolyEncryption”的详细分析与逆向过程。题目核心为一个由C#、Java、Python混合编写的自定义加密算法。本文将详细阐述逆向分析思路,并提供最终的纯Python解密脚本。

分析思路

整体解题思路分为四个阶段:

阶段一:各层代码解密

首先对题目提供的混淆和加密后的各层源码(C#、Java、Python)进行初步解密。此阶段主要通过逆向核心的polyencrypt.dll并分析其解密逻辑来完成,得到了可读的源码。

阶段二:AI辅助分析

使用Cursor等AI编程助手,根据解密得到的源码文件(saw.cssaw.javasaw.py),自动为代码生成详细注释。随后,引导AI基于注释推理出在不同轮次(rr=0,1,2,3)下的数据流,从而理解算法的整体结构。

阶段三:调试与细节补全

初步根据数据流编写解密脚本后,发现解密失败。为解决细节问题,采取了以下调试手段:

  1. polyencrypt.dll中的关键函数进行Patch。
  2. 修改saw.python脚本,增加详细的日志输出。
  3. 修改Hack.java,记录关键方法的调用信息。
  4. 构建包含调试信息的Docker环境进行动态分析。

通过对比调试日志与算法逻辑,最终锁定了所有实现细节。

阶段四:最终算法实现与还原

根据调试结果,完整还原出名为“PolyEnc”的加密算法,并编写出对应的解密脚本。该算法是一种类似AES结构的分组密码,涉及多轮迭代的S盒替换、列混合、行移位和轮密钥加操作。

加密算法Python实现

以下是该算法的完整纯Python实现,包含加密与解密功能:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
PolyEnc 纯Python实现 - 清晰版本
不使用中间变量,直接实现加密算法
"""
import struct

# ============================================================================
# S-box (来自 saw.py)
# ============================================================================
SBOX = (
    0x9b, 0xac, 0x16, 0x92, 0x5d, 0x9c, 0x1f, 0xed, 0xf8, 0x52, 0x18, 0xc4, 0xd9, 0x59, 0xa0, 0x82,
    0x3c, 0x88, 0x69, 0x4a, 0x5a, 0xf6, 0x34, 0xc1, 0xba, 0x27, 0xec, 0x23, 0x10, 0x51, 0x1, 0xe5,
    ... # 此处为完整的256字节S盒,实际代码中已省略
)
# 构建逆S-box
INV_SBOX = [0] * 256
for i in range(256):
    INV_SBOX[SBOX[i]] = i
INV_SBOX = tuple(INV_SBOX)

# ============================================================================
# AES 标准函数
# ============================================================================
def xtime(a):
    """AES xtime: GF(2^8) 乘 2"""
    return (((a << 1) ^ 0x1B) & 0xFF) if (a & 0x80) else (a << 1)

def mix_column(col):
    """
    AES MixColumns 标准实现
    输入: [a0, a1, a2, a3] (4字节)
    输出: [b0, b1, b2, b3]
    """
    a0, a1, a2, a3 = col
    xor_all = a0 ^ a1 ^ a2 ^ a3
    b0 = a0 ^ xor_all ^ xtime(a0 ^ a1)
    b1 = a1 ^ xor_all ^ xtime(a1 ^ a2)
    b2 = a2 ^ xor_all ^ xtime(a2 ^ a3)
    b3 = a3 ^ xor_all ^ xtime(a3 ^ a0)
    return [b0, b1, b2, b3]

def apply_sbox_to_u32(value):
    """对32位整数应用S-box (小端序)"""
    bytes_le = struct.pack("<I", value)
    sboxed = bytes([SBOX[b] for b in bytes_le])
    return struct.unpack("<I", sboxed)[0]

def rol(value, shift):
    """32位循环左移"""
    value &= 0xFFFFFFFF
    return ((value << shift) | (value >> (32 - shift))) & 0xFFFFFFFF

def gf_mult(a, b):
    """GF(2^8) 乘法"""
    p = 0
    for _ in range(8):
        if b & 1:
            p ^= a
        hi_bit_set = a & 0x80
        a = (a << 1) & 0xFF
        if hi_bit_set:
            a ^= 0x1B
        b >>= 1
    return p

def inv_mix_column(col):
    """
    AES 逆 MixColumns 实现
    输入: [b0, b1, b2, b3] (4字节)
    输出: [a0, a1, a2, a3]
    使用矩阵: [0x0E, 0x0B, 0x0D, 0x09]
    """
    b0, b1, b2, b3 = col
    a0 = gf_mult(0x0E, b0) ^ gf_mult(0x0B, b1) ^ gf_mult(0x0D, b2) ^ gf_mult(0x09, b3)
    a1 = gf_mult(0x09, b0) ^ gf_mult(0x0E, b1) ^ gf_mult(0x0B, b2) ^ gf_mult(0x0D, b3)
    a2 = gf_mult(0x0D, b0) ^ gf_mult(0x09, b1) ^ gf_mult(0x0E, b2) ^ gf_mult(0x0B, b3)
    a3 = gf_mult(0x0B, b0) ^ gf_mult(0x0D, b1) ^ gf_mult(0x09, b2) ^ gf_mult(0x0E, b3)
    return [a0, a1, a2, a3]

def apply_inv_sbox_to_u32(value):
    """对32位整数应用逆S-box (小端序)"""
    bytes_le = struct.pack("<I", value)
    inv_sboxed = bytes([INV_SBOX[b] for b in bytes_le])
    return struct.unpack("<I", inv_sboxed)[0]

# ============================================================================
# PolyEnc 加密器
# ============================================================================
class PolyEnc:
    def __init__(self):
        """初始化加密器"""
        # 轮密钥 (10个,对应 Round 2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
        self.round_keys = [3207972492, 1190065579, 4165979424, 2693353696, 3628337899,
                          1707638109, 1003779598, 2653425729, 795752593, 2469382657,
                          0]
        # 初始密钥状态
        self.initial_key = [2427014626,  # s[1] - 初始魔数
                            1166827919,  # s[2] - 初始魔数
                            1240047111,  # s[3] - 初始魔数
                            1038097261   # s[4] - 初始魔数
                            ]

    def encrypt(self, plaintext_bytes, verbose=False):
        """
        加密16字节明文
        算法流程:
        - Round 0: 初始化
        - Round 1-21: 奇数轮(SubBytes+MixColumns) + 偶数轮(AddRoundKey)
        - Round 22: 输出
        """
        if len(plaintext_bytes) != 16:
            raise ValueError("Input must be 16 bytes")
        # 将明文转为4个32位整数 (大端序)
        state = list(struct.unpack('>4I', plaintext_bytes))
        # 复制初始密钥
        key = self.initial_key.copy()
        # 轮密钥索引
        key_index = 0
        if verbose:
            print(f"【初始状态】")
            print(f"  state = {[hex(s) for s in state]}")
            print(f"  key   = {[hex(k) for k in key]}")
        # Round 1-21: 主加密循环
        # Round 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21 (奇数轮): SubBytes + MixColumns
        # Round 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 (偶数轮): ShiftRows + AddRoundKey
        for rr in range(1, 23):
            if rr % 2 == 1:  # 奇数轮: SubBytes + MixColumns
                state = self._odd_round(state, key, rr, verbose)
            else:  # 偶数轮: ShiftRows + AddRoundKey
                state, key, key_index = self._even_round(state, key, key_index, rr, verbose)
        final_state = state
        # 返回密文 (大端序)
        return struct.pack('>4I', *final_state)

    def _odd_round(self, state, key, rr, verbose):
        """
        奇数轮: SubBytes + MixColumns
        返回: 16 字节数组 (4x4 矩阵,按行存储)
        同时更新密钥状态 s[2], s[3], s[4]:
        - s[2] = B ^ A'
        - s[3] = C ^ B ^ A'
        - s[4] = D ^ C ^ B ^ A'
        """
        if verbose:
            print(f"\n【Round {rr}】奇数轮 - SubBytes + MixColumns")
            print(f"  输入 state = {[hex(s) for s in state]}")
            print(f"  输入 key   = {[hex(k) for k in key]}")
        # 1. 密钥更新 (奇数轮)
        if rr == 1:
            # Round 1: 密钥保持初始值,不变
            pass
        else:
            # Round 3, 5, 7, ..., 21: 更新 s[2], s[3], s[4]
            A_prime = key[0]  # 上一个偶数轮更新后的 s[1]
            B = key[1]
            C = key[2]
            D = key[3]
            # 密钥级联更新
            key[1] = (B ^ A_prime) & 0xFFFFFFFF   # s[2] = B ^ A'
            key[2] = (C ^ B ^ A_prime) & 0xFFFFFFFF     # s[3] = C ^ B ^ A'
            key[3] = (D ^ C ^ B ^ A_prime) & 0xFFFFFFFF  # s[4] = D ^ C ^ B ^ A'
            if verbose:
                print(f"  密钥更新:")
                print(f"    A' = {hex(A_prime)}")
                print(f"    s[2] = B ^ A' = {hex(key[1])}")
                print(f"    s[3] = C ^ B ^ A' = {hex(key[2])}")
                print(f"    s[4] = D ^ C ^ B ^ A' = {hex(key[3])}")
        # 2. SubBytes: 对 state 的每个32位整数应用S-box
        sboxed = [apply_sbox_to_u32(s) for s in state]
        if verbose:
            print(f"  SubBytes = {[hex(s) for s in sboxed]}")
        # 3. MixColumns: 对每个 32位整数拆分成 4字节,MixColumns 后得到 16字节
        # 结果按倒序排列(从 Docker 日志观察到)
        mixed_bytes = []
        for val in sboxed:  # 倒序处理
            # 拆分成4字节 (大端序)
            bytes_be = struct.unpack('4B', struct.pack('>I', val))
            # MixColumns
            mixed_col = mix_column(list(bytes_be))
            mixed_bytes.extend(mixed_col)
        if verbose:
            print(f"  MixColumns 16字节: {[hex(b) for b in mixed_bytes]}")
            # 显示为 4x4 矩阵
            print(f"  MixColumns 4x4矩阵 (按列):")
            for row in range(4):
                print(f"    row{row}: {[hex(mixed_bytes[col*4 + row]) for col in range(4)]}")
        return mixed_bytes

    def _even_round(self, state_bytes, key, key_index, rr, verbose):
        """
        偶数轮: ShiftRows + 字节打包 + AddRoundKey
        输入: 16 字节数组 (从奇数轮的 MixColumns 输出)
        输出: 4 个 32位整数
        1. ShiftRows: 对 4x4 矩阵按对角线重排
        2. 字节打包: 每 4 字节打包成一个 32位整数
        3. AddRoundKey: state ^= key
        4. 密钥更新: s[1] = A ^ sbox(rol(D, 8)) ^ round_key[i]
        """
        if verbose:
            print(f"\n【Round {rr}】偶数轮 - ShiftRows + AddRoundKey")
            print(f"  输入 16字节: {[hex(b) for b in state_bytes]}")
            print(f"  输入 key   = {[hex(k) for k in key]}")
        # 1. ShiftRows: 标准 AES ShiftRows(按行存储)
        # 输入: state_bytes 按列存储 [col0_row0-3, col1_row0-3, col2_row0-3, col3_row0-3]
        # 需要先转换为按行存储,然后 ShiftRows,最后按列打包
        # 标准 ShiftRows 索引(按行存储):
        # col0: 0, 5, 10, 15
        # col1: 1, 6, 11, 12
        # col2: 2, 7, 8, 13
        # col3: 3, 4, 9, 14
        # 将按列存储转换为按行存储
        # state_bytes[0..3] = col0, state_bytes[4..7] = col1, ...
        # 转换为 row0 = [0,4,8,12], row1 = [1,5,9,13], ...
        row_based = []
        for row in range(4):
            for col in range(4):
                row_based.append(state_bytes[col * 4 + row])
        # ShiftRows 索引(倒序,因为 state 要倒序)
        shiftrows_indices = [
            [3, 4, 9, 14],   # col3 (倒序第0个)
            [2, 7, 8, 13],   # col2 (倒序第1个)
            [1, 6, 11, 12],  # col1 (倒序第2个)
            [0, 5, 10, 15]   # col0 (倒序第3个)
        ]
        shifted_state = []
        for indices in shiftrows_indices:
            # 从 row_based 按索引取4个字节,打包成32位整数
            val = (row_based[indices[0]] << 24) | (row_based[indices[1]] << 16) | \
                  (row_based[indices[2]] << 8) | row_based[indices[3]]
            shifted_state.append(val)
        if verbose:
            print(f"  ShiftRows + 打包:")
            for col in range(4):
                idx0 = 0*4 + col
                idx1 = 1*4 + ((col + 1) % 4)
                idx2 = 2*4 + ((col + 2) % 4)
                idx3 = 3*4 + ((col + 3) % 4)
                print(f"    state[{col}] = [{hex(state_bytes[idx0])}, {hex(state_bytes[idx1])}, "
                      f"{hex(state_bytes[idx2])}, {hex(state_bytes[idx3])}] = {hex(shifted_state[col])}")
        # 2. AddRoundKey: state ^= key
        shifted_state = shifted_state[::-1]
        new_state = [(shifted_state[i] ^ key[i]) & 0xFFFFFFFF for i in range(4)]
        if verbose:
            print(f"  AddRoundKey (state ^= key):")
            for i in range(4):
                print(f"    state[{i}] = {hex(shifted_state[i])} ^ {hex(key[i])} = {hex(new_state[i])}")
        # 3. 密钥更新: s[1] = A ^ sbox(rol(D, 8)) ^ round_key[i]
        A = key[0]
        D = key[3]
        # rol(D, 8) - 循环左移8位
        rotated = rol(D, 8)
        # sbox(rotated)
        sboxed = apply_sbox_to_u32(rotated)
        # A' = A ^ sboxed ^ round_key[i]
        round_key = self.round_keys[key_index]
        A_prime = (A ^ sboxed ^ round_key) & 0xFFFFFFFF
        key[0] = A_prime
        key_index += 1
        if verbose:
            print(f"  密钥更新:")
            print(f"    rol(s[4], 8) = {hex(rotated)}")
            print(f"    sbox(rotated) = {hex(sboxed)}")
            print(f"    round_key[{key_index-1}] = {hex(round_key)}")
            print(f"    s[1] = {hex(A)} ^ {hex(sboxed)} ^ {hex(round_key)} = {hex(A_prime)}")
        return new_state, key, key_index

    def decrypt(self, ciphertext_bytes, verbose=False):
        """
        解密16字节密文
        算法流程:
        1. 先正向生成所有轮的密钥状态
        2. 从 Round 22 逆向到 Round 1
        """
        if len(ciphertext_bytes) != 16:
            raise ValueError("Input must be 16 bytes")
        # 1. 正向生成所有轮密钥状态
        if verbose:
            print(f"\n【生成轮密钥】")
        key_states = self._generate_all_round_keys()
        if verbose:
            print(f"生成了 {len(key_states)} 组轮密钥")
            for rr in sorted(key_states.keys())[:5]:  # 只显示前5个
                print(f"  Round {rr}: {[hex(k) for k in key_states[rr]]}")
            print(f"  ...")
        # 2. 将密文转为4个32位整数 (大端序)
        state = list(struct.unpack('>4I', ciphertext_bytes))
        if verbose:
            print(f"\n【解密开始】")
            print(f"  密文 (bytes): {ciphertext_bytes.hex().upper()}")
            print(f"  密文 (u32): {[hex(s) for s in state]}")
        # 3. 逆向执行 Round 22 到 Round 1
        for rr in range(22, 0, -1):
            if rr % 2 == 0:  # 偶数轮: 逆AddRoundKey + 逆ShiftRows
                state = self._inv_even_round(state, key_states[rr], rr, verbose)
            else:  # 奇数轮: 逆MixColumns + 逆SubBytes
                state = self._inv_odd_round(state, key_states[rr], rr, verbose)
        if verbose:
            print(f"\n【解密结束】")
            print(f"  明文 (u32): {[hex(s) for s in state]}")
        # 4. 返回明文 (大端序)
        plaintext = struct.pack('>4I', *state)
        if verbose:
            print(f"  明文 (bytes): {plaintext.hex().upper()}")
        return plaintext

    def _generate_all_round_keys(self):
        """
        正向生成所有轮的密钥状态
        返回: 字典 {round_number: key_state}
        """
        key = self.initial_key.copy()
        key_index = 0
        key_states = {0: key.copy()}
        # 模拟加密过程中的密钥更新
        for rr in range(1, 23):
            if rr % 2 == 1:  # 奇数轮
                if rr > 1:
                    # Round 3, 5, 7, ..., 21: 更新 s[2], s[3], s[4]
                    A_prime = key[0]
                    B = key[1]
                    C = key[2]
                    D = key[3]
                    key[1] = (B ^ A_prime) & 0xFFFFFFFF
                    key[2] = (C ^ B ^ A_prime) & 0xFFFFFFFF
                    key[3] = (D ^ C ^ B ^ A_prime) & 0xFFFFFFFF
                key_states[rr] = key.copy()
            else:  # 偶数轮
                key_states[rr] = key.copy()
                # 更新 s[1]
                A = key[0]
                D = key[3]
                rotated = rol(D, 8)
                sboxed = apply_sbox_to_u32(rotated)
                round_key = self.round_keys[key_index]
                A_prime = (A ^ sboxed ^ round_key) & 0xFFFFFFFF
                key[0] = A_prime
                key_index += 1
        return key_states

    def _inv_odd_round(self, state_bytes, key, rr, verbose):
        """
        逆奇数轮: 逆MixColumns + 逆SubBytes
        输入: 16 字节数组
        输出: 4 个 32位整数
        """
        if verbose:
            print(f"\n【Round {rr} 逆向】奇数轮 - InvMixColumns + InvSubBytes")
            print(f"  输入 state (16字节): {[hex(b) for b in state_bytes]}")
            print(f"  输入 key   = {[hex(k) for k in key]}")
            # 显示为 4x4 矩阵
            print(f"  输入 4x4矩阵 (按列):")
            for row in range(4):
                print(f"    row{row}: {[hex(state_bytes[col*4 + row]) for col in range(4)]}")
        # 1. 逆MixColumns: 对每个32位整数拆分成4字节,逆MixColumns后得到16字节
        inv_mixed_bytes = []
        for i in range(4):
            # 提取4个字节(按列存储)
            col_bytes = state_bytes[i*4:(i+1)*4]
            # 逆MixColumns
            inv_mixed_col = inv_mix_column(list(col_bytes))
            inv_mixed_bytes.extend(inv_mixed_col)
        if verbose:
            print(f"  InvMixColumns 16字节: {[hex(b) for b in inv_mixed_bytes]}")
            # 显示为 4x4 矩阵
            print(f"  InvMixColumns 4x4矩阵 (按列):")
            for row in range(4):
                print(f"    row{row}: {[hex(inv_mixed_bytes[col*4 + row]) for col in range(4)]}")
        # 2. 重新打包成4个32位整数(大端序)
        sboxed = []
        for i in range(4):
            bytes_4 = inv_mixed_bytes[i*4:(i+1)*4]
            val = struct.unpack('>I', bytes(bytes_4))[0]
            sboxed.append(val)
        if verbose:
            print(f"  打包成u32 (应用InvSubBytes之前): {[hex(s) for s in sboxed]}")
        # 3. 逆SubBytes: 对每个32位整数应用逆S-box
        state = [apply_inv_sbox_to_u32(s) for s in sboxed]
        if verbose:
            print(f"  InvSubBytes (输出state) = {[hex(s) for s in state]}")
        return state

    def _inv_even_round(self, state, key, rr, verbose):
        """
        逆偶数轮: 逆AddRoundKey + 逆ShiftRows
        输入: 4 个 32位整数
        输出: 16 字节数组
        """
        if verbose:
            print(f"\n【Round {rr} 逆向】偶数轮 - InvAddRoundKey + InvShiftRows")
            print(f"  输入 state = {[hex(s) for s in state]}")
            print(f"  使用 key   = {[hex(k) for k in key]}")
        # 1. 逆AddRoundKey: state ^= key (XOR是自逆的)
        unkeyed_state = [(state[i] ^ key[i]) & 0xFFFFFFFF for i in range(4)]
        if verbose:
            print(f"  InvAddRoundKey (state ^= key):")
            for i in range(4):
                print(f"    state[{i}] = {hex(state[i])} ^ {hex(key[i])} = {hex(unkeyed_state[i])}")
        # 将4个32位整数拆分成16字节
        # 按加密时的打包方式逆向
        inv_shiftrows_indices = [
            [0, 5, 10, 15],   # col0 (对应倒序第3个)
            [1, 6, 11, 12],   # col1 (对应倒序第2个)
            [2, 7, 8, 13],    # col2 (对应倒序第1个)
            [3, 4, 9, 14]     # col3 (对应倒序第0个)
        ]
        # 先将4个u32解包成按行存储的16字节
        row_based = []
        for val in unkeyed_state:
            row_based.append((val >> 24) & 0xFF)
            row_based.append((val >> 16) & 0xFF)
            row_based.append((val >> 8) & 0xFF)
            row_based.append(val & 0xFF)
        if verbose:
            print(f"  解包成16字节(按行): {[hex(b) for b in row_based]}")
        # 创建逆映射
        unshifted_row_based = [0] * 16
        for col_idx, indices in enumerate(inv_shiftrows_indices):
            for byte_idx, src_idx in enumerate(indices):
                unshifted_row_based[src_idx] = row_based[col_idx * 4 + byte_idx]
        if verbose:
            print(f"  InvShiftRows后(按行): {[hex(b) for b in unshifted_row_based]}")
        # 转换回按列存储
        state_bytes = []
        for col in range(4):
            for row in range(4):
                state_bytes.append(unshifted_row_based[row * 4 + col])
        if verbose:
            print(f"  输出 16字节(按列): {[hex(b) for b in state_bytes]}")
            # 显示为 4x4 矩阵
            print(f"  输出 4x4矩阵 (按列):")
            for row in range(4):
                print(f"    row{row}: {[hex(state_bytes[col*4 + row]) for col in range(4)]}")
        return state_bytes
# ============================================================================
# 主程序
# ============================================================================
if __name__ == "__main__":
    # ... (此处为测试函数和主逻辑调用,代码已省略,与原脚本一致)
    pass

运行结果

使用上述脚本解密题目提供的flag.enc文件,成功得到最终flag。

脚本运行结果展示

总结

本题的难点在于对混合语言编写的、经过混淆的加密算法进行逆向工程。解题的关键在于结合静态分析、动态调试(Docker环境)以及AI辅助代码理解,逐步厘清复杂的多轮加密数据流和密钥更新逻辑。最终,通过Python完整复现算法并成功解密。这种多层协作的逆向工程思路,对于分析复杂的现代加密挑战具有借鉴意义。

完整的题目附件以及所有阶段的分析脚本,可在原发布页面查看。




上一篇:Linux内核首现Rust内存安全漏洞CVE-2025-68260:竞态条件导致系统崩溃
下一篇:SpringBoot SPI机制实战:构建可插拔的插件化架构
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-24 19:21 , Processed in 0.235583 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

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