boxmoe_header_banner_img

欢迎来到烨的世界~

加载中

文章导读

鹏程杯


avatar
liuye 2025年12月18日 550

re

babyconnet

Server.exe 有主逻辑

使用XOR 解密

flag_dll_check(buf); 验证flag

WASD移动

SMC:解密

inside.dll的check,2是终点,1是正常

mov eax, 0; ret 是可通过

mov eax, [eax] (eax=0 时崩溃 ) 不可通过

mov dword_10014AC8, 1 终点

这里是

Patch了53

Patch了85

这里实现了nop功能

完整的迷宫是

from collections import deque

def solve_maze():

    # 初始通路 + 特殊位置(传送门)

    initial_paths = {1, 11, 13, 15, 16, 17, 18, 21, 23, 25, 28, 31, 33, 35, 38,

                     41, 42, 43, 45, 46, 48, 56, 58, 61, 62, 63, 64, 66, 68,

                     71, 74, 76, 78, 81, 82, 84, 86, 88}

    start, end = 1, 88

    queue = deque([((start, frozenset()), “D”)])

    visited = {(start, frozenset())}

    while queue:

        (pos, opened), path = queue.popleft()

        if pos == end:

            return path

        current_paths = initial_paths | opened

        for direction, delta in [(‘W’, -10), (‘S’, 10), (‘A’, -1), (‘D’, 1)]:

            new_pos = pos + delta

            # 边界检查…

            if new_pos in current_paths:

                new_opened = set(opened)

                if new_pos == 13: new_opened.add(53)   # 打开位置53

                if new_pos == 82: new_opened.add(85)   # 打开位置85

                new_state = (new_pos, frozenset(new_opened))

                if new_state not in visited:

                    visited.add(new_state)

                    queue.append((new_state, path + direction))

    return None

# 结果

path = “DSSSSDDWWWSSSSSAASSDAWWDDDSSDDWWWWAWWWDDDSSSSSSS”

flag.dll里面是主要的检查那么看这里

按照后面四个字节来求解,密文在&unk_10015000里面是0x50,0x73,0x65,0xCC,0x0,0xC,0x11,0x2E,0x2,0x26,0x2,0x3,0xD,0x7A,0x7A,0x1B,0x36,0x61,0x4C,0x6,0x18,0x4C,0xF,0x46,0x58,0x30,0x30,0x53,0x62,0x58,0x5A,0x68,0xE,0x34,0x55,0x5,0x5B,0x6C,0x4A,0x44,0x5E,0x36,0x42,0x7D

exp

maze_path = “DSSSSDDWWWSSSSSAASSDAWWDDDSSDDWWWWAWWWDDDSSSSSSS”

# flag.dll 的目标密文

target = bytes([

    0x50, 0x73, 0x65, 0xCC, 0x00, 0x0C, 0x11, 0x2E,

    0x02, 0x26, 0x02, 0x03, 0x0D, 0x7A, 0x7A, 0x1B,

    0x36, 0x61, 0x4C, 0x06, 0x18, 0x4C, 0x0F, 0x46,

    0x58, 0x30, 0x30, 0x53, 0x62, 0x58, 0x5A, 0x68,

    0x0E, 0x34, 0x55, 0x05, 0x5B, 0x6C, 0x4A, 0x44,

    0x5E, 0x36, 0x42, 0x7D,

])

print(f”目标密文 ({len(target)} 字节): {target.hex()}”)

# 分析 nullsub_1 的 XOR 逻辑

# 看起来是: data[i] ^= data[i+1] 的循环

# 尝试逆向 XOR 操作

def reverse_xor(data):

    “””逆向 XOR 操作: 从后往前”””

    result = bytearray(data)

    for i in range(len(result) – 2, -1, -1):

        result[i] ^= result[i + 1]

    return bytes(result)

# 尝试多次逆向(因为代码中有3个类似的循环)

decrypted = target

for round_num in range(3):

    decrypted = reverse_xor(decrypted)

    try:

        text = decrypted.decode(‘ascii’)

        if text.startswith(‘flag’) or text.startswith(‘PCC’) or ‘ctf’ in text.lower():

            print(f”\n第 {round_num + 1} 轮解密后: {text}”)

    except:

        pass

    print(f”第 {round_num + 1} 轮: {decrypted.hex()}”)

    # 检查是否有可打印字符

    printable = ”.join(chr(b) if 32 <= b < 127 else ‘.’ for b in decrypted)

    print(f”  可打印: {printable}”)

More more flower

字节码是

bytecode = bytes([

    0x01, 0x08, 0x06, 0x01, 0x01, 0x02, 0x02, 0x04, 0x02, 0x09, 0x01, 0x01,

    0x02, 0x04, 0x05, 0x01, 0x08, 0x08, 0x01, 0x03, 0x04, 0x02, 0x09, 0x01,

    0x01, 0x02, 0x04, 0x05, 0x01, 0x08, 0x08, 0x01, 0x03, 0x04, 0x02, 0x09,

    0x01, 0x01, 0x02, 0x04, 0x05, 0x01, 0x08, 0x08, 0x01, 0x03, 0x03, 0x04,

    0x06, 0x00, 0x03, 0x03, 0x06, 0x1E, 0x01, 0x08, 0x56, 0x01, 0x08, 0x11,

    0x01, 0x08, 0x25, 0x01, 0x08, 0x23, 0x02, 0x08, 0x04, 0x04, 0x06, 0x01,

    0x07, 0x03, 0x05, 0x01, 0x05, 0x05, 0x05, 0x09, 0x05, 0x04, 0x06, 0x01,

    0x04, 0x09, 0x05, 0x01, 0x02, 0x07, 0x04, 0x01, 0x05, 0x07, 0x03, 0x01,

    0x0A, 0x03, 0x36, 0x01, 0x07, 0x07, 0x06, 0x01, 0x03, 0x03, 0x08, 0x04,

    0x02, 0x09, 0x0A, 0x03, 0x03, 0x03, 0x03, 0x06, 0x18, 0x03, 0x02, 0x06,

    0x00, 0x02, 0x02, 0x03, 0x04, 0x07, 0x07, 0x01, 0x00, 0x0A, 0x01, 0x8F,

    0x07, 0x03, 0x01, 0x04, 0x02, 0x09, 0x0A, 0x03, 0x79, 0x0B, 0x01, 0x0B

])

目标值

target = bytes([

    0x21, 0x7A, 0x01, 0x1C, 0x33, 0xD3, 0x3E, 0xF7,

    0x03, 0x78, 0x25, 0x5E, 0x2F, 0xB8, 0x8B, 0x3B,

    0x93, 0x84, 0xAE, 0x5B, 0xDE, 0xA5, 0xD6, 0xE9

])

根据VM可以得到

下面的指令

opcode

1

2

3

4

5

6

7

8

9

10

11

指令

PUSH

POP

MOV

ADD

SHL

SHR

SUB

OR

XOR

JNZ

RET

反VM

  0: PUSH imm 0x6        ; 外层循环计数器 = 6

; === 读取 4 字节组成 32-bit 值 ===

  3: PUSH input[R1]      ; 读取输入字节

  5: POP R0

  7: ADD R1, 1

 10: PUSH input[R1]

 12: POP R2

 14: SHL R0, 8           ; R0 = R0 << 8

 17: OR R0, R2           ; R0 |= R2

 … (重复读取 4 字节)

; === 初始化加密参数 ===

 46: MOV R3, 0x0         ; delta = 0

 50: MOV R2, 0x1E        ; 内层循环 30 次

 54: PUSH imm 0x56       ; 压入 KEY 的 4 个字节

 57: PUSH imm 0x11

 60: PUSH imm 0x25

 63: PUSH imm 0x23

 66: POP dword (var_8)   ; var_8 = 0x23251156 (KEY)

; === 加密内层循环 ===

 68: ADD R3, var_8       ; delta += KEY

 71: PUSH R0_dword       ; 保存 R0

 73: MOV R6, R0          ; R6 = R0

 76: SHL R6, 5           ; R6 = R0 << 5

 79: XOR R6, R3          ; R6 ^= delta

 82: SHR R0, 4           ; temp = R0 >> 4

 85: XOR R6, R0          ; R6 ^= (R0 >> 4)

 88: POP R0_dword        ; 恢复 R0

 90: ADD R0, R6          ; R0 += R6

 93: SUB R2, 1           ; 计数器–

 96: JNZ R2, 54          ; 循环 30 次

 99: PUSH R0_dword       ; 保存加密结果

; === 验证阶段 ===

121: POP R0              ; 逐字节弹出

123: MOV R3, target[R1]  ; 与目标比较

126: SUB R0, R3

129: JNZ R0, 143         ; 不等则失败

141: RET 1               ; 成功返回 1

143: RET 0               ; 失败返回 0

得到算法

KEY = 0x23251156

def encrypt_block(v):

delta = 0

for _ in range(30):

delta = (delta + KEY) & 0xFFFFFFFF

r6 = ((v << 5) & 0xFFFFFFFF) ^ delta ^ (v >> 4)

v = (v + r6) & 0xFFFFFFFF

return v

exp

import hashlib

target = bytes([

    0x21, 0x7A, 0x01, 0x1C, 0x33, 0xD3, 0x3E, 0xF7,

    0x03, 0x78, 0x25, 0x5E, 0x2F, 0xB8, 0x8B, 0x3B,

    0x93, 0x84, 0xAE, 0x5B, 0xDE, 0xA5, 0xD6, 0xE9

])

# 正确的加密函数

KEY = 0x23251156  # var_8 的值

def encrypt_block_correct(v):

    “””正确的加密:delta 累加 KEY”””

    delta = 0

    for _ in range(30):

        delta = (delta + KEY) & 0xFFFFFFFF

        # R6 = (R0 << 5) ^ delta ^ (R0 >> 4)

        r6 = ((v << 5) & 0xFFFFFFFF) ^ delta ^ (v >> 4)

        v = (v + r6) & 0xFFFFFFFF

    return v

def decrypt_single_round_all(v_new, delta):

    “””解密单轮”””

    def calc_encrypted(v_old):

        r6 = ((v_old << 5) & 0xFFFFFFFF) ^ delta ^ (v_old >> 4)

        return (v_old + r6) & 0xFFFFFFFF

    stage1 = []

    for low16 in range(1 << 16):

        r6 = ((low16 << 5) & 0xFFFFFFFF) ^ delta ^ (low16 >> 4)

        result = (low16 + r6) & 0xFFFFFFFF

        if (result & 0xFF) == (v_new & 0xFF):

            stage1.append(low16)

    stage2 = []

    for low16 in stage1:

        for mid8 in range(256):

            low24 = low16 | (mid8 << 16)

            r6 = ((low24 << 5) & 0xFFFFFFFF) ^ delta ^ (low24 >> 4)

            result = (low24 + r6) & 0xFFFFFFFF

            if (result & 0xFFFF) == (v_new & 0xFFFF):

                stage2.append(low24)

    solutions = []

    for low24 in stage2:

        for high8 in range(256):

            v_old = low24 | (high8 << 24)

            if calc_encrypted(v_old) == v_new:

                solutions.append(v_old)

    return solutions

def decrypt_block_correct(enc):

    “””正确的解密:30轮,delta从30*KEY递减”””

    current = [enc]

    delta = (30 * KEY) & 0xFFFFFFFF

    for r in range(30):

        next_solutions = []

        for v in current:

            sols = decrypt_single_round_all(v, delta)

            next_solutions.extend(sols)

        current = next_solutions

        delta = (delta – KEY) & 0xFFFFFFFF

        if not current:

            return []

    return current

# 验证加密

print(“=== Verify encryption ===”)

test_val = 0x41424344

enc = encrypt_block_correct(test_val)

print(f”encrypt(0x41424344) = {enc:#010x}”)

# 验证解密

print(“\n=== Verify decryption ===”)

solutions = decrypt_block_correct(enc)

print(f”Solutions: {len(solutions)}”)

if test_val in solutions:

    print(f”Original value {test_val:#010x} found!”)

else:

    print(“Original not found”)

    for s in solutions[:5]:

        print(f”  {s:#010x}”)

# 解密所有块

print(“\n=== Decrypting flag ===”)

all_solutions = []

for i in range(6):

    start = i * 4

    block = (target[start] << 24) | (target[start + 1] << 16) | (target[start + 2] << 8) | targ

    print(f”Block {i}: {block:#010x}”, end=” -> “)

    solutions = decrypt_block_correct(block)

    print(f”{len(solutions)} solutions”)

    # 过滤可打印

    printable = []

    for sol in solutions:

        b = bytes([(sol >> 24) & 0xFF, (sol >> 16) & 0xFF, (sol >> 8) & 0xFF, sol & 0xFF])

        if all(0x20 <= c < 0x7F for c in b):

            printable.append((sol, b))

    print(f”  Printable: {len(printable)}”)

    for sol, b in printable[:5]:

        print(f”    {sol:#010x} = {b}”)

    all_solutions.append(

        printable if printable else [(s, bytes([(s >> 24) & 0xFF, (s >> 16) & 0xFF, (s >> 8) &

                                     in solutions])

# 组合求解

print(“\n=== Finding flag ===”)

from itertools import product

expected_sha = “3dbe89f66cb189f9cac1fb5ec23fac941df69119792aad4b6d61d63b98ddb527”

total = 1

for sols in all_solutions:

    total *= len(sols)

print(f”Total combinations: {total}”)

if total > 0 and total < 10000000:

    for combo in product(*all_solutions):

        flag = b”

        for sol, _ in combo:

            flag += bytes([(sol >> 24) & 0xFF, (sol >> 16) & 0xFF, (sol >> 8) & 0xFF, sol & 0xF

        sha = hashlib.sha256(flag).hexdigest()

        if sha == expected_sha:

            print(f”\n*** FOUND FLAG ***”)

            print(f”Flag: {flag}”)

            try:

                print(f”String: {flag.decode()}”)

            except:

                pass

            print(f”SHA256: {sha}”)

            break

    else:

        print(“Not found”)

Get_My_Emoji_wp

先看emoji_encoder

标准的RC4加密

那么很好写了.

import shutil

import subprocess

from pathlib import Path

import numpy as np

from PIL import Image

def encode_constant(binary: Path, workdir: Path, value: int, h: int, w: int) -> np.ndarray:

    # 构造全常数 RGBA 图 -> 调用 encoder -> 读回 enc_emoji.png 的 RGBA 数组

    plain = np.full((h, w, 4), value, dtype=np.uint8)

    Image.fromarray(plain, “RGBA”).save(workdir / “my_emoji.png”)

    subprocess.run(

        [str(binary)],

        cwd=str(workdir),

        check=True,

        stdout=subprocess.DEVNULL,

        stderr=subprocess.DEVNULL,

    )

    enc = Image.open(workdir / “enc_emoji.png”).convert(“RGBA”)

    return np.array(enc, dtype=np.uint8)

def main():

    workdir = Path(__file__).resolve().parent

    binary = workdir / “emoji_encoder”

    target = workdir / “enc_emoji.png”  # 题目给的噪声图

    backup = workdir / “enc_emoji_orig.png”

    out = workdir / “decoded_emoji.png”

    if not binary.exists():

        raise FileNotFoundError(“emoji_encoder not found”)

    if not target.exists():

        raise FileNotFoundError(“enc_emoji.png not found”)

    # 备份原始密文图,避免被 encoder 覆盖

    if not backup.exists():

        shutil.copyfile(target, backup)

    # 读取题目密文(从 backup 读,避免后续被覆盖)

    cipher_img = Image.open(backup).convert(“RGBA”)

    cipher = np.array(cipher_img, dtype=np.uint8)

    h, w = cipher.shape[:2]

    # Oracle:全 0 & 全 255

    e0 = encode_constant(binary, workdir, 0, h, w)

    eF = encode_constant(binary, workdir, 255, h, w)

    # 恢复 A 和 X

    add = ((e0.astype(np.uint16) + eF.astype(np.uint16) + 1) % 256)

    add = (add // 2).astype(np.uint8)

    xor = ((e0.astype(np.int16) – add.astype(np.int16)) % 256).astype(np.uint8)

    # 解密:P = (C – A) XOR X

    plain = ((cipher.astype(np.int16) – add.astype(np.int16)) % 256).astype(np.uint8)

    plain = np.bitwise_xor(plain, xor).astype(np.uint8)

    Image.fromarray(plain, “RGBA”).save(out)

    print(f”[+] decoded saved to: {out}”)

if __name__ == “__main__”:

    main()

LinuxChal

主要的是

这里得到一个叫ghost_bin的文件

然后在正常 Linux 环境运行就可以得到flag

exp

#!/usr/bin/env python3

from pathlib import Path

PATCH_OFF = 0x9789

OLD = 0x74  # JE rel8

NEW = 0xEB  # JMP rel8

def main():

    p = Path(“chal”)

    data = bytearray(p.read_bytes())

    if data[PATCH_OFF] != OLD:

        raise SystemExit(

            f”[!] unexpected byte at 0x{PATCH_OFF:x}: “

            f”got 0x{data[PATCH_OFF]:02x}, expected 0x{OLD:02x}”

        )

    data[PATCH_OFF] = NEW

    out = Path(“chal_patched”)

    out.write_bytes(data)

    out.chmod(0o755)

    print(f”[+] patched ok -> {out}”)

    print(“[+] run it:”)

    print(”    ./chal_patched”)

    print(”    # or: sudo ./chal_patched  (if challenge env requires)”)

if __name__ == “__main__”:

    main()

meddddgo

DIE查

GO语言的,无壳.根据Input your flag: 定位到 sub_140001A20()里面

可以看到是处理了\r和\n.

检查位置sub_1400B3740()

汇编里面test rsi, 0xf,要求了是16倍数

输出正确

return sub_1400AA4E0(41, &v6, v3, “Congratulate! The mad go flag is flag{%s}”, (const char *)1);

里面有个魔改的SM4

在sub_1400B32A0()

然后s-box是

0xD6,0x90,0xE9,0xFE,0xCC,0xE1,0x3D,0xB7,0x16,0xB6,0x14,0xC2,0x28,0xFB,0x2C,0x5,0x2B,0x67,0x9A,0x76,0x2A,0xBE,0x4,0xC3,0xAA,0x44,0x13,0x26,0x49,0x86,0x6,0x99,0x9C,0x42,0x50,0xF4,0x91,0xEF,0x98,0x7A,0x33,0x54,0xB,0x43,0xED,0xCF,0xAC,0x62,0xE4,0xB3,0x1C,0xA9,0xC9,0x8,0xE8,0x95,0x80,0xDF,0x94,0xFA,0x75,0x8F,0x3F,0xA6,0x47,0x7,0xA7,0xFC,0xF3,0x73,0x17,0xBA,0x83,0x59,0x3C,0x19,0xE6,0x85,0x4F,0xA8,0x68,0x6B,0x81,0xB2,0x71,0x64,0xDA,0x8B,0xF8,0xEB,0xF,0x4B,0x70,0x56,0x9D,0x35,0x1E,0x24,0xE,0x5E,0x63,0x58,0xD1,0xA2,0x25,0x22,0x7C,0x3B,0x1,0x21,0x78,0x87,0xD4,0x0,0x46,0x57,0x9F,0xD3,0x27,0x52,0x4C,0x36,0x2,0xE7,0xA0,0xC4,0xC8,0x9E,0xEA,0xBF,0x8A,0xD2,0x40,0xC7,0x38,0xB5,0xA3,0xF7,0xF2,0xCE,0xF9,0x61,0x15,0xA1,0xE0,0xAE,0x5D,0xA4,0x9B,0x34,0x1A,0x55,0xAD,0x93,0x32,0x30,0xF5,0x8C,0xB1,0xE3,0x1D,0xF6,0xE2,0x2E,0x82,0x66,0xCA,0x60,0xC0,0x29,0x23,0xAB,0xD,0x53,0x4E,0x6F,0xD5,0xDB,0x37,0x45,0xDE,0xFD,0x8E,0x2F,0x3,0xFF,0x6A,0x72,0x6D,0x6C,0x5B,0x51,0x8D,0x1B,0xAF,0x92,0xBB,0xDD,0xBC,0x7F,0x11,0xD9,0x5C,0x41,0x1F,0x10,0x5A,0xD8,0xA,0xC1,0x31,0x88,0xA5,0xCD,0x7B,0xBD,0x2D,0x74,0xD0,0x12,0xB8,0xE5,0xB4,0xB0,0x89,0x69,0x97,0x4A,0xC,0x96,0x77,0x7E,0x65,0xB9,0xF1,0x9,0xC5,0x6E,0xC6,0x84,0x18,0xF0,0x7D,0xEC,0x3A,0xDC,0x4D,0x20,0x79,0xEE,0x5F,0x3E,0xD7,0xCB,0x39,0x48

SM4是K[i] = MK[i] ^ FK[i]这里是K[i] = MK[i] ^ FK[i] ^ MAGIC[i]

可以从里面找到seed是10 23 45 67 89 ab cd ef 01 35 79 bd f0 22 44 66

exp是

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

import struct

# ————————

# SM4 S-Box / FK / CK

# ————————

SBOX = [

    0xd6,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7,0x16,0xb6,0x14,0xc2,0x28,0xfb,0x2c,0x05,

    0x2b,0x67,0x9a,0x76,0x2a,0xbe,0x04,0xc3,0xaa,0x44,0x13,0x26,0x49,0x86,0x06,0x99,

    0x9c,0x42,0x50,0xf4,0x91,0xef,0x98,0x7a,0x33,0x54,0x0b,0x43,0xed,0xcf,0xac,0x62,

    0xe4,0xb3,0x1c,0xa9,0xc9,0x08,0xe8,0x95,0x80,0xdf,0x94,0xfa,0x75,0x8f,0x3f,0xa6,

    0x47,0x07,0xa7,0xfc,0xf3,0x73,0x17,0xba,0x83,0x59,0x3c,0x19,0xe6,0x85,0x4f,0xa8,

    0x68,0x6b,0x81,0xb2,0x71,0x64,0xda,0x8b,0xf8,0xeb,0x0f,0x4b,0x70,0x56,0x9d,0x35,

    0x1e,0x24,0x0e,0x5e,0x63,0x58,0xd1,0xa2,0x25,0x22,0x7c,0x3b,0x01,0x21,0x78,0x87,

    0xd4,0x00,0x46,0x57,0x9f,0xd3,0x27,0x52,0x4c,0x36,0x02,0xe7,0xa0,0xc4,0xc8,0x9e,

    0xea,0xbf,0x8a,0xd2,0x40,0xc7,0x38,0xb5,0xa3,0xf7,0xf2,0xce,0xf9,0x61,0x15,0xa1,

    0xe0,0xae,0x5d,0xa4,0x9b,0x34,0x1a,0x55,0xad,0x93,0x32,0x30,0xf5,0x8c,0xb1,0xe3,

    0x1d,0xf6,0xe2,0x2e,0x82,0x66,0xca,0x60,0xc0,0x29,0x23,0xab,0x0d,0x53,0x4e,0x6f,

    0xd5,0xdb,0x37,0x45,0xde,0xfd,0x8e,0x2f,0x03,0xff,0x6a,0x72,0x6d,0x6c,0x5b,0x51,

    0x8d,0x1b,0xaf,0x92,0xbb,0xdd,0xbc,0x7f,0x11,0xd9,0x5c,0x41,0x1f,0x10,0x5a,0xd8,

    0x0a,0xc1,0x31,0x88,0xa5,0xcd,0x7b,0xbd,0x2d,0x74,0xd0,0x12,0xb8,0xe5,0xb4,0xb0,

    0x89,0x69,0x97,0x4a,0x0c,0x96,0x77,0x7e,0x65,0xb9,0xf1,0x09,0xc5,0x6e,0xc6,0x84,

    0x18,0xf0,0x7d,0xec,0x3a,0xdc,0x4d,0x20,0x79,0xee,0x5f,0x3e,0xd7,0xcb,0x39,0x48,

]

FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc]

CK = [

    0x00070e15,0x1c232a31,0x383f464d,0x545b6269,0x70777e85,0x8c939aa1,0xa8afb6bd,0xc4cbd2d9,

    0xe0e7eef5,0xfc030a11,0x181f262d,0x343b4249,0x50575e65,0x6c737a81,0x888f969d,0xa4abb2b9,

    0xc0c7ced5,0xdce3eaf1,0xf8ff060d,0x141b2229,0x30373e45,0x4c535a61,0x686f767d,0x848b9299,

    0xa0a7aeb5,0xbcc3cad1,0xd8dfe6ed,0xf4fb0209,0x10171e25,0x2c333a41,0x484f565d,0x646b7279

]

# 程序里额外 xor 的那组常量(魔改点)

MAGIC = [0xA5A5A5A5, 0x3C3C3C3C, 0x5A5A5A5A, 0xC3C3C3C3]

def rotl32(x, n):

    x &= 0xFFFFFFFF

    return ((x << n) & 0xFFFFFFFF) | (x >> (32 – n))

def tau(a):

    b = 0

    for i in range(4):

        byte = (a >> (24 – 8*i)) & 0xFF

        b = (b << 8) | SBOX[byte]

    return b

def L(b):

    return b ^ rotl32(b, 2) ^ rotl32(b, 10) ^ rotl32(b, 18) ^ rotl32(b, 24)

def Lp(b):

    return b ^ rotl32(b, 13) ^ rotl32(b, 23)

def T(x):

    return L(tau(x))

def Tp(x):

    return Lp(tau(x))

def rk_mad(key_bytes: bytes):

    ”’

    魔改 SM4 key schedule:

        K[i] = MK[i] ^ FK[i] ^ MAGIC[i]

    其余流程与标准 SM4 相同

    ”’

    MK = [struct.unpack(“>I”, key_bytes[i*4:(i+1)*4])[0] for i in range(4)]

    K = [MK[i] ^ FK[i] ^ MAGIC[i] for i in range(4)]

    rk = []

    for i in range(32):

        t = K[i+1] ^ K[i+2] ^ K[i+3] ^ CK[i]

        K.append(K[i] ^ Tp(t))

        rk.append(K[i+4])

    return rk

def sm4_encrypt_block(block16: bytes, rk):

    X = [struct.unpack(“>I”, block16[i*4:(i+1)*4])[0] for i in range(4)]

    for i in range(32):

        t = X[i+1] ^ X[i+2] ^ X[i+3] ^ rk[i]

        X.append(X[i] ^ T(t))

    Y = [X[35], X[34], X[33], X[32]]

    return b””.join(struct.pack(“>I”, y) for y in Y)

def sm4_decrypt_ecb(cipher: bytes, key_bytes: bytes) -> bytes:

    rk = rk_mad(key_bytes)

    rk_rev = list(reversed(rk))

    out = b””

    for i in range(0, len(cipher), 16):

        out += sm4_encrypt_block(cipher[i:i+16], rk_rev)

    return out

def derive_key_from_seed(seed16: bytes) -> bytes:

    ”’

    按二进制里的那段 16 字节 key 派生逻辑还原:

    – 第一轮生成 tmp[16]

    – 然后三轮原地混淆(round=0,1,2)

    ”’

    assert len(seed16) == 16

    arr = list(seed16)

    # 第一轮

    tmp = []

    for i in range(16):

        idx = (i*5 + 3) & 0xF

        b = arr[idx]

        b = ((b << 1) & 0xFF) | (b >> 7)  # rol1

        v = (11*i) ^ b ^ 0xA5

        tmp.append(v & 0xFF)

    # 三轮原地混淆

    for r in range(3):

        for j in range(16):

            old = tmp[j]

            b5 = tmp[(j + 5) & 0xF]

            b1 = tmp[(j + 1) & 0xF]

            add = (b5 + b1 + 17*r) & 0xFF

            tmp[j] = (old ^ add) & 0xFF

    return bytes(tmp)

def main():

    # 从二进制里提取到的常量

    seed = bytes.fromhex(“1023456789abcdef013579bdf0224466”)

    cipher = bytes.fromhex(

        “fc66b270e8874c9d734ecd5b766aa589”

        “6dda6cc5349d5f3b44b54aaf5ef9ce49”

    )

    key = derive_key_from_seed(seed)

    plain = sm4_decrypt_ecb(cipher, key)

    inner = plain.decode()

    print(“[+] seed :”, seed.hex())

    print(“[+] key  :”, key.hex())

    print(“[+] inner:”, inner)

    print(“[+] flag :”, f”flag{{{inner}}}”)

if __name__ == “__main__”:

    main()



评论(0)

查看评论列表

暂无评论


发表评论