内核中PEB块和LDR链用处非常多,常见的用处比如就是本贴要讨论的遍历制定进程的模块;
实验环境:Visual Studio 2013,WDK8.1,Windows 7 x64虚拟机,Windbg,DbgView,KMDmanager。
首先我们用Windbg看一下EPROCESS,PEB在EPROCESS中是以PPEB指针形式作为成员变量的:
kd> dt _EPROCESS nt!_EPROCESS +0x000 Pcb : _KPROCESS +0x160 ProcessLock : _EX_PUSH_LOCK +0x168 CreateTime : _LARGE_INTEGER +0x170 ExitTime : _LARGE_INTEGER +0x178 RundownProtect : _EX_RUNDOWN_REF +0x180 UniqueProcessId : Ptr64 Void +0x188 ActiveProcessLinks : _LIST_ENTRY +0x198 ProcessQuotaUsage : [2] Uint8B +0x1a8 ProcessQuotaPeak : [2] Uint8B +0x1b8 CommitCharge : Uint8B +0x1c0 QuotaBlock : Ptr64 _EPROCESS_QUOTA_BLOCK +0x1c8 CpuQuotaBlock : Ptr64 _PS_CPU_QUOTA_BLOCK +0x1d0 PeakVirtualSize : Uint8B +0x1d8 VirtualSize : Uint8B +0x1e0 SessionProcessLinks : _LIST_ENTRY +0x1f0 DebugPort : Ptr64 Void +0x1f8 ExceptionPortData : Ptr64 Void +0x1f8 ExceptionPortValue : Uint8B +0x1f8 ExceptionPortState : Pos 0, 3 Bits +0x200 ObjectTable : Ptr64 _HANDLE_TABLE +0x208 Token : _EX_FAST_REF +0x210 WorkingSetPage : Uint8B +0x218 AddressCreationLock : _EX_PUSH_LOCK +0x220 RotateInProgress : Ptr64 _ETHREAD +0x228 ForkInProgress : Ptr64 _ETHREAD +0x230 HardwareTrigger : Uint8B +0x238 PhysicalVadRoot : Ptr64 _MM_AVL_TABLE +0x240 CloneRoot : Ptr64 Void +0x248 NumberOfPrivatePages : Uint8B +0x250 NumberOfLockedPages : Uint8B +0x258 Win32Process : Ptr64 Void +0x260 Job : Ptr64 _EJOB +0x268 SectionObject : Ptr64 Void +0x270 SectionBaseAddress : Ptr64 Void +0x278 Cookie : Uint4B +0x27c UmsScheduledThreads : Uint4B +0x280 WorkingSetWatch : Ptr64 _PAGEFAULT_HISTORY +0x288 Win32WindowStation : Ptr64 Void +0x290 InheritedFromUniqueProcessId : Ptr64 Void +0x298 LdtInformation : Ptr64 Void +0x2a0 Spare : Ptr64 Void +0x2a8 ConsoleHostProcess : Uint8B +0x2b0 DeviceMap : Ptr64 Void +0x2b8 EtwDataSource : Ptr64 Void +0x2c0 FreeTebHint : Ptr64 Void +0x2c8 FreeUmsTebHint : Ptr64 Void +0x2d0 PageDirectoryPte : _HARDWARE_PTE +0x2d0 Filler : Uint8B +0x2d8 Session : Ptr64 Void +0x2e0 ImageFileName : [15] UChar +0x2ef PriorityClass : UChar +0x2f0 JobLinks : _LIST_ENTRY +0x300 LockedPagesList : Ptr64 Void +0x308 ThreadListHead : _LIST_ENTRY +0x318 SecurityPort : Ptr64 Void +0x320 Wow64Process : Ptr64 Void +0x328 ActiveThreads : Uint4B +0x32c ImagePathHash : Uint4B +0x330 DefaultHardErrorProcessing : Uint4B +0x334 LastThreadExitStatus : Int4B +0x338 Peb : Ptr64 _PEB +0x340 PrefetchTrace : _EX_FAST_REF +0x348 ReadOperationCount : _LARGE_INTEGER +0x350 WriteOperationCount : _LARGE_INTEGER +0x358 OtherOperationCount : _LARGE_INTEGER +0x360 ReadTransferCount : _LARGE_INTEGER +0x368 WriteTransferCount : _LARGE_INTEGER +0x370 OtherTransferCount : _LARGE_INTEGER +0x378 CommitChargeLimit : Uint8B +0x380 CommitChargePeak : Uint8B +0x388 AweInfo : Ptr64 Void +0x390 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO +0x398 Vm : _MMSUPPORT +0x420 MmProcessLinks : _LIST_ENTRY +0x430 HighestUserAddress : Ptr64 Void +0x438 ModifiedPageCount : Uint4B +0x43c Flags2 : Uint4B +0x43c JobNotReallyActive : Pos 0, 1 Bit +0x43c AccountingFolded : Pos 1, 1 Bit +0x43c NewProcessReported : Pos 2, 1 Bit +0x43c ExitProcessReported : Pos 3, 1 Bit +0x43c ReportCommitChanges : Pos 4, 1 Bit +0x43c LastReportMemory : Pos 5, 1 Bit +0x43c ReportPhysicalPageChanges : Pos 6, 1 Bit +0x43c HandleTableRundown : Pos 7, 1 Bit +0x43c NeedsHandleRundown : Pos 8, 1 Bit +0x43c RefTraceEnabled : Pos 9, 1 Bit +0x43c NumaAware : Pos 10, 1 Bit +0x43c ProtectedProcess : Pos 11, 1 Bit +0x43c DefaultPagePriority : Pos 12, 3 Bits +0x43c PrimaryTokenFrozen : Pos 15, 1 Bit +0x43c ProcessVerifierTarget : Pos 16, 1 Bit +0x43c StackRandomizationDisabled : Pos 17, 1 Bit +0x43c AffinityPermanent : Pos 18, 1 Bit +0x43c AffinityUpdateEnable : Pos 19, 1 Bit +0x43c PropagateNode : Pos 20, 1 Bit +0x43c ExplicitAffinity : Pos 21, 1 Bit +0x440 Flags : Uint4B +0x440 CreateReported : Pos 0, 1 Bit +0x440 NoDebugInherit : Pos 1, 1 Bit +0x440 ProcessExiting : Pos 2, 1 Bit +0x440 ProcessDelete : Pos 3, 1 Bit +0x440 Wow64SplitPages : Pos 4, 1 Bit +0x440 VmDeleted : Pos 5, 1 Bit +0x440 OutswapEnabled : Pos 6, 1 Bit +0x440 Outswapped : Pos 7, 1 Bit +0x440 ForkFailed : Pos 8, 1 Bit +0x440 Wow64VaSpace4Gb : Pos 9, 1 Bit +0x440 AddressSpaceInitialized : Pos 10, 2 Bits +0x440 SetTimerResolution : Pos 12, 1 Bit +0x440 BreakOnTermination : Pos 13, 1 Bit +0x440 DeprioritizeViews : Pos 14, 1 Bit +0x440 WriteWatch : Pos 15, 1 Bit +0x440 ProcessInSession : Pos 16, 1 Bit +0x440 OverrideAddressSpace : Pos 17, 1 Bit +0x440 HasAddressSpace : Pos 18, 1 Bit +0x440 LaunchPrefetched : Pos 19, 1 Bit +0x440 InjectInpageErrors : Pos 20, 1 Bit +0x440 VmTopDown : Pos 21, 1 Bit +0x440 ImageNotifyDone : Pos 22, 1 Bit +0x440 PdeUpdateNeeded : Pos 23, 1 Bit +0x440 VdmAllowed : Pos 24, 1 Bit +0x440 CrossSessionCreate : Pos 25, 1 Bit +0x440 ProcessInserted : Pos 26, 1 Bit +0x440 DefaultIoPriority : Pos 27, 3 Bits +0x440 ProcessSelfDelete : Pos 30, 1 Bit +0x440 SetTimerResolutionLink : Pos 31, 1 Bit +0x444 ExitStatus : Int4B +0x448 VadRoot : _MM_AVL_TABLE +0x488 AlpcContext : _ALPC_PROCESS_CONTEXT +0x4a8 TimerResolutionLink : _LIST_ENTRY +0x4b8 RequestedTimerResolution : Uint4B +0x4bc ActiveThreadsHighWatermark : Uint4B +0x4c0 SmallestTimerResolution : Uint4B +0x4c8 TimerResolutionStackRecord : Ptr64 _PO_DIAG_STACK_RECORD
可以看到在x64中EPROCESS在0x338出的偏移(offset)是成员Peb(64位_PEB结构体指针),利用指针的偏移量即可获得PEB结构体指针的地址;
同样看一下在PEB结构体:
kd> dt _PEB nt!_PEB +0x000 InheritedAddressSpace : UChar +0x001 ReadImageFileExecOptions : UChar +0x002 BeingDebugged : UChar +0x003 BitField : UChar +0x003 ImageUsesLargePages : Pos 0, 1 Bit +0x003 IsProtectedProcess : Pos 1, 1 Bit +0x003 IsLegacyProcess : Pos 2, 1 Bit +0x003 IsImageDynamicallyRelocated : Pos 3, 1 Bit +0x003 SkipPatchingUser32Forwarders : Pos 4, 1 Bit +0x003 SpareBits : Pos 5, 3 Bits +0x008 Mutant : Ptr64 Void +0x010 ImageBaseAddress : Ptr64 Void +0x018 Ldr : Ptr64 _PEB_LDR_DATA +0x020 ProcessParameters : Ptr64 _RTL_USER_PROCESS_PARAMETERS +0x028 SubSystemData : Ptr64 Void +0x030 ProcessHeap : Ptr64 Void +0x038 FastPebLock : Ptr64 _RTL_CRITICAL_SECTION +0x040 AtlThunkSListPtr : Ptr64 Void +0x048 IFEOKey : Ptr64 Void +0x050 CrossProcessFlags : Uint4B +0x050 ProcessInJob : Pos 0, 1 Bit +0x050 ProcessInitializing : Pos 1, 1 Bit +0x050 ProcessUsingVEH : Pos 2, 1 Bit +0x050 ProcessUsingVCH : Pos 3, 1 Bit +0x050 ProcessUsingFTH : Pos 4, 1 Bit +0x050 ReservedBits0 : Pos 5, 27 Bits +0x058 KernelCallbackTable : Ptr64 Void +0x058 UserSharedInfoPtr : Ptr64 Void +0x060 SystemReserved : [1] Uint4B +0x064 AtlThunkSListPtr32 : Uint4B +0x068 ApiSetMap : Ptr64 Void +0x070 TlsExpansionCounter : Uint4B +0x078 TlsBitmap : Ptr64 Void +0x080 TlsBitmapBits : [2] Uint4B +0x088 ReadOnlySharedMemoryBase : Ptr64 Void +0x090 HotpatchInformation : Ptr64 Void +0x098 ReadOnlyStaticServerData : Ptr64 Ptr64 Void +0x0a0 AnsiCodePageData : Ptr64 Void +0x0a8 OemCodePageData : Ptr64 Void +0x0b0 UnicodeCaseTableData : Ptr64 Void +0x0b8 NumberOfProcessors : Uint4B +0x0bc NtGlobalFlag : Uint4B +0x0c0 CriticalSectionTimeout : _LARGE_INTEGER +0x0c8 HeapSegmentReserve : Uint8B +0x0d0 HeapSegmentCommit : Uint8B +0x0d8 HeapDeCommitTotalFreeThreshold : Uint8B +0x0e0 HeapDeCommitFreeBlockThreshold : Uint8B +0x0e8 NumberOfHeaps : Uint4B +0x0ec MaximumNumberOfHeaps : Uint4B +0x0f0 ProcessHeaps : Ptr64 Ptr64 Void +0x0f8 GdiSharedHandleTable : Ptr64 Void +0x100 ProcessStarterHelper : Ptr64 Void +0x108 GdiDCAttributeList : Uint4B +0x110 LoaderLock : Ptr64 _RTL_CRITICAL_SECTION +0x118 OSMajorVersion : Uint4B +0x11c OSMinorVersion : Uint4B +0x120 OSBuildNumber : Uint2B +0x122 OSCSDVersion : Uint2B +0x124 OSPlatformId : Uint4B +0x128 ImageSubsystem : Uint4B +0x12c ImageSubsystemMajorVersion : Uint4B +0x130 ImageSubsystemMinorVersion : Uint4B +0x138 ActiveProcessAffinityMask : Uint8B +0x140 GdiHandleBuffer : [60] Uint4B +0x230 PostProcessInitRoutine : Ptr64 void +0x238 TlsExpansionBitmap : Ptr64 Void +0x240 TlsExpansionBitmapBits : [32] Uint4B +0x2c0 SessionId : Uint4B +0x2c8 AppCompatFlags : _ULARGE_INTEGER +0x2d0 AppCompatFlagsUser : _ULARGE_INTEGER +0x2d8 pShimData : Ptr64 Void +0x2e0 AppCompatInfo : Ptr64 Void +0x2e8 CSDVersion : _UNICODE_STRING +0x2f8 ActivationContextData : Ptr64 _ACTIVATION_CONTEXT_DATA +0x300 ProcessAssemblyStorageMap : Ptr64 _ASSEMBLY_STORAGE_MAP +0x308 SystemDefaultActivationContextData : Ptr64 _ACTIVATION_CONTEXT_DATA +0x310 SystemAssemblyStorageMap : Ptr64 _ASSEMBLY_STORAGE_MAP +0x318 MinimumStackCommit : Uint8B +0x320 FlsCallback : Ptr64 _FLS_CALLBACK_INFO +0x328 FlsListHead : _LIST_ENTRY +0x338 FlsBitmap : Ptr64 Void +0x340 FlsBitmapBits : [4] Uint4B +0x350 FlsHighIndex : Uint4B +0x358 WerRegistrationData : Ptr64 Void +0x360 WerShipAssertPtr : Ptr64 Void +0x368 pContextData : Ptr64 Void +0x370 pImageHeaderHash : Ptr64 Void +0x378 TracingFlags : Uint4B +0x378 HeapTracingEnabled : Pos 0, 1 Bit +0x378 CritSecTracingEnabled : Pos 1, 1 Bit +0x378 SpareTracingBits : Pos 2, 30 Bits
可以看出,PEB结构体地址在0x018处偏移是指向了Ldr(_PEB_LDR_DATA结构体的64位指针),同样利用指针的偏移可以得到它,进一步看看_PEB_LDR_DATA这个结构图中有什么:
_PEB_LDR_DATA:
kd> dt _PEB_LDR_DATA nt!_PEB_LDR_DATA +0x000 Length : Uint4B +0x004 Initialized : UChar +0x008 SsHandle : Ptr64 Void +0x010 InLoadOrderModuleList : _LIST_ENTRY +0x020 InMemoryOrderModuleList : _LIST_ENTRY +0x030 InInitializationOrderModuleList : _LIST_ENTRY +0x040 EntryInProgress : Ptr64 Void +0x048 ShutdownInProgress : UChar +0x050 ShutdownThreadId : Ptr64 Void
PEB_LDR_DATA中,在0x010偏移处可以得到InLoadOrderModuleList这个链表,是双向链表_LIST_ENTRY类型的,我们最后遍历这个LIST_ENTRY链表就可以了。
以下为代码实现,说一下几个注意事项:
1.程序中需要利用PsLookupProcessByProcessId来获取制定进程的PERPROCESS,然后进行指针的偏移量计算来获得PEB, LDR以及最后的双向链表InLoadOrderModuleList,其中比如我们第一次得到了PEPROCESS,将其转化为ULONG64类型的数,所做的(ULONG64)eproc + 0x338得到的是PEB结构体指针的地址,所以我们如果要得到PEB结构体的指针(也就是说PEB结构体的地址)的话,需要再用“*”来取他的值,也就是这样:ULONG64 peb_addr = *(PULONG64)((ULONG64)eproc + 0x338);同理获得ldr结构体的地址也需要注意!
2.获取PEB结构体的地址可以使用API:PsGetProcessPeb,但是为了锻炼C语言指针的应用能力,本程序全部使用结构体指针的偏移来获取。
3.本程序用枚举当前虚拟机Win7 x64上的explorer.exe的模块为例,如果重复使用的话需要重新更新一下你所要枚举的进程的当前句柄。
程序如下(C语言):
#include <ntddk.h> #include <windef.h> #define PEB_OFFSET_IN_EPROCESS 0x338 #define LDR_OFFSET_IN_PEB 0x018 #define InLoadOrderModuleList_OFFSET 0x010 typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY64 InLoadOrderLinks; LIST_ENTRY64 InMemoryOrderLinks; LIST_ENTRY64 InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; PVOID SectionPointer; ULONG CheckSum; PVOID LoadedImports; PVOID EntryPointActivationContext; PVOID PatchInformation; LIST_ENTRY64 ForwarderLinks; LIST_ENTRY64 ServiceTagLinks; LIST_ENTRY64 StaticLinks; PVOID ContextInformation; ULONG64 OriginalBase; LARGE_INTEGER LoadTime; } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; typedef struct _KAPC_STATE { LIST_ENTRY ApcListHead[2]; PKPROCESS Process; UCHAR KernelApcInProgress; UCHAR KernelApcPending; UCHAR UserApcPending; } KAPC_STATE, *PKAPC_STATE; // Functions declaration NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process); VOID KeStackAttachProcess(PRKPROCESS PROCESS,PKAPC_STATE ApcState); VOID KeUnstackDetachProcess(PKAPC_STATE ApcState); // // Get the pointer to the EPROCESS of the specific pid on the process. // PEPROCESS LookupProcess(HANDLE hPid) { PEPROCESS eproc = NULL; if (NT_SUCCESS(PsLookupProcessByProcessId(hPid, &eproc))) { return eproc; } return NULL; } // Enumerate the module of the specific process. VOID EnumerateModule(HANDLE hPid) { KAPC_STATE ks; PEPROCESS eproc = LookupProcess(hPid); if (eproc == NULL) { DbgPrint("Can't find the EPROCESS...\n"); return; } __try { // Get the peb address, the PEB structure in the EPROCESS is a pointer to PEB, named PPEB. // So the "(ULONG64)eproc + PEB_OFFSET_IN_EPROCESS" is the address the pointer to PEB. // And finally, use the "*" to get the address to the PEB structure. ULONG64 peb = *(PULONG64)((ULONG64)eproc + PEB_OFFSET_IN_EPROCESS); KeStackAttachProcess(eproc, &ks); // The LDR structure in PEB is also a pointer to PEB_LDR_DATA // So, "(ULONG64)peb + LDR_OFFSET_IN_PEB" is the address of pointer to PEB_LDR_DATA. // And the ULONG64 ldr is finally get the address of PEB_LDR_DATA structure. ULONG64 ldr = *(PULONG64)((ULONG64)peb + LDR_OFFSET_IN_PEB); // Get the address of "InLoadOrderModuleList" which in the PEB_LDR_DATA structure. PLIST_ENTRY pListHead = (PLIST_ENTRY)(ldr + InLoadOrderModuleList_OFFSET); PLIST_ENTRY pMod = pListHead->Flink; while (pMod != pListHead) { DbgPrint("Base=%p, Size=%ld, Path=%wZ\n", (PVOID)(((PLDR_DATA_TABLE_ENTRY)pMod)->DllBase), (ULONG)(((PLDR_DATA_TABLE_ENTRY)pMod)->SizeOfImage), &(((PLDR_DATA_TABLE_ENTRY)pMod)->FullDllName)); pMod = pMod->Flink; } } __except (EXCEPTION_EXECUTE_HANDLER) { DbgPrint("EXCEPTION_EXECUTE_HANDLER is occure...\n"); } KeUnstackDetachProcess(&ks); } VOID DriverUnload(IN PDRIVER_OBJECT pDrvObj) { DbgPrint("DriverUnload...\n"); } NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDrvObj, IN PUNICODE_STRING RegistryPath) { DbgPrint("DriverEntry...\n"); pDrvObj->DriverUnload = DriverUnload; EnumerateModule((HANDLE)1320); // explorer.exe's current handle pid. return STATUS_SUCCESS; }
得到结果(只展示一部分了):