Undocumented 2000 notes

2win 2000 native api

ntoskrnl.exe中将传入的eax作为索引来查询一张表,系统服务表SSD,对应的C定义如下:

typedef NTSTATUS (NTAPI*NTPROC)();

typedef NTPROC* PNTPROC;

#define NTPROC_ sizeof(NTPROC)

typdef struct_SYSTEM_SERVICE_TABLE

{

     PNTPROCServiceTable; // array of entry points

PDOWRD CounterTable; // arrayof usage counters,在debug版本中指向一个数组,

//函数调用计数器

DWORD ServiceLimit; // numberof table entries 2000中默认为248, 2003中为0x135

PBYTEArgumentTable; // array of byte counts,与ServiceTable对应,是所需参数//的字节数

} SYSTEM_SERVICE_TABLE,

*PSYSTEM_SERVICE_TABLE,

**PPSYSTEM_SERVICE_TABLE;

//-----------------------------------------------------------------------------------------------

typedef struct _SERVICE_DESCRIPTOR_TABLE

{

SYSTEM_SERVICE_TABLE ntoskrnl; // ntoskrnl.exe ( native api ),ntoskrnl.exe导出//KeServiceDescriptorTable指向这个表

SYSTEM_SERVICE_TABLE win32k;// win32k.sys (gdi/user support) ,gdi32.dll

// 和user32.dll都通过这个表分派

SYSTEM_SERVICE_TABLE Table3;// not used

SYSTEM_SERVICE_TABLE Table4;// not used

}

SYSTEM_DESCRIPTOR_TABLE,

*PSYSTEM_DESCRIPTOR_TABLE,

**PPSYSTEM_DESCRIPTOR_TABLE;

 

int 2eh的中断处理例程是KiSystemService()ntoskrnl.exe并没有把这个符号导出。原理大致如下:

1.  从线程控制块中获得SDT指针。

每个线程拥有不同的SDT,线程初始化时KeInitializeThread()会将KeServiceDescriptorTable写入线程控制块,不过该值后来可能会改变,例如指向KeServiceDescriptorTableShadow

2.  检测eax中分派id的第1214位来确定使用SDT中的哪个SST,如果id位于0x0000-0x0fff,选择ntoskrnl表,如果位于0x1000-0x1fff,则选择win32k表,0x2000-0x2fff0x30000x3fffTable3Table4保留,如果超过0x3fff,则把前面的位屏蔽掉。

3.  通过分派id011位的值来确定该id在所选SST中对应的ServiceLimit的值,如果id超出了范围将返回错误代码。

4.  通过检测edx中的参数堆栈指针来获得MmUserProbAddress的值。

5.  根据SST中的ArgumentTable中查到的参数字节数,将参数从调用者的堆栈中拷贝到内核堆栈中。

6.  返回后将控制权传递给内部的KiSystemExit().

 

 

两种类型的UNICODE_STRING(独立式和内嵌式)

 

OBJECT_ATTRIBUTES结构定义:

typedef struct _OBJECT_ATTRIBUTES

{

ULONG Length;

HANDLE RootDirectory;

PUNICODE_STRING ObjectName;

ULONG Attributes;

PVOID SecurityDescriptor;

PVOID SecurityQualityOfService;

} OBJECT_ATTRIBUTES,*POBJECT_ATTRIBUTES;

 

IO_STATUS_BLOCK结构:

  用于用户提交操作的处理结果

typedef struct _IO_STRATUS_BLOCK

{

NTSTATUS Status;

ULONG Information;

} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

 

 

执行体用CLIENT_ID在全局范围内来标识唯一的线程。定义如下:

typedef struct _CLIENT_ID

{

HANDLE UniqueProcess;

HANDLE UniqueThread;

} CLIENT_ID, *PCLIENT_ID;

 

 

3.编写内核驱动

#pragma alloc_text(INIT, DriverEntry)使得DriverEntry代码保存在一个新的section--INIT,驱动加载器可以识别这种指定的section,并在初始化之后丢掉该section

 

Win2000可以使用SCM来在运行时加载或卸载驱动程序。

 

4Win2000内存管理机制

PHYSICAL_ADDRESS代表物理地址,是LARGE_INTEGER64位)的别名。

 

小页面(4K)的时候,页目录基址寄存器PDBR包含页目录的物理地址,在i386中由cr3寄存器保存,仅用高20位来寻址。页目录占一页,由102432位的页目录项PDE组成,PDE又被分为20字节的页帧号PFN和一个标志数组,PFN用来寻址页表,每个页表也是占一页,由102432位的页表项PTE组成,每个PTE的高20位指向4KB的一个页。

 

线性地址分三部分来实现地址转换:高10位选择一个PDE,中间10位选择PDE中的PTE,最后12位来确定数据在页中的偏移量。

 

段寄存器是16位的,但段描述符是64位的,解决办法是把段描述符顺序放到内存中的某个位置形成段描述符表,而段寄存器中的16位作为索引信息,指定这个段的属性由段描述符中的第几个描述符来表示。这时的段就是段选择器(选择子)。80386中引入两个新寄存器来管理段描述符表,一个是48位的GDTR,另一个是16位的LDTR

 

实际上,每个任务的局部描述符表LDT作为系统的一个特殊段,由一个描述符描述。而用于描述符LDT的描述符存放在GDT中。在初始化或任务切换过程中,把描述符对应任务LDT的描述符的选择子装入LDTR,处理器根据装入LDTR可见部分的选择子,从GDT中取出对应的描述符,并把LDT的基地址、界限和属性等信息保存到LDTR的不可见的高速缓冲寄存器中。随后对LDT的访问,就可根据保存在高速缓冲寄存器中的有关信息进行合法性检查。

LDTR寄存器包含当前任务的LDT的选择子。所以,装入到LDTR的选择子必须确定一个位于GDT中的类型为LDT的系统段描述符,也即选择子中的TI位必须是0,而且描述符中的类型字段所表示的类型必须为LDT。

可以用一个空选择子装入LDTR,这表示当前任务没有LDT。在这种情况下,所有装入到段寄存器的选择子都必须指示GDT中的描述符,也即当前任务涉及的段均由GDT中的描述符来描述。如果再把一个TI位为1的选择子装入到段寄存器,将引起异常。

   

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值