基础
在Windows中关于进程、线程的数据结构有以下
- EPROCESS、KPROCESS、PEB
- ETHREAD、KTHREAD、TEB
EPROCESS
在内核中即0环存在EPROCESS结构体,该结构用于存储有关进程的关键信息,例如进程的状态、进程的地址空间、进程的安全上下文以及它正在使用的其他资源。每个进程都有一个EPROCESS结构,windows通过一个链表将所有的EPROCESS串起来进行管理。修改此结构体需要驱动。
PEB
在用户模式中存在 进程环境块(Process Environment Block)是一种加载到每个进程中的内存结构,它存储了该进程范围内的全局信息。这些信息对于进程的整个生命周期都至关重要,例如进程加载了哪些动态链接库 (DLL)、完整的命令行参数、启动目录、可执行文件的内存基址、进程堆信息以及一些用于调试和系统兼容性的标志位。
//0x7c8 bytes (sizeof) struct _PEB { UCHAR InheritedAddressSpace; //0x0 UCHAR ReadImageFileExecOptions; //0x1 UCHAR BeingDebugged; //0x2 union { UCHAR BitField; //0x3 struct { UCHAR ImageUsesLargePages:1; //0x3 UCHAR IsProtectedProcess:1; //0x3 UCHAR IsImageDynamicallyRelocated:1; //0x3 UCHAR SkipPatchingUser32Forwarders:1; //0x3 UCHAR IsPackagedProcess:1; //0x3 UCHAR IsAppContainer:1; //0x3 UCHAR IsProtectedProcessLight:1; //0x3 UCHAR IsLongPathAwareProcess:1; //0x3 }; }; UCHAR Padding0[4]; //0x4 VOID* Mutant; //0x8 VOID* ImageBaseAddress; //0x10 struct _PEB_LDR_DATA* Ldr; //0x18 struct _RTL_USER_PROCESS_PARAMETERS* ProcessParameters; //0x20 VOID* SubSystemData; //0x28 VOID* ProcessHeap; //0x30 struct _RTL_CRITICAL_SECTION* FastPebLock; //0x38 union _SLIST_HEADER* volatile AtlThunkSListPtr; //0x40 VOID* IFEOKey; //0x48 union { ULONG CrossProcessFlags; //0x50 struct { ULONG ProcessInJob:1; //0x50 ULONG ProcessInitializing:1; //0x50 ULONG ProcessUsingVEH:1; //0x50 ULONG ProcessUsingVCH:1; //0x50 ULONG ProcessUsingFTH:1; //0x50 ULONG ProcessPreviouslyThrottled:1; //0x50 ULONG ProcessCurrentlyThrottled:1; //0x50 ULONG ProcessImagesHotPatched:1; //0x50 ULONG ReservedBits0:24; //0x50 }; }; UCHAR Padding1[4]; //0x54 union { VOID* KernelCallbackTable; //0x58 VOID* UserSharedInfoPtr; //0x58 }; ULONG SystemReserved; //0x60 ULONG AtlThunkSListPtr32; //0x64 VOID* ApiSetMap; //0x68 ULONG TlsExpansionCounter; //0x70 UCHAR Padding2[4]; //0x74 VOID* TlsBitmap; //0x78 ULONG TlsBitmapBits[2]; //0x80 VOID* ReadOnlySharedMemoryBase; //0x88 VOID* SharedData; //0x90 VOID** ReadOnlyStaticServerData; //0x98 VOID* AnsiCodePageData; //0xa0 VOID* OemCodePageData; //0xa8 VOID* UnicodeCaseTableData; //0xb0 ULONG NumberOfProcessors; //0xb8 ULONG NtGlobalFlag; //0xbc union _LARGE_INTEGER CriticalSectionTimeout; //0xc0 ULONGLONG HeapSegmentReserve; //0xc8 ULONGLONG HeapSegmentCommit; //0xd0 ULONGLONG HeapDeCommitTotalFreeThreshold; //0xd8 ULONGLONG HeapDeCommitFreeBlockThreshold; //0xe0 ULONG NumberOfHeaps; //0xe8 ULONG MaximumNumberOfHeaps; //0xec VOID** ProcessHeaps; //0xf0 VOID* GdiSharedHandleTable; //0xf8 VOID* ProcessStarterHelper; //0x100 ULONG GdiDCAttributeList; //0x108 UCHAR Padding3[4]; //0x10c struct _RTL_CRITICAL_SECTION* LoaderLock; //0x110 ULONG OSMajorVersion; //0x118 ULONG OSMinorVersion; //0x11c USHORT OSBuildNumber; //0x120 USHORT OSCSDVersion; //0x122 ULONG OSPlatformId; //0x124 ULONG ImageSubsystem; //0x128 ULONG ImageSubsystemMajorVersion; //0x12c ULONG ImageSubsystemMinorVersion; //0x130 UCHAR Padding4[4]; //0x134 ULONGLONG ActiveProcessAffinityMask; //0x138 ULONG GdiHandleBuffer[60]; //0x140 VOID (*PostProcessInitRoutine)(); //0x230 VOID* TlsExpansionBitmap; //0x238 ULONG TlsExpansionBitmapBits[32]; //0x240 ULONG SessionId; //0x2c0 UCHAR Padding5[4]; //0x2c4 union _ULARGE_INTEGER AppCompatFlags; //0x2c8 union _ULARGE_INTEGER AppCompatFlagsUser; //0x2d0 VOID* pShimData; //0x2d8 VOID* AppCompatInfo; //0x2e0 struct _UNICODE_STRING CSDVersion; //0x2e8 struct _ACTIVATION_CONTEXT_DATA* ActivationContextData; //0x2f8 struct _ASSEMBLY_STORAGE_MAP* ProcessAssemblyStorageMap; //0x300 struct _ACTIVATION_CONTEXT_DATA* SystemDefaultActivationContextData; //0x308 struct _ASSEMBLY_STORAGE_MAP* SystemAssemblyStorageMap; //0x310 ULONGLONG MinimumStackCommit; //0x318 VOID* SparePointers[4]; //0x320 ULONG SpareUlongs[5]; //0x340 VOID* WerRegistrationData; //0x358 VOID* WerShipAssertPtr; //0x360 VOID* pUnused; //0x368 VOID* pImageHeaderHash; //0x370 union { ULONG TracingFlags; //0x378 struct { ULONG HeapTracingEnabled:1; //0x378 ULONG CritSecTracingEnabled:1; //0x378 ULONG LibLoaderTracingEnabled:1; //0x378 ULONG SpareTracingBits:29; //0x378 }; }; UCHAR Padding6[4]; //0x37c ULONGLONG CsrServerReadOnlySharedMemoryBase; //0x380 ULONGLONG TppWorkerpListLock; //0x388 struct _LIST_ENTRY TppWorkerpList; //0x390 VOID* WaitOnAddressHashTable[128]; //0x3a0 VOID* TelemetryCoverageHeader; //0x7a0 ULONG CloudFileFlags; //0x7a8 ULONG CloudFileDiagFlags; //0x7ac CHAR PlaceholderCompatibilityMode; //0x7b0 CHAR PlaceholderCompatibilityModeReserved[7]; //0x7b1 struct _LEAP_SECOND_DATA* LeapSecondData; //0x7b8 union { ULONG LeapSecondFlags; //0x7c0 struct { ULONG SixtySecondEnabled:1; //0x7c0 ULONG Reserved:31; //0x7c0 }; }; ULONG NtGlobalFlag2; //0x7c4 };
|
AI描述PEB字段含义如下,未必正确
0x0 - 0x3: 基本状态标志 InheritedAddressSpace (0x0) - 是否继承父进程的地址空间 ReadImageFileExecOptions (0x1) - 是否读取可执行文件的特殊执行选项 BeingDebugged (0x2) - 进程是否正在被调试 BitField / 位域 (0x3) ImageUsesLargePages - 是否使用大页内存映射 IsProtectedProcess - 是否受保护进程(如防止注入) IsImageDynamicallyRelocated - 可执行映像是否被动态重定位 SkipPatchingUser32Forwarders - 是否跳过 User32 转发修补 IsPackagedProcess - 是否为打包应用(UWP/Store 应用) IsAppContainer - 是否在 AppContainer 沙箱中运行 IsProtectedProcessLight - 轻量保护进程标志 IsLongPathAwareProcess - 是否支持长路径文件名
0x8 - 0x30: 核心指针 Mutant (0x8) - 内核同步对象指针(用于进程互斥) ImageBaseAddress (0x10) - 可执行映像加载基址 Ldr (0x18) - 指向 _PEB_LDR_DATA,记录加载的模块列表 ProcessParameters (0x20) - 指向 _RTL_USER_PROCESS_PARAMETERS,存储命令行、环境变量等 SubSystemData (0x28) - 子系统数据指针(如 GUI/Console 子系统) ProcessHeap (0x30) - 默认堆指针
0x38 - 0x58: 同步和 Thunk 指针 FastPebLock (0x38) - PEB 快速锁,用于线程安全访问 AtlThunkSListPtr (0x40) - ATL 弹性链表指针,用于内部 thunk IFEOKey (0x48) - Image File Execution Options 注册表键指针 CrossProcessFlags (0x50) - 进程状态标志集合(如 ProcessInJob、使用 VEH/FTH 等)
0x58 - 0x98: 内核回调与 API 重映射 KernelCallbackTable / UserSharedInfoPtr (0x58) - 内核回调表或用户共享信息 SystemReserved (0x60) - 系统保留 AtlThunkSListPtr32 (0x64) - 32 位 ATL 弹性链表指针 ApiSetMap (0x68) - API 重映射表 TlsExpansionCounter (0x70) - TLS 扩展计数器 TlsBitmap (0x78) & TlsBitmapBits (0x80) - TLS 使用情况位图 ReadOnlySharedMemoryBase (0x88) - 只读共享内存基址 SharedData (0x90) - KUSER_SHARED_DATA 全局共享数据指针 ReadOnlyStaticServerData (0x98) - 静态服务器只读数据
0xa0 - 0xb8: 字符编码和 CPU 信息 AnsiCodePageData (0xa0) - ANSI 编码页数据表 OemCodePageData (0xa8) - OEM 编码页数据表 UnicodeCaseTableData (0xb0) - Unicode 大小写映射表 NumberOfProcessors (0xb8) - 系统 CPU 核心数量
0xbc - 0xf0: 堆和同步超时 NtGlobalFlag (0xbc) - 内核调试标志 CriticalSectionTimeout (0xc0) - 临界区超时 HeapSegmentReserve (0xc8)、HeapSegmentCommit (0xd0) - 堆段保留与提交大小 HeapDeCommitTotalFreeThreshold (0xd8)、HeapDeCommitFreeBlockThreshold (0xe0) - 堆释放阈值 NumberOfHeaps (0xe8)、MaximumNumberOfHeaps (0xec) - 堆数量限制 ProcessHeaps (0xf0) - 所有进程堆数组
0xf8 - 0x110: GDI 和 Loader GdiSharedHandleTable (0xf8) - GDI 共享句柄表 ProcessStarterHelper (0x100) - 内核启动辅助指针 GdiDCAttributeList (0x108) - GDI 设备上下文属性列表 LoaderLock (0x110) - 模块加载锁
0x118 - 0x134: OS 版本与子系统 OSMajorVersion/OSMinorVersion/OSBuildNumber/OSCSDVersion - 操作系统版本信息 OSPlatformId - 平台 ID ImageSubsystem/ImageSubsystemMajorVersion/ImageSubsystemMinorVersion - 可执行文件子系统类型和版本 ActiveProcessAffinityMask (0x138) - CPU 亲和掩码
0x140 - 0x2c0: GDI 缓冲、初始化例程、TLS 扩展 GdiHandleBuffer (0x140) - GDI 本地句柄缓存 PostProcessInitRoutine (0x230) - 初始化后回调例程 TlsExpansionBitmap (0x238)、TlsExpansionBitmapBits (0x240) - TLS 扩展位图 SessionId (0x2c0) - Terminal Services / Session ID
0x2c8 - 0x310: 兼容性、激活上下文、程序集 AppCompatFlags/AppCompatFlagsUser - 兼容性标志 pShimData/AppCompatInfo - 应用兼容性数据指针 CSDVersion - 服务包 Unicode 字符串 ActivationContextData/ProcessAssemblyStorageMap/ SystemDefaultActivationContextData/SystemAssemblyStorageMap - 激活上下文和程序集存储映射
0x318 - 0x370: 堆栈与调试指针 MinimumStackCommit - 最小栈提交大小 SparePointers / SpareUlongs - 保留指针和整数 WerRegistrationData / WerShipAssertPtr - Windows 错误报告数据 pUnused / pImageHeaderHash - 未使用或镜像哈希指针
0x378 - 0x3a0: 追踪和任务池 TracingFlags - Heap/CritSec/Loader 追踪开关 CsrServerReadOnlySharedMemoryBase - CSR 服务器共享内存 TppWorkerpListLock / TppWorkerpList - 线程池工作列表锁和列表 WaitOnAddressHashTable - 内核等待地址哈希表
0x7a0 - 0x7c4: 云文件、占位兼容、闰秒 TelemetryCoverageHeader - 遥测覆盖头 CloudFileFlags / CloudFileDiagFlags - 云文件标志 PlaceholderCompatibilityMode - 占位兼容模式 LeapSecondData / LeapSecondFlags - 闰秒数据及启用标志 NtGlobalFlag2 - 第二组内核标志(调试、追踪等)
|
其中重要的是Ldr字段,该字段指向 PEB_LDR_DATA 结构
struct _PEB_LDR_DATA* Ldr; //0x18
|
PEB_LDR_DATA 结构包含三个双向链表
- InLoadOrderModuleList - 加载顺序
- InMemoryOrderModuleList - 内存布局顺序
- InInitializationOrderModuleList - 初始化顺序
//0x58 bytes (sizeof) struct _PEB_LDR_DATA { ULONG Length; //0x0 UCHAR Initialized; //0x4 VOID* SsHandle; //0x8 struct _LIST_ENTRY InLoadOrderModuleList; //0x10 struct _LIST_ENTRY InMemoryOrderModuleList; //0x20 struct _LIST_ENTRY InInitializationOrderModuleList; //0x30 VOID* EntryInProgress; //0x40 UCHAR ShutdownInProgress; //0x48 VOID* ShutdownThreadId; //0x50 };
|
每个双向链表中 _LIST_ENTRY 中的 Flink/Blink 指针,指向了一个 LDR_DATA_TABLE_ENTRY 结构体,该结构是描述加载模块的信息,例如DLL基址、DLL名称。每一个加载的模块(.dll)都有一个这样的结构体。该结构体中分别有三个_LIST_ENTRY类型的InLoadOrderLinks、InMemoryOrderLinks、InInitializationOrderLinks 它们又指向了下一个 LDR_DATA_TABLE_ENTRY 结构体
//0x120 bytes (sizeof) struct _LDR_DATA_TABLE_ENTRY { struct _LIST_ENTRY InLoadOrderLinks; //0x0 struct _LIST_ENTRY InMemoryOrderLinks; //0x10 struct _LIST_ENTRY InInitializationOrderLinks; //0x20 VOID* DllBase; //0x30 VOID* EntryPoint; //0x38 ULONG SizeOfImage; //0x40 struct _UNICODE_STRING FullDllName; //0x48 struct _UNICODE_STRING BaseDllName; //0x58 union { UCHAR FlagGroup[4]; //0x68 ULONG Flags; //0x68 struct { ... }; }; USHORT ObsoleteLoadCount; //0x6c USHORT TlsIndex; //0x6e struct _LIST_ENTRY HashLinks; //0x70 ULONG TimeDateStamp; //0x80 struct _ACTIVATION_CONTEXT* EntryPointActivationContext; //0x88 VOID* Lock; //0x90 struct _LDR_DDAG_NODE* DdagNode; //0x98 struct _LIST_ENTRY NodeModuleLink; //0xa0 struct _LDRP_LOAD_CONTEXT* LoadContext; //0xb0 VOID* ParentDllBase; //0xb8 VOID* SwitchBackContext; //0xc0 struct _RTL_BALANCED_NODE BaseAddressIndexNode; //0xc8 struct _RTL_BALANCED_NODE MappingInfoIndexNode; //0xe0 ULONGLONG OriginalBase; //0xf8 union _LARGE_INTEGER LoadTime; //0x100 ULONG BaseNameHashValue; //0x108 enum _LDR_DLL_LOAD_REASON LoadReason; //0x10c ULONG ImplicitPathOptions; //0x110 ULONG ReferenceCount; //0x114 ULONG DependentLoadFlags; //0x118 UCHAR SigningLevel; //0x11c };
|
三个链表记录加载模块的顺序如下:
InLoadOrderModuleList
notepad.exe - ntdll.dll - kernel32.dll - kernelbase.dll
InMemoryOrderModuleList
notepad.exe - ntdll.dll - kernel32.dll - kernelbase.dll
InInitializationOrderModuleList
ntdll.dll - kernelbase.dll - kernel32.dll
TEB
在用户模式中存在 线程环境块(Thread Environment Block),每个线程都具有一个独立的TEB,它存储了与单个线程相关的特定信息,例如 线程ID (TID)、线程的栈基址和栈顶限制、指向线程局部存储 (Thread Local Storage, TLS) 的指针。
// AI总结TEB结构成员意义,未必正确
0x0 - 0x38: NT_TIB NtTib - 包含异常链表、堆栈边界、线程信息块核心指针,用于结构化异常处理和栈管理
0x38 - 0x60: 环境与线程标识 EnvironmentPointer - 指向环境变量块 ClientId - 线程和进程 ID ActiveRpcHandle - 活动 RPC 会话句柄 ThreadLocalStoragePointer - TLS 基址 ProcessEnvironmentBlock - 指向所属进程的 PEB
0x68 - 0x108: 状态与保留字段 LastErrorValue - 上一次调用 SetLastError/Win32 API 的错误码 CountOfOwnedCriticalSections - 线程拥有的临界区数量 CsrClientThread - CSR 服务器客户端线程指针 Win32ThreadInfo - Win32 子系统线程信息 User32Reserved / UserReserved - User32 和用户保留区 WOW32Reserved - WOW64 子系统保留 CurrentLocale - 当前线程区域设置 FpSoftwareStatusRegister - 浮点状态寄存器模拟标志
0x110 - 0x290: 调试和占位相关 ReservedForDebuggerInstrumentation - 调试器使用的保留指针数组 SystemReserved1 - 系统保留 PlaceholderCompatibilityMode / PlaceholderHydrationAlwaysExplicit / PlaceholderReserved - 占位兼容模式和保留 ProxiedProcessId - 被代理的进程 ID _ActivationStack - 激活上下文栈信息 WorkingOnBehalfTicket - 权限代理票据
0x2c0 - 0x2e8: 异常与上下文 ExceptionCode - 线程异常代码 ActivationContextStackPointer - 当前激活上下文栈指针 InstrumentationCallbackSp/Pc/PreviousSp - 仿真/回调上下文保存 TxFsContext - 事务性文件系统上下文 InstrumentationCallbackDisabled - 仿真回调是否禁用 UnalignedLoadStoreExceptions - 未对齐访问异常标志
0x2f0 - 0x7f8: GDI 与客户端信息 GdiTebBatch - 批量 GDI 绘图缓存 RealClientId - 实际线程和进程 ID GdiCachedProcessHandle / GdiClientPID / GdiClientTID - GDI 相关缓存 GdiThreadLocalInfo - 线程本地 GDI 信息 Win32ClientInfo - Win32 客户端信息数组
0x9f0 - 0x1250: OpenGL 和状态 glDispatchTable / glReserved1/2 - OpenGL 调用表及保留 glSectionInfo, glSection, glTable - OpenGL 线程上下文 glCurrentRC / glContext - 当前 OpenGL 渲染上下文 LastStatusValue - 上一次 NT API 状态返回值 StaticUnicodeString / StaticUnicodeBuffer - 静态 Unicode 字符串缓存
0x1478 - 0x1698: 堆栈与 TLS DeallocationStack - 栈释放指针 TlsSlots - 线程局部存储槽 TlsLinks - TLS 链表,用于线程切换 Vdm - 虚拟 DOS 子系统指针 ReservedForNtRpc - NT RPC 保留 DbgSsReserved - 调试子系统保留 HardErrorMode - 严重错误处理模式 Instrumentation - 调试/监控回调保留
0x1710 - 0x1748: 活动、性能、GDI ActivityId - 活动 GUID,用于事件追踪 SubProcessTag - 子进程标记 PerflibData / EtwTraceData - 性能库和 ETW 事件数据 WinSockData - Winsock 数据 GdiBatchCount - GDI 批量计数 CurrentIdealProcessor / IdealProcessorValue - 理想处理器编号 GuaranteedStackBytes - 最小栈保证
0x1750 - 0x17e8: 线程池、TLS 扩展、语言与模拟 ReservedForPerf / ReservedForOle - 性能和 OLE 保留 WaitingOnLoaderLock - 是否在等待 LoaderLock SavedPriorityState - 保存线程优先级状态 ReservedForCodeCoverage - 代码覆盖工具使用 ThreadPoolData - 线程池信息 TlsExpansionSlots - TLS 扩展槽 DeallocationBStore / BStoreLimit - 备用栈存储指针及限制 MuiGeneration - MUI 数据版本 IsImpersonating - 是否模拟用户 NlsCache - 本地化信息缓存 pShimData - 应用兼容性 Shim 数据 HeapData - 堆相关数据 CurrentTransactionHandle / ActiveFrame - 事务处理句柄和上下文 FlsData - Fiber 本地存储 PreferredLanguages, UserPrefLanguages, MergedPrefLanguages - 线程语言首选项 MuiImpersonation - MUI 模拟状态 CrossTebFlags / SameTebFlags - TEB 标志位,如 SafeThunkCall, InDebugPrint, InitialThread 等 TxnScopeEnterCallback / TxnScopeExitCallback / TxnScopeContext - 事务回调和上下文
0x1808 - 0x1828: 资源与容器 LockCount - 线程锁计数 WowTebOffset - WOW64 TEB 偏移 ResourceRetValue - 资源返回值缓存 ReservedForWdf / ReservedForCrt - WDF 和 CRT 保留 EffectiveContainerId - 有效容器 GUID(AppContainer 标识)
|
其中 ProcessEnvironmentBlock 即PEB,相对TEB地址偏移为:
获取PEB
windows设计了段寄存器来指向TEB地址,因此可以直接读取该值获取TEB地址,再通过偏移获取PEB地址
- x86 架构:FS 段寄存器 指向当前线程 TEB 的基址
- FS:[0x18]:TEB 自身地址
- FS:[0x30]: 指向 PEB 的指针
- x64 架构: GS 段寄存器 指向当前线程 TEB 的基址
- GS:[0x30]: TEB 自身地址
- GS:[0x60]: 指向 PEB 的指针
汇编
x86直接使用汇编
__asm { mov eax, fs:[0x30] mov pPeb, eax }
|
x64默认无法使用汇编,需要新建asm文件
.CODE GetInInitializationOrderModuleList PROC mov rax,gs:[60h] ret GetInInitializationOrderModuleList ENDP END
|
内联函数
在 C 语言中,获取 PEB 的方法会因编译器而异。Visual Studio 在编译 64 位程序时不允许使用内联汇编,但提供了一些替代宏。而 GCC 则要求使用内联汇编
- x86:使用
__readfsdword(offset)
- x64:使用
__readgsqword(offset)
#include <intrin.h>
PTEB GetTEB(void) { #ifdef _M_IX86 return (PTEB)__readfsdword(0x18); // FS:[0x18] = Self #elif defined(_M_X64) return (PTEB)__readgsqword(0x30); // GS:[0x30] = Self #else #error "Unsupported architecture" #endif }
PPEB GetPEB(void) { #ifdef _M_IX86 return (PPEB)__readfsdword(0x30); // FS:[0x30] = PEB指针 #elif defined(_M_X64) return (PPEB)__readgsqword(0x60); // GS:[0x60] = PEB指针 #else #error "Unsupported architecture" #endif }
|
Win API
NtCurrentTeb、RtlGetCurrentPeb、NtQueryInformationProcess 获取PEB地址
武器化
在获取到PEB在内存中的地址后,可以通过修改结构体实现攻防对抗
动态API
获取PEB后通过Ldr结构定位加载的模块例如ntdll,解析导出表可以动态使用API函数,实现隐藏程序的IAT表
peb.h,定义所需要的结构体
#pragma once
typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING;
typedef struct _PEB { UCHAR InheritedAddressSpace; //0x0 UCHAR ReadImageFileExecOptions; //0x1 UCHAR BeingDebugged; //0x2 union { UCHAR BitField; //0x3 struct { UCHAR ImageUsesLargePages : 1; //0x3 UCHAR IsProtectedProcess : 1; //0x3 UCHAR IsImageDynamicallyRelocated : 1; //0x3 UCHAR SkipPatchingUser32Forwarders : 1; //0x3 UCHAR IsPackagedProcess : 1; //0x3 UCHAR IsAppContainer : 1; //0x3 UCHAR IsProtectedProcessLight : 1; //0x3 UCHAR IsLongPathAwareProcess : 1; //0x3 }; }; UCHAR Padding0[4]; //0x4 VOID* Mutant; //0x8 VOID* ImageBaseAddress; //0x10 struct _PEB_LDR_DATA* Ldr; //0x18 } PEB, * PPEB;
typedef struct _PEB_LDR_DATA { ULONG Length; //0x0 UCHAR Initialized; //0x4 VOID* SsHandle; //0x8 struct _LIST_ENTRY InLoadOrderModuleList; //0x10 struct _LIST_ENTRY InMemoryOrderModuleList; //0x20 struct _LIST_ENTRY InInitializationOrderModuleList; //0x30 VOID* EntryInProgress; //0x40 UCHAR ShutdownInProgress; //0x48 VOID* ShutdownThreadId; //0x50 } PEB_LDR_DATA, * PPEB_LDR_DATA;
typedef struct _LDR_DATA_TABLE_ENTRY { struct _LIST_ENTRY InLoadOrderLinks; //0x0 struct _LIST_ENTRY InMemoryOrderLinks; //0x10 struct _LIST_ENTRY InInitializationOrderLinks; //0x20 VOID* DllBase; //0x30 VOID* EntryPoint; //0x38 ULONG SizeOfImage; //0x40 struct _UNICODE_STRING FullDllName; //0x48 struct _UNICODE_STRING BaseDllName; //0x58 } LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;
|
PEB.cpp
#include <iostream> #include <windows.h> #include <peb.h>
PPEB GetPeb() { #ifdef _WIN64 return (PPEB)__readgsqword(0x60); #else return (PPEB)__readfsdword(0x30); #endif }
LPVOID EnumModule(const wchar_t* target) { LPVOID address = nullptr; PPEB peb = GetPeb(); PPEB_LDR_DATA Ldr = peb->Ldr;
_LIST_ENTRY* head = &Ldr->InLoadOrderModuleList; _LIST_ENTRY* current = Ldr->InLoadOrderModuleList.Flink;
while (head != current) { PLDR_DATA_TABLE_ENTRY ldr_data = (PLDR_DATA_TABLE_ENTRY)current; printf("[*] name: %S base_address: %p\n", ldr_data->BaseDllName.Buffer, ldr_data->DllBase); if (wcscmp(ldr_data->BaseDllName.Buffer, target) == 0) { address = ldr_data->DllBase; printf("[!]Found module\n"); } current = current->Flink; } return address; }
FARPROC GetExportedFunctionAddress(LPVOID pBaseAddr, const char* functionName) { PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)pBaseAddr; PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((BYTE*)pBaseAddr + dosHeader->e_lfanew);
// 获取导出表 RVA (Relative Virtual Address) PIMAGE_EXPORT_DIRECTORY exportDir = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)pBaseAddr + ntHeaders->OptionalHeader.DataDirectory[0].VirtualAddress);
// 获取名称表和地址表 DWORD* nameRvas = (DWORD*)((BYTE*)pBaseAddr + exportDir->AddressOfNames); DWORD* addrRvas = (DWORD*)((BYTE*)pBaseAddr + exportDir->AddressOfFunctions); WORD* ordinals = (WORD*)((BYTE*)pBaseAddr + exportDir->AddressOfNameOrdinals);
// 遍历名称表,找到函数名 for (DWORD i = 0; i < exportDir->NumberOfNames; ++i) { // 获取导出函数名 char* exportedFuncName = (char*)pBaseAddr + nameRvas[i]; if (strcmp(exportedFuncName, functionName) == 0) { // 找到函数,返回函数地址 DWORD funcRva = addrRvas[ordinals[i]]; FARPROC funcAddr = (FARPROC)((BYTE*)pBaseAddr + funcRva); return funcAddr; } } return NULL; }
int main() { const wchar_t *target_module = L"ntdll.dll"; LPVOID module_addr = EnumModule(target_module);
FARPROC paddr = GetExportedFunctionAddress(module_addr, "NtCreateProcess"); printf("[*]Addr from PEB: %p\n", paddr); FARPROC ntaddr = GetProcAddress(GetModuleHandleA("ntdll"), "NtCreateProcess"); printf("[*]Addr from API: %p\n", ntaddr);
return 0; }
|
模块隐藏
同样通过Ldr结构修改链表,实现隐藏加载的模块。核心逻辑如下
// 我后面的下一个要指向我的下一个 ldr->InLoadOrderModuleList.Blink->Flink = ldr->InLoadOrderModuleList.Flink; // 我下一个的后面要指向我的后面 ldr->InLoadOrderModuleList.Flink->Blink = ldr->InLoadOrderModuleList.Blink;
|
进程伪装
修改PEB结构体中ProcessParameters,实现伪装命令行参数、伪装启动路径等信息,结构体如下
struct _RTL_USER_PROCESS_PARAMETERS* ProcessParameters; //0x20
//0x440 bytes (sizeof) struct _RTL_USER_PROCESS_PARAMETERS { ULONG MaximumLength; //0x0 ULONG Length; //0x4 ULONG Flags; //0x8 ULONG DebugFlags; //0xc VOID* ConsoleHandle; //0x10 ULONG ConsoleFlags; //0x18 VOID* StandardInput; //0x20 VOID* StandardOutput; //0x28 VOID* StandardError; //0x30 struct _CURDIR CurrentDirectory; //0x38 struct _UNICODE_STRING DllPath; //0x50 struct _UNICODE_STRING ImagePathName; //0x60 struct _UNICODE_STRING CommandLine; //0x70 VOID* Environment; //0x80 ULONG StartingX; //0x88 ULONG StartingY; //0x8c ULONG CountX; //0x90 ULONG CountY; //0x94 ULONG CountCharsX; //0x98 ULONG CountCharsY; //0x9c ULONG FillAttribute; //0xa0 ULONG WindowFlags; //0xa4 ULONG ShowWindowFlags; //0xa8 struct _UNICODE_STRING WindowTitle; //0xb0 struct _UNICODE_STRING DesktopInfo; //0xc0 struct _UNICODE_STRING ShellInfo; //0xd0 struct _UNICODE_STRING RuntimeData; //0xe0 struct _RTL_DRIVE_LETTER_CURDIR CurrentDirectores[32]; //0xf0 ULONGLONG EnvironmentSize; //0x3f0 ULONGLONG EnvironmentVersion; //0x3f8 VOID* PackageDependencyData; //0x400 ULONG ProcessGroupId; //0x408 ULONG LoaderThreads; //0x40c struct _UNICODE_STRING RedirectionDllName; //0x410 struct _UNICODE_STRING HeapPartitionName; //0x420 ULONGLONG* DefaultThreadpoolCpuSetMasks; //0x430 ULONG DefaultThreadpoolCpuSetMaskCount; //0x438 ULONG DefaultThreadpoolThreadMaximum; //0x43c };
|
参考
https://www.vergiliusproject.com/
《寒江独钓》内核学习笔记(2)
《寒江独钓》内核学习笔记(3)
《寒江独钓》内核学习笔记(4)
Peb小结
Windows安全攻防-PEB&TEB