You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确获取段寄存器的不可见部分(AMD虚拟化VMCB场景)

正确获取AMD-V中段寄存器不可见部分的方法

你的代码在解析段选择子、计算段描述符地址、组合base/limit/属性位时存在多个问题,导致VMCB客户机状态无效。以下是具体问题点和修正方案:

原代码的核心问题

  • 段选择子解析错误:((PSEGMENT_SELECTOR) & (pRegister->selector)) 逻辑错误——pRegister->selector 是16位数值,取地址强转指针会指向错误内存区域,应直接将数值强制转换为SEGMENT_SELECTOR类型。
  • 段描述符地址计算错误:每个段描述符固定为8字节,你用sizeof(PVOID)属于逻辑侥幸,应显式使用sizeof(CODE_SEGMENT_DESCRIPTOR);LDT处理逻辑错误,需先从GDT读取LDT对应的描述符获取基址,而非直接用Ldtr * sizeof(PVOID)计算偏移。
  • Base和Limit计算不完整:64位模式下段描述符Base是40位,原代码仅组合低32位会导致值错误;Limit未处理G(粒度位),若G=1需将Limit乘以4096并补0xFFF。
  • 属性位映射错误:VMCB段属性有固定位定义,原代码中保留位Reserved_0/Reserved_1不应设置,且部分位偏移与AMD-V规范不匹配。

修正后的实现步骤与代码

1. 定义符合AMD规范的结构体

typedef struct _SEGMENT_SELECTOR {
    USHORT RPL : 2;
    USHORT TI : 1;
    USHORT Index : 13;
} SEGMENT_SELECTOR, *PSEGMENT_SELECTOR;

typedef struct _CODE_SEGMENT_DESCRIPTOR {
    USHORT SegmentLimit_LowPart;
    USHORT BaseAddress_LowPart;
    UCHAR BaseAddress_MiddlePart;
    UCHAR Type : 4;
    UCHAR S : 1;
    UCHAR DPL : 2;
    UCHAR P : 1;
    UCHAR SegmentLimit_HighPart : 4;
    UCHAR AVL : 1;
    UCHAR L : 1;
    UCHAR D : 1;
    UCHAR G : 1;
    UCHAR BaseAddress_HighPart;
    USHORT ExtendedBaseAddress; // 64位模式下扩展的Base高16位
} CODE_SEGMENT_DESCRIPTOR, *PCODE_SEGMENT_DESCRIPTOR;

typedef struct _VM_SEGMENT_REGISTER {
    USHORT selector;
    struct {
        ULONGLONG base;
        ULONGLONG limit;
        USHORT attrib;
    } HiddenPart;
} VM_SEGMENT_REGISTER, *PVM_SEGMENT_REGISTER;

2. 读取GDTR基址与大小

GDTR STRUCT
    Limit WORD ?
    Base QWORD ?
GDTR ENDS

VOID Asm_ReadGdtr(GDTR* pGdtr) {
    __asm {
        sgdt [pGdtr]
    }
}

3. 修正段描述符获取与填充逻辑

VOID FillCsHiddenPart(PVM_SEGMENT_REGISTER pRegister, const GDTR* pGdtr, USHORT LdtrSelector) {
    pRegister->selector = Asm_ReadCs();
    PSEGMENT_SELECTOR pSel = (PSEGMENT_SELECTOR)&pRegister->selector;

    PCODE_SEGMENT_DESCRIPTOR pCodeDesc = NULL;

    if (pSel->TI == 0) {
        // GDT中段描述符:基址 + 索引*8
        pCodeDesc = (PCODE_SEGMENT_DESCRIPTOR)(pGdtr->Base + (pSel->Index * sizeof(CODE_SEGMENT_DESCRIPTOR)));
    } else {
        // 先从GDT获取LDT描述符
        PCODE_SEGMENT_DESCRIPTOR pLdtDesc = (PCODE_SEGMENT_DESCRIPTOR)(pGdtr->Base + (LdtrSelector >> 3) * sizeof(CODE_SEGMENT_DESCRIPTOR));
        // 计算LDT完整基址(40位)
        ULONGLONG LdtBase = (ULONGLONG)pLdtDesc->BaseAddress_LowPart
                          | ((ULONGLONG)pLdtDesc->BaseAddress_MiddlePart << 16)
                          | ((ULONGLONG)pLdtDesc->BaseAddress_HighPart << 24)
                          | ((ULONGLONG)pLdtDesc->ExtendedBaseAddress << 32);
        // 获取LDT中的代码段描述符
        pCodeDesc = (PCODE_SEGMENT_DESCRIPTOR)(LdtBase + (pSel->Index * sizeof(CODE_SEGMENT_DESCRIPTOR)));
    }

    // 计算完整Base(40位)
    pRegister->HiddenPart.base = (ULONGLONG)pCodeDesc->BaseAddress_LowPart
                               | ((ULONGLONG)pCodeDesc->BaseAddress_MiddlePart << 16)
                               | ((ULONGLONG)pCodeDesc->BaseAddress_HighPart << 24)
                               | ((ULONGLONG)pCodeDesc->ExtendedBaseAddress << 32);

    // 计算Limit,处理粒度位G
    ULONG limit = pCodeDesc->SegmentLimit_LowPart | ((pCodeDesc->SegmentLimit_HighPart & 0xF) << 16);
    if (pCodeDesc->G) {
        limit = (limit << 12) | 0xFFF;
    }
    pRegister->HiddenPart.limit = limit;

    // 严格按照AMD-V规范映射属性位
    if (!pCodeDesc->P) {
        pRegister->HiddenPart.attrib = 0;
    } else {
        pRegister->HiddenPart.attrib = 0;
        pRegister->HiddenPart.attrib |= (pCodeDesc->Type & 0xF);       // 类型位(含A、R、C)
        pRegister->HiddenPart.attrib |= ((pCodeDesc->DPL & 0x3) << 5); // DPL
        pRegister->HiddenPart.attrib |= (pCodeDesc->P << 7);           // 段存在位
        pRegister->HiddenPart.attrib |= (pCodeDesc->AVL << 12);        // AVL
        pRegister->HiddenPart.attrib |= (pCodeDesc->L << 13);          // 64位模式标志
        pRegister->HiddenPart.attrib |= (pCodeDesc->D << 14);          // 默认操作大小
        pRegister->HiddenPart.attrib |= (pCodeDesc->G << 15);          // 粒度位
    }
}

额外注意事项

  • 必须在Ring 0权限下执行代码,用户态无法访问GDT/LDT内存。
  • 64位模式下,代码段的L位必须设为1,D位必须设为0,否则VMCB会判定状态无效。
  • 需验证段描述符的P位是否为1,若为0则段不可用,禁止填充到VMCB。

内容的提问来源于stack exchange,提问作者fahuifai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 05:05:20