Skip to content

Linux 内核 LSM (Linux Security Module) 框架分析 ​

目录 ​

  1. LSM 框架概述
  2. 核心数据结构
  3. 主要 LSM 模块
  4. 钩子实现详解
  5. 策略加载
  6. 架构图

1. LSM 框架概述 ​

1.1 设计目标 ​

LSM (Linux Security Module) 是 Linux 内核的安全模块框架,提供了一种可插拔的安全机制。设计目标包括:

  • 统一接口: 为各种安全模块提供统一的钩子接口
  • 可扩展性: 支持多个安全模块同时运行
  • 最小化信任: 核心框架不依赖任何特定安全策略
  • 性能优化: 使用静态调用(static call)机制减少开销

1.2 安全钩子机制 ​

LSM 通过在内核关键路径上插入安全钩子来实现访问控制。当内核代码执行敏感操作时,会调用相应的 security_XXX() 函数,该函数再调用已注册的 LSM 钩子。

c
// 安全操作函数示例 (security.c:1813)
int security_inode_permission(struct inode *inode, int mask)
{
    if (unlikely(IS_PRIVATE(inode)))
        return 0;
    return call_int_hook(inode_permission, inode, mask);  // 调用所有已注册的 LSM 钩子
}

1.3 框架初始化 ​

LSM 框架的初始化流程 (security/lsm_init.c):

early_security_init()        --> early_initcall
security_init()             --> 初始化 blob 大小、创建缓存
security_initcall_pure()    --> pure_initcall
security_initcall_early()    --> early_initcall
security_initcall_core()    --> core_initcall
security_initcall_subsys()   --> subsys_initcall
security_initcall_fs()       --> fs_initcall
security_initcall_device()   --> device_initcall
security_initcall_late()    --> late_initcall

关键代码 (security/lsm_init.c:405-485):

c
int __init security_init(void)
{
    // 1. 解析 LSM 顺序列表
    if (lsm_order_cmdline)
        lsm_order_parse(lsm_order_cmdline, "cmdline");
    else
        lsm_order_parse(lsm_order_builtin, "builtin");

    // 2. 准备每个 LSM 的 blob 大小
    lsm_order_for_each(lsm)
        lsm_prepare(*lsm);

    // 3. 创建 LSM 缓存
    if (blob_sizes.lbs_file)
        lsm_file_cache = kmem_cache_create("lsm_file_cache", ...);
    if (blob_sizes.lbs_inode)
        lsm_inode_cache = kmem_cache_create("lsm_inode_cache", ...);

    // 4. 分配初始进程的 security blob
    if (lsm_cred_alloc(...))
        panic("early LSM cred alloc failed\n");
    if (lsm_task_alloc(current))
        panic("early LSM task alloc failed\n");

    // 5. 初始化每个 LSM
    lsm_order_for_each(lsm)
        lsm_init_single(*lsm);

    return 0;
}

2. 核心数据结构 ​

2.1 LSM_HOOK 宏定义 ​

钩子通过 LSM_HOOK 宏在 include/linux/lsm_hook_defs.h 中定义:

c
// 来自 lsm_hook_defs.h:142
LSM_HOOK(int, 0, inode_permission, struct inode *inode, int mask)
LSM_HOOK(int, 0, inode_setattr, struct mnt_idmap *idmap, struct dentry *dentry,
         struct iattr *attr)
LSM_HOOK(int, 0, file_permission, struct file *file, int mask)
LSM_HOOK(int, 0, mmap_file, struct file *file, unsigned long reqprot,
         unsigned long prot, unsigned long flags)

宏展开:

c
// 第一个参数: 返回类型
// 第二个参数: 默认值
// 第三个参数: 钩子名称
// 剩余参数: 函数参数

2.2 union security_list_options ​

定义在 include/linux/lsm_hooks.h:38-43,包含所有钩子函数指针:

c
union security_list_options {
    #define LSM_HOOK(RET, DEFAULT, NAME, ...) RET (*NAME)(__VA_ARGS__);
    #include "lsm_hook_defs.h"
    #undef LSM_HOOK
    void *lsm_func_addr;  // 用于通用调用
};

2.3 struct security_hook_list ​

已注册钩子的链表节点 (include/linux/lsm_hooks.h:95-99):

c
struct security_hook_list {
    struct lsm_static_call *scalls;  // 静态调用表
    union security_list_options hook; // 钩子函数
    const struct lsm_id *lsmid;     // LSM 标识
} __randomize_layout;

2.4 struct lsm_static_call ​

静态调用结构 (include/linux/lsm_hooks.h:51-57):

c
struct lsm_static_call {
    struct static_call_key *key;      // 静态调用键
    void *trampoline;                  // 跳转桩
    struct security_hook_list *hl;     // 钩子列表
    struct static_key_false *active;   // 激活状态
} __randomize_layout;

2.5 struct lsm_info ​

LSM 模块信息 (include/linux/lsm_hooks.h:170-184):

c
struct lsm_info {
    const struct lsm_id *id;           // LSM ID
    enum lsm_order order;               // 初始化顺序
    unsigned long flags;                // 标志 (LSM_FLAG_LEGACY_MAJOR, LSM_FLAG_EXCLUSIVE)
    struct lsm_blob_sizes *blobs;      // blob 大小
    int *enabled;                       // 是否启用
    int (*init)(void);                  // 初始化函数

    // 各阶段初始化回调
    int (*initcall_pure)(void);
    int (*initcall_early)(void);
    int (*initcall_core)(void);
    int (*initcall_subsys)(void);
    int (*initcall_fs)(void);
    int (*initcall_device)(void);
    int (*initcall_late)(void);
};

2.6 LSM 顺序枚举 ​

c
enum lsm_order {
    LSM_ORDER_FIRST = -1,     // capability (首先初始化)
    LSM_ORDER_MUTABLE = 0,   // 可变顺序 (通过 CONFIG_LSM 指定)
    LSM_ORDER_LAST = 1,      // integrity 模块 (最后初始化)
};

2.7 struct lsm_blob_sizes ​

定义在 include/linux/lsm_hooks.h:104-122,描述各对象的安全 blob 大小:

c
struct lsm_blob_sizes {
    unsigned int lbs_cred;          // cred 结构
    unsigned int lbs_file;          // file 结构
    unsigned int lbs_ib;           // InfiniBand
    unsigned int lbs_inode;         // inode 结构
    unsigned int lbs_sock;          // socket 结构
    unsigned int lbs_superblock;    // super_block 结构
    unsigned int lbs_ipc;           // IPC
    unsigned int lbs_key;           // key
    unsigned int lbs_msg_msg;       // 消息
    unsigned int lbs_perf_event;     // 性能事件
    unsigned int lbs_task;          // task_struct
    unsigned int lbs_xattr_count;   // xattr 槽位数
    unsigned int lbs_tun_dev;       // tun 设备
    unsigned int lbs_bdev;          // 块设备
    unsigned int lbs_bpf_map;       // BPF map
    unsigned int lbs_bpf_prog;       // BPF program
    unsigned int lbs_bpf_token;     // BPF token
};

2.8 call_int_hook 和 call_void_hook 宏 ​

c
// security.c:454-471
#define __CALL_STATIC_INT(NUM, R, HOOK, LABEL, ...)                 \
do {                                                                   \
    if (static_branch_unlikely(&SECURITY_HOOK_ACTIVE_KEY(HOOK, NUM))) { \
        R = static_call(LSM_STATIC_CALL(HOOK, NUM))(__VA_ARGS__);     \
        if (R != LSM_RET_DEFAULT(HOOK))                               \
            goto LABEL;                                                \
    }                                                                  \
} while (0);

#define call_int_hook(HOOK, ...)                                       \
({                                                                      \
    __label__ OUT;                                                     \
    int RC = LSM_RET_DEFAULT(HOOK);                                    \
    LSM_LOOP_UNROLL(__CALL_STATIC_INT, RC, HOOK, OUT, __VA_ARGS__);    \
OUT:                                                                   \
    RC;                                                                \
})

3. 主要 LSM 模块 ​

3.1 SELinux ​

位置: security/selinux/

特点: MAC (强制访问控制),基于标签的访问控制

定义 (security/selinux/hooks.c:7783-7791):

c
DEFINE_LSM(selinux) = {
    .id = &selinux_lsmid,
    .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,  // 独占模式
    .enabled = &selinux_enabled_boot,
    .blobs = &selinux_blob_sizes,
    .init = selinux_init,
};

核心数据结构:

  • security_id: 安全标识符 (SID)
  • task_security_struct: 进程安全上下文
  • inode_security_struct: inode 安全上下文
  • file_security_struct: 文件安全上下文
  • avc: 访问向量缓存

策略加载 (security/selinux/ss/services.c:2306):

c
int security_load_policy(void *data, size_t len,
                        struct selinux_load_state *load_state)

3.2 AppArmor ​

位置: security/apparmor/

特点: 基于路径的 MAC,使用文件系统路径进行访问控制

定义 (security/apparmor/lsm.c:2569-2577):

c
DEFINE_LSM(apparmor) = {
    .id = &apparmor_lsmid,
    .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,
    .enabled = &apparmor_enabled,
    .blobs = &apparmor_blob_sizes,
    .init = apparmor_init,
};

路径处理 (security/apparmor/path.c:88-100): AppArmor 使用 d_namespace_path() 函数将路径解析为命名空间路径,然后与策略进行匹配。

3.3 Landlock ​

位置: security/landlock/

特点: 用户空间安全模块,用于限制进程的文件系统和网络访问

定义 (security/landlock/setup.c:77-81):

c
DEFINE_LSM(LANDLOCK_NAME) = {
    .id = &landlock_lsmid,
    .init = landlock_init,
    .blobs = &landlock_blob_sizes,
};

核心结构:

  • landlock_ruleset: 规则集
  • landlock_domain: 域(规则的集合)
  • landlock_object: 受保护的对象

钩子示例 (security/landlock/fs.c:1823-1843):

c
static struct security_hook_list landlock_hooks[] __ro_after_init = {
    LSM_HOOK_INIT(inode_free_security_rcu, hook_inode_free_security_rcu),
    LSM_HOOK_INIT(sb_delete, hook_sb_delete),
    LSM_HOOK_INIT(sb_mount, hook_sb_mount),
    LSM_HOOK_INIT(move_mount, hook_move_mount),
    // ... 更多文件系统钩子
};

3.4 LoadPin ​

位置: security/loadpin/

特点: 固件和模块加载固定,将加载固定到特定文件系统

定义 (security/loadpin/loadpin.c:430-437):

c
DEFINE_LSM(loadpin) = {
    .id = &loadpin_lsmid,
    .init = loadpin_init,
    .initcall_fs = init_loadpin_securityfs,
};

核心逻辑 (security/loadpin/loadpin.c:127-160):

c
static int loadpin_check(struct file *file, enum kernel_read_file_id id)
{
    struct super_block *load_root;
    const char *origin = kernel_read_file_id_str(id);
    bool first_root_pin = false;

    // 第一次加载,记录根文件系统
    if (!pinned_root) {
        pinned_root = file->f_path.mnt->mnt_sb;
        first_root_pin = true;
    }

    // 检查是否来自相同的已固定根
    if (file->f_path.mnt->mnt_sb != pinned_root) {
        if (enforce) {
            report_load(origin, file, "pinning-denied");
            return -EPERM;
        }
    }
    return 0;
}

3.5 Lockdown ​

位置: security/lockdown/

特点: 内核锁定,防止某些内核功能的滥用

定义 (security/lockdown/lockdown.c:165-171):

c
#ifdef CONFIG_SECURITY_LOCKDOWN_LSM_EARLY
DEFINE_EARLY_LSM(lockdown) = {
#else
DEFINE_LSM(lockdown) = {
#endif
    .id = &lockdown_lsmid,
    .init = lockdown_lsm_init,
    .initcall_core = lockdown_secfs_init,
};

锁定级别:

  • LOCKDOWN_NONE: 无限制
  • LOCKDOWN_INTEGRITY_MAX: 最大化完整性保护
  • LOCKDOWN_CONFIDENTIALITY_MAX: 最大化机密性保护

钩子实现 (security/lockdown/lockdown.c:59-73):

c
static int lockdown_is_locked_down(enum lockdown_reason what)
{
    if (kernel_locked_down >= what) {
        if (lockdown_reasons[what])
            pr_notice_ratelimited("Lockdown: %s: %s is restricted; see man kernel_lockdown.7\n",
                  current->comm, lockdown_reasons[what]);
        return -EPERM;
    }
    return 0;
}

4. 钩子实现详解 ​

4.1 inodeHooks ​

inode_permission ​

检查对 inode 的访问权限。

SELinux 实现 (security/selinux/hooks.c:3222-3275):

c
static int selinux_inode_permission(struct inode *inode, int requested)
{
    int mask;
    u32 perms;
    u32 sid = current_sid();
    struct task_security_struct *tsec;
    struct inode_security_struct *isec;

    // 将操作码转换为权限位
    mask = inode_mask_to_av(requested);

    // 获取当前进程和 inode 的安全上下文
    tsec = selinux_cred(current_cred())->tsec;
    isec = selinux_inode(inode);

    // 调用 AVC 检查权限
    return avc_has_perm(tsec->sid, isec->sid, isec->sclass, mask, NULL);
}

inode_setattr ​

检查设置 inode 属性的权限。

c
int security_inode_setattr(struct mnt_idmap *idmap,
                           struct dentry *dentry, struct iattr *attr)
{
    if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
        return 0;
    return call_int_hook(inode_setattr, idmap, dentry, attr);
}

4.2 fileHooks ​

file_permission ​

检查文件访问权限。

c
// security.c:2365-2368
int security_file_permission(struct file *file, int mask)
{
    return call_int_hook(file_permission, file, mask);
}

mmap_file ​

检查内存映射权限。

关键代码 (security.c:2500-2514):

c
int security_mmap_file(struct file *file, unsigned long prot,
                       unsigned long flags)
{
    int ret;

    ret = call_int_hook(mmap_file, file, reqprot, prot, flags);
    return ret;
}

file_open ​

文件打开时的检查。

c
// security.c:2526-2535
int security_file_open(struct file *file)
{
    int ret;

    ret = call_int_hook(file_open, file);
    return ret;
}

4.3 taskHooks ​

ptrace_access_check ​

检查 ptrace 访问权限。

SELinux 实现 (security/selinux/hooks.c:1900-1928):

c
static int selinux_ptrace_access_check(struct task_struct *child,
                                        unsigned int mode)
{
    const struct task_security_struct *tsec = selinux_cred(current_cred())->tsec;
    const struct task_security_struct *csec = selinux_cred(__task_cred(child))->tsec;

    // PTRACE_MODE_READ 和 PTRACE_MODE_TRACE 权限检查
    return avc_has_perm(tsec->sid, csec->sid, SECCLASS_PROCESS,
                        PTRACE_MODE_ACCESS, NULL);
}

capget ​

获取进程能力集。

c
// security.c:587-593
int security_capget(const struct task_struct *target,
                    kernel_cap_t *effective,
                    kernel_cap_t *inheritable,
                    kernel_cap_t *permitted)
{
    return call_int_hook(capget, target, effective, inheritable, permitted);
}

4.4 bprm_hooks ​

bprm_check_security ​

在执行二进制文件前进行检查。

c
// security.c:793-796
int security_bprm_check(struct linux_binprm *bprm)
{
    return call_int_hook(bprm_check_security, bprm);
}

bprm_creds_for_exec ​

准备执行凭证。

c
// security.c:752-755
int security_bprm_creds_for_exec(struct linux_binprm *bprm)
{
    return call_int_hook(bprm_creds_for_exec, bprm);
}

5. 策略加载 ​

5.1 安全模块初始化顺序 ​

LSM 通过 CONFIG_LSM 配置选项指定初始化顺序:

makefile
# 来自 Kconfig
config LSM
    string "Order of LSM to initialize"
    default "landlock,lockdown,yama,loadpin,commoncap,selinux,apparmor,tomoyo,ima,eVM"

5.2 security_add_hooks ​

将 LSM 的钩子注册到框架 (security/lsm_init.c:367-378):

c
void __init security_add_hooks(struct security_hook_list *hooks, int count,
                               const struct lsm_id *lsmid)
{
    int i;

    for (i = 0; i < count; i++) {
        hooks[i].lsmid = lsmid;
        if (lsm_static_call_init(&hooks[i]))
            panic("exhausted LSM callback slots with LSM %s\n",
                  lsmid->name);
    }
}

5.3 lsm_static_call_init ​

初始化静态调用 (security/lsm_init.c:339-357):

c
static int __init lsm_static_call_init(struct security_hook_list *hl)
{
    struct lsm_static_call *scall = hl->scalls;
    int i;

    for (i = 0; i < MAX_LSM_COUNT; i++) {
        // 找到第一个未使用的静态调用槽位
        if (!scall->hl) {
            __static_call_update(scall->key, scall->trampoline,
                                 hl->hook.lsm_func_addr);
            scall->hl = hl;
            static_branch_enable(scall->active);
            return 0;
        }
        scall++;
    }
    return -ENOSPC;
}

5.4 LSM 策略加载示例 (SELinux) ​

c
// security/selinux/ss/services.c:2306-2330
int security_load_policy(void *data, size_t len,
                        struct selinux_load_state *load_state)
{
    struct selinux_policy *newpolicy, *oldpolicy;
    struct selinux_policy_convert_data *convert_data;
    int rc = 0;

    // 1. 解析策略文件
    newpolicy = kzalloc_obj(*newpolicy);
    if (!newpolicy)
        return -ENOMEM;

    // 2. 读取和验证策略
    rc = policydb_read(&newpolicy->policydb, fp);
    if (rc)
        goto err;

    // 3. 分配 SID 表
    rc = sidtab_init(&newpolicy->sidtab);
    if (rc)
        goto err;

    // 4. 转换策略数据
    convert_data = selinux_policy_convert(...);

    // 5. 安装新策略
    oldpolicy = rcu_dereference_protected(state->policy, 1);
    rcu_assign_pointer(state->policy, newpolicy);

    // 6. 同步并释放旧策略
    synchronize_rcu();
    selinux_policy_free(oldpolicy);

    return 0;
}

5.5 LSM Hook 调用流程 ​

应用程序
    |
    v
系统调用 (如 open())
    |
    v
VFS 层 (如 vfs_open())
    |
    v
security_file_open()          <-- LSM 钩子入口
    |
    v
call_int_hook(file_open, file)  <-- 遍历所有注册的 LSM
    |
    +---> SELinux: selinux_file_open()
    +---> AppArmor: apparmor_file_open()
    +---> Landlock: landlock_file_open()
    +---> ...
    |
    v
返回 0 表示允许,负值表示拒绝

6. 架构图 ​

6.1 LSM 框架整体架构 ​

+------------------------------------------------------------------+
|                         内核子系统                                 |
|  +------------------+  +------------------+  +------------------+  |
|  |   文件系统 (VFS)  |  |    进程管理      |  |     网络栈       |  |
|  +--------+---------+  +--------+---------+  +--------+---------+  |
|           |                    |                    |              |
|           v                    v                    v              |
|  +--------+---------+  +--------+---------+  +--------+---------+  |
|  | inode_hooks   |  | task_hooks    |  | socket_hooks   |         |
|  | file_hooks    |  | bprm_hooks   |  | netlink_hooks  |         |
|  | path_hooks   |  | cred_hooks   |  |                 |         |
|  +--------+---------+  +--------+---------+  +--------+---------+  |
|           |                    |                    |              |
+---------- | -------------------- | -------------------- | -----------+
            |                    |                    |
            v                    v                    v
+------------------------------------------------------------------+
|                     security.c (LSM 核心)                          |
|                                                                  |
|  call_int_hook(), call_void_hook()                                |
|  security_add_hooks()                                             |
|  lsm_static_call_init()                                           |
|                                                                  |
+------------------------------------------------------------------+
            |                    |                    |
            v                    v                    v
+------------------------------------------------------------------+
|                        LSM 模块                                    |
|  +----------+  +----------+  +----------+  +----------+          |
|  | SELinux  |  | AppArmor |  | Landlock |  | LoadPin  |          |
|  +----------+  +----------+  +----------+  +----------+          |
|  +----------+  +----------+  +----------+  +----------+          |
|  | Lockdown |  |   Yama   |  | Smack   |  |  Tomoyo  |          |
|  +----------+  +----------+  +----------+  +----------+          |
+------------------------------------------------------------------+
            |                    |                    |
            v                    v                    v
+------------------------------------------------------------------+
|                     安全策略 (策略文件)                             |
|  +----------+  +----------+  +----------+  +----------+          |
|  | SELinux  |  | AppArmor |  | Landlock |  |  IMA/EVM |          |
|  |  policy  |  |  policy  |  |  ruleset |  |  policies|          |
|  +----------+  +----------+  +----------+  +----------+          |
+------------------------------------------------------------------+

6.2 Hook 调用机制 ​

                    static_calls_table
                    +------------------+
                    | inode_permission | ---> [SELinux] --> [AppArmor] --> [Landlock]
                    | file_open       | ---> [SELinux] --> [AppArmor]
                    | mmap_file       | ---> [SELinux] --> [Landlock]
                    | socket_connect  | ---> [SELinux] --> [Landlock]
                    | ...              |
                    +------------------+

调用过程:
1. 内核代码调用 security_xxx()
2. security_xxx() 调用 call_int_hook(HOOK, ...)
3. call_int_hook() 使用 UNROLL 宏展开为多个静态调用
4. 每个静态调用检查对应的 static_key 是否激活
5. 如果激活,调用该 LSM 的钩子函数
6. 如果返回非默认值,短路求值

6.3 Blob 内存布局 ​

struct task_struct {
    ...
    void *security;  ---> +---------------------------+
                          | cred blob (LSM1)          |
                          | cred blob (LSM2)          |
                          | ...                       |
                          +---------------------------+
                          | task blob (LSM1)         |
                          | task blob (LSM2)         |
                          | ...                       |
                          +---------------------------+
}

struct inode {
    ...
    void *i_security; ---> +---------------------------+
                           | rcu_head                  |  (由 framework 添加)
                           | inode blob (LSM1)         |
                           | inode blob (LSM2)         |
                           | ...                       |
                           +---------------------------+
}

6.4 LSM 初始化序列 ​

引导时序:
                                                 优先级
early_security_init()      [early LSM]         highest
    |
    +-- lsm_prepare()
    +-- lsm_init_single()
    |
    v
security_init()            [框架初始化]
    |
    +-- 解析 lsm_order
    +-- lsm_prepare() for all
    +-- 创建 slab 缓存
    +-- 分配初始进程的 security blob
    +-- lsm_init_single() for non-early LSMs
    |
    v
pure_initcall(security_initcall_pure)           highest
early_initcall(security_initcall_early)
core_initcall(security_initcall_core)
subsys_initcall(security_initcall_subsys)
fs_initcall(security_initcall_fs)
device_initcall(security_initcall_device)
late_initcall(security_initcall_late)           lowest

附录 ​

A. 关键文件列表 ​

文件路径描述
security/security.cLSM 框架核心实现
security/lsm_init.cLSM 初始化逻辑
include/linux/lsm_hooks.hLSM 钩子数据结构
include/linux/lsm_hook_defs.h钩子定义
security/selinux/SELinux 实现
security/apparmor/AppArmor 实现
security/landlock/Landlock 实现
security/loadpin/LoadPin 实现
security/lockdown/Lockdown 实现

B. 重要宏 ​

宏位置描述
LSM_HOOK_INIT(NAME, HOOK)lsm_hooks.h:136初始化 security_hook_list
DEFINE_LSM(name)lsm_hooks.h:186定义 LSM 模块
DEFINE_EARLY_LSM(name)lsm_hooks.h:191定义早期 LSM
call_int_hook(HOOK, ...)security.c:463调用整型返回钩子
call_void_hook(HOOK, ...)security.c:448调用 void 返回钩子

C. LSM ID 定义 ​

c
// include/uapi/linux/lsm.h
#define LSM_ID_NONE           0
#define LSM_ID_CAPABILITY     1
#define LSM_ID_SELINUX        2
#define LSM_ID_SMACK           3
#define LSM_ID_TOMOYO         4
#define LSM_ID_APPARMOR        5
#define LSM_ID_YAMA           6
#define LSM_ID_LOADPIN        7
#define LSM_ID_LOCKDOWN       8
#define LSM_ID_LANDLOCK       9
#define LSM_ID_IMA            10
#define LSM_ID_EVM             11
#define LSM_ID_BPF             12
#define LSM_ID_SAFESETID       13
#define LSM_ID_BPF             14
#define LSM_ID_IPE             15

文档版本: 1.0生成时间: 2026-04-26内核版本: Linux 7.0 (master branch)

基于 VitePress 构建