Skip to content

Latest commit

 

History

History
5341 lines (4796 loc) · 194 KB

200305071426.txt.md

File metadata and controls

5341 lines (4796 loc) · 194 KB

标题: MSDN系列(1)--Unix程序员的转折点

创建: 2003-05-07 14:26 更新: 链接: http://scz.617.cn/windows/200305071426.txt


目录:

☆ 概述
☆ OBJECT_HEADER及其相关数据结构定义
☆ 查看\Device\PhysicalMemory的DACLs
☆ XP下利用kd获取对象ACLs的标准步骤
   1) 使用kd扩展命令!object观察对象
   2) 使用dt(Display Type)命令解析nt!_OBJECT_HEADER结构
   3) 使用kd扩展命令!sd观察安全描述符
   4) 使用kd扩展命令!sid观察SID
   5) 使用kd扩展命令!acl观察DACLs
☆ 修改\Device\PhysicalMemory的DACLs
☆ 利用ZwQuerySystemInformation()枚举内核模块(module)
☆ 在kd中转换线性地址到物理地址
☆ 对[0x80000000, 0xa0000000)区间上指定线性地址进行指定长度的16进制dump
☆ 利用调用门从Ring 3进入Ring 0
☆ 利用Tool Help Library枚举进程/模块
☆ SE_DEBUG_NAME权限
☆ 利用PSAPI枚举进程/模块
☆ 利用ZwQuerySystemInformation()枚举进程
☆ 参考资源

☆ 概述

这是2003年5月决定背叛Unix时对Windows做的一点小小尝试,现在看来很可笑的尝试, 没什么技术含量。文中的许多疑惑是当时的疑惑,后来不成为疑惑,但我没舍得删除 它们,好歹也算是记录了一名传统Unix程序员背叛瞬间的全过程。留此存照。

Windows下的"\Device\PhysicalMemory"类似Unix下的伪设备/dev/mem。通过这个伪 设备,有足够权限的用户可以在Ring 3代码中直接访问内核空间。意味着Ring 3代码 有"可能"修改GDT、IDT,进而让自己的可控代码获取Ring 0特权。

从未写过Windows 3.1下的C程序,实在缺乏Windows编程基础。惟一可言基础的仅仅 是对IA-32架构保护模式的残缺理解,那也只是历史痕迹,10年前曾经是MS-DOS下彻 底的实模式汇编程序员。如果说后来选择Unix是因为对30年前充满传奇色彩的Unix共 享传统文化圈的无限神往,那今天再次以程序员身份接触微软的操作系统却是因为一 种怀旧,毕竟我们前行的路上有过高中时代的BASIC、大学时代的MS-DOS。一直很感 谢两种操作系统,一个是MS-DOS,一个是Linux。我相当不喜欢Linux,可我非常感谢 它,它对我的帮助廖廖,远不如Solaris、*BSD,但正是它使得沉寂了许久的Unix世 界开始苏醒。我对MS-DOS的喜爱却是朴素的、永久的,以至多年以后还是不能甩去对 比尔本人的崇拜,这种感觉大抵不是现今VCer们所能理解的。

开始抽空学习Windows。虽然Windows通过硬件抽像层(HAL)屏蔽了很多架构相关的处 理细节,但我目前所能接触到的也只是IA-32,所以前些日子翻看Intel卷III的部分 章节以助理解。由于没有什么基础,感觉万事开头难。学习一种新技术,多加实践太 重要了,下面是个人的学习笔记,这是好听的说法,实际只是跟着别人的文章、代码 完整走了一遍而已。笔记中错误百出,可以想像,一些看似无用的冗长注释是写给似 我一般的Unix/C程序员看的,万望海涵并斧正之。

要达到什么目的,我也不清楚。在SPARC/Solaris上写过此类型用户空间程序,打开 /dev/(k)mem,结合/dev/ksyms做用户空间的Kernel Hacking。这次来到一个完全陌 生的领域,走在很久以前别人铺就的坦荡大道上,诚諻诚恐,说不出的落寞。不敢指 望达到一个具体的目的,惟希望通过这次的学习笔记,对一些基础的东西有所了解, 至少熟悉一些Windows工具的使用吧,sigh。

下面所涉及的操作系统是英文版Windows XP SP1。

☆ OBJECT_HEADER及其相关数据结构定义

bosse [email protected]在自己维护的ntifs.h([10])文件中给了一个不太理想的 "struct _OBJECT_HEADER"定义,这里不引用之,有兴趣者自行参看。

Sven B. Schreiber在<<Undocumented Windows 2000 Secrets>>([6])中给出如下一 些定义:


#define OB_FLAG_CREATE_INFO 0x01 // has OBJECT_CREATE_INFO #define OB_FLAG_KERNEL_MODE 0x02 // created by kernel #define OB_FLAG_CREATOR_INFO 0x04 // has OBJECT_CREATOR_INFO #define OB_FLAG_EXCLUSIVE 0x08 // OBJ_EXCLUSIVE #define OB_FLAG_PERMANENT 0x10 // OBJ_PERMANENT #define OB_FLAG_SECURITY 0x20 // has security descriptor #define OB_FLAG_SINGLE_PROCESS 0x40 // no HandleDBList

typedef struct OBJECT_HEADER /* 0x00 /{ / 0x00 / DWORD PointerCount; // number of references / 0x04 / DWORD HandleCount; // number of open handles / 0x08 / POBJECT_TYPE ObjectType; // / 0x0C / BYTE NameOffset; // -> OBJECT_NAME / 0x0D / BYTE HandleDBOffset; // -> OBJECT_HANDLE_DB / 0x0E / BYTE QuotaChargesOffset; // -> OBJECT_QUOTA_CHARGES / 0x0F */ BYTE ObjectFlags; // OB_FLAG* /* 0x10 / union / / { / / // OB_FLAG_CREATE_INFO ? ObjectCreateInfo : QuotaBlock / 0x10 / PQUOTA_BLOCK QuotaBlock; / 0x10 / POBJECT_CREATE_INFO ObjectCreateInfo; / / }; / 0x14 / PSECURITY_DESCRIPTOR SecurityDescriptor; / 0x18 */} OBJECT_HEADER, *POBJECT_HEADER, **PPOBJECT_HEADER;

typedef struct _OBJECT_NAME /* 0x00 /{ / 0x00 / POBJECT_DIRECTORY Directory; / 0x04 / UNICODE_STRING Name; / 0x0C / DWORD Reserved; / 0x10 */} OBJECT_NAME, *POBJECT_NAME, **PPOBJECT_NAME;

这里有如下关系:


OBJECT_NAME = OBJECT_HEADER - NameOffset OBJECT_HANDLE_DB = OBJECT_HEADER - HandleDBOffset OBJECT_QUOTA_CHARGES = OBJECT_HEADER - QuotaChargesOffset

需要注意的一点,SecurityDescriptor成员并不精确指向SECURITY_DESCRIPTOR结构, 它实际是PVOID型,其低3位表示该PVOID指针相对SECURITY_DESCRIPTOR结构首部的偏 移。换句话说,SECURITY_DESCRIPTOR结构首部总是对齐在8字节边界上:

SECURITY_DESCRIPTOR = SecurityDescriptor & ~0x7

UNICODE_STRING定义如下:


/*

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

SECURITY_DESCRIPTOR定义如下:


/*

  • winnt.h */ typedef struct _SECURITY_DESCRIPTOR { BYTE Revision; BYTE Sbz1; SECURITY_DESCRIPTOR_CONTROL Control; PSID Owner; PSID Group; PACL Sacl; PACL Dacl; } SECURITY_DESCRIPTOR;

typedef PVOID PSECURITY_DESCRIPTOR;

typedef struct _ACL { BYTE AclRevision; BYTE Sbz1; WORD AclSize; WORD AceCount; WORD Sbz2; } ACL, *PACL;

typedef struct _ACE_HEADER { BYTE AceType; BYTE AceFlags; WORD AceSize; } ACE_HEADER, *PACE_HEADER;

typedef struct _ACCESS_ALLOWED_ACE { ACE_HEADER Header; ACCESS_MASK Mask; DWORD SidStart; } ACCESS_ALLOWED_ACE;

1 1 1 1 1 1 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +---------------------------------------------------------------+ | SubAuthorityCount |Reserved1 (SBZ)| Revision | +---------------------------------------------------------------+ | IdentifierAuthority[0] | +---------------------------------------------------------------+ | IdentifierAuthority[1] | +---------------------------------------------------------------+ | IdentifierAuthority[2] | +---------------------------------------------------------------+ | | +- - - - - - - - SubAuthority[] - - - - - - - - -+ | | +---------------------------------------------------------------+

#define SECURITY_NT_AUTHORITY {0,0,0,0,0,5}

#define SECURITY_LOCAL_SYSTEM_RID (0x00000012L) #define SECURITY_BUILTIN_DOMAIN_RID (0x00000020L)

#define DOMAIN_ALIAS_RID_ADMINS (0x00000220L)

#define DELETE (0x00010000L) #define READ_CONTROL (0x00020000L) #define WRITE_DAC (0x00040000L) #define WRITE_OWNER (0x00080000L) #define SYNCHRONIZE (0x00100000L) #define STANDARD_RIGHTS_REQUIRED (0x000F0000L) #define STANDARD_RIGHTS_READ (READ_CONTROL) #define STANDARD_RIGHTS_WRITE (READ_CONTROL) #define STANDARD_RIGHTS_EXECUTE (READ_CONTROL) #define STANDARD_RIGHTS_ALL (0x001F0000L) #define SPECIFIC_RIGHTS_ALL (0x0000FFFFL)

/*

  • ntddk.h */ #define SECTION_QUERY 0x0001 #define SECTION_MAP_WRITE 0x0002 #define SECTION_MAP_READ 0x0004 #define SECTION_MAP_EXECUTE 0x0008 #define SECTION_EXTEND_SIZE 0x0010 #define SECTION_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|
    SECTION_QUERY |
    SECTION_MAP_WRITE |
    SECTION_MAP_READ |
    SECTION_MAP_EXECUTE |
    SECTION_EXTEND_SIZE)

关于DACLs/ACE,参考资源[5]中"Security Descriptors and Access Control"小节 以及MSDN。DACLs指明各个SID访问该对象时所受权限控制,每个ACE定义了具体的访 问权限控制。

☆ 查看\Device\PhysicalMemory的DACLs

crazylord [email protected]用kd观察\Device\PhysicalMemory([3]):


kd> !object \Device\PhysicalMemory Object: e1007a78 Type: (8121e390) Section ObjectHeader: e1007a60 HandleCount: 0 PointerCount: 2 Directory Object: e1007810 Name: PhysicalMemory kd> db e1007a60 L0x18 (查看OBJECT_HEADER) e1007a60 02 00 00 00 00 00 00 00-90 e3 21 81 10 00 00 32 ..........!....2 e1007a70 01 00 00 00 73 76 00 e1 ....sv.. kd> db e1007a60-10 L0x10 (查看OBJECT_NAME) e1007a50 10 78 00 e1 1c 00 1c 00-a8 72 00 e1 01 00 00 00 .x.......r...... ~~~~~~~~~~~ kd> du e10072a8 (查看UNICODE_STRING.Buffer) e10072a8 "PhysicalMemory"

OBJECT_HEADER结构与其16进制显示对应如下:


typedef struct _OBJECT_HEADER { DWORD PointerCount = 0x00000002 DWORD HandleCount = 0x00000000 POBJECT_TYPE ObjectType = 0x8121e390 BYTE NameOffset = 0x10 BYTE HandleDBOffset = 0x00 BYTE QuotaChargesOffset = 0x00 BYTE ObjectFlags = 0x32 = 0y00110010 = OB_FLAG_SECURITY | OB_FLAG_PERMANENT | OB_FLAG_KERNEL_MODE union { PQUOTA_BLOCK QuotaBlock = 0x00000001 POBJECT_CREATE_INFO ObjectCreateInfo; }; PSECURITY_DESCRIPTOR SecurityDescriptor = 0xe1007673 } OBJECT_HEADER, *POBJECT_HEADER, **PPOBJECT_HEADER;

如果照搬Phrack Magazine 59-0x10([3])的命令查看安全描述符,会看到如下输出:


kd> !sd e1007673 00006800: Unable to get MIN SID header 00006800: Unable to read in Owner in SD

事实上前面解释过了,安全描述符起始地址对齐在8字节边界上:


kd> !sd e1007673 & fffffff8 ->Revision: 0x1 ->Sbz1 : 0x0 ->Control : 0x8004 SE_DACL_PRESENT SE_SELF_RELATIVE ->Owner : S-1-5-32-544 ->Group : S-1-5-18 ->Dacl : ->Dacl : ->AclRevision: 0x2 ->Dacl : ->Sbz1 : 0x0 ->Dacl : ->AclSize : 0x44 ->Dacl : ->AceCount : 0x2 ->Dacl : ->Sbz2 : 0x0 ->Dacl : ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE ->Dacl : ->Ace[0]: ->AceFlags: 0x0 ->Dacl : ->Ace[0]: ->AceSize: 0x14 ->Dacl : ->Ace[0]: ->Mask : 0x000f001f ->Dacl : ->Ace[0]: ->SID: S-1-5-18

->Dacl : ->Ace[1]: ->AceType: ACCESS_ALLOWED_ACE_TYPE ->Dacl : ->Ace[1]: ->AceFlags: 0x0 ->Dacl : ->Ace[1]: ->AceSize: 0x18 ->Dacl : ->Ace[1]: ->Mask : 0x0002000d ->Dacl : ->Ace[1]: ->SID: S-1-5-32-544

->Sacl : is NULL

tsu00用来自DDK的工具objdir查看\Device\PhysicalMemory的DACLs([4]):


objdir /D \Device\PhysicalMemory Section DACL - Ace[ 0] - Grant - 0xf001f - NT AUTHORITY\SYSTEM Inherit: Access : 0x001F Query MapWrite MapRead MapExecute Extend ( D RCtl WOwn WDacl )

   Ace[ 1] - Grant - 0x2000d - BUILTIN\Administrators
             Inherit:
             Access : 0x000D
                      Query MapRead MapExecute
                      ( RCtl )

默认情况下SYSTEM用户可以读/写访问该对象,Administrator只能读访问该对象,普 通用户无权访问该对象。

☆ XP下利用kd获取对象ACLs的标准步骤

参看"Using Debugging Tools for Windows"([11])。

  1. 使用kd扩展命令!object观察对象

kd> !object \Device\PhysicalMemory Object: e1007a78 Type: (8121e390) Section ObjectHeader: e1007a60 HandleCount: 0 PointerCount: 2 Directory Object: e1007810 Name: PhysicalMemory

注意,Object指向对象体(e1007a78),ObjectHeader指向对象头(0xe1007a60),这是 一个Section对象。

  1. 使用dt(Display Type)命令解析nt!_OBJECT_HEADER结构

dt命令可以查看很多内核数据结构的定义,执行"dt nt!OBJECT"可以看到与对象相 关的一批内核数据结构符号名。下面解析OBJECT_HEADER结构:


kd> dt nt!_OBJECT_HEADER +0x000 PointerCount : Int4B +0x004 HandleCount : Int4B +0x004 NextToFree : Ptr32 Void +0x008 Type : Ptr32 _OBJECT_TYPE +0x00c NameInfoOffset : UChar +0x00d HandleInfoOffset : UChar +0x00e QuotaInfoOffset : UChar +0x00f Flags : UChar +0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION +0x010 QuotaBlockCharged : Ptr32 Void +0x014 SecurityDescriptor : Ptr32 Void +0x018 Body : _QUAD kd> dt nt!_OBJECT_HEADER e1007a60 +0x000 PointerCount : 2 +0x004 HandleCount : 0 +0x004 NextToFree : (null) +0x008 Type : 0x8121e390 +0x00c NameInfoOffset : 0x10 '' +0x00d HandleInfoOffset : 0 '' +0x00e QuotaInfoOffset : 0 '' +0x00f Flags : 0x32 '2' +0x010 ObjectCreateInfo : 0x00000001 +0x010 QuotaBlockCharged : 0x00000001 +0x014 SecurityDescriptor : 0xe1007673 +0x018 Body : _QUAD

SecurityDescriptor成员等于0xe1007673,为了获取精确指向安全描述符结构的指针, 作如下运算:

0xe1007673 & 0xfffffff8 -> 0xe1007670

  1. 使用kd扩展命令!sd观察安全描述符

kd> !sd e1007670 1 ->Revision: 0x1 ->Sbz1 : 0x0 ->Control : 0x8004 SE_DACL_PRESENT SE_SELF_RELATIVE ->Owner : S-1-5-32-544 (Alias: BUILTIN\Administrators) ->Group : S-1-5-18 (Well Known Group: NT AUTHORITY\SYSTEM) ->Dacl : ->Dacl : ->AclRevision: 0x2 ->Dacl : ->Sbz1 : 0x0 ->Dacl : ->AclSize : 0x44 ->Dacl : ->AceCount : 0x2 ->Dacl : ->Sbz2 : 0x0 ->Dacl : ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE ->Dacl : ->Ace[0]: ->AceFlags: 0x0 ->Dacl : ->Ace[0]: ->AceSize: 0x14 ->Dacl : ->Ace[0]: ->Mask : 0x000f001f ->Dacl : ->Ace[0]: ->SID: S-1-5-18 (Well Known Group: NT AUTHORITY\SYSTEM)

->Dacl : ->Ace[1]: ->AceType: ACCESS_ALLOWED_ACE_TYPE ->Dacl : ->Ace[1]: ->AceFlags: 0x0 ->Dacl : ->Ace[1]: ->AceSize: 0x18 ->Dacl : ->Ace[1]: ->Mask : 0x0002000d ->Dacl : ->Ace[1]: ->SID: S-1-5-32-544 (Alias: BUILTIN\Administrators)

->Sacl : is NULL kd> dt nt!_SECURITY_DESCRIPTOR e1007670 +0x000 Revision : 0x1 '' +0x001 Sbz1 : 0 '' +0x002 Control : 0x8004 +0x004 Owner : 0x00000058 (这是偏移,不是指针) +0x008 Group : 0x00000068 (这是偏移,不是指针) +0x00c Sacl : (null) +0x010 Dacl : 0x00000014 (这是偏移,不是指针)

  1. 使用kd扩展命令!sid观察SID

kd> !sid e1007670+58 SID is: S-1-5-32-544 kd> !sid e1007670+68 SID is: S-1-5-18 kd> dt nt!_SID e1007670+58 +0x000 Revision : 0x1 '' +0x001 SubAuthorityCount : 0x2 '' +0x002 IdentifierAuthority : _SID_IDENTIFIER_AUTHORITY +0x008 SubAuthority : [1] 0x20 kd> dt nt!_SID e1007670+68 +0x000 Revision : 0x1 '' +0x001 SubAuthorityCount : 0x1 '' +0x002 IdentifierAuthority : _SID_IDENTIFIER_AUTHORITY +0x008 SubAuthority : [1] 0x12 kd> dd e1007670+58+8 L2 e10076d0 00000020 00000220 kd> dd e1007670+68+8 L1 e10076e0 00000012

关于SID参看winnt.h中的定义。

  1. 使用kd扩展命令!acl观察DACLs

kd> dt nt!_ACL e1007670+14 +0x000 AclRevision : 0x2 '' +0x001 Sbz1 : 0 '' +0x002 AclSize : 0x44 +0x004 AceCount : 2 +0x006 Sbz2 : 0 kd> !acl e1007670+14 1 ACL is: ACL is: ->AclRevision: 0x2 ACL is: ->Sbz1 : 0x0 ACL is: ->AclSize : 0x44 ACL is: ->AceCount : 0x2 ACL is: ->Sbz2 : 0x0 ACL is: ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE ACL is: ->Ace[0]: ->AceFlags: 0x0 ACL is: ->Ace[0]: ->AceSize: 0x14 ACL is: ->Ace[0]: ->Mask : 0x000f001f ACL is: ->Ace[0]: ->SID: S-1-5-18 (Well Known Group: NT AUTHORITY\SYSTEM)

ACL is: ->Ace[1]: ->AceType: ACCESS_ALLOWED_ACE_TYPE ACL is: ->Ace[1]: ->AceFlags: 0x0 ACL is: ->Ace[1]: ->AceSize: 0x18 ACL is: ->Ace[1]: ->Mask : 0x0002000d ACL is: ->Ace[1]: ->SID: S-1-5-32-544 (Alias: BUILTIN\Administrators)

kd>

☆ 修改\Device\PhysicalMemory的DACLs

下面这个演示程序来自参考资源[1]至[4],只是相对容易编译通过而已,可以用DDK 中的objdir检验执行效果。缺省无参数执行时,在DACLs中增加当前用户相关的ACE, 可以指定其它用户、组。

有个疑问,BUILTIN\Administrators初始权限是0x0002000d,就是说没有WRITE_DAC 权限,为何还能指定该权限打开设备获取句柄呢,是否管理员权限总是可以修改ACLs 的。


/*

  • For x86/EWindows XP SP1 & VC 7
  • cl chmod_mem.c /Os /G6 /W3
  • Usage: chmod_mem [everyone | ...] */

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

  •                                                                  *
  •                           Head File                              *
  •                                                                  *

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

#include <stdio.h> #include <stdlib.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)

/*

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

/*

  • ntdef.h
  • 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;

/*

  • 参看DDK文档以及<<Windows NT/2000 Native API Reference>> by Gary Nebbett */ 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 ULONG ( __stdcall *RTLNTSTATUSTODOSERROR ) ( IN NTSTATUS Status );

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

  •                                                                  *
  •                        Function Prototype                        *
  •                                                                  *

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

static VOID InitializeObjectAttributes ( OUT POBJECT_ATTRIBUTES InitializedAttributes, IN PUNICODE_STRING ObjectName, IN ULONG Attributes, IN HANDLE RootDirectory, IN PSECURITY_DESCRIPTOR SecurityDescriptor ); static BOOLEAN LocateNtdllEntry ( void ); static HANDLE OpenPhysicalMemory ( ACCESS_MASK DesiredAccess ); static void PrintWin32Error ( char *message, DWORD dwMessageId ); static void PrintZwError ( char *message, NTSTATUS status ); static BOOLEAN SetPhysicalMemoryDACLs ( HANDLE handle, LPTSTR ptstrName );

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

  •                                                                  *
  •                        Static Global Var                         *
  •                                                                  *

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

static RTLINITUNICODESTRING RtlInitUnicodeString = NULL; static ZWOPENSECTION ZwOpenSection = NULL; static ZWCLOSE ZwClose = NULL; static RTLNTSTATUSTODOSERROR RtlNtStatusToDosError = NULL;

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

/*

  • 在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 */

/*

  • ntdll.dll输出了所有的Native API / static BOOLEAN LocateNtdllEntry ( void ) { if ( !( RtlInitUnicodeString = ( RTLINITUNICODESTRING )GetProcAddress( GetModuleHandle( "ntdll.dll" ), "RtlInitUnicodeString" ) ) ) { return( FALSE ); } if ( !( ZwOpenSection = ( ZWOPENSECTION )GetProcAddress( GetModuleHandle( "ntdll.dll" ), "ZwOpenSection" ) ) ) { return( FALSE ); } if ( !( ZwClose = ( ZWCLOSE )GetProcAddress( GetModuleHandle( "ntdll.dll" ), "ZwClose" ) ) ) { return( FALSE ); } if ( !( RtlNtStatusToDosError = ( RTLNTSTATUSTODOSERROR )GetProcAddress( GetModuleHandle( "ntdll.dll" ), "RtlNtStatusToDosError" ) ) ) { return( FALSE ); } return( TRUE ); } / end of LocateNtdllEntry */

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 */

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 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 */

int main ( int argc, char * argv[] ) { HANDLE mem = NULL;

if ( FALSE == LocateNtdllEntry() )
{
    fprintf( stderr, "LocateNtdllEntry() failed\n" );
    return( EXIT_FAILURE );
}
/*
 * 为读写DACLs而打开句柄
 */
if ( ( mem = OpenPhysicalMemory( READ_CONTROL | WRITE_DAC ) ) == NULL )
{
    return( EXIT_FAILURE );
}
else
{
    if ( argc == 2 )
    {
        SetPhysicalMemoryDACLs( mem, argv[1] );
    }
    else
    {
        SetPhysicalMemoryDACLs( mem, "CURRENT_USER" );
    }
    ZwClose( mem );
    mem = NULL;
}
/*
 * 试图读写打开"\Device\PhysicalMemory"
 */
if ( ( mem = OpenPhysicalMemory( SECTION_MAP_READ | SECTION_MAP_WRITE ) ) == NULL )
{
    return( EXIT_FAILURE );
}
if ( mem != NULL )
{
    ZwClose( mem );
    mem = NULL;
}
return( EXIT_SUCCESS );

} /* end of main */

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


☆ 利用ZwQuerySystemInformation()枚举内核模块(module)

DDK中提供了一个drivers.exe枚举内核模块,下面这个程序完成类似工作,只是将模 块基址一并显示出来而已,实际是<<Windows NT/2000 Native API Reference>>中的 代码。本来我是去写PrivateFindModule( ModuleName )函数,结果就自然演练了一 下ListModule()。


/*

  • For x86/EWindows XP SP1 & VC 7
  • cl listmodule.c /Os /G6 /W3
  • Usage: listmodule */

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

  •                                                                  *
  •                           Head File                              *
  •                                                                  *

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

#include <stdio.h> #include <stdlib.h> #include <malloc.h> #include <windows.h>

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

  •                                                                  *
  •                           Macro                                  *
  •                                                                  *

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

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

typedef LONG NTSTATUS;

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

/*


  • <<Windows NT/2000 Native API Reference>> by Gary Nebbett */

/*

  • 虽然本程序用不到这么多枚举值,还是列出一份最完整的。这个程序本身不求完
  • 美,尽可能多地保留一些未文档化的参考资料。 */ 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

*/

/*

  • 参看DDK文档以及<<Windows NT/2000 Native API Reference>> by Gary Nebbett
  • 这些Native API由ntdll.dll输出 */ 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 );

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

  •                                                                  *
  •                        Function Prototype                        *
  •                                                                  *

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

static void ListModule ( void ); static BOOLEAN LocateNtdllEntry ( void ); static void PrintWin32Error ( char *message, DWORD dwMessageId ); static void PrintZwError ( char *message, NTSTATUS status );

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

  •                                                                  *
  •                        Static Global Var                         *
  •                                                                  *

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

/*

  • 由ntdll.dll输出的Native API函数指针 */ static ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = NULL; static RTLNTSTATUSTODOSERROR RtlNtStatusToDosError = NULL;

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

static void ListModule ( void ) { NTSTATUS status; PSYSTEM_MODULE_INFORMATION module = 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 ListModule_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 ListModule_return;
}
module = ( PSYSTEM_MODULE_INFORMATION )( ( PULONG )buf + 1 );
n      = *( ( PULONG )buf );
for ( i = 0; i < n; i++ )
{
    printf( "0x%08X %s\n", module[i].Base, module[i].ImageName + module[i].ModuleNameOffset );
}

ListModule_return:

if ( buf != NULL )
{
    free( buf );
    buf = NULL;
}
return;

} /* end of ListModule */

/*

  • 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 ( !( 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 */

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 */

int main ( int argc, char * argv[] ) { if ( FALSE == LocateNtdllEntry() ) { fprintf( stderr, "LocateNtdllEntry() failed\n" ); return( EXIT_FAILURE ); } ListModule(); return( EXIT_SUCCESS ); } /* end of main */

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


☆ 在kd中转换线性地址到物理地址

我就不生造概念了,参看<<IA-32 Software Developer's Manual. Volume 3>>([9]) 的3.1小节,下面是Intel的标准定义:

逻辑地址

段选择子(16-bits):段内偏移(32-bits)

也叫远指针。程序中寻址时只能使用逻辑地址。没有办法禁用段机制,但有办法
禁用分页机制。

线性地址

逻辑地址经GDT、LDT转换后得到线性地址。

物理地址

在分页机制启用的情况下,线性地址经分页机制转换后得到物理地址,否则线性
地址直接等于物理地址。

Windows内核启用了分页机制。

基本平坦模式(Basic Flat Model,卷III的3.2.1小节)

CS、SS、DS、ES选择子可能不一样,但其对应的段描述符中基址与段限相同,均
覆盖整个4G空间,换句话说基址是0x00000000,段限是0xFFFFFFFF。

这是Windows内核所采用的内存模式。由于段基址是0x00000000,此时逻辑地址
的段内偏移(32-bits)直接对应线性地址。

IA-32架构上的Solaris、*BSD、Linux等等,其内核都采用基本平坦模式。发生
函数调用时,只有EIP压栈,而没有CS。这也是那些Exploit Code只考虑段内偏
移,从未考虑段选择子的原因。

受保护的平坦模式(Protected Flat Model,卷III的3.2.2小节)

这个就不解释了,反正Windows内核用的不是这种内存模式。

好了,我们需要澄清的概念就这么一点点,省得大家引用的不是同一个概念,最后出 来一笔烂帐。

顺便提一下,dg命令可以直接显示某个选择子对应的段描述符:


kd> dg cs Selector Base Limit Type DPL Size Gran Pres


0008 00000000 ffffffff Code RE 0 Big Page P kd> dg ds Selector Base Limit Type DPL Size Gran Pres


0023 00000000 ffffffff Data RW 3 Big Page P kd> r gdtr,cs,ds gdtr=8003f000 cs=00000008 ds=00000023 kd> db @gdtr+(@cs&fff8) L8 8003f008 ff ff 00 00 00 9a cf 00 ........ kd> db @gdtr+(@ds&fff8) L8 8003f020 ff ff 00 00 00 f2 cf 00 ........

现在演示在kd中如何将线性地址手工转换成物理地址,以IDTR为例:


kd> r idtr (IDTR中是线性地址,不是逻辑地址) idtr=8003f400 kd> .formats 8003f400 Evaluate expression: Hex: 8003f400 Decimal: -2147224576 Octal: 60000772000 Binary: 10000000 00000011 11110100 00000000 Chars: .... Time: ***** Invalid Float: low -3.63037e-040 high -1.#QNAN Double: -1.#QNAN

将0x8003f400按分页机制切割成三部分(10-bits、10-bits、12-bits):

1000000000 0000111111 010000000000 0x200 0x03f 0x400

kd> r cr3 (CR3中是物理地址) cr3=00039000

页目录位于物理地址0x00039000,对应的PDE位于物理地址0x00039800:

0x00039000 + 0x200 * 4 = 0x00039800

kd> !dd 00039800 L1

39800 0003b163

页表位于物理地址0x0003b000,对应的PTE位于物理地址0x0003b0fc:

0x0003b163 & 0xfffff000 + 0x03f * 4 = 0x0003b0fc

kd> !dd 0003b0fc L1

3b0fc 0003f163

IDTR中的线性地址0x8003f400最终转换成物理地址0x0003f400:

0x0003f163 & 0xfffff000 + 0x400 = 0x0003f400

上述步骤完全是分页机制的手工再现,其中!dd以物理地址为参数。下面演示在kd中 另一种手工转换办法,仍以IDTR为例:


kd> r idtr idtr=8003f400 kd> r cr3 cr3=00039000 kd>

将0x8003f400按分页机制切割成三部分(10-bits、10-bits、12-bits):

1000000000 0000111111 010000000000 0x200 0x03f 0x400

对应的PTE位于线性地址0xc02000fc:

0xc0000000 + 0x200 * 0x1000 + 0x03f * 4 = 0xc02000fc

kd> dd c02000fc L1 c02000fc 0003f163

IDTR中的线性地址0x8003f400最终转换成物理地址0x0003f400:

0x0003f163 & 0xfffff000 + 0x400 = 0x0003f400

这里基于一个重要概念,页表被映射到线性地址0xc0000000,页目录被映射到线性地 址0xc0300000,一个页表占4096(0x1000)字节,一个PDE/PTE占4字节。

接下来演示第三种手工转换办法:


kd> !pte 8003f400 VA 8003f400 (如果这里的显示与命令行参数不同,不要相信输出) PDE at C0300800 PTE at C02000FC contains 0003B163 contains 0003F163 pfn 3b -G-DA--KWV pfn 3f -G-DA--KWV

线性地址0x8003f400对应的PDE位于线性地址0xc0300800,对应的PTE位于线性地址0x c02000fc,最终物理地址的PFN(20-bits)为0x0003f,最终物理地址为0x0003f400:

0x0003f << 0n12 + 0x8003f400 & 0xfffff000 = 0x0003f400

使用!pte时,如果VA后的地址与命令行参数不同,就不要相信这条命令的输出,似乎 与PDE/PTE的重叠相关,也可能与Prototype PTE相关,以我目前的知识没有继续深究 此处,有机会再来学习。在转换线性地址0xc0300000时可能会碰上这种情况。此时应 使用前两种手工转换办法,尤以第一种手工转换办法最可靠。

还有第四种办法,是!vtop命令,号称可以这样用:


kd> !vtop 00039000 8003f400 Cannot find HARDWARE_PTE

之所以失败,可能与我远程调试VMware中XP SP1有关吧。kd -kl进入本机调试后不能 访问所有寄存器。不确认两台物理机器之间远程调试环境中是否可以使用!vtop。

现在验证"IDTR中的线性地址0x8003f400最终转换成物理地址0x0003f400":


kd> !idt

Dumping IDT:

30: 806ceb10 31: 81137c3c fdf5b4a2 (KINTERRUPT 81137c00) 38: 806c8110 3b: 811284dc fdcefd00 (KINTERRUPT 811284a0) 3c: 81112b64 fdf62800 (KINTERRUPT 81112b28) 3e: 81200ce4 fdda5fbe (KINTERRUPT 81200ca8) 3f: 812021bc fde3d232 (KINTERRUPT 81202180)

kd> db 8003f400 + 30 * 8 L8 8003f580 10 eb 08 00 00 8e 6c 80 ......l. kd> !db 0003f400 + 30 * 8 L8

3f580 10 eb 08 00 00 8e 6c 80 ......l.<|......


参看Intel卷III的4.8.3小节,门描述符结构类型定义如下:


Gate STRUC OffsetL DW 0 ; 32-bit偏移的低16位 Selector DW 0 ; 段选择子 PCount DB 0 ; 参数个数 GType DB 0 ; 类型 OffsetH DW 0 ; 32-bit偏移的高16位 Gate ENDS

☆ 对[0x80000000, 0xa0000000)区间上指定线性地址进行指定长度的16进制dump

webcrazy给出过2K的MmGetPhysicalAddress()部分反汇编代码([4]):


kd> u nt!MmGetPhysicalAddress l 30 ntoskrnl!MmGetPhysicalAddress: 801374e0 56 push esi 801374e1 8b742408 mov esi,[esp+0x8] 801374e5 33d2 xor edx,edx 801374e7 81fe00000080 cmp esi,0x80000000 801374ed 722c jb ntoskrnl!MmGetPhysicalAddress+0x2b (8013751b) 801374ef 81fe000000a0 cmp esi,0xa0000000 801374f5 7324 jnb ntoskrnl!MmGetPhysicalAddress+0x2b (8013751b) 801374f7 39153ce71780 cmp [ntoskrnl!MmKseg2Frame (8017e73c)],edx 801374fd 741c jz ntoskrnl!MmGetPhysicalAddress+0x2b (8013751b) 801374ff 8bc6 mov eax,esi 80137501 c1e80c shr eax,0xc 80137504 25ffff0100 and eax,0x1ffff 80137509 6a0c push 0xc 8013750b 59 pop ecx 8013750c e8d3a7fcff call ntoskrnl!_allshl (80101ce4) 80137511 81e6ff0f0000 and esi,0xfff 80137517 03c6 add eax,esi 80137519 eb17 jmp ntoskrnl!MmGetPhysicalAddress+0x57 (80137532) 8013751b 8bc6 mov eax,esi 8013751d c1e80a shr eax,0xa 80137520 25fcff3f00 and eax,0x3ffffc 80137525 2d00000040 sub eax,0x40000000 8013752a 8b00 mov eax,[eax] 8013752c a801 test al,0x1 8013752e 7506 jnz ntoskrnl!MmGetPhysicalAddress+0x44 (80137536) 80137530 33c0 xor eax,eax 80137532 5e pop esi 80137533 c20400 ret 0x4

在[0x80000000, 0xa0000000)这个左闭右开区间上线性地址到物理地址的转换很简单:

物理地址 = 线性地址 & 0x1fffffff

但是在En XP SP1上我得到了不同的反汇编代码:


kd> u nt!MmGetPhysicalAddress l 30 nt!MmGetPhysicalAddress: 804e1dea 8b4c2404 mov ecx,[esp+0x4] 804e1dee 8bc1 mov eax,ecx 804e1df0 c1e814 shr eax,0x14 804e1df3 25fc0f0000 and eax,0xffc 804e1df8 8b90000030c0 mov edx,[eax+0xc0300000] 804e1dfe 8bc2 mov eax,edx 804e1e00 66258100 and ax,0x81 804e1e04 3c81 cmp al,0x81 804e1e06 0f850c780200 jne nt!MmGetPhysicalAddress+0x2f (80509618) 804e1e0c 8bc1 mov eax,ecx 804e1e0e c1e80c shr eax,0xc 804e1e11 25ff030000 and eax,0x3ff 804e1e16 c1ea0c shr edx,0xc 804e1e19 03c2 add eax,edx 804e1e1b 33d2 xor edx,edx 804e1e1d 0fa4c20c shld edx,eax,0xc 804e1e21 c1e00c shl eax,0xc 804e1e24 81e1ff0f0000 and ecx,0xfff 804e1e2a 03c1 add eax,ecx 804e1e2c c20400 ret 0x4 nt!MmAllocateContiguousMemorySpecifyCache: 804e1e2f 55 push ebp 804e1e30 8bec mov ebp,esp 804e1e32 51 push ecx 804e1e33 51 push ecx 804e1e34 57 push edi 804e1e35 8d45f8 lea eax,[ebp-0x8] 804e1e38 50 push eax 804e1e39 8d45fc lea eax,[ebp-0x4] 804e1e3c 50 push eax 804e1e3d e8b6e60200 call nt!RtlGetCallersAddress (805104f8) 804e1e42 8b450c mov eax,[ebp+0xc] 804e1e45 8b5510 mov edx,[ebp+0x10] 804e1e48 b10c mov cl,0xc 804e1e4a e82bee0200 call nt!_allshr (80510c7a) 804e1e4f 8bf8 mov edi,eax 804e1e51 b8ff0f0000 mov eax,0xfff 804e1e56 85450c test [ebp+0xc],eax 804e1e59 0f8599620300 jne nt!MmAllocateContiguousMemorySpecifyCache+0x2c (805180f8) 804e1e5f 85451c test [ebp+0x1c],eax 804e1e62 0f8596620300 jne nt!MmAllocateContiguousMemorySpecifyCache+0x32 (805180fe) 804e1e68 8b451c mov eax,[ebp+0x1c] 804e1e6b 8b5520 mov edx,[ebp+0x20] 804e1e6e 56 push esi 804e1e6f b10c mov cl,0xc 804e1e71 e804ee0200 call nt!_allshr (80510c7a) 804e1e76 8b5518 mov edx,[ebp+0x18] 804e1e79 8bf0 mov esi,eax 804e1e7b 8b4514 mov eax,[ebp+0x14]

也就是说,一进入MmGetPhysicalAddress()便直奔0xc0300000查页目录去了,与2K有 区别。

在水木清华MSDN版与一众人等讨论了一下,webcrazy建议继续使用上述转换办法。从 更前面"IDTR中的线性地址0x8003f400最终转换成物理地址0x0003f400"也可以看出, XP可能不直接在MmGetPhysicalAddress()中作此类优化,而是在设置页目录、页表的 时候作过优化。与2K相比,这样优化更有利于将来可能出现的变动。反正我也没有其 它更好的办法完成线性地址到物理地址的转换,只能冒险一试。

下面这个演示程序试图对[0x80000000, 0xa0000000)区间上指定线性地址进行指定长 度的16进制dump。没有做太多边界条件检查,比如我没有理会线性地址加上长度之后 已经越过0x9fffffff的情形,谁让这只是一个演示程序呢。显然不能相信过长的dump 输出。


/*

  • For x86/EWindows XP SP1 & VC 7
  • cl memdump.c /Os /G6 /W3
  • Usage: memdump [-h] [-a Address] [-l Length] */

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

  •                                                                  *
  •                           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)

/*


  • 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>>的3.5.1小
  • 节,GDTR/IDTR均适用,这里假设是IA-32架构 / #pragma pack(push, 1) typedef struct _PSEUDODESCRIPTOR { unsigned short int limit; unsigned int base; } PSEUDODESCRIPTOR; #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 );

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

  •                                                                  *
  •                        Function Prototype                        *
  •                                                                  *

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

static VOID InitializeObjectAttributes ( OUT POBJECT_ATTRIBUTES InitializedAttributes, IN PUNICODE_STRING ObjectName, IN ULONG Attributes, IN HANDLE RootDirectory, IN PSECURITY_DESCRIPTOR SecurityDescriptor ); static BOOLEAN LocateNtdllEntry ( 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 PHYSICAL_ADDRESS PrivateMmGetPhysicalAddress ( IN PVOID LinearAddress ); 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;

static SYSTEM_INFO system_info;

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

/*

  • 在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 */

/*

  • 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 */

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 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 */

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, "\n" "Usage: %s [-h] [-a Address] [-l Length]\n" "\n" "Address\n" "\n" " 0x80000000\n" " ... ...\n" " 0x9fffffff\n", arg ); exit( EXIT_FAILURE ); } / end of usage */

int main ( int argc, char * argv[] ) { int i; PSEUDODESCRIPTOR GDTR; WORD CURRENT_CS, CURRENT_DS, CURRENT_ES, CURRENT_SS, CURRENT_FS; HANDLE mem = NULL; PVOID orig_LinearAddress = NULL; PVOID LinearAddress = NULL; ULONG orig_MapSize = 0; ULONG MapSize = 0; PHYSICAL_ADDRESS orig_PhysicalAddress; PHYSICAL_ADDRESS PhysicalAddress;

/*
 * 从argv[1]开始循环处理命令行参数
 */
for ( i = 1; i < argc; i++ )
{
    /*
     * 同时支持-和/两种引入命令行参数的方式
     */
    if ( ( argv[i][0] == '-' ) || ( argv[i][0] == '/' ) )
    {
        /*
         * 在这个字节上,大小写不敏感
         */
        switch ( tolower( argv[i][1] ) )
        {
        case 'a':
            /*
             * 线性地址
             */
            orig_LinearAddress = ( PVOID )strtoul( argv[++i], NULL, 0 );
            break;
        case 'l':
            orig_MapSize       = strtoul( argv[++i], NULL, 0 );
            break;
        case 'h':
        case '?':
        default:
            usage( argv[0] );
        }  /* end of switch */
    }
    else
    {
        usage( argv[0] );
    }
}  /* end of for */
if ( NULL == orig_LinearAddress )
{
    ZeroMemory( &GDTR, sizeof( GDTR ) );
    /*
     * 获取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 );
    orig_LinearAddress = ( PVOID )GDTR.base;
}
if ( ( ULONG )orig_LinearAddress < 0x80000000 || ( ULONG )orig_LinearAddress >= 0xa0000000 )
{
    fprintf( stderr, "Checking your [-a 0x%08X]\n", ( unsigned int )orig_LinearAddress );
    return( EXIT_FAILURE );
}
if ( FALSE == LocateNtdllEntry() )
{
    fprintf( stderr, "LocateNtdllEntry() 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 == orig_MapSize )
{
    orig_MapSize = system_info.dwPageSize;
}
MapSize              = orig_MapSize;
ZeroMemory( &orig_PhysicalAddress, sizeof( orig_PhysicalAddress ) );
ZeroMemory( &PhysicalAddress, sizeof( PhysicalAddress ) );
orig_PhysicalAddress = PrivateMmGetPhysicalAddress( orig_LinearAddress );
if ( orig_PhysicalAddress.LowPart == 0 )
{
    fprintf( stderr, "orig_PhysicalAddress.LowPart == 0\n" );
    return( EXIT_FAILURE );
}
PhysicalAddress      = orig_PhysicalAddress;
/*
 * 为读写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 );
}
if ( TRUE == MapPhysicalMemory( mem, &LinearAddress, &MapSize, &PhysicalAddress, PAGE_READWRITE ) )
{
    orig_LinearAddress = ( unsigned char * )LinearAddress + ( orig_PhysicalAddress.LowPart - PhysicalAddress.LowPart );
    outputBinary( stderr, ( const unsigned char * )orig_LinearAddress, ( const size_t )orig_MapSize );
    UnmapPhysicalMemory( LinearAddress );
}
if ( mem != NULL )
{
    ZwClose( mem );
    mem = NULL;
}
return( EXIT_SUCCESS );

} /* end of main */

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


这是memdump从用户空间dump全局描述符表前64字节与kd从内核空间dump的对比:


memdump -a 0x8003f000 -l 64 PageSize = 0x00001000 AllocationGranularity = 0x00010000 byteArray [ 64 bytes ] -> 00000000 00 00 00 00 00 00 00 00-FF FF 00 00 00 9A CF 00 ................ 00000010 FF FF 00 00 00 92 CF 00-FF FF 00 00 00 FA CF 00 ................ 00000020 FF FF 00 00 00 F2 CF 00-AB 20 00 20 04 8B 00 80 ......... . .... 00000030 01 00 00 F0 DF 92 C0 FF-FF 0F 00 E0 FD F3 40 7F ..............@.

kd> !db 0003f000 L0n64

3f000 00 00 00 00 00 00 00 00-ff ff 00 00 00 9a cf 00 ................

3f010 ff ff 00 00 00 92 cf 00-ff ff 00 00 00 fa cf 00 ................

3f020 ff ff 00 00 00 f2 cf 00-ab 20 00 20 04 8b 00 80 ......... . ....

3f030 01 00 00 f0 df 92 c0 ff-ff 0f 00 00 00 f3 40 00 ..............@.


由于在VMware Workstation 3.2中无法正确完成sgdt指令,只好先在kd中用"r gdtr" 获取GDTR中保存的线性基址,然后在memdump命令行上指定这个线性地址。如果是真 实主机,不必如此麻烦。

注意第7个(从0计)描述符的段基址从0x7ffde000变成了0x00000000。

参看Intel卷III的3.4.3小节,段描述符结构类型定义如下:


Desc STRUC LimitL DW 0 ; 段界限(bit0-15) BaseL DW 0 ; 段基地址(bit0-15) BaseM DB 0 ; 段基地址(bit16-23) Attributes DB 0 ; 段属性 LimitH DB 0 ; 段界限(bit16-19)(含段属性的高4位) BaseH DB 0 ; 段基地址(bit24-31) Desc ENDS

☆ 利用调用门从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代码 没有任何意义,后者对页级保护来说统称为系统特权级,而前者称为用户特权级。系 统特权级代码对任意页拥有读、写、执行权限。当然,这是CR0的WP标志(bit-16)为0 时的情形,如果WP标志为1,写操作情形有变,参看Inet卷III的2.5小节。

本小节的目标很简单,在不写驱动程序的情况下读访问任意线性地址,而不是局限在 [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]指定 的地址导致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 ) ); GATEDESCRIPTOR new_callgate;

/*
 * 设置成DPL等于3的调用门
 */
ZeroMemory( &new_callgate, sizeof( new_callgate ) );
new_callgate.offset_low      = ( WORD )( CodeOffset & 0x0000ffff );
new_callgate.selector        = RING0_CODE_SELECTOR;
new_callgate.parameter_count = 2;
new_callgate.reserved        = 0;
new_callgate.type            = 12;
new_callgate.s               = 0;
new_callgate.dpl             = 3;
new_callgate.p               = 1;
new_callgate.offset_high     = ( WORD )( CodeOffset >> 16 );
/*
 * 搜索空闲描述符
 */
for ( ; ( ULONG )callgate > Gdtrbase; callgate-- )
{
    /*
     * GDT中不一定都是门描述符,但所有类型的描述符P位在同一位域,并且
     * 意义接近。
     */
    if ( 0 == callgate->p )
    {
        /*
         * 备份即将被征用的"空闲"描述符
         */
        memcpy( orig_callgate, callgate, sizeof( GATEDESCRIPTOR ) );
        /*
         * 征用"空闲"描述符
         */
        memcpy( callgate, &new_callgate, sizeof( GATEDESCRIPTOR ) );
        return( callgate );
    }
    /*
     * 考虑上次dump.exe在Ring 0异常终止的情形,被征用的"空闲"描述符得
     * 不到释放。
     */
    if ( 0 == memcmp( callgate, &new_callgate, sizeof( GATEDESCRIPTOR ) ) )
    {
        memcpy( orig_callgate, callgate, sizeof( GATEDESCRIPTOR ) );
        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 );
        /*
         * 释放被征用的"空闲"描述符
         */
        orig_callgate.p          = 0;
        memcpy( callgate, &orig_callgate, sizeof( GATEDESCRIPTOR ) );
    }
    /*
     * 解除对GDT的映射
     */
    UnmapPhysicalMemory( GdtMapAddress );
}
if ( mem != NULL )
{
    ZwClose( mem );
    mem = NULL;
}
return( EXIT_SUCCESS );

} /* end of main */

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


顺便说一句,对于gcc,为了指定naked属性,语法如下:


void attribute((naked)) func ( ... ) { /* * prologue */ asm (" ... ... ");

... ...

/*
 * epilogue
 */
__asm__
("
    ... ...
");

} /* end of func */

或者

void func ( ... ) attribute ((naked));

void func ( ... ) { ... ... } /* end of func */

☆ 利用Tool Help Library枚举进程/模块

这可能是最简单(也最没用)的枚举进程/模块方式,我当然拣最容易的开练,后面再 逐层向内核递降,演练其它方式。listprocess.exe输出类似这个效果:


PID PPID Path 0x00000000 0x00000000 [System Process] Base Size Path 0x00400000 0x0000A000 J:\onlytemp\listprocess.exe 0x77F50000 0x000A7000 H:\WINDOWS\System32\ntdll.dll 0x77E60000 0x000E6000 H:\WINDOWS\system32\kernel32.dll 0x00000004 0x00000000 System CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, 0x00000004 ) failed: 拒绝访问。 0x00000154 0x00000004 smss.exe CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, 0x00000154 ) failed: 拒绝访问。 0x000001C8 0x00000154 csrss.exe CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, 0x000001C8 ) failed: 拒绝访问。 0x000001E0 0x00000154 winlogon.exe CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, 0x000001E0 ) failed: 拒绝访问。 ... ... 0x0000047C 0x00000450 explorer.exe Base Size Path 0x01000000 0x000F8000 H:\WINDOWS\Explorer.EXE 0x77F50000 0x000A7000 H:\WINDOWS\System32\ntdll.dll 0x77E60000 0x000E6000 H:\WINDOWS\system32\kernel32.dll 0x77C10000 0x00053000 H:\WINDOWS\system32\msvcrt.dll 0x77DD0000 0x0008D000 H:\WINDOWS\system32\ADVAPI32.dll ... ... ... ... 0x000000C8 0x0000047C cmd.exe Base Size Path 0x4AD00000 0x0005E000 H:\WINDOWS\system32\cmd.exe 0x77F50000 0x000A7000 H:\WINDOWS\System32\ntdll.dll 0x77E60000 0x000E6000 H:\WINDOWS\system32\kernel32.dll 0x77C10000 0x00053000 H:\WINDOWS\system32\msvcrt.dll 0x77D40000 0x00086000 H:\WINDOWS\system32\USER32.dll ... ... ... ...

注意到对某些进程调用CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, ... )失败。 不知是我用法不当,还是Tool Help Library实现中的内在局限性所致,请指点。当 前帐号已经是管理员权限。

顺便问一句,我在Unix/Bash下可以"prog 1> out.txt 2>&1",使得stdout、stderr 一块转向out.txt,现在Windows/cmd下,如何达到同样效果。单独的"1> out.txt"、 "2> out.txt"都没问题,可如何同时转向out.txt呢。

cygwin@smth: Windows/cmd中是一样的用法。


/*

  • For x86/EWindows XP SP1 & VC 7
  • cl listprocess.c /Os /G6 /W3 */

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

  •                                                                  *
  •                           Head File                              *
  •                                                                  *

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

#include <stdio.h> #include <stdlib.h> #include <windows.h> #include <tlhelp32.h>

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

  •                                                                  *
  •                           Macro                                  *
  •                                                                  *

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

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

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

  •                                                                  *
  •                        Function Prototype                        *
  •                                                                  *

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

static void PrintModuleList ( DWORD pid ); static void PrintProcessList ( void ); static void PrintWin32Error ( char *message, DWORD dwMessageId );

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

  •                                                                  *
  •                        Static Global Var                         *
  •                                                                  *

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

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

static void PrintModuleList ( DWORD pid ) { HANDLE snapshot = INVALID_HANDLE_VALUE; MODULEENTRY32 me; char errmsg[256];

/*
 * Header : Declared in Tlhelp32.h.
 * Library: Use Kernel32.lib.
 *
 * HANDLE WINAPI CreateToolhelp32Snapshot
 * (
 *     DWORD dwFlags,
 *     DWORD th32ProcessID
 * );
 */
snapshot  = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, pid );
if ( snapshot == INVALID_HANDLE_VALUE )
{
    sprintf( errmsg, "CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, 0x%08X ) failed", pid );
    PrintWin32Error( errmsg, GetLastError() );
    goto PrintModuleList_exit;
}
ZeroMemory( &me, sizeof( me ) );
/*
 * 调用Module32First()之前,必须初始化该成员。
 */
me.dwSize = sizeof( me );
if ( FALSE == Module32First( snapshot, &me ) )
{
    PrintWin32Error( "Module32First() failed", GetLastError() );
    goto PrintModuleList_exit;
}
printf( "    Base       Size       Path\n" );
do
{
    /*
     * typedef struct tagMODULEENTRY32
     * {
     *     DWORD    dwSize;
     *     DWORD    th32ModuleID;
     *     DWORD    th32ProcessID;
     *     DWORD    GlblcntUsage;
     *     DWORD    ProccntUsage;
     *     BYTE    *modBaseAddr;
     *     DWORD    modBaseSize;
     *     HMODULE  hModule;
     *     TCHAR    szModule[MAX_MODULE_NAME32 + 1];
     *     TCHAR    szExePath[MAX_PATH];
     * } MODULEENTRY32;
     */
    printf( "    0x%08X 0x%08X %s\n", me.modBaseAddr, me.modBaseSize, me.szExePath );
}
while ( TRUE == Module32Next( snapshot, &me ) );

PrintModuleList_exit:

if ( snapshot != INVALID_HANDLE_VALUE )
{
    CloseHandle( snapshot );
    snapshot = INVALID_HANDLE_VALUE;
}
return;

} /* end of PrintModuleList */

static void PrintProcessList ( void ) { HANDLE snapshot = INVALID_HANDLE_VALUE; PROCESSENTRY32 pe;

snapshot  = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
if ( snapshot == INVALID_HANDLE_VALUE )
{
    PrintWin32Error( "CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ) failed", GetLastError() );
    goto PrintProcessList_exit;
}
ZeroMemory( &pe, sizeof( pe ) );
/*
 * 调用Process32First()之前,必须初始化该成员。
 */
pe.dwSize = sizeof( pe );
if ( FALSE == Process32First( snapshot, &pe ) )
{
    PrintWin32Error( "Process32First() failed", GetLastError() );
    goto PrintProcessList_exit;
}
printf( "PID        PPID       Path\n" );
do
{
    /*
     * typedef struct tagPROCESSENTRY32
     * {
     *     DWORD     dwSize;
     *     DWORD     cntUsage;
     *     DWORD     th32ProcessID;
     *     ULONG_PTR th32DefaultHeapID;
     *     DWORD     th32ModuleID;
     *     DWORD     cntThreads;
     *     DWORD     th32ParentProcessID;
     *     LONG      pcPriClassBase;
     *     DWORD     dwFlags;
     *     TCHAR     szExeFile[MAX_PATH];
     * } PROCESSENTRY32;
     */
    printf( "0x%08X 0x%08X %s\n",
            pe.th32ProcessID, pe.th32ParentProcessID,
            pe.szExeFile );
    PrintModuleList( pe.th32ProcessID );
}
while ( TRUE == Process32Next( snapshot, &pe ) );

PrintProcessList_exit:

if ( snapshot != INVALID_HANDLE_VALUE )
{
    CloseHandle( snapshot );
    snapshot = INVALID_HANDLE_VALUE;
}
return;

} /* end of PrintProcessList */

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 */

int main ( int argc, char * argv[] ) { PrintProcessList(); return( EXIT_SUCCESS ); } /* end of main */

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


☆ SE_DEBUG_NAME权限

listprocess.exe在试图枚举smss.exe、csrss.exe、winlogon.exe、services.exe、 lsass.exe等进程的模块时得到一个错误提示,拒绝访问。这些进程特殊在什么地方, 可以参看Inside 2K([5])中如下章节:

Chapter 2/System Architecture - Key System Components - System Processes Chapter 4/Startup and Shutdown - Boot Process - Smss, Csrss, and Winlogon Chapter 8/Security

如果看中文版,对应如下章节:

2.5.8 系统进程 4.1.4 Smss、Csrss和Winlogon 8 安全

rain vrain@smth解释了这个"拒绝访问"的问题,指出可以利用SE_DEBUG_NAME权限 打开(OpenProcess())任一进程。关于SE_DEBUG_NAME,还可参考资源[7]、[8],其中 说是使用PSAPI枚举这些进程的模块时不存在类似问题,回头验证一下。Keith Brown 在MSJ上有一篇不错的短文,从程序员角度对Privileges进行了介绍([13])。rain给 出了演示代码,此外可参看Tomas Restrepo [email protected]写的"Adjusting Process Token Privileges"([12])。这些都是很不错的资源,列举在此,节省一点 后来者的时间。

下面将rain的演示代码应用到listprocess_full.c中,以枚举特殊进程的模块。现在 除了这样一条错误信息之外,不再有"拒绝访问"的错误信息。

0x00000004 0x00000000 System CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, 0x00000004 ) failed: 存储空间不足,无法处理此命令。


/*

  • For x86/EWindows XP SP1 & VC 7
  • cl listprocess_full.c /Os /G6 /W3
  • rain vrain@smth */

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

  •                                                                  *
  •                           Head File                              *
  •                                                                  *

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

#include <stdio.h> #include <stdlib.h> #include <windows.h> #include <tlhelp32.h>

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

  •                                                                  *
  •                           Macro                                  *
  •                                                                  *

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

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

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

  •                                                                  *
  •                        Function Prototype                        *
  •                                                                  *

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

static BOOL DisableCurrentProcessDebugPrivilege ( void ); static BOOL EnableCurrentProcessDebugPrivilege ( void ); static void PrintModuleList ( DWORD pid ); static void PrintProcessList ( void ); static void PrintWin32Error ( char *message, DWORD dwMessageId ); static BOOL SetCurrentProcessPrivilege ( LPCTSTR PrivilegeName, BOOL EnableFlag ); static BOOL SetPrivilege ( HANDLE TokenHandle, LPCTSTR PrivilegeName, BOOL EnableFlag );

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

  •                                                                  *
  •                        Static Global Var                         *
  •                                                                  *

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

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

static BOOL DisableCurrentProcessDebugPrivilege ( void ) { return( SetCurrentProcessPrivilege( SE_DEBUG_NAME, FALSE ) ); } /* end of DisableCurrentProcessDebugPrivilege */

static BOOL EnableCurrentProcessDebugPrivilege ( void ) { return( SetCurrentProcessPrivilege( SE_DEBUG_NAME, TRUE ) ); } /* end of EnableCurrentProcessDebugPrivilege */

static void PrintModuleList ( DWORD pid ) { HANDLE snapshot = INVALID_HANDLE_VALUE; MODULEENTRY32 me; char errmsg[256];

/*
 * Header : Declared in Tlhelp32.h.
 * Library: Use Kernel32.lib.
 *
 * HANDLE WINAPI CreateToolhelp32Snapshot
 * (
 *     DWORD dwFlags,
 *     DWORD th32ProcessID
 * );
 */
snapshot  = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, pid );
if ( snapshot == INVALID_HANDLE_VALUE )
{
    sprintf( errmsg, "CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, 0x%08X ) failed", pid );
    PrintWin32Error( errmsg, GetLastError() );
    goto PrintModuleList_exit;
}
ZeroMemory( &me, sizeof( me ) );
/*
 * 调用Module32First()之前,必须初始化该成员。
 */
me.dwSize = sizeof( me );
if ( FALSE == Module32First( snapshot, &me ) )
{
    PrintWin32Error( "Module32First() failed", GetLastError() );
    goto PrintModuleList_exit;
}
printf( "    Base       Size       Path\n" );
do
{
    /*
     * typedef struct tagMODULEENTRY32
     * {
     *     DWORD    dwSize;
     *     DWORD    th32ModuleID;
     *     DWORD    th32ProcessID;
     *     DWORD    GlblcntUsage;
     *     DWORD    ProccntUsage;
     *     BYTE    *modBaseAddr;
     *     DWORD    modBaseSize;
     *     HMODULE  hModule;
     *     TCHAR    szModule[MAX_MODULE_NAME32 + 1];
     *     TCHAR    szExePath[MAX_PATH];
     * } MODULEENTRY32;
     */
    printf( "    0x%08X 0x%08X %s\n", me.modBaseAddr, me.modBaseSize, me.szExePath );
}
while ( TRUE == Module32Next( snapshot, &me ) );

PrintModuleList_exit:

if ( snapshot != INVALID_HANDLE_VALUE )
{
    CloseHandle( snapshot );
    snapshot = INVALID_HANDLE_VALUE;
}
return;

} /* end of PrintModuleList */

static void PrintProcessList ( void ) { HANDLE snapshot = INVALID_HANDLE_VALUE; PROCESSENTRY32 pe;

snapshot  = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
if ( snapshot == INVALID_HANDLE_VALUE )
{
    PrintWin32Error( "CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ) failed", GetLastError() );
    goto PrintProcessList_exit;
}
ZeroMemory( &pe, sizeof( pe ) );
/*
 * 调用Process32First()之前,必须初始化该成员。
 */
pe.dwSize = sizeof( pe );
if ( FALSE == Process32First( snapshot, &pe ) )
{
    PrintWin32Error( "Process32First() failed", GetLastError() );
    goto PrintProcessList_exit;
}
printf( "PID        PPID       Path\n" );
do
{
    /*
     * typedef struct tagPROCESSENTRY32
     * {
     *     DWORD     dwSize;
     *     DWORD     cntUsage;
     *     DWORD     th32ProcessID;
     *     ULONG_PTR th32DefaultHeapID;
     *     DWORD     th32ModuleID;
     *     DWORD     cntThreads;
     *     DWORD     th32ParentProcessID;
     *     LONG      pcPriClassBase;
     *     DWORD     dwFlags;
     *     TCHAR     szExeFile[MAX_PATH];
     * } PROCESSENTRY32;
     */
    printf( "0x%08X 0x%08X %s\n",
            pe.th32ProcessID, pe.th32ParentProcessID,
            pe.szExeFile );
    PrintModuleList( pe.th32ProcessID );
}
while ( TRUE == Process32Next( snapshot, &pe ) );

PrintProcessList_exit:

if ( snapshot != INVALID_HANDLE_VALUE )
{
    CloseHandle( snapshot );
    snapshot = INVALID_HANDLE_VALUE;
}
return;

} /* end of PrintProcessList */

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 BOOL SetCurrentProcessPrivilege ( LPCTSTR PrivilegeName, BOOL EnableFlag ) { HANDLE TokenHandle = ( HANDLE )-1; BOOL ret = TRUE;

/*
 * Header : Declared in Winbase.h; include Windows.h.
 * Library: Use Advapi32.lib.
 *
 * BOOL OpenProcessToken
 * (
 *     HANDLE  ProcessHandle,
 *     DWORD   DesiredAccess,
 *     PHANDLE TokenHandle
 * );
 */
if ( FALSE == OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &TokenHandle ) )
{
    PrintWin32Error( "OpenProcessToken() failed", GetLastError() );
    ret = FALSE;
    goto SetCurrentProcessPrivilege_exit;
}
ret = SetPrivilege( TokenHandle, PrivilegeName, EnableFlag );

SetCurrentProcessPrivilege_exit:

if ( TokenHandle != ( HANDLE )-1 )
{
    CloseHandle( TokenHandle );
    TokenHandle = ( HANDLE )-1;
}
return( ret );

} /* end of SetCurrentProcessPrivilege */

static BOOL SetPrivilege ( HANDLE TokenHandle, LPCTSTR PrivilegeName, BOOL EnableFlag ) { DWORD error; BOOL ret = TRUE; /* * * typedef struct _TOKEN_PRIVILEGES * { * DWORD PrivilegeCount; * LUID_AND_ATTRIBUTES Privileges[]; * } TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES; * * typedef struct _LUID_AND_ATTRIBUTES * { * LUID Luid; * DWORD Attributes; * } LUID_AND_ATTRIBUTES, *PLUID_AND_ATTRIBUTES; */ TOKEN_PRIVILEGES tp = { 1, { { { 0, 0 }, 0 } } };

if ( EnableFlag == TRUE )
{
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
}
/*
 * BOOL LookupPrivilegeValue
 * (
 *     LPCTSTR lpSystemName,
 *     LPCTSTR lpName,
 *     PLUID   lpLuid
 * );
 *
 * 第二形参的可取值在winnt.h中有定义,"NT Defined Privileges"
 */
if ( FALSE == LookupPrivilegeValue( NULL, PrivilegeName, &tp.Privileges[0].Luid ) )
{
    PrintWin32Error( "LookupPrivilegeValue() failed", GetLastError() );
    ret = FALSE;
    goto SetPrivilege_exit;
}
/*
 * BOOL AdjustTokenPrivileges
 * (
 *     HANDLE            TokenHandle,
 *     BOOL              DisableAllPrivileges,
 *     PTOKEN_PRIVILEGES NewState,
 *     DWORD             BufferLength,
 *     PTOKEN_PRIVILEGES PreviousState,
 *     PDWORD            ReturnLength
 * );
 *
 * The AdjustTokenPrivileges function cannot add new privileges to the
 * access token. It can only enable or disable the token's existing
 * privileges. To determine the token's privileges, call the
 * GetTokenInformation function.
 */
if ( FALSE == AdjustTokenPrivileges( TokenHandle, FALSE, &tp, sizeof( tp ), NULL, NULL ) )
{
    PrintWin32Error( "AdjustTokenPrivileges() failed", GetLastError() );
    ret = FALSE;
    goto SetPrivilege_exit;
}
else
{
    error = GetLastError();
    /*
     * 这种情况带来的误判很隐蔽,务必留心。
     *
     * ERROR_NOT_ALL_ASSIGNED
     */
    if ( ERROR_SUCCESS != error )
    {
        PrintWin32Error( "AdjustTokenPrivileges() failed", error );
        ret = FALSE;
        goto SetPrivilege_exit;
    }
}

SetPrivilege_exit:

return( ret );

} /* end of SetPrivilege */

int main ( int argc, char * argv[] ) { EnableCurrentProcessDebugPrivilege(); PrintProcessList(); DisableCurrentProcessDebugPrivilege(); return( EXIT_SUCCESS ); } /* end of main */

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


☆ 利用PSAPI枚举进程/模块

[7]中提到利用PSAPI可以直接枚举特殊进程的模块,而不需要SE_DEBUG_NAME权限。 我有点将信将疑,索性测试一番。微软Q175030([14])讨论了PSAPI,不过最新版MSDN 已经自带例子。

EnumProc.c不调用EnableCurrentProcessDebugPrivilege()时,得到如下错误信息:

OpenProcess() for PID<0> failed: 参数不正确。 EnumProcessModules() for PID<4> failed: 仅完成部分的 ReadProcessMemoty 或 WriteProcessMemory 请求。 OpenProcess() for PID<456> failed: 拒绝访问。 OpenProcess() for PID<180> failed: 拒绝访问。

后两个进程是:

456 csrss.exe 180 alg.exe

此时可以枚举smss.exe、winlogon.exe等其它特殊进程的模块。增加SE_DEBUG_NAME 权限后,只剩下前两个错误信息。个人认为PSAPI不是太理想,尽管缺省状态下比 Tool Help Library更能枚举特殊进程的模块,但同样不能枚举所有进程的模块,最 终还是需要SE_DEBUG_NAME权限。其次,利用PSAPI无法获取PPID。最后,PSAPI没有 更方便的办法提前获知当前系统中究竟存在多少进程、某个进程究竟存在多少模块, 必须使用一定编程技巧以确保枚举所有进程及某指定进程的所有模块。在这点上,我 讨厌为EnumProcesses()、EnumProcessModules()提供固定的超大数组这种作法,其 实就是讨厌这种作法不能面对未知环境。


/*

  • For x86/EWindows XP SP1 & VC 7
  • cl EnumProc.c /Os /G6 /W3 */

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

  •                                                                  *
  •                           Head File                              *
  •                                                                  *

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

#include <stdio.h> #include <stdlib.h> #include <windows.h> #include <psapi.h>

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

  •                                                                  *
  •                           Macro                                  *
  •                                                                  *

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

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

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

  •                                                                  *
  •                        Function Prototype                        *
  •                                                                  *

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

static BOOL DisableCurrentProcessDebugPrivilege ( void ); static BOOL EnableCurrentProcessDebugPrivilege ( void ); static void PrintModuleInfo ( HANDLE proc, HMODULE module ); static void PrintModuleList ( DWORD pid ); static void PrintProcessList ( void ); static void PrintWin32Error ( char *message, DWORD dwMessageId ); static BOOL SetCurrentProcessPrivilege ( LPCTSTR PrivilegeName, BOOL EnableFlag ); static BOOL SetPrivilege ( HANDLE TokenHandle, LPCTSTR PrivilegeName, BOOL EnableFlag );

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

  •                                                                  *
  •                        Static Global Var                         *
  •                                                                  *

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

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

static BOOL DisableCurrentProcessDebugPrivilege ( void ) { return( SetCurrentProcessPrivilege( SE_DEBUG_NAME, FALSE ) ); } /* end of DisableCurrentProcessDebugPrivilege */

static BOOL EnableCurrentProcessDebugPrivilege ( void ) { return( SetCurrentProcessPrivilege( SE_DEBUG_NAME, TRUE ) ); } /* end of EnableCurrentProcessDebugPrivilege */

static void PrintModuleInfo ( HANDLE proc, HMODULE module ) { MODULEINFO moduleinfo; char path[MAX_PATH];

ZeroMemory( &moduleinfo, sizeof( moduleinfo ) );
/*
 * BOOL GetModuleInformation
 * (
 *     HANDLE       hProcess,   // handle to process
 *     HMODULE      hModule,    // handle to module
 *     LPMODULEINFO lpmodinfo,  // information buffer
 *     DWORD        cb          // size of buffer
 * );
 *
 * typedef struct _MODULEINFO
 * {
 *     LPVOID lpBaseOfDll;
 *     DWORD  SizeOfImage;
 *     LPVOID EntryPoint;
 * } MODULEINFO, *LPMODULEINFO;
 *
 * DWORD GetModuleFileNameEx
 * (
 *     HANDLE hProcess,    // handle to process
 *     HMODULE hModule,    // handle to module
 *     LPTSTR lpFilename,  // path buffer
 *     DWORD nSize         // maximum characters to retrieve
 * );
 */
if ( FALSE == GetModuleInformation( proc, module, &moduleinfo, sizeof( moduleinfo ) ) )
{
    PrintWin32Error( "GetModuleInformation() failed", GetLastError() );
    goto PrintModuleInfo_exit;
}
if ( 0 == GetModuleFileNameEx( proc, module, path, sizeof( path ) ) )
{
    PrintWin32Error( "GetModuleFileNameEx() failed", GetLastError() );
    goto PrintModuleInfo_exit;
}
printf( "    0x%08X 0x%08X %s\n", moduleinfo.lpBaseOfDll,
        moduleinfo.SizeOfImage, path );

PrintModuleInfo_exit:

return;

} /* end of PrintModuleInfo */

static void PrintModuleList ( DWORD pid ) { HANDLE proc = NULL; HMODULE *modulearray = NULL; DWORD modulecount = 1; DWORD needed = 0; DWORD i; char errmsg[256];

/*
 * Header : Declared in Winbase.h; include Windows.h.
 * Library: Use Kernel32.lib.
 *
 * HANDLE OpenProcess
 * (
 *     DWORD dwDesiredAccess,  // access flag
 *     BOOL  bInheritHandle,   // handle inheritance option
 *     DWORD dwProcessId       // process identifier
 * );
 */
if ( NULL == ( proc = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid ) ) )
{
    sprintf( errmsg, "OpenProcess() for PID<%u> failed", pid );
    PrintWin32Error( errmsg, GetLastError() );
    goto PrintModuleList_exit;
}
/*
 * BOOL EnumProcessModules
 * (
 *     HANDLE   hProcess,   // handle to process
 *     HMODULE *lphModule,  // array of module handles
 *     DWORD    cb,         // size of array
 *     LPDWORD  lpcbNeeded  // number of bytes required
 * );
 */
for ( modulecount = 1; ; modulecount *= 2 )
{
    if ( NULL == ( modulearray = ( HMODULE * )calloc( modulecount, sizeof( HMODULE ) ) ) )
    {
        fprintf( stderr, "calloc( %u, sizeof( HMODULE ) ) failed\n", modulecount );
        goto PrintModuleList_exit;
    }
    needed = 0;
    if ( FALSE == EnumProcessModules( proc, modulearray, modulecount * sizeof( HMODULE ), &needed ) )
    {
        sprintf( errmsg, "EnumProcessModules() for PID<%u> failed", pid );
        PrintWin32Error( errmsg, GetLastError() );
        goto PrintModuleList_exit;
    }
    if ( needed < ( modulecount * sizeof( HMODULE ) ) )
    {
        modulecount = needed / sizeof( HMODULE );
        break;
    }
    else
    {
        free( modulearray );
        modulearray = NULL;
    }
}  /* end of for */
printf( "    Base       Size       Path\n" );
for ( i = 0; i < modulecount; i++ )
{
    PrintModuleInfo( proc, modulearray[i] );
}  /* end of for */

PrintModuleList_exit:

if ( modulearray != NULL )
{
    free( modulearray );
    modulearray = NULL;
}
if ( proc != NULL )
{
    CloseHandle( proc );
    proc = NULL;
}
return;

} /* end of PrintModuleList */

static void PrintProcessList ( void ) { DWORD *pidarray = NULL; DWORD pidcount = 1; DWORD needed = 0; DWORD i;

/*
 * Header : Declared in Psapi.h.
 * Library: Use Psapi.lib.
 *
 * BOOL EnumProcesses
 * (
 *     DWORD *lpidProcess,  // array of process identifiers
 *     DWORD  cb,           // size of array
 *     DWORD *cbNeeded      // number of bytes returned
 * );
 *
 * 与Tool Help Library不同,PSAPI没有更方便的办法提前获知当前系统中究
 * 竟存在多少进程,必须使用如下技巧确保枚举所有进程。按几何级数递增、
 * 快速逼近。
 */
for ( pidcount = 1; ; pidcount *= 2 )
{
    if ( NULL == ( pidarray = ( DWORD * )calloc( pidcount, sizeof( DWORD ) ) ) )
    {
        fprintf( stderr, "calloc( %u, sizeof( DWORD ) ) failed\n", pidcount );
        goto PrintProcessList_exit;
    }
    needed = 0;
    if ( FALSE == EnumProcesses( pidarray, pidcount * sizeof( DWORD ), &needed ) )
    {
        PrintWin32Error( "EnumProcesses() failed", GetLastError() );
        goto PrintProcessList_exit;
    }
    if ( needed < ( pidcount * sizeof( DWORD ) ) )
    {
        pidcount = needed / sizeof( DWORD );
        break;
    }
    else
    {
        free( pidarray );
        pidarray = NULL;
    }
}  /* end of for */
printf( "PID\n" );
for ( i = 0; i < pidcount; i++ )
{
    printf( "%u\n", pidarray[i] );
    PrintModuleList( pidarray[i] );
}  /* end of for */

PrintProcessList_exit:

if ( pidarray != NULL )
{
    free( pidarray );
    pidarray = NULL;
}
return;

} /* end of PrintProcessList */

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 BOOL SetCurrentProcessPrivilege ( LPCTSTR PrivilegeName, BOOL EnableFlag ) { HANDLE TokenHandle = ( HANDLE )-1; BOOL ret = TRUE;

/*
 * Header : Declared in Winbase.h; include Windows.h.
 * Library: Use Advapi32.lib.
 *
 * BOOL OpenProcessToken
 * (
 *     HANDLE  ProcessHandle,
 *     DWORD   DesiredAccess,
 *     PHANDLE TokenHandle
 * );
 */
if ( FALSE == OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &TokenHandle ) )
{
    PrintWin32Error( "OpenProcessToken() failed", GetLastError() );
    ret = FALSE;
    goto SetCurrentProcessPrivilege_exit;
}
ret = SetPrivilege( TokenHandle, PrivilegeName, EnableFlag );

SetCurrentProcessPrivilege_exit:

if ( TokenHandle != ( HANDLE )-1 )
{
    CloseHandle( TokenHandle );
    TokenHandle = ( HANDLE )-1;
}
return( ret );

} /* end of SetCurrentProcessPrivilege */

static BOOL SetPrivilege ( HANDLE TokenHandle, LPCTSTR PrivilegeName, BOOL EnableFlag ) { DWORD error; BOOL ret = TRUE; /* * * typedef struct _TOKEN_PRIVILEGES * { * DWORD PrivilegeCount; * LUID_AND_ATTRIBUTES Privileges[]; * } TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES; * * typedef struct _LUID_AND_ATTRIBUTES * { * LUID Luid; * DWORD Attributes; * } LUID_AND_ATTRIBUTES, *PLUID_AND_ATTRIBUTES; */ TOKEN_PRIVILEGES tp = { 1, { { { 0, 0 }, 0 } } };

if ( EnableFlag == TRUE )
{
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
}
/*
 * BOOL LookupPrivilegeValue
 * (
 *     LPCTSTR lpSystemName,
 *     LPCTSTR lpName,
 *     PLUID   lpLuid
 * );
 *
 * 第二形参的可取值在winnt.h中有定义,"NT Defined Privileges"
 */
if ( FALSE == LookupPrivilegeValue( NULL, PrivilegeName, &tp.Privileges[0].Luid ) )
{
    PrintWin32Error( "LookupPrivilegeValue() failed", GetLastError() );
    ret = FALSE;
    goto SetPrivilege_exit;
}
/*
 * BOOL AdjustTokenPrivileges
 * (
 *     HANDLE            TokenHandle,
 *     BOOL              DisableAllPrivileges,
 *     PTOKEN_PRIVILEGES NewState,
 *     DWORD             BufferLength,
 *     PTOKEN_PRIVILEGES PreviousState,
 *     PDWORD            ReturnLength
 * );
 *
 * The AdjustTokenPrivileges function cannot add new privileges to the
 * access token. It can only enable or disable the token's existing
 * privileges. To determine the token's privileges, call the
 * GetTokenInformation function.
 */
if ( FALSE == AdjustTokenPrivileges( TokenHandle, FALSE, &tp, sizeof( tp ), NULL, NULL ) )
{
    PrintWin32Error( "AdjustTokenPrivileges() failed", GetLastError() );
    ret = FALSE;
    goto SetPrivilege_exit;
}
else
{
    error = GetLastError();
    /*
     * 这种情况带来的误判很隐蔽,务必留心。
     *
     * ERROR_NOT_ALL_ASSIGNED
     */
    if ( ERROR_SUCCESS != error )
    {
        PrintWin32Error( "AdjustTokenPrivileges() failed", error );
        ret = FALSE;
        goto SetPrivilege_exit;
    }
}

SetPrivilege_exit:

return( ret );

} /* end of SetPrivilege */

int main ( int argc, char * argv[] ) { EnableCurrentProcessDebugPrivilege(); PrintProcessList(); DisableCurrentProcessDebugPrivilege(); return( EXIT_SUCCESS ); } /* end of main */

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


☆ 利用ZwQuerySystemInformation()枚举进程

这次使用Native API枚举进程,比Win32 API降一层。可以获取PPID。下次再降一层。


/*

  • For x86/EWindows XP SP1 & VC 7
  • cl Zwlistprocess.c /Os /G6 /W3 */

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

  •                                                                  *
  •                           Head File                              *
  •                                                                  *

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

#include <stdio.h> #include <stdlib.h> #include <windows.h>

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

  •                                                                  *
  •                           Macro                                  *
  •                                                                  *

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

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

typedef LONG NTSTATUS;

/*

  • you'll find a list of NTSTATUS status codes in the DDK header
  • ntstatus.h (\WINDDK\2600.1106\inc\ddk\wxp) */ #define NT_SUCCESS(status) ((NTSTATUS)(status)>=0) #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)

/*


  • ntddk.h */

typedef LONG KPRIORITY;

/*

  • ntddk.h

*/

/*


  • ntdef.h */

typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING, *PUNICODE_STRING;

/*

  • ntdef.h

*/

/*


  • <<Windows NT/2000 Native API Reference>> - Gary Nebbett */

typedef enum _SYSTEM_INFORMATION_CLASS { SystemProcessesAndThreadsInformation = 5 } SYSTEM_INFORMATION_CLASS;

/*

  • Information Class 5
  • 为了便于编译,我将InheritedFromProcessId之后的成员扔掉了 */ typedef struct _SYSTEM_PROCESSES { ULONG NextEntryDelta; ULONG ThreadCount; ULONG Reserved1[6]; LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime; UNICODE_STRING ProcessName; KPRIORITY BasePriority; ULONG ProcessId; ULONG InheritedFromProcessId; } SYSTEM_PROCESSES, *PSYSTEM_PROCESSES;

/*

  • <<Windows NT/2000 Native API Reference>> - Gary Nebbett

*/

/*

  • 参看DDK文档以及<<Windows NT/2000 Native API Reference>> - Gary Nebbett
  • 这些Native API由ntdll.dll输出 */ 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 );

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

  •                                                                  *
  •                        Function Prototype                        *
  •                                                                  *

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

static BOOLEAN LocateNtdllEntry ( void ); static void PrintProcessList ( void ); static void PrintWin32Error ( char *message, DWORD dwMessageId ); static void PrintZwError ( char *message, NTSTATUS status );

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

  •                                                                  *
  •                        Static Global Var                         *
  •                                                                  *

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

/*

  • 由ntdll.dll输出的Native API函数指针 */ static ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = NULL; static RTLNTSTATUSTODOSERROR RtlNtStatusToDosError = NULL;

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

/*

  • 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 ( !( 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 */

static void PrintProcessList ( void ) { NTSTATUS status; PVOID buf = NULL; ULONG size = 1; PSYSTEM_PROCESSES proc = NULL; ULONG delta = 0;

for ( size = 1; ; size *= 2 )
{
    if ( NULL == ( buf = calloc( size, 1 ) ) )
    {
        fprintf( stderr, "calloc( %u, 1 ) failed\n", size );
        goto PrintProcessList_exit;
    }
    status = ZwQuerySystemInformation( SystemProcessesAndThreadsInformation, buf, size, NULL );
    if ( !NT_SUCCESS( status ) )
    {
        if ( STATUS_INFO_LENGTH_MISMATCH == status )
        {
            free( buf );
            buf = NULL;
        }
        else
        {
            PrintZwError( "ZwQuerySystemInformation() failed", status );
            goto PrintProcessList_exit;
        }
    }
    else
    {
        /*
         * printf( "size = %u\n", size );
         */
        break;
    }
}  /* end of for */
proc = ( PSYSTEM_PROCESSES )buf;
printf( "PID        PPID       Path\n" );
do
{
    printf( "%-10u %-10u ",
            proc->ProcessId,
            proc->InheritedFromProcessId );
    wprintf( L"%s\n", proc->ProcessName.Buffer );
    delta = proc->NextEntryDelta;
    proc  = ( PSYSTEM_PROCESSES )( ( char * )proc + delta );
}
while ( 0 != delta );

PrintProcessList_exit:

if ( buf != NULL )
{
    free( buf );
    buf = NULL;
}
return;

} /* end of PrintProcessList */

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 */

int main ( int argc, char * argv[] ) { if ( FALSE == LocateNtdllEntry() ) { fprintf( stderr, "LocateNtdllEntry() failed\n" ); return( EXIT_FAILURE ); } PrintProcessList(); return( EXIT_SUCCESS ); } /* end of main */

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


☆ 参考资源

[ 1] NT's "\dev\kmem" http://www.sysinternals.com/files/physmem.zip

[ 2] Giocherellare con la IDT su Win2k(意大利文) http://pmode.impazz.it/tuts/win2k_idt.htm http://pmode.impazz.it/attach/idt_dump.zip

[ 3] Playing with Windows /dev/(k)mem http://www.phrack.org/phrack/59/p59-0x10.txt

[ 4] Windows NT/2000/XP下不用驱动的Ring0代码实现 http://person.okey.net/~webcrazy/ntring0.htm http://person.okey.net/~webcrazy/ntring0.zip

[ 5] <<Inside Microsoft Windows 2000 Third Edition>> - David A. Solomon, Mark E. Russinovich

[ 6] <<Undocumented Windows 2000 Secrets>> - Sven B. Schreiber

[ 7] Windows XP Escape from DLL Hell with Custom Debugging and Instrumentation Tools and Utilities - Christophe Nasarre http://msdn.microsoft.com/msdnmag/issues/02/06/debug/default.aspx

 Windows XP Escape from DLL Hell with Custom Debugging and Instrumentation Tools and Utilities, Part 2
 http://msdn.microsoft.com/msdnmag/issues/02/08/EscapefromDLLHell/default.aspx

[ 8] Win32 Q&A, MSJ March 1998 http://www.microsoft.com/msj/0398/Win320398.aspx

[ 9] Intel Architecture Software Developer's Manual. Volume 1 ftp://download.intel.com/design/PentiumII/manuals/24319002.pdf

 Intel Architecture Software Developer's Manual. Volume 2
 ftp://download.intel.com/design/PentiumII/manuals/24319102.pdf

 Intel Architecture Software Developer's Manual. Volume 3
 ftp://download.intel.com/design/PentiumII/manuals/24319202.pdf

[10] http://www.acc.umu.se/~bosse/ntifs.h

[11] Using Debugging Tools for Windows(debugger.chm)

[12] "Adjusting Process Token Privileges" - Tomas Restrepo [email protected] http://www.mvps.org/windev/security/tokens.html

[13] Security Briefs, MSJ August 1999 http://www.microsoft.com/msj/0899/security/security0899.aspx

[14] Enumerate Applications Using Win32 APIs http://support.microsoft.com/default.aspx?scid=kb;en-us;175030

[15] <<Windows NT/2000 Native API Reference>> - Gary Nebbett