一、模板攻击原理

模板攻击(Template Attacks, TA)是一种高效的侧信道分析方法,利用密码设备运行时的物理泄漏信息(如功耗)恢复密钥。其核心思想是通过建模密钥相关中间值的泄漏特征,构建攻击模板。模板攻击分为两个阶段:
模板构建:在已知密钥条件下,采集大量功耗曲线并计算中间值(如S盒输出的汉明重量)。针对每个可能的中间值(0-8),计算其对应的平均功耗曲线作为模板。这些模板表征了不同中间值在硬件上的独特功耗特征,形成"指纹库"。
模板匹配:攻击未知密钥时,采集目标设备的功耗曲线。对于每条新曲线,计算其与所有模板的欧氏距离。通过最大似然估计,选择最匹配的模板对应的中间值,进而推导出密钥字节。此过程实质是将实测功耗与预存模板进行模式匹配。

相较于CPA等分析方法,模板攻击直接建立中间值与原始功耗的映射关系,避免了对泄漏模型的线性假设。它通过多变量统计分析捕获更复杂的功耗特征,在相同曲线数量下通常具有更高的攻击效率,尤其适合对抗防护措施较强的设备。

二、Python实现

运行代码前先激活Python环境,并执行pip install numpy tqdm trsfile matplotlib安装需要的库。另外保存能量迹数据的trsfile文件可以在采集自AES算法无防护纯软件实现的功耗曲线 免费下载。

1. 数据加载与预处理

load_trs_to_dict()函数负责解析.trs格式的功耗文件。该格式专用于存储侧信道数据,包含曲线采样点和加密参数(如明文)。函数读取文件头信息后,按指定范围提取曲线数据,同时将关联的明文参数结构化存储。

get_intermediate_values()计算攻击所需的中间值矩阵。基于汉明重量泄漏模型,对每条曲线的目标字节位置,计算真实密钥(已知)和256种假设密钥的S盒输出值。通过预计算的汉明重量查找表(HW_ARRAY)高效生成中间值向量,避免重复计算S盒操作。

2. 模板构建与概率计算

build_template()实现核心模板构建逻辑。按汉明重量值0-8分组曲线,计算每组的平均功耗曲线。该函数输出9×N的模板矩阵(N为采样点数),每行代表特定中间值对应的功耗特征向量。

ProbCalculator类管理密钥概率计算。其update()方法计算新曲线与所有模板的欧氏距离平方和(即似然度),累加到密钥假设向量。随着曲线增加,正确密钥的累积似然度将显著低于错误假设。

3. 攻击执行与评估

single_attack()执行单次模板攻击。采用分批处理策略,每新增若干曲线即更新密钥概率。通过np.argsort计算真实密钥的当前排名(Guess Entropy),排名为0时表示成功恢复密钥。

guess_entropy()进行鲁棒性评估。通过1000次随机排列实验,统计不同曲线数量下的成功率(SR)和猜测熵(GE)。关键指标包括:SR>0.9所需曲线数、GE降为0所需曲线数,结果自动保存至文本文件。

具体代码如下:

"""
对AES算法的软件实现进行简单模板攻击(TA)的Python代码
泄漏模型:S盒输出的汉明重量
运行前先激活Python环境,执行pip install numpy tqdm trsfile matplotlib命令安装所需的库
'AES_POWER_STM32F_NO-PROTECT_60000.trs'是功耗曲线文件,请点击https://download.csdn.net/download/weixin_43261410/91057055免费下载
查看.trs格式文件介绍请浏览https://blog.csdn.net/weixin_43261410/article/details/148737286?fromshare=blogdetail&sharetype=blogdetail&sharerId=148737286&sharerefer=PC&sharesource=weixin_43261410&sharefrom=from_link
"""import os
import numpy as np
from tqdm import tqdm  # 用于显示进度条
from trsfile import trs_open  # 用于读取.trs格式的侧信道数据文件
import matplotlib.pyplot as plt  # 用于画图AES_SBOX = np.array([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
], dtype=np.uint8)# 汉明重量查找表,用于快速计算一个字节的汉明重量(二进制中1的个数)
HW_ARRAY = np.array([0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
])def load_trs_to_dict(trs_file_path: str, start_trace: int = None, end_trace: int = None):"""加载.trs文件到字典结构中参数:trs_file_path: .trs文件路径start_trace: 起始能量曲线索引(可选)end_trace: 结束能量曲线索引(可选)返回:包含头信息、能量曲线数据和额外参数的字典"""header, traces, param = {}, [], {}with trs_open(trs_file_path, 'r') as trs_file:# 读取文件头信息for key, value in trs_file.get_headers().items():header[key.name] = value# 处理能量曲线范围number_traces = header['NUMBER_TRACES']start_trace = 0 if start_trace is None else min(start_trace, number_traces - 1)end_trace = number_traces if end_trace is None else min(end_trace, number_traces)header['NUMBER_TRACES'] = end_trace - start_trace# 初始化能量曲线数据数组traces = np.zeros((header['NUMBER_TRACES'], header['NUMBER_SAMPLES']), dtype=np.float32)# 初始化参数数组param = {}for key in trs_file[0].parameters:param_len = len(trs_file[0].parameters[key].value)param[key] = np.zeros((header['NUMBER_TRACES'], param_len), dtype=np.uint8)# 读取能量曲线数据和参数for i, trace in enumerate(trs_file[start_trace:end_trace]):for key in trace.parameters:param[key][i] = trace.parameters[key].valuetraces[i] = np.array(trace.samples)# 返回结构化数据trs_dict = {"HEADER": header,"TRACES": traces,"PARAM": param}return trs_dictdef get_intermediate_values(plaintext, target_byte, real_key):"""生成中间值矩阵(基于汉明重量模型)参数:plaintext: 明文数组 (N x 16)target_byte: 目标字节位置(0-15)real_key: 真实密钥字节返回:actual: 真实中间值向量(长度等于能量曲线数量)hypo: 假设中间值矩阵(能量曲线数量 × 256种密钥假设)"""# 计算真实的S盒输入actual_sbox_in = plaintext[:, target_byte] ^ real_key# 计算S盒输出actual_sbox_out = AES_SBOX[actual_sbox_in]# 计算汉明重量作为功耗模型actual = HW_ARRAY[actual_sbox_out]# 生成256种可能的密钥假设hypo_key = np.array(range(256), np.uint8)# 计算S盒输入hypo_sbox_in = np.array([pt ^ hypo_key for pt in plaintext[:, target_byte]], np.uint8)# 计算S盒输出hypo_sbox_out = AES_SBOX[hypo_sbox_in]# 将汉明重量作为功耗模型hypo = HW_ARRAY[hypo_sbox_out]return actual, hypoclass ProbCalculator():"""计算密钥假设的似然概率"""def __init__(self):# 初始化密钥假设的似然累加器self.guess_key_likelihood = np.zeros((256))def update(self, traces, hypo, template):"""更新似然值参数:traces: 当前批次的能量曲线hypo: 当前批次的假设中间值template: 构建的模板(9个汉明重量对应的平均功耗曲线)"""# 转置模板以便后续计算template = template.transpose()# 计算所有密钥假设的平方差ln_likelihood = np.sum((traces[:, :, None] - template) ** 2, axis=1)# 累加每个密钥假设的似然值guess_key_likelihood = np.sum([ln_likelihood[i, hypo[i]] for i in range(traces.shape[0])], axis=0)self.guess_key_likelihood = self.guess_key_likelihood + guess_key_likelihooddef __call__(self):"""返回当前似然值向量"""return self.guess_key_likelihooddef build_template(traces, actual):"""构建模板:计算每个汉明重量(0-8)对应的平均功耗曲线参数:traces: 用于构建模板的能量曲线actual: 每条曲线对应的真实汉明重量返回:template: 模板矩阵(9 x 曲线长度)"""template = np.zeros((9, traces.shape[1]))for i in range(9):# 找到所有汉明重量为i的曲线索引index = np.where(actual == i)[0]# 计算这些曲线的平均功耗template[i] = np.mean(traces[index], axis=0)return templatedef single_attack(traces, hypo, template, real_key, index):"""执行单次TA攻击参数:traces: 能量曲线数据hypo: 假设中间值数据template: 构建的模板real_key: 真实密钥字节(用于评估攻击效果)index: 分批处理的索引列表返回:key_rank_list: 每次更新后真实密钥的排名列表"""# 初始化概率计算器probability = ProbCalculator()key_rank_list = []# 分批处理能量曲线数据for i in range(len(index) - 1):# 获取当前批次的曲线和中间值start_idx = index[i]end_idx = index[i + 1]current_traces = traces[start_idx:end_idx]current_hypo = hypo[start_idx:end_idx]# 更新密钥似然值probability.update(current_traces, current_hypo, template)# 获取当前所有密钥假设的似然值prob = probability()# 计算密钥排名(似然值越小排名越高)prob_sort = np.argsort(np.argsort(prob))# 记录真实密钥的当前排名key_rank = prob_sort[real_key]key_rank_list.append(key_rank)return key_rank_listdef guess_entropy(traces, hypo, template, real_key, result_path='result.txt', repeat_n=100, sample=500, header='', step=1):"""计算猜测熵和成功率参数:traces: 能量曲线数据hypo: 假设中间值数据template: 构建的模板real_key: 真实密钥字节result_path: 结果保存路径repeat_n: 重复实验次数sample: 每次实验使用的能量曲线数量header: 结果文件头部信息step: 能量曲线数量增加的步长"""def get_value_n(arr, value, traces_number, less):"""辅助函数: 获取达到特定值所需的最少能量曲线数量"""if less:mask = arr <= valueelse:mask = arr >= valuevalid_index = np.cumprod(mask[::-1])[::-1]valid_index = np.where(valid_index)[0]return traces_number[valid_index[0]] if len(valid_index) > 0 else None# 设置分批索引列表index = [i for i in range(0, sample, step)]index.append(sample)# 准备随机排列key_rank_arr = []permutations = np.zeros((repeat_n, sample), dtype=int)for i in range(repeat_n):permutations[i] = np.random.permutation(traces.shape[0])[:sample]# 多次重复实验for i in tqdm(range(repeat_n)):traces_random = traces[permutations[i]]hypo_random = hypo[permutations[i]]key_rank_list = single_attack(traces_random, hypo_random, template, real_key, index)key_rank_arr.append(key_rank_list)key_rank_arr = np.array(key_rank_arr)traces_number = np.array(index[1:])# 计算猜测熵guess_entropy = np.mean(key_rank_arr, axis=0)guess_entropy_10 = get_value_n(guess_entropy, 10, traces_number, True)guess_entropy_1 = get_value_n(guess_entropy, 1, traces_number, True)guess_entropy_0 = get_value_n(guess_entropy, 0, traces_number, True)# 计算成功率success_rate = np.mean(key_rank_arr < 10, axis=0)  # 排名小于10被认为攻击成功success_rate_p9 = get_value_n(success_rate, 0.9, traces_number, False)success_rate_p99 = get_value_n(success_rate, 0.99, traces_number, False)success_rate_1 = get_value_n(success_rate, 1, traces_number, False)# 保存结果到文件mode = 'a' if os.path.exists(result_path) else 'w'with open(result_path, mode, encoding='utf-8') as f:f.write(header)trs_n = ' '.join(f"{int(x)}" for x in traces_number)f.write('traces number: ' + trs_n + '\n')success_rate = ' '.join(f"{x:.2f}" for x in success_rate)f.write('success rate: ' + success_rate + '\n')guess_entropy = ' '.join(f"{x:.2f}" for x in guess_entropy)f.write('guess entropy: ' + guess_entropy + '\n')f.write(f'tips: When guess entropy equals 10, 1, or 0, the corresponding traces number are {guess_entropy_10}, {guess_entropy_1}, and {guess_entropy_0}.\n')f.write(f'tips: When success rate equals 0.9, 0.99, or 1, the corresponding traces number are {success_rate_p9}, {success_rate_p99}, and {success_rate_1}.\n\n')def show_result():"""可视化攻击结果从result.txt文件中读取数据并绘制成功率与猜测熵曲线"""result_path = 'result.txt'# 读取结果文件with open(result_path, 'r', encoding='utf-8') as f:lines = f.readlines()# 初始化数据列表traces = []success_rate = []guess_entropy = []# 解析结果文件for line in lines:if line.startswith('traces number:'):traces = list(map(int, line.split(':')[1].strip().split()))elif line.startswith('success rate:'):success_rate = list(map(float, line.split(':')[1].strip().split()))elif line.startswith('guess entropy:'):guess_entropy = list(map(float, line.split(':')[1].strip().split()))# 检查是否成功读取数据if not traces or not success_rate or not guess_entropy:print("Error: Could not find valid data in result file.")return# 创建图形和双轴fig, ax1 = plt.subplots(figsize=(12, 6))# 绘制成功率曲线(左轴,蓝色)color = 'tab:blue'ax1.set_xlabel('Number of Traces', fontsize=12)ax1.set_ylabel('Success Rate', color=color, fontsize=12)ax1.plot(traces, success_rate, color=color, label='Success Rate', linewidth=2)ax1.tick_params(axis='y', labelcolor=color)ax1.grid(True, linestyle='--', alpha=0.7)ax1.set_ylim(0, 1.05)  # 设置成功率y轴范围# 创建第二个y轴用于猜测熵ax2 = ax1.twinx()color = 'tab:red'ax2.set_ylabel('Guess Entropy', color=color, fontsize=12)ax2.plot(traces, guess_entropy, color=color, label='Guess Entropy', linewidth=2)ax2.tick_params(axis='y', labelcolor=color)ax2.set_ylim(0, 256)  # 固定猜测熵y轴范围为0-256# 添加图例lines1, labels1 = ax1.get_legend_handles_labels()lines2, labels2 = ax2.get_legend_handles_labels()ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper right', fontsize=10)# 添加标题plt.title('TA Attack Performance: Success Rate and Guess Entropy', fontsize=14, pad=20)# 调整布局并保存图像(同时保存svg格式和png格式)fig.tight_layout()plt.savefig('result.svg', format='svg', bbox_inches='tight')plt.savefig('result.png', dpi=600, bbox_inches='tight')plt.show()def test():"""测试函数:执行完整的模板攻击流程"""target_byte = 0  # 攻击的字节位置(0表示第一个字节)traces_build_n = 50000  # 用于构建模板的曲线数量# 主密钥(16字节)mkey = [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10]# 加载能量曲线数据trs_path = 'AES_POWER_STM32F_NO-PROTECT_60000.trs'trs_dict = load_trs_to_dict(trs_path)traces, plaintext = trs_dict['TRACES'], trs_dict['PARAM']['plaintext']# 生成真实中间值向量和假设中间值矩阵actual, hypo = get_intermediate_values(plaintext, target_byte, mkey[target_byte])# 分割数据集:构建模板和攻击评估traces_build, traces_match = traces[:traces_build_n], traces[traces_build_n:]actual_build, hypo_match = actual[:traces_build_n], hypo[traces_build_n:]# 构建模板template = build_template(traces_build, actual_build)# 设置实验参数repeat_n = 1000  # 重复次数sample = 100     # 每次实验使用的能量曲线数量step = 1         # 能量曲线数量增加的步长# 执行猜测熵分析guess_entropy(traces_match, hypo_match, template, mkey[target_byte], repeat_n=repeat_n, sample=sample, step=step)# 可视化结果show_result()if __name__ == '__main__':test()

三、实现结果

攻击目标为AES首字节密钥(真实值0x01),使用STM32F3设备的60,000条功耗曲线。前50,000条构建模板,后10,000条用于评估。实验显示:当使用17条曲线时,成功率已达90%;当曲线增至26条时,猜测熵降为1,成功率99%以上。

可视化结果呈现两条关键曲线:蓝色成功率曲线呈对数增长,40条曲线内趋近100%;红色猜测熵曲线快速下降,30条曲线后趋近0。这表明模板攻击对未防护设备极其高效,验证了汉明重量模型的适用性。

四、总结

本文通过Python实现了针对AES算法的模板攻击(TA),成功验证了该方法的有效性。实验基于STM32F3设备的60,000条功耗曲线,以S盒输出的汉明重量作为泄漏模型,在32条攻击曲线内即可完全恢复目标密钥字节。结果表明,模板攻击在已知密钥建模阶段充分学习设备的物理泄漏特征后,对未知密钥的攻击效率高于DPA、CPA等传统方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/909774.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/909774.shtml
英文地址,请注明出处:http://en.pswp.cn/news/909774.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI集群全光交换网络技术原理与发展趋势研究

​ 引言 随着人工智能技术的飞速发展&#xff0c;AI训练集群对网络互连的带宽、延迟和能效提出了前所未有的挑战。全光交换网络作为一种新兴技术&#xff0c;正在成为解决这些挑战的关键方案。 全光交换网络的基本概念 全光交换网络(Optical Circuit Switch, OCS)是一种利用…

PHP Swoft2 框架精华系列:Validator 校验器详解

文章目录 校验器校验器类型@Validate 注解属性说明校验器校验主要流程系统校验器每个属性存储结构校验器规则定义,注解、注解解析器定义校验器注解使用实现一个自定义的校验器校验器注解校验器注解解析器校验器规则系统校验器Controller/Action 绑定校验器校验器 校验器是 sw…

MySQL 类型转换与加密函数深度解析

MySQL 类型转换与加密函数深度解析 一、类型转换函数详解 1. 显式类型转换 CAST 函数 CAST(expression AS type)支持类型&#xff1a;BINARY, CHAR, DATE, DATETIME, TIME, DECIMAL, SIGNED [INTEGER], UNSIGNED [INTEGER]示例&#xff1a;SELECT CAST(2023-08-15 AS DATE);…

FPGA基础 -- Verilog 行为级建模之条件语句

Verilog 的行为级建模&#xff08;Behavioral Modeling&#xff09;中的条件语句&#xff08;Conditional Statements&#xff09;&#xff0c;逐步从基础到实际工程应用&#xff0c;适合有RTL开发基础但希望深入行为建模的人。 一、行为级建模简介 行为级建模&#xff08;Beh…

linux618 NFS web.cn NFS.cn backup.cn

权限问题 推测 ssh root登录失败 root192.168.235.100s password: ▒▒▒ʱ▒▒ܾ▒ root192.168.235.100s password: ▒▒▒ʱ▒▒ܾ▒ root192.168.235.100s password: ▒▒▒ʱ▒▒ܾ▒ root192.168.235.100s password: ▒▒▒ʱ▒▒ܾ▒ root192.168.235.100s password: …

氧化镱:稀土科技的“夜视高手”

氧化镱&#xff08;Yb₂O₃&#xff09;是一种重要的稀土氧化物&#xff0c;这种略带黄色的粉末&#xff0c;既不像黄金那样耀眼&#xff0c;也不像稀土家族里的“明星”如钕、铽那样广为人知&#xff0c;却在背后默默支撑着许多高科技产业&#xff0c;特别是在红外技术领域&am…

class对象【C#】2025复习

对象 西方思想是&#xff1a;复杂的事让秘书去做就行。老板只需简单的下达命令。 代码格式如下 秘书类型 秘书A new 秘书类型(); . 秘书A.开始工作(); // 调用实例对象的方法。 特别注意的是&#xff0c;程序只会用到 秘书A&#xff0c;秘书B&…

Qt程序启动动画

一、Qt有3种方式实现程序启动动画&#xff08;介绍&#xff09; 1、QSplashScreen 静态图片(png、jpg等格式) 2、QMovie 动态图片(gif格式) 3、QAxWidget 视频(swf格式) 1.QSplashScreen 静态图片(png、jpg等格式) //创建启动动画类实例 QSplashScreen splash(QPixmap(&qu…

贪心算法经典问题

目录 贪心思想 一、Dijkstra最短路问题 问题描述&#xff1a; 贪心策略&#xff1a; 二、Prim 和 Kruskal 最小生成树问题 Prim 算法&#xff1a; Kruskal 算法&#xff1a; 三、Huffman树问题 问题描述&#xff1a; 贪心策略&#xff1a; 四、背包问题 问题描述&a…

零知开源——STM32F4实现ILI9486显示屏UI界面系列教程(一):电子书阅读器功能

本教程将详细介绍如何在零知增强板上使用3.5寸ILI9486显示屏实现电子书阅读器功能。我们将使用LVGL库构建用户界面&#xff0c;并实现翻页、进度显示等核心功能。 目录 一、硬件连接 二、软件UI组件实现 三、零知IDE配置 四、演示效果 五、常见问题解决 六、总结与扩展 一…

支持selenium的chrome driver更新到137.0.7151.119

最近chrome释放新版本&#xff1a;137.0.7151.119 如果运行selenium自动化测试出现以下问题&#xff0c;是需要升级chromedriver才可以解决的。 selenium.common.exceptions.SessionNotCreatedException: Message: session not created: This version of ChromeDriver only s…

架构下的最终瓶颈:数据库如何破局?

在分布式系统和云原生架构逐渐成熟的当下&#xff0c;我们已能够灵活扩展计算资源、水平扩展服务节点、拆分业务模块等。然而&#xff0c;在经历过多轮架构优化之后&#xff0c;数据库常常成为系统的“最后瓶颈”。尤其当数据量、并发量、实时性要求剧增时&#xff0c;数据库即…

湖北理元理律师事务所小微企业债务重组方案:司法与经营的共生逻辑

小微企业债务问题常陷入“救企业还是保老板”的困局。湖北理元理律师事务所为某汽车零部件供应商设计的“经营性债务重组”方案&#xff0c;提供了创新解题思路。 核心矛盾拆解 该企业面临三重困境&#xff1a; 矛盾类型 具体表现 法律风险等级 担保链危机 老板个人担保牵…

FastAdmin退出登录不提示的修改方法

修改退出登录后的提示行为 在FastAdmin中&#xff0c;默认退出登录后会显示"退出成功"的提示信息并跳转页面。要实现不显示提示信息直接跳转&#xff0c;可以通过以下方式修改&#xff1a; 方法一&#xff1a;修改控制器逻辑 找到application/admin/controller/Log…

工信部发布《中国工业软件产业发展研究报告(2025)》:PLM垄断加剧,Ai为国产PLM软件发展契机

在6月17日上午举行的2025南京软件大会开幕式上&#xff0c;工信部电子第五研究所现场发布《中国工业软件产业发展研究报告&#xff08;2025&#xff09;》&#xff08;以下简称《研究报告》&#xff09;&#xff0c;并从工业软件产业发展现状、产业发展趋势&#xff0c;以及我国…

Flutter JSON解析全攻略:使用json_serializable实现高效序列化

引言&#xff1a;为什么我们需要JSON序列化工具&#xff1f; 在现代移动应用开发中&#xff0c;与服务器进行数据交互是必不可少的功能。JSON&#xff08;JavaScript Object Notation&#xff09;作为一种轻量级的数据交换格式&#xff0c;因其易读性、简洁性和广泛支持性&…

shelve模块的使用

shelve模块的使用 1. 什么是Shelve2. Shelve模块的数据存储与读取3. Shelve的读取数据4. Shelve模块的高级操作_ Shelve的数据更新和删除5. 删除操作可以使用del语句&#xff1a;6. Shelve的数据查询和处理_使用for循环来遍历Shelve对象中的所有键值对&#xff1a;7. Shelve模块…

python大学校园旧物捐赠系统

目录 技术栈介绍具体实现截图系统设计研究方法&#xff1a;设计步骤设计流程核心代码部分展示研究方法详细视频演示试验方案论文大纲源码获取/详细视频演示 技术栈介绍 Django-SpringBoot-php-Node.js-flask 本课题的研究方法和研究步骤基本合理&#xff0c;难度适中&#xf…

Python爬虫实战:研究eventlet库相关技术

1. 引言 在当今信息爆炸的时代,网络上的数据量呈现出指数级增长的趋势。从海量的网络信息中获取有价值的数据并进行分析,对于企业决策、学术研究以及个人兴趣等方面都具有重要意义。网络爬虫作为一种自动化获取网页内容的技术手段,应运而生并得到了广泛的应用。 网络爬虫(…

文字识别接口-智能文本处理-文字提取技术

文字识别接口&#xff0c;顾名思义&#xff0c;就是一种将图像文字或手写文字转换为可编辑文本的技术。文字识别接口&#xff0c;基于深度学习算法与自主ocr核心实现多种场景字符的高精度识别与结构化信息提取&#xff0c;现已被广泛应用于银行、医疗、财会、教育等多个领域。 …