现在的位置: 首页 > 综合 > 正文

利用调用门从Ring 3进入Ring 0[转]

2013年08月23日 ⁄ 综合 ⁄ 共 39494字 ⁄ 字号 评论关闭
☆ 利用调用门从Ring 3进入Ring 0

观察用户空间程序memdump.exe执行时CS、DS、FS所对应的段描述符:

--------------------------------------------------------------------------
> memdump -l 8
GDTR.base             = 0x8003F000
GDTR.limit            = 0x03FF
CURRENT_CS            = 0x001B
CURRENT_DS            = 0x0023
CURRENT_ES            = 0x0023
CURRENT_SS            = 0x0023
CURRENT_FS            = 0x0038
PageSize              = 0x00001000
AllocationGranularity = 0x00010000
byteArray [ 8 bytes ] ->
00000000  00 00 00 00 00 00 00 00                            ........

> memdump -l 8 -a 0x8003F018
PageSize              = 0x00001000
AllocationGranularity = 0x00010000
byteArray [ 8 bytes ] ->
00000000  FF FF 00 00 00 FB CF 00                            ........

> memdump -l 8 -a 0x8003F020
PageSize              = 0x00001000
AllocationGranularity = 0x00010000
byteArray [ 8 bytes ] ->
00000000  FF FF 00 00 00 F3 CF 00                            ........

> memdump -l 8 -a 0x8003F038
PageSize              = 0x00001000
AllocationGranularity = 0x00010000
byteArray [ 8 bytes ] ->
00000000  FF 0F 00 E0 FD F3 40 7F                            ......@.
--------------------------------------------------------------------------

代码段、数据段描述符的DPL都是3,均覆盖整个4G空间。选择子等于0x001B,并非对
应第0x001B个(从0计)描述符,有时候容易忘了这茬,其对应的描述符所在线性地址
为:

GDTR.base + ( CURRENT_CS & 0xfff8 ) -> 0x8003F018

再比如FS对应的是GDT中第7个描述符:

0x0038 >> 3 -> 0x0007

可能有人奇怪了,数据段描述符覆盖整个4G空间并且DPL等于3,那岂非任一用户空间
程序都可读写访问整个4G空间,包括高2G的内核空间。微软没这么傻,事实上除段级
保护外,IA-32还同时提供页级保护,Windows内核启用了分页机制,其页级保护将阻
止Ring 3代码访问内核空间。页级保护只对Ring 3代码有意义,对Ring 2、1、0代码
没有任何意义,后者对页级保护来说统称为系统特权级,而前者称为用户特权级。系
统特权级代码对任意页拥有读、写、执行权限。

本小节的目标很简单,在不写驱动程序的情况下读访问任意线性地址,而不是局限在
[0x80000000, 0xa0000000)区间上。为达目标,现在有两条路,一是修改页级保护,
二是让自己的代码拥有系统特权级。页目录、页表在0xc0300000、0xc0000000,就前
几节所演示的技术而言,没法简单修改页级保护,就算有办法,也太过麻烦并且冒很
大风险。事实上我们只有一条路,让自己的代码拥有Ring 0权限。

crazylord演示了一种技术([3])。他利用/Device/PhysicalMemory在GDT中搜索P位为
0的空闲描述符,然后将这个空闲描述符设置成DPL等于3的调用门,用户空间Ring 3
代码通过这个调用门获取Ring 0权限,于是内核空间完全暴露在我们面前。

前面有一节中已经看到内核中代码段选择子等于0x0008,其对应的描述符如下:

--------------------------------------------------------------------------
> memdump -l 8 -a 0x8003F008
PageSize              = 0x00001000
AllocationGranularity = 0x00010000
byteArray [ 8 bytes ] ->
00000000  FF FF 00 00 00 9B CF 00                            ........
--------------------------------------------------------------------------

与0x8003F018相比,仅仅是DPL不同,0x8003F008的DPL是0。Windows内核采用基本平
坦模式,一个函数在这两种代码段选择子下对应一样的段内偏移,这样就很容易设置
调用门。

如果调用门导致向内层特权级跃迁,必然发生堆栈切换。压栈的形参将从Ring 3的堆
栈复制到Ring 0的堆栈中,并且CS随EIP一起压栈,我们不能依赖编译器处理形参。

dump.c演示了如何在用户空间编程中调用内核函数nt!MmGetPhysicalAddress。因为
现在进入Ring 0已不成问题,如果能获取nt!MmGetPhysicalAddress的线性地址就搞
定。crazylord对LoadLibrary()理解有问题,他在Phrack Magazine 59-0x10([3])中
这部分代码是错误的,后来为了让他的代码跑起来,他硬性定义了一个ntoskrnl.exe
基址:

/*
* default base address for ntoskrnl.exe on win2k
*
#define BASEADD 0x7FFE0000

ntoskrnl.exe的基址不可能低于0x80000000。dump.c中LocateNtoskrnlEntry()才是
正确的实现。

顺带在这里验证线性地址0xc0300000的确指向页目录,办法就是先取CR3的物理地址,
然后调用nt!MmGetPhysicalAddress( 0xc0300000 ),看返回的物理地址是否与CR3一
致。

h0ck0r@smth可能是要搞破解吧,他折腾过将内核中的驱动dump出来。当时提问如何
访问[0x80000000, 0xa0000000)以外的内核空间线性地址。后来他的实现就是直接在
Ring 0代码中访问这些线性地址。我当时想绕了,先调用nt!MmGetPhysicalAddress
再利用/Device/PhysicalMemory,事实上h0ck0r@smth的办法更直接。dump.c演示了
他的办法。

dump.c没有考虑太多边界情形,可能会导致不可预知的后果,比如[-a Address]指定
0,结果dump.exe在Ring 0中立即终止,没有回到Ring 3来,于是所征用的空闲描述
符得不到释放,此时我用kd手工释放,最简单的办法将相应描述符的第6字节(从1计)
清为0x00即可。

演示程序是为Unix程序员小闹Windows而写,注释冗长可以理解,Windows程序员勿怪。
Unix程序员如无IA-32基础,万勿执行dump.exe!

--------------------------------------------------------------------------
/*
* For x86/EWindows XP SP1 & VC 7
* cl dump.c /Os /G6 /W3 /Fadump.asm
*
* Usage: dump [-h] [-g Gdtrbase] [-a Address] [-l Length]
*/

/*
* 名为dump.c,实则与dump非紧藕合,dump功能仅为其中一种演示而已。
*
* 该程序仅为演示用途,其潜在的危险由使用者本人承担,否则请勿执行之。
*
* 由于参考太多源代码,我不是太清楚该将哪些作者的名字列于此处:
*
* crazylord <crazylord@minithins.net>
* Gary Nebbett
* h0ck0r@smth
* Mark E. Russinovich
* tsu00 <tsu00@263.net>
*
* 这是此番学习笔记中惟一列举源作者的C程序。总之,该程序与我没有太大关系,
* 就不贪天功为己有了,顺带少些风险,上场当念下场时。
*/

/************************************************************************
*                                                                      *
*                               Head File                              *
*                                                                      *
************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <windows.h>
#include <aclapi.h>
#include <memory.h>

/************************************************************************
*                                                                      *
*                               Macro                                  *
*                                                                      *
************************************************************************/

#pragma comment( linker, "/subsystem:console" )
#pragma comment( lib,    "advapi32.lib"       )

typedef LONG NTSTATUS;

#define NT_SUCCESS(status)      ((NTSTATUS)(status)>=0)

#define RING0_CODE_SELECTOR     ((unsigned short int)0x0008)

/*
*************************************************************************
* ntdef.h
*/
typedef struct _UNICODE_STRING
{
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

/*
* Valid values for the Attributes field
*/
#define OBJ_INHERIT             0x00000002L
#define OBJ_PERMANENT           0x00000010L
#define OBJ_EXCLUSIVE           0x00000020L
#define OBJ_CASE_INSENSITIVE    0x00000040L
#define OBJ_OPENIF              0x00000080L
#define OBJ_OPENLINK            0x00000100L
#define OBJ_KERNEL_HANDLE       0x00000200L
#define OBJ_FORCE_ACCESS_CHECK  0x00000400L
#define OBJ_VALID_ATTRIBUTES    0x000007F2L

typedef struct _OBJECT_ATTRIBUTES
{
    ULONG           Length;
    HANDLE          RootDirectory;
    PUNICODE_STRING ObjectName;
    ULONG           Attributes;
    PVOID           SecurityDescriptor;
    PVOID           SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;

typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS;
/*
* ntdef.h
*************************************************************************
*/

/*
*************************************************************************
* <<Windows NT/2000 Native API Reference>> by Gary Nebbett
*/
typedef enum _SECTION_INHERIT
{
    ViewShare = 1,
    ViewUnmap = 2
} SECTION_INHERIT;

/*
* 虽然本程序用不到这么多枚举值,还是列出一份最完整的。这个程序本身不求完
* 美,尽可能多地保留一些未文档化的参考资料。
*/
typedef enum _SYSTEM_INFORMATION_CLASS     //    Q S
{
    SystemBasicInformation,                // 00 Y N
    SystemProcessorInformation,            // 01 Y N
    SystemPerformanceInformation,          // 02 Y N
    SystemTimeOfDayInformation,            // 03 Y N
    SystemNotImplemented1,                 // 04 Y N
    SystemProcessesAndThreadsInformation,  // 05 Y N
    SystemCallCounts,                      // 06 Y N
    SystemConfigurationInformation,        // 07 Y N
    SystemProcessorTimes,                  // 08 Y N
    SystemGlobalFlag,                      // 09 Y Y
    SystemNotImplemented2,                 // 10 Y N
    SystemModuleInformation,               // 11 Y N
    SystemLockInformation,                 // 12 Y N
    SystemNotImplemented3,                 // 13 Y N
    SystemNotImplemented4,                 // 14 Y N
    SystemNotImplemented5,                 // 15 Y N
    SystemHandleInformation,               // 16 Y N
    SystemObjectInformation,               // 17 Y N
    SystemPagefileInformation,             // 18 Y N
    SystemInstructionEmulationCounts,      // 19 Y N
    SystemInvalidInfoClass1,               // 20
    SystemCacheInformation,                // 21 Y Y
    SystemPoolTagInformation,              // 22 Y N
    SystemProcessorStatistics,             // 23 Y N
    SystemDpcInformation,                  // 24 Y Y
    SystemNotImplemented6,                 // 25 Y N
    SystemLoadImage,                       // 26 N Y
    SystemUnloadImage,                     // 27 N Y
    SystemTimeAdjustment,                  // 28 Y Y
    SystemNotImplemented7,                 // 29 Y N
    SystemNotImplemented8,                 // 30 Y N
    SystemNotImplemented9,                 // 31 Y N
    SystemCrashDumpInformation,            // 32 Y N
    SystemExceptionInformation,            // 33 Y N
    SystemCrashDumpStateInformation,       // 34 Y Y/N
    SystemKernelDebuggerInformation,       // 35 Y N
    SystemContextSwitchInformation,        // 36 Y N
    SystemRegistryQuotaInformation,        // 37 Y Y
    SystemLoadAndCallImage,                // 38 N Y
    SystemPrioritySeparation,              // 39 N Y
    SystemNotImplemented10,                // 40 Y N
    SystemNotImplemented11,                // 41 Y N
    SystemInvalidInfoClass2,               // 42
    SystemInvalidInfoClass3,               // 43
    SystemTimeZoneInformation,             // 44 Y N
    SystemLookasideInformation,            // 45 Y N
    SystemSetTimeSlipEvent,                // 46 N Y
    SystemCreateSession,                   // 47 N Y
    SystemDeleteSession,                   // 48 N Y
    SystemInvalidInfoClass4,               // 49
    SystemRangeStartInformation,           // 50 Y N
    SystemVerifierInformation,             // 51 Y Y
    SystemAddVerifier,                     // 52 N Y
    SystemSessionProcessesInformation      // 53 Y N
} SYSTEM_INFORMATION_CLASS;

typedef struct _SYSTEM_MODULE_INFORMATION  // Information Class 11
{
    ULONG  Reserved[2];
    PVOID  Base;
    ULONG  Size;
    ULONG  Flags;
    USHORT Index;
    USHORT Unknown;
    USHORT LoadCount;
    USHORT ModuleNameOffset;
    CHAR   ImageName[256];
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;
/*
* <<Windows NT/2000 Native API Reference>> by Gary Nebbett
*************************************************************************
*/

/*
*************************************************************************
* 参<<Intel Architecture Software Developer's Manual. Volume 3>>
*/

#pragma pack(push, 1)

/*
* 卷III的3.5.1小节,GDTR/IDTR均适用,这里假设是IA-32架构
*/
typedef struct _PSEUDODESCRIPTOR
{
    unsigned short int limit;
    unsigned int       base;
} PSEUDODESCRIPTOR;

/*
* 卷III的4.8.3小节。
*/
typedef struct _GATEDESCRIPTOR
{
    unsigned offset_low      : 16;  /* 32-bit偏移的低16位 */
    unsigned selector        : 16;  /* 段选择子           */
    unsigned parameter_count :  5;  /* 参数个数           */
    unsigned reserved        :  3;  /* 保留,总为0        */
    unsigned type            :  4;  /* 类型               */
    unsigned s               :  1;  /* 总为0,系统描述符  */
    unsigned dpl             :  2;  /* 描述符特权级DPL    */
    unsigned p               :  1;  /* 为1表示有效        */
    unsigned offset_high     : 16;  /* 32-bit偏移的高16位 */
} GATEDESCRIPTOR;

typedef struct _CALL_ARG_0
{
    unsigned int cr0;
    unsigned int cr2;
    unsigned int cr3;
    /*
     * unsigned int cr4;
     */
    unsigned int dr0;
    unsigned int dr1;
    unsigned int dr2;
    unsigned int dr3;
    unsigned int dr6;
    unsigned int dr7;
} CALL_ARG_0;

/*
* MmGetPhysicalAddress
*/
typedef struct _CALL_ARG_1
{
    PVOID            LinearAddress;
    PHYSICAL_ADDRESS PhysicalAddress;
} CALL_ARG_1;

/*
* 内存复制
*/
typedef struct _CALL_ARG_2
{
    PVOID src;
    PVOID dst;
    ULONG len;
} CALL_ARG_2;

#pragma pack(pop)

/*
* <<Intel Architecture Software Developer's Manual. Volume 3>>
*************************************************************************
*/

/*
* 参看DDK文档以及<<Windows NT/2000 Native API Reference>> by Gary Nebbett
* 这些Native API由ntdll.dll输出
*/
typedef VOID     ( __stdcall *RTLINITUNICODESTRING     ) ( IN OUT PUNICODE_STRING DestinationString, IN PCWSTR SourceString );
typedef NTSTATUS ( __stdcall *ZWOPENSECTION            ) ( OUT PHANDLE SectionHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes );
typedef NTSTATUS ( __stdcall *ZWCLOSE                  ) ( IN HANDLE Handle );
typedef NTSTATUS ( __stdcall *ZWMAPVIEWOFSECTION       ) ( IN HANDLE SectionHandle, IN HANDLE ProcessHandle, IN OUT PVOID *BaseAddress, IN ULONG ZeroBits, IN ULONG CommitSize, IN OUT PLARGE_INTEGER SectionOffset, IN OUT PULONG ViewSize, IN SECTION_INHERIT InheritDisposition, IN ULONG AllocationType, IN ULONG Protect );
typedef NTSTATUS ( __stdcall *ZWUNMAPVIEWOFSECTION     ) ( IN HANDLE ProcessHandle, IN PVOID BaseAddress );
typedef NTSTATUS ( __stdcall *ZWQUERYSYSTEMINFORMATION ) ( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, IN OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength OPTIONAL );
typedef ULONG    ( __stdcall *RTLNTSTATUSTODOSERROR    ) ( IN NTSTATUS Status );

/*
* 参看ntddk.h以及Phrack Magazine 59-0x10,这些Kernel API由ntoskrnl.exe输
* 出。
*/
typedef PHYSICAL_ADDRESS ( *MMGETPHYSICALADDRESS ) ( IN PVOID BaseAddress );

/************************************************************************
*                                                                      *
*                            Function Prototype                        *
*                                                                      *
************************************************************************/

static VOID    ExecuteRing0Code   ( PVOID Ring0Code,
                                    ULONG Ring0CodeLength,
                                    unsigned short int selector,
                                    unsigned int call_type,
                                    void *call_arg );
static VOID    InitializeObjectAttributes
                                  (
                                    OUT POBJECT_ATTRIBUTES InitializedAttributes,
                                    IN PUNICODE_STRING ObjectName,
                                    IN ULONG Attributes,
                                    IN HANDLE RootDirectory,
                                    IN PSECURITY_DESCRIPTOR SecurityDescriptor
                                  );
static GATEDESCRIPTOR *
               InstallCallgate    ( ULONG Gdtrbase, ULONG Gdtrlimit,
                                    DWORD CodeOffset,
                                    GATEDESCRIPTOR *orig_callgate );
static BOOLEAN LocateNtdllEntry   ( void );
static BOOLEAN LocateNtoskrnlEntry
                                  ( void );
static BOOLEAN MapPhysicalMemory  (
                                    IN     HANDLE          SectionHandle,
                                    IN OUT PVOID          *LinearAddress,
                                    IN OUT PULONG          MapSize,
                                    IN OUT PLARGE_INTEGER  PhysicalAddress,
                                    IN     ULONG           Protect
                                  );
static HANDLE  OpenPhysicalMemory ( ACCESS_MASK DesiredAccess );
static void    outputBinary       ( FILE *out,
                                    const unsigned char *byteArray,
                                    const size_t byteArrayLen );
static void    PrintWin32Error    ( char *message, DWORD dwMessageId );
static void    PrintZwError       ( char *message, NTSTATUS status );
static PVOID   PrivateFindModule  ( const char *ModuleName );
static PHYSICAL_ADDRESS
               PrivateMmGetPhysicalAddress
                                  ( IN PVOID LinearAddress );
/*
* 不能定义Ring0Code()函数原型
*/
static BOOLEAN SetPhysicalMemoryDACLs
                                  ( HANDLE handle, LPTSTR ptstrName );
static VOID    UnmapPhysicalMemory
                                  ( IN PVOID LinearAddress );
static void    usage              ( char *arg );

/************************************************************************
*                                                                      *
*                            Static Global Var                         *
*                                                                      *
************************************************************************/

/*
* 由ntdll.dll输出的Native API函数指针
*/
static RTLINITUNICODESTRING     RtlInitUnicodeString     = NULL;
static ZWOPENSECTION            ZwOpenSection            = NULL;
static ZWCLOSE                  ZwClose                  = NULL;
static ZWMAPVIEWOFSECTION       ZwMapViewOfSection       = NULL;
static ZWUNMAPVIEWOFSECTION     ZwUnmapViewOfSection     = NULL;
static ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = NULL;
static RTLNTSTATUSTODOSERROR    RtlNtStatusToDosError    = NULL;

/*
* 由ntoskrnl.exe输出的Kernel API函数指针
*/
static MMGETPHYSICALADDRESS     MmGetPhysicalAddress     = NULL;

static SYSTEM_INFO              system_info;

/************************************************************************/

static VOID ExecuteRing0Code ( PVOID Ring0Code,
                               ULONG Ring0CodeLength,
                               unsigned short int selector,
                               unsigned int call_type,
                               void *call_arg )
{
    unsigned short int farcall[3];
    HANDLE             Thread;

    if ( 0 == VirtualLock( Ring0Code, Ring0CodeLength ) )
    {
        PrintWin32Error( "VirtualLock() failed", GetLastError() );
    }
    else
    {
        farcall[2] = selector;
        Thread     = GetCurrentThread();
        SetThreadPriority( Thread, THREAD_PRIORITY_TIME_CRITICAL );
        Sleep( 0 );
        printf( "ExecuteRing0Code() begin/n" );
        /*
         * 实际上这种情形下的SEH(结构化异常处理)没有意义,一旦在Ring 0代码
         * 中引发异常,控制不会再回到Ring 3代码中来。也可能我对SEH的理解太
         * 浅,还有别的办法让控制返回Ring 3代码。
         */
        __try
        {
            /*
             * 形参从右至左压栈,这是C调用风格,主要便于处理形参个数不定的
             * 情况。虽然我这里只有两个形参,还是用了C调用风格,谁叫咱是标
             * 准C程序员。
             *
             * 堆栈由Ring0Code中的"retf 8"负责平衡,这是最快的办法。
             */
            __asm
            {
                push call_arg
                push call_type
                call fword ptr [farcall]
            }
        }
        __except ( EXCEPTION_EXECUTE_HANDLER )
        {
            fprintf( stderr, "ExecuteRing0Code() failed/n" );
        }
        printf( "ExecuteRing0Code() end/n" );
        SetThreadPriority( Thread, THREAD_PRIORITY_NORMAL );
        VirtualUnlock( Ring0Code, Ring0CodeLength );
    }
    return;
}  /* end of ExecuteRing0Code */

/*
* 在DDK的ntdef.h中InitializeObjectAttributes()是一个宏
*/
static VOID InitializeObjectAttributes ( OUT POBJECT_ATTRIBUTES InitializedAttributes,
                                         IN PUNICODE_STRING ObjectName,
                                         IN ULONG Attributes,
                                         IN HANDLE RootDirectory,
                                         IN PSECURITY_DESCRIPTOR SecurityDescriptor
                                       )
{
    InitializedAttributes->Length                   = sizeof( OBJECT_ATTRIBUTES );
    InitializedAttributes->RootDirectory            = RootDirectory;
    InitializedAttributes->Attributes               = Attributes;
    InitializedAttributes->ObjectName               = ObjectName;
    InitializedAttributes->SecurityDescriptor       = SecurityDescriptor;
    InitializedAttributes->SecurityQualityOfService = NULL;
    return;
}  /* end of InitializeObjectAttributes */

static GATEDESCRIPTOR * InstallCallgate ( ULONG Gdtrbase, ULONG Gdtrlimit, DWORD CodeOffset,
                                          GATEDESCRIPTOR *orig_callgate )
{
    /*
     * callgate指向GDT中最后一个描述符
     */
    GATEDESCRIPTOR *callgate = ( GATEDESCRIPTOR * )( Gdtrbase + ( Gdtrlimit & 0xfffffff8 ) );

    /*
     * 搜索空闲描述符
     */
    for ( ; ( ULONG )callgate > Gdtrbase; callgate-- )
    {
        /*
         * GDT中不一定都是门描述符,但所有类型的描述符P位在同一位域,并且
         * 意义接近。
         */
        if ( 0 == callgate->p )
        {
            /*
             * 备份即将被征用的"空闲"描述符
             */
            memcpy( orig_callgate, callgate, sizeof( GATEDESCRIPTOR ) );
            /*
             * 设置成DPL等于3的调用门
             */
            callgate->offset_low      = ( WORD )( CodeOffset & 0x0000ffff );
            callgate->selector        = RING0_CODE_SELECTOR;
            callgate->parameter_count = 2;
            callgate->reserved        = 0;
            callgate->type            = 12;
            callgate->s               = 0;
            callgate->dpl             = 3;
            callgate->p               = 1;
            callgate->offset_high     = ( WORD )( CodeOffset >> 16 );
            return( callgate );
        }
    }
    return( NULL );
}  /* end of InstallCallgate */

/*
* ntdll.dll输出了所有的Native API
*/
static BOOLEAN LocateNtdllEntry ( void )
{
    BOOLEAN boolean_ret = FALSE;
    char    NTDLL_DLL[] = "ntdll.dll";
    HMODULE ntdll_dll   = NULL;

    /*
     * returns a handle to a mapped module without incrementing its
     * reference count
     */
    if ( ( ntdll_dll = GetModuleHandle( NTDLL_DLL ) ) == NULL )
    {
        PrintWin32Error( "GetModuleHandle() failed", GetLastError() );
        return( FALSE );
    }
    if ( !( RtlInitUnicodeString = ( RTLINITUNICODESTRING )GetProcAddress( ntdll_dll,
         "RtlInitUnicodeString" ) ) )
    {
        goto LocateNtdllEntry_return;
    }
    if ( !( ZwOpenSection = ( ZWOPENSECTION )GetProcAddress( ntdll_dll,
         "ZwOpenSection" ) ) )
    {
        goto LocateNtdllEntry_return;
    }
    if ( !( ZwClose = ( ZWCLOSE )GetProcAddress( ntdll_dll,
         "ZwClose" ) ) )
    {
        goto LocateNtdllEntry_return;
    }
    if ( !( ZwMapViewOfSection = ( ZWMAPVIEWOFSECTION )GetProcAddress( ntdll_dll,
         "ZwMapViewOfSection" ) ) )
    {
        goto LocateNtdllEntry_return;
    }
    if ( !( ZwUnmapViewOfSection = ( ZWUNMAPVIEWOFSECTION )GetProcAddress( ntdll_dll,
         "ZwUnmapViewOfSection" ) ) )
    {
        goto LocateNtdllEntry_return;
    }
    if ( !( ZwQuerySystemInformation = ( ZWQUERYSYSTEMINFORMATION )GetProcAddress( ntdll_dll,
         "ZwQuerySystemInformation" ) ) )
    {
        goto LocateNtdllEntry_return;
    }
    if ( !( RtlNtStatusToDosError = ( RTLNTSTATUSTODOSERROR )GetProcAddress( ntdll_dll,
         "RtlNtStatusToDosError" ) ) )
    {
        goto LocateNtdllEntry_return;
    }
    boolean_ret = TRUE;

LocateNtdllEntry_return:

    if ( FALSE == boolean_ret )
    {
        PrintWin32Error( "GetProcAddress() failed", GetLastError() );
    }
    ntdll_dll = NULL;
    return( boolean_ret );
}  /* end of LocateNtdllEntry */

/*
* ntoskrnl.exe输出Kernel API
*/
static BOOLEAN LocateNtoskrnlEntry ( void )
{
    BOOLEAN        boolean_ret    = TRUE;
    char           NTOSKRNL_EXE[] = "ntoskrnl.exe";
    HMODULE        ntoskrnl_exe   = NULL;
    unsigned char *Base           = NULL;

    Base = ( unsigned char * )PrivateFindModule( ( const char * )NTOSKRNL_EXE );
    if ( NULL == Base )
    {
        fprintf( stderr, "PrivateFindModule() failed/n" );
        boolean_ret = FALSE;
        goto LocateNtoskrnlEntry_return;
    }
    /*
     * Header : Declared in Winbase.h; include Windows.h
     * Library: Use Kernel32.lib
     *
     * HMODULE LoadLibrary
     * (
     *     LPCTSTR lpFileName
     * );
     */
    if ( NULL == ( ntoskrnl_exe = LoadLibrary( NTOSKRNL_EXE ) ) )
    {
        PrintWin32Error( "LoadLibrary() failed", GetLastError() );
        boolean_ret = FALSE;
        goto LocateNtoskrnlEntry_return;
    }
    if ( !( MmGetPhysicalAddress = ( MMGETPHYSICALADDRESS )GetProcAddress( ntoskrnl_exe,
         "MmGetPhysicalAddress" ) ) )
    {
        boolean_ret = FALSE;
        goto LocateNtoskrnlEntry_return;
    }
    MmGetPhysicalAddress = ( MMGETPHYSICALADDRESS )
                           ( Base + ( unsigned int )
                             ( ( unsigned char * )MmGetPhysicalAddress -
                               ( unsigned char * )ntoskrnl_exe
                             )
                           );
    printf( "ntoskrnl.exe base     = 0x%08X/n"
            "MmGetPhysicalAddress  = 0x%08X/n", Base, MmGetPhysicalAddress );

LocateNtoskrnlEntry_return:

    if ( ntoskrnl_exe != NULL )
    {
        FreeLibrary( ntoskrnl_exe );
        ntoskrnl_exe = NULL;
    }
    return( boolean_ret );
}  /* end of LocateNtoskrnlEntry */

static BOOLEAN MapPhysicalMemory ( IN     HANDLE          SectionHandle,
                                   IN OUT PVOID          *LinearAddress,
                                   IN OUT PULONG          MapSize,
                                   IN OUT PLARGE_INTEGER  PhysicalAddress,
                                   IN     ULONG           Protect
                                 )
{
    NTSTATUS status;
    char     error_msg[256];
    ULONG    mapsize = *MapSize;
    DWORD    LowPart = PhysicalAddress->LowPart;

    *LinearAddress  = NULL;
#if 0
    /*
     * 假设GDTR的物理基址在0x0003F000,而虚拟内存分配粒度是64KB,向下舍入
     * 后得到0x00030000,ZwMapViewOfSection()会报告"试图访问无效的地址"。
     */
    LowPart        %= system_info.dwAllocationGranularity;
    if ( LowPart != 0 )
    {
        /*
         * 向下舍入到dwAllocationGranularity(内存分配粒度)的边界
         */
        PhysicalAddress->LowPart -= LowPart;
        mapsize                  += LowPart;
        *MapSize                  = mapsize;
    }
#endif
    /*
     * 按照DDK文档的意思,物理地址要向下舍入到内存分配粒度的边界。按我的理
     * 解,这里的(物理)内存分配粒度应该是一页,而GetSystemInfo()返回的是虚
     * 拟内存分配粒度(64KB)。可能这个理解有问题,暂时先搁置一下。
     */
    LowPart        %= system_info.dwPageSize;
    if ( LowPart != 0 )
    {
        /*
         * 向下舍入到dwPageSize(页大小)的边界(4096)
         */
        PhysicalAddress->LowPart -= LowPart;
        mapsize                  += LowPart;
        *MapSize                  = mapsize;
    }
    mapsize        %= system_info.dwPageSize;
    if ( mapsize != 0 )
    {
        /*
         * 向上舍入到dwPageSize(页大小)的边界
         */
        *MapSize += system_info.dwPageSize - mapsize;
    }
    /*
     * DDK文档里有详细介绍。
     *
     * ZwMapViewOfSection
     * (
     *     IN     HANDLE           SectionHandle,
     *     IN     HANDLE           ProcessHandle,
     *     IN OUT PVOID           *BaseAddress,
     *     IN     ULONG            ZeroBits,
     *     IN     ULONG            CommitSize,
     *     IN OUT PLARGE_INTEGER   SectionOffset,
     *     IN OUT PULONG           ViewSize,
     *     IN     SECTION_INHERIT  InheritDisposition,
     *     IN     ULONG            AllocationType,
     *     IN     ULONG            Protect
     * );
     *
     * BaseAddress
     *
     *     目标映射到进程空间后的线性基址,初始化成NULL则由操作系统任意安
     *     排映射后的线性基址。Unix程序员可与mmap()的第一形参做个比较。
     *
     * CommitSize
     *
     *     以字节为单位,向上舍入到dwPageSize(页大小)的边界。
     *
     * SectionOffset
     *
     *     对我们这个程序来说,就是物理地址,向下舍入到
     *     dwAllocationGranularity(内存分配粒度)的边界。
     *
     * ViewSize
     *
     *     可以简单等同于CommitSize,向上舍入到dwPageSize(页大小)的边界。
     *
     * InheritDisposition
     *
     *     指明子进程如何继承该映射。
     *
     * Protect
     *
     *     指明访问权限,比如PAGE_READONLY、PAGE_READWRITE。
     */
    status = ZwMapViewOfSection
            (
                SectionHandle,
                ( HANDLE )-1,
                LinearAddress,
                0,
                *MapSize,
                PhysicalAddress,
                MapSize,
                ViewShare,
                0,
                Protect
            );
    if ( !NT_SUCCESS( status ) )
    {
        sprintf( error_msg, "Could not map 0x%08X bytes PhysicalMemory from 0x%08X",
                 *MapSize, PhysicalAddress->LowPart );
        PrintZwError( error_msg, status );
        return( FALSE );
    }
    return( TRUE );
}  /* end of MapPhysicalMemory */

static HANDLE OpenPhysicalMemory ( ACCESS_MASK DesiredAccess )
{
    OBJECT_ATTRIBUTES attributes;
    HANDLE            mem;
    UNICODE_STRING    memString;
    WCHAR             memName[] = L"//Device//PhysicalMemory";
    NTSTATUS          status;

    RtlInitUnicodeString( &memString, memName );
    InitializeObjectAttributes( &attributes, &memString,
                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                                NULL, NULL );
    status = ZwOpenSection( &mem, DesiredAccess, &attributes );
    if ( !NT_SUCCESS( status ) )
    {
        PrintZwError( "Could not open //Device//PhysicalMemory", status );
        return( NULL );
    }
    return( mem );
}  /* end of OpenPhysicalMemory */

/*
* 这是一个偏向Unix编程风格的函数。跟VI与EMACS是两种哲学流派一样,我不习惯
* 也不喜欢匈牙利命名法。更深层次的原因是我非Windows程序员,见谅,:-)
*/
static void outputBinary ( FILE *out, const unsigned char *byteArray, const size_t byteArrayLen )
{
    size_t offset, k, j, i;

    fprintf( out, "byteArray [ %u bytes ] -> /n", byteArrayLen );
    if ( byteArrayLen <= 0 )
    {
        return;
    }
    i      = 0;
    offset = 0;
    for ( k = byteArrayLen / 16; k > 0; k--, offset += 16 )
    {
        fprintf( out, "%08X ", offset );
        for ( j = 0; j < 16; j++, i++ )
        {
            if ( j == 8 )
            {
                fprintf( out, "-%02X", byteArray[i] );
            }
            else
            {
                fprintf( out, " %02X", byteArray[i] );
            }
        }
        fprintf( out, "    " );
        i -= 16;
        for ( j = 0; j < 16; j++, i++ )
        {
            /*
             * if ( isprint( (int)byteArray[i] ) )
             */
#if 0
            if ( ( byteArray[i] >= ' ' ) && ( byteArray[i] <= 255 )
                 && ( byteArray[i] != 0x7f ) )
#endif
            if ( ( byteArray[i] >= ' ' ) && ( byteArray[i] < 0x7f ) )
            {
                fprintf( out, "%c", byteArray[i] );
            }
            else
            {
                fprintf( out, "." );
            }
        }
        fprintf( out, "/n" );
    }  /* end of for */
    k = byteArrayLen - i;
    if ( k <= 0 )
    {
        return;
    }
    fprintf( out, "%08X ", offset );
    for ( j = 0 ; j < k; j++, i++ )
    {
        if ( j == 8 )
        {
            fprintf( out, "-%02X", byteArray[i] );
        }
        else
        {
            fprintf( out, " %02X", byteArray[i] );
        }
    }
    i -= k;
    for ( j = 16 - k; j > 0; j-- )
    {
        fprintf( out, "   " );
    }
    fprintf( out, "    " );
    for ( j = 0; j < k; j++, i++ )
    {
#if 0
        if ( ( byteArray[i] >= ' ' ) && ( byteArray[i] <= 255 )
             && ( byteArray[i] != 0x7f ) )
#endif
        if ( ( byteArray[i] >= ' ' ) && ( byteArray[i] < 0x7f ) )
        {
            fprintf( out, "%c", byteArray[i] );
        }
        else
        {
            fprintf( out, "." );
        }
    }
    fprintf( out, "/n" );
    return;
}  /* end of outputBinary */

static void PrintWin32Error ( char *message, DWORD dwMessageId )
{
    char *errMsg;

    FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
                   dwMessageId,
                   MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
                   ( LPTSTR )&errMsg, 0, NULL );
    fprintf( stderr, "%s: %s", message, errMsg );
    LocalFree( errMsg );
    return;
}  /* end of PrintWin32Error */

static void PrintZwError ( char *message, NTSTATUS status )
{
    char *errMsg;

    FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
                   RtlNtStatusToDosError( status ),
                   MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
                   ( LPTSTR )&errMsg, 0, NULL );
    fprintf( stderr, "%s: %s", message, errMsg );
    LocalFree( errMsg );
    return;
}  /* end of PrintZwError */

static PVOID PrivateFindModule ( const char *ModuleName )
{
    NTSTATUS                    status;
    PSYSTEM_MODULE_INFORMATION  module = NULL;
    PVOID                       Base   = NULL;
    ULONG                       n      = 0;
    ULONG                       i      = 0;
    void                       *buf    = NULL;

    ZwQuerySystemInformation( SystemModuleInformation, &n, 0, &n );
    if ( NULL == ( buf = calloc( ( size_t )n, 1 ) ) )
    {
        fprintf( stderr, "calloc() failed/n" );
        goto PrivateFindModule_return;
    }
    /*
     * <<Windows NT/2000 Native API Reference>> by Gary Nebbett所给的例子
     * 1.3以及A.2对第二、三形参理解有误,下面才是正确的用法。
     */
    status = ZwQuerySystemInformation( SystemModuleInformation, buf, n, NULL );
    if ( !NT_SUCCESS( status ) )
    {
        PrintZwError( "ZwQuerySystemInformation() failed", status );
        goto PrivateFindModule_return;
    }
    module = ( PSYSTEM_MODULE_INFORMATION )( ( PULONG )buf + 1 );
    n      = *( ( PULONG )buf );
    for ( i = 0; i < n; i++ )
    {
        if ( 0 == _stricmp( module[i].ImageName + module[i].ModuleNameOffset, ModuleName ) )
        {
            Base = module[i].Base;
        }
    }

PrivateFindModule_return:

    if ( buf != NULL )
    {
        free( buf );
        buf = NULL;
    }
    return( Base );
}  /* end of PrivateFindModule */

/*
* 将某固定范围的线性地址转换成物理地址
*/
static PHYSICAL_ADDRESS PrivateMmGetPhysicalAddress ( IN PVOID LinearAddress )
{
    /*
     * Header: Declared in Winnt.h; include Windows.h.
     *
     * typedef union _LARGE_INTEGER
     * {
     *     struct
     *     {
     *         DWORD LowPart;
     *         LONG  HighPart;
     *     };
     *     LONGLONG QuadPart;
     * } LARGE_INTEGER, *PLARGE_INTEGER;
     */
    PHYSICAL_ADDRESS physical_address;

    physical_address.HighPart = 0;
    if ( ( ULONG )LinearAddress >= 0x80000000 &&
         ( ULONG )LinearAddress < 0xa0000000 )
    {
        physical_address.LowPart = ( DWORD )LinearAddress & 0x1fffffff;
    }
    else
    {
        physical_address.LowPart = 0;
    }
    return( physical_address );
}  /* end of PrivateMmGetPhysicalAddress */

/*
* 这种技术在VC范畴内对我而言太陌生了,好在我可以将之想像成汇编代码。由于
* 通过调用门进入,必须考虑CS被压栈、特权级改变、堆栈切换等一系列问题,其
* 具体细节可以参看MSDN以及Intel卷III。
*
* 这里写成"void Ring0Code ( void )"也可以,形参部分没有意义,也不能使用,
* CS被压栈后形参定位有变。变通的办法就是下面演示的技术,在prolog中用汇编
* 指令将形参取出并赋予局部变量,后续的C代码使用局部变量。其实这里很好理解,
* 只需按平日里用汇编语言写调用门的理解去做就是。
*
* 这段代码搭了一个"可用"的框架,允许以标准C编程技巧向内传递形参,至于这个
* call_arg如何解释,完全依赖于call_type。框架是我的主意,但下面演示的三种
* 具体操作不是我的主意,见其附近的注释。
*/
static __declspec(naked) void Ring0Code ( unsigned int unused_call_type, void *unused_call_arg )
{
    unsigned int  call_type;
    void         *call_arg;

    /*
     * prolog
     */
    __asm
    {
        push    ebp
        mov     ebp,esp
        sub     esp,__LOCAL_SIZE
        pushad
        pushfd
        mov     eax,[ebp+0xc]
        mov     call_type,eax
        mov     eax,[ebp+0x10]
        mov     call_arg,eax
    }

    /*
     * 不要在这里做涉及I/O的操作
     */
    switch ( call_type )
    {
    case 0:
        /*
         * 获取控制寄存器CRn、调试寄存器DRn的值。cr4未被支持。这是tsu00的
         * 主意。
         */
        __asm
        {
            mov     ebx,call_arg
            mov     eax,cr0
            mov     [ebx]CALL_ARG_0.cr0,eax
            mov     eax,cr2
            mov     [ebx]CALL_ARG_0.cr2,eax
            mov     eax,cr3
            mov     [ebx]CALL_ARG_0.cr3,eax
            mov     eax,dr0
            mov     [ebx]CALL_ARG_0.dr0,eax
            mov     eax,dr1
            mov     [ebx]CALL_ARG_0.dr1,eax
            mov     eax,dr2
            mov     [ebx]CALL_ARG_0.dr2,eax
            mov     eax,dr3
            mov     [ebx]CALL_ARG_0.dr3,eax
            mov     eax,dr6
            mov     [ebx]CALL_ARG_0.dr6,eax
            mov     eax,dr7
            mov     [ebx]CALL_ARG_0.dr7,eax
        }
        break;
    case 1:
        /*
         * 调用内核函数MmGetPhysicalAddress。这是crazylord的主意。
         */
        __asm
        {
            mov     ebx,call_arg
            push    [ebx]CALL_ARG_1.LinearAddress
            call    MmGetPhysicalAddress
            mov     [ebx]CALL_ARG_1.PhysicalAddress.LowPart,eax
            mov     [ebx]CALL_ARG_1.PhysicalAddress.HighPart,edx
        }
        break;
    case 2:
        /*
         * 内存复制。由于在Ring 0,直接访问内核空间线性地址没有任何问题,
         * 这是h0ck0r@smth的主意。
         */
        __asm
        {
            mov     ebx,call_arg
            mov     esi,[ebx]CALL_ARG_2.src
            mov     edi,[ebx]CALL_ARG_2.dst
            mov     ecx,[ebx]CALL_ARG_2.len
            rep     movsb
        }
        break;
    default:
        break;
    }  /* end of switch */

    /*
     * epilog
     */
    __asm
    {
        popfd
        popad
        mov     esp,ebp
        pop     ebp
        retf    8
    }
}  /* end of Ring0Code */

static BOOLEAN SetPhysicalMemoryDACLs ( HANDLE handle, LPTSTR ptstrName )
{
    BOOLEAN              boolean_ret        = TRUE;
    DWORD                ret                = ERROR_SUCCESS;
    PACL                 OldDACLs           = NULL;
    PACL                 NewDACLs           = NULL;
    PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
    EXPLICIT_ACCESS      Access;

    /*
     * Header : Declared in Aclapi.h
     * Library: Use Advapi32.lib
     *
     * DWORD GetSecurityInfo
     * (
     *     HANDLE                handle,
     *     SE_OBJECT_TYPE        ObjectType,
     *     SECURITY_INFORMATION  SecurityInfo,
     *     PSID                 *ppsidOwner,
     *     PSID                 *ppsidGroup,
     *     PACL                 *ppDacl,
     *     PACL                 *ppSacl,
     *     PSECURITY_DESCRIPTOR *ppSecurityDescriptor
     * );
     */
    ret                                     = GetSecurityInfo
                                              (
                                                  handle,
                                                  SE_KERNEL_OBJECT,
                                                  DACL_SECURITY_INFORMATION,
                                                  NULL,
                                                  NULL,
                                                  &OldDACLs,
                                                  NULL,
                                                  &SecurityDescriptor
                                              );
    if ( ret != ERROR_SUCCESS )
    {
        PrintWin32Error( "GetSecurityInfo() failed", ret );
        boolean_ret = FALSE;
        goto SetPhysicalMemoryDACLs_return;
    }
    /*
     * DWORD SetEntriesInAcl
     * (
     *     ULONG             cCountOfExplicitEntries,
     *     PEXPLICIT_ACCESS  pListOfExplicitEntries,
     *     PACL              OldAcl,
     *     PACL             *NewAcl
     * );
     */
    ZeroMemory( &Access, sizeof( Access ) );
    Access.grfAccessPermissions             = SECTION_ALL_ACCESS;
    Access.grfAccessMode                    = GRANT_ACCESS;
    Access.grfInheritance                   = NO_INHERITANCE;
    Access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
    Access.Trustee.TrusteeForm              = TRUSTEE_IS_NAME;
    Access.Trustee.TrusteeType              = TRUSTEE_IS_USER;
    Access.Trustee.ptstrName                = ptstrName;
    ret                                     = SetEntriesInAcl( 1, &Access, OldDACLs, &NewDACLs );
    if ( ret != ERROR_SUCCESS )
    {
        PrintWin32Error( "SetEntriesInAcl() failed", ret );
        boolean_ret = FALSE;
        goto SetPhysicalMemoryDACLs_return;
    }
    /*
     * DWORD SetSecurityInfo
     * (
     *     HANDLE               handle,
     *     SE_OBJECT_TYPE       ObjectType,
     *     SECURITY_INFORMATION SecurityInfo,
     *     PSID                 psidOwner,
     *     PSID                 psidGroup,
     *     PACL                 pDacl,
     *     PACL                 pSacl
     * );
     */
    ret                                     = SetSecurityInfo
                                              (
                                                  handle,
                                                  SE_KERNEL_OBJECT,
                                                  DACL_SECURITY_INFORMATION,
                                                  NULL,
                                                  NULL,
                                                  NewDACLs,
                                                  NULL
                                              );
    if ( ret != ERROR_SUCCESS )
    {
        PrintWin32Error( "SetSecurityInfo() failed", ret );
        boolean_ret = FALSE;
        goto SetPhysicalMemoryDACLs_return;
    }

SetPhysicalMemoryDACLs_return:

    if ( NewDACLs != NULL )
    {
        LocalFree( NewDACLs );
        NewDACLs = NULL;
    }
    if ( SecurityDescriptor != NULL )
    {
        LocalFree( SecurityDescriptor );
        SecurityDescriptor = NULL;
    }
    return( boolean_ret );
}  /* end of SetPhysicalMemoryDACLs */

static VOID UnmapPhysicalMemory ( IN PVOID LinearAddress )
{
    NTSTATUS status;
    char     error_msg[256];

    /*
     * NTSTATUS ZwUnmapViewOfSection
     * (
     *     IN HANDLE ProcessHandle,
     *     IN PVOID  BaseAddress
     * );
     */
    status = ZwUnmapViewOfSection( ( HANDLE )-1, LinearAddress );
    if ( !NT_SUCCESS( status ) )
    {
        sprintf( error_msg, "Could not unmap LinearAddress from 0x%08X", ( unsigned int )LinearAddress );
        PrintZwError( error_msg, status );
    }
    return;
}  /* end of UnmapPhysicalMemory */

static void usage ( char *arg )
{
    fprintf( stderr, "Usage: %s [-h] [-g Gdtrbase] [-a Address] [-l Length]/n", arg );
    exit( EXIT_FAILURE );
}  /* end of usage */

int main ( int argc, char * argv[] )
{
    int                 i;
    GATEDESCRIPTOR     *callgate           = NULL;
    GATEDESCRIPTOR      orig_callgate;
    unsigned short int  selector           = RING0_CODE_SELECTOR;
    ULONG               Ring0CodeLength    = 0;
    CALL_ARG_0          call_arg_0;
    CALL_ARG_1          call_arg_1;
    CALL_ARG_2          call_arg_2;
    PSEUDODESCRIPTOR    GDTR;
    WORD                CURRENT_CS, CURRENT_DS, CURRENT_ES, CURRENT_SS, CURRENT_FS;
    HANDLE              mem                = NULL;
    PVOID               orig_GdtMapAddress = NULL;
    PVOID               GdtMapAddress      = NULL;
    ULONG               GdtMapSize         = 0;
    PHYSICAL_ADDRESS    orig_GdtPhysicalAddress;
    PHYSICAL_ADDRESS    GdtPhysicalAddress;
    ULONG               Address            = 0;
    ULONG               Length             = 0;
    unsigned char       buf[8192];

    ZeroMemory( &GDTR, sizeof( GDTR ) );
    /*
     * 从argv[1]开始循环处理命令行参数
     */
    for ( i = 1; i < argc; i++ )
    {
        /*
         * 同时支持-和/两种引入命令行参数的方式
         */
        if ( ( argv[i][0] == '-' ) || ( argv[i][0] == '/' ) )
        {
            /*
             * 在这个字节上,大小写不敏感
             */
            switch ( tolower( argv[i][1] ) )
            {
            case 'a':
                /*
                 * 欲访问的线性地址
                 */
                Address    = strtoul( argv[++i], NULL, 0 );
                break;
            case 'g':
                /*
                 * 有些环境中sgdt失灵,可通过-g指定GDTR的基址。但此时指定
                 * 的limit很冒险。
                 */
                GDTR.base  = ( unsigned int )strtoul( argv[++i], NULL, 0 );
                GDTR.limit = 0x03FF;
                break;
            case 'l':
                Length     = strtoul( argv[++i], NULL, 0 );
                break;
            case 'h':
            case '?':
            default:
                usage( argv[0] );
            }  /* end of switch */
        }
        else
        {
            usage( argv[0] );
        }
    }  /* end of for */
    if ( GDTR.base < 0x80000000 || GDTR.base >= 0xa0000000 )
    {
        /*
         * 获取GDTR寄存器的值。这个动作无法在VMware Workstation 3.2中完成,返
         * 回的线性基址不正确,必须在真实主机上测试。
         */
        __asm
        {
            sgdt    GDTR
            mov     CURRENT_CS,cs
            mov     CURRENT_DS,ds
            mov     CURRENT_ES,es
            mov     CURRENT_SS,ss
            mov     CURRENT_FS,fs
        }

抱歉!评论已关闭.