创建时间:2005-04-21 更新时间:2005-04-21 文章属性:原创 文章提交:sunwear (btwlu_at_163.com) 再谈进程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) |