什么啊,我只有打招新赛的时候才能做出题吗(
记得半年前打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
# 待完善