吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1101|回复: 13
上一主题 下一主题
收起左侧

[CTF] 吾爱破解 2026 春节所有题 WP

  [复制链接]
跳转到指定楼层
楼主
LiuXing0327 发表于 2026-3-4 14:01 回帖奖励
本帖最后由 LiuXing0327 于 2026-3-5 10:16 编辑

送分题

这个就不多言了。

Windows 初级题一

直接把程序拖进 IDA 后查看字符串发现没有明文,查一下壳。

peS2yU1.png

查壳后发现真实程序被打包进了 Resource ,那么直接使用 Resource Hacker 打开。

RCData 中找到数据开头是 4D 5A 的资源项,这是典型的 Windows PE 文件头标志。

peSRZqJ.png

把它保存为 bin 文件,然后拖进 IDA 发现有明文了。找个关键字符串列出对的交叉引用一路跟踪至关键函数 sub_401620

_BYTE *__cdecl sub_401620(int a1)
{
  _BYTE *result; // eax

  *(_DWORD *)a1 = 758280311;
  *(_DWORD *)(a1 + 4) = 1663511336;
  *(_DWORD *)(a1 + 8) = 1880974179;
  *(_DWORD *)(a1 + 12) = 494170226;
  *(_DWORD *)(a1 + 16) = 842146570;
  *(_DWORD *)(a1 + 20) = 657202491;
  *(_DWORD *)(a1 + 24) = 658185525;
  *(_BYTE *)(a1 + 30) = 99;
  *(_WORD *)(a1 + 28) = 12323;
  result = (_BYTE *)a1;
  // 对前 31 字节逐个 XOR 0x42
  do
    *result++ ^= 0x42u;
  while ( result != (_BYTE *)(a1 + 31) );
  *(_BYTE *)(a1 + 31) = 0;
  return result;
}

解密脚本:

import struct

data = [758280311, 1663511336, 1880974179, 494170226, 842146570, 657202491, 658185525]

raw = b''.join(struct.pack('<I', x) for x in data)
raw += struct.pack('<H', 12323)
raw += struct.pack('<B', 99)

flag = bytes([b ^ 0x42 for b in raw])
print(flag.decode())

得到 flag:52pojie!!!_2026_Happy_new_year!

Android 初级题

玩游戏得到 flag:flag{Wu41_P0j13_2026_Spr1ng_F3st1v4l}

Windows 初级题二

运行程序得知这个是 Python 版本的,拖进 IDA 发现是 PyInstaller 打包的。
使用 pyinstxtractor 解包,解包日志中给出了程序入口点 crackme_easy.pyc ,由于 uncompyle6 不兼容 3.9+ 以上版本,所以直接 dump 出字节码分析。

import marshal, dis  

f=open("crackme_easy.pyc","rb")  
f.read(16)  
code=marshal.load(f)  
dis.dis(code)

字节码中发现关键处 correct_flag = generate_flag(),既然 generate_flag 是生成 flag 的,那么就直接调用程序的内部函数得到 flag:52p0j!3#2026*H4ppy-N3w-Y34r@@@

import marshal, dis  

path = "crackme_easy.pyc"  

f=open(path,"rb")  
f.read(16)  
code=marshal.load(f)  
dis.dis(code)  

with open(path,"rb") as f:  
    f.read(16)  
    code = marshal.load(f)  

# 加载程序  
exec(code)  

# 直接拿 flag
print(generate_flag())

Windows 中级题一

peppuTA.png

查壳后发现这个由 Nuitka 打包使用 --onefile 编译,在 IDA 分析的过程中也证实了是使用 --onefile 标志编译。

peppdkn.png

使用 --onefile 标志,也省得提取了,直接在默认解压路径中找到关键文件 crackme_hard.dll 把它拖进 Resource Hacker 把 RCData 中唯一一个资源项保存为 bin 文件。

import re  
import pathlib  

d = pathlib.Path("RCData3.bin").read_bytes()  

for m in re.finditer(rb"[ -~]{4,}", d):  
    s = m.group()  
    print(s)  
    if b"CrackMeCore" in s or b"checksum" in s:  
        print(s.decode("latin1", "ignore"))

保存后用 Python 把 bin 文件的所有字符串扫描出来后找一下关键字,找到关键字后过滤其它字符串:

achecksum
aCrackMeCore
aget_target_checksum
uCrackMeCore.__init__
uCrackMeCore._decrypt_char
uCrackMeCore._get_char_at_position
uCrackMeCore.verify
uCrackMeCore.checksum
uCrackMeCore.get_target_checksum

这说明逻辑能通过 Nuitka 常量恢复。

IDA 分析  crackme_hard.dll 与未过滤的字符串得知 __main__ 为关键模块。

pepiWDO.png

得知关键模块还不够,还要分析 Nuitka 编译器 是怎么解析常量的。
IDA 字符串搜索 .bytecode 得到两个结果找不是 run_code 的那个函数。

run_code 不负责解析。

通过字符串定位到 sub_313410170 函数

pepMgxg.png

继续跟踪 sub_31340ECE0 ,简单分析发现这就是常量池解析函数。
编写一个对齐的解析逻辑

import struct  
from pathlib import Path  

def load_entries(path: str):  
    buf = Path(path).read_bytes()  
    marker = b".bytecode"  
    m = buf.find(marker)  
    blob = m - 8  
    crc, total = struct.unpack_from("<II", buf, blob)  
    p = blob + 8  
    end = p + total  
    entries = {}  
    while p < end:  
        z = buf.index(0, p)  
        name = buf[p:z].decode("latin1")  
        p = z + 1  

        size = struct.unpack_from("<I", buf, p)[0]  
        p += 4  

        entries[name] = buf[p:p + size]  
        p += size  
    return entries  

class Parser:  
    def __init__(self, data: bytes):  
        self.data = data  
        self.pos = 0  
        self.consts = []  

    def read_u8(self):  
        v = self.data[self.pos]  
        self.pos += 1  
        return v  

    def read_cstring(self, encoding="utf-8"):  
        z = self.data.index(0, self.pos)  
        s = self.data[self.pos:z]  
        self.pos = z + 1  
        return s.decode(encoding)  

    def read_bytes_cstring(self):  
        z = self.data.index(0, self.pos)  
        s = self.data[self.pos:z]  
        self.pos = z + 1  
        return s  

    def varint(self):  
        result = 0  
        shift = 0  

        while True:  
            v = self.read_u8()  
            result |= (v & 0x7F) << shift  
            if v < 0x80:  
                return result  
            shift += 7  

    def parse_one(self):  
        tag = chr(self.read_u8())  

        if tag == "p":  
            return self.consts[-1]  

        if tag == "L":  
            return [self.parse_one() for _ in range(self.varint())]  

        if tag == "l":  
            return self.varint()  

        if tag == "q":  
            return -self.varint()  

        if tag in ("a", "u"):  
            return self.read_cstring()  

        if tag == "b":  
            n = self.varint()  
            s = self.data[self.pos:self.pos + n]  
            self.pos += n  
            return s  

        if tag == "c":  
            return self.read_bytes_cstring()  

        if tag == "d":  
            return bytes([self.read_u8()])  

        raise RuntimeError(f"Unsupported tag: {tag!r}")  

    def parse_consts(self, n):  
        self.pos += 2  

        for _ in range(n):  
            v = self.parse_one()  
            self.consts.append(v)  

        return self.consts  

def main():  
    entries = load_entries("RCData323.bin")  

    main_blob = entries["__main__"]  

    parser = Parser(main_blob)  
    consts = parser.parse_consts(5)  

    parts, name, key, key_name, total_len = consts  

    print(f"name      : {name}")  
    print(f"key       : {key}")  
    print(f"key_name  : {key_name}")  
    print(f"total_len : {total_len}")  

    print("parts hex :", [p.hex() for p in parts])

解析出关键数据:

name      : _parts
key       : 81
key_name  : _key
total_len : 30
parts hex : ['646321613b6062', '11', '63616367', '2f', '1965212128', '0e', '1f6226', '0e', '08626523', '707070']

再加上题目提示:1337 5p34k & 5ymb0l5!_decrypt_char 函数以及存在 key。
在解析脚本添加:

def xor_join(parts, key):  
    return ''.join(  
        chr(b ^ key)  
        for chunk in parts  
        for b in chunk  
    )  

在解析脚本的 main 函数中添加:

result = xor_join(parts, 81)  
print(result)

得到 flag:52p0j13@2026~H4ppy_N3w_Y34r!!!

番外篇 初级题

玩游戏得到 flag:flag{52pojie_2026_Happy_New_Year! >w<}

Windows 中级题二

把程序拖进 IDA 发现添加了 UPX 壳,可以自动脱壳,不用操心去手脱。
脱壳后拖进 IDA 打开字符串内容不多,一眼看过去就发现了关键。
通过字符串定位到了主对话框函数,在对话框函数中发现 sub_140008720 函数获取了用户输入的字符串。

pep4Xdg.png

sub_140008720 函数中发现了文件解密的逻辑。

pep5Eo4.png

进入 sub_1400081E0 函数中发现有两个子函数,将这两个函数分析后得知其作用是数据变换(sub_140008080)和 CRC 比较(sub_140008480)。

分析 sub_140008080 还原出每块运算:

  • plain[i] = cipher[i] ^ keystream[i] ^ feedback[i]
  • 第一块的 feedback 来自文件头里的 IV(sub_140008310 读入)
  • 每处理完一块后 feedback = 当前密文块(下一块用)

返回 sub_140008720 继续分析 sub_140008310

__int64 __fastcall sub_140008310(__int64 a1, __int64 a2, _DWORD *a3)
{
  __int64 result; // rax
  int v6; // eax

  result = 0;
  if ( a3 )
  {
    if ( *a3 == 909266243 )
    {
      sub_140008360(a1, a2, a3 + 2);
      v6 = a3[1];
      *(_DWORD *)(a1 + 288) = -1;
      *(_DWORD *)(a1 + 292) = v6;
      return 1;
    }
  }
  return result;
}

函数中有魔数 0x36324D43 的检查,在确认文件头有效后调用 sub_140008360

void *__fastcall sub_140008360(_QWORD *a1, __int64 a2, __int64 a3)
{
  __int64 v3; // rax

  v3 = 0;
  *a1 = a2;
  do
  {
    *((_BYTE *)a1 + v3 + 16) = *(_BYTE *)(a3 + v3);
    ++v3;
  }
  while ( v3 != 16 );
  return memcpy(a1 + 4, &unk_14000A270, 0x100u);
}

说明程序会 unk_14000A270 的 256 字节表复制到 a1 使用,用 IDA 把 0x4000A270 开始的 0x100 字节 dump 出来。

dump 出来后,编写解密脚本:

import struct, zlib, re  
from pathlib import Path  

enc = Path("flag.png.encrypted").read_bytes()  
assert enc[:4] == b"CM26"  
crc_expect = struct.unpack_from("<I", enc, 4)[0]  
iv, ct = enc[8:16], enc[16:]  
s = bytes.fromhex(  
    "637c777bf26b6fc53001672bfed7ab76ca82c97dfa5947f0add4a2af9ca472c0"  
    "b7fd9326363ff7cc34a5e5f171d8311504c723c31896059a071280e2eb27b275"    "09832c1a1b6e5aa0523bd6b329e32f8453d100ed20fcb15b6acbbe394a4c58cf"    "d0efaafb434d338545f9027f503c9fa851a3408f929d38f5bcb6da2110fff3d2"    "cd0c13ec5f974417c4a77e3d645d197360814fdc222a908846eeb814de5e0bdb"    "e0323a0a4906245cc2d3ac629195e479e7c8376d8dd54ea96c56f4ea657aae08"    "ba78252e1ca6b4c6e8dd741f4bbd8b8a703eb5664803f60e613557b986c11d9e"    "e1f8981169d98e949b1e87e9ce5528df8ca1890dbfe6426841992d0fb054bb16")  
inv = [0] * 256  
for i, b in enumerate(s): inv[b] = i  

def rol64(x, n): return ((x << n) & 0xffffffffffffffff) | (x >> (64 - n))  

def ror64(x, n): return (x >> n) | ((x << (64 - n)) & 0xffffffffffffffff)  

def f(k):  
    x = rol64(k, 3)  
    for _ in range(8): x = ((x << 8) & 0xffffffffffffffff) | s[(x >> 56) & 0xff]  
    return x  

def f_inv(y):  
    t = int.from_bytes(bytes(inv[b] for b in y.to_bytes(8, "big")), "big")  
    return ror64(t, 3)  

png = b"\x89PNG\r\n\x1a\n"  
state0 = int.from_bytes(bytes(ct[i] ^ png[i] ^ iv[i] for i in range(8)), "little")  
key = f_inv(state0)  

fb = bytearray(iv)  
out = bytearray()  
crc = 0xffffffff  
for off in range(0, len(ct), 8):  
    c = ct[off:off + 8]  
    st = f(key)  
    key = st  
    ks = st.to_bytes(8, "little")  
    p = bytes(c[i] ^ ks[i] ^ fb[i] for i in range(8))  
    fb[:] = c  
    out += p  
    crc = zlib.crc32(p, crc)  

pad = out[-1]  
pt = bytes(out[:-pad]) if 0 < pad <= 8 else bytes(out)  
Path("flag.png").write_bytes(pt)  

m = re.search(rb"flag\{[^}]+}", pt)  
print(m.group().decode())

得到 flag:flag{EncrypTIoN_Is_haRd_52p0jIE_2o26_m62Tc4uj78maAq1C}

Android 中级题

通过 Java 层分析确认两件事:

  1. assets/hjm_pack.bin 是关键资源。
  2. 输入字符串会进入 NativeBridge.verifyAndDecrypt(),其返回字节后续会被 h1.a.S(...) 解析。
  3. 成功判定点是:h1.a.S((byte[]) obj) == null 时走“验证成功”。

拖入 IDA 后确认:

Java 方法 Native 偏移
startSessionBytes 0x247b0
checkRhythm 0x24da8
updateExp 0x24ea4
decryptFrames 0x2541c
verifyAndDecrypt 0x257dc
setDebugBypass 0x25c90

进入 verifyAndDecrypt 后发现不是直接字符串比较,而是:

  1. 解包得到目标位图
  2. 将输入字符串转位图
  3. 比较位图

进一步分析得到关键函数:

  • sub_2dcdc:key 生成
  • sub_2ddf8:version=2 解包核心
  • 输出改写后的 pack 数据并还原位图读文本

Unicorn 中跑最小链路,直接执行:

sub_2dcdc
sub_2ddf8

拿到改写后的 pack 数据。
分析前 4 个字节:48 4A 4D 31 ,即:HJM1

解析脚本:

import struct  
from pathlib import Path  

FILE_PATH = "pack.dec.bin"  

def unpack_1bpp(frame: bytes, w: int, h: int) -> bytes:  
    out = bytearray(w * h)  
    for i in range(w * h):  
        b = frame[i >> 3]  
        out[i] = (b >> (7 - (i & 7))) & 1  
    return bytes(out)  

def render(bits: bytes, w: int, h: int, on="#", off=" "):  
    for y in range(h):  
        row = bits[y * w:(y + 1) * w]  
        print("".join(on if p else off for p in row))  

def main():  
    buf = Path(FILE_PATH).read_bytes()  
    if buf[:4] != b"HJM1":  
        raise ValueError(f"not HJM1 file, len={len(buf)}")  

    ver, frames, w, h = struct.unpack("<4I", buf[4:20])  
    frame_bytes = (w * h + 7) // 8  
    need = frames * frame_bytes  
    if len(buf) < need:  
        raise ValueError(f"file too short, len={len(buf)}, need={need}")  

    payload_off = len(buf) - need  
    frame0 = buf[payload_off:payload_off + frame_bytes]  

    print(f"HJM1 v{ver}, frames={frames}, w={w}, h={h}, payload_off=0x{payload_off:x}")  
    bits = unpack_1bpp(frame0, w, h)  
    render(bits, w, h)  

if __name__ == "__main__":  
    main()

得到 flag:FLAG{HJMWAPJ2026NBLD}

Web 中级题

先看 verify.js

  1. 点击“生成验证码”会调用 wasm_bindgen.gen(uid, voice)
  2. 生成后会把 currentHash 赋值为 challenge.h。
  3. 点击“提交”时执行 checkCode(code, currentHash)
  4. checkCode 逻辑是:code 连续做 0x2026 次 SHA-256,再和 currentHash 比较。
  5. WASM 侧随机输入来自 crypto.getRandomValues,长度固定为 17 字节(可 hook 验证)。

所以核心不是听语音,而是拿到当前轮次的 code,即可构造 flag{code}。

生成验证码:

 const challenge = wasm_bindgen.gen(uid, voice)
 currentHash = challenge.h

提交:

checkCode(code, currentHash)

关键常量从 wasm.memory.buffer 读取:

  1. 映射表:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789?!。
  2. 14 字节 key:00 01 01 01 01 01 01 00 01 00 01 00 05 02。

一把梭:

(() => {
  const keyBytes = Uint8Array.from([0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x01,0x00,0x01,0x00,0x05,0x02]);
  const table = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789?!";
  let lastRand = null;

  if (!window.__grv_hooked__) {
    const orig = crypto.getRandomValues.bind(crypto);
    crypto.getRandomValues = (arr) => {
      const ret = orig(arr);
      if (arr && arr.length === 17) lastRand = new Uint8Array(arr);
      return ret;
    };
    window.__grv_hooked__ = true;
  }

  function decodeFromJ(j) {
    let a = 0, f = 1, b = 0, h = 0, c = 0, i = 0, l = 0;
    const out = [];
    while (true) {
      const t0 = j[f - 1];
      l = t0;
      h = (l | (h << 8));
      c = b;
      while (true) {
        i = (h >> (b = c + 2)) & 63;
        out.push(table[i]);
        a++;
        c -= 6;
        if (b > 5) continue;
        break;
      }
      b = c + 8;
      const cont = f !== 37;
      f += cont ? 1 : 0;
      if (!cont) break;
    }
    if (c !== -8) out.push(table[(l << (-2 - c)) & 63]);
    return out.join("");
  }

  async function hmac16(first21) {
    const key = await crypto.subtle.importKey("raw", keyBytes, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
    const sig = await crypto.subtle.sign("HMAC", key, first21);
    return new Uint8Array(sig).slice(0, 16);
  }

  window.genFlag = async (uid = 2355817, voice = "c") => {
    document.getElementById("uid").value = String(uid);
    document.getElementById("voice").value = voice;

    lastRand = null;
    document.getElementById("checkbox-text").click();
    await new Promise(r => setTimeout(r, 1200));
    if (!lastRand) throw new Error("No random number captured");

    const j = new Uint8Array(37);
    j[0] = lastRand[0] ^ (uid & 0xff);
    j[1] = lastRand[1] ^ ((uid >>> 8) & 0xff);
    j[2] = lastRand[2] ^ ((uid >>> 16) & 0xff);
    j[3] = lastRand[3] ^ ((uid >>> 24) & 0xff);
    j.set(lastRand.slice(0, 8), 4);
    j.set(lastRand.slice(8, 16), 12);
    j[20] = lastRand[16];
    j.set(await hmac16(j.slice(0, 21)), 21);

    const code = decodeFromJ(j);
    const flag = `flag{${code}}`;
    document.getElementById("verifyInput").value = flag;
    return { flag, code };
  };
})();

使用:

await genFlag(uid, "c")

Windows 高级题

最折磨我的一题。

一开始我发现有 UPX 壳,想着先自动脱壳试试,结果脱下来了。
我还纳闷,拖进 IDA 发现,原来是控制流混淆等着我呢。

打开字符串果然加密了,但是我在 Imports 中找到了 WideCharToMultiByte 函数,xref 定位到 sub_1400CD490 验证入口。

分析入口得到关键函数:

  1. sub_1400CF090:把输入 flag 的 hex 文本转字节。
  2. sub_1400CF270:长度校验(必须是 0x40 字节)。
  3. sub_1400CF910:核心校验调度(含反调试)。
  4. sub_1400FD790:目标值生成(混淆太严重)。
  5. sub_1400D3B20:最终 64 字节比较点。

sub_1400CD490

pe9hMRI.png

sub_1400CF090 汇编里面明确看到:

  1. 先把输入的长度除以 2(每两位 hex 组成 1 字节)
  2. 分支判断 0-9 / a-f / A-F。
  3. 组合方式为 (high << 4) | low 写入输出缓冲。

pe9hQzt.png

loc_1400CF0F5:
shl     bpl, 4
or      bpl, dl
cmp     rax, rdi
jz      short loc_1400CF140

pe9hKJA.png

sub_1400CF270 虽然有点混淆,但关键点非常直白。长度必须是 0x40 字节,结合前面分析得到:

解码后长度必须是 = 64 字节
输入长度必须是 = 128 个 hex 字符

pe9h3sf.png

sub_1400CF910 汇编内可见:

  1. 多处反调试。
  2. 目标缓冲生成后进入最终比较调用。
  3. 比较前明确 mov r8d, 40h,随后 call sub_1400D3B20

pe9hJeS.png

pe9h8L8.png

结论:输入的 flag 必须是 128 个 hex 字符(解码后 64 字节)。

知道这些信息后,就无需再做静态分析,直接上 x64dbg 动态调试。
前面分析知道是有反调试的,但 ScyllaHide 基本能过掉,也不用操心。
直接在最终比较点 sub_1400D3B20 下断点。程序跑起来后输入uid 和错的 flag(128 hex),点击验证 flag,程序断在比较函数。
此时在寄存器 RCX/RDX -> 在转储中跟随,RCX是刚才输入的,RDX是真实的 flag。

pe9e2CD.png

比如我的 uid 是:2355817 ,那么对应 flag 就是:06401594023537c80f8cffede100b0fd4cb3bb4c6feb890bc8dabafd24f68d92b16084f2ae4ea6cc3567eea9a91e90c364e7f620407304b820ac44ccb6db6987

注册机:

import frida  
import glob  
import time  

# 你的 uid
uid = "uid"  

targets = glob.glob(  
    "【2026春节】解题领红包之十 {Windows 高级题} 出题老师:Poner.exe")  
if not targets:  
    raise SystemExit("target exe not found")  
target = targets[0]  

js = r'''  
const base = Process.enumerateModules()[0].base;  
function p(off){ return base.add(off); }  

const f_cd490 = new NativeFunction(p(0xCD490), 'uint64', ['pointer','pointer']);  
const g_b418 = p(0x2632418);  
const g_b419 = p(0x2632419);  

let lastY = '';  

function toHex(ptr, n){  
  const u = new Uint8Array(ptr.readByteArray(n));  let s = '';  for (let i = 0; i < u.length; i++) {    let h = u[i].toString(16);    if (h.length < 2) h = '0' + h;    s += h;  }  return s;}  

Interceptor.attach(p(0xD3B20), {  
  onEnter(args){    lastY = toHex(args[1], 64);  }});  

Interceptor.attach(p(0x0C1B90), { onLeave(ret){ ret.replace(ptr(0)); } });  
Interceptor.attach(p(0x09B30),  { onLeave(ret){ ret.replace(ptr(0)); } });  

rpc.exports = {  
  derive(uid){    g_b418.writeU8(0);    g_b419.writeU8(0);    lastY = '';    const pUid = Memory.allocUtf16String(uid);    const pDummy = Memory.allocUtf16String('00'.repeat(64));    f_cd490(pUid, pDummy);    return lastY;  },  verify(uid, flag){    g_b418.writeU8(0);    g_b419.writeU8(0);    const pUid = Memory.allocUtf16String(uid);    const pFlag = Memory.allocUtf16String(flag);    return f_cd490(pUid, pFlag).toString();  }};  
'''  

pid = frida.spawn([target])  
session = frida.attach(pid)  
script = session.create_script(js)  
script.load()  
frida.resume(pid)  
time.sleep(1)  

api = script.exports_sync  
flag = api.derive(uid)  
ret = api.verify(uid, flag)  

print("uid =", uid)  
print("flag =", flag)  
print("verify_ret =", ret)  

frida.kill(pid)

MCP 中级题

提示词发给 AI :

ctf_request 填的是口令本身,不是 access_token;  
audit_log_id 一定要用“被拒访问时返回的完整编号”,别截断别改;  
而且只认“同一会话里最近那次拒绝”出来的编号,跨会话或旧编号都不行;  
复核这段链路别配太杂,越单一越不容易断;  
复核凭据是有时效、且一次性的,失败后要重新触发拒绝再拿新编号;  
另外,复核阶段拿到的凭据只是打通流程,最终读密卷还需要后续凭据。

最终跑出 flag:flag{new_year_2026_keep_warm}

免费评分

参与人数 3吾爱币 +3 热心值 +3 收起 理由
ftasy + 1 + 1 我很赞同!
ABuSiDeLuoYin + 1 + 1 是高级题,膜拜大佬
hhxxhg + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
Hmily 发表于 2026-3-5 16:41
通杀选手太强了。。。

3#
flying1008 发表于 2026-3-5 16:54
4#
lij0210 发表于 2026-3-5 17:36
5#
w16387369 发表于 2026-3-6 08:12
通杀选手很强!
6#
zhaozhenzj 发表于 2026-3-6 08:50
没看懂 先赞一个
7#
cxkd 发表于 2026-3-6 11:53
条理清楚
8#
wpd 发表于 2026-3-6 13:57
学习一下
9#
ldx090 发表于 2026-3-6 15:26
学习,学习了。
10#
Rabbit1124 发表于 2026-3-6 16:58
妥妥的技术贴!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-3-10 03:25

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表