利用調用門從Ring 3進入Ring 0[轉]

☆ 利用調用門從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 <[email protected]>
* Gary Nebbett
* h0ck0r@smth
* Mark E. Russinovich
* tsu00 <[email protected]>
*
* 這是此番學習筆記中惟一列舉源作者的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
        }
        printf( "GDTR.base             = 0x%08X/n"
                "GDTR.limit            = 0x%04X/n"
                "CURRENT_CS            = 0x%04X/n"
                "CURRENT_DS            = 0x%04X/n"
                "CURRENT_ES            = 0x%04X/n"
                "CURRENT_SS            = 0x%04X/n"
                "CURRENT_FS            = 0x%04X/n",
                GDTR.base, GDTR.limit, CURRENT_CS, CURRENT_DS,
                CURRENT_ES, CURRENT_SS, CURRENT_FS );
        if ( GDTR.base < 0x80000000 || GDTR.base >= 0xa0000000 )
        {
            fprintf( stderr, "Checking your GDTR.base( 0x%08X )/n", GDTR.base );
            return( EXIT_FAILURE );
        }
    }
    /*
     * 如果沒有指定線性地址,缺省處理GDT
     */
    if ( 0 == Address )
    {
        Address = GDTR.base;
    }
    if ( FALSE == LocateNtdllEntry() )
    {
        fprintf( stderr, "LocateNtdllEntry() failed/n" );
        return( EXIT_FAILURE );
    }
    if ( FALSE == LocateNtoskrnlEntry() )
    {
        fprintf( stderr, "LocateNtoskrnlEntry() failed/n" );
        return( EXIT_FAILURE );
    }
    ZeroMemory( &system_info, sizeof( system_info ) );
    GetSystemInfo( &system_info );
    printf( "PageSize              = 0x%08X/n"
            "AllocationGranularity = 0x%08X/n",
            system_info.dwPageSize, system_info.dwAllocationGranularity );
    /*
     * 如果沒有指定長度,缺省按一頁處理。
     */
    if ( 0 == Length )
    {
        Length = system_info.dwPageSize;
    }
    if ( Length > sizeof( buf ) )
    {
        Length = sizeof( buf );
    }
    /*
     * GDTR.limit對應的是最後有效偏移,不要先增一再強制類型轉換,否則可能
     * 發生短整型溢出,這是編譯器相關的。
     */
    GdtMapSize              = ( ULONG )GDTR.limit + 1;
    ZeroMemory( &orig_GdtPhysicalAddress, sizeof( orig_GdtPhysicalAddress ) );
    ZeroMemory( &GdtPhysicalAddress, sizeof( GdtPhysicalAddress ) );
    /*
     * 爲映射GDT作準備
     */
    orig_GdtPhysicalAddress = PrivateMmGetPhysicalAddress( ( PVOID )GDTR.base );
    if ( orig_GdtPhysicalAddress.LowPart == 0 )
    {
        fprintf( stderr, "orig_GdtPhysicalAddress.LowPart == 0/n" );
        return( EXIT_FAILURE );
    }
    GdtPhysicalAddress      = orig_GdtPhysicalAddress;
    /*
     * 爲讀寫DACLs而打開句柄
     */
    if ( ( mem = OpenPhysicalMemory( READ_CONTROL | WRITE_DAC ) ) == NULL )
    {
        return( EXIT_FAILURE );
    }
    else
    {
        SetPhysicalMemoryDACLs( mem, "CURRENT_USER" );
        ZwClose( mem );
        mem = NULL;
    }
    /*
     * 試圖讀寫打開"/Device/PhysicalMemory"
     */
    if ( ( mem = OpenPhysicalMemory( SECTION_MAP_READ | SECTION_MAP_WRITE ) ) == NULL )
    {
        return( EXIT_FAILURE );
    }
    /*
     * 讀/寫映射GDT
     */
    if ( TRUE == MapPhysicalMemory( mem, &GdtMapAddress, &GdtMapSize, &GdtPhysicalAddress, PAGE_READWRITE ) )
    {
        orig_GdtMapAddress = ( unsigned char * )GdtMapAddress + ( orig_GdtPhysicalAddress.LowPart - GdtPhysicalAddress.LowPart );
        /*
         * 搜索空閒描述符並設置調用門
         */
        callgate = InstallCallgate( ( ULONG )orig_GdtMapAddress,
                                    ( ULONG )GDTR.limit,
                                    ( DWORD )Ring0Code,
                                    &orig_callgate );
        if ( NULL != callgate )
        {
            /*
             * 指向GDT中調用門的選擇子,RPL等於0。
             */
            selector                 = ( unsigned short int )( ( unsigned char * )callgate - ( unsigned char * )orig_GdtMapAddress );
            printf( "idle selector         = 0x%04X/n", selector );
            /*
             * 獲取Ring 0權限,執行Ring 0代碼
             */
            ZeroMemory( &call_arg_0, sizeof( call_arg_0 ) );
            Ring0CodeLength          = ( ULONG )( ( unsigned char * )SetPhysicalMemoryDACLs - ( unsigned char * )Ring0Code );
            ExecuteRing0Code( ( PVOID )Ring0Code, Ring0CodeLength, selector, 0, &call_arg_0 );
            printf( "CR0                   = 0x%08X/n"
                    "CR2                   = 0x%08X/n"
                    "CR3                   = 0x%08X/n"
                    "DR0                   = 0x%08X/n"
                    "DR1                   = 0x%08X/n"
                    "DR2                   = 0x%08X/n"
                    "DR3                   = 0x%08X/n"
                    "DR6                   = 0x%08X/n"
                    "DR7                   = 0x%08X/n",
                    call_arg_0.cr0, call_arg_0.cr2, call_arg_0.cr3,
                    call_arg_0.dr0, call_arg_0.dr1, call_arg_0.dr2,
                    call_arg_0.dr3, call_arg_0.dr6, call_arg_0.dr7 );
            ZeroMemory( &call_arg_1, sizeof( call_arg_1 ) );
            call_arg_1.LinearAddress = ( PVOID )( GDTR.base );
            ExecuteRing0Code( ( PVOID )Ring0Code, Ring0CodeLength, selector, 1, &call_arg_1 );
            printf( "GDTR.base(Physical)   = 0x%08X/n", call_arg_1.PhysicalAddress.LowPart );
            ZeroMemory( &call_arg_1, sizeof( call_arg_1 ) );
            call_arg_1.LinearAddress = ( PVOID )0xc0300000;
            ExecuteRing0Code( ( PVOID )Ring0Code, Ring0CodeLength, selector, 1, &call_arg_1 );
            /*
             * 注意與CR3的值作比較,如果沒出錯的話,CR3 == PDR
             */
            printf( "PDR(Physical)         = 0x%08X/n", call_arg_1.PhysicalAddress.LowPart );
            ZeroMemory( &call_arg_2, sizeof( call_arg_2 ) );
            /*
             * 如果Address導致異常,結果未知,別問我。
             */
            call_arg_2.src           = ( PVOID )Address;
            call_arg_2.dst           = ( PVOID )buf;
            call_arg_2.len           = Length;
            ExecuteRing0Code( ( PVOID )Ring0Code, Ring0CodeLength, selector, 2, &call_arg_2 );
            outputBinary( stdout, ( const unsigned char * )call_arg_2.dst, ( const size_t )call_arg_2.len );
            /*
             * 恢復被徵用的"空閒"描述符
             */
            memcpy( callgate, &orig_callgate, sizeof( GATEDESCRIPTOR ) );
        }
        /*
         * 解除對GDT的映射
         */
        UnmapPhysicalMemory( GdtMapAddress );
    }
    if ( mem != NULL )
    {
        ZwClose( mem );
        mem = NULL;
    }
    return( EXIT_SUCCESS );
}  /* end of main */

/************************************************************************/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章