再谈进程PID相同的深入探究
作者:sunwear [E.S.T]
shellcoder@163.com
http://blog.csdn.net/sunwear
由于自己疏忽,在前几天写的分析文章中在一个环节中有错误。主要是在进程结束后的分析不够。所以这次在深入的分析一下。结论也许很简单,但是其中的分析,看看会很有帮助。
我们来分析一下进程创建的过程。如果有兴趣可以自己跟CreateProcess函数。创建进程就靠他咯。
首先CreateProcess找到执行程序对应的WIN32映射执行程序后,创建执行程序对象。
然后就是设置EPROCESS块其中包括把进程和会话ID存储到对应的字段中,设置进程退出状态,并创建访问令牌。
再创建初始地址空间和内核进程块与地址空间的设置以及PEB的设置。
创建线程和堆栈环境。
下面的一部就是向WIN32子系统传递信息,包括新建的进程线程句柄。创建标志中的项以及ID和确认其属于WIN32应用程序的标志。后面就是初始化线程并完成整个进程的初始化。
这个过程中,需要详细说明的就是设置EPROCESS结构(也叫KPEB)。每个Windows 2000进程都由一个执行程序进程(EPROCESS)块表示,也就是说在内核中,进程是靠EPROCESS来识别的.下面是EPROCESS的结构定义:typedef struct _EPROCESS {
KPROCESS Pcb;
NTSTATUS ExitStatus;
KEVENT LockEvent;
ULONG LockCount;
LARGE_INTEGER CreateTime;
LARGE_INTEGER ExitTime;
PKTHREAD LockOwner;
HANDLE UniqueProcessId;
LIST_ENTRY ActiveProcessLinks;
SIZE_T QuotaPeakPoolUsage[2];
SIZE_T QuotaPoolUsage[2];
SIZE_T PagefileUsage;
SIZE_T CommitCharge;
SIZE_T PeakPagefileUsage;
SIZE_T PeakVirtualSize;
SIZE_T VirtualSize;
MMSUPPORT Vm;
LIST_ENTRY SessionProcessLinks;
PVOID DebugPort;
PVOID ExceptionPort;
PHANDLE_TABLE ObjectTable;
PACCESS_TOKEN Token;
FAST_MUTEX WorkingSetLock;
PFN_NUMBER WorkingSetPage;
BOOLEAN ProcessOutswapEnabled;
BOOLEAN ProcessOutswapped;
UCHAR AddressSpaceInitialized;
BOOLEAN AddressSpaceDeleted;
FAST_MUTEX AddressCreationLock;
KSPIN_LOCK HyperSpaceLock;
struct _ETHREAD *ForkInProgress;
USHORT VmOperation;
UCHAR ForkWasSuccessful;
UCHAR MmAgressiveWsTrimMask;
PKEVENT VmOperationEvent;
PVOID PaeTop;
ULONG LastFaultCount;
ULONG ModifiedPageCount;
PVOID VadRoot;
PVOID VadHint;
PVOID CloneRoot;
PFN_NUMBER NumberOfPrivatePages;
PFN_NUMBER NumberOfLockedPages;
USHORT NextPageColor;
BOOLEAN ExitProcessCalled;
BOOLEAN CreateProcessReported;
HANDLE SectionHandle;
PPEB Peb;
PVOID SectionBaseAddress;
PEPROCESS_QUOTA_BLOCK QuotaBlock;
NTSTATUS LastThreadExitStatus;
PPAGEFAULT_HISTORY WorkingSetWatch;
HANDLE Win32WindowStation;
HANDLE InheritedFromUniqueProcessId;
ACCESS_MASK GrantedAccess;
ULONG DefaultHardErrorProcessing;
PVOID LdtInformation;
PVOID VadFreeHint;
PVOID VdmObjects;
PVOID DeviceMap;
ULONG SessionId;
LIST_ENTRY PhysicalVadList;
union {
HARDWARE_PTE PageDirectoryPte;
ULONGLONG Filler;
};
ULONG PaePageDirectoryPage;
UCHAR ImageFileName[ 16 ];
ULONG VmTrimFaultValue;
BOOLEAN SetTimerResolution;
UCHAR PriorityClass;
union {
struct {
UCHAR SubSystemMinorVersion;
UCHAR SubSystemMajorVersion;
};
USHORT SubSystemVersion;
};
PVOID Win32Process;
struct _EJOB *Job;
ULONG JobStatus;
LIST_ENTRY JobLinks;
PVOID LockedPagesList;
PVOID SecurityPort ;
PWOW64_PROCESS Wow64Process;
LARGE_INTEGER ReadOperationCount;
LARGE_INTEGER WriteOperationCount;
LARGE_INTEGER OtherOperationCount;
LARGE_INTEGER ReadTransferCount;
LARGE_INTEGER WriteTransferCount;
LARGE_INTEGER OtherTransferCount;
SIZE_T CommitChargeLimit;
SIZE_T CommitChargePeak;
LIST_ENTRY ThreadListHead;
PRTL_BITMAP VadPhysicalPagesBitMap;
ULONG_PTR VadPhysicalPages;
KSPIN_LOCK AweLock;
} EPROCESS;
!kdex2x86.strct EPROCESS也可以给出EPROCESS的定义,主要是我觉得,这个里面的 HANDLE UniqueProcessId;比void *UniqueProcessId 更能体现出pid的类型。这个结构中包含了进程的N多信息。其中在UniqueProcessId中存储着进程的ID,也就是我们常说的PID。
我门在来看下进程结束方面。
ExitProcess(process.c) : 2793
->NtTerminateProcess(psdelete.c) : 378
->ObDereferenceObject(obref.c) : 1721
->ObfDereferenceObject(obref.c) : 1200
ASSERT(ObjectHeader->HandleCount == 0);
...
ObpRemoveObjectRoutine( Object );
...
---------------------------
CloseHandle(handle.c) : 87
->NtClose(obclose.c) : 311
ObpDecrementHandleCount( PsGetCurrentProcess(),
ObjectHeader,
ObjectHeader->Type,
CapturedGrantedAccess );
ObDereferenceObject( Object );
由于代码多,涉及头文件也过多,所以在此进行了一些省略,把重要的贴出来,ExitProcess是调用了NtTerminateProcess来结束进程的。
EPROCESS会在HandleCount等于0的时候从内存中自动释放。如果进程退出,只有等待拥有该进程Handle的进程CloseHandle了相应的进程后,由函数ObpDecrementHandleCount将HandleCount降低,从而是的HandleCount = 0,才将EPROCESS释放出内存。
那我们在来看一个例子,将EPROCESS释放出内存,并不是将EPROCESS这些数据从内存中删除。
打开比记本,然后我们来分析一下在这个进程结束前与结束后,EPROCESS中的数据变化。
lkd>!process 0 0
。。。。。
。。。。。
PROCESS 81bef448 SessionId: 0 Cid: 0d00 Peb: 7ffdf000 ParentCid: 0734
DirBase: 03d30000 ObjectTable: e1274b28 HandleCount: 38.
Image: notepad.exe
然后查看该进程的EPROCESS数据
lkd> dt _eprocess 81bef448
nt!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER 0x1c540c9`dab0f904
+0x078 ExitTime : _LARGE_INTEGER 0x0
+0x080 RundownProtect : _EX_RUNDOWN_REF
+0x084 UniqueProcessId : 0x00000d00
+0x088 ActiveProcessLinks : _LIST_ENTRY [ 0x80570de8 - 0x81be4a48 ]
+0x090 QuotaUsage : [3] 0x960
+0x09c QuotaPeak : [3] 0xb38
+0x0a8 CommitCharge : 0x162
+0x0ac PeakVirtualSize : 0x1e45000
+0x0b0 VirtualSize : 0x1bc6000
+0x0b4 SessionProcessLinks : _LIST_ENTRY [ 0xf8973010 - 0x81be4a74 ]
+0x0bc DebugPort : (null)
+0x0c0 ExceptionPort : 0xe1455d08
+0x0c4 ObjectTable : 0xe1274b28
+0x0c8 Token : _EX_FAST_REF
+0x0cc WorkingSetPage : 0x97f3
+0x0d0 AddressCreationLock : _KGUARDED_MUTEX
+0x0f0 HyperSpaceLock : 0
+0x0f4 ForkInProgress : (null)
+0x0f8 HardwareTrigger : 0
+0x0fc PhysicalVadRoot : (null)
+0x100 CloneRoot : (null)
+0x104 NumberOfPrivatePages : 0xc5
+0x108 NumberOfLockedPages : 0
+0x10c Win32Process : 0xbc2eae70
+0x110 Job : (null)
+0x114 SectionObject : 0xe1d530a0
+0x118 SectionBaseAddress : 0x01000000
+0x11c QuotaBlock : 0x820da788
+0x120 WorkingSetWatch : (null)
+0x124 Win32WindowStation : 0x00000028
+0x128 InheritedFromUniqueProcessId : 0x00000734
+0x12c LdtInformation : (null)
+0x130 VadFreeHint : (null)
+0x134 VdmObjects : (null)
+0x138 DeviceMap : 0xe16457e8
+0x13c Spare0 : [3] (null)
+0x148 PageDirectoryPte : _HARDWARE_PTE
+0x148 Filler : 0
+0x150 Session : 0xf8973000
+0x154 ImageFileName : [16] "notepad.exe"
+0x164 JobLinks : _LIST_ENTRY [ 0x0 - 0x0 ]
+0x16c LockedPagesList : (null)
+0x170 ThreadListHead : _LIST_ENTRY [ 0x81bcbdb4 - 0x81bcbdb4 ]
+0x178 SecurityPort : (null)
+0x17c PaeTop : (null)
+0x180 ActiveThreads : 1
+0x184 GrantedAccess : 0x1f0fff
+0x188 DefaultHardErrorProcessing : 1
+0x18c LastThreadExitStatus : 0
+0x190 Peb : 0x7ffdf000
+0x194 PrefetchTrace : _EX_FAST_REF
+0x198 ReadOperationCount : _LARGE_INTEGER 0xb
+0x1a0 WriteOperationCount : _LARGE_INTEGER 0xb
+0x1a8 OtherOperationCount : _LARGE_INTEGER 0xdf
+0x1b0 ReadTransferCount : _LARGE_INTEGER 0x2ec
+0x1b8 WriteTransferCount : _LARGE_INTEGER 0x318
+0x1c0 OtherTransferCount : _LARGE_INTEGER 0x128c
+0x1c8 CommitChargeLimit : 0
+0x1cc CommitChargePeak : 0x162
+0x1d0 AweInfo : (null)
+0x1d4 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
+0x1d8 Vm : _MMSUPPORT
+0x238 MmProcessLinks : _LIST_ENTRY [ 0x8056abc8 - 0x81be4bf8 ]
+0x240 ModifiedPageCount : 9
+0x244 JobStatus : 0
+0x248 Flags : 0x450801
+0x248 CreateReported : 0y1
+0x248 NoDebugInherit : 0y0
+0x248 ProcessExiting : 0y0
+0x248 ProcessDelete : 0y0
+0x248 Wow64SplitPages : 0y0
+0x248 VmDeleted : 0y0
+0x248 OutswapEnabled : 0y0
+0x248 Outswapped : 0y0
+0x248 ForkFailed : 0y0
+0x248 Wow64VaSpace4Gb : 0y0
+0x248 AddressSpaceInitialized : 0y10
+0x248 SetTimerResolution : 0y0
+0x248 BreakOnTermination : 0y0
+0x248 SessionCreationUnderway : 0y0
+0x248 WriteWatch : 0y0
+0x248 ProcessInSession : 0y1
+0x248 OverrideAddressSpace : 0y0
+0x248 HasAddressSpace : 0y1
+0x248 LaunchPrefetched : 0y0
+0x248 InjectInpageErrors : 0y0
+0x248 VmTopDown : 0y0
+0x248 ImageNotifyDone : 0y1
+0x248 PdeUpdateNeeded : 0y0
+0x248 VdmAllowed : 0y0
+0x248 Unused : 0y0000000 (0)
+0x24c ExitStatus : 259
+0x250 NextPageColor : 0x8bc4
+0x252 SubSystemMinorVersion : 0 ''
+0x253 SubSystemMajorVersion : 0x4 ''
+0x252 SubSystemVersion : 0x400
+0x254 PriorityClass : 0x2 ''
+0x258 VadRoot : _MM_AVL_TABLE
然后在将进程结束掉
lkd> dt _eprocess 81bef448
nt!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER 0x1c540c9`dab0f904
+0x078 ExitTime : _LARGE_INTEGER 0x1c540ca`012ed006
+0x080 RundownProtect : _EX_RUNDOWN_REF
+0x084 UniqueProcessId : 0x00000d00
+0x088 ActiveProcessLinks : _LIST_ENTRY [ 0x81f2b328 - 0x81be4a48 ]
+0x090 QuotaUsage : [3] 0
+0x09c QuotaPeak : [3] 0xb38
+0x0a8 CommitCharge : 0
+0x0ac PeakVirtualSize : 0x1e45000
+0x0b0 VirtualSize : 0x17d2000
+0x0b4 SessionProcessLinks : _LIST_ENTRY [ 0x81f2b354 - 0x81be4a74 ]
+0x0bc DebugPort : (null)
+0x0c0 ExceptionPort : (null)
+0x0c4 ObjectTable : (null)
+0x0c8 Token : _EX_FAST_REF
+0x0cc WorkingSetPage : 0x97f3
+0x0d0 AddressCreationLock : _KGUARDED_MUTEX
+0x0f0 HyperSpaceLock : 0
+0x0f4 ForkInProgress : (null)
+0x0f8 HardwareTrigger : 0
+0x0fc PhysicalVadRoot : (null)
+0x100 CloneRoot : (null)
+0x104 NumberOfPrivatePages : 0
+0x108 NumberOfLockedPages : 0
+0x10c Win32Process : (null)
+0x110 Job : (null)
+0x114 SectionObject : (null)
+0x118 SectionBaseAddress : 0x01000000
+0x11c QuotaBlock : 0x820da788
+0x120 WorkingSetWatch : (null)
+0x124 Win32WindowStation : 0x00000028
+0x128 InheritedFromUniqueProcessId : 0x00000734
+0x12c LdtInformation : (null)
+0x130 VadFreeHint : (null)
+0x134 VdmObjects : (null)
+0x138 DeviceMap : (null)
+0x13c Spare0 : [3] (null)
+0x148 PageDirectoryPte : _HARDWARE_PTE
+0x148 Filler : 0
+0x150 Session : 0xf8973000
+0x154 ImageFileName : [16] "notepad.exe"
+0x164 JobLinks : _LIST_ENTRY [ 0x0 - 0x0 ]
+0x16c LockedPagesList : (null)
+0x170 ThreadListHead : _LIST_ENTRY [ 0x81bef5b8 - 0x81bef5b8 ]
+0x178 SecurityPort : 0x00000001
+0x17c PaeTop : (null)
+0x180 ActiveThreads : 0
+0x184 GrantedAccess : 0x1f0fff
+0x188 DefaultHardErrorProcessing : 1
+0x18c LastThreadExitStatus : 0
+0x190 Peb : 0x7ffdf000
+0x194 PrefetchTrace : _EX_FAST_REF
+0x198 ReadOperationCount : _LARGE_INTEGER 0xf
+0x1a0 WriteOperationCount : _LARGE_INTEGER 0xf
+0x1a8 OtherOperationCount : _LARGE_INTEGER 0x104
+0x1b0 ReadTransferCount : _LARGE_INTEGER 0x3fc
+0x1b8 WriteTransferCount : _LARGE_INTEGER 0x438
+0x1c0 OtherTransferCount : _LARGE_INTEGER 0x176c
+0x1c8 CommitChargeLimit : 0
+0x1cc CommitChargePeak : 0x163
+0x1d0 AweInfo : (null)
+0x1d4 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
+0x1d8 Vm : _MMSUPPORT
+0x238 MmProcessLinks : _LIST_ENTRY [ 0x81f2b4d8 - 0x81be4bf8 ]
+0x240 ModifiedPageCount : 0x11
+0x244 JobStatus : 0
+0x248 Flags : 0x44082d
+0x248 CreateReported : 0y1
+0x248 NoDebugInherit : 0y0
+0x248 ProcessExiting : 0y1
+0x248 ProcessDelete : 0y1
+0x248 Wow64SplitPages : 0y0
+0x248 VmDeleted : 0y1
+0x248 OutswapEnabled : 0y0
+0x248 Outswapped : 0y0
+0x248 ForkFailed : 0y0
+0x248 Wow64VaSpace4Gb : 0y0
+0x248 AddressSpaceInitialized : 0y10
+0x248 SetTimerResolution : 0y0
+0x248 BreakOnTermination : 0y0
+0x248 SessionCreationUnderway : 0y0
+0x248 WriteWatch : 0y0
+0x248 ProcessInSession : 0y0
+0x248 OverrideAddressSpace : 0y0
+0x248 HasAddressSpace : 0y1
+0x248 LaunchPrefetched : 0y0
+0x248 InjectInpageErrors : 0y0
+0x248 VmTopDown : 0y0
+0x248 ImageNotifyDone : 0y1
+0x248 PdeUpdateNeeded : 0y0
+0x248 VdmAllowed : 0y0
+0x248 Unused : 0y0000000 (0)
+0x24c ExitStatus : 0
+0x250 NextPageColor : 0x8bc5
+0x252 SubSystemMinorVersion : 0 ''
+0x253 SubSystemMajorVersion : 0x4 ''
+0x252 SubSystemVersion : 0x400
+0x254 PriorityClass : 0x2 ''
+0x258 VadRoot : _MM_AVL_TABLE
进程结束后EPROCESS依然还在内存中。
而从刚才EPROCESS结构定义中的这句PHANDLE_TABLE ObjectTable; 我们知道 ObjectTable就是指向的HanDleTable 也就是句柄的数据,在进程结束后+0x0c4 ObjectTable : (null)。证明ObjectTable的数据已经被清空。而进程结束后会关闭所有句柄,而释放EPROCESS,但是EPROCESS的数据并没有被删除,只是部分数据被清0。+0x084 UniqueProcessId : 0x00000d00 在进程结束前与结束后,数据依然没有变化。就证明PID数据依然储存在内存中。
知道了这些,我们可以举个例子。
一个父进程创建了子进程,然后子进程结束。那么根据上面的分析,EPROCESS数据依然存在在内存中,没有被释放。而EPROCESS中的UniqueProcessId中还存储着进程ID。win32子系统进程CSRSS.exe,依然继续着它的任务,创建进程。而在这个时候,CSRSS.EXE并不会去验证保存在内存中那些名存实亡的数据。所以还在用他的规律不段的创建进程,这样就会出现不同的EPROCESS使用着同样的PID。也就是说。两个执行程序进程块共同使用着一个PID。这就是所谓的进程PID相同。
在这种情况下任务管理器是看不到这个进程的。
任务管理器调用NtQuerySystemInformation函数来枚举进程信息。
而NtQuerySystemInformation函数是获取进程的链表。然后在执行其他工作。
不过我们来看看在windows2k源代码中关于PspExitProcess的代码
VOID
PspExitProcess(
IN BOOLEAN TrimAddressSpace,
IN PEPROCESS Process
)
{
…………
PoRundownProcess(Process);
if ( Process->ActiveProcessLinks.Flink != NULL &&
Process->ActiveProcessLinks.Blink != NULL ) {
ExAcquireFastMutex(&PspActiveProcessMutex);
RemoveEntryList(&Process->ActiveProcessLinks);
Process->ActiveProcessLinks.Flink = NULL;
Process->ActiveProcessLinks.Blink = NULL;
ExReleaseFastMutex(&PspActiveProcessMutex);
}
……………………
}
从这里看的出来,在进程结束的时候,已经将链表移除了。所以在任务管理器中就无法显示了。但是任务管理器看不到相同PID不一定就证明没有PID相同的进程。上面说了,进程结束把ActiveProcessLinks移除。但是EPROCESS在进程结束的时候,数据依然保存在内存中。
依据人们所知道的关于进程的定义(所有的进程都由一个进程执行块表示,进程执行块就是EPROCESS)而EPROCESS数据保存在内存中并没被删除。在后来的不断创建进程中,就会出现不同的EPROCESS中使用着同样的进程ID。结论也就是两个进程PID相同,但是这样的结果并不会影响系统的正常工作。
后记
因为使用着相同PID的不同的两个EPROCESS,其中一个名存实亡,并非活动中的进程。换句话说,挂着号但是没有实际用途的进程。
由于篇幅原因,在此对win32子系统(CSRSS.exe)产生PID的规则就一带而过,没有做过多解释。
感谢eva,zwell指出第一篇文章中的错误。
联系作者:sunwear:(shellcoder@163.com)