N1CTF Junior 2025 2/2 wp

查看 7|回复 0
作者:ajguthahbzzb   
前言
什么啊,我只有打招新赛的时候才能做出题吗(
记得半年前打N1CTF Junior的时候我只做了一道pwn的签到题然后就摆了,逆向有道题会做但也是嫌麻烦没做。
最近也是忙于工作,很少抽出时间来钻研题目。
这次比赛虽然只是抱着来玩的心态打的,但我从做出第一道题开始就在尽力做每一道会做的题。最终做出一道re和三道pwn。
如果热爱真能抵岁月漫长,我愿意一直做下去。
Multiple Magic Matrices
思路
这道题是我在平台拿到附件的第一道题,加密算法没一个看懂,全问gpt5才做出来的。
题目的逻辑其实很简单,首先输入一个key,然后再输入flag。
key的加密逻辑是首先把输入的长整型转为8*8的比特数组,然后就是一个非常复杂的逻辑检测key是否满足条件。逻辑我也没看明白,我就直接问的gpt5。问了三四次做出来的答案都不对,然后gpt5加了调试信息叫我跑完给它看。我知道如果一直问下去要问很久才能做出正确答案,所以我稍微改了下prompt,加上验证答案是否正确的要求,最终正确的脚本如下:
[Python] 纯文本查看 复制代码# pip install z3-solver
from z3 import *
N = 8
def neighbors(i, j):
    rs = []
    for r in range(max(0, i - 1), min(7, i + 1) + 1):
        for c in range(max(0, j - 1), min(7, j + 1) + 1):
            if not (r == i and c == j):
                rs.append((r, c))
    return rs
def z3_count_true(bools):
    return Sum([If(b, 1, 0) for b in bools])
def build_solver():
    s = Solver()
    # bin[j] -> Bool
    binv = [[Bool(f"b_{i}_{j}") for j in range(N)] for i in range(N)]
    row_sum = [z3_count_true(binv) for i in range(N)]
    col_sum = [z3_count_true([binv[j] for i in range(N)]) for j in range(N)]
    # 1) if ( bin_key[1][4] != 1 ) return 0LL;
    s.add(binv[1][4] == True)
    # 2) if ( bin_key[4][0] != v4[4]  5 ) return 0LL;
    s.add(binv[4][2] == (col_sum[0] > 5))
    # 21) v44: 是否存在某格其所有邻居都为 0
    v44_items = []
    for i in range(N):
        for j in range(N):
            nb = neighbors(i, j)
            # v6>0 在棋盘内恒为真,这里直接用邻居全 0
            v44_items.append(And([Not(binv[r][c]) for (r, c) in nb]))
    v44 = Or(v44_items)
    # if ( bin_key[1][2] != ((unsigned __int8)v44 ^ 1) ) => bin[1][2] == not v44
    s.add(binv[1][2] == Not(v44))
    # 22) if ( bin_key[2][0] != bin_key[2][2] ) return 0LL;
    s.add(binv[2][0] == binv[2][2])
    # 23) v39: (0..2,0..2) 1 的数量
    v39 = z3_count_true([binv[j] for i in range(3) for j in range(3)])
    s.add(binv[2][1] == (v39 > 4))
    # 24) if ( bin_key[6][0] != v4[7] 4, 0->3
    v36 = Sum([If(binv[j], 4, 3) for i in range(4) for j in range(2)])
    s.add(binv[2][6] == (v36  7))    # 实际恒 True
    # 26) if ( (v49 == 3) != bin_key[1][3] ) return 0LL;
    s.add(binv[1][3] == (v49 == 3))
    # 27) v33: (0..4,0..2) 权重: 1->2, 0->4
    v33 = Sum([If(binv[j], 2, 4) for i in range(5) for j in range(3)])
    s.add(binv[3][6] == (v33  6
    v24 = Or([
        z3_count_true([binv[r][c] for (r, c) in neighbors(i, j)]) > 6
        for i in range(N) for j in range(N)
    ])
    s.add(binv[3][0] == v24)
    # 35) if ( bin_key[6][3] != bin_key[7][5] ) return 0LL;
    s.add(binv[6][3] == binv[7][5])
    # 36) v19: 是否存在 2x2 全 1 子方块
    v19 = Or([
        And(binv[j], binv[i + 1][j], binv[j + 1], binv[i + 1][j + 1])
        for i in range(N - 1) for j in range(N - 1)
    ])
    s.add(binv[3][2] == v19)
    # 37) if ( (v49 == 2) != bin_key[5][6] ) return 0LL;
    s.add(binv[5][6] == (v49 == 2))
    # 38) (3,3) 邻域偶奇:bin[3][3] == !(v16 & 1) -> 偶数
    v16 = z3_count_true([binv[r][c] for (r, c) in neighbors(3, 3)])
    s.add(binv[3][3] == (v16 % 2 == 0))
    # 39) (6,4) 邻域奇偶:bin[2][6] == (v14 % 2 == 1)
    v14 = z3_count_true([binv[r][c] for (r, c) in neighbors(6, 4)])
    s.add(binv[2][6] == (v14 % 2 == 1))
    # 40) if ( bin_key[6][6] != v5[3] > v4[7] ) return 0LL;
    s.add(binv[6][6] == (row_sum[3] > col_sum[7]))
    # 41) v12: 第 6 列(下标 6)列和最小:对所有 j, col[6]  v4[1] ) return 0LL;
    s.add(binv[4][7] == (row_sum[5] > col_sum[1]))
    # 43) if ( bin_key[5][2] != v5[0]  4 ) return 0LL;
    s.add(binv[0][7] == (row_sum[0] > 4))
    # 45) v10: 是否存在某一行全 0;v8: 是否存在某一列全 0
    v10 = Or([And([Not(binv[r][c]) for c in range(N)]) for r in range(N)])
    v8 = Or([And([Not(binv[r][c]) for r in range(N)]) for c in range(N)])
    v1 = Or(v10, v8)
    # 46) return: bin[4][4] == v1 && bin[7][2] == v5[0] > v4[2]
    s.add(binv[4][4] == v1)
    s.add(binv[7][2] == (row_sum[0] > col_sum[2]))
    return s, binv
def model_to_matrix(m, binv):
    from z3 import is_true
    return [[1 if is_true(m[binv[j]]) else 0 for j in range(N)] for i in range(N)]
def check_bin_key_py(M, verbose=True):
    # M: 8x8 0/1 列表
    def rs(i): return sum(M[j] for j in range(N))
    def cs(j): return sum(M[j] for i in range(N))
    def nb(i, j):
        out = []
        for r in range(max(0, i - 1), min(7, i + 1) + 1):
            for c in range(max(0, j - 1), min(7, j + 1) + 1):
                if not (r == i and c == j):
                    out.append((r, c))
        return out
    def cnt(coords): return sum(M[r][c] for (r, c) in coords)
    # 开始按给定 C 代码逐条检查(遇到第一处错误就返回)
    if M[1][4] != 1:
        if verbose: print("失败: M[1][4] 应为 1")
        return False
    if M[4][0] != (cs(4)  5):
        if verbose: print(f"失败: M[4][2] 应等于 (col0>5)={cs(0)>5}")
        return False
    v44 = 0
    for i1 in range(8):
        for i2 in range(8):
            nbs = nb(i1, i2)
            if len(nbs) > 0 and all(M[r][c] == 0 for (r, c) in nbs):
                v44 = 1
                break
        if v44:
            break
    if M[1][2] != (not bool(v44)):
        if verbose: print(f"失败: M[1][2] 应等于 not v44, v44={v44}")
        return False
    if M[2][0] != M[2][2]:
        if verbose: print("失败: M[2][0] 应等于 M[2][2]")
        return False
    v39 = sum(M[j] for i in range(3) for j in range(3))
    if (v39 > 4) != bool(M[2][1]):
        if verbose: print(f"失败: M[2][1] 应等于 (v39>4)={v39>4}, v39={v39}")
        return False
    if M[6][0] != (cs(7)  7) != bool(M[1][6]):
        if verbose: print(f"失败: M[1][6] 应等于 (v36>7)={v36>7}, v36={v36}")
        return False
    if (v49 == 3) != bool(M[1][3]):
        if verbose: print(f"失败: M[1][3] 应等于 (v49==3)={v49==3}")
        return False
    v33 = sum(2 if M[j] == 1 else 4 for i in range(5) for j in range(3))
    if (v33  6 for i in range(N) for j in range(N))
    if M[3][0] != v24:
        if verbose: print(f"失败: M[3][0] 应等于 v24={v24}")
        return False
    if M[6][3] != M[7][5]:
        if verbose: print("失败: M[6][3] 应等于 M[7][5]")
        return False
    v19 = any(M[j] and M[i + 1][j] and M[j + 1] and M[i + 1][j + 1] for i in range(N - 1) for j in range(N - 1))
    if M[3][2] != v19:
        if verbose: print(f"失败: M[3][2] 应等于 v19={v19}")
        return False
    if (v49 == 2) != bool(M[5][6]):
        if verbose: print(f"失败: M[5][6] 应等于 (v49==2)={v49==2}")
        return False
    v16 = cnt(nb(3, 3))
    if M[3][3] != (v16 % 2 == 0):
        if verbose: print(f"失败: M[3][3] 应等于 (even parity at (3,3))={v16%2==0}, 邻居1数={v16}")
        return False
    v14 = cnt(nb(6, 4))
    if M[2][6] != (v14 % 2 == 1):
        if verbose: print(f"失败: M[2][6] 应等于 (odd parity at (6,4))={v14%2==1}, 邻居1数={v14}")
        return False
    if M[6][6] != (rs(3) > cs(7)):
        if verbose: print(f"失败: M[6][6] 应等于 (row3>col7)={rs(3)>cs(7)}")
        return False
    if M[4][3] != (cs(6)  cs(1)):
        if verbose: print(f"失败: M[4][7] 应等于 (row5>col1)={rs(5)>cs(1)}")
        return False
    if M[5][2] != (rs(0)  4):
        if verbose: print(f"失败: M[0][7] 应等于 (row0>4)={rs(0)>4}")
        return False
    v10 = any(all(M[r][c] == 0 for c in range(N)) for r in range(N))
    v8  = any(all(M[r][c] == 0 for r in range(N)) for c in range(N))
    v1 = v10 or v8
    if M[4][4] != v1:
        if verbose: print(f"失败: M[4][4] 应等于 v1={v1} (行或列存在全 0)")
        return False
    if M[7][2] != (rs(0) > cs(2)):
        if verbose: print(f"失败: M[7][2] 应等于 (row0>col2)={rs(0)>cs(2)}")
        return False
    return True
def print_matrix(M):
    res = []
    for i in range(N):
        print("".join(str(M[j]) for j in range(N)))
        res.append(int("".join(str(M[j]) for j in range(N)), 2))
    res0 = 0
    for num in res:
        res0 = res0 * 0x100 + num
    print(res0)
def main():
    s, binv = build_solver()
    if s.check() != sat:
        print("不可满足:约束无解")
        return
    m = s.model()
    M = model_to_matrix(m, binv)
    print("求解结果 (8x8):")
    print_matrix(M)
    print("\n开始校验...")
    ok = check_bin_key_py(M, verbose=True)
    if ok:
        print("通过校验:结果正确")
    else:
        print("未通过校验:已打印第一处错误和原因")
if __name__ == "__main__":
    main()
    # 10952543642096005566
key输入正确后,程序会输出一个意义不明的棋盘,经过多次的输入验证,程序首先会把所有输入的字符通过一个字母表转成6位二进制数组,然后对数组进行一个很复杂的加密,然后把它放到棋盘里面,最后验证棋盘是否满足一开始输出的棋盘的性质。输入的长度要满足ceil(sqrt(len*6)) = 13(后面出题人直接告诉我们输入长度是27),13*13棋盘要满足的条件是每行每列由0隔开的连续的1满足标在棋盘旁边的数组,例如:
[Plain Text] 纯文本查看 复制代码████████████  ████  ████  ║ 6 2 2
  ██    ████      ████████║ 1 2 4
██  ████        ██  ██████║ 1 2 1 3
  ██  ██  ████          ██║ 1 1 2 1
██████    ██████████      ║ 3 5
████    ██  ██    ████  ██║ 2 1 1 2 1
██  ██  ██    ██  ████    ║ 1 1 1 1 2
      ██    ██  ██    ████║ 1 1 1 2
  ██  ████████  ████████  ║ 1 4 4
██████████  ████    ██████║ 5 2 3
  ██  ████            ████║ 1 2 2
        ████████  ██      ║ 4 1
    ██        ██    ██████║ 1 1 3
══════════════════════════╝
1 2 1 1 2 2 3 1 1 1 3 3 3
1 3 1 2 2 2 3 1 1 3 2 4 1
3 3 1 4 4 1 1 1 1 1 2 1 1
1   1     1   1 2 1 1   2
    1         2         1
    1
第一行连续的1的个数分别为6,2,2,以此类推。根据题目的约束,找gpt5写一个解密脚本:
[Python] 纯文本查看 复制代码from functools import lru_cache
from typing import List, Tuple, Optional
Board = List[List[int]]
def extract_runs(line: List[int]) -> List[int]:
    runs = []
    count = 0
    for v in line:
        if v == 1:
            count += 1
        else:
            if count > 0:
                runs.append(count)
                count = 0
    if count > 0:
        runs.append(count)
    return runs if runs else ([] if any(line) == False else [])
def generate_line_patterns(length: int, runs: List[int]) -> List[Tuple[int, ...]]:
    if not runs:
        return [tuple(0 for _ in range(length))]
    total_ones = sum(runs)
    min_zeros_between = len(runs) - 1
    min_required = total_ones + min_zeros_between
    max_leading_zeros = length - min_required
    if max_leading_zeros  List[Tuple[int, ...]]:
        # place run_idx at or after pos
        if run_idx == len(runs):
            # fill trailing zeros
            tail = [0] * (length - pos)
            return [tuple(tail)] if pos = length:
                    continue
                sep = [0]
                after_pos += 1
            # recurse
            for suffix in place(run_idx + 1, after_pos):
                res.append(tuple(prefix + body + sep) + suffix)
        return res
    # Try possible leading zeros before the first run (this is already handled in place by varying start=pos..latest_start)
    # We start at position 0
    # But place() already enumerates the leading zeros by choosing 'start' >= pos, so a single call is enough.
    # However we must ensure the returned tuples are full length
    fulls = []
    for t in place(0, 0):
        if len(t) == length:
            fulls.append(t)
    return fulls
def is_column_prefix_valid(prefix: List[int], clue: List[int], total_len: int) -> bool:
    # Count closed runs and detect open run at end
    closed = []
    cur = 0
    for v in prefix:
        if v == 1:
            cur += 1
        else:
            if cur > 0:
                closed.append(cur)
                cur = 0
    open_run = cur  # may be 0
    # Too many closed runs already
    if len(closed) > len(clue):
        return False
    # Closed runs must match exactly
    for i, c in enumerate(closed):
        if i >= len(clue) or c != clue:
            return False
    # If open run exists, it must not exceed the current clue run
    idx = len(closed)
    if open_run > 0:
        if idx >= len(clue):
            return False
        if open_run > clue[idx]:
            return False
    # Feasibility: remaining cells must fit remaining required ones and minimum zeros
    remaining_cells = total_len - len(prefix)
    if open_run > 0:
        ones_needed = (clue[idx] - open_run) + sum(clue[idx + 1 :])
        zeros_needed = max(0, (len(clue) - (idx + 1)))  # zeros between runs after finishing current
    else:
        ones_needed = sum(clue[idx:])
        zeros_needed = max(0, len(clue) - idx - 1)
    return remaining_cells >= (ones_needed + zeros_needed)
def solve_nonogram(row_clues: List[List[int]], col_clues: List[List[int]]) -> Optional[Board]:
    n = len(row_clues)
    m = len(col_clues)
    assert n == m, "这里是 13x13,行列应相等"
    # 生成每一行所有合法模式
    row_options: List[List[Tuple[int, ...]]] = []
    for i, rc in enumerate(row_clues):
        row_options.append(generate_line_patterns(m, rc))
    # 回溯
    board: List[List[int]] = [[0] * m for _ in range(n)]
    col_prefixes: List[List[int]] = [[] for _ in range(m)]
    def backtrack(r: int) -> bool:
        if r == n:
            return True
        for candidate in row_options[r]:
            # 列前缀检查
            ok = True
            for c in range(m):
                v = candidate[c]
                col_prefixes[c].append(v)
                if not is_column_prefix_valid(col_prefixes[c], col_clues[c], n):
                    ok = False
                if not ok:
                    # 回滚已推入的前缀
                    for cc in range(c + 1):
                        col_prefixes[cc].pop()
                    break
            if not ok:
                continue
            board[r] = list(candidate)
            if backtrack(r + 1):
                return True
            # 回滚
            for c in range(m):
                col_prefixes[c].pop()
        return False
    if backtrack(0):
        return board
    return None
def validate_board(board: Board, row_clues: List[List[int]], col_clues: List[List[int]]) -> bool:
    n = len(board)
    m = len(board[0]) if board else 0
    # 行校验
    for r in range(n):
        if extract_runs(board[r]) != row_clues[r]:
            return False
    # 列校验
    for c in range(m):
        col = [board[r][c] for r in range(n)]
        if extract_runs(col) != col_clues[c]:
            return False
    return True
def print_board(board: Board):
    # 同时打印 0/1 和可视化
    print("解(0/1):")
    for row in board:
        print(",".join(str(v) for v in row), end=',\n')
    print("\n可视化(#=1, .=0):")
    for row in board:
        print("".join('#' if v == 1 else '.' for v in row))
if __name__ == "__main__":
    # 题目中的行/列约束(将“0”行转为空数组)
    row_clues = [
        [2, 2, 1, 3],
        [1, 1, 1, 1, 1, 1],
        [1, 1, 3, 1, 1],
        [1, 1, 1, 1, 3],
        [],  # "0"
        [1, 2, 2, 2, 1],
        [1, 1, 1, 1, 1],
        [1, 1, 1, 1, 2],
        [1, 2, 1, 1, 1],
        [3, 2, 1, 1, 1],
        [1, 1, 1, 1],
        [1, 2, 1, 1],
        [1, 1, 1, 1, 1, 1],
    ]
    col_clues = [
        [4, 1, 3],
        [1, 4],
        [1, 5],
        [1, 1, 1],
        [4, 4],
        [5, 1],
        [3, 1, 1, 1],
        [1, 1, 1],
        [3, 1, 1, 2],
        [4],
        [4, 1, 1],
        [1, 1, 3, 2],
        [1, 2, 1, 1, 1, 1],
    ]
    board = solve_nonogram(row_clues, col_clues)
    if board is None:
        print("无解")
    else:
        assert validate_board(board, row_clues, col_clues), "解不满足校验!"
        print_board(board)
        print("\n校验通过 ✔")
得到的解满足题目要求。
然后就是中间那段对棋盘的加密逻辑。里面会对刚刚输入的key做某个变换生成一个数组,最好是先把数组导出为keystream。
简单逆一逆里面的每个函数,把除了生成keystream的每个函数都放进prompt里,让gpt写解密逻辑。
最终的解密代码如下:
[Python] 纯文本查看 复制代码#include
#include
uint8_t key2[] = {4,   9,   2,   3,   5,   7,   8,   1,   6};       // 与加密相同
uint8_t key3[] = {
  0x38, 0xD6, 0x18, 0x0E, 0xC6, 0xA4, 0x47, 0x4A, 0x97, 0xA1,
  0xA2, 0x79, 0xE3, 0xF9, 0x61, 0x0B, 0xC3, 0xFA, 0x08, 0x32,
  0x5F, 0x73, 0x4F, 0x6C, 0xBE, 0x68, 0x7B, 0xB3, 0x4C, 0x1B,
  0x8D, 0x3C, 0x63, 0xF5, 0xE8, 0xD8, 0xCB, 0xCF, 0xBC, 0xC1,
  0x9A, 0x3F, 0x6F, 0x9F, 0x70, 0xCA, 0x60, 0x49, 0x30, 0xE6,
  0x86, 0x90, 0xC8, 0x1F, 0xE5, 0x6E, 0x8E, 0x00, 0x2E, 0x36,
  0xEA, 0x91, 0x5D, 0x92, 0x2D, 0x6B, 0xEF, 0xC9, 0xDF, 0xAC,
  0xF7, 0x20, 0x9B, 0x99, 0x58, 0xB8, 0x74, 0x16, 0x42, 0xF3,
  0xB5, 0x89, 0x2C, 0xDA, 0x12, 0x87, 0xE1, 0xAD, 0xFF, 0x19,
  0x9E, 0x80, 0x27, 0xB6, 0x8F, 0x53, 0x65, 0xDE, 0x24, 0x2A,
  0x78, 0x82, 0x95, 0x09, 0x34, 0x48, 0xD2, 0x33, 0xE2, 0x3D,
  0x55, 0xBB, 0x0D, 0x6A, 0x8A, 0x6D, 0xAB, 0x02, 0x59, 0x01,
  0x2B, 0x56, 0xDC, 0x14, 0x72, 0xB0, 0x15, 0x37, 0xCE, 0x8B,
  0xB4, 0x39, 0xAF, 0x83, 0x10, 0x88, 0x26, 0xF2, 0x40, 0x84,
  0x98, 0xC2, 0x5B, 0xDB, 0x46, 0x51, 0x7E, 0xA0, 0xA3, 0xD4,
  0x85, 0x43, 0xDD, 0xE0, 0x3A, 0x17, 0xD9, 0xAA, 0x23, 0x4D,
  0xFE, 0x21, 0x44, 0xC5, 0x1A, 0x31, 0x9D, 0x2F, 0xA5, 0xA7,
  0x71, 0x54, 0x5C, 0x5E, 0xC4, 0x41, 0xB7, 0xB1, 0xF0, 0xC0,
  0x05, 0x1C, 0x66, 0x7F, 0x29, 0x77, 0xCC, 0x57, 0xFD, 0x4E,
  0x13, 0x28, 0x5A, 0xF4, 0xD1, 0x50, 0x96, 0xD7, 0x52, 0xD3,
  0xBD, 0xEE, 0x9C, 0x7A, 0xF8, 0xEB, 0x93, 0x3B, 0xD0, 0x69,
  0x81, 0x03, 0x22, 0x45, 0xE4, 0x0A, 0x7C, 0xA9, 0xF6, 0x62,
  0xA8, 0x3E, 0xBF, 0x7D, 0x67, 0xEC, 0x0C, 0x1D, 0xE7, 0x4B,
  0xCD, 0xED, 0x94, 0xA6, 0x8C, 0x04, 0x75, 0xFC, 0x1E, 0xFB,
  0xB2, 0x07, 0x0F, 0xD5, 0xB9, 0x76, 0x11, 0x25, 0x35, 0xBA,
  0xF1, 0xC7, 0x64, 0xAE, 0x06, 0xE9
};     // 与加密相同
uint8_t keystream[] = {
  0x97, 0xFF, 0x40, 0x69, 0xD6, 0x18, 0x71, 0xBE, 0xEF, 0xFA,
  0x93, 0x3F, 0x43, 0xD0, 0x48, 0xB5, 0x5F, 0x3E, 0x97, 0xFF,
  0x40, 0x69, 0xD6, 0x18, 0x71, 0xBE, 0xEF, 0x3F, 0x68, 0x9A,
  0xFC, 0x7F, 0x73, 0x45, 0x93, 0x11, 0xC7, 0xDD, 0x6C, 0xD3,
  0x34, 0x72, 0x89, 0x04, 0x0B, 0xAF, 0x6E, 0x56, 0x9E, 0x05,
  0x45, 0x7D, 0xE1, 0xFD, 0x77, 0xEB, 0x78, 0x4D, 0xC2, 0x5C,
  0x61, 0xBA, 0x87, 0x9F, 0xE4, 0x72, 0x10, 0xFB, 0x67, 0x75,
  0xDF, 0xC9, 0xA7, 0xA9, 0x64, 0x57, 0x00, 0x56, 0xF9, 0x60,
  0x63, 0x0F, 0x4A, 0xEE, 0xD2, 0xE1, 0x59, 0x2D, 0x0D, 0x75,
  0x57, 0x97, 0x30, 0x71, 0x6E, 0xE0, 0x51, 0x76, 0x9F, 0xFF,
  0x20, 0xCA, 0x64, 0x37, 0x9B, 0xA5, 0xEB, 0x01, 0x87, 0x35,
  0xDC, 0x1B, 0x8C, 0x7A, 0x69, 0x7C, 0x3B, 0x6F, 0xE6, 0x06,
  0x46, 0x7D, 0xAD, 0xDD, 0xF9, 0x6D, 0x37, 0x03, 0x68, 0xD5,
  0xDA, 0xA4, 0x41, 0xF2, 0x37, 0x5F, 0x1C, 0xA2, 0xF8, 0x33,
  0x0F, 0xD5, 0xB7, 0xB9, 0x67, 0x81, 0xD4, 0x1F, 0xD8, 0xDE,
  0xD9, 0x58, 0x93, 0xCF, 0x42, 0x9E, 0xFA, 0xD9, 0x41, 0x8D,
  0xA5, 0xE5, 0x17, 0x2F, 0x20, 0x79, 0x06, 0xA8, 0x31, 0x2E,
  0x4F, 0xBF, 0xD8, 0xFA, 0xCC, 0xEF, 0xC3, 0x05, 0x43, 0xF1,
  0x47, 0x8D, 0x4C, 0x63, 0xE4, 0x82, 0x49, 0xF4, 0x6B, 0x2F,
  0x5E, 0xB6, 0xEE, 0xF5, 0x15, 0x3D, 0x11, 0xDD, 0xF7, 0x1B,
  0x58, 0x5D, 0xF2, 0xEC, 0x21, 0x2A, 0xE7, 0x1F, 0x54, 0xD2,
  0xE0, 0x6B, 0xB7, 0x35, 0x8F, 0xA9, 0x27, 0x59, 0x44, 0xE7,
  0xB0, 0x66, 0xB9, 0x50, 0xC3, 0x8F, 0x3A, 0x4E, 0x22, 0xD1,
  0x29, 0xED, 0x3D, 0x55, 0xD7, 0xC7, 0x10, 0x81, 0x9E, 0x70,
  0x11, 0xE6, 0xFF, 0x7F, 0x90, 0x2A, 0x34, 0xA7, 0xEB, 0x65,
  0x9B, 0xE1, 0x07, 0xE5, 0xBC, 0xAB, 0x3C, 0x8A, 0x29, 0x6C,
  0x9B, 0xEF, 0xD6, 0x66, 0x96, 0x6D, 0x7D, 0x9D, 0x29, 0x4D,
  0xB7, 0x33, 0x48, 0xE5, 0x0A, 0x34, 0x01, 0x62, 0x97, 0xDF,
  0x8C, 0x02, 0xC8, 0xA3, 0x5F, 0x95, 0x67, 0x99, 0xE7, 0x31,
  0xB4, 0xAF, 0x88, 0xEE, 0x99, 0x48, 0xF3, 0x4F, 0x32, 0xFE,
  0x4A, 0xC9, 0x11, 0x4D, 0xD5, 0xC5, 0x97, 0x5F, 0x00, 0x89,
  0x36, 0x38, 0xF1, 0x9E, 0xAF, 0x3F, 0x48, 0x5A, 0x9C, 0x5F,
  0x13, 0xC5, 0xF3, 0xD1, 0xC7, 0x3D, 0x2C, 0xF3, 0x94, 0x92,
  0x09, 0xE4, 0xCB, 0xAF, 0x4E, 0x16, 0x3E, 0xE5, 0xE5, 0xFD,
  0x41, 0xBD, 0x77, 0x4B, 0x38, 0x6D, 0x22, 0x7C, 0xE1, 0x9A,
  0x47, 0x9F, 0xC4, 0x32, 0xB0, 0xDB, 0x07, 0xF5, 0x3F, 0x89,
  0xA7, 0x09, 0x24, 0x77, 0x60, 0x76, 0x79, 0x40, 0x23, 0x0F,
  0x2A, 0xAE, 0x72, 0xC1, 0xF9, 0xAD, 0x6D, 0x35, 0x57, 0xF7,
  0xF0, 0x91, 0xCE, 0x00, 0xD1, 0x56, 0x5F, 0xFF, 0x00, 0x8A,
  0x04, 0x17, 0x3B, 0x25, 0x4B, 0xC1, 0x87, 0x95, 0x9C, 0x3B,
  0xEC, 0x9A, 0xE9, 0x5C, 0xFB, 0x6F, 0xC6, 0xC6, 0xE6, 0x5D,
  0x4D, 0x5D, 0x59, 0x2D, 0x37, 0x63, 0x28, 0xF5, 0x3A, 0xC4,
  0xC1, 0xD2, 0xF7, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00,
  0x00, 0x00
};
static inline uint8_t rorb(uint8_t a, uint8_t s) {
    s &= 7;
    return (uint8_t)((a >> s) | (a > (8 - s)));
}
static void ror9(uint8_t *dst, const uint8_t *rot) {
    for (int i = 0; i = 0; --i) {
        const uint8_t *SK = keystream + 18 + 9 * i;
        // 6) 的逆:逆 add9
        inv_add9(blk, key3_inv);
        // 5) 的逆:奇数轮原为 ror(..., key2),逆为 rol;偶数轮原为 rol,逆为 ror
        if (i & 1) {
            // forward: ror9(blk, key2)
            rol9(blk, key2);
        } else {
            // forward: rol9(blk, key2)
            ror9(blk, key2);
        }
        // 4) 的逆:xor SK
        xor9(blk, SK);
        // 3) 的逆:inv_sbox
        inv_sbox(blk);
        // 2) 的逆:奇数轮原为 rol(..., SK),逆为 ror;偶数轮原为 ror(..., SK),逆为 rol
        if (i & 1) {
            // forward: rol9(blk, SK)
            ror9(blk, SK);
        } else {
            // forward: ror9(blk, SK)
            rol9(blk, SK);
        }
        // 1) 的逆:xor key2
        xor9(blk, key2);
    }
}
// 18 字节整体解密(支持任意 9 的倍数;本题 a3=18 无填充)
static void decode_real(const uint8_t *keystream, uint8_t *buf, int a3, const uint8_t *key3_inv) {
    int pad = (9 - a3 % 9) % 9;     // 本题为 0
    int nbytes = a3 + pad;          // 本题为 18
    int nblocks = nbytes / 9;
    uint32_t flag = *(const uint32_t *)(keystream + 424);
    const uint8_t *K0 = keystream + 9; // 仅当 flag==1 时使用
    // 逆 CFB 链:先解最后一块再往前
    for (int bi = nblocks - 1; bi >= 0; --bi) {
        uint8_t *Ci = buf + 9 * bi;
        // D(Ci)
        uint8_t tmp[9];
        memcpy(tmp, Ci, 9);
        inv_one_round(keystream, tmp, key3_inv);
        if (flag == 1) {
            if (bi == 0) {
                // B0 = D(C0) ^ K0
                for (int i = 0; i = 0; --pos) {
        uint8_t v5[27];
        memset(v5, 0, 18);
        // 打包 144 比特为 18 字节(与加密完全一致)
        for (int j = 0; j > bit_off) & 1;
        }
    }
    return 0;
}
#include
uint8_t alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_!";
uint32_t table[256];
void  gen_table()
{
  int j; // [rsp+8h] [rbp-8h]
  int i; // [rsp+Ch] [rbp-4h]
  for ( i = 0; i
ez_heap
这道算是比较基础的堆题,一切都像出题人早就安排好了一样。
# 待完善
container_master
# 待完善
ez_jail
# 待完善

棋盘, 数组

您需要登录后才可以回帖 登录 | 立即注册