|
typedef struct _SPY_SEGMENT
{
X86_SELECTOR Selector;
X86_DESCRIPTOR Descriptor;
PVOID pBase;
DWORD dLimit;
BOOL fOk;
}
SPY_SEGMENT, *PSPY_SEGMENT, **PPSPY_SEGMENT;
#define SPY_SEGMENT_ sizeof (SPY_SEGMENT)
NTSTATUS SpyOutputSegment (DWORD dSelector,
PVOID pOutput,
DWORD dOutput,
PDWORD pdInfo)
{
SPY_SEGMENT ss;
SpySegment (X86_SEGMENT_OTHER, dSelector, &ss);
return SpyOutputBinary (&ss, SPY_SEGMENT_,
pOutput, dOutput, pdInfo);
}
BOOL SpySegment (DWORD dSegment,
DWORD dSelector,
PSPY_SEGMENT pSegment)
{
BOOL fOk = FALSE;
if (pSegment != NULL)
{
fOk = TRUE;
if (!SpySelector (dSegment, dSelector,
&pSegment->Selector))
{
fOk = FALSE;
}
if (!SpyDescriptor (&pSegment->Selector,
&pSegment->Descriptor))
{
fOk = FALSE;
}
pSegment->pBase =
SpyDescriptorBase (&pSegment->Descriptor);
pSegment->dLimit =
SpyDescriptorLimit (&pSegment->Descriptor);
pSegment->fOk = fOk;
}
return fOk;
}
列表 4-15. 查询段的属性
SpySegment() 函数依赖其他几个帮助函数,以构建 SPY_SEGMENT 结构的某些部分。首先, SpySelector() 复制一个选择器的值到传入的 X86_SELECTOR 结构中。如果 SpySelector() 函数的第一个参数 dSegment 被设置为 X86_SEGMENT_OTHER (即 0 ), dSelector 参数将假定已经指定了一个有效的选择器值,因此该值将被简单的附给输出结构 X86_SELECTOR 的 wValue 成员。否则, dSelector 将被忽略, dSegment 会被用于一个 switch/case 结构中以便选择一个段寄存器或任务寄存器 TR 。注意,这种请求需要少量的嵌入式汇编, C 语言没有提供标准的方法访问处理器相关的特性,如段寄存器。
#define X86_SEGMENT_OTHER 0
#define X86_SEGMENT_CS 1
#define X86_SEGMENT_DS 2
#define X86_SEGMENT_ES 3
#define X86_SEGMENT_FS 4
#define X86_SEGMENT_GS 5
#define X86_SEGMENT_SS 6
#define X86_SEGMENT_TSS 7
//---------------------------------------------------------------
BOOL SpySelector (DWORD dSegment,
DWORD dSelector,
PX86_SELECTOR pSelector)
{
X86_SELECTOR Selector = {0, 0};
BOOL fOk = FALSE;
if (pSelector != NULL)
{
fOk = TRUE;
switch (dSegment)
{
case X86_SEGMENT_OTHER:
{
if (fOk = ((dSelector >> X86_SELECTOR_SHIFT)
<= X86_SELECTOR_LIMIT))
{
Selector.wValue = (WORD) dSelector;
}
break;
}
case X86_SEGMENT_CS:
{
__asm mov Selector.wValue, cs
break;
}
case X86_SEGMENT_DS:
{
__asm mov Selector.wValue, ds
break;
}
case X86_SEGMENT_ES:
{
__asm mov Selector.wValue, es
break;
}
case X86_SEGMENT_FS:
{
__asm mov Selector.wValue, fs
break;
}
case X86_SEGMENT_GS:
{
__asm mov Selector.wValue, gs
break;
}
case X86_SEGMENT_SS:
{
__asm mov Selector.wValue, ss
break;
}
case X86_SEGMENT_TSS:
{
__asm str Selector.wValue
break;
}
default:
{
fOk = FALSE;
break;
}
}
RtlCopyMemory (pSelector, &Selector, X86_SELECTOR_);
}
return fOk;
}
列表 4-16. 获取选择器( selector )的值
SpyDispatcher() 将从一个 64 位的描述符中读取数据,段选择器指向该描述符(见 列表 4-17 )。像你记得的那样,所有的选择器都包含一个表指示符( Table Indicator, TI )位,以确定选择器引用的描述符是位于 GDT ( TI=0 )中还是 LDT ( TI=1 )中。 列表 4-17 的上半部分处理了是 LDT 的情况。首先,使用汇编指令 SLDT 和 SGDT 分别读取 LDT 选择器的值以及段的大小限制和 GDT 的基地址。还记得 GDT 的线性基地址是显示指定的,而 LDT 是由 GDT 中的选择器间接引用的吗?所以, SpyDispatcher() 会首先验证 LDT 选择器的值。如果段选择器不为空并且没有超过 GDT 的限制,就会调用 SpyDescriptorType() 、 SpyDescriptorLimit() 和 SpyDescriptorBase()( 列表 4-17 给出了这些函数 ) 来获取 LDT 的基本属性:
l SpyDescriptorType() 返回描述符的类型数据及其 S 位域(参见 列表 4-2 )。 LDT 选择器必须指向一个类型为 X86_DESCRIPTOR_SYS_LDT 的系统描述符。
l SpyDescriptorLimit() 从描述符的 Limit1 、 Limit2 这两个位域中汇总段的大小限制。根据描述符的 G 标志指定的内存分配粒度的不同,其处理方式也会不同。
l SpyDescriptorBase() 只是简单的通过适当的组织描述符的 Base1 、 Base2 和 Base3 位域以获取一个 32 位的线性地址。
BOOL SpyDescriptor (PX86_SELECTOR pSelector,
PX86_DESCRIPTOR pDescriptor)
{
X86_SELECTOR ldt;
X86_TABLE gdt;
DWORD dType, dLimit;
BOOL fSystem;
PX86_DESCRIPTOR pDescriptors = NULL;
BOOL fOk = FALSE;
if (pDescriptor != NULL)
{
if (pSelector != NULL)
{
if (pSelector->TI) // ldt descriptor
{
__asm
{
sldt ldt.wValue
sgdt gdt.wLimit
}
if ((!ldt.TI) && ldt.Index &&
((ldt.wValue & X86_SELECTOR_INDEX)
<= gdt.wLimit))
{
dType = SpyDescriptorType (gdt.pDescriptors +
ldt.Index,
&fSystem);
dLimit = SpyDescriptorLimit (gdt.pDescriptors +
ldt.Index);
if (fSystem && (dType == X86_DESCRIPTOR_SYS_LDT)
&&
((DWORD) (pSelector->wValue
& X86_SELECTOR_INDEX)
<= dLimit))
{
pDescriptors =
SpyDescriptorBase (gdt.pDescriptors +
ldt.Index);
}
}
}
else // gdt descriptor
{
if (pSelector->Index)
{
__asm
{
sgdt gdt.wLimit
}
if ((pSelector->wValue & X86_SELECTOR_INDEX)
<= gdt.wLimit)
{
pDescriptors = gdt.pDescriptors;
}
}
}
}
if (pDescriptors != NULL)
{
RtlCopyMemory (pDescriptor,
pDescriptors + pSelector->Index,
X86_DESCRIPTOR_);
fOk = TRUE;
}
else
{
RtlZeroMemory (pDescriptor,
X86_DESCRIPTOR_);
}
}
return fOk;
}
// -----------------------------------------------------------------
PVOID SpyDescriptorBase (PX86_DESCRIPTOR pDescriptor)
{
return (PVOID) ((pDescriptor->Base1 ) |
(pDescriptor->Base2 << 16) |
(pDescriptor->Base3 << 24));
}
// -----------------------------------------------------------------
DWORD SpyDescriptorLimit (PX86_DESCRIPTOR pDescriptor)
{
return (pDescriptor->G ? (pDescriptor->Limit1 << 12) |
(pDescriptor->Limit2 << 28) | 0xFFF
: (pDescriptor->Limit1 ) |
(pDescriptor->Limit2 << 16));
}
// -----------------------------------------------------------------
DWORD SpyDescriptorType (PX86_DESCRIPTOR pDescriptor,
PBOOL pfSystem)
{
if (pfSystem != NULL) *pfSystem = !pDescriptor->S;
return pDescriptor->Type;
}
列表 4-17. 获取描述符的值
如果选择器的 TI 位指定了一个 GDT 描述符,事情就简单了。再次使用 SGDT 指令来取出 GDT 在线性内存中的位置和大小,如果选择器指定的描述符索引位于适当的范围, pDescriptors 变量将被设置为指向 GDT 的基地址。对于 LDT 和 GDT 来说, pDescriptors 变量都不会为空。如果调用者传入的选择器是有效的, 64 位的描述符值将被复制到调用者提供的 X86_DESCRIPTOR 结构中。否则,该结构的所有成员都会被 RtlZeroMemory() 设为 0 。
我们仍然在讨论 列表 4-15 中的 SpySegment() 函数。 SpySelector() 和 SpyDescriptor() 调用已经解释了。只剩下最后的 SpyDescriptorBase() 和 SpyDescriptorLimit() 调用,不过你应该已经知道这些函数作了些什么(见 列表 4-17 )。如果 SpySelector() 和 SpyDescriptor() 成功,返回的 SPY_SEGMENT 结构将是有效的。 SpyDescriptorBase() 和 SpyDescriptorLimit() 不会返回出错标志。因为它们不可能失败,如果提供的描述符无效,只是会让它们返回错误的数据而已。 上一页 [1] [2] [3] |