关于ring3到ring0的代码实现
作者:admin 日期:2009-10-22
☆ 利用调用门从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 );
}
else
{
fprintf( out, " %02X", byteArray );
}
}
fprintf( out, " " );
i -= 16;
for ( j = 0; j < 16; j++, i++ )
{
/*
* if ( isprint( (int)byteArray ) )
*/
#if 0
if ( ( byteArray >= ' ' ) && ( byteArray <= 255 )
&& ( byteArray != 0x7f ) )
#endif
if ( ( byteArray >= ' ' ) && ( byteArray < 0x7f ) )
{
fprintf( out, "%c", byteArray );
}
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 );
}
else
{
fprintf( out, " %02X", byteArray );
}
}
i -= k;
for ( j = 16 - k; j > 0; j-- )
{
fprintf( out, " " );
}
fprintf( out, " " );
for ( j = 0; j < k; j++, i++ )
{
#if 0
if ( ( byteArray >= ' ' ) && ( byteArray <= 255 )
&& ( byteArray != 0x7f ) )
#endif
if ( ( byteArray >= ' ' ) && ( byteArray < 0x7f ) )
{
fprintf( out, "%c", byteArray );
}
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.ImageName + module.ModuleNameOffset, ModuleName ) )
{
Base = module.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[0] == '-' ) || ( argv[0] == '/' ) )
{
/*
* 在这个字节上,大小写不敏感
*/
switch ( tolower( argv[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 */
/************************************************************************/
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/LionD8/archive/2005/06/30/408124.aspx
观察用户空间程序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 );
}
else
{
fprintf( out, " %02X", byteArray );
}
}
fprintf( out, " " );
i -= 16;
for ( j = 0; j < 16; j++, i++ )
{
/*
* if ( isprint( (int)byteArray ) )
*/
#if 0
if ( ( byteArray >= ' ' ) && ( byteArray <= 255 )
&& ( byteArray != 0x7f ) )
#endif
if ( ( byteArray >= ' ' ) && ( byteArray < 0x7f ) )
{
fprintf( out, "%c", byteArray );
}
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 );
}
else
{
fprintf( out, " %02X", byteArray );
}
}
i -= k;
for ( j = 16 - k; j > 0; j-- )
{
fprintf( out, " " );
}
fprintf( out, " " );
for ( j = 0; j < k; j++, i++ )
{
#if 0
if ( ( byteArray >= ' ' ) && ( byteArray <= 255 )
&& ( byteArray != 0x7f ) )
#endif
if ( ( byteArray >= ' ' ) && ( byteArray < 0x7f ) )
{
fprintf( out, "%c", byteArray );
}
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.ImageName + module.ModuleNameOffset, ModuleName ) )
{
Base = module.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[0] == '-' ) || ( argv[0] == '/' ) )
{
/*
* 在这个字节上,大小写不敏感
*/
switch ( tolower( argv[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 */
/************************************************************************/
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/LionD8/archive/2005/06/30/408124.aspx
评论: 0 | 查看次数: 11797