首页
社区
课程
招聘
[原创] KCTF2025 - 第四题血色试炼 题解
发表于: 2025-8-21 09:07 3592

[原创] KCTF2025 - 第四题血色试炼 题解

2025-8-21 09:07
3592

换表,小端序,padding特殊处理的 base64 + 修改了密钥派生函数的 AES-128 + 自定义 tweak 流密码异或
手工实现三个修改过的 base64 + AES + tweak 就可以解密,密文套了四层

可能存在速通方法,因为实际的校验是:

所以如果 hook 自定义的加密,然后调用三次应该就能得到期望的 serial,无需苦力逆向。
但笔者在逆玩第一个解密字符串的函数之后才意识到整个程序用的都是这一套算法,输.jpg

搜字符串找到明文 base64 码表和 key:
图片描述
跟进引用发现是一系列初始化函数,被初始化的全局变量再引用一次进到 TLSCallBack 里:
图片描述
这里面还初始化了两组密钥,还有 AES 的标准 S 盒,逆 S 盒,置换表
函数的下方还有导出 ntdll 里 Zw 开头的函数并存在(大概是)map 里的操作,应该是隐藏了某些 API call
图片描述
函数末尾取出加密字符串,解密后迫不及待地call了一次(调试看是 ZwQueryInformationProcess ):
图片描述
这里如果发现被调试就修改异常状态崩掉程序,直接 patch 成 jmp 就可以过掉整个程序唯一一个生效的反调
图片描述

不过其实如果不调后面部分的话,不 patch 也可以,因为反调试是第一次解密后的,我们已经可以调完整的解密过程了

复原解密就是普通的苦力活(不过可以喂给 GPT 写出完全一致的实现)
于是挨个喂了 base64 解码,密钥派生,然后检查了一下 AES 的其他部分都是完全常规的(但 GPT 一起造出来了)
再手动封装一层用于异或的密钥流派生就可以完美还原加密逻辑了 (就是用另一组密钥依次加密 struct.pack("<I", 0x24D67345 + index) * 4 作为无限长的密钥流)

然后跟到 mian 里面注册的中断处理:
图片描述
大概看一下逻辑就是上文说的

然后再喊 GPT 直接根据代码把逆运算都写出来 (这里写 base64 的逆 GPT 总是直接写不对,笔者这里需要再喂加密的 C 伪代码才行。但笔者并没有试过直接用程序里的 base64 解密逻辑让 GPT 写)
除了 base64 别的都能一遍过,以下是完整脚本:

输出:

最近两题都干翻了丐版 MCP 自动逆向工作流,但从人工解题的过程来看,LLM 完全有能力根据伪代码完美复现源码实现,或许是笔者的 prompt 不对,或许应该考虑尝试对于比较复杂的任务要求 LLM 优先挨个分析关键函数(并写出源码实现验证),然后再进行大的逆向。直接要求 LLM 做过大任务的话 LLM 很可能只能理解部分事实,比如认为上一题就是一个 RSA (而不是很多个 RSA 套在一起),或者认为本题是 base64 + XTS-AES (而不是非标准实现)。

目前笔者观察到 GPT 的另一个限制是如果用户直接提供完整任务(给出样例 name + serial,并且告诉 AI 目标是生成 KCTF 的serial), GPT 会急于求出 KCTF 的密钥而省略大量逆向过程。
但如果只告诉 AI 第一个任务是 还原 name - serial 的比较逻辑 的话, AI 就能更好的工作。

这一观察也和今年软工顶会 FSE'25 上的一篇文章相符:
【论文分享】突破误报瓶颈:LLM × 静态分析的新范式——BugLens

所以想要机器人队自动上分或许还需要帮 LLM 把任务分成合理的小目标去做
(笔者依旧乐观地认为如果方法得当,LLM 不仅能自动还原算法实现,也能发现原程序已经有逆运算的可能并且直接 hook call自动生成密钥)

图片描述

import struct
from typing import List
 
sbox = [
    0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
    0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
    0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
    0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
    0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
    0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
    0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
    0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
    0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
    0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
    0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
    0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
    0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
    0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
    0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
    0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
]
 
inv_sbox = [
    0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
    0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
    0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
    0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
    0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
    0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
    0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
    0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
    0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
    0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
    0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
    0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
    0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
    0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
    0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
    0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
]
 
 
def gmul(a: int, b: int) -> int:
    p = 0
    for _ in range(8):
        if b & 1:
            p ^= a
        hi = a & 0x80
        a = (a << 1) & 0xFF
        if hi:
            a ^= 0x1B
        b >>= 1
    return p
 
 
def mix_single_column(col: List[int]) -> List[int]:
    a0, a1, a2, a3 = col
    return [
        gmul(a0, 2) ^ gmul(a1, 3) ^ a2 ^ a3,
        a0 ^ gmul(a1, 2) ^ gmul(a2, 3) ^ a3,
        a0 ^ a1 ^ gmul(a2, 2) ^ gmul(a3, 3),
        gmul(a0, 3) ^ a1 ^ a2 ^ gmul(a3, 2),
    ]
 
 
def inv_mix_single_column(col: List[int]) -> List[int]:
    a0, a1, a2, a3 = col
    return [
        gmul(a0, 14) ^ gmul(a1, 11) ^ gmul(a2, 13) ^ gmul(a3, 9),
        gmul(a0, 9) ^ gmul(a1, 14) ^ gmul(a2, 11) ^ gmul(a3, 13),
        gmul(a0, 13) ^ gmul(a1, 9) ^ gmul(a2, 14) ^ gmul(a3, 11),
        gmul(a0, 11) ^ gmul(a1, 13) ^ gmul(a2, 9) ^ gmul(a3, 14),
    ]
 
 
def load_state_from_bytes(block: bytes) -> List[List[int]]:
    """column-major: s[r][c] = block[4*c + r]"""
    s = [[0] * 4 for _ in range(4)]
    for c in range(4):
        for r in range(4):
            s[r][c] = block[4 * c + r]
    return s
 
 
def state_to_bytes(state: List[List[int]]) -> bytes:
    out = bytearray(16)
    for c in range(4):
        for r in range(4):
            out[4 * c + r] = state[r][c]
    return bytes(out)
 
 
def sub_bytes(state: List[List[int]]) -> None:
    for r in range(4):
        for c in range(4):
            state[r][c] = sbox[state[r][c]]
 
 
def inv_sub_bytes(state: List[List[int]]) -> None:
    for r in range(4):
        for c in range(4):
            state[r][c] = inv_sbox[state[r][c]]
 
 
def shift_rows(state: List[List[int]]) -> None:
    state[1] = state[1][1:] + state[1][:1]
    state[2] = state[2][2:] + state[2][:2]
    state[3] = state[3][3:] + state[3][:3]
 
 
def inv_shift_rows(state: List[List[int]]) -> None:
    state[1] = state[1][-1:] + state[1][:-1]
    state[2] = state[2][-2:] + state[2][:-2]
    state[3] = state[3][-3:] + state[3][:-3]
 
 
def mix_columns(state: List[List[int]]) -> None:
    cols = list(zip(state[0], state[1], state[2], state[3]))
    new_cols = [mix_single_column(list(col)) for col in cols]
    for c in range(4):
        state[0][c], state[1][c], state[2][c], state[3][c] = new_cols[c]
 
 
def inv_mix_columns(state: List[List[int]]) -> None:
    cols = list(zip(state[0], state[1], state[2], state[3]))
    new_cols = [inv_mix_single_column(list(col)) for col in cols]
    for c in range(4):
        state[0][c], state[1][c], state[2][c], state[3][c] = new_cols[c]
 
 
def add_round_key(state: List[List[int]], round_words: List[int]) -> None:
    for c in range(4):
        w = round_words[c]
        state[0][c] ^= (w >> 0) & 0xFF
        state[1][c] ^= (w >> 8) & 0xFF
        state[2][c] ^= (w >> 16) & 0xFF
        state[3][c] ^= (w >> 24) & 0xFF
 
 
def _assemble_initial_words_from_key(key_bytes: bytes) -> List[int]:
    s = load_state_from_bytes(key_bytes)
    words = []
    for c in range(4):
        v = 0
        for r in range(4):
            b = s[r][c]
            v = ((b | v) << 8) & 0xFFFFFFFF
        words.append(v)
    return words
 
 
def expand_key_nonstandard(key_bytes: bytes) -> List[int]:
    assert len(key_bytes) == 16
    W = _assemble_initial_words_from_key(key_bytes)
    rcons = [0x01, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B]
    r_idx = 0
    for i in range(4, 44):
        if i % 4 == 0:
            temp = (rcons[r_idx] << 24) & 0xFFFFFFFF
            r_idx += 1
            W.append(W[i - 4] ^ temp)
        else:
            W.append(W[i - 4] ^ W[i - 1])
    return W
 
 
def encrypt_block_nonstandard(plaintext: bytes, key: bytes) -> bytes:
    assert len(plaintext) == 16 and len(key) == 16
    W = expand_key_nonstandard(key)
    state = load_state_from_bytes(plaintext)
    add_round_key(state, W[0:4])  # round 0
    for r in range(1, 10):  # rounds 1..9
        sub_bytes(state)
        shift_rows(state)
        mix_columns(state)
        add_round_key(state, W[4 * r:4 * r + 4])
    sub_bytes(state)  # final
    shift_rows(state)
    add_round_key(state, W[40:44])
    return state_to_bytes(state)
 
 
def decrypt_block_nonstandard(ciphertext: bytes, key: bytes) -> bytes:
    assert len(ciphertext) == 16 and len(key) == 16
    W = expand_key_nonstandard(key)
    state = load_state_from_bytes(ciphertext)
    add_round_key(state, W[40:44])  # initial (round 10)
    for r in range(9, 0, -1):  # rounds 9..1
        inv_sub_bytes(state)
        inv_shift_rows(state)
        add_round_key(state, W[4 * r:4 * r + 4])
        inv_mix_columns(state)
    inv_sub_bytes(state)  # final
    inv_shift_rows(state)
    add_round_key(state, W[0:4])
    return state_to_bytes(state)
 
 
_B64_ALPHABET = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789+/="
_B64_MAP = {ch: i for i, ch in enumerate(_B64_ALPHABET)}  # 包含 '=': 64
_B64_PAD = _B64_ALPHABET[-1]
_NULL_INDEX = len(_B64_ALPHABET)  # == 65,对应 C 的 strchr(..., '\0')
 
 
def b64_decode_le(s: str) -> bytes:
    s = "".join(ch for ch in s if not ch.isspace())
    out = bytearray()
    i = 0
    n = len(s)
 
    while i < n:
        quad = s[i:i + 4]
        i += 4
 
        v = 0
        pad = 0
        for j in range(3, -1, -1):
            if j < len(quad):
                ch = quad[j]
                if ch == _B64_PAD:
                    pad += 1
                    continue
                idx = _B64_MAP.get(ch)
                if idx is None:
                    raise ValueError(f"Invalid base64 char: {ch!r}")
            else:
                idx = _NULL_INDEX
            v = ((v << 6) + idx) & 0xFFFFFFFF
 
        if pad == 0:
            out.append(v & 0xFF)
            out.append((v >> 8) & 0xFF)
            out.append((v >> 16) & 0xFF)
        elif pad == 1:
            out.append(v & 0xFF)
            out.append((((v & 0x0FFF) + ((v >> 2) & 0xF000)) >> 8) & 0xFF)
        elif pad == 2:
            out.append(((v & 0x3F) + ((v >> 4) & 0xC0)) & 0xFF)
        else:
            pass
 
    return bytes(out)
 
 
def b64_encode_le(data: bytes) -> str:
    enc = _B64_ALPHABET[:-1# 你的特殊字母表(不含 '=')
    PAD = _B64_PAD
    out = []
    i = 0
    n = len(data)
    while i < n:
        rem = n - i
        if rem >= 3:
            b0, b1, b2 = data[i], data[i + 1], data[i + 2]
            i += 3
            v = b0 | (b1 << 8) | (b2 << 16)
            out.append(enc[(v >> 0) & 0x3F])
            out.append(enc[(v >> 6) & 0x3F])
            out.append(enc[(v >> 12) & 0x3F])
            out.append(enc[(v >> 18) & 0x3F])
        elif rem == 2:
            b0, b1 = data[i], data[i + 1]
            i += 2
            q0 = b0 & 0x3F
            q1 = ((b1 & 0x0F) << 2) | (b0 >> 6)
            q2 = ((b1 >> 4) & 0x0F) << 2
            out.extend([enc[q0], enc[q1 & 0x3F], enc[q2], PAD])
        else# rem == 1
            b0 = data[i]
            i += 1
            q0 = b0 & 0x3F
            q1 = ((b0 >> 6) & 0x03) << 4
            out.extend([enc[q0], enc[q1], PAD, PAD])
 
    return "".join(out)
 
 
def encrypt_blocks_nonstandard(data: bytes, key: bytes) -> bytes:
    out = bytearray()
    for i in range(0, len(data), 16):
        block = data[i:i + 16]
        if len(block) < 16:
            raise ValueError("Input length must be a multiple of 16")
        out += encrypt_block_nonstandard(block, key)
    return bytes(out)
 
 
def decrypt_blocks_nonstandard(data: bytes, key: bytes) -> bytes:
    out = bytearray()
    for i in range(0, len(data), 16):
        block = data[i:i + 16]
        if len(block) < 16:
            raise ValueError("Input length must be a multiple of 16")
        out += decrypt_block_nonstandard(block, key)
    return bytes(out)
 
 
key = b"ThisEncAndDecKey"
key2 = b"tweakKey_2024521"
 
 
def inverse_main(final_s: str) -> str:
    s = final_s
    for _ in range(4):
        pt = s.encode("utf-8")
        if len(pt) % 16 != 0:
            pt += b"\x00" * (16 - (len(pt) % 16))
        ct_out = bytearray()
        for j in range(0, len(pt), 16):
            pblk = pt[j:j + 16]
            twk = struct.pack("<I", 0x24D67344 + (j // 16) + 1) * 4
            d = encrypt_blocks_nonstandard(twk, key2)
            pt1 = bytes(pb ^ db for pb, db in zip(pblk, d))
            ct = encrypt_blocks_nonstandard(pt1, key)
            ct_out.extend(ct)
        s = b64_encode_le(bytes(ct_out))
    return s
 
 
def main(s):
    for _ in range(4):
        ct_r = b64_decode_le(s)
        pt = []
        for j in range(0, len(ct_r), 16):
            ct = ct_r[j:j + 16]
            pt1 = decrypt_blocks_nonstandard(ct, key)
            pt2 = struct.pack("<I", 0x24D67344 + (j // 16) + 1) * 4
            d = encrypt_blocks_nonstandard(pt2, key2)
            for i in range(16):
                pt.append(pt1[i] ^ d[i])
        pt = bytes(pt)
        while pt[-1] == 0:
            pt = pt[:-1]
        s = pt.decode()
    return s
 
 
if __name__ == "__main__":
    x = "7PqLbsGeTagqDgXbYjcvqd0mJNUkK8yzy0uQYYJZx+mzDahZf8A38LcEZVSd7UcSd/n0bSt0pZCPyecxXZlOBA=="
    print(x)
    x = main(x)
    print(x)
    x = inverse_main(x)
    print(x)
    x = main(x)
    print(x)
 
    print("Server flag is:")
    c = inverse_main("KCTF")
    print(c)
    c = main(c)
    print(c)
import struct
from typing import List
 
sbox = [
    0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
    0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
    0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
    0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
    0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
    0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
    0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
    0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
    0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
    0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
    0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
    0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
    0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
    0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
    0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
    0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
]
 
inv_sbox = [
    0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
    0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
    0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
    0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
    0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
    0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
    0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
    0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
    0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
    0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
    0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
    0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
    0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
    0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
    0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
    0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
]
 
 
def gmul(a: int, b: int) -> int:
    p = 0
    for _ in range(8):
        if b & 1:
            p ^= a
        hi = a & 0x80
        a = (a << 1) & 0xFF
        if hi:
            a ^= 0x1B
        b >>= 1
    return p
 
 
def mix_single_column(col: List[int]) -> List[int]:
    a0, a1, a2, a3 = col
    return [
        gmul(a0, 2) ^ gmul(a1, 3) ^ a2 ^ a3,
        a0 ^ gmul(a1, 2) ^ gmul(a2, 3) ^ a3,
        a0 ^ a1 ^ gmul(a2, 2) ^ gmul(a3, 3),
        gmul(a0, 3) ^ a1 ^ a2 ^ gmul(a3, 2),
    ]
 
 
def inv_mix_single_column(col: List[int]) -> List[int]:
    a0, a1, a2, a3 = col
    return [
        gmul(a0, 14) ^ gmul(a1, 11) ^ gmul(a2, 13) ^ gmul(a3, 9),
        gmul(a0, 9) ^ gmul(a1, 14) ^ gmul(a2, 11) ^ gmul(a3, 13),
        gmul(a0, 13) ^ gmul(a1, 9) ^ gmul(a2, 14) ^ gmul(a3, 11),
        gmul(a0, 11) ^ gmul(a1, 13) ^ gmul(a2, 9) ^ gmul(a3, 14),
    ]
 
 
def load_state_from_bytes(block: bytes) -> List[List[int]]:
    """column-major: s[r][c] = block[4*c + r]"""
    s = [[0] * 4 for _ in range(4)]
    for c in range(4):
        for r in range(4):
            s[r][c] = block[4 * c + r]
    return s
 
 
def state_to_bytes(state: List[List[int]]) -> bytes:
    out = bytearray(16)
    for c in range(4):
        for r in range(4):
            out[4 * c + r] = state[r][c]
    return bytes(out)
 
 
def sub_bytes(state: List[List[int]]) -> None:
    for r in range(4):
        for c in range(4):
            state[r][c] = sbox[state[r][c]]
 
 
def inv_sub_bytes(state: List[List[int]]) -> None:
    for r in range(4):
        for c in range(4):
            state[r][c] = inv_sbox[state[r][c]]
 
 
def shift_rows(state: List[List[int]]) -> None:
    state[1] = state[1][1:] + state[1][:1]
    state[2] = state[2][2:] + state[2][:2]
    state[3] = state[3][3:] + state[3][:3]
 
 
def inv_shift_rows(state: List[List[int]]) -> None:
    state[1] = state[1][-1:] + state[1][:-1]
    state[2] = state[2][-2:] + state[2][:-2]
    state[3] = state[3][-3:] + state[3][:-3]
 
 
def mix_columns(state: List[List[int]]) -> None:
    cols = list(zip(state[0], state[1], state[2], state[3]))
    new_cols = [mix_single_column(list(col)) for col in cols]
    for c in range(4):
        state[0][c], state[1][c], state[2][c], state[3][c] = new_cols[c]
 
 
def inv_mix_columns(state: List[List[int]]) -> None:
    cols = list(zip(state[0], state[1], state[2], state[3]))
    new_cols = [inv_mix_single_column(list(col)) for col in cols]
    for c in range(4):
        state[0][c], state[1][c], state[2][c], state[3][c] = new_cols[c]
 
 
def add_round_key(state: List[List[int]], round_words: List[int]) -> None:
    for c in range(4):
        w = round_words[c]
        state[0][c] ^= (w >> 0) & 0xFF
        state[1][c] ^= (w >> 8) & 0xFF
        state[2][c] ^= (w >> 16) & 0xFF
        state[3][c] ^= (w >> 24) & 0xFF
 
 
def _assemble_initial_words_from_key(key_bytes: bytes) -> List[int]:
    s = load_state_from_bytes(key_bytes)
    words = []
    for c in range(4):
        v = 0
        for r in range(4):
            b = s[r][c]
            v = ((b | v) << 8) & 0xFFFFFFFF
        words.append(v)
    return words
 
 
def expand_key_nonstandard(key_bytes: bytes) -> List[int]:
    assert len(key_bytes) == 16
    W = _assemble_initial_words_from_key(key_bytes)
    rcons = [0x01, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B]
    r_idx = 0
    for i in range(4, 44):
        if i % 4 == 0:
            temp = (rcons[r_idx] << 24) & 0xFFFFFFFF
            r_idx += 1
            W.append(W[i - 4] ^ temp)
        else:
            W.append(W[i - 4] ^ W[i - 1])
    return W
 
 
def encrypt_block_nonstandard(plaintext: bytes, key: bytes) -> bytes:
    assert len(plaintext) == 16 and len(key) == 16
    W = expand_key_nonstandard(key)
    state = load_state_from_bytes(plaintext)
    add_round_key(state, W[0:4])  # round 0
    for r in range(1, 10):  # rounds 1..9
        sub_bytes(state)
        shift_rows(state)
        mix_columns(state)
        add_round_key(state, W[4 * r:4 * r + 4])
    sub_bytes(state)  # final

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2025-8-22 04:04 被只管出不管做编辑 ,原因: 漂亮图图
收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回