☆ 利用調用門從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 */
/************************************************************************/
利用調用門從Ring 3進入Ring 0[轉]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章
{轉載}惠普前總裁孫振耀指點職業規劃
我有個有趣的觀察,外企公司多的是25-35歲的白領,40歲以上的員工很少,二三十歲的外企員工是意氣風發的,但外企公司40歲附近的經理人是很尷尬 的。我
luckheadline
2020-07-03 01:51:54
java7和java8 hashmap擴容機制及區別
若Q清风
2020-06-28 00:07:26
libxml2庫函數的解析
xml|函數 libxml(一) 摘要 Libxml是一個有免費許可的用於處理XML、可以輕鬆跨越多個平臺的C語言庫。這個指南提供它的基本函數的例子。 緒論 L
四方山下
2020-06-16 07:24:37
反病毒引擎設計[轉]
本文將對當今先進的病毒/反病毒技術做全面而細緻的介紹,重點當然放在了反病毒上,特別是虛擬機和實時監控技術。文中首先介紹幾種當今較爲流行的病毒技術,包括
觉鸿
2020-06-14 13:12:12
Java web 中的target屬性
garylijia
2020-06-13 18:50:11
內存分配(new/delete,malloc/free,allocator,內存池)
无缰之马
2020-02-25 15:44:47
求遞歸算法時間複雜度:遞歸樹
无缰之马
2020-02-25 15:44:37
{轉載}Linux/Unix平臺可執行文件格式分析
luckheadline
2020-02-24 08:45:00
如何在Windows NT中隱藏自己[轉]
觉鸿
2020-02-23 18:37:01
Phrack最新公佈的內核態RootKit的技術細節[轉]
觉鸿
2020-02-23 18:37:01
Windows Rootkit相關鏈接[轉]
觉鸿
2020-02-23 18:37:01
解析Windows2000的IDT擴展機制[轉]
觉鸿
2020-02-23 18:37:01
CreateProcess in KernelMode[轉]
觉鸿
2020-02-23 18:37:01
Java程序員面試中的多線程問題
张毓飞
2020-02-23 12:50:59