VMP 加固与 VMProtect 原理与使用

VMP 加固与 VMProtect 原理与使用

目录

  1. 概述与来源说明
  2. [VMP 加固常见疑问与实操要点](#VMP 加固常见疑问与实操要点)
  3. [VMProtect 核心原理](#VMProtect 核心原理)
  4. [VMProtect 使用方法](#VMProtect 使用方法)
  5. 虚拟机指令集与寄存器轮转
  6. 典型性能数据与测试方法论
  7. 性能优化与保护级别平衡
  8. [VMProtect 与其他保护工具对比](#VMProtect 与其他保护工具对比)
  9. 对抗动态分析与自校验
  10. [ARM 与 x86 架构差异](#ARM 与 x86 架构差异)
  11. 最佳实践与常见问题
  12. 总结

概述与来源说明

本文档整理自常见问答与公开资料,围绕 VMP(VMProtect)加固 的接入方式、产物操作、对 JNI/cgo/崩溃堆栈的影响,以及 VMProtect 的原理、使用方法、虚拟机设计、性能与保护级别、与其他工具对比等内容做归纳,供学习与选型参考。内容为通用技术整理,不构成任何产品推荐或保证。

核心要点:VMP 支持零侵入(对二进制直接保护)与轻侵入(SDK 标记)两种方式;保护粒度可配置(全部/部分),保护类型分为 Mutation、Virtualization、Ultra;对 JNI/cgo 以二进制兼容为主,崩溃堆栈需依赖保留符号与加固前后对应关系做解析。

核心概念速览

概念 含义
Mutation 指令层等价变换、垃圾指令、重排;抗特征码,性能影响极小
Virtualization 机器码→自定义字节码,由内嵌 VM 解释执行;强度高,性能开销中到高
Ultra Mutation + Virtualization,强度最高,开销最大
VM_CONTEXT 虚拟机上下文,存放虚拟寄存器(slot),与物理寄存器映射可动态轮转
寄存器轮转 运行期动态改变 slot↔物理寄存器映射,增加逆向难度

VMP 加固常见疑问与实操要点

一、源代码侵入性与语法支持

维度 说明
侵入性 支持零侵入 (直接对编译产出的 PE/ELF SO 做虚拟化保护)与轻侵入 (在代码中加 VMProtect SDK 标记如 VMProtectBegin/Virtualization/End)。两种方式都不要求改业务逻辑;SDK 标记可精确控制保护粒度,最终效果仍依赖 VMP 工程配置与编译流程。
语法支持 VMP 作用于已生成的机器码/LLVM IR,与 C++20/23 语法本身无直接耦合;只要编译器能产出目标架构(如 x86-64/ARM64)的 SO/EXE,VMP 即可处理。实际兼容性取决于目标架构、调用约定、异常处理、内联汇编/模板实例化等,建议在目标工具链与加固配置下做全量回归测试。
保护粒度与原则 支持"全部保护"或"部分保护"。工程上通常只虚拟化关键路径 (核心算法、校验、协议编解码、授权相关),原则:高频调用函数慎用强保护、对外不可见函数可优先、对性能不敏感函数可适度加强。保护类型:Mutation (抗签名/静态分析,性能影响小)、Virtualization (强度中、性能中)、Ultra(变异+虚拟化,强度高、性能低),可按函数粒度分别配置。
保护对象 既可保护导出函数 (如 SDK 对外 JNI 接口),也可保护内部函数。仅保护导出函数时内部实现仍可能暴露;保护内部函数时需注意跨模块/跨库的调用约定与可见性,避免破坏链接与反射/回调。

二、生成产物的操作流程

平台/形态 操作要点
Windows 可执行文件 在 VMP 中 "File → Open" 载入工程,按需标记要保护的函数或段,选择保护类型(Mutation/Virtualization/Ultra)与反调试/完整性校验等,编译后生成新文件(如原文件旁生成 .vmp.exe)。若用 SDK 标记,需确保标记可达且未被编译器优化剥离。
Android SO(有源码) 在 C/C++ 源码中引入 VMProtectSDK.h 并插入 VMProtectBegin/Virtualization/End 等标记;用 NDK 编译为 .so,再用 VMP 对 SO 做虚拟化/变异(或由加固平台集成)。
Android SO(无源码) 直接对已有 .so 做二进制级 VMP 加固;通常需提供符号信息(如 .map 或调试符号)以提升可配置性与可调试性。加固后 SO 会增加解释器与字节码段,运行时由自定义 VM 解释执行被保护函数。
产物形态与注意 VMP 处理后在 PE/ELF 中通常会新增专用段(如 .vmp0/.vmp1 等),原始函数体可能被清空并以字节码替代;若启用压缩/加密,区段布局与原始二进制差异更大。务必保留未加固的基准产物与符号,用于后续符号化与问题定位。

三、对产物使用的影响

方面 说明
JNI 与加载重定位 对 JNI 基本无侵入。只要被调用的 JNI_OnLoad/JNI 导出函数及其调用链保持正确 ABI/调用约定与可见性,JNI 注册与调用即可按原样进行;被虚拟化的函数仍通过原生入口被调用,仅函数体内改为 VM 解释执行。注意避免在 JNI 边界传递依赖未处理结构体/对象布局的假设,以免栈布局变化导致问题。
cgo 与重定位 以二进制兼容为主,关键在于被 cgo 调用的 C/C++ 函数是否保持预期 ABI/名称修饰/调用约定。若这些函数被虚拟化,入口会被替换为 VM 桩代码,但仍需满足外部链接与重定位要求;若 cgo 产生非常规重定位,需在链接阶段修正或避免虚拟化相关目标。
崩溃堆栈与符号解析 虚拟化会改变函数体指令与地址映射,若仅发布"去符号"的发布包,崩溃堆栈中的函数名/行号将难以还原。建议保留并与加固产物匹配的未剥离符号文件(如 .pdb/.sym/.so.debug),在符号服务器/构建系统中建立加固前后二进制与符号的对应关系;对关键路径可选择性降低虚拟化强度以兼顾可调试性。

VMProtect 核心原理

代码虚拟化 (Virtualization)

  • 原始机器码 翻译为自定义字节码 (Bytecode) ,由内嵌在程序中的虚拟机 (VM) 解释执行。
  • 静态分析工具(如 IDA)无法直接识别原始逻辑,需先逆向私有 VM 架构。

虚拟化执行流程示意

复制代码
  调用方 (Caller)
       │
       │  CALL 被保护函数
       ▼
  ┌─────────────────┐
  │  原始函数入口    │  ← 入口被替换为 VM 桩代码
  └────────┬────────┘
           │ 跳转
           ▼
  ┌─────────────────┐
  │  VStartVM        │  保存宿主机寄存器 → VM_CONTEXT
  └────────┬────────┘
           │
           ▼
  ┌─────────────────┐
  │  VMDispatcher    │  循环:取字节码 → 解码 → 查表
  └────────┬────────┘
           │
           ▼
  ┌─────────────────┐
  │  Handler 1..N    │  执行虚拟指令(如 vm_add, vm_push...)
  └────────┬────────┘
           │
           │  (可选) 寄存器轮转
           │
           ▼
  ┌─────────────────┐
  │  VM_Exit         │  从 VM_CONTEXT 恢复寄存器 → RET
  └────────┬────────┘
           │
           ▼
  返回调用方

指令集与虚拟机设计

设计点 说明
基于栈的 VM 操作数通过压栈/出栈传递,打乱原始寄存器使用模式,控制流分析更复杂。
精简且异质指令集 复杂 x86 指令被拆解为更简单虚拟指令;例如仅用 NOR 指令组合模拟 NOT、AND、OR、XOR,增加语义恢复难度。
寄存器轮转 虚拟寄存器与物理寄存器的映射在运行期动态改变,破坏"变量-寄存器"对应关系,增加逆向难度。

NOR 逻辑与组合实现 (定义:NOR(a,b) = ~a & ~b):

目标运算 用 NOR 的实现方式
NOT(a) NOR(a, a)
AND(a,b) NOR(NOR(a,a), NOR(b,b))
OR(a,b) NOR(NOR(a,b), NOR(a,b))
XOR(a,b) NOR(NOR(NOR(a,a),NOR(b,b)), NOR(a,b))

x86 到 VM 的翻译示例(语义等价,形态大变):

原始 x86 可能的 VM 等价(栈式)
add eax, ecx push [vm_ctx+ecx]; push [vm_ctx+eax]; vm_add; pop [vm_ctx+eax]
mov eax, [mem] push imm mem; vm_load; pop [vm_ctx+eax]

代码变异 (Mutation) 与混淆

  • 在不改变程序逻辑的前提下对机器码进行修改:插入无效指令、重排顺序、不可达分支等,主要对抗基于特征码的静态检测。

保护与校验机制

  • Ultra 模式:变异 + 虚拟化,保护最强、性能开销最大。
  • 完整性校验:运行时计算代码段哈希并与预设值比对,检测篡改。
  • 反调试/反虚拟机 :如 VMProtectIsDebuggerPresent 等,可触发自毁逻辑。
  • 水印 (Watermarking):嵌入唯一标识,用于追踪泄露源。

VMProtect 使用方法

1. 源代码集成模式 (SDK)

  • 引入 SDK :包含 VMProtectSDK.h,按平台链接相应库(如 VMProtectSDK32.lib)。
  • 核心标记(需成对出现,作用域尽量小以减性能影响):

SDK 标记示例代码

cpp 复制代码
#include "VMProtectSDK.h"

void CriticalFunction() {
    VMProtectBeginUltra("CriticalFunction");
    // 核心逻辑:算法、校验、协议编解码等
    if (SomeCondition()) {
        DoSomething();
    }
    VMProtectEnd();
}
保护类型 开始标记 结束标记
通用保护 VMProtectBegin("tag") VMProtectEnd()
虚拟化 VMProtectBeginVirtualization("tag") VMProtectEnd()
变异 VMProtectBeginMutation("tag") VMProtectEnd()
Ultra VMProtectBeginUltra("tag") VMProtectEnd()
  • SDK 还提供字符串加密、硬件绑定、序列号验证等,用于授权系统。

2. 无源码加壳模式

  • 在 VMProtect 图形界面中打开可执行文件(.exe/.dll/.so)。
  • 通过 MAP 文件或内置反汇编器选择要保护的函数/区段/字符串。
  • 为选定单元设置保护类型(Mutation/Virtualization/Ultra)及反调试、压缩等选项。
  • 编译生成新文件(如 test.vmp.exe);DLL 建议重命名以避免冲突。

3. 自动化与命令行

  • 控制台版本 VMProtect_Con.exe 可通过脚本调用,便于集成到自动化构建流程。

虚拟机指令集与寄存器轮转

指令集设计原则

  • 基于栈 :如 add eax, ecx 可译为 push ecx; push eax; add; pop eax
  • 逻辑运算 :常仅提供一条 NOR,通过其组合实现 NOT、AND、OR、XOR,极大增加语义恢复复杂度。
  • 字节码与解码:虚拟字节码在文件中常加密/混淆存储;Dispatcher 取指时通过内联解码逻辑解密为 Handler 索引,解码算法和 Seed 在不同构建中会变化,增加脱壳难度。

字节码分散式解码示意(每次只解密一小段,静态难以整体还原):

复制代码
  磁盘/内存中的加固段
  ┌─────────────────────────────────────────┐
  │ 加密字节码流 │ 加密字节码流 │ 加密字节码流 │ ...
  └──────┬──────────────┬──────────────┬─────┘
         │              │              │
         ▼              ▼              ▼
  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐
  │ Dispatcher  │  │ Handler A   │  │ Handler B   │  ...
  │ 取指时解密   │  │ 取数时解密   │  │ 取数时解密   │
  │ → Handler索引│  │ → 操作数    │  │ → 操作数    │
  └─────────────┘  └─────────────┘  └─────────────┘
  解码逻辑分散内联,算法/Seed 随构建变化,难以静态还原完整字节码

寄存器轮转机制

  • VM_CONTEXT:所有虚拟寄存器存放在一块内存结构体中,通过偏移访问,而非直接使用 EAX、EBX 等。
  • 静态映射表(编译期):定义各 slot 初始对应的物理寄存器(如 slot[0]→EAX)。
  • 动态轮转(运行期):在基本块结束或长指令后执行"齿轮转动",动态交换 slot 与物理寄存器的映射(如 slot[0]→ECX),规则由 VMP 内部决定,运行时无明文。
  • 效果:静态分析难以建立"变量↔寄存器"的稳定对应,自动化还原工具面临"寄存器二义性"障碍。

轮转前后映射变化示意(仅说明概念,非真实布局):

复制代码
  编译期 / 轮转前          运行期 / 轮转后(一次转动后)
  ┌──────────────┐         ┌──────────────┐
  │ slot[0] → EAX│         │ slot[0] → ECX│
  │ slot[1] → ECX│   ===>  │ slot[1] → EDX│
  │ slot[2] → EDX│         │ slot[2] → EAX│
  └──────────────┘         └──────────────┘
  分析者看到的只是 slot 偏移,无法稳定对应到"原始用的是 EAX 还是 ECX"

典型性能数据与测试方法论

加固前后产物结构示意(ELF SO 为例)

复制代码
  原始 .so                          加固后 .so
  ┌─────────────────────┐           ┌─────────────────────┐
  │ .text (原始代码)     │           │ .text (可能被清空/桩) │
  │ .data / .rodata      │           │ .vmp0 / .vmp1 (VM+字节码) │
  │ .symtab / .strtab    │           │ .data / .rodata      │
  └─────────────────────┘           │ (符号建议单独保留)   │
                                    └─────────────────────┘
  务必保留:未加固的 .so + 符号文件,用于崩溃符号化与对比调试。

不同算法在不同保护级别下的性能影响(经验值)

以下为相对原生性能(1.0)的倍数,数值越大表示越慢;实际项目需在目标平台实测。

算法类型 Mutation Virtualization Ultra
AES-128 CBC ~1.05--1.15× ~3--10× >10×
SHA-256 ~1.02--1.10× ~2--5× ~5--15×
RSA-2048 签名 ~1.03--1.10× ~5--20× >20×
RSA-2048 验签 ~1.02--1.08× ~4--15× ~10--30×
ECC-256 签名 ~1.02--1.08× ~6--25× >25×
图像处理(如高斯模糊) ~1.01--1.08× ~2--6× ~5--20×

规律:虚拟化对计算密集、指令级并行度高的代码(如 AES、图像处理)影响大;RSA/ECC 等大数运算本身并行度低,虚拟化后开销比例往往更高。

性能测试方法论

步骤 说明
1. 建立基线 在未加固版本上用 perf/gprof/高精度计时器找出热点函数,记录执行时间或 QPS。
2. 分级打点 对同一代码生成 A=不保护、B=Mutation、C=Virtualization、D=Ultra 四个版本;在目标函数入口/出口打点计时。
3. 测试矩阵 覆盖不同数据规模(如 1KB / 1MB)与不同路径,重复多次取平均,减少抖动。
4. 量化公式 执行时间开销 = (T_protected - T_baseline) / T_baseline;吞吐下降 = (QPS_baseline - QPS_protected) / QPS_baseline
5. 迭代优化 根据结果调整保护范围与级别,在预发布环境做 A/B 测试,确认可接受后再全量。

性能优化与保护级别平衡

保护类型 原理 保护强度 性能开销 适用场景
Mutation 指令层等价变换、垃圾指令、重排 弱,抗特征码扫描 极低 库函数、非核心逻辑、"去特征"
Virtualization 机器码→字节码,VM 解释执行 中到高 核心算法、授权校验等关键函数
Ultra 变异 + 虚拟化 最强 很高,可能一个数量级以上 调用极低、安全要求极高的少量代码

策略建议:热点函数优先 Mutation 或不保护;关键但非热点用 Virtualization;极少数关键函数在确认性能可接受时再用 Ultra。通过基线性能分析、分级保护、灰度测试与迭代优化,找到安全与性能的平衡点。

保护策略决策简表(按函数特征选型):

函数特征 建议保护类型 说明
热点、性能极度敏感(如主循环、加解密核心) 不保护或仅 Mutation 避免虚拟化导致一个数量级性能损失
关键但调用不频繁(如授权校验、协议编解码) Virtualization 平衡安全与性能的常见选择
调用极少、安全要求极高(如密钥派生、激活逻辑) Ultra 可接受较高开销时使用
仅需"去特征"、抗签名扫描 Mutation 性能影响最小

VMProtect 与其他保护工具对比

VMProtect vs. Themida

维度 VMProtect Themida / WinLicense
核心技术 以代码虚拟化为核心 多合一:虚拟机、混淆、压缩、反调试、反沙箱等
侧重点 虚拟机保护强度、虚拟指令集与寄存器轮转 反调试与反分析的广度
性能 开销主要来自虚拟化 因大量反调试与校验,整体开销通常更高
授权 序列号、硬件绑定、水印等 同样强大,更侧重反篡改与反盗版整体方案

VMProtect vs. UPX

维度 VMProtect UPX
定位 商业软件保护,防逆向与盗版 开源可执行文件压缩,主要目标为减小体积
原理 虚拟化、变异、混淆、反调试、完整性校验 压缩/解压,运行时解压到内存再执行
安全性 高,静态分析极其困难 低,壳逻辑公开,易被脱壳,几乎无防逆向能力
性能 可能显著增加,可配置控制 解压一次性开销,之后原生速度运行

小结:抗自动化/去特征选 Mutation;保护核心算法选 Virtualization;追求最高强度考虑 Ultra 或 Themida;仅需减体积用 UPX,但无安全性可言。


对抗动态分析与自校验

调试器检测

  • API 探测:如 ptrace(Linux/Android)、IsDebuggerPresent(Windows)。
  • 异常行为检测:如 int 3 断点、单步异常。
  • 时间差检测:关键函数执行时间异常则判定被调试。
  • 代码校验:运行时计算关键代码段 CRC/哈希,被修改则触发自毁。

反模拟器/反虚拟机

  • 环境特征探测:CPUID、系统调用行为、设备指纹等识别 QEMU、Bochs、VMware、VirtualBox 等。
  • 指令执行异常、Timing/Resource 异常等。

代码自校验(对抗内存断点与 JIT 攻击)

  • 完整性校验:在启动或关键路径计算受保护代码段哈希(如 CRC32、SHA-256),与预设值比对,不一致则终止。
  • 校验范围:覆盖函数主体、序言、返回地址及动态生成/解密代码块;还可校验数据段、IAT、重定位表等。
  • 校验逻辑:分散内联到 VM 解释器主循环,且校验代码自身被混淆/虚拟化,难以定位与篡改。
  • 对抗 JIT Spraying:通过上述完整性校验与多维度校验,使注入或篡改的代码在校验时失败。

自校验与对抗手段对照

攻击方式 自校验/防御手段
内存断点修改代码 校验范围覆盖断点区域;分段动态计算校验和;校验逻辑自身加固
硬件断点 (DRx) 检测调试寄存器使用,或对关键系统调用监控
JIT 注入 / JIT Spraying 校验覆盖动态生成代码块;校验内联化、多维度(代码+数据+IAT)
篡改 IAT/重定位表 对 IAT、重定位表等做哈希校验,防止重定向执行流

ARM 与 x86 架构差异

维度 x86/x64 ARM (AArch64)
寄存器集 16 个通用寄存器,部分有特殊用途 31 个 64 位通用寄存器,X29(FP)、X30(LR) 等有特殊用途
调用约定 参数多经栈,部分经寄存器 前 8 个参数优先 X0--X7,返回值 X0/X1,对寄存器使用更严格
轮转实现 需避开 RSP、RFLAGS 等 需避开 SP、LR、XZR 等,并遵守调用约定与栈帧
指令形态 变长指令,寻址复杂 定长 32 位,寻址相对规整,PC 相对寻址等需特殊处理

寄存器轮转的核心思想在两种架构上一致(VM_CONTEXT + 动态映射),实现细节因寄存器集与 ABI 不同而不同。ARM 异常处理时,VM 上下文需由软件在入口/出口完整保存与恢复,并与操作系统异常处理协同(保存通用寄存器、VM 状态等),不能破坏栈帧与调用约定。


最佳实践与常见问题

最佳实践速览

类别 建议
接入前 在目标工具链(含 C++20/23、异常、内联汇编)下做全量回归;明确要保护的函数清单与级别。
产物与符号 始终保留未加固的基准产物与符号文件(.pdb/.sym/.so.debug),建立加固前后二进制与符号的对应关系。
性能 先做基线性能分析,再分级保护;热点用 Mutation 或不保护,关键非热点用 Virtualization,极少数用 Ultra。
崩溃与调试 发布包可去符号,但符号服务器必须保留与加固产物匹配的符号;关键路径可酌情降低虚拟化强度以便排错。
CI 集成 使用 VMProtect_Con 等命令行工具,将加固步骤纳入构建流水线,并自动化生成/归档符号。

常见问题(FAQ)

问题 简要回答
SDK 标记被优化掉怎么办? 确保标记成对、作用域内代码被引用,必要时关闭该函数的内联或优化,或改用无源码方式在 MAP 中指定范围。
加固后 JNI/cgo 调用失败? 检查 ABI、调用约定、导出符号是否被虚拟化后仍满足链接与重定位要求;边界避免依赖未约定的栈/结构体布局。
崩溃堆栈无法符号化? 使用与加固产物一一对应的未剥离符号文件,并在符号服务器中按构建 ID 或版本关联;必要时保留"未加固但同构"的符号用于对照。
如何评估抗静态/动态分析能力? 静态:用 IDA 等看代码段是否被 VM/字节码替代、自动脱壳工具是否失效。动态:看反调试/反虚拟机/自校验是否在调试或篡改下触发。
RSA/ECC 与 AES 保护策略有何不同? RSA/ECC 核心运算对性能极敏感,通常仅 Mutation 或不保护核心循环,外围或低频逻辑可用 Virtualization;AES 同理,核心加解密循环慎用虚拟化。

总结

  • 接入方式:零侵入(直接对二进制)或轻侵入(SDK 标记);与 C++20/23 语法无直接耦合,兼容性取决于工具链与架构;保护粒度与对象(导出/内部函数)可配置,建议只保护关键路径并按性能敏感度选择 Mutation/Virtualization/Ultra。
  • 产物操作:Windows 用 GUI 打开工程配置后编译;Android SO 可有源码(SDK+NDK)或无源码(二进制加固),需保留未加固产物与符号以便符号化与排错。
  • 对使用的影响:JNI/cgo 以二进制兼容为主,注意 ABI 与重定位;崩溃堆栈依赖保留符号与加固前后对应关系做解析。
  • 原理:代码虚拟化(机器码→字节码+VM 解释)、基于栈与 NOR 等精简指令集、寄存器轮转、变异与混淆、完整性校验与反调试等。
  • 使用:SDK 标记(Begin/End 成对)与无源码加壳两种模式;可命令行集成 CI。
  • 性能与选型:Mutation 开销极小,Virtualization 中等偏高,Ultra 最高;需结合热点分析与分级策略平衡安全与性能;与 Themida 侧重不同,与 UPX 定位完全不同(保护 vs 压缩)。
  • 对抗:反调试、反模拟器、代码自校验(含对抗内存断点与 JIT 注入)等多层手段;ARM 与 x86 在轮转与异常处理上有架构差异,实现时需遵循各自 ABI 与异常模型。
  • 性能与选型:典型算法(AES/SHA/RSA/ECC/图像)在 Mutation/Virtualization/Ultra 下的经验数据可供参考,需结合测试方法论做实测;保护策略按函数特征(热点/关键/低频高安全)选型。
  • 实践:保留未加固产物与符号、建立加固前后对应、分级保护与灰度测试、CI 集成与 FAQ 中的常见问题排查,可减少上线风险。

本文档为通用技术整理,仅供学习与选型参考,不涉及任何第三方产品背书。

相关推荐
独断万古他化1 天前
【SSM开发实战:博客系统】(三)核心业务功能开发与安全加密实现
spring boot·spring·mybatis·博客系统·加密
羑悻的小杀马特8 天前
逆向之刃出鞘!Ghidra 全栈部署 + 实战破译手册(2026 硬核版)
反编译·逆向工程
深信达沙箱9 天前
源代码防泄密软件的综合对比分析
加密·源代码·沙盒
不凉帅14 天前
NO.4信息安全技术基础知识
网络安全·信息安全·软考·高项·加密
深信达沙箱16 天前
如何选择源代码加密软件?应关注哪些核心技术要素
linux·服务器·网络·加密·软件·源代码·沙盒
深信达沙箱16 天前
SDC沙箱能够满足哪些场景需求?
网络·加密·软件·源代码·沙盒
深信达沙箱17 天前
业务系统安全办公沙箱解决方案
网络·系统安全·加密·源代码·沙盒
BOB-wangbaohai18 天前
软考-系统架构师-信息安全技术基础知识(二)
网络安全·软考·加密·系统架构设计师·解密
BOB-wangbaohai19 天前
软考-系统架构师-信息安全技术基础知识(一)
安全·软考·系统架构师·加密·解密