热门搜索:Photoshop 平面设计 Linux Vista Windows ASP.NET qq word 病毒 XP Excel 标志设计 

《Undocumented Windows 2000 Secrets》翻译 --- 第四章(4)

来源:ccxunmeng(读取中...) 2005-8-3 【字体: 】 切换为
尽管 Spy 设备使用可缓冲的 I/O ,但它还是会检查输入 / 输出缓冲区的有效性。因为客户端程序传入的数据可能比所需的少或者提供的缓冲区不够容纳输出数据。系统不能捕获这些语意错误,因为它不知道在一次 IOCTL 传输中所传输的数据的类型。

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] 

关注此文的读者还看过:
    用户评论
评论内容:不能超过100字,需审核,请自觉遵守互联网相关政策法规。
发表评论: 匿名发表 用户名: loading 位网友发表了评论 查看评论
(0/100)