[2025HNCPC]GOGOGO

直接定位逻辑

file

接下来是判断flag长度,输入41位数字,然后掐头去尾剩下36为数字,然后使用replace去掉输入里面的“-”

file

file

显然这里的算法逻辑已经为后话埋下了一个大坑,不过没问题待会儿再骂。

接下来典型的倒序算法

file

题目的主要逻辑也就是下面这一块了

file

首先取8个字节为一个块,然后采用掩码交换位置。其实可以看看这些掩码的二进制你会有所发现

file

然后再通过swap从头到尾交换一边,最后异或v36这个v36每一轮都会被ROL更新,最后再Base32编码。

file

但是这个base32是有所变化的,在下面的aPaniccalledwit中可以看到取base32码表的过程,打断点直接调试

file

可以看到如下Base32编码表:13456789ABCDEFGHJKLMNPQRSTUVWXYZ

file

接下来复现还原算法:


MASK64 = (1 << 64) - 1
CUSTOM_B32_ALPHABET = "13456789ABCDEFGHJKLMNPQRSTUVWXYZ"

def rol(x, r):
    r &= 63
    return ((x << r) & MASK64) | ((x & MASK64) >> (64 - r))

def byteswap64(x):
    return int.from_bytes(x.to_bytes(8, 'little')[::-1], 'little')

def bit_permute_steps(x):
    x = ((x >> 1) & 0x5555555555555555) | ((x & 0x5555555555555555) << 1)
    x = ((x >> 2) & 0x3333333333333333) | ((x & 0x3333333333333333) << 2)
    x = ((x >> 4) & 0x0F0F0F0F0F0F0F0F) | ((x & 0x0F0F0F0F0F0F0F0F) << 4)
    return x & MASK64

def custom_base32_encode(data: bytes) -> str:
    bits = 0
    bitlen = 0
    out = []
    for byte in data:
        bits = (bits << 8) | byte
        bitlen += 8
        while bitlen >= 5:
            bitlen -= 5
            index = (bits >> bitlen) & 0x1F
            out.append(CUSTOM_B32_ALPHABET[index])
    if bitlen > 0:
        index = (bits << (5 - bitlen)) & 0x1F
        out.append(CUSTOM_B32_ALPHABET[index])
    return ''.join(out)

def transform(inp: str) -> str:
    if len(inp) < 41:
        raise ValueError("input length must be at least 41")
    s = inp[5:5+36]
    s = s[::-1]

    out_bytes = bytearray()
    state = 0xDEADBEEFCAFEBABE

    for i in range(0, len(s), 8):
        blk = s[i:i+8].encode('latin1')
        if len(blk) < 8:
            blk = blk + b'\x00' * (8 - len(blk))
        x = int.from_bytes(blk, 'big')
        x = rol(x, 13)
        x ^= state
        x = rol(x, 32)
        v46 = bit_permute_steps(x)
        v21 = byteswap64(v46)
        state = v21 ^ rol(state, 7)
        out_bytes += (v46 & MASK64).to_bytes(8, 'little')

    b32 = custom_base32_encode(bytes(out_bytes)).upper()
    parts = [b32[i:i+6] for i in range(0, len(b32), 6)]
    return '-'.join(parts)

if __name__ == "__main__":
    test_inp = "1" * 41
    print("output:", transform(test_inp))

经过调试和程序产生的一致。

题目的算法部分就是这么简单,但是好像在代码中没有看到比较部分。

在汇编中体现:

file

接下来就是反向解密了:

from typing import Tuple

MASK64 = (1 << 64) - 1
CUSTOM_B32_ALPHABET = "13456789ABCDEFGHJKLMNPQRSTUVWXYZ"
ALPHABET_MAP = {c: i for i, c in enumerate(CUSTOM_B32_ALPHABET)}

def rol(x: int, r: int) -> int:
    r &= 63
    return ((x << r) & MASK64) | ((x & MASK64) >> (64 - r))

def byteswap64(x: int) -> int:
    return int.from_bytes(x.to_bytes(8, 'little')[::-1], 'little')

def bit_permute_steps(x: int) -> int:

    x = ((x >> 1) & 0x5555555555555555) | ((x & 0x5555555555555555) << 1)
    x &= MASK64
    x = ((x >> 2) & 0x3333333333333333) | ((x & 0x3333333333333333) << 2)
    x &= MASK64
    x = ((x >> 4) & 0x0F0F0F0F0F0F0F0F) | ((x & 0x0F0F0F0F0F0F0F0F) << 4)
    x &= MASK64
    return x

def custom_base32_decode(s: str) -> bytes:

    s = ''.join(ch for ch in s if ch not in '- \n\r\t')
    bits = 0
    bitlen = 0
    out = bytearray()
    for ch in s:
        if ch not in ALPHABET_MAP:
            raise ValueError(f"invalid base32 char: {ch!r}")
        val = ALPHABET_MAP[ch]
        bits = (bits << 5) | val
        bitlen += 5
        while bitlen >= 8:
            bitlen -= 8
            byte = (bits >> bitlen) & 0xFF
            out.append(byte)

    return bytes(out)

def inverse_transform(encoded: str) -> Tuple[str, bytes]:

    raw = custom_base32_decode(encoded)

    if len(raw) % 8 != 0:

        raw = raw + b'\x00' * (8 - (len(raw) % 8))

    nblocks = len(raw) // 8
    state = 0xDEADBEEFCAFEBABE
    recovered_bytes = bytearray()

    for i in range(nblocks):
        v46_le = raw[i*8:(i+1)*8]
        v46 = int.from_bytes(v46_le, 'little')
        x3 = bit_permute_steps(v46)
        x2 = rol(x3, 32)
        x1 = x2 ^ state
        b_be = rol(x1, 64 - 13)
        b_be &= MASK64

        blk_be = b_be.to_bytes(8, 'big')
        recovered_bytes += blk_be

        v21 = byteswap64(v46)
        state = v21 ^ rol(state, 7)
        state &= MASK64

    recovered36 = bytes(recovered_bytes[:36])

    s_recovered = recovered36[::-1].decode('latin1')
    return s_recovered, recovered36

if __name__ == "__main__":
    encoded = "LYHX68-AZF785-6QU5E9-JFZY8B-E6ZEKU-RCQ65Y-QJB76Y-37MQXQ-5UJJ"
    recovered_str, recovered_bytes = inverse_transform(encoded)
    print("Encoded input: ", encoded)
    print("Recovered 36-bytes (as latin1):", repr(recovered_str))

这题很抽象,解密出来时32位的可视字符,但是开局的时候被抹除了四根”-“这”-“应该补全在哪儿我们不得而知,只能猜测uuid格式。

file

但实际上在哪儿都是对的。

锐评就是垃圾题,flag都不收敛。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇