日志文章

2007年11月23日 14:54:47

再谈进程PID相同的深入探究

创建时间: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)

类别: 防病毒技术 |  评论(1) |  浏览(1550) |  收藏
一共有 1 条评论
1楼 [楼主]逆风飞扬 2007年11月23日 14:56:28 Says:
点评:该技术研究虽然较早,但从今天的一些成果研究来看,还是有一些研究价值. 感谢这些前沿高手的研究和技术传承. 对作者表示感谢!
发表评论
看不清楚,换一张