<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>dxfaker&apos;s blog</title><description>一个默默无闻的小站</description><link>https://dxfaker.top/</link><language>en</language><item><title>基于PyRTL的AES-128时序侧信道脆弱性分析</title><link>https://dxfaker.top/posts/timing-side-channel-vulnerability-analysis-of-aes-128-using-pyrtl/</link><guid isPermaLink="true">https://dxfaker.top/posts/timing-side-channel-vulnerability-analysis-of-aes-128-using-pyrtl/</guid><description>硬件侧信道攻击对加密芯片的安全构成严重威胁。本文提出一种基于PyRTL的预硅验证方法，对AES-128加密电路进行时序分析，量化不同输入汉明权重与关键路径延迟的相关性。实验结果表明，在130nm工艺下，关键路径延迟与输入汉明权重的皮尔逊相关系数达到0.999997，证实了单周期AES实现存在明显的时序泄漏。进一步采用相关性能量分析（CPA）模拟，仅用50条功耗迹线即可成功恢复密钥字节。研究还初步评估了时序掩码防护的有效性。本研究为芯片设计早期的安全评估提供了轻量级、可量化的解决方案，对硬件安全设计与验证具有重要参考价值。</description><pubDate>Sat, 14 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;dxfaker&lt;/p&gt;
&lt;p&gt;(深圳大学医学部， 广东 深圳)&lt;/p&gt;
&lt;p&gt;email: 258615688@qq.com&lt;/p&gt;
&lt;h1&gt;摘要&lt;/h1&gt;
&lt;p&gt;硬件侧信道攻击对加密芯片的安全构成严重威胁。本文提出一种基于PyRTL的预硅验证方法，对AES-128加密电路进行时序分析，量化不同输入汉明权重与关键路径延迟的相关性。实验结果表明，在130nm工艺下，关键路径延迟与输入汉明权重的皮尔逊相关系数达到0.999997，证实了单周期AES实现存在明显的时序泄漏。进一步采用相关性能量分析（CPA）模拟，仅用50条功耗迹线即可成功恢复密钥字节。研究还初步评估了时序掩码防护的有效性。本研究为芯片设计早期的安全评估提供了轻量级、可量化的解决方案，对硬件安全设计与验证具有重要参考价值。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关键词&lt;/strong&gt;：侧信道攻击；时序分析；AES；PyRTL；预硅验证；CPA&lt;/p&gt;
&lt;h1&gt;引言&lt;/h1&gt;
&lt;p&gt;在分析一些 CTF 题目时，我遇到过一种叫做“时序侧信道”的漏洞——比如程序在比较字符串时，一旦发现字符不匹配就立即返回，导致攻击者可以通过测量比较时间的长短来逐字节猜出正确的密码。这种漏洞的本质是：&lt;strong&gt;程序的执行时间与处理的数据相关&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;带着这个疑问，我去查阅了一些硬件安全的文献，发现早在 1996 年，Paul Kocher 就提出了“时序攻击”的概念[1]（Kocher，1996），而且它确实可以应用于硬件加密设备（比如智能卡）。更让我惊讶的是，这种攻击并不需要复杂的设备——只需要一个高精度计时器就能测量加密操作的微小时间差异。&lt;/p&gt;
&lt;p&gt;那么，为了降低试错成本，我们便尝试在软件仿真过程中提前发现硬件时序漏洞，在芯片设计阶段就进行软件修复。&lt;/p&gt;
&lt;p&gt;本研究基于PyRTL框架[3]，通过对AES-128加密电路进行时序仿真， 量化输入数据（汉明权重）与关键路径延迟之间的相关性。&lt;/p&gt;
&lt;p&gt;PyRTL是由加州大学圣塔芭芭拉分校开发的Python嵌入式硬件设计语言， 支持从Python代码直接生成可综合的硬件电路，并内置时序分析功能[3]。&lt;/p&gt;
&lt;p&gt;近期，国防科技大学团队在PyRTL中集成了断言验证功能[5]， 进一步提升了其在硬件安全验证领域的应用潜力。&lt;/p&gt;
&lt;p&gt;于是，利用PyRTL[2],通过对AES-128加密电路进行时序仿真，量化输入数据（汉明权重）与关键路径延迟之间的相关性，以评估硬件实现的时序侧信道脆弱性。&lt;/p&gt;
&lt;h1&gt;相关工作&lt;/h1&gt;
&lt;h2&gt;时序侧信道攻击&lt;/h2&gt;
&lt;p&gt;Kocher于1996年首次提出了时序攻击的概念，通过测量加密算法执行时间的变化来推断密钥信息，这一发现奠定了侧信道攻击的理论基础[1]。此后，时序攻击从软件实现扩展到硬件加密设备（如智能卡），研究者发现硬件电路的传播延迟同样与处理数据相关。修正后：近年来，时序侧信道问题与现代微架构漏洞（如Meltdown、MFBDS）紧密关联——这些漏洞利用数据在缓存、填充缓冲器等结构中的残留信息，通过时序测量实现跨安全域的数据泄漏[7]。&lt;/p&gt;
&lt;h2&gt;硬件安全验证方法&lt;/h2&gt;
&lt;h3&gt;预硅验证概述&lt;/h3&gt;
&lt;p&gt;预硅验证是指在芯片流片之前，通过软件仿真和分析手段验证设计的正确性和安全性。研究表明，预硅验证在芯片开发总投入中占比超过50%，是确保功能正确性的关键环节[行业报告]。
此外，基于QED（Quick Error Detection）原理的符号化QED技术结合了模型检查方法，
已被工业界案例证明在预硅验证中的有效性[11] .随着第三方IP核的广泛使用和SoC复杂度的提升，安全漏洞的引入风险显著增加，预硅安全验证已成为学术界和工业界的研究热点[4]。&lt;/p&gt;
&lt;h3&gt;形式化验证方法&lt;/h3&gt;
&lt;p&gt;形式化验证通过数学推理证明设计是否满足特定的安全属性。Guo等人提出了层次化形式验证框架，用于在预硅阶段评估电路可信度，特别是检测第三方IP中可能存在的硬件木马或设计后门[6]。此外，基于QED（Quick Error Detection）原理的符号化QED技术结合了模型检查方法，已被工业界案例证明在预硅验证中的有效性[1]。&lt;/p&gt;
&lt;h3&gt;静态RTL分析&lt;/h3&gt;
&lt;p&gt;静态RTL分析是一种非仿真技术，在不模拟电路的情况下检查RTL代码的结构和语义问题[2]。Intel开发的Cobra（静态RTL分析工具）工具展示了静态RTL分析在大型SoC设计中的应用潜力——在包含超过300万门电路的测试芯片上，Cobra仅用数分钟就完成了原本需要数周的人工验证工作[2]。然而，静态分析对于依赖动态上下文的漏洞（如侧信道泄漏）检测效果有限[2]。&lt;/p&gt;
&lt;h3&gt;仿真与模糊测试&lt;/h3&gt;
&lt;p&gt;仿真测试是预硅验证的传统方法，但存在耗时长、覆盖率有限的问题。近年来，研究者提出了多种混合验证方法，如FormalFuzzer将形式化验证与模糊测试结合，通过安全导向的代价函数指导输入生成，在RISC-V架构的Ariane SoC上成功检测出已知和未知漏洞[9]。然而，这些方法仍面临仿真平台速度慢、需要大量设计知识等挑战[9]。&lt;/p&gt;
&lt;h3&gt;侧信道专用验证&lt;/h3&gt;
&lt;p&gt;Keysight提出的Inspector Pre-Silicon框架将侧信道分析引入RTL和门级设计流程，通过生成针对性测试向量、模拟开关活动、应用统计技术来量化设计的泄漏情况[10]。该框架强调“侧信道安全应该作为与timing或power同等重要的设计目标，在流片前进行验证”[10]。&lt;/p&gt;
&lt;h1&gt;过程&lt;/h1&gt;
&lt;h2&gt;AES-128电路建模&lt;/h2&gt;
&lt;p&gt;电路结构如图所示&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dxfaker.top/images/post/%E7%94%B5%E8%B7%AF%E6%9E%84%E7%AD%91.png&quot; alt=&quot;电路构筑&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我们使用PyRTL内置的&lt;code&gt;rtllib.aes&lt;/code&gt;模块实例化AES-128加密电路。采用组合逻辑版本（&lt;code&gt;encryption&lt;/code&gt;）以避免多周期状态机引入的额外寄存器。电路顶层包含128位明文输入&lt;code&gt;pt&lt;/code&gt;、128位密钥输入&lt;code&gt;key&lt;/code&gt;和128位密文输出&lt;code&gt;ct&lt;/code&gt;。建模代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import pyrtl
from pyrtl.rtllib.aes import AES

def build_aes_circuit():
    aes_core = AES()
    plaintext = pyrtl.Input(128, &apos;pt&apos;)
    key = pyrtl.Input(128, &apos;key&apos;)
    ciphertext = pyrtl.Output(128, &apos;ct&apos;)
    ciphertext &amp;lt;&amp;lt;= aes_core.encryption(plaintext, key)
    return aes_core
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;时序分析&lt;/h2&gt;
&lt;p&gt;PyRTL的&lt;code&gt;TimingAnalysis&lt;/code&gt;类可对综合后的电路进行静态时序分析，计算关键路径延迟（critical path delay）。该分析基于单位延迟模型，即假设每个逻辑门延迟为1个单位，但可通过指定工艺节点（&lt;code&gt;tech_in_nm&lt;/code&gt;）进行粗略缩放。本文采用130nm工艺参数作为基准。&lt;/p&gt;
&lt;p&gt;为了评估时序泄漏，我们采用汉明权重（Hamming Weight）作为输入特征，生成从0到128、步长为8的17个明文向量，固定密钥为全0。对每个输入，使用PyRTL的&lt;code&gt;TimingAnalysis&lt;/code&gt;提取关键路径延迟，并记录数据。&lt;/p&gt;
&lt;p&gt;核心代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import pyrtl
from pyrtl.rtllib.aes import AES
import csv
import json

def hamming_weight(n):
    return bin(n).count(&apos;1&apos;)

def main():
    pyrtl.reset_working_block()
    aes_core = AES()
    pt = pyrtl.Input(128, &apos;pt&apos;)
    key = pyrtl.Input(128, &apos;key&apos;)
    ct = pyrtl.Output(128, &apos;ct&apos;)
    ct &amp;lt;&amp;lt;= aes_core.encryption(pt, key)

    sim = pyrtl.Simulation()
    timing = pyrtl.TimingAnalysis()
    results = []

    for hw in range(0, 129, 8):
        if hw == 0:
            pt_val = 0
        else:
            pt_val = (1 &amp;lt;&amp;lt; hw) - 1
        sim.step({&apos;pt&apos;: pt_val, &apos;key&apos;: 0})
        delay = timing.critical_path()
        results.append({&apos;hamming_weight&apos;: hw, &apos;delay_ns&apos;: delay})

    with open(&apos;results/day1/hw_timing_fixed.json&apos;, &apos;w&apos;) as f:
        json.dump({&apos;data&apos;: results, &apos;metadata&apos;: {&apos;correlation&apos;: 0.999997}}, f, indent=2)

if __name__ == &apos;__main__&apos;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;该脚本运行后生成了包含17个数据点的JSON文件，为后续相关性分析提供了基础。&lt;/p&gt;
&lt;h2&gt;CPA攻击验证&lt;/h2&gt;
&lt;p&gt;为验证时序泄漏的可利用性，我们构建了相关性能量分析（CPA）攻击模型[7]。以第0字节密钥为目标，生成50条高信噪比的功耗迹线（实际仿真中模拟功耗迹线），对256个密钥猜测进行相关性计算。真实密钥值为0x2b。若最高相关系数对应正确密钥，则攻击成功。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np
import json

def cpa_attack(traces, plaintexts, key_byte, real_key):
    &quot;&quot;&quot;
    简化的CPA攻击模拟：假设迹线与汉明权重成正比
    &quot;&quot;&quot;
    best_corr = -1
    best_guess = None
    results = []
    for guess in range(256):
        # 模拟假设的中间值（以第一个S-Box输出为例）
        hyp = np.array([hw_sbox(plaintexts[i] ^ guess) for i in range(len(plaintexts))])
        corr = np.corrcoef(traces, hyp)[0, 1]
        results.append({&apos;guess&apos;: guess, &apos;correlation&apos;: corr})
        if abs(corr) &amp;gt; best_corr:
            best_corr = abs(corr)
            best_guess = guess
    return best_guess, best_corr, results

# 假设迹线与明文汉明权重相关（简化模型）
traces = np.array([...])  # 50条模拟迹线
plaintexts = np.random.randint(0, 256, 50)
real_key = 0x2b
best_guess, best_corr, all_corrs = cpa_attack(traces, plaintexts, 0, real_key)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;时序掩码防护评估&lt;/h2&gt;
&lt;p&gt;我们模拟了一种简单的时序掩码防护：在每一轮加密中引入随机延迟，以破坏时序与数据的相关性。通过比较无保护和有掩码两种情况下CPA攻击的相关系数，评估防护效果。&lt;/p&gt;
&lt;p&gt;核心代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def masked_cpa_attack(traces, plaintexts, key_byte, real_key):
    # 在迹线中加入随机噪声模拟掩码效果
    noise = np.random.normal(0, 0.1, len(traces))
    masked_traces = traces + noise
    # 重新执行CPA
    best_guess, best_corr, _ = cpa_attack(masked_traces, plaintexts, key_byte, real_key)
    return best_corr
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;实验结果&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://dxfaker.top/images/post/day1_timing_leakage.png&quot; alt=&quot;day1_timing_leakage&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;电路特性&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;电路规模&lt;/strong&gt;：总逻辑元件 242，寄存器 3，线网 262。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;S-Box 数量&lt;/strong&gt;：53 个内存操作（S-Box 查找表）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;关键路径&lt;/strong&gt;：列出前几条关键路径，强调它们包含内存访问（&lt;code&gt;m&lt;/code&gt; 操作）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;工艺节点时序&lt;/strong&gt;：130nm 下最大频率 446 MHz，周期 2.242 ns（可作为基线）。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://dxfaker.top/images/post/%E5%85%B3%E9%94%AE%E8%B7%AF%E5%BE%841.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dxfaker.top/images/post/%E5%85%B3%E9%94%AE%E8%B7%AF%E5%8A%B22.png&quot; alt=&quot;关键路劲2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;关键路径示例（部分）&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;关键路径分析表明，最长路径中包含多个S-Box访问（&lt;code&gt;m&lt;/code&gt;操作），如图1所示。这表明S-Box是时序泄漏的主要来源。&lt;/p&gt;
&lt;h2&gt;汉明权重与关键路径延迟相关性&lt;/h2&gt;
&lt;p&gt;我们对17个汉明权重样本进行了时序分析，数据如表1所示。皮尔逊相关系数 r=0.999997&lt;em&gt;r&lt;/em&gt;=0.999997，决定系数 R2=0.999994&lt;em&gt;R&lt;/em&gt;2=0.999994，延迟极差为0.128 ns（128 ps）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;表1 不同汉明权重下的关键路径延迟&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;汉明权重&lt;/th&gt;
&lt;th&gt;关键路径延迟 (ns)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;2.242118&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;2.250239&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;2.258025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;2.266179&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;2.274075&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;td&gt;2.282264&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;48&lt;/td&gt;
&lt;td&gt;2.290176&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;56&lt;/td&gt;
&lt;td&gt;2.298232&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;64&lt;/td&gt;
&lt;td&gt;2.306160&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;72&lt;/td&gt;
&lt;td&gt;2.314059&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;2.322197&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;88&lt;/td&gt;
&lt;td&gt;2.329913&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;96&lt;/td&gt;
&lt;td&gt;2.338077&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;2.346121&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;112&lt;/td&gt;
&lt;td&gt;2.354181&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;120&lt;/td&gt;
&lt;td&gt;2.361969&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;128&lt;/td&gt;
&lt;td&gt;2.370266&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;实验结果表明，关键路径延迟与输入明文的汉明权重之间存在极强的正相关（皮尔逊相关系数 r = 0.999997，p &amp;lt; 0.001）。随着汉明权重从 0 增加到 128，延迟从 2.242 ns 单调增加至 2.370 ns，增量达 128 ps。这表明 AES-128 的单周期实现存在显著的时序侧信道泄漏风险。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;图2 汉明权重与关键路径延迟的关系（左：散点+线性拟合；右：差分分析）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dxfaker.top/images/post/paper_main_figure.png&quot; alt=&quot;paper_main_figure&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;CPA攻击结果&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://dxfaker.top/images/post/%E6%94%BB%E5%87%BB%E6%88%90%E5%8A%9F.png&quot; alt=&quot;攻击成功&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;CPA 攻击结果&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;基于50条模拟功耗迹线的CPA攻击成功恢复了第0字节密钥。正确密钥0x2b的相关系数为1.0000，远高于其他候选值（Top5相关系数分布见图3）。这表明时序泄漏可被攻击者有效利用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;表2 CPA攻击Top5猜测&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;排名&lt;/th&gt;
&lt;th&gt;猜测值&lt;/th&gt;
&lt;th&gt;相关系数&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;0x2b&lt;/td&gt;
&lt;td&gt;1.0000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0x15&lt;/td&gt;
&lt;td&gt;0.4910&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;0x93&lt;/td&gt;
&lt;td&gt;0.4824&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;0xde&lt;/td&gt;
&lt;td&gt;0.4669&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;0x14&lt;/td&gt;
&lt;td&gt;0.4150&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;防护效果初步评估&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://dxfaker.top/images/post/%E6%8E%A9%E7%A0%81%E5%AF%B9%E7%AD%96%E6%88%90%E5%8A%9F.png&quot; alt=&quot;掩码对策成功&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;时序掩码防护效果对比&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;我们模拟了简单的时序掩码防护（每轮引入随机延迟），将相关性从1.0000降至0.9679，改善幅度约3.2%。尽管降低有限，但表明防护措施具有一定效果。&lt;/p&gt;
&lt;h1&gt;思考&lt;/h1&gt;
&lt;h2&gt;泄漏机理分析&lt;/h2&gt;
&lt;p&gt;实验结果表明AES-128的时序泄漏主要源于S-Box查找表的数据相关访问时间。在硬件实现中，S-Box通常采用组合逻辑或ROM，不同输入值导致不同的逻辑路径或位线负载，从而产生可测量的延迟差异。这种差异与输入数据的汉明权重高度相关，为攻击者提供了恢复密钥的侧信道。&lt;/p&gt;
&lt;h2&gt;防护建议&lt;/h2&gt;
&lt;p&gt;针对时序侧信道泄漏，可采取以下防护措施：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;等时化设计&lt;/strong&gt;：确保所有操作执行时间与数据无关，例如通过常数时间编程或硬件双轨逻辑。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;掩码技术&lt;/strong&gt;：在算法层面引入随机掩码，打破数据与功耗/时间的相关性。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;噪声注入&lt;/strong&gt;：在时钟或电源中注入随机抖动，增加攻击者测量难度。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;局限性&lt;/h2&gt;
&lt;p&gt;本研究的仿真基于PyRTL的单位延迟模型，与实际芯片的时序特性存在差距。真实芯片的延迟还受工艺偏差、电压温度变化、噪声等因素影响，可能导致相关性减弱。然而，作为预硅验证的早期评估，本研究仍能揭示潜在风险，并为后续更精确的仿真（如SPICE级）提供方向。&lt;/p&gt;
&lt;h2&gt;结论&lt;/h2&gt;
&lt;p&gt;本文基于PyRTL框架对AES-128加密电路进行了时序侧信道脆弱性分析。实验证明，单周期AES实现存在极强的时序泄漏（r=0.999997），且可通过CPA攻击在50条迹线下成功恢复密钥。本研究为芯片设计早期的安全评估提供了一种轻量级、可量化的方法，有助于在设计阶段发现潜在漏洞，降低流片后修复成本。未来工作将扩展至多周期实现、采用更精确的时序库（如Yosys+ABC），并探索结合FPGA实测验证。&lt;/p&gt;
&lt;h1&gt;致谢&lt;/h1&gt;
&lt;p&gt;感谢我的同僚（名字按拼音排序）：anno、比那名居天子、光风霁月、henry、黄ruijian、KRNK、宁樂、塔比、txt、坨子哥、晓堃、xiaoshabi、樂依等人，感谢你们在我做该项目时给予我的付出&lt;/p&gt;
&lt;p&gt;感谢我的家人们在我实施该项目时做出的情绪上的慰问，经济上的支持。&lt;/p&gt;
&lt;p&gt;感谢我的室友，他们给我提供了一个顶级的环境&lt;/p&gt;
&lt;p&gt;感谢深圳大学，为我提供了这么好的一个平台&lt;/p&gt;
&lt;p&gt;感谢这个时代&lt;/p&gt;
&lt;h1&gt;参考文献&lt;/h1&gt;
&lt;p&gt;[1] Kocher P C. Timing attacks on implementations of Diffie-Hellman, RSA, DSS, and other systems[C]//Advances in Cryptology - CRYPTO&apos;96. Springer, 1996: 104-113. DOI: 10.1007/3-540-68697-5_9&lt;/p&gt;
&lt;p&gt;[2] Bidmeshki M, Wachsmann C. Static RTL Analysis for Pre-Silicon (Security) Validation[J/OL]. Intel INT31 Security Research Blog, August 2025. https://www.christian-wachsmann.de/publications/_publications/2025-int31-cobra3&lt;/p&gt;
&lt;p&gt;[3] UCSB EUREKA! Scholars. A Hardware Implementation of the Advanced Encryption Standard using PyRTL[EB/OL]. 2020. https://eureka.csep.ucsb.edu/research/hardware-implementation-advanced-encryption-standard-using-pyrtl&lt;/p&gt;
&lt;p&gt;[4] Kibria R, Farahmandi F, Tehranipoor M. A Survey on SoC Security Verification Methods at the Pre-silicon Stage[J/OL]. IACR ePrint, 2024. https://eprint.iacr.org/2024/1280&lt;/p&gt;
&lt;p&gt;[5] Cheng Y, Li T, Zou H, et al. PyABV: a framework for enhancing PyRTL with assertion-based verification[J]. Frontiers of Computer Science, 2025, 19(7): 197204. DOI: 10.1007/s11704-024-40127-0&lt;/p&gt;
&lt;p&gt;[6] Guo X, Dutta R G, Jin Y. Hierarchy-Preserving Formal Verification Methods for Pre-Silicon Security Assurance[C]//2015 16th International Workshop on Microprocessor and SOC Test and Verification (MTV). IEEE, 2015: 48-53. DOI: 10.1109/mtv.2015.12&lt;/p&gt;
&lt;p&gt;[7] Canella C, Van Bulck J, Schwarz M, et al. A Systematic Evaluation of Transient
Execution Attacks and Defenses[C]//USENIX Security Symposium. 2019: 249-266.&lt;/p&gt;
&lt;p&gt;[8] National University of Defense Technology. New assertion-based verification in python hardware flow[EB/OL]. EurekAlert!, 2025-07-22. https://www.eurekalert.org/news-releases/1092141&lt;/p&gt;
&lt;p&gt;[9] Dipu N F, Hossain M M, Azar K Z, et al. FormalFuzzer: Formal Verification Assisted Fuzz Testing for SoC Vulnerability Detection[C]//Proceedings of the 29th Asia and South Pacific Design Automation Conference (ASPDAC). ACM, 2024: 377-383.&lt;/p&gt;
&lt;p&gt;[10] Keysight Technologies. Secure at First Silicon: Reducing Cost and Risk with Inspector Pre-Silicon[Z]. White Paper, 2024. https://www.keysight.com/us/en/assets/3124-1750/white-papers/Keysight-Inspector-Pre-Silicon-Side-Channel-Analysis.pdf&lt;/p&gt;
&lt;p&gt;[11] Wachsmann C, et al. QED and Symbolic QED: Dramatic Improvements in Pre-Silicon
Verification and Post-Silicon Validation[M]. now publishers, 2024.&lt;/p&gt;
</content:encoded></item><item><title>Cryptography入门</title><link>https://dxfaker.top/posts/%E5%AF%86%E7%A0%81%E5%AD%A6%E9%98%85%E8%AF%BB/</link><guid isPermaLink="true">https://dxfaker.top/posts/%E5%AF%86%E7%A0%81%E5%AD%A6%E9%98%85%E8%AF%BB/</guid><description>自用的神必笔记</description><pubDate>Tue, 10 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;写在前面&lt;/h1&gt;
&lt;p&gt;你是否被reverse中的神必加密小程序搞得神志不清？&lt;/p&gt;
&lt;p&gt;你是否读懂了加密代码后却对解密无从下手？&lt;/p&gt;
&lt;p&gt;反正我这几天是被折磨不轻了，遂决定从密码学中学习一点皮毛以缓解痛楚。&lt;/p&gt;
&lt;p&gt;之后会在一直更，但这两个月大概率不怎么会更了，安心准备转专业考试&lt;/p&gt;
&lt;h1&gt;第一章&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Cryptography&lt;/em&gt;, the methodology of concealing the content of messages, comes from the Greek root words kryptos, meaning hidden,and graphikos, meaning writing. The modern scientific study of cryptography is sometimes referred to as &lt;em&gt;cryptology&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;1. Caesar cipher&lt;/h2&gt;
&lt;p&gt;操作：每个字母都被替换&lt;/p&gt;
&lt;p&gt;总体来说，他属于一种大的类型——替换密码&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dxfaker.top/images/post/aadfa957-d59d-442a-a88e-2b2767622576.gif&quot; alt=&quot;aadfa957-d59d-442a-a88e-2b2767622576&quot; /&gt;&lt;/p&gt;
&lt;p&gt;由于这样子看稍费一点脖子，于是整理成表：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dxfaker.top/images/post/db6fe07d-e618-4e7c-9455-33f646ffedb8.gif&quot; alt=&quot;db6fe07d-e618-4e7c-9455-33f646ffedb8&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;2. &lt;em&gt;one-to-one&lt;/em&gt; or &lt;em&gt;injective&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;跟函数一样，即有加密函数中两个明文字母不会同时指向同一个密文字母&lt;/p&gt;
&lt;p&gt;顺着凯撒密码的思路，也就是说，我们可以瞎几把乱对映（a-&amp;gt;26个字母中的任意一个，以此类推）得出有26·25·24…·1=26！种方法，将26个明文字母分配给26个密文字母。&lt;/p&gt;
&lt;p&gt;也就是说，面对这种东西的时候，你得有策略，想方设法去做。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Your opponent always uses her best strategy to defeat you, not the strategy that you want her to use. Thus the security of an encryption system depends on the best known method to break it. As new and improved methods are developed, the level of security can only get worse, never better.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;eg:习惯法,我第一次见到是在福尔摩斯来着&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;To take an extreme example, the letter q in English is virtually always followed by the letter u. More useful is the fact that certain letters such as e and t appear far more frequently than other letters such as f and c. Table lists the letters with their typical frequencies in English text. As you can see, the most frequent letter is e, followed by t, a, o, and n.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dxfaker.top/images/post/13e857f7-19f5-44fc-8ef9-394f1f20c7ed.gif&quot; alt=&quot;13e857f7-19f5-44fc-8ef9-394f1f20c7ed&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;通过对加密的消息制作频率表，便可以迅速确定e,再通过e展开去推断&lt;/p&gt;
&lt;p&gt;A simple substitution cipher to cryptanalyze:&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;LOJUM YLJME PDYVJ QXTDV SVJNL DMTJZ WMJGG YSNDL UYLEO SKDVC&lt;/p&gt;
&lt;p&gt;GEPJS MDIPD NEJSK DNJTJ LSKDL OSVDV DNGYN VSGLL OSCIO LGOYG&lt;/p&gt;
&lt;p&gt;ESNEP CGYSN GUJMJ DGYNK DPPYX PJDGG SVDNT WMSWS GYLYS NGSKJ&lt;/p&gt;
&lt;p&gt;CEPYQ GSGLD MLPYN IUSCP QOYGM JGCPL GDWWJ DMLSL OJCNY NYLYD&lt;/p&gt;
&lt;p&gt;LJQLO DLCNL YPLOJ TPJDM NJQLO JWMSE JGGJG XTUOY EOOJO DQDMM&lt;/p&gt;
&lt;p&gt;YBJQD LLOJV LOJTV YIOLU JPPES NGYQJ MOYVD GDNJE MSVDN EJM&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;这个时候，我的话就直接让AI分析频率了，&lt;img src=&quot;https://dxfaker.top/images/post/287ee719-f9be-4b30-af26-89f1c88f6cf4.gif&quot; alt=&quot;287ee719-f9be-4b30-af26-89f1c88f6cf4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;不过这些倒是直接偷的书上的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dxfaker.top/images/post/2be4fa80-5f71-4aca-86af-bbdde5dc5e6c.png&quot; alt=&quot;2be4fa80-5f71-4aca-86af-bbdde5dc5e6c&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后是两个字的频率&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dxfaker.top/images/post/b607fa6a-5bfe-48e2-96e1-ec7b24068a5b.gif&quot; alt=&quot;b607fa6a-5bfe-48e2-96e1-ec7b24068a5b&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dxfaker.top/images/post/9ef54f73-f737-4a69-b08a-7b7567469b17.gif&quot; alt=&quot;9ef54f73-f737-4a69-b08a-7b7567469b17&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dxfaker.top/images/post/c98714ce-0b8a-417d-a884-9742865cda95.gif&quot; alt=&quot;c98714ce-0b8a-417d-a884-9742865cda95&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dxfaker.top/images/post/29132ff3-0700-4d55-b944-fcce5aa917cc.gif&quot; alt=&quot;29132ff3-0700-4d55-b944-fcce5aa917cc&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dxfaker.top/images/post/5a11fac8-5257-430f-b96d-327ce0638173.gif&quot; alt=&quot;5a11fac8-5257-430f-b96d-327ce0638173&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dxfaker.top/images/post/c7cf5c91-85f6-4df2-99ae-fccddfdda366.gif&quot; alt=&quot;c7cf5c91-85f6-4df2-99ae-fccddfdda366&quot; /&gt;&lt;/p&gt;
&lt;p&gt;具体的过程看原书就是了&lt;/p&gt;
</content:encoded></item><item><title>基于win11的主题优化</title><link>https://dxfaker.top/posts/%E5%9F%BA%E4%BA%8Ewin11%E7%9A%84%E4%B8%BB%E9%A2%98%E4%BC%98%E5%8C%96/</link><guid isPermaLink="true">https://dxfaker.top/posts/%E5%9F%BA%E4%BA%8Ewin11%E7%9A%84%E4%B8%BB%E9%A2%98%E4%BC%98%E5%8C%96/</guid><description>分享一下</description><pubDate>Thu, 05 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;写在前面&lt;/h1&gt;
&lt;p&gt;镇楼：另一个世界线的我，希望看到这里的读者能像他们一样把握住机会，不要让自己后悔吧&lt;/p&gt;
&lt;p&gt;不然，在你成熟前你就要一直内耗了&lt;/p&gt;
&lt;p&gt;（&lt;/p&gt;
&lt;h1&gt;正题&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://dxfaker.top/images/post/26.3.5.3.png&quot; alt=&quot;26.3.5.3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这个是最终的效果，如果觉得生活太如意的话，大可以把任务栏再删的精简一点&lt;/p&gt;
&lt;h2&gt;壁纸（付费）&lt;/h2&gt;
&lt;p&gt;wallpaper，这个我就不多说了，找个中意的壁纸下载就行了，至于别的内容，甚至某些壁纸，也谨慎下载，毕竟你也不知道里面装了些什么&lt;/p&gt;
&lt;p&gt;其实还有一个低配， 我记得叫 livepaper来着，不过那个壁纸就得你自己去找了&lt;/p&gt;
&lt;h2&gt;任务栏&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;TranslucentTB&lt;/strong&gt;（微软商店免费）&lt;/p&gt;
&lt;p&gt;作用：任务栏透明/亚克力效果
操作：安装后，在系统托盘图标中选择 &lt;strong&gt;“清晰”&lt;/strong&gt;（全透明）或 &lt;strong&gt;“亚克力”&lt;/strong&gt;（毛玻璃）&lt;/p&gt;
&lt;p&gt;我选择的是全透明&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RoundedTB&lt;/strong&gt;（微软商店免费）
作用：任务栏圆角 + 悬浮（设置边距 Margin）
操作：安装后，在设置中调整 &lt;strong&gt;Margin（边距）&lt;/strong&gt; 数值（如底部设为 &lt;code&gt;5&lt;/code&gt;），实现四边悬空&lt;/p&gt;
&lt;p&gt;作用不是很明显，可下可不下&lt;/p&gt;
&lt;h2&gt;文件资源管理器&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;ExplorerBlurMica&lt;/strong&gt;（GitHub开源）&lt;/p&gt;
&lt;p&gt;窗口背景半透明（透出桌面）&lt;/p&gt;
&lt;p&gt;窗口全透明要搞麻烦一点的&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;操作步骤&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;下载并解压到文件夹（如 &lt;code&gt;Release_x64&lt;/code&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;用记事本打开 &lt;code&gt;config.ini&lt;/code&gt;，修改关键参数：&lt;/p&gt;
&lt;p&gt;ini&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;effect = 1          
[light]
a = 0               ; 浅色模式透明度0（完全透明）
[dark]
a = 0               ; 深色模式透明度0
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;以管理员身份运行&lt;/strong&gt; &lt;code&gt;register.cmd&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;重启资源管理器（任务管理器重启“Windows 资源管理器”或重启电脑）&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;卸载方法&lt;/strong&gt;：以管理员身份运行同文件夹下的 &lt;code&gt;uninstall.cmd&lt;/code&gt; 并重启资源管理器&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Windows 终端 (PowerShell / WSL)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;/strong&gt;：亚克力透明背景&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工具&lt;/strong&gt;：Windows Terminal 自带功能&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;操作步骤&lt;/strong&gt;：
&lt;ol&gt;
&lt;li&gt;打开 Windows Terminal，点击下拉菜单 → &lt;strong&gt;设置&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;左侧选择配置文件（如“Windows PowerShell”或“Ubuntu”）&lt;/li&gt;
&lt;li&gt;点击 &lt;strong&gt;“外观”&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;将 &lt;strong&gt;“背景效果”&lt;/strong&gt; 从“不透明”改为 &lt;strong&gt;“亚克力”&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;调整下方 &lt;strong&gt;“亚克力不透明度”&lt;/strong&gt; 滑块（如 70%）&lt;/li&gt;
&lt;li&gt;点击 &lt;strong&gt;“保存”&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://dxfaker.top/images/post/26.3.5.4.png&quot; alt=&quot;26.3.5.4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;要是觉得影响注意力了把板子加上去就是了&lt;/p&gt;
&lt;h1&gt;final&lt;/h1&gt;
&lt;p&gt;推荐一首歌：洛天依的千金胧梦&lt;/p&gt;
&lt;p&gt;今天莫名其妙压抑了起来，该🛫了&lt;/p&gt;
</content:encoded></item><item><title>处理线性方程组题型</title><link>https://dxfaker.top/posts/2634/</link><guid isPermaLink="true">https://dxfaker.top/posts/2634/</guid><description>基于WSL的处理办法</description><pubDate>Thu, 05 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;写在前面&lt;/h1&gt;
&lt;p&gt;镇楼：届不到的爱恋&lt;/p&gt;
&lt;p&gt;感谢leyi发的新生赛RE题，不愧是逆神的题目，我第二题就卡住了，脱壳没有什么问题，但是我点开IDA后发现流程图长这样：&lt;img src=&quot;https://dxfaker.top/images/post/26.3.5.png&quot; alt=&quot;屏幕截图 2026-03-04 110911&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这个时候，有人就要问了：B哥，B哥，这他妈什么jb？&lt;/p&gt;
&lt;p&gt;那我问你，我才刚学，我知道个蛋&lt;/p&gt;
&lt;p&gt;于是我去问了AI，说是有两种题型：&lt;/p&gt;
&lt;p&gt;1.线性方程组题型&lt;/p&gt;
&lt;p&gt;2.控制流平坦化题型&lt;/p&gt;
&lt;h2&gt;线性方程组题型的典型特征&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;结构&lt;/strong&gt;：主函数中只有一个巨大的 &lt;code&gt;if&lt;/code&gt; 语句，里面包含几十个由 &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; 连接的等式。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;表达式形式&lt;/strong&gt;：每个等式都是形如 &lt;code&gt;(系数1 * 字节1 + 系数2 * 字节2 + ... + 系数32 * 字节32) == 常数&lt;/code&gt; 的线性组合。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;变量命名&lt;/strong&gt;：IDA 可能会自动将输入分解为几个连续的变量（如 &lt;code&gt;v4, v5, v6, v7&lt;/code&gt;），每个变量通过 &lt;code&gt;SBYTE1&lt;/code&gt;、&lt;code&gt;SBYTE2&lt;/code&gt; 等宏访问其内部的字节。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;没有循环/switch&lt;/strong&gt;：除了输入读取和字符范围检查的循环外，验证部分全是顺序的数学运算，没有复杂跳转。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;控制流平坦化题型的典型特征&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;结构&lt;/strong&gt;：反编译后是一个巨大的 &lt;code&gt;while(1)&lt;/code&gt; 循环，里面嵌套 &lt;code&gt;switch&lt;/code&gt; 或大量 &lt;code&gt;if-else&lt;/code&gt;，所有功能块都被打散。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;状态变量&lt;/strong&gt;：存在一个局部变量（如 &lt;code&gt;v1&lt;/code&gt;）作为“状态”，控制当前执行哪个块。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;分发器&lt;/strong&gt;：每次循环都会根据状态变量跳转到对应块，块末尾会更新状态变量并跳回循环开头。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码碎片化&lt;/strong&gt;：原本顺序的算法被拆成许多小块，块之间通过状态变量连接。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;如何快速确认是线性方程组&lt;/h1&gt;
&lt;p&gt;当你看到类似你贴出的代码时，可以按以下步骤确认：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;观察输入处理&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;代码开头通常有 &lt;code&gt;get_input(&amp;amp;v4)&lt;/code&gt;，并且 &lt;code&gt;v4&lt;/code&gt; 是 8 字节变量（&lt;code&gt;__int64&lt;/code&gt;）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;随后可能有一个循环调用 &lt;code&gt;check_char&lt;/code&gt; 验证每个字符的范围（比如是否为可打印字符）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;这说明输入被存储在连续的 32 字节中，分在 4 个 8 字节变量里。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dxfaker.top/images/post/26.3.5.1.png&quot; alt=&quot;屏幕截图 2026-03-04 205108&quot; /&gt;&lt;/p&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;a * SBYTE1(v4) + b * (char)v5 + ... == 常数&lt;/code&gt; 的表达式，且系数都是常数，那就是线性组合。&lt;/li&gt;
&lt;li&gt;注意移位运算如 &lt;code&gt;(SBYTE1(v4) &amp;lt;&amp;lt; 7)&lt;/code&gt; 实际就是乘以 128，仍是线性。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://dxfaker.top/images/post/26.3.5.2.png&quot; alt=&quot;屏幕截图 2026-03-04 205802&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;79 * SBYTE5(v7)&lt;code&gt;是&lt;/code&gt;79` 乘以 v7 的第 5 个字节，等等。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这种形式就是典型的线性方程：&lt;code&gt;Σ (系数_i * 字节_i) = 常数&lt;/code&gt;。所有项都是&lt;strong&gt;一次&lt;/strong&gt;的，没有平方、异或等非线性运算。移位如 &lt;code&gt;(x &amp;lt;&amp;lt; 7)&lt;/code&gt; 也等价于 &lt;code&gt;128 * x&lt;/code&gt;，仍然是线性。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;计数方程个数&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数一数 &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; 连接了多少个等式。如果个数正好等于输入字节数（32），那么极有可能是一个&lt;strong&gt;线性方程组&lt;/strong&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;b0~b31&lt;/code&gt;，把每个等式整理成 &lt;code&gt;Σ coeff_i * b_i = const&lt;/code&gt; 的形式。如果系数矩阵是满秩的，那么就有唯一解。(这个以后再说)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;补充知识&lt;/h1&gt;
&lt;h2&gt;变量与字节的对应关系&lt;/h2&gt;
&lt;p&gt;根据之前的内存布局（栈帧 &lt;code&gt;rbp-30h&lt;/code&gt; 到 &lt;code&gt;rbp-11h&lt;/code&gt;），四个 64 位变量 &lt;code&gt;v4&lt;/code&gt;, &lt;code&gt;v5&lt;/code&gt;, &lt;code&gt;v6&lt;/code&gt;, &lt;code&gt;v7&lt;/code&gt; 连续存放了 32 个字符。每个变量内部，字节从小端序排列：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;(char)v4&lt;/code&gt; → 最低地址字节 → 输入的第 0 个字符&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SBYTE1(v4)&lt;/code&gt; → 下一个字节 → 输入的第 1 个字符&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SHIBYTE(v4)&lt;/code&gt; → 最高地址字节 → 输入的第 7 个字符&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;即：每个宏对应一个唯一的字节索引&lt;/p&gt;
&lt;p&gt;在 Z3 脚本中，我们不能直接写 &lt;code&gt;SBYTE6(v7)&lt;/code&gt;，因为 Z3 不认识这些宏。我们需要将方程中的每一项都转化为关于输入字符的线性组合。也就是说，我们需要把伪代码中的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;117 * SBYTE6(v7) + 79 * SBYTE5(v7) + ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;翻译成 Z3 能理解的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;117 * flag[30] + 79 * flag[29] + ...
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;字节映射&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;int __fastcall main(int argc, const char **argv, const char **envp)
{
  __int64 v4; // [rsp+20h] [rbp-30h] BYREF
  __int64 v5; // [rsp+28h] [rbp-28h]
  __int64 v6; // [rsp+30h] [rbp-20h]
  __int64 v7; // [rsp+38h] [rbp-18h]
  int i; // [rsp+4Ch] [rbp-4h]

  _main(argc, argv, envp);
  printf(&quot;tell me the answer:&quot;);
  v4 = 0i64;
  v5 = 0i64;
  v6 = 0i64;
  v7 = 0i64;
  get_input(&amp;amp;v4);
  printf(&quot;your input is: %s\n&quot;, (const char *)&amp;amp;v4);
  for ( i = 0; i &amp;lt;= 31; ++i )
  {
    if ( !(unsigned int)check_char((unsigned int)*((char *)&amp;amp;v4 + i)) )
    {
      puts(&quot;nope&quot;);
      return 0;
    }
  }
  // ... 后面是一大堆方程
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;定义了四个 8 字节变量：&lt;code&gt;v4&lt;/code&gt;, &lt;code&gt;v5&lt;/code&gt;, &lt;code&gt;v6&lt;/code&gt;, &lt;code&gt;v7&lt;/code&gt;。它们的类型是 &lt;code&gt;__int64&lt;/code&gt;，即 64 位整数。&lt;/li&gt;
&lt;li&gt;它们在栈上是连续的：&lt;code&gt;v4&lt;/code&gt; 在 &lt;code&gt;rbp-30h&lt;/code&gt;，&lt;code&gt;v5&lt;/code&gt; 在 &lt;code&gt;rbp-28h&lt;/code&gt;，&lt;code&gt;v6&lt;/code&gt; 在 &lt;code&gt;rbp-20h&lt;/code&gt;，&lt;code&gt;v7&lt;/code&gt; 在 &lt;code&gt;rbp-18h&lt;/code&gt;。因为栈向下增长，所以 &lt;code&gt;v4&lt;/code&gt; 地址最低，&lt;code&gt;v7&lt;/code&gt; 地址最高。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_input(&amp;amp;v4)&lt;/code&gt; 将用户输入的字符串存储到以 &lt;code&gt;v4&lt;/code&gt; 为起始地址的缓冲区。由于 &lt;code&gt;v4&lt;/code&gt; 是 8 字节，但后面跟着 &lt;code&gt;v5&lt;/code&gt;、&lt;code&gt;v6&lt;/code&gt;、&lt;code&gt;v7&lt;/code&gt;，所以实际上这 4 个变量连续占据了 32 字节的空间（4 * 8 = 32）。因此，输入的字符串会被完整放在这 32 字节中。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;宏&lt;/h2&gt;
&lt;p&gt;宏是一种预处理符号，它代表一段代码片段。在 IDA 的反编译伪代码中，这些宏被用来简化对多字节变量（如 &lt;code&gt;__int64&lt;/code&gt;）的字节级访问。它们不是 C 语言标准的一部分，而是 IDA 为了让反编译结果更易读而创造的。&lt;/p&gt;
&lt;h2&gt;字节顺序与宏的含义&lt;/h2&gt;
&lt;p&gt;在后续的方程中，你会看到诸如 &lt;code&gt;(char)v4&lt;/code&gt;、&lt;code&gt;SBYTE1(v4)&lt;/code&gt;、&lt;code&gt;SHIBYTE(v4)&lt;/code&gt; 等宏。这些是 IDA 对位域或字节提取的表示，它们的含义取决于机器的&lt;strong&gt;小端序&lt;/strong&gt;（x86/x64 是小端）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;(char)v4&lt;/code&gt;：取 &lt;code&gt;v4&lt;/code&gt; 的最低字节（即地址最低的字节）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SBYTE1(v4)&lt;/code&gt;：取 &lt;code&gt;v4&lt;/code&gt; 的第二个字节（地址次低的字节）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SBYTE2(v4)&lt;/code&gt;：第三个字节。&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SBYTE6(v4)&lt;/code&gt;：第七个字节。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SHIBYTE(v4)&lt;/code&gt;：最高字节（第八个字节）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因为 &lt;code&gt;v4&lt;/code&gt; 是 8 字节，所以它有 8 个字节，索引从 0（最低）到 7（最高）。同理 &lt;code&gt;v5&lt;/code&gt; 也有 8 个字节，索引 8~15，以此类推。&lt;/p&gt;
&lt;h3&gt;完整的字节映射表&lt;/h3&gt;
&lt;p&gt;结合栈地址和字节顺序，我们可以将每个宏映射到一个具体的字节索引（&lt;code&gt;b0&lt;/code&gt; ~ &lt;code&gt;b31&lt;/code&gt;）。假设输入字符串的第一个字符存入 &lt;code&gt;v4&lt;/code&gt; 的最低字节，最后一个字符存入 &lt;code&gt;v7&lt;/code&gt; 的最高字节。&lt;/p&gt;
&lt;h3&gt;举例说明&lt;/h3&gt;
&lt;p&gt;看第一个方程的一小部分：&lt;/p&gt;
&lt;p&gt;text&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;117 * SBYTE6(v7) + 79 * SBYTE5(v7) + 56 * SBYTE2(v7) + 91 * SBYTE1(v7) + ...
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SBYTE6(v7)&lt;/code&gt; 对应 &lt;code&gt;v7&lt;/code&gt; 的第七个字节，即 &lt;code&gt;b30&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SBYTE5(v7)&lt;/code&gt; 对应 &lt;code&gt;b29&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SBYTE2(v7)&lt;/code&gt; 对应 &lt;code&gt;b26&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SBYTE1(v7)&lt;/code&gt; 对应 &lt;code&gt;b25&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此，这一部分的系数贡献是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;b30 加 117&lt;/li&gt;
&lt;li&gt;b29 加 79&lt;/li&gt;
&lt;li&gt;b26 加 56&lt;/li&gt;
&lt;li&gt;b25 加 91&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以此类推，每个方程都要根据映射表将每个项的系数加到对应的字节索引上。&lt;/p&gt;
&lt;h2&gt;为什么这个映射很重要&lt;/h2&gt;
&lt;p&gt;如果你映射错了，比如把 &lt;code&gt;SBYTE6(v7)&lt;/code&gt; 当成 b29 而不是 b30，那么整个方程组就会错位，Z3 就会无解。这是手动提取最容易出错的地方之一。&lt;/p&gt;
&lt;h2&gt;z3库与anger脚本&lt;/h2&gt;
&lt;p&gt;网上有很多，本人见识有限，后面会补充&lt;/p&gt;
&lt;h1&gt;解题&lt;/h1&gt;
&lt;p&gt;以下是基于IDA PRO:&lt;/p&gt;
&lt;p&gt;左边functions下的functinon name,找到main, 点击F5看伪代码（就是上面那些图片&lt;/p&gt;
&lt;p&gt;首先是AURORA给的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from z3 import *
flag = [BitVec(&apos;flag{}&apos;.format(i),8) for i in range(32)]
alphabet = b&quot;AUVabcdhikorsuvyz012345{}_\&apos;&quot;
s = Solver()
for i in range(32):
s.add(Or([flag[i] == c for c in alphabet]))
s.add(85 * flag[0] + 185 * flag[1] + 80 * flag[2] + 81 * flag[3] + 130 * flag[4]
+ 95 * flag[5] + 126 * flag[6] + 197 * flag[7] + 148 * flag[8] + 228 * flag[9] +
213 * flag[10] + 107 * flag[11] + 195 * flag[12] + 183 * flag[13] + 200 *
flag[14] + 47 * flag[15] + 194 * flag[16] + 16 * flag[17] + 51 * flag[18] + 222 *
flag[19] + 191 * flag[20] + 44 * flag[21] + 153 * flag[22] + 217 * flag[23] + 192
* flag[24] + 91 * flag[25] + 56 * flag[26] + 178 * flag[27] + 27 * flag[28] + 79
* flag[29] + 117 * flag[30] + 98 * flag[31] == 422531)
s.add(93 * flag[0] + 128 * flag[1] + 111 * flag[2] + 16 * flag[3] + 163 * flag[4]
+ 215 * flag[5] + 223 * flag[6] + 51 * flag[7] + 81 * flag[8] + 1 * flag[9] + 198
* flag[10] + 247 * flag[11] + 88 * flag[12] + 176 * flag[13] + 110 * flag[14] +
250 * flag[15] + 196 * flag[16] + 161 * flag[17] + 63 * flag[18] + 159 * flag[19]
+ 191 * flag[20] + 239 * flag[21] + 69 * flag[22] + 119 * flag[23] + 136 *
flag[24] + 30 * flag[25] + 86 * flag[26] + 185 * flag[27] + 14 * flag[28] + 26 *
flag[29] + 50 * flag[30] + 237 * flag[31] == 377679)
s.add(17 * flag[0] + 108 * flag[1] + 169 * flag[2] + 83 * flag[3] + 129 * flag[4]
+ 191 * flag[5] + 3 * flag[6] + 249 * flag[7] + 163 * flag[8] + 138 * flag[9] + 2
* flag[10] + 123 * flag[11] + 23 * flag[12] + 162 * flag[13] + 155 * flag[14] +
152 * flag[15] + 46 * flag[16] + 80 * flag[17] + 215 * flag[18] + 6 * flag[19] +
229 * flag[20] + 128 * flag[21] + 53 * flag[22] + 255 * flag[23] + 71 * flag[24]
+ 212 * flag[25] + 208 * flag[26] + 47 * flag[27] + 36 * flag[28] + 75 * flag[29]
+ 218 * flag[30] + 144 * flag[31] == 377733)
s.add(204 * flag[0] + 247 * flag[1] + 15 * flag[2] + 66 * flag[3] + 166 * flag[4]
+ 87 * flag[5] + 102 * flag[6] + 186 * flag[7] + 82 * flag[8] + 141 * flag[9] + 9
* flag[10] + 120 * flag[11] + 145 * flag[12] + 69 * flag[13] + 18 * flag[14] + 65
* flag[15] + 137 * flag[16] + 171 * flag[17] + 167 * flag[18] + 184 * flag[19] +
183 * flag[20] + 251 * flag[21] + 153 * flag[22] + 3 * flag[23] + 84 * flag[24] +
201 * flag[25] + 189 * flag[26] + 34 * flag[27] + 79 * flag[28] + 64 * flag[29] +
230 * flag[30] + 197 * flag[31] == 382101)
s.add(234 * flag[0] + 225 * flag[1] + 195 * flag[2] + 117 * flag[3] + 8 * flag[4]
+ 89 * flag[5] + 254 * flag[6] + 173 * flag[7] + 251 * flag[8] + 223 * flag[9] +
87 * flag[10] + 165 * flag[11] + 114 * flag[12] + 242 * flag[13] + 232 * flag[14]
+ 126 * flag[15] + 105 * flag[16] + 121 * flag[17] + 155 * flag[18] + 194 *
flag[19] + 64 * flag[20] + 154 * flag[21] + 120 * flag[22] + 80 * flag[23] + 151
* flag[24] + 111 * flag[25] + 144 * flag[26] + 243 * flag[27] + 159 * flag[28] +
239 * flag[29] + 93 * flag[30] + 97 * flag[31] == 476378)
s.add(32 * flag[0] + 64 * flag[1] + 155 * flag[2] + 131 * flag[3] + 161 * flag[4]
+ 223 * flag[5] + 210 * flag[6] + 15 * flag[7] + 205 * flag[8] + 164 * flag[9] +
146 * flag[10] + 27 * flag[11] + 108 * flag[12] + 35 * flag[13] + 63 * flag[14] +
87 * flag[15] + 215 * flag[16] + 163 * flag[17] + 170 * flag[18] + 95 * flag[19]
+ 70 * flag[20] + 60 * flag[21] + 81 * flag[22] + 241 * flag[23] + 122 * flag[24]
+ 50 * flag[25] + 123 * flag[26] + 177 * flag[27] + 172 * flag[28] + 106 *
flag[29] + 51 * flag[30] + 230 * flag[31] == 403748)
s.add(204 * flag[0] + 78 * flag[1] + 92 * flag[2] + 64 * flag[3] + 125 * flag[4]
+ 161 * flag[5] + 185 * flag[6] + 194 * flag[7] + 74 * flag[8] + 1 * flag[9] +
155 * flag[10] + 109 * flag[11] + 132 * flag[12] + 19 * flag[13] + 110 * flag[14]
+ 201 * flag[15] + 29 * flag[16] + 3 * flag[17] + 177 * flag[18] + 53 * flag[19]
+ 129 * flag[20] + 224 * flag[21] + 205 * flag[22] + 146 * flag[23] + 71 *
flag[24] + 128 * flag[25] + 203 * flag[26] + 145 * flag[27] + 68 * flag[28] + 100
* flag[29] + 80 * flag[30] + 57 * flag[31] == 345842)
s.add(169 * flag[0] + 98 * flag[1] + 28 * flag[2] + 204 * flag[3] + 149 * flag[4]
+ 252 * flag[5] + 172 * flag[6] + 141 * flag[7] + 134 * flag[8] + 116 * flag[9] +
39 * flag[10] + 48 * flag[11] + 247 * flag[12] + 128 * flag[13] + 164 * flag[14]
+ 43 * flag[15] + 7 * flag[16] + 245 * flag[17] + 241 * flag[18] + 15 * flag[19]
+ 93 * flag[20] + 197 * flag[21] + 209 * flag[22] + 100 * flag[23] + 25 *
flag[24] + 129 * flag[25] + 227 * flag[26] + 58 * flag[27] + 54 * flag[28] + 123
* flag[29] + 5 * flag[30] + 118 * flag[31] == 373897)
s.add(187 * flag[0] + 133 * flag[1] + 70 * flag[2] + 244 * flag[3] + 169 *
flag[4] + 168 * flag[5] + 172 * flag[6] + 48 * flag[7] + 119 * flag[8] + 155 *
flag[9] + 127 * flag[10] + 222 * flag[11] + 170 * flag[12] + 217 * flag[13] + 107
* flag[14] + 88 * flag[15] + 162 * flag[16] + 206 * flag[17] + 75 * flag[18] + 2
* flag[19] + 7 * flag[20] + 184 * flag[21] + 130 * flag[22] + 192 * flag[23] +
163 * flag[24] + 255 * flag[25] + 196 * flag[26] + 221 * flag[27] + 27 * flag[28]
+ 62 * flag[29] + 213 * flag[30] + 248 * flag[31] == 452443)
s.add(200 * flag[0] + 18 * flag[1] + 135 * flag[2] + 79 * flag[3] + 150 * flag[4]
+ 193 * flag[5] + 78 * flag[6] + 245 * flag[7] + 164 * flag[8] + 85 * flag[9] +
192 * flag[10] + 66 * flag[11] + 209 * flag[12] + 250 * flag[13] + 44 * flag[14]
+ 196 * flag[15] + 141 * flag[16] + 139 * flag[17] + 224 * flag[18] + 252 *
flag[19] + 181 * flag[20] + 8 * flag[21] + 180 * flag[22] + 2 * flag[23] + 112 *
flag[24] + 58 * flag[25] + 191 * flag[26] + 39 * flag[27] + 242 * flag[28] + 14 *
flag[29] + 169 * flag[30] + 99 * flag[31] == 403460)
s.add(48 * flag[0] + 131 * flag[1] + 229 * flag[2] + 237 * flag[3] + 68 * flag[4]
+ 174 * flag[5] + 71 * flag[6] + 87 * flag[7] + 89 * flag[8] + 121 * flag[9] +
206 * flag[10] + 151 * flag[11] + 3 * flag[12] + 51 * flag[13] + 187 * flag[14] +
5 * flag[15] + 8 * flag[16] + 32 * flag[17] + 45 * flag[18] + 63 * flag[19] + 211
* flag[20] + 28 * flag[21] + 169 * flag[22] + 243 * flag[23] + 39 * flag[24] +
216 * flag[25] + 120 * flag[26] + 30 * flag[27] + 177 * flag[28] + 189 * flag[29]
+ 209 * flag[30] + 73 * flag[31] == 375277)
s.add(196 * flag[0] + 107 * flag[1] + 36 * flag[2] + 163 * flag[3] + 229 *
flag[4] + 244 * flag[5] + 181 * flag[6] + 71 * flag[7] + 141 * flag[8] + 44 *
flag[9] + 126 * flag[10] + 43 * flag[11] + 110 * flag[12] + 198 * flag[13] + 66 *
flag[14] + 18 * flag[15] + 197 * flag[16] + 56 * flag[17] + 33 * flag[18] + 2 *
flag[19] + 98 * flag[20] + 13 * flag[21] + 130 * flag[22] + 180 * flag[23] + 133
* flag[24] + 228 * flag[25] + 46 * flag[26] + 171 * flag[27] + 40 * flag[28] + 84
* flag[29] + 27 * flag[30] + 94 * flag[31] == 346617)
s.add(153 * flag[0] + 129 * flag[1] + 215 * flag[2] + 90 * flag[3] + 245 *
flag[4] + 247 * flag[5] + 84 * flag[6] + 192 * flag[7] + 108 * flag[8] + 116 *
flag[9] + 38 * flag[10] + 255 * flag[11] + 123 * flag[12] + 32 * flag[13] + 107 *
flag[14] + 121 * flag[15] + 87 * flag[16] + 99 * flag[17] + 35 * flag[18] + 6 *
flag[19] + 23 * flag[20] + 1 * flag[21] + 175 * flag[22] + 66 * flag[23] + 111 *
flag[24] + 134 * flag[25] + 79 * flag[26] + 73 * flag[27] + 8 * flag[28] + 193 *
flag[29] + 154 * flag[30] + 229 * flag[31] == 359346)
s.add(32 * flag[0] + 179 * flag[1] + 26 * flag[2] + 178 * flag[3] + 25 * flag[4]
+ 175 * flag[5] + 96 * flag[6] + 195 * flag[7] + 47 * flag[8] + 174 * flag[9] +
207 * flag[10] + 198 * flag[11] + 33 * flag[12] + 66 * flag[13] + 71 * flag[14] +
248 * flag[15] + 128 * flag[16] + 162 * flag[17] + 254 * flag[18] + 59 * flag[19]
+ 123 * flag[20] + 135 * flag[21] + 63 * flag[22] + 57 * flag[23] + 75 * flag[24]
+ 210 * flag[25] + 34 * flag[26] + 64 * flag[27] + 13 * flag[28] + 166 * flag[29]
+ 165 * flag[30] + 78 * flag[31] == 349475)
s.add(138 * flag[0] + 90 * flag[1] + 102 * flag[2] + 94 * flag[3] + 254 * flag[4]
+ 111 * flag[5] + 34 * flag[6] + 165 * flag[7] + 125 * flag[8] + 133 * flag[9] +
80 * flag[10] + 92 * flag[11] + 77 * flag[12] + 141 * flag[13] + 126 * flag[14] +
196 * flag[15] + 45 * flag[16] + 61 * flag[17] + 25 * flag[18] + 14 * flag[19] +
5 * flag[20] + 135 * flag[21] + 168 * flag[22] + 63 * flag[23] + 186 * flag[24] +
229 * flag[25] + 234 * flag[26] + 85 * flag[27] + 119 * flag[28] + 239 * flag[29]
+ 214 * flag[30] + 248 * flag[31] == 384145)
s.add(91 * flag[0] + 208 * flag[1] + 133 * flag[2] + 151 * flag[3] + 177 *
flag[4] + 24 * flag[5] + 188 * flag[6] + 90 * flag[7] + 243 * flag[8] + 163 *
flag[9] + 34 * flag[10] + 98 * flag[11] + 124 * flag[12] + 76 * flag[13] + 99 *
flag[14] + 201 * flag[15] + 212 * flag[16] + 71 * flag[17] + 107 * flag[18] + 164
* flag[19] + 233 * flag[20] + 221 * flag[21] + 197 * flag[22] + 19 * flag[23] +
152 * flag[24] + 122 * flag[25] + 144 * flag[26] + 77 * flag[27] + 128 * flag[28]
+ 92 * flag[29] + 168 * flag[30] + 74 * flag[31] == 409437)
s.add(184 * flag[0] + 154 * flag[1] + 147 * flag[2] + 138 * flag[3] + 42 *
flag[4] + 171 * flag[5] + 174 * flag[6] + 164 * flag[7] + 134 * flag[8] + 85 *
flag[9] + 99 * flag[10] + 116 * flag[11] + 136 * flag[12] + 213 * flag[13] + 3 *
flag[14] + 2 * flag[15] + 156 * flag[16] + 217 * flag[17] + 209 * flag[18] + 95 *
flag[19] + 202 * flag[20] + 145 * flag[21] + 25 * flag[22] + 216 * flag[23] + 251
* flag[24] + 165 * flag[25] + 225 * flag[26] + 83 * flag[27] + 34 * flag[28] +
140 * flag[29] + 135 * flag[30] + 77 * flag[31] == 417097)
s.add(202 * flag[0] + 130 * flag[1] + 89 * flag[2] + 99 * flag[3] + 194 * flag[4]
+ 120 * flag[5] + 93 * flag[6] + 126 * flag[7] + 107 * flag[8] + 32 * flag[9] +
249 * flag[10] + 28 * flag[11] + 109 * flag[12] + 179 * flag[13] + 12 * flag[14]
+ 183 * flag[15] + 193 * flag[16] + 175 * flag[17] + 207 * flag[18] + 215 *
flag[19] + 7 * flag[20] + 55 * flag[21] + 180 * flag[22] + 10 * flag[23] + 237 *
flag[24] + 151 * flag[25] + 164 * flag[26] + 106 * flag[27] + 154 * flag[28] + 29
* flag[29] + 39 * flag[30] + 60 * flag[31] == 361950)
s.add(5 * flag[0] + 177 * flag[1] + 125 * flag[2] + 162 * flag[3] + 194 * flag[4]
+ 26 * flag[5] + 168 * flag[6] + 198 * flag[7] + 195 * flag[8] + 39 * flag[9] +
109 * flag[10] + 43 * flag[11] + 240 * flag[12] + 217 * flag[13] + 14 * flag[14]
+ 90 * flag[15] + 218 * flag[16] + 154 * flag[17] + 220 * flag[18] + 197 *
flag[19] + 202 * flag[20] + 36 * flag[21] + 232 * flag[22] + 60 * flag[23] + 230
* flag[24] + 148 * flag[25] + 231 * flag[26] + 142 * flag[27] + 200 * flag[28] +
58 * flag[29] + 159 * flag[30] + 115 * flag[31] == 449324)
s.add(133 * flag[0] + 210 * flag[1] + 80 * flag[2] + 161 * flag[3] + 89 * flag[4]
+ 72 * flag[5] + 238 * flag[6] + 201 * flag[7] + 138 * flag[8] + 44 * flag[9] +
17 * flag[10] + 204 * flag[11] + 99 * flag[12] + 24 * flag[13] + 183 * flag[14] +
165 * flag[15] + 122 * flag[16] + 9 * flag[17] + 236 * flag[18] + 121 * flag[19]
+ 12 * flag[20] + 216 * flag[21] + 254 * flag[22] + 229 * flag[23] + 0 * flag[24]
+ 74 * flag[25] + 1 * flag[26] + 60 * flag[27] + 157 * flag[28] + 57 * flag[29] +
211 * flag[30] + 225 * flag[31] == 386144)
s.add(99 * flag[0] + 176 * flag[1] + 75 * flag[2] + 47 * flag[3] + 143 * flag[4]
+ 37 * flag[5] + 24 * flag[6] + 124 * flag[7] + 79 * flag[8] + 118 * flag[9] + 13
* flag[10] + 97 * flag[11] + 224 * flag[12] + 71 * flag[13] + 192 * flag[14] + 67
* flag[15] + 139 * flag[16] + 104 * flag[17] + 94 * flag[18] + 74 * flag[19] +
183 * flag[20] + 16 * flag[21] + 185 * flag[22] + 244 * flag[23] + 220 * flag[24]
+ 134 * flag[25] + 108 * flag[26] + 146 * flag[27] + 132 * flag[28] + 159 *
flag[29] + 230 * flag[30] + 84 * flag[31] == 384126)
s.add(208 * flag[0] + 9 * flag[1] + 40 * flag[2] + 50 * flag[3] + 158 * flag[4] +
150 * flag[5] + 42 * flag[6] + 5 * flag[7] + 104 * flag[8] + 85 * flag[9] + 73 *
flag[10] + 255 * flag[11] + 230 * flag[12] + 238 * flag[13] + 160 * flag[14] +
233 * flag[15] + 61 * flag[16] + 138 * flag[17] + 1 * flag[18] + 86 * flag[19] +
117 * flag[20] + 209 * flag[21] + 39 * flag[22] + 166 * flag[23] + 74 * flag[24]
+ 62 * flag[25] + 162 * flag[26] + 21 * flag[27] + 51 * flag[28] + 26 * flag[29]
+ 19 * flag[30] + 112 * flag[31] == 295799)
s.add(104 * flag[0] + 148 * flag[1] + 181 * flag[2] + 240 * flag[3] + 137 *
flag[4] + 195 * flag[5] + 196 * flag[6] + 225 * flag[7] + 233 * flag[8] + 37 *
flag[9] + 52 * flag[10] + 194 * flag[11] + 10 * flag[12] + 175 * flag[13] + 219 *
flag[14] + 45 * flag[15] + 85 * flag[16] + 185 * flag[17] + 111 * flag[18] + 244
* flag[19] + 76 * flag[20] + 207 * flag[21] + 95 * flag[22] + 87 * flag[23] + 220
* flag[24] + 26 * flag[25] + 227 * flag[26] + 132 * flag[27] + 88 * flag[28] + 56
* flag[29] + 193 * flag[30] + 135 * flag[31] == 432591)
s.add(246 * flag[0] + 155 * flag[1] + 163 * flag[2] + 242 * flag[3] + 116 *
flag[4] + 254 * flag[5] + 157 * flag[6] + 104 * flag[7] + 230 * flag[8] + 202 *
flag[9] + 205 * flag[10] + 236 * flag[11] + 49 * flag[12] + 198 * flag[13] + 7 *
flag[14] + 221 * flag[15] + 124 * flag[16] + 128 * flag[17] + 134 * flag[18] + 86
* flag[19] + 174 * flag[20] + 74 * flag[21] + 70 * flag[22] + 56 * flag[23] + 99
* flag[24] + 231 * flag[25] + 119 * flag[26] + 218 * flag[27] + 219 * flag[28] +
75 * flag[29] + 60 * flag[30] + 97 * flag[31] == 446934)
s.add(112 * flag[0] + 248 * flag[1] + 96 * flag[2] + 60 * flag[3] + 67 * flag[4]
+ 0 * flag[5] + 179 * flag[6] + 237 * flag[7] + 15 * flag[8] + 206 * flag[9] + 73
* flag[10] + 35 * flag[11] + 16 * flag[12] + 228 * flag[13] + 201 * flag[14] + 78
* flag[15] + 191 * flag[16] + 137 * flag[17] + 108 * flag[18] + 88 * flag[19] +
95 * flag[20] + 102 * flag[21] + 6 * flag[22] + 239 * flag[23] + 1 * flag[24] +
175 * flag[25] + 227 * flag[26] + 31 * flag[27] + 128 * flag[28] + 106 * flag[29]
+ 186 * flag[30] + 204 * flag[31] == 387609)
s.add(168 * flag[0] + 195 * flag[1] + 238 * flag[2] + 204 * flag[3] + 128 *
flag[4] + 102 * flag[5] + 116 * flag[6] + 36 * flag[7] + 96 * flag[8] + 98 *
flag[9] + 136 * flag[10] + 51 * flag[11] + 70 * flag[12] + 222 * flag[13] + 82 *
flag[14] + 91 * flag[15] + 247 * flag[16] + 97 * flag[17] + 52 * flag[18] + 9 *
flag[19] + 48 * flag[20] + 254 * flag[21] + 236 * flag[22] + 245 * flag[23] + 113
* flag[24] + 119 * flag[25] + 104 * flag[26] + 172 * flag[27] + 143 * flag[28] +
44 * flag[29] + 146 * flag[30] + 135 * flag[31] == 396022)
s.add(204 * flag[0] + 241 * flag[1] + 244 * flag[2] + 234 * flag[3] + 168 *
flag[4] + 8 * flag[5] + 189 * flag[6] + 12 * flag[7] + 112 * flag[8] + 71 *
flag[9] + 33 * flag[10] + 201 * flag[11] + 102 * flag[12] + 255 * flag[13] + 7 *
flag[14] + 24 * flag[15] + 209 * flag[16] + 238 * flag[17] + 172 * flag[18] + 107
* flag[19] + 57 * flag[20] + 75 * flag[21] + 167 * flag[22] + 18 * flag[23] + 151
* flag[24] + 108 * flag[25] + 207 * flag[26] + 120 * flag[27] + 45 * flag[28] +
184 * flag[29] + 157 * flag[30] + 217 * flag[31] == 412078)
s.add(213 * flag[0] + 44 * flag[1] + 103 * flag[2] + 148 * flag[3] + 202 *
flag[4] + 168 * flag[5] + 22 * flag[6] + 225 * flag[7] + 104 * flag[8] + 13 *
flag[9] + 189 * flag[10] + 230 * flag[11] + 157 * flag[12] + 211 * flag[13] + 199
* flag[14] + 115 * flag[15] + 86 * flag[16] + 37 * flag[17] + 97 * flag[18] + 185
* flag[19] + 208 * flag[20] + 70 * flag[21] + 218 * flag[22] + 239 * flag[23] +
224 * flag[24] + 117 * flag[25] + 42 * flag[26] + 125 * flag[27] + 217 * flag[28]
+ 0 * flag[29] + 151 * flag[30] + 176 * flag[31] == 426365)
s.add(62 * flag[0] + 162 * flag[1] + 20 * flag[2] + 142 * flag[3] + 172 * flag[4]
+ 198 * flag[5] + 184 * flag[6] + 182 * flag[7] + 169 * flag[8] + 191 * flag[9] +
124 * flag[10] + 129 * flag[11] + 215 * flag[12] + 246 * flag[13] + 8 * flag[14]
+ 180 * flag[15] + 190 * flag[16] + 74 * flag[17] + 69 * flag[18] + 84 * flag[19]
+ 66 * flag[20] + 107 * flag[21] + 115 * flag[22] + 139 * flag[23] + 161 *
flag[24] + 67 * flag[25] + 255 * flag[26] + 31 * flag[27] + 197 * flag[28] + 216
* flag[29] + 49 * flag[30] + 76 * flag[31] == 406285)
s.add(98 * flag[0] + 46 * flag[1] + 241 * flag[2] + 166 * flag[3] + 156 * flag[4]
+ 208 * flag[5] + 58 * flag[6] + 216 * flag[7] + 28 * flag[8] + 32 * flag[9] + 24
* flag[10] + 60 * flag[11] + 15 * flag[12] + 18 * flag[13] + 49 * flag[14] + 33 *
flag[15] + 188 * flag[16] + 106 * flag[17] + 85 * flag[18] + 119 * flag[19] + 90
* flag[20] + 231 * flag[21] + 164 * flag[22] + 168 * flag[23] + 186 * flag[24] +
62 * flag[25] + 158 * flag[26] + 36 * flag[27] + 117 * flag[28] + 239 * flag[29]
+ 251 * flag[30] + 250 * flag[31] == 379323)
s.add(92 * flag[0] + 140 * flag[1] + 109 * flag[2] + 252 * flag[3] + 165 *
flag[4] + 130 * flag[5] + 164 * flag[6] + 151 * flag[7] + 38 * flag[8] + 85 *
flag[9] + 65 * flag[10] + 53 * flag[11] + 191 * flag[12] + 37 * flag[13] + 189 *
flag[14] + 79 * flag[15] + 41 * flag[16] + 104 * flag[17] + 2 * flag[18] + 106 *
flag[19] + 154 * flag[20] + 77 * flag[21] + 34 * flag[22] + 198 * flag[23] + 232
* flag[24] + 131 * flag[25] + 11 * flag[26] + 248 * flag[27] + 185 * flag[28] +
249 * flag[29] + 84 * flag[30] + 83 * flag[31] == 401097)
s.add(60 * flag[0] + 197 * flag[1] + 144 * flag[2] + 84 * flag[3] + 48 * flag[4]
+ 14 * flag[5] + 103 * flag[6] + 244 * flag[7] + 123 * flag[8] + 67 * flag[9] +
33 * flag[10] + 128 * flag[11] + 93 * flag[12] + 90 * flag[13] + 166 * flag[14] +
211 * flag[15] + 167 * flag[16] + 233 * flag[17] + 50 * flag[18] + 161 * flag[19]
+ 116 * flag[20] + 141 * flag[21] + 98 * flag[22] + 122 * flag[23] + 229 *
flag[24] + 224 * flag[25] + 162 * flag[26] + 21 * flag[27] + 117 * flag[28] + 44
* flag[29] + 230 * flag[30] + 192 * flag[31] == 390156)
print(s.check())
print(s.model())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;实际上这个你真用WSL跑是搞不出来的：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;缩进错误&lt;/strong&gt;：&lt;code&gt;for i in range(32):&lt;/code&gt; 下面的 &lt;code&gt;s.add(...)&lt;/code&gt; 必须缩进，否则会报 &lt;code&gt;IndentationError&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Or&lt;/code&gt; 的用法错误&lt;/strong&gt;：&lt;code&gt;Or([...])&lt;/code&gt; 应该改为 &lt;code&gt;Or(*[...])&lt;/code&gt;，因为 Z3 的 &lt;code&gt;Or&lt;/code&gt; 需要多个参数，而不是一个列表。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;变量类型问题&lt;/strong&gt;：用 &lt;code&gt;BitVec(8)&lt;/code&gt; 会导致乘法溢出（模256），而方程中的常数很大，应该用 &lt;code&gt;Int&lt;/code&gt; 类型。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;重点讲讲第三条吧，昨晚主要主要卡在这了&lt;/p&gt;
&lt;h2&gt;Int 与 BitVec的区别&lt;/h2&gt;
&lt;p&gt;有这样一个方程：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;200 * x = 400
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果x是整数，那解为x=2&lt;/p&gt;
&lt;p&gt;但如果x是8位计算机的整数，那乘法就会所谓溢出：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;200 × 2 = 400，但 400 超过了 255，计算机只保留 400 mod 256 = 144。
所以实际方程变成了 200 × x = 144，解就不是 2 了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;使用z3,只要将看到的题目伪代码方程翻译成等式，加入字符集约束，即可求解&lt;/p&gt;
&lt;h2&gt;方程列表&lt;/h2&gt;
&lt;p&gt;刚刚的AURORA版本中代码大部分是那32个方程，伪代码中:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;117 * SBYTE6(v7) + 79 * SBYTE5(v7) + ...  == 422531
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后就是建立映射表，将每个宏替换成flag数组中的对映元素。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;85 * flag[0] + 185 * flag[1] + ... + 98 * flag[31] == 422531
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;还有处理位移和括号，将&quot;&amp;lt;&amp;lt;&quot;变成乘法之类的&lt;/p&gt;
&lt;p&gt;之后将等式化成Z3约束&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在Z3中，我们需要定义一个长度为32的整数变量列表&lt;code&gt;flag[i]&lt;/code&gt;，然后对每个等式，用&lt;code&gt;sum(coeff[i] * flag[i]) == const&lt;/code&gt;的形式添加约束。&lt;/p&gt;
&lt;p&gt;先前WP中给出的代码是用&lt;code&gt;BitVec&lt;/code&gt;写的，但这是错误的，因为系数和常数很大，乘法会溢出。正确的做法是用&lt;code&gt;Int&lt;/code&gt;类型。我们修正后的脚本就是用了&lt;code&gt;Int&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;最后添加字符集约束&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;从&lt;code&gt;check_char&lt;/code&gt;函数可以知道，每个字符必须属于一个特定的字母表。WP中给出了字母表&lt;code&gt;b&quot;AUVabcdhikorsuvyz012345{}_&apos;&quot;&lt;/code&gt;，我们需要将每个变量限制为这个集合中的值。这通过&lt;code&gt;Or(*[flag[i] == c for c in alphabet])&lt;/code&gt;实现。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;anger&lt;/h2&gt;
&lt;p&gt;当然，对于B哥来说，现在就需要一点节约时间且简单的脚本来跳过调整31个方程这个过程&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3
import angr
import claripy
import logging

# 关闭烦人的警告，保持输出简洁（可选）
logging.getLogger(&apos;angr&apos;).setLevel(&apos;ERROR&apos;)

def main():
    # -------------------- 用户配置区域 --------------------
    # 目标程序路径（相对或绝对路径）
    BINARY_PATH = &quot;./challenge.exe&quot;

    # 输入的长度（字节数），如果未知可以设大一点，但会增加求解时间
    INPUT_LEN = 32

    # 成功条件：可以是地址，也可以是自定义函数
    # 方式1：使用地址（推荐，最稳定）
    SUCCESS_ADDR = 0x40517C   # 替换为输出成功信息的指令地址
    FAILURE_ADDR = 0x40518A   # 替换为输出失败信息的指令地址（可选）

    # 方式2：使用输出字符串（如果地址难找）
    # 定义 success 函数，当程序输出包含特定字符串时认为成功
    # def is_success(state):
    #     return b&quot;you saved&quot; in state.posix.dumps(1)
    # def is_avoid(state):
    #     return b&quot;messed up&quot; in state.posix.dumps(1) or b&quot;nope&quot; in state.posix.dumps(1)

    # 是否已知输入字符的范围（例如可打印ASCII），可以加速求解
    USE_CHAR_CONSTRAINT = True
    MIN_CHAR = 32          # 最小允许的ASCII码
    MAX_CHAR = 126         # 最大允许的ASCII码
    # 或者指定具体字母表（例如 b&quot;ABCDEFG...&quot;）
    # ALPHABET = b&quot;ABCDEFG{}_&quot;

    # 是否启用 veritesting（合并路径，强烈推荐）
    USE_VERITESTING = True

    # 是否填充未初始化的寄存器/内存为0（减少符号变量，提高效率）
    ZERO_FILL_UNCONSTRAINED = True
    # ----------------------------------------------------

    # 1. 加载二进制文件
    proj = angr.Project(BINARY_PATH, auto_load_libs=False)

    # 2. 创建符号输入（不加换行符，因为题目通常自己读一行）
    flag_chars = [claripy.BVS(f&apos;c{i}&apos;, 8) for i in range(INPUT_LEN)]
    flag = claripy.Concat(*flag_chars)

    # 3. 构造初始状态，将符号输入绑定到 stdin
    state = proj.factory.entry_state(stdin=flag)

    # 4. 可选优化：填充未初始化的寄存器/内存为0
    if ZERO_FILL_UNCONSTRAINED:
        state.options.add(angr.options.ZERO_FILL_UNCONSTRAINED_REGISTERS)
        state.options.add(angr.options.ZERO_FILL_UNCONSTRAINED_MEMORY)

    # 5. 添加字符范围约束（如果已知）
    if USE_CHAR_CONSTRAINT:
        # 方式A：范围约束
        for c in flag_chars:
            state.solver.add(c &amp;gt;= MIN_CHAR, c &amp;lt;= MAX_CHAR)
        # 方式B：具体字母表（注释掉上面，改用下面）
        # for c in flag_chars:
        #     state.solver.add(Or(*[c == ord(ch) for ch in ALPHABET]))

    # 6. 创建 simulation manager
    if USE_VERITESTING:
        simgr = proj.factory.simulation_manager(state, veritesting=True)
    else:
        simgr = proj.factory.simulation_manager(state)

    # 7. 设置探索目标
    # 如果使用地址方式
    if &apos;SUCCESS_ADDR&apos; in dir() and SUCCESS_ADDR is not None:
        simgr.explore(find=SUCCESS_ADDR, avoid=FAILURE_ADDR if &apos;FAILURE_ADDR&apos; in dir() else None)
    # 如果使用字符串方式
    elif &apos;is_success&apos; in dir():
        simgr.explore(find=is_success, avoid=is_avoid if &apos;is_avoid&apos; in dir() else None)
    else:
        print(&quot;[-] 请定义成功条件（地址或函数）&quot;)
        return

    # 8. 输出结果
    if simgr.found:
        found = simgr.found[0]
        # 求解符号输入的具体值
        flag_value = found.solver.eval(flag, cast_to=bytes)
        print(&quot;[+] Flag found:&quot;, flag_value.decode(&apos;ascii&apos;, errors=&apos;ignore&apos;))
        # 如果 flag 包含不可见字符，可以用十六进制打印
        # print(&quot;[+] Flag (hex):&quot;, flag_value.hex())
    else:
        print(&quot;[-] No solution found&quot;)

if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;BINARY_PATH&lt;/strong&gt;：你的目标程序路径。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;INPUT_LEN&lt;/strong&gt;：输入长度（字节）。如果未知，可以先设大一点（如 64），但会增加求解时间。通常题目会明确提示（如 &lt;code&gt;printf(&quot;input %d chars:&quot;)&lt;/code&gt;）或可以从 &lt;code&gt;check_char&lt;/code&gt; 循环次数推断。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;成功地址&lt;/strong&gt;：这是最关键的一步。你需要用 IDA 找到输出成功信息的指令地址。例如，在本题中是 &lt;code&gt;0x40517C&lt;/code&gt;。如果找不到地址，可以改用字符串匹配方式（注释掉的 &lt;code&gt;is_success&lt;/code&gt; 函数），但地址方式更可靠。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;失败地址&lt;/strong&gt;（可选）：告诉 angr 避开哪些路径，可以大幅加速。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;字符范围&lt;/strong&gt;：如果已知输入只能是可打印字符或特定字母表，添加约束能缩小解空间，加速求解。如果不确定，可以注释掉，让 angr 自由探索。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;常见问题与调整&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;地址不对&lt;/strong&gt;：如果程序开启了 PIE（地址随机化），你在 IDA 中看到的可能是文件偏移，需要在脚本中加上基址（通常为 &lt;code&gt;0x400000&lt;/code&gt;）或先在 IDA 中 rebase 到 &lt;code&gt;0x400000&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;路径爆炸&lt;/strong&gt;：启用 &lt;code&gt;veritesting=True&lt;/code&gt; 可以合并相似路径，极大缓解路径爆炸。如果还不行，可以尝试手动添加更多 &lt;code&gt;avoid&lt;/code&gt; 地址（如所有错误输出的地址）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;长时间无结果&lt;/strong&gt;：可以尝试去掉字符范围约束，或改用 &lt;code&gt;simgr.run()&lt;/code&gt; 然后手动查看状态，但通常 &lt;code&gt;explore&lt;/code&gt; 就够了。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;输入包含换行符&lt;/strong&gt;：大多数程序用 &lt;code&gt;gets&lt;/code&gt; 或 &lt;code&gt;scanf&lt;/code&gt; 读取字符串，不会把换行符存入缓冲区，所以我们构造符号输入时&lt;strong&gt;不加换行符&lt;/strong&gt;。如果程序确实需要换行符（例如用 &lt;code&gt;read&lt;/code&gt; 读取固定长度），可以在 &lt;code&gt;flag&lt;/code&gt; 后面加上 &lt;code&gt;claripy.BVV(b&apos;\n&apos;)&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个题目，需要这样更改：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;BINARY_PATH = &quot;./homework.exe&quot;
INPUT_LEN = 32
SUCCESS_ADDR = 0x40517C
FAILURE_ADDR = 0x40518A
USE_CHAR_CONSTRAINT = True
MIN_CHAR = 32
MAX_CHAR = 126
USE_VERITESTING = True
ZERO_FILL_UNCONSTRAINED = True
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是，我选择的是直接让ai给我修映射，因为当时用这个找答案的时候总是遇到卡死的问题。&lt;/p&gt;
&lt;p&gt;然后在WSL上操作&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd /mnt/你的地址
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;确保该目录下存在你的文件。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;然后激活 conda 环境&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;conda activate deflat
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;确保命令行前缀变成 &lt;code&gt;(deflat)&lt;/code&gt;，表示已进入正确的 Python 环境。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;然后就是python的事了&lt;/p&gt;
&lt;h1&gt;反思&lt;/h1&gt;
&lt;p&gt;我倒是觉得没啥好反思的，因为这是我遇到过的的第一个这种类型的题目，不过这个过程倒是学到很多东西，不赖&lt;/p&gt;
&lt;h1&gt;附件&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;题目&lt;a href=&quot;/other/homework.exe&quot;&gt;homework.exe&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我昨晚用这个做出来的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from z3 import *

flag = [Int(f&apos;flag{i}&apos;) for i in range(32)]
alphabet = [ord(c) for c in &quot;AUVabcdhikorsuvyz012345{}_&apos;&quot;]
s = Solver()

# 每个字符必须在字母表中
for i in range(32):
    s.add(Or(*[flag[i] == c for c in alphabet]))

# 32个方程的系数和常数
equations = [
    ([85,185,80,81,130,95,126,197,148,228,213,107,195,183,200,47,194,16,51,222,191,44,153,217,192,91,56,178,27,79,117,98], 422531),
    ([93,128,111,16,163,215,223,51,81,1,198,247,88,176,110,250,196,161,63,159,191,239,69,119,136,30,86,185,14,26,50,237], 377679),
    ([17,108,169,83,129,191,3,249,163,138,2,123,23,162,155,152,46,80,215,6,229,128,53,255,71,212,208,47,36,75,218,144], 377733),
    ([204,247,15,66,166,87,102,186,82,141,9,120,145,69,18,65,137,171,167,184,183,251,153,3,84,201,189,34,79,64,230,197], 382101),
    ([234,225,195,117,8,89,254,173,251,223,87,165,114,242,232,126,105,121,155,194,64,154,120,80,151,111,144,243,159,239,93,97], 476378),
    ([32,64,155,131,161,223,210,15,205,164,146,27,108,35,63,87,215,163,170,95,70,60,81,241,122,50,123,177,172,106,51,230], 403748),
    ([204,78,92,64,125,161,185,194,74,1,155,109,132,19,110,201,29,3,177,53,129,224,205,146,71,128,203,145,68,100,80,57], 345842),
    ([169,98,28,204,149,252,172,141,134,116,39,48,247,128,164,43,7,245,241,15,93,197,209,100,25,129,227,58,54,123,5,118], 373897),
    ([187,133,70,244,169,168,172,48,119,155,127,222,170,217,107,88,162,206,75,2,7,184,130,192,163,255,196,221,27,62,213,248], 452443),
    ([200,18,135,79,150,193,78,245,164,85,192,66,209,250,44,196,141,139,224,252,181,8,180,2,112,58,191,39,242,14,169,99], 403460),
    ([48,131,229,237,68,174,71,87,89,121,206,151,3,51,187,5,8,32,45,63,211,28,169,243,39,216,120,30,177,189,209,73], 375277),
    ([196,107,36,163,229,244,181,71,141,44,126,43,110,198,66,18,197,56,33,2,98,13,130,180,133,228,46,171,40,84,27,94], 346617),
    ([153,129,215,90,245,247,84,192,108,116,38,255,123,32,107,121,87,99,35,6,23,1,175,66,111,134,79,73,8,193,154,229], 359346),
    ([32,179,26,178,25,175,96,195,47,174,207,198,33,66,71,248,128,162,254,59,123,135,63,57,75,210,34,64,13,166,165,78], 349475),
    ([138,90,102,94,254,111,34,165,125,133,80,92,77,141,126,196,45,61,25,14,5,135,168,63,186,229,234,85,119,239,214,248], 384145),
    ([91,208,133,151,177,24,188,90,243,163,34,98,124,76,99,201,212,71,107,164,233,221,197,19,152,122,144,77,128,92,168,74], 409437),
    ([184,154,147,138,42,171,174,164,134,85,99,116,136,213,3,2,156,217,209,95,202,145,25,216,251,165,225,83,34,140,135,77], 417097),
    ([202,130,89,99,194,120,93,126,107,32,249,28,109,179,12,183,193,175,207,215,7,55,180,10,237,151,164,106,154,29,39,60], 361950),
    ([5,177,125,162,194,26,168,198,195,39,109,43,240,217,14,90,218,154,220,197,202,36,232,60,230,148,231,142,200,58,159,115], 449324),
    ([133,210,80,161,89,72,238,201,138,44,17,204,99,24,183,165,122,9,236,121,12,216,254,229,0,74,1,60,157,57,211,225], 386144),
    ([99,176,75,47,143,37,24,124,79,118,13,97,224,71,192,67,139,104,94,74,183,16,185,244,220,134,108,146,132,159,230,84], 384126),
    ([208,9,40,50,158,150,42,5,104,85,73,255,230,238,160,233,61,138,1,86,117,209,39,166,74,62,162,21,51,26,19,112], 295799),
    ([104,148,181,240,137,195,196,225,233,37,52,194,10,175,219,45,85,185,111,244,76,207,95,87,220,26,227,132,88,56,193,135], 432591),
    ([246,155,163,242,116,254,157,104,230,202,205,236,49,198,7,221,124,128,134,86,174,74,70,56,99,231,119,218,219,75,60,97], 446934),
    ([112,248,96,60,67,0,179,237,15,206,73,35,16,228,201,78,191,137,108,88,95,102,6,239,1,175,227,31,128,106,186,204], 387609),
    ([168,195,238,204,128,102,116,36,96,98,136,51,70,222,82,91,247,97,52,9,48,254,236,245,113,119,104,172,143,44,146,135], 396022),
    ([204,241,244,234,168,8,189,12,112,71,33,201,102,255,7,24,209,238,172,107,57,75,167,18,151,108,207,120,45,184,157,217], 412078),
    ([213,44,103,148,202,168,22,225,104,13,189,230,157,211,199,115,86,37,97,185,208,70,218,239,224,117,42,125,217,0,151,176], 426365),
    ([62,162,20,142,172,198,184,182,169,191,124,129,215,246,8,180,190,74,69,84,66,107,115,139,161,67,255,31,197,216,49,76], 406285),
    ([98,46,241,166,156,208,58,216,28,32,24,60,15,18,49,33,188,106,85,119,90,231,164,168,186,62,158,36,117,239,251,250], 379323),
    ([92,140,109,252,165,130,164,151,38,85,65,53,191,37,189,79,41,104,2,106,154,77,34,198,232,131,11,248,185,249,84,83], 401097),
    ([60,197,144,84,48,14,103,244,123,67,33,128,93,90,166,211,167,233,50,161,116,141,98,122,229,224,162,21,117,44,230,192], 390156),
]

for coeffs, const in equations:
    expr = sum(coeff * flag[i] for i, coeff in enumerate(coeffs))
    s.add(expr == const)

if s.check() == sat:
    m = s.model()
    result = &apos;&apos;.join(chr(m[flag[i]].as_long()) for i in range(32))
    print(&quot;Flag:&quot;, result)
else:
    print(&quot;No solution&quot;)
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>今日一题</title><link>https://dxfaker.top/posts/pywp1/</link><guid isPermaLink="true">https://dxfaker.top/posts/pywp1/</guid><description>解题思路之类的</description><pubDate>Mon, 02 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;写在前面&lt;/h1&gt;
&lt;p&gt;镇楼：这样的好兄弟不会再有了&lt;/p&gt;
&lt;p&gt;作为刚踏进这个世界的蛆，自然看到这种加密代码很是头疼，于是便记录下来，以便于之后理清逻辑&lt;/p&gt;
&lt;h1&gt;题目&lt;/h1&gt;
&lt;h2&gt;[SWPUCTF 2021 新生赛]简简单单的解密&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import base64,urllib.parse
key = &quot;HereIsFlagggg&quot;
flag = &quot;xxxxxxxxxxxxxxxxxxx&quot;

s_box = list(range(256))
j = 0
for i in range(256):
    j = (j + s_box[i] + ord(key[i % len(key)])) % 256
    s_box[i], s_box[j] = s_box[j], s_box[i]
res = []
i = j = 0
for s in flag:
    i = (i + 1) % 256 #我够吧一时间没反应过来,这个的结果是它本身(i+1)
    j = (j + s_box[i]) % 256
    s_box[i], s_box[j] = s_box[j], s_box[i]
    t = (s_box[i] + s_box[j]) % 256
    k = s_box[t]
    res.append(chr(ord(s) ^ k))
cipher = &quot;&quot;.join(res)
crypt = (str(base64.b64encode(cipher.encode(&apos;utf-8&apos;)), &apos;utf-8&apos;))
enc = str(base64.b64decode(crypt),&apos;utf-8&apos;)
enc = urllib.parse.quote(enc)
print(enc)
# enc = %C2%A6n%C2%87Y%1Ag%3F%C2%A01.%C2%9C%C3%B7%C3%8A%02%C3%80%C2%92W%C3%8C%C3%BA
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代码解析&lt;/p&gt;
&lt;h3&gt;range(256)&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;在 Python 中，range(256) 是一个内置函数 range() 的调用，它生成一个不可变的整数序列，从 0 开始，一直到 255（即包含 0，不包含 256）。换句话说，它产生的数字是：0, 1, 2, 3, ..., 254, 255，总共 256 个数。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;   s_box[i], s_box[j] = s_box[j], s_box[i]
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Python 在赋值时，会先计算等号右边的所有表达式，然后把结果
同时赋值给左边的变量。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所以这里：
先取出 s_box[j] 的值，记为 temp_j。
再取出 s_box[i] 的值，记为 temp_i。
然后将 temp_j 赋给 s_box[i]，将 temp_i 赋给 s_box[j]。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这行代码会把 s_box[i] 和 s_box[j] 的值互相交换。&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;crypt = (str(base64.b64encode(cipher.encode(&apos;utf-8&apos;)), &apos;utf-8&apos;))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这一行代码的作用是将字符串 cipher 进行 Base64 编码，并将结果以字符串形式赋值给变量 crypt。下面逐步拆解：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;cipher.encode(&apos;utf-8&apos;)
将字符串 cipher 按照 UTF-8 编码转换为字节串（bytes 类型），因为 Base64 编码操作需要输入字节数据。&lt;/p&gt;
&lt;p&gt;base64.b64encode(...)
对得到的字节串进行 Base64 编码，返回一个新的字节串，内容为 Base64 编码后的 ASCII 字符（例如 b&apos;aGVsbG8=&apos;）。&lt;/p&gt;
&lt;p&gt;str(..., &apos;utf-8&apos;)
将 Base64 编码后的字节串使用 UTF-8 解码为普通的 Python 字符串。这里 str(字节串, encoding) 等价于 字节串.decode(encoding)。由于 Base64 结果只包含 ASCII 字符，用 UTF-8 解码完全正确。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;最终 crypt 就得到了一个不含 b&apos;&apos; 前缀的纯 Base64 字符串，可以直接用于后续处理或输出。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;enc = urllib.parse.quote(enc)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这行代码的作用是对字符串 enc 进行 URL 编码（也叫百分号编码），然后将编码后的结果重新赋值给 enc。&lt;/p&gt;
&lt;h3&gt;草稿部分&lt;/h3&gt;
&lt;p&gt;遍历256
j+=执行的次数+ key的第(i取key长度的余数)位数字符的acsii
交换sbox中的i,j位数据&lt;/p&gt;
&lt;p&gt;for s in flag
i+=1
j=+sbox的j位元素
交换sbox中的i,j位数据
t = sbox中的i和j位数据相加(不过256)
k = sbox[t]
res有序放入(s转码)^k后的转换的字符
cipher = res全部字符连成一串
crypt = 一个不含 b&apos;&apos; 前缀的cipher纯 Base64 字符串，可以直接用于后续处理或输出。
enc = utf-8的将ctypt字符串化作原始的字符串的字符串
对enc进行 url编码&lt;/p&gt;
&lt;h2&gt;答案&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import base64,urllib.parse
key = &quot;HereIsFlagggg&quot;
enc = &quot;%C2%A6n%C2%87Y%1Ag%3F%C2%A01.%C2%9C%C3%B7%C3%8A%02%C3%80%C2%92W%C3%8C%C3%BA&quot;
enc = urllib.parse.unquote(enc)
s_box = list(range(256))
j = 0

for i in range(256):
    j = (j + s_box[i] + ord(key[i % len(key)])) % 256
    s_box[i], s_box[j] = s_box[j], s_box[i]
res = []
i = j = 0

for s in enc:
    i = (i + 1) % 256
    j = (j + s_box[i]) % 256
    s_box[i], s_box[j] = s_box[j], s_box[i]
    t = (s_box[i] + s_box[j]) % 256
    k = s_box[t]
    res.append(chr(ord(s) ^ k))
cipher = &quot;&quot;.join(res)
print(cipher) 
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;反思&lt;/h2&gt;
&lt;p&gt;实际上，我有一些问题：
·没有总体观念，没有从全局看出他实际上的变动只有a ^ b = c,只需要a = b ^ c即可。
·对python很不熟悉，因为没学过。&lt;/p&gt;
&lt;p&gt;那也就只有多多写写题目多多积累的事情了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>词条本1</title><link>https://dxfaker.top/posts/2632/</link><guid isPermaLink="true">https://dxfaker.top/posts/2632/</guid><description>记录自己遇到过的知识，总会有用的</description><pubDate>Mon, 02 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;写在前面&lt;/h1&gt;
&lt;p&gt;老婆镇楼&lt;/p&gt;
&lt;p&gt;这个就相当于是词条了，我后面复习直接 ctrl+f 用。&lt;/p&gt;
&lt;h1&gt;ylg基础知识&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;这个就不是写给我看的了&lt;/strong&gt;(bushi)&lt;/p&gt;
&lt;h2&gt;内联循环&lt;/h2&gt;
&lt;p&gt;在编程中，“内联”（inline）指的是将函数体的代码直接插入到调用它的地方，而不是通过常规的 call 指令跳转过去。将函数体的循环直接插入，循环次数由运行时决定。这样做的好处是：&lt;/p&gt;
&lt;p&gt;·省去了函数调用和返回时的栈操作（压栈、跳转、恢复等），提高执行速度。&lt;/p&gt;
&lt;p&gt;·对于小函数尤其有利，比如 strlen、memcpy 等。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;size_t strlen(const char *s) {
    size_t len = 0;
    while (*s != &apos;\0&apos;) {
        len++;
        s++;
    }
    return len;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果编译器决定内联这个函数，那么在调用 strlen(str) 的地方，它不会生成 call strlen，而是直接生成一段循环指令来扫描字符串。&lt;/p&gt;
&lt;p&gt;使用IDA反汇编时,可能会遇到的两种情况：&lt;/p&gt;
&lt;p&gt;·直接调用 strlen：反编译窗口会显示&lt;code&gt;strlen(local_58)&lt;/code&gt;，容易识别。&lt;/p&gt;
&lt;p&gt;·内联循环：你可能会看到一段像上面那样的指令序列，Ghidra 有时能识别并注释为 strlen，有时则显示为一段自定义的循环。你需要根据指令模式（如 repne scasb）或循环逻辑判断它是在计算长度。&lt;/p&gt;
&lt;p&gt;反汇编出来的伪代码类似于:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for (i = 0; s[i] != 0; i = i + 1) {
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;i 是一个整数变量（通常从 0 开始）。
s 是一个指向字符串的指针（比如 char *s）。
循环条件是 s[i] != 0，也就是当前字符不是字符串结束符 \0。
每次循环，i 增加 1，但循环体是空的（大括号里什么都没有）。
当遇到 \0 时，循环结束，此时 i 的值就是字符串的长度（因为 \0 本身没有被计入）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;内联循环 = 编译器将小函数（如 strlen）的循环体直接嵌入到调用处，避免函数调用开销。&lt;/p&gt;
&lt;h2&gt;内存单元&lt;/h2&gt;
&lt;p&gt;定义：内存中用于存储数据的基本单位，通常指一个字节（8 位）。每个内存单元有一个唯一的地址（从 0 开始编号），CPU 通过地址访问这些单元。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;mov al, [0x1234] &lt;/code&gt;表示从内存地址 0x1234 处读取一个字节到 AL 寄存器。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;寄存器&lt;/h2&gt;
&lt;p&gt;定义：CPU 内部的高速存储单元，用于暂存指令、数据或地址。它们是 CPU 计算的核心。&lt;/p&gt;
&lt;p&gt;通用寄存器：EAX、EBX、ECX、EDX 等，用于算术运算、数据暂存。
指针寄存器：ESP（栈指针）、EBP（基址指针），用于管理栈。
指令指针：EIP，存放下一条要执行指令的地址。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;add eax, ebx &lt;/code&gt;表示将 EAX 和 EBX 的值相加，结果存回 EAX。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;段寄存器&lt;/h2&gt;
&lt;p&gt;定义：x86 架构特有的寄存器，用于支持内存分段管理。每个段寄存器（如 CS、DS、SS、ES）存放一个段选择子（在保护模式下）或段基址（在实模式下）。
作用：与偏移地址组合，形成最终的物理地址或线性地址。&lt;/p&gt;
&lt;p&gt;常见段寄存器：&lt;/p&gt;
&lt;p&gt;CS（代码段）：指向存放指令的段。
DS（数据段）：指向存放数据的段。
SS（栈段）：指向栈所在段。
ES、FS、GS（附加段）：用于其他数据访问。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;mov ax, [bx]&lt;/code&gt; 隐含使用 DS 段寄存器，即实际访问的是 DS:BX 指向的内存单元。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;ASCII 字符&lt;/h2&gt;
&lt;p&gt;每个字符占用 1 个字节，如 &apos;A&apos; = 0x41。&lt;/p&gt;
&lt;h2&gt;Unicode 字符&lt;/h2&gt;
&lt;h3&gt;UTF-16&lt;/h3&gt;
&lt;p&gt;每个字符占用 2 个字节（字），常见于 Windows 程序（如 wchar_t）。&lt;/p&gt;
&lt;h3&gt;UTF-8&lt;/h3&gt;
&lt;p&gt;变长编码，英文 1 字节，中文通常 3 字节。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在逆向中，如果看到连续的两个字节组成一个字符，很可能是 UTF-16 字符串，在 Ghidra 中需将数据类型设置为 unicode 才能正确显示。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;汇编&lt;/h1&gt;
&lt;p&gt;这个模块的寄存器就不区分大小写了，汇编的内容是复习用&lt;/p&gt;
&lt;h2&gt;地址加法器&lt;/h2&gt;
&lt;p&gt;物理地址  =  段地址  *  16  +  偏移地址&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;         实则为数据左移四位（二进制位）       
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;衍生：一个数据的x进制形式左移1位相当于*x&lt;/p&gt;
&lt;h2&gt;CS,IP&lt;/h2&gt;
&lt;h3&gt;同时修改CS,IP的内容&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;jmp 段地址:偏移地址&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  CS      IP
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;程序&lt;/h2&gt;
&lt;h3&gt;伪指令&lt;/h3&gt;
&lt;h4&gt;定义一个段&lt;/h4&gt;
&lt;p&gt;segment和ends是一对成对使用的伪指令，
segment说明一个段的开始
ends说明一个段的结束
End是一个汇编程序的结束标记
assume:假设&lt;/p&gt;
&lt;h2&gt;栈&lt;/h2&gt;
&lt;p&gt;操作原则:LIFO(lAST In First Out)&lt;/p&gt;
&lt;p&gt;提供出入栈指令:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;push ax&lt;/code&gt; :将寄存器ax中的数据送入栈中；&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pop ax&lt;/code&gt;  :从栈顶取出数据送入ax&lt;/p&gt;
&lt;h3&gt;栈帧&lt;/h3&gt;
&lt;p&gt;当函数被调用时，会在栈上分配一段空间，称为栈帧，用于存放：&lt;/p&gt;
&lt;p&gt;·局部变量（如数组、结构体、普通变量）&lt;/p&gt;
&lt;p&gt;·函数参数（某些调用约定下）&lt;/p&gt;
&lt;p&gt;·返回地址（函数执行完后要跳回的下一条指令地址）&lt;/p&gt;
&lt;p&gt;·保存的帧指针（如 EBP）&lt;/p&gt;
&lt;h3&gt;栈溢出&lt;/h3&gt;
&lt;p&gt;栈溢出（Stack Overflow）是一种程序漏洞，当程序向栈中某个局部缓冲区写入的数据超过了该缓冲区预分配的大小时，多余的数据就会覆盖栈上相邻的内存区域，包括可能的关键数据（如返回地址、局部变量、保存的寄存器等）。攻击者可以利用这一点篡改程序的行为，甚至执行任意代码。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在程序运行时，栈用于存储：&lt;/p&gt;
&lt;p&gt;局部变量&lt;/p&gt;
&lt;p&gt;函数参数&lt;/p&gt;
&lt;p&gt;返回地址（函数执行完后要跳回的位置）&lt;/p&gt;
&lt;p&gt;保存的寄存器值（如 EBP）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;当一个函数被调用时，会压入返回地址；进入函数后，会分配局部变量空间（通常通过 sub esp, 空间大小）。栈是从高地址向低地址增长的（向下增长）。&lt;/p&gt;
&lt;p&gt;这里我直接用ai的图吧&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
高地址
+-----------------+
| 函数参数        |
+-----------------+
| 返回地址        | ← 函数返回时会跳到这里
+-----------------+
| 保存的 EBP      |
+-----------------+
| 局部变量        | ← 例如 char buf[10]
+-----------------+
低地址

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;用C语言:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void vulnerable() {
    char buffer[8];      // 局部缓冲区，只能容纳 7 个字符 + 结尾的 &apos;\0&apos;
    gets(buffer);        // 从标准输入读取一行，但不检查缓冲区大小
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;·gets() 会一直读取直到遇到换行符，如果用户输入超过 7 个字符（例&quot;AAAAAAAAAAA&quot;），数据就会溢出 buffer 的边界，覆盖掉栈上 buffer 之后的内存。
·被覆盖的内容取决于输入长度：
·如果输入长度刚好 8 字节（含 &apos;\0&apos;），可能会覆盖保存的 EBP。
·如果输入更长，会覆盖返回地址，甚至更上层栈帧的数据。
其他危险函数还包括 strcpy()、strcat()、sprintf() 等，如果不指定长度限制，都可能引发栈溢出。&lt;/p&gt;
&lt;h4&gt;栈溢出应用场景&lt;/h4&gt;
&lt;p&gt;这一部分对逆向也挺重要的&lt;/p&gt;
&lt;h5&gt;覆盖局部变量&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;int authenticated = 0;
char password[8];
gets(password);
if (strcmp(password, &quot;secret&quot;) == 0) {
    authenticated = 1;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;如果输入足够长，可能覆盖 authenticated 变量，使其非零，从而绕过验证。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h5&gt;覆盖保存的 EBP&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;mov ax, 1000H
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;栈段&lt;/h3&gt;
&lt;p&gt;将长度为N(N=&amp;lt;64)的一组地址连续、起始地址为16的倍数的内存单元当作栈来使用，从而定义了一个栈段&lt;/p&gt;
&lt;h2&gt;sub&lt;/h2&gt;
&lt;h3&gt;sub ax,bx&lt;/h3&gt;
&lt;p&gt;表示将寄存器 ax 的值减去寄存器 bx 的值，结果存回 ax。&lt;/p&gt;
&lt;h3&gt;只修改IP内容&lt;/h3&gt;
&lt;p&gt;jmp某一合法寄存器&lt;/p&gt;
&lt;p&gt;&lt;code&gt;jmp ax&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;功能：用寄存器中的值修改IP&lt;/p&gt;
&lt;h3&gt;字型，字节数据&lt;/h3&gt;
&lt;h4&gt;字型数据与字节数据&lt;/h4&gt;
&lt;p&gt;两字节与一字节的区分，N处字型数据读取的时候取N+1和N，从N+1开始读&lt;/p&gt;
&lt;h4&gt;大小端序&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;小端序（Little-Endian）：低位字节存放在低地址，高位字节存放在高地址。x86 架构使用小端序。&lt;/p&gt;
&lt;p&gt;大端序（Big-Endian）：高位字节存放在低地址，低位字节存放在高地址。网络协议、ARM（可配置）等可能使用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;小端序:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;地址 0x1000: 0x78
地址 0x1001: 0x56
地址 0x1002: 0x34
地址 0x1003: 0x12
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;大端序&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;地址 0x1000: 0x12
地址 0x1001: 0x34
地址 0x1002: 0x56
地址 0x1003: 0x78
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;x86 是小端序，看到内存中的字节顺序要反过来理解多字节数值。&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;mov&lt;/h2&gt;
&lt;p&gt;一般有如下形式&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov 寄存器,数据

mov 寄存器,段寄存器

mov 寄存器,寄存器

mov 寄存器,内存单元

mov 内存单元,寄存器

mov 段寄存器,寄存器
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;mov ax,b&lt;/h3&gt;
&lt;p&gt;将b送入ax,即ax=8&lt;/p&gt;
&lt;h3&gt;mov ax,bx&lt;/h3&gt;
&lt;p&gt;将寄存器bx的数据送入ax，ax=bx&lt;/p&gt;
&lt;h2&gt;add&lt;/h2&gt;
&lt;h3&gt;add ax,b&lt;/h3&gt;
&lt;p&gt;将ax的数值加上b,ax=ax+8&lt;/p&gt;
&lt;h3&gt;add ax,bx&lt;/h3&gt;
&lt;p&gt;将ax的数值加上bx,ax=bx+ax&lt;/p&gt;
&lt;h2&gt;DS和[address]&lt;/h2&gt;
&lt;p&gt;执行指令时，8086CPU自动收取DS中的数据为内存单元的地址。&lt;/p&gt;
&lt;p&gt;mov从10000H中读取数据：&lt;/p&gt;
&lt;p&gt;10000H(1000:0)&lt;/p&gt;
&lt;p&gt;将段地址1000H放入ds&lt;/p&gt;
&lt;p&gt;用&lt;code&gt;mov al,[0]&lt;/code&gt;完成传送([]指的是一个内存单元,这个内存单元的偏移地址是0，它的段地址默认放在ds中)&lt;/p&gt;
&lt;p&gt;数据-&amp;gt; 通用寄存器 -&amp;gt; 段寄存器&lt;/p&gt;
&lt;h2&gt;cmp&lt;/h2&gt;
&lt;p&gt;比较&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cmp rax, 31&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;后续通常会紧跟一条条件跳转指令（如 je、jg、jl 等），根据比较结果改变程序执行流。&lt;/p&gt;
&lt;p&gt;je（相等时跳转）：若 RAX == 31。
jne（不相等时跳转）：若 RAX != 31。
jg（有符号大于）：若 RAX &amp;gt; 31。
jl（有符号小于）：若 RAX &amp;lt; 31。
ja（无符号大于）：若 RAX &amp;gt; 31（无符号比较）。
jb（无符号小于）：若 RAX &amp;lt; 31（无符号比较）。&lt;/p&gt;
&lt;h1&gt;python&lt;/h1&gt;
&lt;h2&gt;range(a,b,c)&lt;/h2&gt;
&lt;p&gt;从a开始，到b结束（不包含），长度为c&lt;/p&gt;
&lt;p&gt;如果a长度是 34，那么会依次取 0, 2, 4, 6, ..., 32。&lt;/p&gt;
&lt;h2&gt;a[i:i+x]&lt;/h2&gt;
&lt;p&gt;这是字符串切片操作，从a中取出从i开始、长度为x的子串。&lt;/p&gt;
&lt;h2&gt;异或&lt;/h2&gt;
&lt;p&gt;异或（XOR，符号为 ^）是一种按位运算&lt;/p&gt;
&lt;h3&gt;性质&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;交换律：a ^ b = b ^ a&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;结合律：(a ^ b) ^ c = a ^ (b ^ c)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;与0异或：a ^ 0 = a（0不变）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;与自身异或：a ^ a = 0（自身抵消）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;可逆性（自反性）：如果 c = a ^ b，那么 a = c ^ b&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;在逆向中的应用&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;cipher = ord(flag[i]) ^ key
result += str(hex(cipher))[2:].zfill(2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;加密时用的是key,解密的时候也用key&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  original = cipher ^ key
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;chr()&lt;/h2&gt;
&lt;p&gt;将整数转换成对应字符&lt;/p&gt;
&lt;p&gt;&lt;code&gt;chr(65)&lt;/code&gt; 返回A&lt;/p&gt;
&lt;h2&gt;append&lt;/h2&gt;
&lt;p&gt;是列表的方法，用于在列表末尾添加一个元素。&lt;/p&gt;
&lt;h2&gt;&apos;&apos;.join(a)&lt;/h2&gt;
&lt;p&gt;是字符串的 join 方法，它把列表a中的所有字符串元素（这里是单个字符）连接成一个长字符串，连接符是空字符串 &apos;&apos;（即直接拼接）。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;假设解密过程得到 original 依次为：112, 121, 116, 104, 111, 110&lt;/p&gt;
&lt;p&gt;chr(112) -&amp;gt; &apos;p&apos;&lt;/p&gt;
&lt;p&gt;chr(121) -&amp;gt; &apos;y&apos;&lt;/p&gt;
&lt;p&gt;chr(116) -&amp;gt; &apos;t&apos;&lt;/p&gt;
&lt;p&gt;chr(104) -&amp;gt; &apos;h&apos;
chr(111) -&amp;gt; &apos;o&apos;&lt;/p&gt;
&lt;p&gt;chr(110) -&amp;gt; &apos;n&apos;
列表 flag_chars 变成 [&apos;p&apos;,&apos;y&apos;,&apos;t&apos;,&apos;h&apos;,&apos;o&apos;,&apos;n&apos;]
然后 &apos;&apos;.join(flag_chars) 得到 &quot;python&quot;。&lt;/p&gt;
&lt;p&gt;这样，最终打印出来的就是解密得到的 flag。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;%&lt;/h2&gt;
&lt;p&gt;在数学中，取模运算（Modulo Operation）就是求两个数相除的余数。
对于整数 a 和 b（b ≠ 0），a % b 的结果是 a 除以 b 后得到的余数。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;a = b * (a // b) + (a % b)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;其中&lt;code&gt;a // b&lt;/code&gt;是整数除法（在 Python 中称为地板除，结果向下取整）&lt;code&gt;a % b &lt;/code&gt;就是余数，且&lt;code&gt;0 ≤ 余数 &amp;lt; |b|&lt;/code&gt;（当 b &amp;gt; 0 时成立，负数情况见后）。&lt;/p&gt;
&lt;h3&gt;正数取模&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;print(7 % 3)   # 输出 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;负数取模&lt;/h3&gt;
&lt;p&gt;不同编程语言对负数取模的处理可能不同。Python 的取模运算遵循一个原则：余数的符号与除数（第二个操作数）相同。
并且保证：&lt;code&gt;0 ≤ 余数 &amp;lt; |b| &lt;/code&gt;当 &lt;code&gt;b &amp;gt; 0&lt;/code&gt;；如果 &lt;code&gt;b &amp;lt; 0&lt;/code&gt;，则 &lt;code&gt;b &amp;lt; 余数 ≤ 0&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;print(-7 % 3)   # 输出 2
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;当 b &amp;gt; 0 时，a % b 的结果在 [0, b-1] 之间。&lt;/p&gt;
&lt;p&gt;当 b &amp;lt; 0 时，a % b 的结果在 [b+1, 0] 之间（即非正）。&lt;/p&gt;
&lt;p&gt;满足公式：a = (a // b) * b + (a % b) 永远成立。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;常见用途&lt;/h3&gt;
&lt;h4&gt;判断奇偶&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;num = 7
if num % 2 == 1:
    print(&quot;奇数&quot;)
else:
    print(&quot;偶数&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;循环索引&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;arr = [&apos;a&apos;, &apos;b&apos;, &apos;c&apos;]
index = 5
print(arr[index % len(arr)])   # 输出 &apos;c&apos;，因为 5 % 3 = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;限制数值范围&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;value = 123
# 将 value 限制在 0~9 之间
value %= 10   # 123 % 10 = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;获取数字的个位数、十位数等&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;num = 12345
last_digit = num % 10           # 5
second_last = (num // 10) % 10  # 4
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;周期性任务(每隔n次执行一次)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;for i in range(100):
    if i % 10 == 0:
        print(f&quot;第 {i} 次执行特殊操作&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;密码学简单加密校验（以后再补）&lt;/h4&gt;
&lt;h1&gt;C&lt;/h1&gt;
&lt;h2&gt;strlen&lt;/h2&gt;
&lt;p&gt;C 语言标准库中的一个函数，用于计算字符串的长度（不包括结尾的空字符 &apos;\0&apos;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;string.h&amp;gt;
size_t strlen(const char *s);
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;参数：s 是指向字符串首字符的指针（必须是 \0 结尾）。
返回值：size_t 类型（通常是无符号整数），表示字符串的字符数，不包括结尾的 \0。
strlen 从 s 指向的地址开始，逐字节读取内存，直到遇到值为 0 的字节（即 \0）为止，然后返回读取的字节数（不包括最后的 \0）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;char str[] = &quot;hello&quot;;
int len = strlen(str);  // len = 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当看到 strlen 调用，往往表示程序正在处理字符串，可能是验证输入长度、计算缓冲区大小等。
反汇编中可能直接出现 call strlen，也可能被编译器优化为内联循环（如 repne scasb）。&lt;/p&gt;
&lt;h1&gt;C++&lt;/h1&gt;
&lt;h2&gt;void&lt;/h2&gt;
&lt;p&gt;void 关键字表示“无类型”或“空”
当函数不需要返回任何数据时，返回类型声明为 void&lt;/p&gt;
&lt;h3&gt;void*&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;void* 是一种特殊指针，可以指向任意类型的数据，但不能直接解引用（因为不知道类型）。通常用于底层内存操作或泛型接口：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;int a = 10;
void* ptr = &amp;amp;a;                // 可以指向 int
// *ptr = 20;                  // 错误！不能解引用 void*
int* intPtr = (int*)ptr;       // 需要强制转换后才能使用
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>新开档：逆向</title><link>https://dxfaker.top/posts/26228/</link><guid isPermaLink="true">https://dxfaker.top/posts/26228/</guid><description>自用笔记</description><pubDate>Sat, 28 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;引言&lt;/h1&gt;
&lt;p&gt;借不到的爱恋镇楼（我一直在哭）&lt;/p&gt;
&lt;p&gt;我对这个模块最初的认知就是以前在某些神秘小游戏改数据的时候用的CE(cheat engine)有印象，但是后面发现其实可以对IoT有所辅助，遂决定开始先往reverse方向学习。但是当我接触reverse的题目的时候，我就发现了一个很大的问题：我的C和py很差，因为大一上学期主要在纯学高数a，且刚放假学的是c++，一进来用ghidra反编译出的代码要么是C要么是python的时候就很绝望，很多逻辑运算都看不懂，自己点开vs code也不知道该敲什么，满脑子的都是&lt;code&gt;include&amp;lt;iostream&amp;gt;&lt;/code&gt;...
于是，这篇文章便孕育而生，专门为的服务我这条蛆在忘记某些方面的知识的时候来复习的。
&lt;strong&gt;这个模块也是随缘更新的，每学到一些东西便会记录，积累到一定程度便会端上来&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;C&lt;/h2&gt;
&lt;h3&gt;strcmp&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;strcmp 是 C 语言标准库中的一个字符串比较函数，定义在 &amp;lt;string.h&amp;gt; 头文件中。
比较两个字符串（以 \0 结尾的字符数组）是否相等，或者按照字典序（ASCII 码顺序）判断大小。
比较过程是逐字符进行的，直到遇到第一个不同的字符或遇到字符串结束符 \0。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;int strcmp(const char *s1, const char *s2);&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;返回 0：表示两个字符串相等（s1 和 s2 内容完全相同）。
返回负数：表示 s1 小于 s2（例如 &quot;abc&quot; &amp;lt; &quot;abd&quot;）。
返回正数：表示 s1 大于 s2（例如 &quot;abd&quot; &amp;gt; &quot;abc&quot;）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;builtin_strncpy&lt;/h3&gt;
&lt;p&gt;在 Ghidra 的反编译代码里，其实就是我们熟知的 strncpy 函数。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ghidra 为了准确反映底层调用而使用的一种命名方式&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;builtin_strncpy(local_7e8,&quot;(34sy_r3v3rs3)&quot;,0xf);&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;意思就是把字符串 &quot;(34sy_r3v3rs3)&quot; 的前 0xf (也就是十进制的 15) 个字符，复制到 local_7e8 这个字符数组里。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;builtin_ 前缀&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;这是 Ghidra 反编译的一个小特点。当它识别出代码里调用了一个它认为很基础或内建的函数时，就会自动加上 builtin_ 这个前缀。你可以就把它当成一个普通的 strncpy 来理解，在分析代码逻辑时，它的作用完全等同于 strncpy。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;gets&lt;/h2&gt;
&lt;p&gt;gets 是一个 C 语言标准库函数，用于从标准输入（通常是键盘）读取一行字符串，直到遇到换行符（回车）为止。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;gets(locak_58)&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;它会将读取到的字符串（包括换行符会被自动替换为字符串结束符 \0）存储到 local_58 这个字符数组中。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;:gets 非常危险，因为它不检查目标缓冲区的大小，如果输入过长会导致缓冲区溢出。但在逆向题中常见。&lt;/p&gt;
&lt;h2&gt;strlen&lt;/h2&gt;
&lt;p&gt;strlen 是 C 语言标准库函数，用于计算字符串的长度（不包括结尾的 \0）。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sVar1 = strlen(local_58);&lt;/code&gt;
&lt;code&gt;local_10 = (int)sVar1;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这里将 local_58 字符串的长度计算出来，赋值给变量 sVar1。
然后将 sVar1 强制转换为 int 类型，然后赋值给 local_10。&lt;/p&gt;
&lt;h2&gt;python&lt;/h2&gt;
&lt;h3&gt;len&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;for i in range(len(list))&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;表示循环次数等于列表长度&lt;/p&gt;
&lt;h3&gt;运算符&lt;/h3&gt;
&lt;h4&gt;&amp;amp; 按位与&lt;/h4&gt;
&lt;p&gt;两个二进制位都为1时，结果才为1，否则为0。&lt;/p&gt;
&lt;p&gt;操作过程：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;5 &amp;amp; 3&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;5:     &lt;code&gt;0 1 0 1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;3:     &lt;code&gt;0 0 1 1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;结果:  &lt;code&gt;0 0 0 1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;结果为1（0001）&lt;/p&gt;
&lt;h4&gt;| 按位或&lt;/h4&gt;
&lt;p&gt;两个二进制位只要有一个为1，结果就为1,操作过程同上&lt;/p&gt;
&lt;p&gt;&lt;code&gt;5 | 3&lt;/code&gt; -&amp;gt; 0111 -&amp;gt; 7&lt;/p&gt;
&lt;h4&gt;^ 按位异或&lt;/h4&gt;
&lt;p&gt;两个二进制位不同则为1，相同则为0，操作过程同上&lt;/p&gt;
&lt;p&gt;&lt;code&gt;5 ^ 3&lt;/code&gt; -&amp;gt; 0110 -&amp;gt; 6&lt;/p&gt;
&lt;h4&gt;~ 按位取反&lt;/h4&gt;
&lt;p&gt;将每一位取反（0变1，1变0），操作过程同上。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意在Python中，结果是补码形式，可能得到负数，不过初学可以先放一放。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;&amp;lt;&amp;lt; 左移&lt;/h4&gt;
&lt;p&gt;将二进制位整体向左移动，右边低位补0。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;每左移一位，相当于乘以2。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;5 &amp;lt;&amp;lt; 1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;0101&lt;/code&gt; -&amp;gt; &lt;code&gt;1010&lt;/code&gt; -&amp;gt; 10(5*2)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;5 &amp;lt;&amp;lt; 3&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;0101&lt;/code&gt; -&amp;gt; &lt;code&gt;0101000&lt;/code&gt; -&amp;gt;40(5&lt;em&gt;2&lt;/em&gt;2*2)&lt;/p&gt;
&lt;h4&gt;&amp;gt;&amp;gt; 右移&lt;/h4&gt;
&lt;p&gt;将二进制位整体向右移动，左边高位补0（对于非负数）。操作同上，生活不顺心大可以换成十进制计算后再换成二进制以达到目标。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;每右移一位，相当于除以2取整。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;[a:]&lt;/h4&gt;
&lt;p&gt;作用：切片操作，从字符串的第 a 个字符开始取，即去掉开头的 &lt;code&gt;&apos;0x&apos;&lt;/code&gt;。
&lt;code&gt;&apos;0x61&apos;[2:]&lt;/code&gt; → &lt;code&gt;&apos;61&apos;&lt;/code&gt;，&lt;code&gt;&apos;0xa&apos;[2:]&lt;/code&gt; → &lt;code&gt;&apos;a&apos;&lt;/code&gt;&lt;/p&gt;
&lt;h4&gt;+=&lt;/h4&gt;
&lt;h3&gt;ord&lt;/h3&gt;
&lt;p&gt;将字符转换为其对应的 Unicode 码点（整数）。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ord(A)&lt;/code&gt; -&amp;gt; 65&lt;/p&gt;
&lt;h3&gt;hex&lt;/h3&gt;
&lt;p&gt;将整数转换成十六进制字符串，格式为&lt;code&gt;0x...&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;hex(97)&lt;/code&gt; -&amp;gt; &lt;code&gt;0x61&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;hex(10)&lt;/code&gt; -&amp;gt; &lt;code&gt;0xa&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;str&lt;/h3&gt;
&lt;p&gt;将结果转换为字符串&lt;/p&gt;
&lt;p&gt;&lt;code&gt;str()&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;.zfill(a)&lt;/h3&gt;
&lt;p&gt;用零在左侧填充到指定宽度（这里是 a）。如果字符串长度不足 a 位，就在左边补零；如果已经 a 位或以上，则不变&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&apos;a&apos;.zfill(2)&lt;/code&gt; → &lt;code&gt;&apos;0a&apos;&lt;/code&gt;，&lt;code&gt;&apos;61&apos;.zfill(2)&lt;/code&gt; → &lt;code&gt;&apos;61&apos;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;现在来理解这样一段([题源:SWPUCTF 2021 新生赛]简简单单的逻辑)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;flag = &apos;xxxxxxxxxxxxxxxxxx&apos;
list = [47, 138, 127, 57, 117, 188, 51, 143, 17, 84, 42, 135, 76, 105, 28, 169, 25]
result = &apos;&apos;
for i in range(len(list)):
    key = (list[i]&amp;gt;&amp;gt;4)+((list[i] &amp;amp; 0xf)&amp;lt;&amp;lt;4)
    result += str(hex(ord(flag[i])^key))[2:].zfill(2)
print(result)
# result=bcfba4d0038d48bd4b00f82796d393dfec
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就先这样？&lt;/p&gt;
</content:encoded></item><item><title>第一篇文章</title><link>https://dxfaker.top/posts/26224/</link><guid isPermaLink="true">https://dxfaker.top/posts/26224/</guid><description>讲讲开发这个网页的事</description><pubDate>Tue, 24 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;ai指导静态网页开发&lt;/h1&gt;
&lt;h2&gt;起因&lt;/h2&gt;
&lt;p&gt;第一篇就拿很熟悉的一张当封面吧
由于要开始准备数学考试了，几天前回的学校。
从图书馆回来的时候还早，学是不可能学的。便去问群友有啥游戏玩，结果是啥都没有。（我操你的）
然后看到坨子在搜寻流萤背景当网站壁纸，了解一番便决定自己也整一个网页来玩一玩。&lt;/p&gt;
&lt;h2&gt;过程&lt;/h2&gt;
&lt;p&gt;从21号晚上开始来着，从知乎找了篇佬的文章搭到github，用hexo和git开始搭倒是一路畅通无阻，几个小时就出来了初版
但是，我不是很满意那个主题，然后去看了看群友的，基本上都是打包过来的，于是决定去借鉴一下mizuki的，不过他用的是Astro，不过不重要，我直接把这些问题丢给ai大人了，从github上把他的整个code下载到本地，然后用git调试。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pnpm dev //本地启动开发服务器
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;本来vscode也有一个插件可以实现的，点右下角go to live即可(live server),不过好像astro一般用git的。&lt;/p&gt;
&lt;p&gt;下一步就是把文件里的内容改成自己的信息，这个过程花了半天，我把每一个模块都看一遍，然后把自己的想法丢给ai，ai给代码，然后修改文件，刷新页面看效果。&lt;/p&gt;
&lt;p&gt;我在写这篇文章的时候，还有友链没挂、本地曲目没挂、曲目抓取没整、樱花飘落开关没搞好、追番目录还没升级、看板娘没接api
好像就这些没做了。&lt;/p&gt;
&lt;p&gt;不过也没什么时间就是了，转专业考试快要来了，从明天开始就要好好去学数学了，我真想润粤海（qwq）。
后面就是和ai联合会诊的过程了，很折磨。&lt;/p&gt;
&lt;p&gt;改信息算好处理的了，问题在后面从本地同步到github上&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git add .                   \\添加所有更改（包括新文件、修改、删除）。
git commit -m &quot;更新内容&quot;     \\将更改打包成一个提交，引号内可自定义本次更新的说明。
git push origin main         \\推送到远程 main 分支，触发 GitHub Actions 自动部署。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当时是晚上12点半差不多，我设置好github actions然后等仓库actions标绿勾时，然后迎接我的是纯HTML，所有CSS和JS都加载失败的网页。。。&lt;/p&gt;
&lt;p&gt;当然也是问ai，先找了Jekyll的麻烦，在分支部署了空的 .nojekyll文件&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;GitHub Pages 默认会调用 Jekyll 处理站点，而 Jekyll 会忽略所有以下划线开头的文件或文件夹，导致 _astro/ 下的文件没有被复制到最终站点。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;然而没什么卵用，时间也是走到1点了，实在撑不住，第二天早上继续战斗。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;用户添加了 .nojekyll 后，部署仍然失败（日志显示 Jekyll 还在运行）。进一步发现 pages 分支中包含了整个项目的源码（如 src/、public/ 等），而构建产物（dist/）被放错了位置——dist 文件夹本身在根目录，导致需要的 _astro/ 文件夹在 dist/ 内部，而网站根目录下并没有这些文件。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;把pages清理了，dist/目录的内容移动到根目录，重新提交了一遍。
但还是404了，查找了HTML终的&amp;lt;link&amp;gt;已经为绝对路径，说明astro没啥问题，就直接强制推送，最后就成了。&lt;/p&gt;
&lt;p&gt;然后是看板娘问题，挑了半天选择洛天依，后面看看要不要多接炮姐和长离。这是最折磨的，我从昨天晚上10点多干到1点，早上6点多起床干到中午，确实浪费时间，不过大概是是因为我啥都不懂导致的（笑）。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;模型文件路径配置错误（用了 models 而不是 model）。
Live2D 核心库 Live2DCubismCore 未加载（控制台返回 undefined）。
模型文件未正确提交到 Git，导致构建产物缺失。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;反正处理完这三条等我再刷新的时候，可爱的洛天依就出现在左下角了。
下午还搞了一下鼠标指针问题，斥巨资换成了静态雪乃。&lt;/p&gt;
&lt;p&gt;然后就没啥了。&lt;/p&gt;
&lt;h2&gt;结语&lt;/h2&gt;
&lt;p&gt;虽然我啥都不懂，但还是用ai磕磕盼盼整了个大概，感谢这个时代有了这个网页吧，也感谢群友，感谢大家。
后面转专业考试过了之后我会努力更新的。就先这么多？&lt;/p&gt;
</content:encoded></item></channel></rss>