Saturday, February 19, 2005

 

Some notes on Process

进程的话题主要包括三个方面,查找/隐藏进程,计算进程的运行时间和进程注入,另外还有一些其它内容,如notify消息,Windows Process Session设置,以下分两篇篇essay介绍

1.
Detecting Windows NT/2K process execution
By Ivo Ivanov
An article on how to get notification from the OS when a process starts
http://www.codeproject.com/threads/procmon.asp

2.
微软推荐的两种方法
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/perfmon/base/enumerating_all_processes.asp

大量的实例
http://www.codeproject.com/threads/#Processes

3.
获得进程的EPROCESS

发布日期:2004-06-02
文摘内容:
--------------------------------------------------------------------------------
文摘出处:http://www.xfocus.net/articles/200406/706.html

创建时间:2004-06-01
文章属性:原创
文章提交:MustBE (zf35_at_citiz.net)

By [I.T.S]SystEm32

Welcome to our web site http://itaq.ynpc.com/itsbbs/

thanks to SobeIt : P
---------------------------------------------------------------------------------------------

每个Windows进程都有一个相对应的执行体进程(EPROCESS,也就是KTEB),EPROCESS不仅包括了进程的许多属性,还包扩了许多指向其他数据结构的指针,其中包含了大量有用的信息.本文仅讲述如何获得特定进程对应的EPROCESS,EPROCESS的作用及数据结构不在本文讨论范围之内.

绿盟高手flier在他的文章中提到,使用ZwQuerySystemInformation函数获取所有核心句柄表,线性搜索到进程句柄,其指向的内核对象就是EPROCESS。

ZwQuerySystemInformation函数原形如下

NTSYSAPI
NTSTATUS
NTAPI
ZwQuerySystemInformation
(
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength OPTIONAL
);

参数意义如下

SystemInformationClass:被查询的系统信息的类型,SYSTEM_INFORMATION_CLASS的枚举类型之一

SystemInformation:指向一个接受系统信息的缓冲区的指针

SystemInformationLength:缓冲区长度

ReturnLength:指向一个接受实际返回字节数的变量,可以为0


为了获取EPROCESS,我们使用SYSTEM_HANDLE_INFORMATION作为第一参数来调用 ZwQuerySystemInformation

SYSTEM_INFORMATION_CLASS的结构如下

typedef struct _SYSTEM_HANDLE_INFORMATION
{
ULONG ProcessId;
UCHAR ObjectTypeNumber;
UCHAR Flags;
USHORT Handle;
PVOID Object;
ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

ProcessId:进程标识符

ObjectTypeNumber;打开的对象的类型

Flags:句柄属性标志

Handle:句柄数值,在进程打开的句柄中唯一标识某个句柄

Object:这个就是句柄对应的EPROCESS的地址

GrantedAccess:句柄对象的访问权限


下面我写了一个小程序来获得EPROCESS( GetKTEB.cpp )

比较faint的是程序写好后发现并未如预期般获得EPROCESS,通过调试发现ZwQuerySystemInformation()返回的进程的句柄中并没有进程本身的句柄

怎么会这样?难道程序写错了?*_*

现在只好靠SoftICE给出答案了,CTRL+D唤出SoftICE,随便选了个进程--QQ,让我们来看看SoftICE的输出

:proc -o QQ
Process KPEB PID Threads Pri User Time Krnl Time Status
QQ 827CD520 11C 2A 8 00000B90 000008D4 Ready

---- Handle Table Information ----

Handle Table: FFAD93C8 Handle Array: E2BEB000 Entries: 590

Handle Ob Hdr * Object * Type
0000 00000000 00000018 ?
0004 E2DA5E58 E2DA5E70 Section
0008 FFAB35C8 FFAB35E0 Event
000C FFAB3B08 FFAB3B20 Event
0010 85C70188 85C701A0 Event
0014 81515778 81515790 Directory
0018 FFAB7BB2 FFAB7BCA ?
001C 814A1858 814A1870 Directory
0020 80288C88 80288CA0 Event
0024 E2CFE7F9 E2CFE811 ?
0028 842D7B08 842D7B20 Event
002C 80E9B989 80E9B9A1 ?
0030 E1372198 E13721B0 Section
0034 814602C0 814602D8 WindowStation
0038 81455CE0 81455CF8 Desktop
003C 814602C0 814602D8 WindowStation
0040 E2B3C1A8 E2B3C1C0 Key
0044 E286D6E8 E286D700 Key
0048 E2B3C0E8 E2B3C100 Key
004C E2B3C068 E2B3C080 Key
0050 E2BEE688 E2BEE6A0 Key
0054 8147C998 8147C9B0 Directory
0058 829D1128 829D1140 Event
005C 83F991E8 83F99200 Event
0060 E2BEE608 E2BEE620 Key
0064 FFB07568 FFB07580 Event
0068 801747E8 80174800 Event
006C 80174828 80174840 Event
0070 845E8808 845E8820 Event
0074 81448798 814487B0 Event
0078 E2B9A888 E2B9A8A0 Key
007C 845E8648 845E8660 Event
0080 FF9E2DB8 FF9E2DD0 Mutant
0084 FF9E2D58 FF9E2D70 Mutant
0088 83CFC378 83CFC390 Mutant
008C 801749B0 801749C8 File
0090 E2C48668 E2C48680 Section
0094 FF965168 FF965180 Event
0098 FF9E7D88 FF9E7DA0 Event
009C FFAD3DE8 FFAD3E00 Event
00A0 80AD63C8 80AD63E0 Event
00A4 E28073A8 E28073C0 Key
00A8 FF955588 FF9555A0 Thread
00AC E2770728 E2770740 Key
00B0 FF923438 FF923450 Mutant
00B4 FFAE3B38 FFAE3B50 Mutant
00B8 83B80728 83B80740 Event
00BC 83B80668 83B80680 Event
00C0 E2E3C448 E2E3C460 Section
00C4 83776A08 83776A20 Thread
00C8 81489E48 81489E60 Event
00CC 83776CC8 83776CE0 Event
00D0 83776C88 83776CA0 Event
00D4 83776768 83776780 Event
00D8 E2837D88 E2837DA0 Key
00DC 8146B3A8 8146B3C0 Event
00E0 FF908308 FF908320 Event
00E4 81494868 81494880 Event
00E8 FF9064C8 FF9064E0 Event
00EC FF908FC8 FF908FE0 Event
00F0 FF908F88 FF908FA0 Event
00F4 FF955588 FF9555A0 Thread
00F8 FF908F48 FF908F60 Event
00FC E2CB1558 E2CB1570 Port
0100 FF90A2C8 FF90A2E0 IoCompletion
0104 E2CFE708 E2CFE720 Port
0108 FF90A2C8 FF90A2E0 IoCompletion
010C 837762A8 837762C0 Thread
0110 8103BBC8 8103BBE0 Event
0114 813DBDB8 813DBDD0 Event
0118 FF814788 FF8147A0 Event
011C E1358DA8 E1358DC0 Key
0120 E2CFC428 E2CFC440 Key
0124 8103B9C8 8103B9E0 Event
0128 E2C9A968 E2C9A980 Key
012C 83B34E88 83B34EA0 Event
0130 E2CFD948 E2CFD960 Key
0134 83B34E08 83B34E20 Event
....
.....................省略

看了一阵,确实没有QQ本身进程的Handle,那么怎么办呢?

想了一会儿...既然Win32子系统是由CSRSS.EXE来管理的,那么用户创建的进程的句柄应该在CSRSS.EXE里面找得到,用SoftICE验证后发现确实如此

可是这没办法得到指定进程的句柄,和我所需相去甚远,只有另选它路

后来总算想到解决办法,既然没有进程的句柄,那就创建一个吧,OpenProcess()这个函数可以打开一个进程的句柄,正合所需.

果然加上这么一句后,ZwQuerySystemInformation()获得了EPROCESS

修改好的程序代码如下,获得本身进程的EPROCESS地址,稍作修改可获取任意进程

#include
#include
#include
#include

/*
* 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)
#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)

/*
*************************************************************************
* ntddk.h
*/
typedef LONG NTSTATUS;
typedef ULONG ACCESS_MASK;
/*
* ntdef.h
*************************************************************************
*/

/*
*************************************************************************
* <> - Gary Nebbett
*/

typedef enum _SYSTEM_INFORMATION_CLASS
{
SystemHandleInformation = 16
} SYSTEM_INFORMATION_CLASS;

/*
*Information Class 16
*/
typedef struct _SYSTEM_HANDLE_INFORMATION
{
ULONG ProcessId;
UCHAR ObjectTypeNumber;
UCHAR Flags;
USHORT Handle;
PVOID Object;
ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

#define InitializeObjectAttributes( p, n, a, r, s ) { (p)->Length = sizeof( OBJECT_ATTRIBUTES ); (p)->RootDirectory = r; (p)->Attributes = a; (p)->ObjectName = n; (p)->SecurityDescriptor = s; (p)->SecurityQualityOfService = NULL; }
/*
*************************************************************************
* <> - Gary Nebbett
*************************************************************************
*/
typedef ULONG ( __stdcall *RTLNTSTATUSTODOSERROR ) ( IN NTSTATUS Status );
typedef NTSTATUS ( __stdcall *ZWQUERYSYSTEMINFORMATION ) ( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, IN OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength OPTIONAL );
/************************************************************************
* *
* Function Prototype *
* *
************************************************************************/

static DWORD GetEprocessFromPid ( ULONG PID );
static BOOL LocateNtdllEntry ( void );


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

static RTLNTSTATUSTODOSERROR RtlNtStatusToDosError = NULL;
static ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = NULL;

static HMODULE hModule = NULL;
/************************************************************************/


static DWORD GetEprocessFromPid ( ULONG PID )
{
NTSTATUS status;
PVOID buf = NULL;
ULONG size = 1;
ULONG NumOfHandle = 0;
ULONG i;
PSYSTEM_HANDLE_INFORMATION h_info = NULL;

for ( size = 1; ; size *= 2 )
{
if ( NULL == ( buf = calloc( size, 1 ) ) )
{
fprintf( stderr, "calloc( %u, 1 ) failed\n", size );
goto GetEprocessFromPid_exit;
}
status = ZwQuerySystemInformation( SystemHandleInformation, buf, size, NULL );
if ( !NT_SUCCESS( status ) )
{
if ( STATUS_INFO_LENGTH_MISMATCH == status )
{
free( buf );
buf = NULL;
}
else
{
printf( "ZwQuerySystemInformation() failed");
goto GetEprocessFromPid_exit;
}
}
else
{
break;
}
} /* end of for */

//返回到缓冲区的首先是一个ULONG类型的数据,表示有多少数组
NumOfHandle = (ULONG)buf;

h_info = ( PSYSTEM_HANDLE_INFORMATION )((ULONG)buf+4);

for(i = 0; i {
if( ( h_info[i].ProcessId == PID )&&( h_info[i].ObjectTypeNumber == 5 ))//&&( h_info[i].Handle==0x3d8 ) )
{
printf("Handle:0x%x,OBJECT 0x%x\n\r",h_info[i].Handle,h_info[i].Object);
return((DWORD)(h_info[i].Object));
}
}
GetEprocessFromPid_exit:
if ( buf != NULL )
{
free( buf );
buf = NULL;
}
return(FALSE);
}


/*
* ntdll.dll
*/
static BOOL LocateNtdllEntry ( void )
{
BOOL ret = FALSE;
char NTDLL_DLL[] = "ntdll.dll";
HMODULE ntdll_dll = NULL;


if ( ( ntdll_dll = GetModuleHandle( NTDLL_DLL ) ) == NULL )
{
printf( "GetModuleHandle() failed");
return( FALSE );
}
if ( !( ZwQuerySystemInformation = ( ZWQUERYSYSTEMINFORMATION )GetProcAddress( ntdll_dll, "ZwQuerySystemInformation" ) ) )
{
goto LocateNtdllEntry_exit;
}
ret = TRUE;

LocateNtdllEntry_exit:

if ( FALSE == ret )
{
printf( "GetProcAddress() failed");
}
ntdll_dll = NULL;
return( ret );
} /* end of LocateNtdllEntry */


int main(int argc,char **argv)
{

LocateNtdllEntry( );

//打开自身句柄,这样才能在handle列表中找到自己,PROCESS 对应 ObjectTypeNum 为5
OpenProcess( PROCESS_ALL_ACCESS,FALSE,GetCurrentProcessId() );

DWORD Addr = GetEprocessFromPid( (DWORD)GetCurrentProcessId() );

printf("result: Current EPROCESS's Address is 0x%x \n\r",Addr);

return TRUE;
}

4.
解析Windows 2000/XP进程工作集
By WebCrazy 2003-3-17 15:45:21
From http://webcrazy.yeah.net

在《解析Windows 2000/XP物理内存管理》中我详细的介绍了页框数据库(Page Frame Database)的概念,提到在物理内存的组织与管理方面对于每个页面系统都在页框数据库中保存一个结构,用于跟踪页面状态等。但页框数据库并不能真正协调物理内存的使用。我们知道,Windows是一个多任务的操作系统,而物理内存却是一个相对贫乏的资源,为避免某个进程(或是系统)耗尽这一资源,引入了工作集(WorkingSet)的概念。WorkingSet是内存管理一个相当重要的术语,在Windows 2000/XP中通常分为两种即进程工作集与系统工作集,分别用于跟踪各个进程与系统的物理内存使用情况。由于终端服务的引入,另有一种工作集会话(Session)工作集,用于跟踪各个Session使用物理内存的情况。本文从进程工作集的内部组织方式出发,简要阐述工作集在Windows 2000/XP中的组织与管理。

EPROCESS是描述进程的结构,所以从EPROCESS入手,肯定也能找到进程工作集的表示方式。实际上位于EPROCESS中的子结构MMSUPPORT就是关于进程与内存子系统相关的一些关键内容,进程工作集自然也在此。对于早期的内核版本这些内容没有集成至MMSUPPORT结构中,而且各版本间MMSUPPORT的定义是不相同的,底下列出MMSUPPORT在Windows XP Build 2600 SP0中的定义(本文中所有结构都可能只适用于这一版本):

typedef struct _MMSUPPORT {
LARGE_INTEGER LastTrimTime;
MMSUPPORT_FLAGS Flags;
ULONG PageFaultCount;
ULONG PeakWorkingSetSize;
ULONG WorkingSetSize;
ULONG MinimumWorkingSetSize;
ULONG MaximumWorkingSetSize;
PMMWSL VmWorkingSetList;
LIST_ENTRY WorkingSetExpansionLinks;
ULONG Claim;
ULONG NextEstimationSlot;
ULONG NextAgingSlot;
ULONG EstimatedAvailable;
ULONG GrowthSinceLastEstimate;
} MMSUPPORT, *PMMSUPPORT;

MMSUPPORT中PeakWorkingSetSize、WorkingSetSize、MinimumWorkingSetSize与MaximumWorkingSetSize分别表示此进程的工作集峰值、当然工作集大小、允许工作集的最大值与最小值。性能监视器(perfmon.msc)与任务管理器(taskmgr.exe)都可对这些数据进程跟踪显示。Win32 API GetProcessWorkingSetSize(Ex)和SetProcessWorkingSetSize(Ex)在具有相应PROCESS_QUERY_INFORMATION与PROCESS_SET_QUOTA权限后即能获取或设置MinimumWorkingSetSize与MaximumWorkingSetSize等。

进程在建立时,进程工作集总为空的,CreateProcess等在建立进程过程中有责任初始化进程工作集。它会分配一个物理页面,然后调用MiInitializeWorkingSetList初始化进程工作集。后者以刚建立的EPROCESS作为参数初始化我们上面提到的MMSUPPORT结构。这里要提到一个很重要的成员VmWorkingSetList(结构MMWSL),定义如下:

+0x000 Quota : Uint4B
+0x004 FirstFree : Uint4B
+0x008 FirstDynamic : Uint4B
+0x00c LastEntry : Uint4B
+0x010 NextSlot : Uint4B
+0x014 Wsle : Ptr32 _MMWSLE
+0x018 LastInitializedWsle : Uint4B
+0x01c NonDirectCount : Uint4B
+0x020 HashTable : Ptr32 _MMWSLE_HASH
+0x024 HashTableSize : Uint4B
+0x028 NumberOfCommittedPageTables : Uint4B
+0x02c HashTableStart : Ptr32 Void
+0x030 HighestPermittedHashAddress : Ptr32 Void
+0x034 NumberOfImageWaiters : Uint4B
+0x038 VadBitMapHint : Uint4B
+0x03c UsedPageTableEntries : [768] Uint2B
+0x63c CommittedPageTables : [24] Uint4B

效率上考虑,Windows 2000/XP均将这一结构映射至一固定的虚拟内存地址中。由内核变量MmWorkingSetList指定,实际上MiInitializeWorkingSetList就是直接引用这个变量对MMSUPPORT结构的VmWorkingSetList成员进行操作的。MmWorkingSetList位于内核区域(在Windows XP Build 2600 Professional中为0xc0503000),通常内核区域均是由所有进程共享的,但显然MmWorkingSetList指定的WorkingSet情况对于每个进程都有不同的映射,即具有不同的内容,这与进程页目录或是页表一样。后者我在《小议Windows NT/2000分页机制》中详细的做过测试。

因为进程WorkingSet是用于描述进程使用物理内存的情况,换句话说位于WorkingSet中的页面均位于物理内存中(没有被置换到pagefile.sys中等),所以访问这些页面均不会导致Page Fault。我们可以使用VirtualLock将页面置入进程工作集中。反过来想,系统如何知道某一页面(使用虚拟页面地址),针对这一进程是否存在于工作集中呢?粗粗浏览一下上面给出的MMWSL的定义,就知道Windows 2000/XP使用哈希表(HashTable)来组织这些页面。HashTable具有快速检索的特点,正好适合于WorkingSet频繁访问的特点。另一个例子是系统全局命名内核的组织,详见《剖析Windows NT/2000内核对象组织》。与Windbg提供dump全局命令内核对象的!object命令一样,Windbg提供!wsle用于dump进程工作集。例如:

kd> !wsle 7

Working Set @ c0503000
FirstFree: 469 FirstDynamic: 7
LastEntry 46c NextSlot: 4 LastInitialized 658
NonDirect 145 HashTable: c06f4000 HashTableSize: 400

Reading the WSLE data...
..
Virtual Address Age Locked ReferenceCount
c0300203 0 1 1
c0301203 0 1 1
c0502203 0 1 1
c0503203 0 1 1
c0504203 0 1 1
c06f4203 0 1 1
c06f5203 0 1 1
c0505203 0 1 1
c0506203 0 1 1
77c47029 0 0 1
.
.
.

wsle命令只是将VmWorkingSetList的Wsle成员(MMWSLE指针)指向的数组的每个元素dump出(每个元素32bit)。windbg的!wsle命令获得的结果中Virtual Address列即Wsle的每一个32bit的内容。如下windbg命令所示:

kd> dd MmWorkingSetList l 1 //当前进程MMWSL结构所在的地址,如本文前头描述。
805467d0 c0503000
kd> dd c0503000 l 10 //MMWSL内容
c0503000 000003b9 000003ba 00000007 000003b9
c0503010 00000004 c050369c 00000658 0000014c
--------
|_MMWSLE内容(如上给出的MMWSL定义,MMWSLE是一个指针)

c0503020 c06f4000 00000400 0000001a c06f4000
| |_HashTableSize(Uint4B)哈希表大小
|_HashTable(MMWSLE_HASH)地址(底下将会用到这两个数值)
c0503030 c0800000 00000000 0000005c 004d023a



kd> dd c050369c
//结果即上面wsle命令输出的Virtual Address列(WorkingSet
//频繁变动,如果有稍许不同可能是系统已经更改过了)。
c050369c c0300203 c0301203 c0502203 c0503203
c05036ac c0504203 c06f4203 c06f5203 c0505203

实际上这里的每一个Virtual Address,就像上所示的如c0300203不仅仅是Virtual Address,因为WorkingSet是以页面为单位的,所以这些32bit的内容中有12bit用于其它用途。实际上在Windows XP中这个32bit的内容定义为MMWSLENTRY,具体为:

Valid : Pos 0, 1 Bit
LockedInWs : Pos 1, 1 Bit
LockedInMemory : Pos 2, 1 Bit
Protection : Pos 3, 5 Bits
SameProtectAsProto : Pos 8, 1 Bit
Direct : Pos 9, 1 Bit
Age : Pos 10, 2 Bits
VirtualPageNumber : Pos 12, 20 Bits

wsle命令也即根据这低12bit输出WSLE的一些属性:如Age与Locked。ReferenceCount则位于PFN中,具体请参阅《解析Windows 2000/XP物理内存管理》。

整个结构至此已经比较明朗了,但是正像上面提到的WorkingSet访问是非常频繁的,在检索指定虚拟地址的页面是否在WorkingSet中还要依靠另一个重要的成员HashTable。既然通过HashTable,我们来给出HashFunction(有兴趣想知道如何得到HashFunction的可像我一样看看MiInsertWsle是如何实现的)。

((PVA >>a) & 0x3ffffc) % (HashTableSize-1)

这里,PVA指页面虚拟地址,而HashTableSize指当前进程的WorkingSet哈希表的大小。对于给定的一个页面,如何在WSLE数组中快速的检索到这个页面的数组下标呢?有了哈希表,当然通过Hash表了。这样描述还是比较抽象,我们以一个具体的例子说明问题:从上面wsle命令输出结果,我们知道虚拟地址77c47000(77c47029那一行),未于MMWSLE的第十项(数组下标为9,即index为9),而这个进程的工作集HashTableSize值为0x400(这个值可能系统会在需要时通常MiGrowWsleHash更改),所以:

((77c47029>>a)&0x3ffffc) % (0x400-1)

值为0x9a,所以位于HashTable的第0x9a个Bucket中(以0开始),通过上面得到的HashTable地址c06f4000,找到第0x9a个bucket。而每个Bucket的大小呢?需要说明的是这个HashTable的每个Bucket如下定义(_MMWSLE_HASH):

+0x000 Key : Uint4B
+0x004 Index : Uint4B

即每个bucket为8个字节,所以我们用如下kd命令得到结果:

kd> dd c06f4000+9a*8 l 2
c06f44d0 77c47000 00000009

其Key值为77c47000,即虚拟地址,Index值为9,即验证了上面windbg的wsle命令输出结果。现在,对于WorkingSet的组织也已经讨论的差不多了,需要指出的是在Windows XP中WorkingSet的设计远比这讨论的多很多内容,比如WorkingSet的哈希表是可扩展的(通过MiGrowWsleHash),HashTable内容的插入、更改、删除,还有工作集修整(通过MiTrimWorkingSet)等等,特别是工作集修整,文章开头提到工作集的一个主要作用合理利用物理内存,避免某个进程(或是系统)耗尽物理内存,通过WorkingSet的最大、最小值与Quota指定的限额,限定物理内存的使用。如果出现越出这样的一个范围或是物理内存耗尽,则会使用工作集修整。Andrew Tanenbaum的《Modern Operating Systems》介绍了多种工作集修整的算法,在单处理器中Windows 2000/XP中使用了更像LRU的时候算法(Clock algorithm正像很多Unix系统实现一样),你应该看到上面输出的Age的值吧。由于条件限制我只能在单处理器上实验过。为了篇幅完整,我简要介绍一下多处理器的情况:多处理中Windows 2000使用FIFO(First In First Out)算法,但从我看到的Microsoft的一些介绍中,似乎Windows XP/.Net Server 2003在多处理中也使用LRU了,看来Windows的内核是越来越完善了。

本文只介绍了进程工作集,对于系统工作集及Session工作集,大同小异,实际上我是在分析了三种工作集后,才开始着手写这样的一篇。这一些些的概念、结构在自己的学习过程中不断被发现,也着实让自己兴奋不已,但我从来没有看过任何关于这些结构层次上的讨论,错误之处,在所难免,敬请见谅,谢谢

5.
在windows 2003中,如何在驱动中取得当前进程完整路径

By 黄森堂(vcmfc) 2004-11-11
From http://blog.csdn.net/vcmfc/archive/2004/11/11/177106.aspx

好久没有访问CSDN,现在都变的不太认识.

由于这几年从事驱动开发,就发一些自已的心得.

在驱动开发中,有时候我们需要取得当前进程的路径,在之前,大家都是在抄xfilt的代码(xfilt是抄osr).
#define BASE_PROCESS_PEB_OFFSET 0x01B0
#define BASE_PEB_PROCESS_PARAMETER_OFFSET 0x0010
#define BASE_PROCESS_PARAMETER_FULL_IMAGE_NAME 0x003C

PCWSTR KfGetProcessFullName()
/*++
Arguments:

pFullImageName - Pointer to get the process name, etc: "C:\WINNT\notepad.exe".

--*/
{
DWORD dwAddress;

if(KeGetCurrentIrql() != PASSIVE_LEVEL)
return NULL;

dwAddress = (DWORD)PsGetCurrentProcess();

if(dwAddress == 0 || dwAddress == 0xFFFFFFFF)
return NULL;
dwAddress += BASE_PROCESS_PEB_OFFSET;
if((dwAddress = *(DWORD*)dwAddress) == 0) return 0;
dwAddress += BASE_PEB_PROCESS_PARAMETER_OFFSET;
if((dwAddress = *(DWORD*)dwAddress) == 0) return 0;
dwAddress += BASE_PROCESS_PARAMETER_FULL_IMAGE_NAME;
if((dwAddress = *(DWORD*)dwAddress) == 0) return 0;

return (PCWSTR)dwAddress;
}

这段代码在2k/xp能正确工作,可在2003上,一执行就蓝屏,为什么呢???

首先你要理想这段代码的工作原理(知道还看什么,快关IE.)

流程:
1.取得EPROCESS(PsGetCurrentProcess();)
2. 通过偏移量取得PEB, (Address + BASE_PROCESS_PEB_OFFSET )
3.通过PEB指针的偏移量取得RTL_USER_PROCESS_PARAMETER( Address + BASE_PEB_PROCESS_PARAMETER_OFFSET)
最后是取得其ImagePathName, 得到是UNICODE_STRING结构.

知道原理后,下面演示我是如何在2003上取得:



调试心得记录:使用Windbg 分析2003的全路径存在哪儿?

1. 在主机安装2003 sym,不知道,可以去死了.

2.启动2003,并开一个IE.

3. 先得到结构参数

dt nt!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER
+0x078 ExitTime : _LARGE_INTEGER
+0x080 RundownProtect : _EX_RUNDOWN_REF
+0x084 UniqueProcessId : Ptr32 Void
+0x088 ActiveProcessLinks : _LIST_ENTRY
+0x090 QuotaUsage : [3] Uint4B
+0x09c QuotaPeak : [3] Uint4B
+0x0a8 CommitCharge : Uint4B
+0x0ac PeakVirtualSize : Uint4B
+0x0b0 VirtualSize : Uint4B
+0x0b4 SessionProcessLinks : _LIST_ENTRY
+0x0bc DebugPort : Ptr32 Void
+0x0c0 ExceptionPort : Ptr32 Void
+0x0c4 ObjectTable : Ptr32 _HANDLE_TABLE
+0x0c8 Token : _EX_FAST_REF
+0x0cc WorkingSetPage : Uint4B
+0x0d0 AddressCreationLock : _KGUARDED_MUTEX
+0x0f0 HyperSpaceLock : Uint4B
+0x0f4 ForkInProgress : Ptr32 _ETHREAD
+0x0f8 HardwareTrigger : Uint4B
+0x0fc PhysicalVadRoot : Ptr32 _MM_AVL_TABLE
+0x100 CloneRoot : Ptr32 Void
+0x104 NumberOfPrivatePages : Uint4B
+0x108 NumberOfLockedPages : Uint4B
+0x10c Win32Process : Ptr32 Void
+0x110 Job : Ptr32 _EJOB
+0x114 SectionObject : Ptr32 Void
+0x118 SectionBaseAddress : Ptr32 Void
+0x11c QuotaBlock : Ptr32 _EPROCESS_QUOTA_BLOCK
+0x120 WorkingSetWatch : Ptr32 _PAGEFAULT_HISTORY
+0x124 Win32WindowStation : Ptr32 Void
+0x128 InheritedFromUniqueProcessId : Ptr32 Void
+0x12c LdtInformation : Ptr32 Void
+0x130 VadFreeHint : Ptr32 Void
+0x134 VdmObjects : Ptr32 Void
+0x138 DeviceMap : Ptr32 Void
+0x13c Spare0 : [3] Ptr32 Void
+0x148 PageDirectoryPte : _HARDWARE_PTE
+0x148 Filler : Uint8B
+0x150 Session : Ptr32 Void
+0x154 ImageFileName : [16] UChar
+0x164 JobLinks : _LIST_ENTRY
+0x16c LockedPagesList : Ptr32 Void
+0x170 ThreadListHead : _LIST_ENTRY
+0x178 SecurityPort : Ptr32 Void
+0x17c PaeTop : Ptr32 Void
+0x180 ActiveThreads : Uint4B
+0x184 GrantedAccess : Uint4B
+0x188 DefaultHardErrorProcessing : Uint4B
+0x18c LastThreadExitStatus : Int4B
+0x190 Peb : Ptr32 _PEB
+0x194 PrefetchTrace : _EX_FAST_REF
+0x198 ReadOperationCount : _LARGE_INTEGER
+0x1a0 WriteOperationCount : _LARGE_INTEGER
+0x1a8 OtherOperationCount : _LARGE_INTEGER
+0x1b0 ReadTransferCount : _LARGE_INTEGER
+0x1b8 WriteTransferCount : _LARGE_INTEGER
+0x1c0 OtherTransferCount : _LARGE_INTEGER
+0x1c8 CommitChargeLimit : Uint4B
+0x1cc CommitChargePeak : Uint4B
+0x1d0 AweInfo : Ptr32 Void
+0x1d4 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
+0x1d8 Vm : _MMSUPPORT
+0x238 MmProcessLinks : _LIST_ENTRY
+0x240 ModifiedPageCount : Uint4B
+0x244 JobStatus : Uint4B
+0x248 Flags : Uint4B
+0x248 CreateReported : Pos 0, 1 Bit
+0x248 NoDebugInherit : Pos 1, 1 Bit
+0x248 ProcessExiting : Pos 2, 1 Bit
+0x248 ProcessDelete : Pos 3, 1 Bit
+0x248 Wow64SplitPages : Pos 4, 1 Bit
+0x248 VmDeleted : Pos 5, 1 Bit
+0x248 OutswapEnabled : Pos 6, 1 Bit
+0x248 Outswapped : Pos 7, 1 Bit
+0x248 ForkFailed : Pos 8, 1 Bit
+0x248 Wow64VaSpace4Gb : Pos 9, 1 Bit
+0x248 AddressSpaceInitialized : Pos 10, 2 Bits
+0x248 SetTimerResolution : Pos 12, 1 Bit
+0x248 BreakOnTermination : Pos 13, 1 Bit
+0x248 SessionCreationUnderway : Pos 14, 1 Bit
+0x248 WriteWatch : Pos 15, 1 Bit
+0x248 ProcessInSession : Pos 16, 1 Bit
+0x248 OverrideAddressSpace : Pos 17, 1 Bit
+0x248 HasAddressSpace : Pos 18, 1 Bit
+0x248 LaunchPrefetched : Pos 19, 1 Bit
+0x248 InjectInpageErrors : Pos 20, 1 Bit
+0x248 VmTopDown : Pos 21, 1 Bit
+0x248 ImageNotifyDone : Pos 22, 1 Bit
+0x248 PdeUpdateNeeded : Pos 23, 1 Bit
+0x248 VdmAllowed : Pos 24, 1 Bit
+0x248 Unused : Pos 25, 7 Bits
+0x24c ExitStatus : Int4B
+0x250 NextPageColor : Uint2B
+0x252 SubSystemMinorVersion : UChar
+0x253 SubSystemMajorVersion : UChar
+0x252 SubSystemVersion : Uint2B
+0x254 PriorityClass : UChar
+0x258 VadRoot : _MM_AVL_TABLE
 

kd> dt nt!_Peb
nt!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
+0x003 SpareBool : UChar
+0x004 Mutant : Ptr32 Void
+0x008 ImageBaseAddress : Ptr32 Void
+0x00c Ldr : Ptr32 _PEB_LDR_DATA
+0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
+0x014 SubSystemData : Ptr32 Void
+0x018 ProcessHeap : Ptr32 Void
+0x01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION
+0x020 SparePtr1 : Ptr32 Void
+0x024 SparePtr2 : Ptr32 Void
+0x028 EnvironmentUpdateCount : Uint4B
+0x02c KernelCallbackTable : Ptr32 Void
+0x030 SystemReserved : [1] Uint4B
+0x034 ExecuteOptions : Pos 0, 2 Bits
+0x034 SpareBits : Pos 2, 30 Bits
+0x038 FreeList : Ptr32 _PEB_FREE_BLOCK
+0x03c TlsExpansionCounter : Uint4B
+0x040 TlsBitmap : Ptr32 Void
+0x044 TlsBitmapBits : [2] Uint4B
+0x04c ReadOnlySharedMemoryBase : Ptr32 Void
+0x050 ReadOnlySharedMemoryHeap : Ptr32 Void
+0x054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void
+0x058 AnsiCodePageData : Ptr32 Void
+0x05c OemCodePageData : Ptr32 Void
+0x060 UnicodeCaseTableData : Ptr32 Void
+0x064 NumberOfProcessors : Uint4B
+0x068 NtGlobalFlag : Uint4B
+0x070 CriticalSectionTimeout : _LARGE_INTEGER
+0x078 HeapSegmentReserve : Uint4B
+0x07c HeapSegmentCommit : Uint4B
+0x080 HeapDeCommitTotalFreeThreshold : Uint4B
+0x084 HeapDeCommitFreeBlockThreshold : Uint4B
+0x088 NumberOfHeaps : Uint4B
+0x08c MaximumNumberOfHeaps : Uint4B
+0x090 ProcessHeaps : Ptr32 Ptr32 Void
+0x094 GdiSharedHandleTable : Ptr32 Void
+0x098 ProcessStarterHelper : Ptr32 Void
+0x09c GdiDCAttributeList : Uint4B
+0x0a0 LoaderLock : Ptr32 _RTL_CRITICAL_SECTION
+0x0a4 OSMajorVersion : Uint4B
+0x0a8 OSMinorVersion : Uint4B
+0x0ac OSBuildNumber : Uint2B
+0x0ae OSCSDVersion : Uint2B
+0x0b0 OSPlatformId : Uint4B
+0x0b4 ImageSubsystem : Uint4B
+0x0b8 ImageSubsystemMajorVersion : Uint4B
+0x0bc ImageSubsystemMinorVersion : Uint4B
+0x0c0 ImageProcessAffinityMask : Uint4B
+0x0c4 GdiHandleBuffer : [34] Uint4B
+0x14c PostProcessInitRoutine : Ptr32
+0x150 TlsExpansionBitmap : Ptr32 Void
+0x154 TlsExpansionBitmapBits : [32] Uint4B
+0x1d4 SessionId : Uint4B
+0x1d8 AppCompatFlags : _ULARGE_INTEGER
+0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER
+0x1e8 pShimData : Ptr32 Void
+0x1ec AppCompatInfo : Ptr32 Void
+0x1f0 CSDVersion : _UNICODE_STRING
+0x1f8 ActivationContextData : Ptr32 _ACTIVATION_CONTEXT_DATA
+0x1fc ProcessAssemblyStorageMap : Ptr32 _ASSEMBLY_STORAGE_MAP
+0x200 SystemDefaultActivationContextData : Ptr32 _ACTIVATION_CONTEXT_DATA
+0x204 SystemAssemblyStorageMap : Ptr32 _ASSEMBLY_STORAGE_MAP
+0x208 MinimumStackCommit : Uint4B
+0x20c FlsCallback : Ptr32 Ptr32 Void
+0x210 FlsListHead : _LIST_ENTRY
+0x218 FlsBitmap : Ptr32 Void
+0x21c FlsBitmapBits : [4] Uint4B
+0x22c FlsHighIndex : Uint4B
 

kd> dt nt!_RTL_USER_PROCESS_PARAMETERS
+0x000 MaximumLength : Uint4B
+0x004 Length : Uint4B
+0x008 Flags : Uint4B
+0x00c DebugFlags : Uint4B
+0x010 ConsoleHandle : Ptr32 Void
+0x014 ConsoleFlags : Uint4B
+0x018 StandardInput : Ptr32 Void
+0x01c StandardOutput : Ptr32 Void
+0x020 StandardError : Ptr32 Void
+0x024 CurrentDirectory : _CURDIR
+0x030 DllPath : _UNICODE_STRING
+0x038 ImagePathName : _UNICODE_STRING
+0x040 CommandLine : _UNICODE_STRING
+0x048 Environment : Ptr32 Void
+0x04c StartingX : Uint4B
+0x050 StartingY : Uint4B
+0x054 CountX : Uint4B
+0x058 CountY : Uint4B
+0x05c CountCharsX : Uint4B
+0x060 CountCharsY : Uint4B
+0x064 FillAttribute : Uint4B
+0x068 WindowFlags : Uint4B
+0x06c ShowWindowFlags : Uint4B
+0x070 WindowTitle : _UNICODE_STRING
+0x078 DesktopInfo : _UNICODE_STRING
+0x080 ShellInfo : _UNICODE_STRING
+0x088 RuntimeData : _UNICODE_STRING
+0x090 CurrentDirectores : [32] _RTL_DRIVE_LETTER_CURDIR
 

此时,不用我告诉你了吧!,去抄费尔提到用的方法.把偏移量改一改就可以.

黄森堂 2004-5-31

所有,从上面可以看出来,xfilt的第一个PEB偏移量是错的,2003里,正确是:0x0190,而不是0x01B0.

注:有人会问,从你的结构上看,最后是0x38,可上面代码是0x03C???,
老兄,0x38是UNICODE_STRING, UNICODE_STRING 的结构是:
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING *PUNICODE_STRING;

所有返回其字符串指针要加上4个字节的偏移量:


附上完整代码:
/////////////////////////////////////////////////////////////////
// 类别:
// 文件系统操作
// 功能:
// 取得当前进程名完整路径名(例:C:\WINNT\notepad.exe)
// 参数:
//
// 返回值:
// STATUS_SUCCESS 成功
// 原理:取得EPROCESS->PEB->RTL_USER_PROCESS_PARAMETER->ImagePathName
// 从这原理也可以取出Command Line

// 注意:目前只支持Windows 2000/XP/2003
/////////////////////////////////////////////////////////////////
// 修改历史:
// 黄森堂 2004.2.11
/////////////////////////////////////////////////////////////////
#define BASE_PROCESS_PEB_OFFSET 0x01B0
#define BASE_PEB_PROCESS_PARAMETER_OFFSET 0x0010
#define BASE_PROCESS_PARAMETER_FULL_IMAGE_NAME 0x003C

#define W2003_BASE_PROCESS_PEB_OFFSET 0x0190

PCWSTR FsdGetProcessFullName()
{
DWORD dwAddress;

if(KeGetCurrentIrql() != PASSIVE_LEVEL)
return NULL;

dwAddress = (DWORD)PsGetCurrentProcess();

if(dwAddress == 0 || dwAddress == 0xFFFFFFFF)
return NULL;

//目前只支持Win 2000/xp/2003
if( (g_OsMajorVersion < 5) || (g_OsMinorVersion > 2 ) )
return NULL;

//取得PEB,不同平台的位置是不同的。
if( (g_OsMajorVersion == 5) && (g_OsMinorVersion < 2) )
dwAddress += BASE_PROCESS_PEB_OFFSET;
else
dwAddress += W2003_BASE_PROCESS_PEB_OFFSET;

if((dwAddress = *(DWORD*)dwAddress) == 0) return 0;
// 通过peb取得RTL_USER_PROCESS_PARAMETERS
dwAddress += BASE_PEB_PROCESS_PARAMETER_OFFSET;
if((dwAddress = *(DWORD*)dwAddress) == 0) return 0;

//在RTL_USER_PROCESS_PARAMETERS->ImagePathName保存了路径,偏移为38,
dwAddress += BASE_PROCESS_PARAMETER_FULL_IMAGE_NAME;
if((dwAddress = *(DWORD*)dwAddress) == 0) return 0;

return (PCWSTR)dwAddress;
}

从上面原理中,同样可以得到Command line等等.



<< Home

This page is powered by Blogger. Isn't yours?